From b2fea51f107eb35bfb37f3af33fa15fd9e00bf86 Mon Sep 17 00:00:00 2001
From: Nils <>
Date: Sun, 23 Feb 2020 21:22:41 +0100
Subject: [PATCH] Use some magic that looks like static files as template
---
template/.gitignore | 127 +
template/LICENSE | 674 ++++
template/Makefile.in | 89 +
template/README.md | 34 +
template/__init__.py | 0
template/calfbox/.gitignore | 31 +
template/calfbox/API | 133 +
template/calfbox/AUTHORS | 1 +
template/calfbox/COPYING | 674 ++++
template/calfbox/ChangeLog | 0
template/calfbox/INSTALL | 365 ++
template/calfbox/Makefile.am | 152 +
template/calfbox/NEWS | 0
template/calfbox/README | 1 +
template/calfbox/README.md | 63 +
template/calfbox/adhoc_example.py | 47 +
template/calfbox/app.c | 391 ++
template/calfbox/app.h | 51 +
template/calfbox/appmenu.c | 466 +++
template/calfbox/autogen.sh | 6 +
template/calfbox/auxbus.c | 102 +
template/calfbox/auxbus.h | 47 +
template/calfbox/background_example.py | 6 +
template/calfbox/biquad-float.h | 398 ++
template/calfbox/blob.c | 130 +
template/calfbox/blob.h | 39 +
template/calfbox/cboxrc-example | 574 +++
template/calfbox/chorus.c | 175 +
template/calfbox/cleanpythonbuild.sh | 11 +
template/calfbox/cmd.c | 208 +
template/calfbox/cmd.h | 64 +
template/calfbox/compressor.c | 155 +
template/calfbox/config-api.c | 357 ++
template/calfbox/config-api.h | 53 +
template/calfbox/configure.ac | 160 +
template/calfbox/delay.c | 139 +
template/calfbox/distortion.c | 138 +
template/calfbox/dom.c | 372 ++
template/calfbox/dom.h | 155 +
template/calfbox/drvjunk/miditest.py | 152 +
template/calfbox/drvjunk/omega.c | 255 ++
template/calfbox/dspmath.h | 238 ++
template/calfbox/engine.c | 445 ++
template/calfbox/engine.h | 72 +
template/calfbox/envelope.h | 322 ++
template/calfbox/eq.c | 194 +
template/calfbox/eq.h | 31 +
template/calfbox/errors.c | 70 +
template/calfbox/errors.h | 44 +
template/calfbox/example.py | 554 +++
template/calfbox/experiments/interactive.py | 57 +
template/calfbox/experiments/meta.py | 602 +++
.../experiments/playPatternsAsMeasures.py | 51 +
.../calfbox/experiments/printAllMidiEvents.py | 32 +
.../printAllMidiEventsSpecificPort.py | 35 +
.../printNotesOnlyDuringPlayback.py | 79 +
.../calfbox/experiments/simplePlayback.py | 30 +
.../calfbox/experiments/simplerPlayback.py | 25 +
template/calfbox/experiments/testmetadata.py | 82 +
template/calfbox/fbr.c | 373 ++
template/calfbox/fifo.c | 22 +
template/calfbox/fifo.h | 136 +
template/calfbox/fluid.c | 412 ++
template/calfbox/fuzz.c | 173 +
template/calfbox/fxchain.c | 231 ++
template/calfbox/gate.c | 181 +
template/calfbox/hwcfg.c | 172 +
template/calfbox/hwcfg.h | 28 +
template/calfbox/instr.c | 251 ++
template/calfbox/instr.h | 61 +
template/calfbox/io.c | 625 +++
template/calfbox/io.h | 194 +
template/calfbox/ioenv.h | 46 +
template/calfbox/jack_api_example.py | 177 +
template/calfbox/jack_audio_routing.py | 65 +
template/calfbox/jack_output_routing.py | 50 +
template/calfbox/jack_scene_routing.py | 36 +
template/calfbox/jackinput.c | 153 +
template/calfbox/jackio.c | 1408 +++++++
template/calfbox/layer.c | 293 ++
template/calfbox/layer.h | 63 +
template/calfbox/limiter.c | 154 +
template/calfbox/main.c | 450 +++
template/calfbox/master.c | 389 ++
template/calfbox/master.h | 100 +
template/calfbox/menu.c | 288 ++
template/calfbox/menu.h | 67 +
template/calfbox/menuitem.c | 303 ++
template/calfbox/menuitem.h | 133 +
template/calfbox/meter.c | 137 +
template/calfbox/meter.h | 38 +
template/calfbox/midi.c | 113 +
template/calfbox/midi.h | 125 +
template/calfbox/mididest.c | 254 ++
template/calfbox/mididest.h | 76 +
template/calfbox/module.c | 270 ++
template/calfbox/module.h | 181 +
template/calfbox/novabox.py | 107 +
template/calfbox/onepole-float.h | 205 +
template/calfbox/onepole-int.h | 120 +
template/calfbox/pattern-maker.c | 183 +
template/calfbox/pattern-maker.h | 40 +
template/calfbox/pattern.c | 461 +++
template/calfbox/pattern.h | 57 +
template/calfbox/phaser.c | 251 ++
template/calfbox/prefetch_pipe.c | 309 ++
template/calfbox/prefetch_pipe.h | 95 +
template/calfbox/py/__init__.py | 0
template/calfbox/py/_cbox2.py | 187 +
template/calfbox/py/cbox.py | 1147 ++++++
template/calfbox/py/drum_pattern_editor.py | 603 +++
template/calfbox/py/drumkit_editor.py | 540 +++
template/calfbox/py/fx_gui.py | 432 ++
template/calfbox/py/gui_tools.py | 345 ++
template/calfbox/py/instr_gui.py | 385 ++
template/calfbox/py/metadata.py | 99 +
template/calfbox/py/nocturn.py | 122 +
template/calfbox/py/nullbox.py | 53 +
template/calfbox/py/sfzparser.py | 63 +
template/calfbox/recsrc.c | 130 +
template/calfbox/recsrc.h | 64 +
template/calfbox/reverb.c | 355 ++
template/calfbox/rt.c | 506 +++
template/calfbox/rt.h | 208 +
template/calfbox/sampler.c | 761 ++++
template/calfbox/sampler.h | 339 ++
template/calfbox/sampler_api_example.py | 59 +
template/calfbox/sampler_api_example2.py | 32 +
template/calfbox/sampler_api_example4.py | 27 +
template/calfbox/sampler_api_test.py | 202 +
template/calfbox/sampler_channel.c | 519 +++
template/calfbox/sampler_gen.c | 509 +++
template/calfbox/sampler_impl.h | 63 +
template/calfbox/sampler_layer.c | 1991 +++++++++
template/calfbox/sampler_layer.h | 611 +++
template/calfbox/sampler_nif.c | 114 +
template/calfbox/sampler_prevoice.c | 86 +
template/calfbox/sampler_prg.c | 596 +++
template/calfbox/sampler_prg.h | 120 +
template/calfbox/sampler_rll.c | 165 +
template/calfbox/sampler_voice.c | 993 +++++
template/calfbox/scene.c | 1238 ++++++
template/calfbox/scene.h | 82 +
template/calfbox/scripting.c | 579 +++
template/calfbox/scripting.h | 19 +
template/calfbox/seq-adhoc.c | 56 +
template/calfbox/seq.c | 930 +++++
template/calfbox/seq.h | 199 +
template/calfbox/setup.py | 144 +
template/calfbox/sfzloader.c | 292 ++
template/calfbox/sfzloader.h | 26 +
template/calfbox/sfzparser.c | 523 +++
template/calfbox/sfzparser.h | 47 +
template/calfbox/skel.c | 105 +
template/calfbox/song.c | 357 ++
template/calfbox/song.h | 62 +
template/calfbox/song_api_example.py | 76 +
template/calfbox/song_api_example2.py | 91 +
template/calfbox/stm.h | 43 +
template/calfbox/streamplay.c | 734 ++++
template/calfbox/streamrec.c | 244 ++
template/calfbox/synthbass.sfz | 12 +
template/calfbox/tarfile.c | 339 ++
template/calfbox/tarfile.h | 74 +
template/calfbox/test.py | 364 ++
template/calfbox/tests.c | 675 ++++
template/calfbox/tests.h | 48 +
template/calfbox/tonectl.c | 133 +
template/calfbox/tonewheel.c | 586 +++
template/calfbox/track.c | 279 ++
template/calfbox/track.h | 62 +
template/calfbox/ui.c | 65 +
template/calfbox/ui.h | 40 +
template/calfbox/usb_api_example.py | 61 +
template/calfbox/usbaudio.c | 679 ++++
template/calfbox/usbio.c | 502 +++
template/calfbox/usbio_impl.h | 255 ++
template/calfbox/usbmidi.c | 397 ++
template/calfbox/usbprobe.c | 751 ++++
template/calfbox/wavebank.c | 541 +++
template/calfbox/wavebank.h | 77 +
template/configure.template | 68 +
template/documentation/LICENSE | 430 ++
template/documentation/build.py | 119 +
template/documentation/css/normalize.css | 427 ++
template/documentation/css/style.css | 203 +
template/documentation/english.adoc.template | 116 +
template/documentation/genlektor.py | 56 +
template/documentation/german.adoc.template | 123 +
template/documentation/index.adoc.template | 31 +
template/documentation/readme.template | 120 +
template/engine/__init__.py | 0
template/engine/api.py | 486 +++
template/engine/data.py | 59 +
template/engine/duration.py | 128 +
template/engine/history.py | 148 +
template/engine/input_midi.py | 246 ++
template/engine/ly2cbox.py | 87 +
template/engine/metronome.py | 128 +
template/engine/midi.py | 313 ++
template/engine/pitch.py | 1165 ++++++
template/engine/resources/LICENSE | 1 +
.../engine/resources/metronome/metronome.sfz | 2 +
.../engine/resources/metronome/normal.wav | Bin 0 -> 133692 bytes
.../engine/resources/metronome/stressed.wav | Bin 0 -> 106120 bytes
template/engine/sampler_sf2.py | 323 ++
template/engine/sequencer.py | 667 +++
template/engine/session.py | 179 +
template/gitignore.template | 130 +
template/helper.py | 98 +
template/qtgui/__init__.py | 0
template/qtgui/about.py | 173 +
template/qtgui/chooseSessionDirectory.py | 115 +
template/qtgui/constantsAndConfigs.py | 170 +
template/qtgui/debugScript.py | 71 +
template/qtgui/designer/about.py | 150 +
template/qtgui/designer/about.ui | 355 ++
.../qtgui/designer/chooseSessionDirectory.py | 119 +
.../qtgui/designer/chooseSessionDirectory.ui | 248 ++
template/qtgui/designer/usermanual.py | 54 +
template/qtgui/designer/usermanual.ui | 75 +
template/qtgui/flowlayout.py | 159 +
template/qtgui/helper.py | 166 +
template/qtgui/mainwindow.py | 384 ++
template/qtgui/menu.py | 396 ++
template/qtgui/nsmclient.py | 630 +++
template/qtgui/nsmsingleserver.py | 119 +
template/qtgui/resources.py | 3574 +++++++++++++++++
template/qtgui/resources/buildresources.sh | 7 +
template/qtgui/resources/euterpe.license.txt | 94 +
template/qtgui/resources/euterpe.ttf | Bin 0 -> 50864 bytes
template/qtgui/resources/icons/wholeNote.png | Bin 0 -> 1914 bytes
template/qtgui/resources/resources.qrc | 7 +
.../qtgui/resources/translations/config.pro | 8 +
template/qtgui/resources/translations/de.qm | Bin 0 -> 2981 bytes
template/qtgui/resources/translations/de.ts | 116 +
.../qtgui/resources/translations/update.sh | 4 +
template/qtgui/submenus.py | 122 +
template/qtgui/usermanual.py | 70 +
template/start.py | 310 ++
240 files changed, 57776 insertions(+)
create mode 100644 template/.gitignore
create mode 100644 template/LICENSE
create mode 100644 template/Makefile.in
create mode 100644 template/README.md
create mode 100644 template/__init__.py
create mode 100644 template/calfbox/.gitignore
create mode 100644 template/calfbox/API
create mode 100644 template/calfbox/AUTHORS
create mode 100644 template/calfbox/COPYING
create mode 100644 template/calfbox/ChangeLog
create mode 100644 template/calfbox/INSTALL
create mode 100644 template/calfbox/Makefile.am
create mode 100644 template/calfbox/NEWS
create mode 120000 template/calfbox/README
create mode 100644 template/calfbox/README.md
create mode 100644 template/calfbox/adhoc_example.py
create mode 100644 template/calfbox/app.c
create mode 100644 template/calfbox/app.h
create mode 100644 template/calfbox/appmenu.c
create mode 100755 template/calfbox/autogen.sh
create mode 100644 template/calfbox/auxbus.c
create mode 100644 template/calfbox/auxbus.h
create mode 100644 template/calfbox/background_example.py
create mode 100644 template/calfbox/biquad-float.h
create mode 100644 template/calfbox/blob.c
create mode 100644 template/calfbox/blob.h
create mode 100644 template/calfbox/cboxrc-example
create mode 100644 template/calfbox/chorus.c
create mode 100755 template/calfbox/cleanpythonbuild.sh
create mode 100644 template/calfbox/cmd.c
create mode 100644 template/calfbox/cmd.h
create mode 100644 template/calfbox/compressor.c
create mode 100644 template/calfbox/config-api.c
create mode 100644 template/calfbox/config-api.h
create mode 100644 template/calfbox/configure.ac
create mode 100644 template/calfbox/delay.c
create mode 100644 template/calfbox/distortion.c
create mode 100644 template/calfbox/dom.c
create mode 100644 template/calfbox/dom.h
create mode 100644 template/calfbox/drvjunk/miditest.py
create mode 100644 template/calfbox/drvjunk/omega.c
create mode 100644 template/calfbox/dspmath.h
create mode 100644 template/calfbox/engine.c
create mode 100644 template/calfbox/engine.h
create mode 100644 template/calfbox/envelope.h
create mode 100644 template/calfbox/eq.c
create mode 100644 template/calfbox/eq.h
create mode 100644 template/calfbox/errors.c
create mode 100644 template/calfbox/errors.h
create mode 100644 template/calfbox/example.py
create mode 100755 template/calfbox/experiments/interactive.py
create mode 100644 template/calfbox/experiments/meta.py
create mode 100755 template/calfbox/experiments/playPatternsAsMeasures.py
create mode 100755 template/calfbox/experiments/printAllMidiEvents.py
create mode 100755 template/calfbox/experiments/printAllMidiEventsSpecificPort.py
create mode 100755 template/calfbox/experiments/printNotesOnlyDuringPlayback.py
create mode 100755 template/calfbox/experiments/simplePlayback.py
create mode 100755 template/calfbox/experiments/simplerPlayback.py
create mode 100755 template/calfbox/experiments/testmetadata.py
create mode 100644 template/calfbox/fbr.c
create mode 100644 template/calfbox/fifo.c
create mode 100644 template/calfbox/fifo.h
create mode 100644 template/calfbox/fluid.c
create mode 100644 template/calfbox/fuzz.c
create mode 100644 template/calfbox/fxchain.c
create mode 100644 template/calfbox/gate.c
create mode 100644 template/calfbox/hwcfg.c
create mode 100644 template/calfbox/hwcfg.h
create mode 100644 template/calfbox/instr.c
create mode 100644 template/calfbox/instr.h
create mode 100644 template/calfbox/io.c
create mode 100644 template/calfbox/io.h
create mode 100644 template/calfbox/ioenv.h
create mode 100644 template/calfbox/jack_api_example.py
create mode 100644 template/calfbox/jack_audio_routing.py
create mode 100644 template/calfbox/jack_output_routing.py
create mode 100644 template/calfbox/jack_scene_routing.py
create mode 100644 template/calfbox/jackinput.c
create mode 100644 template/calfbox/jackio.c
create mode 100644 template/calfbox/layer.c
create mode 100644 template/calfbox/layer.h
create mode 100644 template/calfbox/limiter.c
create mode 100644 template/calfbox/main.c
create mode 100644 template/calfbox/master.c
create mode 100644 template/calfbox/master.h
create mode 100644 template/calfbox/menu.c
create mode 100644 template/calfbox/menu.h
create mode 100644 template/calfbox/menuitem.c
create mode 100644 template/calfbox/menuitem.h
create mode 100644 template/calfbox/meter.c
create mode 100644 template/calfbox/meter.h
create mode 100644 template/calfbox/midi.c
create mode 100644 template/calfbox/midi.h
create mode 100644 template/calfbox/mididest.c
create mode 100644 template/calfbox/mididest.h
create mode 100644 template/calfbox/module.c
create mode 100644 template/calfbox/module.h
create mode 100644 template/calfbox/novabox.py
create mode 100644 template/calfbox/onepole-float.h
create mode 100644 template/calfbox/onepole-int.h
create mode 100644 template/calfbox/pattern-maker.c
create mode 100644 template/calfbox/pattern-maker.h
create mode 100644 template/calfbox/pattern.c
create mode 100644 template/calfbox/pattern.h
create mode 100644 template/calfbox/phaser.c
create mode 100644 template/calfbox/prefetch_pipe.c
create mode 100644 template/calfbox/prefetch_pipe.h
create mode 100644 template/calfbox/py/__init__.py
create mode 100644 template/calfbox/py/_cbox2.py
create mode 100644 template/calfbox/py/cbox.py
create mode 100644 template/calfbox/py/drum_pattern_editor.py
create mode 100644 template/calfbox/py/drumkit_editor.py
create mode 100644 template/calfbox/py/fx_gui.py
create mode 100644 template/calfbox/py/gui_tools.py
create mode 100644 template/calfbox/py/instr_gui.py
create mode 100644 template/calfbox/py/metadata.py
create mode 100644 template/calfbox/py/nocturn.py
create mode 100644 template/calfbox/py/nullbox.py
create mode 100644 template/calfbox/py/sfzparser.py
create mode 100644 template/calfbox/recsrc.c
create mode 100644 template/calfbox/recsrc.h
create mode 100644 template/calfbox/reverb.c
create mode 100644 template/calfbox/rt.c
create mode 100644 template/calfbox/rt.h
create mode 100644 template/calfbox/sampler.c
create mode 100644 template/calfbox/sampler.h
create mode 100644 template/calfbox/sampler_api_example.py
create mode 100644 template/calfbox/sampler_api_example2.py
create mode 100644 template/calfbox/sampler_api_example4.py
create mode 100644 template/calfbox/sampler_api_test.py
create mode 100644 template/calfbox/sampler_channel.c
create mode 100644 template/calfbox/sampler_gen.c
create mode 100644 template/calfbox/sampler_impl.h
create mode 100644 template/calfbox/sampler_layer.c
create mode 100644 template/calfbox/sampler_layer.h
create mode 100644 template/calfbox/sampler_nif.c
create mode 100644 template/calfbox/sampler_prevoice.c
create mode 100644 template/calfbox/sampler_prg.c
create mode 100644 template/calfbox/sampler_prg.h
create mode 100644 template/calfbox/sampler_rll.c
create mode 100644 template/calfbox/sampler_voice.c
create mode 100644 template/calfbox/scene.c
create mode 100644 template/calfbox/scene.h
create mode 100644 template/calfbox/scripting.c
create mode 100644 template/calfbox/scripting.h
create mode 100644 template/calfbox/seq-adhoc.c
create mode 100644 template/calfbox/seq.c
create mode 100644 template/calfbox/seq.h
create mode 100755 template/calfbox/setup.py
create mode 100644 template/calfbox/sfzloader.c
create mode 100644 template/calfbox/sfzloader.h
create mode 100644 template/calfbox/sfzparser.c
create mode 100644 template/calfbox/sfzparser.h
create mode 100644 template/calfbox/skel.c
create mode 100644 template/calfbox/song.c
create mode 100644 template/calfbox/song.h
create mode 100644 template/calfbox/song_api_example.py
create mode 100644 template/calfbox/song_api_example2.py
create mode 100644 template/calfbox/stm.h
create mode 100644 template/calfbox/streamplay.c
create mode 100644 template/calfbox/streamrec.c
create mode 100644 template/calfbox/synthbass.sfz
create mode 100644 template/calfbox/tarfile.c
create mode 100644 template/calfbox/tarfile.h
create mode 100644 template/calfbox/test.py
create mode 100644 template/calfbox/tests.c
create mode 100644 template/calfbox/tests.h
create mode 100644 template/calfbox/tonectl.c
create mode 100644 template/calfbox/tonewheel.c
create mode 100644 template/calfbox/track.c
create mode 100644 template/calfbox/track.h
create mode 100644 template/calfbox/ui.c
create mode 100644 template/calfbox/ui.h
create mode 100644 template/calfbox/usb_api_example.py
create mode 100644 template/calfbox/usbaudio.c
create mode 100644 template/calfbox/usbio.c
create mode 100644 template/calfbox/usbio_impl.h
create mode 100644 template/calfbox/usbmidi.c
create mode 100644 template/calfbox/usbprobe.c
create mode 100644 template/calfbox/wavebank.c
create mode 100644 template/calfbox/wavebank.h
create mode 100644 template/configure.template
create mode 100644 template/documentation/LICENSE
create mode 100644 template/documentation/build.py
create mode 100644 template/documentation/css/normalize.css
create mode 100644 template/documentation/css/style.css
create mode 100644 template/documentation/english.adoc.template
create mode 100644 template/documentation/genlektor.py
create mode 100644 template/documentation/german.adoc.template
create mode 100644 template/documentation/index.adoc.template
create mode 100644 template/documentation/readme.template
create mode 100644 template/engine/__init__.py
create mode 100644 template/engine/api.py
create mode 100644 template/engine/data.py
create mode 100644 template/engine/duration.py
create mode 100644 template/engine/history.py
create mode 100644 template/engine/input_midi.py
create mode 100644 template/engine/ly2cbox.py
create mode 100644 template/engine/metronome.py
create mode 100644 template/engine/midi.py
create mode 100644 template/engine/pitch.py
create mode 100644 template/engine/resources/LICENSE
create mode 100644 template/engine/resources/metronome/metronome.sfz
create mode 100644 template/engine/resources/metronome/normal.wav
create mode 100644 template/engine/resources/metronome/stressed.wav
create mode 100644 template/engine/sampler_sf2.py
create mode 100644 template/engine/sequencer.py
create mode 100644 template/engine/session.py
create mode 100644 template/gitignore.template
create mode 100644 template/helper.py
create mode 100644 template/qtgui/__init__.py
create mode 100644 template/qtgui/about.py
create mode 100644 template/qtgui/chooseSessionDirectory.py
create mode 100644 template/qtgui/constantsAndConfigs.py
create mode 100644 template/qtgui/debugScript.py
create mode 100644 template/qtgui/designer/about.py
create mode 100644 template/qtgui/designer/about.ui
create mode 100644 template/qtgui/designer/chooseSessionDirectory.py
create mode 100644 template/qtgui/designer/chooseSessionDirectory.ui
create mode 100644 template/qtgui/designer/usermanual.py
create mode 100644 template/qtgui/designer/usermanual.ui
create mode 100755 template/qtgui/flowlayout.py
create mode 100644 template/qtgui/helper.py
create mode 100644 template/qtgui/mainwindow.py
create mode 100644 template/qtgui/menu.py
create mode 100644 template/qtgui/nsmclient.py
create mode 100644 template/qtgui/nsmsingleserver.py
create mode 100644 template/qtgui/resources.py
create mode 100644 template/qtgui/resources/buildresources.sh
create mode 100644 template/qtgui/resources/euterpe.license.txt
create mode 100644 template/qtgui/resources/euterpe.ttf
create mode 100644 template/qtgui/resources/icons/wholeNote.png
create mode 100644 template/qtgui/resources/resources.qrc
create mode 100644 template/qtgui/resources/translations/config.pro
create mode 100644 template/qtgui/resources/translations/de.qm
create mode 100644 template/qtgui/resources/translations/de.ts
create mode 100644 template/qtgui/resources/translations/update.sh
create mode 100644 template/qtgui/submenus.py
create mode 100644 template/qtgui/usermanual.py
create mode 100644 template/start.py
diff --git a/template/.gitignore b/template/.gitignore
new file mode 100644
index 0000000..efa8610
--- /dev/null
+++ b/template/.gitignore
@@ -0,0 +1,127 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# pyenv
+.python-version
+
+# celery beat schedule file
+celerybeat-schedule
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+
+
+#nuitka and makefile
+*.bin
+*.build
+Makefile
+site-packages
+calfbox/.deps
+calfbox/build
+calfbox/autom4te.cache
+calfbox/Makefile.in
+calfbox/aclocal.m4
+calfbox/compile
+calfbox/config.h
+calfbox/config.h.in
+calfbox/config.h.in~
+calfbox/config.status
+calfbox/configure
+calfbox/depcomp
+calfbox/install-sh
+calfbox/ltmain.sh
+calfbox/missing
+calfbox/stamp-h1
diff --git a/template/LICENSE b/template/LICENSE
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/template/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/template/Makefile.in b/template/Makefile.in
new file mode 100644
index 0000000..c1c2be3
--- /dev/null
+++ b/template/Makefile.in
@@ -0,0 +1,89 @@
+
+#Will be added by configure before this line:
+##PREFIX
+#PROGRAM
+#VERSION
+#This is a makefile and needs tabs, not spaces.
+
+.PHONY: install uninstall clean gitclean resources calfbox
+
+all: | calfbox
+ #Our Program
+ printf "prefix = \"$(PREFIX)\"" > compiledprefix.py
+ PYTHONPATH="site-packages" python3 -m nuitka --recurse-all --python-flag -O --warn-unusual-code --warn-implicit-exceptions --recurse-not-to=PyQt5 --show-progress --show-modules --file-reference-choice=runtime $(PROGRAM)
+ rm compiledprefix.py
+
+#A mode that just compiles calfbox locally so you can run the whole program standalone without nuitka
+calfbox:
+ mkdir -p site-packages
+ #First build the shared lib. Instead of running make install we create the lib ourselves directly
+ cd template/calfbox && echo $(shell pwd)
+ #cd template/calfbox && make && make install DESTDIR=$(shell pwd)/../../site-packages
+ cd template/calfbox && make
+ cp template/calfbox/.libs/libcalfbox.so.0.0.0 site-packages/"lib$(PROGRAM).so.$(VERSION)"
+ #We need to be in the directory, make uses subshells which will forget the work-dir in the next line. So here is a trick:
+ #cd template/calfbox && python3 setup.py build && python3 setup.py install --user --install-lib ../../site-packages
+ #The line above is too much for our specialized use-case. We just copy the few files we need manually.
+ mkdir -p site-packages/calfbox
+ cp template/calfbox/py/cbox.py site-packages/calfbox
+ cp template/calfbox/py/_cbox2.py site-packages/calfbox
+ cp template/calfbox/py/__init__.py site-packages/calfbox
+ cp template/calfbox/py/metadata.py site-packages/calfbox
+ cp template/calfbox/py/sfzparser.py site-packages/calfbox
+ cp template/calfbox/py/nullbox.py site-packages/calfbox
+
+clean:
+ cd template/calfbox && make distclean && rm -rf build
+
+ rm -rf site-packages
+ rm -f "$(PROGRAM).bin"
+ rm -rf "$(PROGRAM).build"
+ rm Makefile
+
+#Convenience function for developing, not used for the build or install process
+gitclean:
+ git clean -f -X -d
+
+#Convenience function for developing, not used for the build or install process
+resources:
+ cd template/documentation && python3 build.py
+ cd documentation && sh build-documentation.sh
+ cd qtgui/resources && sh buildresources.sh
+
+install:
+ install -D -m 755 $(PROGRAM).bin $(DESTDIR)$(PREFIX)/bin/$(PROGRAM)
+
+ install -D -m 644 documentation/out/* -t $(DESTDIR)$(PREFIX)/share/doc/$(PROGRAM)
+ install -D -m 644 README.md $(DESTDIR)$(PREFIX)/share/doc/$(PROGRAM)/README.md
+ install -D -m 644 LICENSE $(DESTDIR)$(PREFIX)/share/doc/$(PROGRAM)/LICENSE
+
+ install -D -m 644 desktop/desktop.desktop $(DESTDIR)$(PREFIX)/share/applications/$(PROGRAM).desktop
+
+ #Icons
+ for size in 16 32 64 128 256 512 ; do \
+ install -D -m 644 desktop/images/"$$size"x"$$size".png $(DESTDIR)$(PREFIX)/share/icons/hicolor/"$$size"x"$$size"/apps/$(PROGRAM).png ; \
+ done
+ install -D -m 644 desktop/images/256x256.png $(DESTDIR)$(PREFIX)/share/pixmaps/$(PROGRAM).png
+
+ install -D -m 755 site-packages/lib$(PROGRAM).so.$(VERSION) -t $(DESTDIR)$(PREFIX)/lib/$(PROGRAM)
+
+ mkdir -p $(DESTDIR)$(PREFIX)/share/$(PROGRAM)/
+ cp -r engine/resources/* $(DESTDIR)$(PREFIX)/share/$(PROGRAM)/
+ install -D -m 644 template/engine/resources/metronome/* -t $(DESTDIR)$(PREFIX)/share/$(PROGRAM)/template/metronome
+
+uninstall:
+ #Directories
+ rm -rf $(DESTDIR)$(PREFIX)/share/template/$(PROGRAM)
+ rm -rf $(DESTDIR)$(PREFIX)/share/doc/$(PROGRAM)
+ rm -rf $(DESTDIR)$(PREFIX)/share/$(PROGRAM)
+ rm -rf $(DESTDIR)$(PREFIX)/lib/$(PROGRAM)
+
+ #Files
+ rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGRAM)
+ rm -f $(DESTDIR)$(PREFIX)/share/applications/$(PROGRAM).desktop
+
+ #Icons
+ for size in 16 32 64 128 256 512 ; do \
+ rm -f $(DESTDIR)$(PREFIX)/share/icons/hicolor/"$$size"x"$$size"/apps/$(PROGRAM).png ; \
+ done
+ rm -f $(DESTDIR)$(PREFIX)/share/pixmaps/$(PROGRAM).png
diff --git a/template/README.md b/template/README.md
new file mode 100644
index 0000000..d0e5d79
--- /dev/null
+++ b/template/README.md
@@ -0,0 +1,34 @@
+# File Structure, Description and How to Update the Template
+
+The principle of this program is that it can be used in a self-contained "all in one directory" version
+but also in a compiled version with files all over the system, following the linux file hirarchy.
+
+For that reason that are some processes that must be done manually and which will create generated
+but static files (not at compile or runtime) that are included in git. For example the qt resources
+and translation or documentation html files from asciidoctor sources.
+
+
+## Copied files from 3rd party libs that need to be updated manually.
+* For Calfbox copy the whole source directory into template/calfbox and delete its .git
+* nsmclient.py from pynsm2 into qtgui.
+
+
+# Menu
+There is a menu in the example MainWindow but it is emtpy.
+Some default menu entries will be added dynamically by the template.
+
+You can merge template and client menus. But there are some naming conventions you must uphold:
+ menuFile
+ menuEdit
+ menuHelp
+ menuDebug
+
+These menuActions are standard and can only be hidden, deactivated or rerouted. But you can't create them on your own in QtDesigner
+ actionUndo
+ actionRedo
+ actionAbout
+ actionUser_Manual
+
+
+
+
diff --git a/template/__init__.py b/template/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/template/calfbox/.gitignore b/template/calfbox/.gitignore
new file mode 100644
index 0000000..36fd692
--- /dev/null
+++ b/template/calfbox/.gitignore
@@ -0,0 +1,31 @@
+aclocal.m4
+autom4te.cache
+config.h.in
+config.h.in~
+config.h
+configure
+compile
+depcomp
+install-sh
+ltmain.sh
+missing
+*.o
+*.lo
+.libs/
+.deps/
+libcalfbox.la
+libtool
+Makefile.in
+Makefile
+config.log
+config.status
+stamp-h1
+calfbox
+calfbox_tests
+
+__pycache__/
+*.py[cod]
+*$py.class
+build/
+config.guess
+config.sub
diff --git a/template/calfbox/API b/template/calfbox/API
new file mode 100644
index 0000000..07f9522
--- /dev/null
+++ b/template/calfbox/API
@@ -0,0 +1,133 @@
+@module >> @chorus, @phaser, ...
+
+@moduleslot/status() ->
+ /insert_engine(string engine),
+ /insert_preset(string preset),
+ /bypass(int bypassed)
+@moduleslot/insert_engine(string engine)
+@moduleslot/insert_preset(string engine)
+@moduleslot/engine/{add: @module}
+@moduleslot/set_bypass(int bypassed)
+
+@track/add_clip(int pos, int offset, int length, string pattern)
+
+/master/
+/master/status() -> /sample_rate, /tempo, /timesig, /playing, /pos, /pos_ppqn
+/master/tell() -> /playing, /pos, /pos_ppqn
+/master/set_tempo(float tempo)
+/master/set_timesig(int num, int denom)
+/master/play()
+/master/stop()
+/master/seek_samples(int samples)
+/master/seek_ppqn(int ppqn)
+
+/meter/
+/meter/get_peak() -> /peak(float left, float right)
+/meter/get_rms() -> /rms(float left, float right)
+
+/config/
+/config/sections([string prefix]) -> [/section(string name)]
+/config/keys(string section, string ?prefix) -> [/section(string name)]
+/config/get(string section, string key) -> /value(string value)
+/config/set(string section, string key, string value)
+/config/delete(string section, string key)
+/config/delete_section(string section)
+/config/save(string ?filename)
+
+/engine/
+/engine/status() -> /scene(object scene)
+/engine/render_stereo(int nframes)
+/engine/master_effect/{add: @moduleslot}
+/engine/new_scene() -> uuid
+/engine/new_recorder() -> uuid
+
+/scene/
+/scene/transpose(int semitones)
+/scene/clear()
+/scene/load(string scene_name)
+/scene/add_layer(int layer_pos, string layer_name)
+/scene/add_instrument_layer(int layer_pos, string instrument_name)
+/scene/delete_layer(int pos)
+/scene/move_layer(int oldpos, int newpos)
+/scene/instr/
+/scene/instr//status() ->
+ /engine(string name),
+ /aux_offset(int first_aux_output_no),
+ /outputs(int stereo_output_count)
+/scene/instr//output//status() ->
+ /gain_linear(float gain),
+ /gain(float gain_dB),
+ /output(int output_bus),
+ {add: @moduleslot/status()}
+/scene/instr//output//gain(float gain_dB),
+/scene/instr//output//output(int output_bus)
+/scene/instr//output//{add: @moduleslot}
+/scene/instr//aux//status() ->
+ /gain_linear(float gain),
+ /gain(float gain_dB),
+ /bus(string output_bus),
+ {add: @moduleslot/status()} XXXKF ????
+/scene/instr//aux//gain(float gain_dB)
+/scene/instr//aux//bus(string bus)
+/scene/instr//aux//{add: @moduleslot}
+/scene/layer//
+/scene/layer//status() ->
+ /enable(int),
+ /instrument_name(string iname),
+ /instrument_uuid(string uuid),
+ /consume(int consume),
+ /ignore_scene_transpose(int ignore),
+ /disable_aftertouch(int disable),
+ /transpose(int semitones),
+ /fixed_note(int note),
+ /low_note(int note),
+ /high_note(int note),
+ /in_channel(int channel),
+ /out_channel(int channel)
+/scene/layer//enable(int)
+/scene/layer//instrument_name(string iname)
+/scene/layer//consume(int consume)
+/scene/layer//ignore_scene_transpose(int ignore)
+/scene/layer//disable_aftertouch(int disable)
+/scene/layer//transpose(int semitones)
+/scene/layer//fixed_note(int note)
+/scene/layer//low_note(int note)
+/scene/layer//high_note(int note)
+/scene/layer//in_channel(int channel)
+/scene/layer//out_channel(int channel)
+/scene/aux//status
+/scene/aux//slot/{add: @module}
+/scene/load_aux(string name)
+/scene/delete_aux(string name)
+/scene/status() ->
+ /name(string),
+ /title(string),
+ /transpose(int semitones),
+ [/layer(string uuid)],
+ [/instrument(string instance, string engine)],
+ [/aux(string name, string uuid)]
+
+/rt/
+/rt/status() -> /audio_channels(int inputs, int outputs)
+/song/
+/song/status() -> [/track(int index, string name, int items)], [/pattern(int index, string name, int length)]
+/waves/
+/waves/status() -> /bytes(int bytes), /max_bytes(int max_bytes), /count(int count)
+/waves/list() -> [/waveform(int id)]
+/waves/info(int id) -> /filename(string), /name(string), /bytes(int)
+/on_idle() -> {any}
+/send_event_to(string output, int)
+/send_event_to(string output, int, int)
+/send_event_to(string output, int, int, int)
+/play_note(int ch, int note, int velocity) (plays a note with duration=1 on the next buffer)
+/play_drum_pattern(string pattern)
+/play_drum_track(string track)
+/play_blob(blob serialized_pattern, int length_ticks)
+/stop_pattern()
+/get_pattern() -> /pattern(blob serialized_pattern, int length_ticks)
+/print_s(string)
+/print_i(int)
+/print_f(float)
+/new_meter() -> /uuid
+/new_recorder(string filename) -> /uuid
+
diff --git a/template/calfbox/AUTHORS b/template/calfbox/AUTHORS
new file mode 100644
index 0000000..7c52993
--- /dev/null
+++ b/template/calfbox/AUTHORS
@@ -0,0 +1 @@
+Krzysztof Foltman
diff --git a/template/calfbox/COPYING b/template/calfbox/COPYING
new file mode 100644
index 0000000..94a9ed0
--- /dev/null
+++ b/template/calfbox/COPYING
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/template/calfbox/ChangeLog b/template/calfbox/ChangeLog
new file mode 100644
index 0000000..e69de29
diff --git a/template/calfbox/INSTALL b/template/calfbox/INSTALL
new file mode 100644
index 0000000..7d1c323
--- /dev/null
+++ b/template/calfbox/INSTALL
@@ -0,0 +1,365 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
+2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+are permitted in any medium without royalty provided the copyright
+notice and this notice are preserved. This file is offered as-is,
+without warranty of any kind.
+
+Basic Installation
+==================
+
+ Briefly, the shell commands `./configure; make; make install' should
+configure, build, and install this package. The following
+more-detailed instructions are generic; see the `README' file for
+instructions specific to this package. Some packages provide this
+`INSTALL' file but do not implement all of the features documented
+below. The lack of an optional feature in a given package is not
+necessarily a bug. More recommendations for GNU packages can be found
+in *note Makefile Conventions: (standards)Makefile Conventions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You need `configure.ac' if
+you want to change it or regenerate `configure' using a newer version
+of `autoconf'.
+
+ The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system.
+
+ Running `configure' might take a while. While running, it prints
+ some messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package, generally using the just-built uninstalled binaries.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation. When installing into a prefix owned by root, it is
+ recommended that the package be configured and built as a regular
+ user, and only the `make install' phase executed with root
+ privileges.
+
+ 5. Optionally, type `make installcheck' to repeat any self-tests, but
+ this time using the binaries in their final installed location.
+ This target does not install anything. Running this target as a
+ regular user, particularly if the prior `make install' required
+ root privileges, verifies that the installation completed
+ correctly.
+
+ 6. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+ 7. Often, you can also type `make uninstall' to remove the installed
+ files again. In practice, not all packages have tested that
+ uninstallation works correctly, even though it is required by the
+ GNU Coding Standards.
+
+ 8. Some packages, particularly those that use Automake, provide `make
+ distcheck', which can by used by developers to test that all other
+ targets like `make install' and `make uninstall' work correctly.
+ This target is generally not run by end users.
+
+Compilers and Options
+=====================
+
+ Some systems require unusual options for compilation or linking that
+the `configure' script does not know about. Run `./configure --help'
+for details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+ You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you can use GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'. This
+is known as a "VPATH" build.
+
+ With a non-GNU `make', it is safer to compile the package for one
+architecture at a time in the source code directory. After you have
+installed the package for one architecture, use `make distclean' before
+reconfiguring for another architecture.
+
+ On MacOS X 10.5 and later systems, you can create libraries and
+executables that work on multiple system types--known as "fat" or
+"universal" binaries--by specifying multiple `-arch' options to the
+compiler but only a single `-arch' option to the preprocessor. Like
+this:
+
+ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
+ CPP="gcc -E" CXXCPP="g++ -E"
+
+ This is not guaranteed to produce working output in all cases, you
+may have to build one architecture at a time and combine the results
+using the `lipo' tool if you have problems.
+
+Installation Names
+==================
+
+ By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX', where PREFIX must be an
+absolute file name.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them. In general, the
+default for these options is expressed in terms of `${prefix}', so that
+specifying just `--prefix' will affect all of the other directory
+specifications that were not explicitly provided.
+
+ The most portable way to affect installation locations is to pass the
+correct locations to `configure'; however, many packages provide one or
+both of the following shortcuts of passing variable assignments to the
+`make install' command line to change installation locations without
+having to reconfigure or recompile.
+
+ The first method involves providing an override variable for each
+affected directory. For example, `make install
+prefix=/alternate/directory' will choose an alternate location for all
+directory configuration variables that were expressed in terms of
+`${prefix}'. Any directories that were specified during `configure',
+but not in terms of `${prefix}', must each be overridden at install
+time for the entire installation to be relocated. The approach of
+makefile variable overrides for each directory variable is required by
+the GNU Coding Standards, and ideally causes no recompilation.
+However, some platforms have known limitations with the semantics of
+shared libraries that end up requiring recompilation when using this
+method, particularly noticeable in packages that use GNU Libtool.
+
+ The second method involves providing the `DESTDIR' variable. For
+example, `make install DESTDIR=/alternate/directory' will prepend
+`/alternate/directory' before all installation names. The approach of
+`DESTDIR' overrides is not required by the GNU Coding Standards, and
+does not work on platforms that have drive letters. On the other hand,
+it does better at avoiding recompilation issues, and works well even
+when some directory options were not specified in terms of `${prefix}'
+at `configure' time.
+
+Optional Features
+=================
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+ Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+ Some packages offer the ability to configure how verbose the
+execution of `make' will be. For these packages, running `./configure
+--enable-silent-rules' sets the default to minimal output, which can be
+overridden with `make V=1'; while running `./configure
+--disable-silent-rules' sets the default to verbose, which can be
+overridden with `make V=0'.
+
+Particular systems
+==================
+
+ On HP-UX, the default C compiler is not ANSI C compatible. If GNU
+CC is not installed, it is recommended to use the following options in
+order to use an ANSI C compiler:
+
+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
+
+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
+
+ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
+parse its `' header file. The option `-nodtk' can be used as
+a workaround. If GNU CC is not installed, it is therefore recommended
+to try
+
+ ./configure CC="cc"
+
+and if that doesn't work, try
+
+ ./configure CC="cc -nodtk"
+
+ On Solaris, don't put `/usr/ucb' early in your `PATH'. This
+directory contains several dysfunctional programs; working variants of
+these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
+in your `PATH', put it _after_ `/usr/bin'.
+
+ On Haiku, software installed for all users goes in `/boot/common',
+not `/usr/local'. It is recommended to use the following options:
+
+ ./configure --prefix=/boot/common
+
+Specifying the System Type
+==========================
+
+ There may be some features `configure' cannot figure out
+automatically, but needs to determine by the type of machine the package
+will run on. Usually, assuming the package is built to be run on the
+_same_ architectures, `configure' can figure that out, but if it prints
+a message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS
+ KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+ If you want to set default values for `configure' scripts to share,
+you can create a site shell script called `config.site' that gives
+default values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+ Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script).
+
+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
+an Autoconf bug. Until the bug is fixed you can use this workaround:
+
+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+`configure' Invocation
+======================
+
+ `configure' recognizes the following options to control how it
+operates.
+
+`--help'
+`-h'
+ Print a summary of all of the options to `configure', and exit.
+
+`--help=short'
+`--help=recursive'
+ Print a summary of the options unique to this package's
+ `configure', and exit. The `short' variant lists options used
+ only in the top level, while the `recursive' variant lists options
+ also present in any nested packages.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`--prefix=DIR'
+ Use DIR as the installation prefix. *note Installation Names::
+ for more details, including other options available for fine-tuning
+ the installation locations.
+
+`--no-create'
+`-n'
+ Run the configure checks, but stop before creating any output
+ files.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/template/calfbox/Makefile.am b/template/calfbox/Makefile.am
new file mode 100644
index 0000000..32dcce2
--- /dev/null
+++ b/template/calfbox/Makefile.am
@@ -0,0 +1,152 @@
+AM_CPPFLAGS = -I$(srcdir) -Wall -Wsign-compare -D_GNU_SOURCE
+
+AM_CFLAGS = $(JACK_DEPS_CFLAGS) $(GLIB_DEPS_CFLAGS) $(FLUIDSYNTH_DEPS_CFLAGS) $(PYTHON_DEPS_CFLAGS) $(LIBSMF_DEPS_CFLAGS) $(LIBSNDFILE_DEPS_CFLAGS) $(LIBUSB_DEPS_CFLAGS) $(ARCH_OPT_CFLAGS) $(NCURSES_DEPS_CFLAGS)
+
+lib_LTLIBRARIES = libcalfbox.la
+
+bin_PROGRAMS = calfbox
+noinst_PROGRAMS = calfbox_tests
+
+calfbox_SOURCES = \
+ appmenu.c \
+ main.c \
+ menu.c \
+ menuitem.c \
+ ui.c
+
+calfbox_LDADD = libcalfbox.la $(JACK_DEPS_LIBS) $(GLIB_DEPS_LIBS) $(FLUIDSYNTH_DEPS_LIBS) $(PYTHON_DEPS_LIBS) $(LIBSMF_DEPS_LIBS) $(LIBSNDFILE_DEPS_LIBS) $(LIBUSB_DEPS_LIBS) $(NCURSES_DEPS_LIBS) -lpthread -luuid -lm -lrt
+
+calfbox_tests_SOURCES = \
+ tests.c
+
+calfbox_tests_LDADD = libcalfbox.la $(GLIB_DEPS_LIBS) -lpthread -lm -lrt
+
+libcalfbox_la_SOURCES = \
+ app.c \
+ auxbus.c \
+ blob.c \
+ chorus.c \
+ cmd.c \
+ compressor.c \
+ config-api.c \
+ delay.c \
+ distortion.c \
+ dom.c \
+ engine.c \
+ eq.c \
+ errors.c \
+ fbr.c \
+ fifo.c \
+ fluid.c \
+ fuzz.c \
+ fxchain.c \
+ gate.c \
+ hwcfg.c \
+ instr.c \
+ io.c \
+ jackinput.c \
+ jackio.c \
+ layer.c \
+ limiter.c \
+ master.c \
+ meter.c \
+ midi.c \
+ mididest.c \
+ module.c \
+ pattern.c \
+ pattern-maker.c \
+ phaser.c \
+ prefetch_pipe.c \
+ recsrc.c \
+ reverb.c \
+ rt.c \
+ sampler.c \
+ sampler_channel.c \
+ sampler_gen.c \
+ sampler_layer.c \
+ sampler_nif.c \
+ sampler_prevoice.c \
+ sampler_prg.c \
+ sampler_rll.c \
+ sampler_voice.c \
+ scene.c \
+ scripting.c \
+ seq.c \
+ seq-adhoc.c \
+ sfzloader.c \
+ sfzparser.c \
+ song.c \
+ streamplay.c \
+ streamrec.c \
+ tarfile.c \
+ tonectl.c \
+ tonewheel.c \
+ track.c \
+ usbaudio.c \
+ usbio.c \
+ usbmidi.c \
+ usbprobe.c \
+ wavebank.c
+
+libcalfbox_la_LIBADD = $(JACK_DEPS_LIBS) $(GLIB_DEPS_LIBS) $(FLUIDSYNTH_DEPS_LIBS) $(PYTHON_DEPS_LIBS) $(LIBSMF_DEPS_LIBS) $(LIBSNDFILE_DEPS_LIBS) $(LIBUSB_DEPS_LIBS) -lpthread -luuid -lm -lrt
+
+if USE_SSE
+ARCH_OPT_CFLAGS=-msse -ffast-math
+else
+if USE_NEON
+ARCH_OPT_CFLAGS=-mfloat-abi=hard -mfpu=neon -ffast-math
+endif
+endif
+
+noinst_HEADERS = \
+ app.h \
+ auxbus.h \
+ biquad-float.h \
+ blob.h \
+ cmd.h \
+ config-api.h \
+ dom.h \
+ dspmath.h \
+ envelope.h \
+ engine.h \
+ eq.h \
+ errors.h \
+ fifo.h \
+ hwcfg.h \
+ instr.h \
+ io.h \
+ ioenv.h \
+ layer.h \
+ master.h \
+ menu.h \
+ menuitem.h \
+ meter.h \
+ midi.h \
+ mididest.h \
+ module.h \
+ onepole-int.h \
+ onepole-float.h \
+ pattern.h \
+ pattern-maker.h \
+ prefetch_pipe.h \
+ recsrc.h \
+ rt.h \
+ sampler.h \
+ sampler_impl.h \
+ sampler_layer.h \
+ sampler_prg.h \
+ scene.h \
+ scripting.h \
+ seq.h \
+ sfzloader.h \
+ sfzparser.h \
+ song.h \
+ stm.h \
+ tarfile.h \
+ tests.h \
+ track.h \
+ ui.h \
+ usbio_impl.h \
+ wavebank.h
+
+EXTRA_DIST = cboxrc-example
diff --git a/template/calfbox/NEWS b/template/calfbox/NEWS
new file mode 100644
index 0000000..e69de29
diff --git a/template/calfbox/README b/template/calfbox/README
new file mode 120000
index 0000000..42061c0
--- /dev/null
+++ b/template/calfbox/README
@@ -0,0 +1 @@
+README.md
\ No newline at end of file
diff --git a/template/calfbox/README.md b/template/calfbox/README.md
new file mode 100644
index 0000000..de03056
--- /dev/null
+++ b/template/calfbox/README.md
@@ -0,0 +1,63 @@
+# Calfbox
+
+Website: https://github.com/kfoltman/calfbox
+
+Calfbox, the "open source musical instrument", offers assorted music-related code.
+
+Originally intended as a standalone instrument for Linux and embedded devices (USB TV Sticks)
+it can be used as Python module as well.
+
+# Packaging
+If you are a packager and want to create a binary package for your distribution please package only the python module.
+The binary executable is not maintained and untested at the moment. It should not be used by anyone.
+
+
+# Calfbox as Python Module
+Calfbox can be used as a Python module that can be imported to create short scripts or
+full fledged programs ( https://www.laborejo.org/software ).
+
+Most notably it features a midi sequencer and an audio sampler (for sfz files and sf2 via fluidsynth).
+
+## Building
+
+A convenience script `cleanpythonbuild.py` has been supplied to quickly build and install the cbox python module.
+
+```
+make clean
+rm build -rf
+sh autogen.sh
+./configure
+make
+python3 setup.py build
+sudo python3 setup.py install
+```
+
+## How to write programs with cbox
+You can find several `.py` files in the main directory, such as `sampler_api_example.py` or
+`song_api_example.py`.
+
+Also there is a directory `/experiments` which contains a small example framework.
+
+
+# Using Calfbox as standalone instrument
+
+Using Calfbox as standalone instrument requires a .cfg config file.
+
+This part of the program is currently unmaintained and untested. Please do not use it.
+
+# License
+
+This code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+
+For the full license see the file COPYING
diff --git a/template/calfbox/adhoc_example.py b/template/calfbox/adhoc_example.py
new file mode 100644
index 0000000..04a96ff
--- /dev/null
+++ b/template/calfbox/adhoc_example.py
@@ -0,0 +1,47 @@
+import os
+import sys
+import struct
+import time
+import unittest
+
+sys.path = ["./py"] + sys.path
+
+import cbox
+
+global Document
+global Transport
+Document = cbox.Document
+Transport = cbox.Transport
+
+song = Document.get_song()
+
+# Delete all the tracks and patterns
+song.clear()
+
+# Create a binary blob that contains the MIDI events
+pblob = bytes()
+for noteindex in range(20):
+ # note on
+ pblob += cbox.Pattern.serialize_event(noteindex * 12, 0x90, 36+noteindex*3, 127)
+ # note off
+ pblob += cbox.Pattern.serialize_event(noteindex * 12 + 11, 0x90, 36+noteindex*3, 0)
+
+# This will be the length of the pattern (in pulses). It should be large enough
+# to fit all the events
+pattern_len = 10 * 24 * 2
+
+# Create a new pattern object using events from the blob
+pattern = song.pattern_from_blob(pblob, pattern_len)
+
+retrig = 10
+i = 0
+while i < 50:
+ i += 1
+ retrig -= 1
+ if retrig <= 0:
+ print ("Triggering adhoc pattern with ID 1")
+ Document.get_scene().play_pattern(pattern, 240, 0)
+ retrig = 5
+ # Query JACK ports, new USB devices etc.
+ cbox.call_on_idle()
+ time.sleep(0.1)
diff --git a/template/calfbox/app.c b/template/calfbox/app.c
new file mode 100644
index 0000000..cf778a0
--- /dev/null
+++ b/template/calfbox/app.c
@@ -0,0 +1,391 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2013 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "app.h"
+#include "blob.h"
+#include "config-api.h"
+#include "engine.h"
+#include "instr.h"
+#include "io.h"
+#include "layer.h"
+#include "menu.h"
+#include "menuitem.h"
+#include "meter.h"
+#include "midi.h"
+#include "module.h"
+#include "scene.h"
+#include "seq.h"
+#include "song.h"
+#include "track.h"
+#include "ui.h"
+#include "wavebank.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static gboolean lookup_midi_merger(const char *output, struct cbox_midi_merger **pmerger, GError **error)
+{
+ if (*output)
+ {
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, output, error))
+ return FALSE;
+
+ *pmerger = cbox_rt_get_midi_output(app.rt, &uuid);
+ if (!*pmerger)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown MIDI output UUID: '%s'", output);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (!app.engine->scene_count)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Scene not set");
+ return FALSE;
+ }
+ *pmerger = &app.engine->scenes[0]->scene_input_merger;
+ }
+ return TRUE;
+}
+
+static gboolean app_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ if (!cmd->command)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "NULL command");
+ return FALSE;
+ }
+ if (cmd->command[0] != '/')
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid global command path '%s'", cmd->command);
+ return FALSE;
+ }
+ const char *obj = &cmd->command[1];
+ const char *pos = strchr(obj, '/');
+ if (pos)
+ {
+ if (!strncmp(obj, "master/", 7))
+ return cbox_execute_sub(&app.engine->master->cmd_target, fb, cmd, pos, error);
+ else
+ if (!strncmp(obj, "config/", 7))
+ return cbox_execute_sub(&app.config_cmd_target, fb, cmd, pos, error);
+ else
+ if (!strncmp(obj, "scene/", 6))
+ return cbox_execute_sub(&app.engine->scenes[0]->cmd_target, fb, cmd, pos, error);
+ else
+ if (!strncmp(obj, "engine/", 7))
+ return cbox_execute_sub(&app.engine->cmd_target, fb, cmd, pos, error);
+ else
+ if (!strncmp(obj, "rt/", 3))
+ return cbox_execute_sub(&app.rt->cmd_target, fb, cmd, pos, error);
+ else
+ if (!strncmp(obj, "io/", 3))
+ return cbox_execute_sub(&app.io.cmd_target, fb, cmd, pos, error);
+ else
+ if (!strncmp(obj, "song/", 5) && app.engine->master->song)
+ return cbox_execute_sub(&app.engine->master->song->cmd_target, fb, cmd, pos, error);
+ else
+ if (!strncmp(obj, "waves/", 6))
+ return cbox_execute_sub(&cbox_waves_cmd_target, fb, cmd, pos, error);
+ else
+ if (!strncmp(obj, "doc/", 4))
+ return cbox_execute_sub(cbox_document_get_cmd_target(app.document), fb, cmd, pos, error);
+ else
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types);
+ return FALSE;
+ }
+ }
+ else
+ if (!strcmp(obj, "on_idle") && !strcmp(cmd->arg_types, ""))
+ {
+ return cbox_app_on_idle(fb, error);
+ }
+ else
+ if (!strcmp(obj, "send_event_to") && (!strcmp(cmd->arg_types, "siii") || !strcmp(cmd->arg_types, "sii") || !strcmp(cmd->arg_types, "si")))
+ {
+ const char *output = CBOX_ARG_S(cmd, 0);
+ struct cbox_midi_merger *merger = NULL;
+ if (!lookup_midi_merger(output, &merger, error))
+ return FALSE;
+ int mcmd = CBOX_ARG_I(cmd, 1);
+ int arg1 = 0, arg2 = 0;
+ if (cmd->arg_types[2] == 'i')
+ {
+ arg1 = CBOX_ARG_I(cmd, 2);
+ if (cmd->arg_types[3] == 'i')
+ arg2 = CBOX_ARG_I(cmd, 3);
+ }
+ struct cbox_midi_buffer buf;
+ cbox_midi_buffer_init(&buf);
+ cbox_midi_buffer_write_inline(&buf, 0, mcmd, arg1, arg2);
+ cbox_engine_send_events_to(app.engine, merger, &buf);
+ return TRUE;
+ }
+ else
+ if (!strcmp(obj, "send_sysex_to") && !strcmp(cmd->arg_types, "sb"))
+ {
+ const char *output = CBOX_ARG_S(cmd, 0);
+ struct cbox_midi_merger *merger = NULL;
+ if (!lookup_midi_merger(output, &merger, error))
+ return FALSE;
+ const struct cbox_blob *blob = CBOX_ARG_B(cmd, 1);
+ struct cbox_midi_buffer buf;
+ cbox_midi_buffer_init(&buf);
+ cbox_midi_buffer_write_event(&buf, 0, blob->data, blob->size);
+ cbox_engine_send_events_to(app.engine, merger, &buf);
+ return TRUE;
+ }
+ else
+ if (!strcmp(obj, "update_playback") && !strcmp(cmd->arg_types, ""))
+ {
+ cbox_engine_update_song_playback(app.engine);
+ return TRUE;
+ }
+ else
+ if (!strcmp(obj, "get_pattern") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ if (app.engine->master->song && app.engine->master->song->tracks)
+ {
+ struct cbox_track *track = app.engine->master->song->tracks->data;
+ if (track)
+ {
+ struct cbox_track_item *item = track->items->data;
+ struct cbox_midi_pattern *pattern = item->pattern;
+ int length = 0;
+ struct cbox_blob *blob = cbox_midi_pattern_to_blob(pattern, &length);
+ gboolean res = cbox_execute_on(fb, NULL, "/pattern", "bi", error, blob, length);
+ cbox_blob_destroy(blob);
+ if (!res)
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ else
+ if (!strcmp(obj, "new_meter") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ struct cbox_meter *meter = cbox_meter_new(app.document, app.rt->io_env.srate);
+
+ return cbox_execute_on(fb, NULL, "/uuid", "o", error, meter);
+ }
+ else
+ if (!strcmp(obj, "new_engine") && !strcmp(cmd->arg_types, "ii"))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ struct cbox_engine *e = cbox_engine_new(app.document, NULL);
+ e->io_env.srate = CBOX_ARG_I(cmd, 0);
+ e->io_env.buffer_size = CBOX_ARG_I(cmd, 1);
+
+ return e ? cbox_execute_on(fb, NULL, "/uuid", "o", error, e) : FALSE;
+ }
+ else
+ if (!strcmp(obj, "print_s") && !strcmp(cmd->arg_types, "s"))
+ {
+ g_message("Print: %s", CBOX_ARG_S(cmd, 0));
+ return TRUE;
+ }
+ else
+ if (!strcmp(obj, "print_i") && !strcmp(cmd->arg_types, "i"))
+ {
+ g_message("Print: %d", CBOX_ARG_I(cmd, 0));
+ return TRUE;
+ }
+ else
+ if (!strcmp(obj, "print_f") && !strcmp(cmd->arg_types, "f"))
+ {
+ g_message("Print: %f", CBOX_ARG_F(cmd, 0));
+ return TRUE;
+ }
+ else
+ if (!strcmp(obj, "print_b") && !strcmp(cmd->arg_types, "b"))
+ {
+ g_message("Print: %s", (char *)CBOX_ARG_B(cmd, 0)->data);
+ return TRUE;
+ }
+ else
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types);
+ return FALSE;
+ }
+}
+
+struct config_foreach_data
+{
+ const char *prefix;
+ const char *command;
+ struct cbox_command_target *fb;
+ GError **error;
+ gboolean success;
+};
+
+void api_config_cb(void *user_data, const char *key)
+{
+ struct config_foreach_data *cfd = user_data;
+ if (!cfd->success)
+ return;
+ if (cfd->prefix && strncmp(cfd->prefix, key, strlen(cfd->prefix)))
+ return;
+
+ if (!cbox_execute_on(cfd->fb, NULL, cfd->command, "s", cfd->error, key))
+ {
+ cfd->success = FALSE;
+ return;
+ }
+}
+
+static gboolean config_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ if (!strcmp(cmd->command, "/sections") && (!strcmp(cmd->arg_types, "") || !strcmp(cmd->arg_types, "s")))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ struct config_foreach_data cfd = {cmd->arg_types[0] == 's' ? CBOX_ARG_S(cmd, 0) : NULL, "/section", fb, error, TRUE};
+ cbox_config_foreach_section(api_config_cb, &cfd);
+ return cfd.success;
+ }
+ else if (!strcmp(cmd->command, "/keys") && (!strcmp(cmd->arg_types, "s") || !strcmp(cmd->arg_types, "ss")))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ struct config_foreach_data cfd = {cmd->arg_types[1] == 's' ? CBOX_ARG_S(cmd, 1) : NULL, "/key", fb, error, TRUE};
+ cbox_config_foreach_key(api_config_cb, CBOX_ARG_S(cmd, 0), &cfd);
+ return cfd.success;
+ }
+ else if (!strcmp(cmd->command, "/get") && !strcmp(cmd->arg_types, "ss"))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ const char *value = cbox_config_get_string(CBOX_ARG_S(cmd, 0), CBOX_ARG_S(cmd, 1));
+ if (!value)
+ return TRUE;
+ return cbox_execute_on(fb, NULL, "/value", "s", error, value);
+ }
+ else if (!strcmp(cmd->command, "/set") && !strcmp(cmd->arg_types, "sss"))
+ {
+ cbox_config_set_string(CBOX_ARG_S(cmd, 0), CBOX_ARG_S(cmd, 1), CBOX_ARG_S(cmd, 2));
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/delete") && !strcmp(cmd->arg_types, "ss"))
+ {
+ cbox_config_remove_key(CBOX_ARG_S(cmd, 0), CBOX_ARG_S(cmd, 1));
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/delete_section") && !strcmp(cmd->arg_types, "s"))
+ {
+ cbox_config_remove_section(CBOX_ARG_S(cmd, 0));
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/save") && !strcmp(cmd->arg_types, ""))
+ {
+ return cbox_config_save(NULL, error);
+ }
+ else if (!strcmp(cmd->command, "/save") && !strcmp(cmd->arg_types, "s"))
+ {
+ return cbox_config_save(CBOX_ARG_S(cmd, 0), error);
+ }
+ else
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types);
+ return FALSE;
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+gboolean cbox_app_on_idle(struct cbox_command_target *fb, GError **error)
+{
+ if (app.rt->io)
+ {
+ GError *error2 = NULL;
+ if (cbox_io_get_disconnect_status(&app.io, &error2))
+ cbox_io_poll_ports(&app.io, fb);
+ else
+ {
+ if (error2)
+ g_error_free(error2);
+ int auto_reconnect = cbox_config_get_int("io", "auto_reconnect", 0);
+ if (auto_reconnect > 0)
+ {
+ sleep(auto_reconnect);
+ GError *error2 = NULL;
+ if (!cbox_io_cycle(&app.io, fb, &error2))
+ {
+ gboolean suppress = FALSE;
+ if (fb)
+ suppress = cbox_execute_on(fb, NULL, "/io/cycle_failed", "s", NULL, error2->message);
+ if (!suppress)
+ g_warning("Cannot cycle the I/O: %s", (error2 && error2->message) ? error2->message : "Unknown error");
+ g_error_free(error2);
+ }
+ else
+ {
+ if (fb)
+ cbox_execute_on(fb, NULL, "/io/cycled", "", NULL);
+ }
+ }
+ }
+ }
+ if (app.rt)
+ {
+ // Process results of asynchronous commands
+ cbox_rt_handle_cmd_queue(app.rt);
+
+ if (!cbox_midi_appsink_send_to(&app.engine->appsink, fb, error))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+struct cbox_app app =
+{
+ .rt = NULL,
+ .current_scene_name = NULL,
+ .cmd_target =
+ {
+ .process_cmd = app_process_cmd,
+ .user_data = &app
+ },
+ .config_cmd_target =
+ {
+ .process_cmd = config_process_cmd,
+ .user_data = &app
+ },
+};
+
diff --git a/template/calfbox/app.h b/template/calfbox/app.h
new file mode 100644
index 0000000..df20506
--- /dev/null
+++ b/template/calfbox/app.h
@@ -0,0 +1,51 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_APP_H
+#define CBOX_APP_H
+
+#include "cmd.h"
+#include "dom.h"
+#include "io.h"
+#include "rt.h"
+#include
+
+struct cbox_song;
+struct cbox_tarpool;
+
+struct cbox_app
+{
+ struct cbox_io io;
+ struct cbox_document *document;
+ struct cbox_rt *rt;
+ struct cbox_engine *engine;
+ struct cbox_command_target cmd_target;
+ struct cbox_command_target config_cmd_target;
+ gchar *current_scene_name;
+ struct cbox_tarpool *tarpool;
+};
+
+struct cbox_menu;
+
+extern struct cbox_app app;
+
+struct cbox_menu *create_main_menu(void);
+
+extern gboolean cbox_app_on_idle(struct cbox_command_target *fb, GError **error);
+
+#endif
diff --git a/template/calfbox/appmenu.c b/template/calfbox/appmenu.c
new file mode 100644
index 0000000..aa2053a
--- /dev/null
+++ b/template/calfbox/appmenu.c
@@ -0,0 +1,466 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2013 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "app.h"
+#include "blob.h"
+#include "config-api.h"
+#include "engine.h"
+#include "instr.h"
+#include "io.h"
+#include "layer.h"
+#include "menu.h"
+#include "menuitem.h"
+#include "meter.h"
+#include "midi.h"
+#include "module.h"
+#include "scene.h"
+#include "seq.h"
+#include "song.h"
+#include "track.h"
+#include "ui.h"
+#include "wavebank.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if USE_NCURSES
+
+int cmd_quit(struct cbox_menu_item_command *item, void *context)
+{
+ return 1;
+}
+
+static void set_current_scene_name(gchar *name)
+{
+ if (app.current_scene_name)
+ g_free(app.current_scene_name);
+ app.current_scene_name = name;
+}
+
+int cmd_load_scene(struct cbox_menu_item_command *item, void *context)
+{
+ GError *error = NULL;
+ struct cbox_scene *scene = app.engine->scenes[0];
+ cbox_scene_clear(scene);
+ if (!cbox_scene_load(scene, item->item.item_context, &error))
+ cbox_print_error(error);
+ set_current_scene_name(g_strdup_printf("scene:%s", (const char *)item->item.item_context));
+ return 0;
+}
+
+int cmd_load_instrument(struct cbox_menu_item_command *item, void *context)
+{
+ GError *error = NULL;
+ struct cbox_scene *scene = app.engine->scenes[0];
+ cbox_scene_clear(scene);
+ struct cbox_layer *layer = cbox_layer_new_with_instrument(scene, (char *)item->item.item_context, &error);
+
+ if (layer)
+ {
+ if (!cbox_scene_add_layer(scene, layer, &error))
+ cbox_print_error(error);
+ set_current_scene_name(g_strdup_printf("instrument:%s", (const char *)item->item.item_context));
+ }
+ else
+ {
+ cbox_print_error(error);
+ }
+ return 0;
+}
+
+int cmd_load_layer(struct cbox_menu_item_command *item, void *context)
+{
+ GError *error = NULL;
+ struct cbox_scene *scene = app.engine->scenes[0];
+ cbox_scene_clear(scene);
+ struct cbox_layer *layer = cbox_layer_new_from_config(scene, (char *)item->item.item_context, &error);
+
+ if (layer)
+ {
+ if (!cbox_scene_add_layer(scene, layer, &error))
+ cbox_print_error(error);
+ set_current_scene_name(g_strdup_printf("layer:%s", (const char *)item->item.item_context));
+ }
+ else
+ {
+ cbox_print_error(error);
+ CBOX_DELETE(scene);
+ }
+ return 0;
+}
+
+gchar *scene_format_value(const struct cbox_menu_item_static *item, void *context)
+{
+ if (app.current_scene_name)
+ return g_strdup(app.current_scene_name);
+ else
+ return g_strdup("- No scene -");
+}
+
+gchar *transport_format_value(const struct cbox_menu_item_static *item, void *context)
+{
+ // XXXKF
+ // struct cbox_bbt bbt;
+ // cbox_master_to_bbt(app.engine->master, &bbt);
+ if (app.engine->master->spb == NULL)
+ return g_strdup("N/A");
+ if (!strcmp((const char *)item->item.item_context, "pos"))
+ return g_strdup_printf("%d", (int)app.engine->master->spb->song_pos_samples);
+ else
+ return g_strdup_printf("%d", (int)app.engine->master->spb->song_pos_ppqn);
+}
+
+struct cbox_config_section_cb_data
+{
+ struct cbox_menu *menu;
+ cbox_menu_item_execute_func func;
+ const char *prefix;
+};
+
+static void config_key_process(void *user_data, const char *key)
+{
+ struct cbox_config_section_cb_data *data = user_data;
+
+ if (!strncmp(key, data->prefix, strlen(data->prefix)))
+ {
+ char *title = cbox_config_get_string(key, "title");
+ if (title)
+ cbox_menu_add_item(data->menu, cbox_menu_item_new_command(title, data->func, strdup(key + strlen(data->prefix)), mif_dup_label | mif_free_context));
+ else
+ cbox_menu_add_item(data->menu, cbox_menu_item_new_command(key, data->func, strdup(key + strlen(data->prefix)), mif_dup_label | mif_free_context));
+ }
+}
+
+struct cbox_menu *create_scene_menu(struct cbox_menu_item_menu *item, void *menu_context)
+{
+ struct cbox_menu *scene_menu = cbox_menu_new();
+ struct cbox_config_section_cb_data cb = { .menu = scene_menu };
+
+ cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Scenes", NULL, NULL, 0));
+ cb.prefix = "scene:";
+ cb.func = cmd_load_scene;
+ cbox_config_foreach_section(config_key_process, &cb);
+ cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Layers", NULL, NULL, 0));
+ cb.prefix = "layer:";
+ cb.func = cmd_load_layer;
+ cbox_config_foreach_section(config_key_process, &cb);
+ cbox_menu_add_item(scene_menu, cbox_menu_item_new_static("Instruments", NULL, NULL, 0));
+ cb.prefix = "instrument:";
+ cb.func = cmd_load_instrument;
+ cbox_config_foreach_section(config_key_process, &cb);
+
+ cbox_menu_add_item(scene_menu, cbox_menu_item_new_ok());
+ return scene_menu;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static struct cbox_command_target *find_module_target(const struct cbox_menu_item_command *item)
+{
+ struct cbox_instrument *instr = item->item.item_context;
+ return &instr->module->cmd_target;
+
+}
+
+int cmd_stream_rewind(struct cbox_menu_item_command *item, void *context)
+{
+ GError *error = NULL;
+ struct cbox_command_target *target = find_module_target(item);
+ if (target)
+ cbox_execute_on(target, NULL, "/seek", "i", &error, 0);
+ cbox_print_error_if(error);
+ return 0;
+}
+
+int cmd_stream_play(struct cbox_menu_item_command *item, void *context)
+{
+ GError *error = NULL;
+ struct cbox_command_target *target = find_module_target(item);
+ if (target)
+ cbox_execute_on(target, NULL, "/play", "", &error);
+ cbox_print_error_if(error);
+ return 0;
+}
+
+int cmd_stream_stop(struct cbox_menu_item_command *item, void *context)
+{
+ GError *error = NULL;
+ struct cbox_command_target *target = find_module_target(item);
+ if (target)
+ cbox_execute_on(target, NULL, "/stop", "", &error);
+ cbox_print_error_if(error);
+ return 0;
+}
+
+int cmd_stream_unload(struct cbox_menu_item_command *item, void *context)
+{
+ GError *error = NULL;
+ struct cbox_command_target *target = find_module_target(item);
+ if (target)
+ cbox_execute_on(target, NULL, "/unload", "", &error);
+ cbox_print_error_if(error);
+ return 0;
+}
+
+struct stream_response_data
+{
+ gchar *filename;
+ uint32_t pos, length, sample_rate, channels;
+};
+
+gboolean result_parser_status(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct stream_response_data *res = ct->user_data;
+ if (!strcmp(cmd->command, "/filename"))
+ res->filename = g_strdup(cmd->arg_values[0]);
+ if (!strcmp(cmd->command, "/pos"))
+ res->pos = *((uint32_t **)cmd->arg_values)[0];
+ if (!strcmp(cmd->command, "/length"))
+ res->length = *((uint32_t **)cmd->arg_values)[0];
+ if (!strcmp(cmd->command, "/sample_rate"))
+ res->sample_rate = *((uint32_t **)cmd->arg_values)[0];
+ if (!strcmp(cmd->command, "/channels"))
+ res->channels = *((uint32_t **)cmd->arg_values)[0];
+ //cbox_osc_command_dump(cmd);
+ return TRUE;
+}
+
+char *cmd_stream_status(const struct cbox_menu_item_static *item, void *context)
+{
+ struct stream_response_data data = { NULL, 0, 0, 0, 0 };
+ struct cbox_command_target response = { &data, result_parser_status };
+ GError *error = NULL;
+ struct cbox_command_target *target = find_module_target((struct cbox_menu_item_command *)item);
+ if (target)
+ cbox_execute_on(target, &response, "/status", "", &error);
+ cbox_print_error_if(error);
+ gchar *res = NULL;
+ if (data.filename && data.length && data.sample_rate)
+ {
+ double duration = data.length * 1.0 / data.sample_rate;
+ res = g_strdup_printf("%s (%um%0.2fs, %uch, %uHz) (%0.2f%%)", data.filename, (unsigned)floor(duration / 60), duration - 60 * floor(duration / 60), (unsigned)data.channels, (unsigned)data.sample_rate, data.pos * 100.0 / data.length);
+ }
+ else
+ res = g_strdup("-");
+ g_free(data.filename);
+ return res;
+}
+
+struct load_waveform_context
+{
+ struct cbox_menu_item_context header;
+ struct cbox_instrument *instrument;
+ char *filename;
+};
+
+static void destroy_load_waveform_context(void *p)
+{
+ struct load_waveform_context *context = p;
+ g_free(context->filename);
+ free(context);
+}
+
+int cmd_stream_load(struct cbox_menu_item_command *item, void *context)
+{
+ struct load_waveform_context *ctx = item->item.item_context;
+ GError *error = NULL;
+ struct cbox_command_target *target = &ctx->instrument->module->cmd_target;
+ if (target)
+ cbox_execute_on(target, NULL, "/load", "si", &error, ctx->filename, 0);
+ cbox_print_error_if(error);
+ return 0;
+}
+
+struct cbox_menu *create_streamplay_menu(struct cbox_menu_item_menu *item, void *menu_context)
+{
+ struct cbox_menu *menu = cbox_menu_new();
+ struct cbox_instrument *instr = item->item.item_context;
+
+ assert(instr);
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("Current stream", NULL, NULL, 0));
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("File", cmd_stream_status, instr, 0));
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("Module commands", NULL, NULL, 0));
+ cbox_menu_add_item(menu, cbox_menu_item_new_command("Play stream", cmd_stream_play, instr, 0));
+ cbox_menu_add_item(menu, cbox_menu_item_new_command("Stop stream", cmd_stream_stop, instr, 0));
+ cbox_menu_add_item(menu, cbox_menu_item_new_command("Rewind stream", cmd_stream_rewind, instr, 0));
+ cbox_menu_add_item(menu, cbox_menu_item_new_command("Unload stream", cmd_stream_unload, instr, 0));
+
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("Files available", NULL, NULL, 0));
+ glob_t g;
+ gboolean found = (glob("*.wav", GLOB_TILDE_CHECK, NULL, &g) == 0);
+ found = glob("*.ogg", GLOB_TILDE_CHECK | (found ? GLOB_APPEND : 0), NULL, &g) || found;
+ if (found)
+ {
+ for (size_t i = 0; i < g.gl_pathc; i++)
+ {
+ struct load_waveform_context *context = calloc(1, sizeof(struct load_waveform_context));
+ context->header.destroy_func = destroy_load_waveform_context;
+ context->instrument = instr;
+ context->filename = g_strdup(g.gl_pathv[i]);
+ cbox_menu_add_item(menu, cbox_menu_item_new_command(g_strdup_printf("Load: %s", g.gl_pathv[i]), cmd_stream_load, context, mif_free_label | mif_context_is_struct));
+ }
+ }
+ globfree(&g);
+
+ cbox_menu_add_item(menu, cbox_menu_item_new_ok());
+ return menu;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct cbox_menu *create_module_menu(struct cbox_menu_item_menu *item, void *menu_context)
+{
+ struct cbox_menu *menu = cbox_menu_new();
+
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("Scene instruments", NULL, NULL, 0));
+ struct cbox_scene *scene = app.engine->scenes[0];
+ for (uint32_t i = 0; i < scene->instrument_count; ++i)
+ {
+ struct cbox_instrument *instr = scene->instruments[i];
+ create_menu_func menufunc = NULL;
+
+ if (!strcmp(instr->module->engine_name, "stream_player"))
+ menufunc = create_streamplay_menu;
+ if (menufunc)
+ cbox_menu_add_item(menu, cbox_menu_item_new_dynamic_menu(g_strdup_printf("%s (%s)", instr->module->instance_name, instr->module->engine_name), menufunc, instr, mif_free_label));
+ else
+ cbox_menu_add_item(menu, cbox_menu_item_new_static(g_strdup_printf("%s (%s)", instr->module->instance_name, instr->module->engine_name), NULL, NULL, mif_free_label));
+ }
+ cbox_menu_add_item(menu, cbox_menu_item_new_ok());
+ return menu;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void restart_song()
+{
+ cbox_master_stop(app.engine->master);
+ cbox_master_seek_ppqn(app.engine->master, 0);
+ cbox_master_play(app.engine->master);
+}
+
+int cmd_pattern_none(struct cbox_menu_item_command *item, void *context)
+{
+ cbox_song_clear(app.engine->master->song);
+ cbox_engine_update_song_playback(app.engine);
+ return 0;
+}
+
+int cmd_pattern_simple(struct cbox_menu_item_command *item, void *context)
+{
+ cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_new_metronome(app.engine->master->song, 1, app.engine->master->ppqn_factor));
+ restart_song();
+ return 0;
+}
+
+int cmd_pattern_normal(struct cbox_menu_item_command *item, void *context)
+{
+ cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_new_metronome(app.engine->master->song, app.engine->master->timesig_num, app.engine->master->ppqn_factor));
+ restart_song();
+ return 0;
+}
+
+int cmd_load_drumpattern(struct cbox_menu_item_command *item, void *context)
+{
+ cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load(app.engine->master->song, item->item.item_context, 1, app.engine->master->ppqn_factor));
+ restart_song();
+ return 0;
+}
+
+int cmd_load_drumtrack(struct cbox_menu_item_command *item, void *context)
+{
+ cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load_track(app.engine->master->song, item->item.item_context, 1, app.engine->master->ppqn_factor));
+ restart_song();
+ return 0;
+}
+
+int cmd_load_pattern(struct cbox_menu_item_command *item, void *context)
+{
+ cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load(app.engine->master->song, item->item.item_context, 0, app.engine->master->ppqn_factor));
+ restart_song();
+ return 0;
+}
+
+int cmd_load_track(struct cbox_menu_item_command *item, void *context)
+{
+ cbox_song_use_looped_pattern(app.engine->master->song, cbox_midi_pattern_load_track(app.engine->master->song, item->item.item_context, 0, app.engine->master->ppqn_factor));
+ restart_song();
+ return 0;
+}
+
+struct cbox_menu *create_pattern_menu(struct cbox_menu_item_menu *item, void *menu_context)
+{
+ struct cbox_menu *menu = cbox_menu_new();
+ struct cbox_config_section_cb_data cb = { .menu = menu };
+
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("Pattern commands", NULL, NULL, 0));
+ cbox_menu_add_item(menu, cbox_menu_item_new_command("No pattern", cmd_pattern_none, NULL, 0));
+ cbox_menu_add_item(menu, cbox_menu_item_new_command("Simple metronome", cmd_pattern_simple, NULL, 0));
+ cbox_menu_add_item(menu, cbox_menu_item_new_command("Normal metronome", cmd_pattern_normal, NULL, 0));
+
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("Drum tracks", NULL, NULL, 0));
+ cb.prefix = "drumtrack:";
+ cb.func = cmd_load_drumtrack;
+ cbox_config_foreach_section(config_key_process, &cb);
+
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("Melodic tracks", NULL, NULL, 0));
+ cb.prefix = "track:";
+ cb.func = cmd_load_track;
+ cbox_config_foreach_section(config_key_process, &cb);
+
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("Drum patterns", NULL, NULL, 0));
+ cb.prefix = "drumpattern:";
+ cb.func = cmd_load_drumpattern;
+ cbox_config_foreach_section(config_key_process, &cb);
+
+ cbox_menu_add_item(menu, cbox_menu_item_new_static("Melodic patterns", NULL, NULL, 0));
+ cb.prefix = "pattern:";
+ cb.func = cmd_load_pattern;
+ cbox_config_foreach_section(config_key_process, &cb);
+
+ cbox_menu_add_item(menu, cbox_menu_item_new_ok());
+ return menu;
+}
+
+struct cbox_menu *create_main_menu()
+{
+ struct cbox_menu *main_menu = cbox_menu_new();
+ cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Current scene:", scene_format_value, NULL, 0));
+ cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Set scene", create_scene_menu, NULL, 0));
+ cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Module control", create_module_menu, NULL, 0));
+ cbox_menu_add_item(main_menu, cbox_menu_item_new_dynamic_menu("Pattern control", create_pattern_menu, NULL, 0));
+
+ cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Variables", NULL, NULL, 0));
+ // cbox_menu_add_item(main_menu, cbox_menu_item_new_int("foo:", &var1, 0, 127, NULL));
+ // cbox_menu_add_item(main_menu, "bar:", menu_item_value_double, &mx_double_var2, &var2);
+ //cbox_menu_add_item(main_menu, cbox_menu_item_new_static("pos:", transport_format_value, "pos", 0));
+ //cbox_menu_add_item(main_menu, cbox_menu_item_new_static("bbt:", transport_format_value, "bbt", 0));
+ cbox_menu_add_item(main_menu, cbox_menu_item_new_static("Commands", NULL, NULL, 0));
+ cbox_menu_add_item(main_menu, cbox_menu_item_new_command("Quit", cmd_quit, NULL, 0));
+ return main_menu;
+}
+
+#endif
diff --git a/template/calfbox/autogen.sh b/template/calfbox/autogen.sh
new file mode 100755
index 0000000..13a0322
--- /dev/null
+++ b/template/calfbox/autogen.sh
@@ -0,0 +1,6 @@
+aclocal --force || exit 1
+libtoolize --force --automake --copy || exit 1
+autoheader --force || exit 1
+autoconf --force || exit 1
+automake --add-missing --copy || exit 1
+
diff --git a/template/calfbox/auxbus.c b/template/calfbox/auxbus.c
new file mode 100644
index 0000000..da43be1
--- /dev/null
+++ b/template/calfbox/auxbus.c
@@ -0,0 +1,102 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "auxbus.h"
+#include "scene.h"
+#include
+#include
+
+extern gboolean cbox_scene_insert_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *aux_bus);
+extern void cbox_scene_remove_aux_bus(struct cbox_scene *scene, struct cbox_aux_bus *bus);
+
+CBOX_CLASS_DEFINITION_ROOT(cbox_aux_bus)
+
+static gboolean cbox_aux_bus_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct cbox_aux_bus *aux_bus = ct->user_data;
+ struct cbox_rt *rt = (struct cbox_rt *)cbox_document_get_service(CBOX_GET_DOCUMENT(aux_bus), "rt");
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ return cbox_execute_on(fb, NULL, "/name", "s", error, aux_bus->name) &&
+ CBOX_OBJECT_DEFAULT_STATUS(aux_bus, fb, error);
+ }
+ else
+ if (!strncmp(cmd->command, "/slot/", 6))
+ {
+ return cbox_module_slot_process_cmd(&aux_bus->module, fb, cmd, cmd->command + 5, CBOX_GET_DOCUMENT(aux_bus), rt, aux_bus->owner->engine, error);
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+}
+
+struct cbox_aux_bus *cbox_aux_bus_load(struct cbox_scene *scene, const char *name, struct cbox_rt *rt, GError **error)
+{
+ struct cbox_module *module = cbox_module_new_from_fx_preset(name, CBOX_GET_DOCUMENT(scene), rt, scene->engine, error);
+ if (!module)
+ return NULL;
+
+ struct cbox_aux_bus *p = malloc(sizeof(struct cbox_aux_bus));
+ CBOX_OBJECT_HEADER_INIT(p, cbox_aux_bus, CBOX_GET_DOCUMENT(scene));
+ cbox_command_target_init(&p->cmd_target, cbox_aux_bus_process_cmd, p);
+ p->name = g_strdup(name);
+ p->owner = scene;
+ p->module = module;
+ p->refcount = 0;
+ // XXXKF this work up to buffer size of 8192 floats, this should be determined from JACK settings and updated when
+ // JACK buffer size changes
+ p->input_bufs[0] = malloc(8192 * sizeof(float));
+ p->input_bufs[1] = malloc(8192 * sizeof(float));
+ p->output_bufs[0] = malloc(8192 * sizeof(float));
+ p->output_bufs[1] = malloc(8192 * sizeof(float));
+ CBOX_OBJECT_REGISTER(p);
+ cbox_scene_insert_aux_bus(scene, p);
+
+ return p;
+}
+
+void cbox_aux_bus_ref(struct cbox_aux_bus *bus)
+{
+ ++bus->refcount;
+}
+
+void cbox_aux_bus_unref(struct cbox_aux_bus *bus)
+{
+ assert(bus->refcount > 0);
+ --bus->refcount;
+}
+
+void cbox_aux_bus_destroyfunc(struct cbox_objhdr *objhdr)
+{
+ struct cbox_aux_bus *bus = CBOX_H2O(objhdr);
+ if (bus->owner)
+ {
+ cbox_scene_remove_aux_bus(bus->owner, bus);
+ bus->owner = NULL;
+ }
+ CBOX_DELETE(bus->module);
+ bus->module = NULL;
+ assert(!bus->refcount);
+ g_free(bus->name);
+ free(bus->input_bufs[0]);
+ free(bus->input_bufs[1]);
+ free(bus);
+}
+
diff --git a/template/calfbox/auxbus.h b/template/calfbox/auxbus.h
new file mode 100644
index 0000000..63159b5
--- /dev/null
+++ b/template/calfbox/auxbus.h
@@ -0,0 +1,47 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_AUXBUS_H
+#define CBOX_AUXBUS_H
+
+#include "dom.h"
+#include "module.h"
+
+struct cbox_scene;
+
+CBOX_EXTERN_CLASS(cbox_aux_bus)
+
+struct cbox_aux_bus
+{
+ CBOX_OBJECT_HEADER()
+ struct cbox_command_target cmd_target;
+ struct cbox_scene *owner;
+
+ gchar *name;
+ struct cbox_module *module;
+ int refcount;
+
+ float *input_bufs[2];
+ float *output_bufs[2];
+};
+
+extern struct cbox_aux_bus *cbox_aux_bus_load(struct cbox_scene *scene, const char *name, struct cbox_rt *rt, GError **error);
+extern void cbox_aux_bus_ref(struct cbox_aux_bus *bus);
+extern void cbox_aux_bus_unref(struct cbox_aux_bus *bus);
+
+#endif
diff --git a/template/calfbox/background_example.py b/template/calfbox/background_example.py
new file mode 100644
index 0000000..7addb7c
--- /dev/null
+++ b/template/calfbox/background_example.py
@@ -0,0 +1,6 @@
+import _cbox
+import time
+while True:
+ _cbox.do_cmd("/on_idle", None, [])
+ time.sleep(0.1)
+
diff --git a/template/calfbox/biquad-float.h b/template/calfbox/biquad-float.h
new file mode 100644
index 0000000..f2789ab
--- /dev/null
+++ b/template/calfbox/biquad-float.h
@@ -0,0 +1,398 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_BIQUAD_FLOAT_H
+#define CBOX_BIQUAD_FLOAT_H
+
+#include "config.h"
+#include "dspmath.h"
+
+struct cbox_biquadf_state
+{
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+};
+
+struct cbox_biquadf_coeffs
+{
+ float a0;
+ float a1;
+ float a2;
+ float b1;
+ float b2;
+};
+
+static inline void cbox_biquadf_reset(struct cbox_biquadf_state *state)
+{
+ state->x1 = state->y1 = state->x2 = state->y2 = 0.f;
+}
+
+static inline float cbox_biquadf_is_audible(struct cbox_biquadf_state *state, float level)
+{
+ return fabs(state->x1) + fabs(state->x2) + fabs(state->y1) + fabs(state->y2) >= level;
+}
+
+// Based on filter coefficient equations by Robert Bristow-Johnson
+static inline void cbox_biquadf_set_lp_rbj(struct cbox_biquadf_coeffs *coeffs, float fc, float q, float sr)
+{
+ float omega=(float)(2*M_PI*fc/sr);
+ float sn=sin(omega);
+ float cs=cos(omega);
+ float alpha=(float)(sn/(2*q));
+ float inv=(float)(1.0/(1.0+alpha));
+
+ coeffs->a2 = coeffs->a0 = (float)(inv*(1 - cs)*0.5f);
+ coeffs->a1 = coeffs->a0 + coeffs->a0;
+ coeffs->b1 = (float)(-2*cs*inv);
+ coeffs->b2 = (float)((1 - alpha)*inv);
+}
+
+static inline void cbox_biquadf_set_lp_rbj_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, float q)
+{
+ float sn=sincos->sine;
+ float cs=sincos->cosine;
+ float alpha=(float)(sn/(2*q));
+ float inv=(float)(1.0/(1.0+alpha));
+
+ coeffs->a2 = coeffs->a0 = (float)(inv*(1 - cs)*0.5f);
+ coeffs->a1 = coeffs->a0 + coeffs->a0;
+ coeffs->b1 = (float)(-2*cs*inv);
+ coeffs->b2 = (float)((1 - alpha)*inv);
+}
+
+// Based on filter coefficient equations by Robert Bristow-Johnson
+static inline void cbox_biquadf_set_hp_rbj(struct cbox_biquadf_coeffs *coeffs, float fc, float q, float sr)
+{
+ float omega=(float)(2*M_PI*fc/sr);
+ float sn=sin(omega);
+ float cs=cos(omega);
+ float alpha=(float)(sn/(2*q));
+ float inv=(float)(1.0/(1.0+alpha));
+
+ coeffs->a2 = coeffs->a0 = (float)(inv*(1 + cs)*0.5f);
+ coeffs->a1 = -2 * coeffs->a0;
+ coeffs->b1 = (float)(-2*cs*inv);
+ coeffs->b2 = (float)((1 - alpha)*inv);
+}
+
+static inline void cbox_biquadf_set_hp_rbj_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, float q)
+{
+ float sn=sincos->sine;
+ float cs=sincos->cosine;
+ float alpha=(float)(sn/(2*q));
+ float inv=(float)(1.0/(1.0+alpha));
+
+ coeffs->a2 = coeffs->a0 = (float)(inv*(1 + cs)*0.5f);
+ coeffs->a1 = -2 * coeffs->a0;
+ coeffs->b1 = (float)(-2*cs*inv);
+ coeffs->b2 = (float)((1 - alpha)*inv);
+}
+
+// Based on filter coefficient equations by Robert Bristow-Johnson
+static inline void cbox_biquadf_set_bp_rbj(struct cbox_biquadf_coeffs *coeffs, float fc, float q, float sr)
+{
+ float omega=(float)(2*M_PI*fc/sr);
+ float sn=sin(omega);
+ float cs=cos(omega);
+ float alpha=(float)(sn/(2*q));
+ float inv=(float)(1.0/(1.0+alpha));
+
+ coeffs->a0 = (float)(inv*alpha);
+ coeffs->a1 = 0.f;
+ coeffs->a2 = -coeffs->a0;
+ coeffs->b1 = (float)(-2*cs*inv);
+ coeffs->b2 = (float)((1 - alpha)*inv);
+}
+
+static inline void cbox_biquadf_set_bp_rbj_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, float q)
+{
+ float sn=sincos->sine;
+ float cs=sincos->cosine;
+ float alpha=(float)(sn/(2*q));
+ float inv=(float)(1.0/(1.0+alpha));
+
+ coeffs->a0 = (float)(inv*alpha);
+ coeffs->a1 = 0.f;
+ coeffs->a2 = -coeffs->a0;
+ coeffs->b1 = (float)(-2*cs*inv);
+ coeffs->b2 = (float)((1 - alpha)*inv);
+}
+
+
+// Based on filter coefficient equations by Robert Bristow-Johnson
+static inline void cbox_biquadf_set_peakeq_rbj(struct cbox_biquadf_coeffs *coeffs, float freq, float q, float peak, float sr)
+{
+ float A = sqrt(peak);
+ float w0 = freq * 2 * M_PI * (1.0 / sr);
+ float alpha = sin(w0) / (2 * q);
+ float ib0 = 1.0 / (1 + alpha/A);
+ coeffs->a1 = coeffs->b1 = -2*cos(w0) * ib0;
+ coeffs->a0 = ib0 * (1 + alpha*A);
+ coeffs->a2 = ib0 * (1 - alpha*A);
+ coeffs->b2 = ib0 * (1 - alpha/A);
+}
+
+static inline void cbox_biquadf_set_peakeq_rbj_scaled(struct cbox_biquadf_coeffs *coeffs, float freq, float q, float A, float sr)
+{
+ float w0 = freq * 2 * M_PI * (1.0 / sr);
+ float alpha = sin(w0) / (2 * q);
+ float ib0 = 1.0 / (1 + alpha/A);
+ coeffs->a1 = coeffs->b1 = -2*cos(w0) * ib0;
+ coeffs->a0 = ib0 * (1 + alpha*A);
+ coeffs->a2 = ib0 * (1 - alpha*A);
+ coeffs->b2 = ib0 * (1 - alpha/A);
+}
+
+// This is my math, and it's rather suspect
+static inline void cbox_biquadf_set_1plp(struct cbox_biquadf_coeffs *coeffs, float freq, float sr)
+{
+ float w = hz2w(freq, sr);
+ float x = tan (w * 0.5f);
+ float q = 1 / (1 + x);
+ float a01 = x*q;
+ float b1 = a01 - q;
+
+ coeffs->a0 = a01;
+ coeffs->a1 = a01;
+ coeffs->b1 = b1;
+ coeffs->a2 = 0;
+ coeffs->b2 = 0;
+}
+
+static inline void cbox_biquadf_set_1php(struct cbox_biquadf_coeffs *coeffs, float freq, float sr)
+{
+ float w = hz2w(freq, sr);
+ float x = tan (w * 0.5f);
+ float q = 1 / (1 + x);
+ float a01 = x*q;
+ float b1 = a01 - q;
+
+ coeffs->a0 = q;
+ coeffs->a1 = -q;
+ coeffs->b1 = b1;
+ coeffs->a2 = 0;
+ coeffs->b2 = 0;
+}
+
+static inline void cbox_biquadf_set_1p(struct cbox_biquadf_coeffs *coeffs, float a0, float a1, float b1, int two_copies)
+{
+ if (two_copies)
+ {
+ // (a0 + a1z) * (a0 + a1z) = a0^2 + 2*a0*a1*z + a1^2*z^2
+ // (1 - b1z) * (1 - b1z) = 1 - 2b1*z + b1^2*z^2
+ coeffs->a0 = a0*a0;
+ coeffs->a1 = 2*a0*a1;
+ coeffs->b1 = 2 * b1;
+ coeffs->a2 = a1*a1;
+ coeffs->b2 = b1*b1;
+ }
+ else
+ {
+ coeffs->a0 = a0;
+ coeffs->a1 = a1;
+ coeffs->b1 = b1;
+ coeffs->a2 = 0;
+ coeffs->b2 = 0;
+ }
+}
+
+static inline void cbox_biquadf_set_1plp_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, int two_copies)
+{
+ float x = sincos->prewarp;
+ float q = sincos->prewarp2;
+ float a01 = x*q;
+ float b1 = a01 - q;
+
+ cbox_biquadf_set_1p(coeffs, a01, a01, b1, two_copies);
+}
+
+static inline void cbox_biquadf_set_1php_lookup(struct cbox_biquadf_coeffs *coeffs, const struct cbox_sincos *sincos, int two_copies)
+{
+ float x = sincos->prewarp;
+ float q = sincos->prewarp2;
+ float a01 = x*q;
+ float b1 = a01 - q;
+
+ cbox_biquadf_set_1p(coeffs, q, -q, b1, two_copies);
+}
+
+#if USE_NEON
+
+#include
+
+static inline void cbox_biquadf_process(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, float *buffer)
+{
+ int i;
+ float32x2_t c0 = {coeffs->a0, 0};
+ float32x2_t c1 = {coeffs->a1, -coeffs->b1};
+ float32x2_t c2 = {coeffs->a2, -coeffs->b2};
+ float32x2_t s1 = {state->x1, state->y1};
+ float32x2_t s2 = {state->x2, state->y2};
+
+ for (i = 0; i < CBOX_BLOCK_SIZE; i ++)
+ {
+ float32x2_t in12 = {buffer[i], 0.f};
+
+ float32x2_t out12 = vmla_f32(vmla_f32(vmul_f32(c1, s1), c2, s2), in12, c0); // [a1 * x1 + a2 * x2 + a0 * in, -b1 * y1 - b2 * y2 + 0]
+ float32x2x2_t trn = vtrn_f32(out12, in12); // [[a1 * x1 + a2 * x2 + a0 * in, in12], [-b1 * y1 - b2 * y2, 0]]
+ float32x2_t out120 = vadd_f32(trn.val[0], trn.val[1]);
+
+ s2 = s1;
+ s1 = vrev64_f32(out120);
+ buffer[i] = out120[0];
+ }
+ state->x1 = s1[0];
+ state->y1 = s1[1];
+ state->x2 = s2[0];
+ state->y2 = s2[1];
+}
+
+#else
+
+static inline void cbox_biquadf_process(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, float *buffer)
+{
+ int i;
+ float a0 = coeffs->a0;
+ float a1 = coeffs->a1;
+ float a2 = coeffs->a2;
+ float b1 = coeffs->b1;
+ float b2 = coeffs->b2;
+ double y1 = state->y1;
+ double y2 = state->y2;
+
+ for (i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ float in = buffer[i];
+ double out = a0 * in + a1 * state->x1 + a2 * state->x2 - b1 * y1 - b2 * y2;
+
+ buffer[i] = out;
+ state->x2 = state->x1;
+ state->x1 = in;
+ y2 = y1;
+ y1 = out;
+ }
+ state->y2 = sanef(y2);
+ state->y1 = sanef(y1);
+}
+
+#endif
+
+static inline void cbox_biquadf_process_stereo(struct cbox_biquadf_state *lstate, struct cbox_biquadf_state *rstate, struct cbox_biquadf_coeffs *coeffs, float *buffer)
+{
+ int i;
+ float a0 = coeffs->a0;
+ float a1 = coeffs->a1;
+ float a2 = coeffs->a2;
+ float b1 = coeffs->b1;
+ float b2 = coeffs->b2;
+ double ly1 = lstate->y1;
+ double ly2 = lstate->y2;
+ double ry1 = rstate->y1;
+ double ry2 = rstate->y2;
+
+ for (i = 0; i < 2 * CBOX_BLOCK_SIZE; i += 2)
+ {
+ float inl = buffer[i], inr = buffer[i + 1];
+ float outl = a0 * inl + a1 * lstate->x1 + a2 * lstate->x2 - b1 * ly1 - b2 * ly2;
+ float outr = a0 * inr + a1 * rstate->x1 + a2 * rstate->x2 - b1 * ry1 - b2 * ry2;
+
+ lstate->x2 = lstate->x1;
+ lstate->x1 = inl;
+ ly2 = ly1;
+ ly1 = outl;
+ buffer[i] = outl;
+
+ rstate->x2 = rstate->x1;
+ rstate->x1 = inr;
+ ry2 = ry1;
+ ry1 = outr;
+ buffer[i + 1] = outr;
+ }
+ lstate->y2 = sanef(ly2);
+ lstate->y1 = sanef(ly1);
+ rstate->y2 = sanef(ry2);
+ rstate->y1 = sanef(ry1);
+}
+
+static inline double cbox_biquadf_process_sample(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, double in)
+{
+ double out = sanef(coeffs->a0 * sanef(in) + coeffs->a1 * state->x1 + coeffs->a2 * state->x2 - coeffs->b1 * state->y1 - coeffs->b2 * state->y2);
+
+ state->x2 = state->x1;
+ state->x1 = in;
+ state->y2 = state->y1;
+ state->y1 = out;
+
+ return out;
+}
+
+static inline void cbox_biquadf_process_to(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, float *buffer_in, float *buffer_out)
+{
+ int i;
+ float a0 = coeffs->a0;
+ float a1 = coeffs->a1;
+ float a2 = coeffs->a2;
+ float b1 = coeffs->b1;
+ float b2 = coeffs->b2;
+ double y1 = state->y1;
+ double y2 = state->y2;
+
+ for (i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ float in = buffer_in[i];
+ double out = a0 * in + a1 * state->x1 + a2 * state->x2 - b1 * y1 - b2 * y2;
+
+ buffer_out[i] = out;
+ state->x2 = state->x1;
+ state->x1 = in;
+ y2 = y1;
+ y1 = out;
+ }
+ state->y2 = sanef(y2);
+ state->y1 = sanef(y1);
+}
+
+static inline void cbox_biquadf_process_adding(struct cbox_biquadf_state *state, struct cbox_biquadf_coeffs *coeffs, float *buffer_in, float *buffer_out)
+{
+ int i;
+ float a0 = coeffs->a0;
+ float a1 = coeffs->a1;
+ float a2 = coeffs->a2;
+ float b1 = coeffs->b1;
+ float b2 = coeffs->b2;
+ double y1 = state->y1;
+ double y2 = state->y2;
+
+ for (i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ float in = buffer_in[i];
+ double out = a0 * in + a1 * state->x1 + a2 * state->x2 - b1 * y1 - b2 * y2;
+
+ buffer_out[i] += out;
+ state->x2 = state->x1;
+ state->x1 = in;
+ y2 = y1;
+ y1 = out;
+ }
+ state->y2 = sanef(y2);
+ state->y1 = sanef(y1);
+}
+
+#endif
diff --git a/template/calfbox/blob.c b/template/calfbox/blob.c
new file mode 100644
index 0000000..e8d65f6
--- /dev/null
+++ b/template/calfbox/blob.c
@@ -0,0 +1,130 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "blob.h"
+#include "tarfile.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct cbox_blob *cbox_blob_new(size_t size)
+{
+ struct cbox_blob *p = malloc(sizeof(struct cbox_blob));
+ if (!p)
+ return NULL;
+ p->data = size ? malloc(size) : NULL;
+ p->size = size;
+ return p;
+}
+
+struct cbox_blob *cbox_blob_new_copy_data(const void *data, size_t size)
+{
+ struct cbox_blob *p = cbox_blob_new(size);
+ if (!p)
+ return NULL;
+ memcpy(p, data, size);
+ return p;
+}
+
+struct cbox_blob *cbox_blob_new_acquire_data(void *data, size_t size)
+{
+ struct cbox_blob *p = malloc(sizeof(struct cbox_blob));
+ if (!p)
+ return NULL;
+ p->data = data;
+ p->size = size;
+ return p;
+}
+
+static struct cbox_blob *read_from_fd(const char *context_name, const char *pathname, int fd, size_t size, GError **error)
+{
+ struct cbox_blob *blob = cbox_blob_new(size + 1);
+ if (!blob)
+ {
+ g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot allocate memory for file '%s'", context_name, pathname);
+ return NULL;
+ }
+ uint8_t *data = blob->data;
+ size_t nread = 0;
+ do {
+ size_t chunk = size - nread;
+ if (chunk > 131072)
+ chunk = 131072;
+ size_t nv = read(fd, data + nread, chunk);
+ if (nv == (size_t)-1)
+ {
+ if (errno == EINTR)
+ continue;
+ g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot read '%s': %s", context_name, pathname, strerror(errno));
+ cbox_blob_destroy(blob);
+ return NULL;
+ }
+ nread += nv;
+ } while(nread < size);
+ // Make sure that the content is 0-padded but still has the original size
+ // (without extra zero byte)
+ data[nread] = '\0';
+ blob->size = nread;
+ return blob;
+}
+
+struct cbox_blob *cbox_blob_new_from_file(const char *context_name, struct cbox_tarfile *tarfile, const char *path, const char *name, size_t max_size, GError **error)
+{
+ gchar *fullpath = g_build_filename(path, name, NULL);
+ struct cbox_blob *blob = NULL;
+ if (tarfile)
+ {
+ struct cbox_taritem *item = cbox_tarfile_get_item_by_name(tarfile, fullpath, TRUE);
+ if (item)
+ {
+ int fd = cbox_tarfile_openitem(tarfile, item);
+ if (fd >= 0)
+ {
+ blob = read_from_fd(context_name, fullpath, fd, item->size, error);
+ cbox_tarfile_closeitem(tarfile, item, fd);
+ }
+ }
+ }
+ else
+ {
+ int fd = open(fullpath, O_RDONLY | O_LARGEFILE);
+ if (fd >= 0)
+ {
+ uint64_t size = lseek64(fd, 0, SEEK_END);
+ if (size <= max_size)
+ blob = read_from_fd(context_name, fullpath, fd, size, error);
+ else
+ g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: file '%s' too large (%llu while max size is %u)", context_name, fullpath, (unsigned long long)size, (unsigned)max_size);
+ close(fd);
+ }
+ else
+ g_set_error(error, G_FILE_ERROR, g_file_error_from_errno (errno), "%s: cannot open '%s': %s", context_name, fullpath, strerror(errno));
+ }
+ g_free(fullpath);
+ return blob;
+}
+
+void cbox_blob_destroy(struct cbox_blob *blob)
+{
+ free(blob->data);
+ free(blob);
+}
diff --git a/template/calfbox/blob.h b/template/calfbox/blob.h
new file mode 100644
index 0000000..4999b0c
--- /dev/null
+++ b/template/calfbox/blob.h
@@ -0,0 +1,39 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_BLOB_H
+#define CBOX_BLOB_H
+
+#include
+#include
+
+struct cbox_blob
+{
+ void *data;
+ size_t size;
+};
+
+struct cbox_tarfile;
+
+extern struct cbox_blob *cbox_blob_new(size_t size);
+extern struct cbox_blob *cbox_blob_new_from_file(const char *context_name, struct cbox_tarfile *tarfile, const char *path, const char *name, size_t max_size, GError **error);
+extern struct cbox_blob *cbox_blob_new_copy_data(const void *data, size_t size);
+extern struct cbox_blob *cbox_blob_new_acquire_data(void *data, size_t size);
+extern void cbox_blob_destroy(struct cbox_blob *blob);
+
+#endif
diff --git a/template/calfbox/cboxrc-example b/template/calfbox/cboxrc-example
new file mode 100644
index 0000000..fba4902
--- /dev/null
+++ b/template/calfbox/cboxrc-example
@@ -0,0 +1,574 @@
+[io]
+inputs=2
+in_1=#1
+in_2=#2
+out_1=#1
+out_2=#2
+
+;#midi=alsa_pcm:E-MU-XMidi2X2/midi_capture_2;alsa_pcm:E-MU-Xboard25/midi_capture_1
+;midi=alsa_pcm:E-MU-XMidi2X2/midi_capture_2;alsa_pcm:E-MU-XMidi2X2/midi_capture_1
+;midi=~alsa_pcm:in-.*-E-MU-XMidi2X2-MIDI-1;~alsa_pcm:in-.*-E-MU-XMidi2X2-MIDI-2;~alsa_pcm:in-.*-padKONTROL-MIDI-2
+midi=*.*
+
+[master]
+tempo=100
+beats_per_bar=4
+;effect=cbox_reverb
+
+[fxpreset:cbox_reverb]
+engine=reverb
+reverb_time=800
+wet_gain=-12
+dry_gain=0
+stereo=-12
+
+[scene:example]
+layer1=organ
+layer2=piano
+
+[layer:piano]
+instrument=progmega
+out_channel=1
+low_note=C3
+
+[layer:organ]
+instrument=default
+out_channel=1
+high_note=B2
+
+[instrument:default]
+engine=tonewheel_organ
+percussion=1
+percussion_3rd=1
+upper_drawbars=888000000
+;upper_drawbars=888888888
+;upper_drawbars=888000008
+;upper_drawbars=800000888
+;upper_drawbars=800064000
+;upper_drawbars=802244220
+lower_drawbars=838000000
+pedal_drawbars=80
+vibrato_upper=1
+vibrato_lower=1
+vibrato_mode=c3
+
+[instrument:progmega]
+engine=fluidsynth
+sf2=ProgMegaBank.sf2
+reverb=1
+chorus=1
+channel1=SP250
+channel2=jRhodes3a
+channel3=P5 Brass
+
+[instrument:progmega_cheap]
+engine=fluidsynth
+sf2=ProgMegaBank.sf2
+reverb=0
+chorus=0
+channel1=SP250
+channel2=jRhodes3a
+channel3=P5 Brass
+
+[instrument:progmega_fx]
+engine=fluidsynth
+sf2=ProgMegaBank.sf2
+reverb=0
+chorus=0
+insert=chain
+channel1=SP250
+channel2=jRhodes3a
+channel3=P5 Brass
+
+[fxpreset:chain]
+engine=fxchain
+effect1=stream_phaser
+effect2=stream_delay
+
+[fxpreset:stream_phaser]
+engine=phaser
+center=500
+mod_depth=300
+lfo_freq=3
+
+[fxpreset:stream_delay]
+engine=delay
+delay=250
+wet_amt=0.25
+feedback_gain=-12
+
+[autojack]
+soundcard0=Omega
+soundcard1=STA
+soundcard2=Intel
+jack_options=-r -T
+alsa_options=-p 128 -n 3 -X raw
+jackd=/usr/bin/jackd
+
+[soundcard:Omega]
+usbid=1210:0002
+
+[soundcard:STA]
+device=DSP24
+
+[soundcard:Intel]
+device=Intel
+
+[pattern:walkingminor]
+title=Walking-minor
+beats=4
+resolution=1
+track1=bass
+bass_channel=2
+bass_vel=80
+bass_notes=c1,d1,eb1,g1
+
+[track:two]
+title=Cm/Ebm
+pos1=walkingminor
+pos2=walkingminor+3
+
+[drumpattern:pat1]
+title=Straight - Verse
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=9... .... 9.6. ....
+sd_trigger=.... 9..5 .2.. 9...
+hh_trigger=9353 7353 7353 73.3
+ho_trigger=.... .... .... ..3.
+
+[drumpattern:pat1fill]
+title=Straight - Fill
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=9... .... 9.6. ....
+sd_trigger=..64 9..5 .2.7 99.9
+hh_trigger=9353 7353 7353 73.3
+ho_trigger=.... .... .... ..3.
+
+[drumpattern:pat2]
+title=Enigma - Verse
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=9..7 ..7. ..6. ....
+sd_trigger=.... 9... .... 9...
+hh_trigger=9353 7353 7353 7353
+ho_trigger=.... .... ..3. ....
+
+[drumpattern:pat2fill]
+title=Enigma - Fill
+beats=4
+resolution=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+track5=ht
+track6=mt
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+ht_note=d2
+mt_note=b1
+bd_trigger=9..7 ..7. ..6. ....
+sd_trigger=.... 9... .... 9..9
+hh_trigger=9353 7353 7353 7353
+ho_trigger=.... .... ..3. ....
+ht_trigger=.5.. .... ...7 ....
+mt_trigger=.... ...5 .... ..5.
+
+[drumpattern:pat3]
+title=Shuffle - Verse
+beats=4
+swing=6
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+track5=ht
+track6=mt
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+ht_note=d2
+mt_note=b1
+bd_trigger=9.8. ...8 ..6. ....
+sd_trigger=.... 94D. .54. 9..D
+hh_trigger=9353 7353 7353 7353
+ho_trigger=.... .... .... ....
+ht_trigger=.... .... .... ....
+mt_trigger=.... .... .... ....
+
+[drumpattern:pat3fill]
+title=Shuffle - Fill
+beats=4
+swing=6
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+track5=ht
+track6=mt
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+ht_note=d2
+mt_note=b1
+bd_trigger=9.8. ...8 ..6. ....
+sd_trigger=.... 9... F... F..9
+hh_trigger=9353 7353 7353 7353
+ho_trigger=.... .... .... ....
+ht_trigger=.... .... ..9. .5..
+mt_trigger=.... .... ...7 ..7.
+
+[drumpattern:pat4]
+title=Swing
+beats=4
+resolution=3
+track1=bd
+track2=sd
+track3=hp
+track4=rc
+track5=rb
+bd_note=c1
+sd_note=d1
+hp_note=g#1
+rc_note=d#2
+rb_note=f2
+bd_trigger=... ... ... ..3
+sd_trigger=... ..3 ..2 .3.
+hp_trigger=... 6.. ... 6..
+rc_trigger=2.. ..4 4.. ..3
+rb_trigger=3.. ... 3.. ...
+
+[drumpattern:pat4fill]
+title=Swing
+beats=4
+resolution=3
+track1=bd
+track2=sd
+track3=hp
+track4=rc
+track5=rb
+bd_note=c1
+sd_note=d1
+hp_note=g#1
+rc_note=d#2
+rb_note=f2
+bd_trigger=7.. 5.. ... ..5
+sd_trigger=..5 ..3 .25 43.
+hp_trigger=... 6.. ... 6..
+rc_trigger=2.. ..4 4.. ...
+rb_trigger=4.. ... 2.. ...
+
+[drumpattern:pat5]
+title=6/8 Blues - Verse
+beats=4
+resolution=3
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=7.. ... 7.. .44
+sd_trigger=... 8.. ... 8..
+hh_trigger=7.5 6.4 7.5 6..
+ho_trigger=... ... ... ..3
+
+[drumpattern:pat5fill]
+title=6/8 Blues - Fill
+beats=4
+resolution=3
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+track5=ht
+track6=mt
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+ht_note=d2
+mt_note=b1
+bd_trigger=7.. ... ... ..4
+sd_trigger=... 8.D 75. 85.
+hh_trigger=7.5 6.. 7.. ...
+ho_trigger=.3. ... ... ...
+ht_trigger=... ... ..7 ...
+mt_trigger=... ... ... ..5
+
+[drumpattern:pat6]
+title=Pompopom - Verse
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=9.97 ...9 .996 ....
+sd_trigger=.... 9... .... 9...
+hh_trigger=9.7. 7.7. 9.7. 7.7.
+ho_trigger=.... .3.. .... ....
+
+[drumpattern:pat6fill]
+title=Pompopom - Fill
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=9..7 ...9 .9.6 ....
+sd_trigger=.6.. 9.7. ..7D 97.9
+hh_trigger=9.7. 7.7. 9.7. 7.7.
+ho_trigger=.... .3.. .... ....
+
+[drumpattern:pat7]
+title=Rockish
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=98.6 ...7 .95. ....
+sd_trigger=.... 9... .... 9..D
+hh_trigger=7.5. 7.5. 7.5. 7.5.
+ho_trigger=...3 .... .... ....
+
+[drumpattern:pat7fill]
+title=Rockish - Fill
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+track5=mt
+track6=ht
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+mt_note=b1
+ht_note=d2
+bd_trigger=98.6 ...7 .95. ....
+sd_trigger=.... 9..7 ..9. 9..9
+hh_trigger=7.5. 7.5. 7.5. 7.5.
+ho_trigger=.5.3 .... .... ....
+mt_trigger=.... .... .... ..8.
+ht_trigger=.... .... ...9 ....
+
+[drumpattern:pat8]
+title=80s - Verse
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=96.. ..96 ..6. ....
+sd_trigger=.... 9... .... 9...
+hh_trigger=7.7. 7.7. 7.7. 7.7.
+ho_trigger=.... .... .... ....
+
+[drumpattern:pat8fill]
+title=80s - Fill
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+track5=mt
+track6=ht
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+mt_note=b1
+ht_note=d2
+bd_trigger=96.. ..96 ..6. ....
+sd_trigger=..7. 9... 7.7. 9...
+hh_trigger=7.7. 7.7. 7.7. 7...
+ho_trigger=.... .... .... ....
+ht_trigger=.... .... .9.. .97.
+mt_trigger=.... .... ...7 ...5
+
+[drumpattern:pat9]
+title=Disco - Verse
+beats=4
+swing=2
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+track5=rs
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+rs_note=c#1
+bd_trigger=9... 9... 9... 9...
+rs_trigger=..4. .4.. 4..4 ..4.
+sd_trigger=.... 8..6 .... 8...
+hh_trigger=74.7 74.7 74.7 74.7
+ho_trigger=..5. ..5. ..5. ..5.
+
+[drumpattern:pat9fill]
+title=Disco - Fill
+beats=4
+swing=2
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=9... 9... 9... 9...
+sd_trigger=.6.. 8.58 .6.5 8688
+hh_trigger=74.7 74.7 74.7 74.7
+ho_trigger=..5. ..5. ..5. ..5.
+
+[drumpattern:crash]
+title=Crash cymbal
+beats=4
+track1=cc
+cc_note=c#2
+cc_trigger=9...
+
+[drumpattern:crash2]
+title=Kick+crash cymbal
+beats=4
+track1=cc
+track2=bd
+cc_note=a#2
+bd_note=b0
+cc_trigger=5...
+bd_trigger=5...
+
+[drumtrack:trk1]
+title=Straight
+pos1=pat1,crash
+pos2=pat1
+pos3=pat1
+pos4=pat1fill
+
+[drumtrack:trk2]
+title=Enigma
+pos1=pat2,crash
+pos2=pat2
+pos3=pat2
+pos4=pat2fill
+
+[drumtrack:trk3]
+title=Shuffle
+pos1=pat3,crash
+pos2=pat3
+pos3=pat3
+pos4=pat3fill
+
+[drumtrack:trk4]
+title=Swing
+pos1=pat4,crash2
+pos2=pat4
+pos3=pat4
+pos4=pat4fill
+
+[drumtrack:trk5]
+title=6/8 Blues
+pos1=pat5,crash
+pos2=pat5
+pos3=pat5
+pos4=pat5fill
+
+[drumtrack:trk6]
+title=Pompopom
+pos1=pat6,crash
+pos2=pat6
+pos3=pat6
+pos4=pat6fill
+
+[drumtrack:trk7]
+title=Rockish
+pos1=pat7,crash
+pos2=pat7
+pos3=pat7
+pos4=pat7fill
+
+[drumtrack:trk8]
+title=80s
+pos1=pat8,crash
+pos2=pat8
+pos3=pat8
+pos4=pat8fill
+
+[drumtrack:trk9]
+title=Disco
+pos1=pat9,crash
+pos2=pat9
+pos3=pat9
+pos4=pat9fill
+
+[layer:samplertest]
+instrument=samplertest
+
+[instrument:samplertest]
+engine=sampler
+program0=prog
+
+[spgm:prog]
+layer1=saw1
+layer2=saw2
+
+[slayer:saw1]
+sample=*saw
+tune=-5
+pan=-30
+volume=-6
+
+[slayer:saw2]
+sample=*saw
+tune=+5
+pan=30
+volume=-6
diff --git a/template/calfbox/chorus.c b/template/calfbox/chorus.c
new file mode 100644
index 0000000..06945da
--- /dev/null
+++ b/template/calfbox/chorus.c
@@ -0,0 +1,175 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_CHORUS_LENGTH 4096
+
+#define MODULE_PARAMS chorus_params
+
+static float sine_table[2049];
+
+struct chorus_params
+{
+ float lfo_freq;
+ float min_delay;
+ float mod_depth;
+ float wet_dry;
+ float sphase;
+};
+
+struct chorus_module
+{
+ struct cbox_module module;
+
+ float storage[MAX_CHORUS_LENGTH][2];
+ struct chorus_params *params;
+ int pos;
+ float tp32dsr;
+ uint32_t phase;
+};
+
+MODULE_PROCESSCMD_FUNCTION(chorus)
+{
+ struct chorus_module *m = (struct chorus_module *)ct->user_data;
+
+ EFFECT_PARAM("/min_delay", "f", min_delay, double, , 1, 20) else
+ EFFECT_PARAM("/mod_depth", "f", mod_depth, double, , 1, 20) else
+ EFFECT_PARAM("/lfo_freq", "f", lfo_freq, double, , 0, 20) else
+ EFFECT_PARAM("/stereo_phase", "f", sphase, double, , 0, 360) else
+ EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ return cbox_execute_on(fb, NULL, "/min_delay", "f", error, m->params->min_delay) &&
+ cbox_execute_on(fb, NULL, "/mod_depth", "f", error, m->params->mod_depth) &&
+ cbox_execute_on(fb, NULL, "/lfo_freq", "f", error, m->params->lfo_freq) &&
+ cbox_execute_on(fb, NULL, "/stereo_phase", "f", error, m->params->sphase) &&
+ cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry) &&
+ CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error);
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void chorus_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct chorus_module *m = (struct chorus_module *)module;
+}
+
+void chorus_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct chorus_module *m = (struct chorus_module *)module;
+ struct chorus_params *p = m->params;
+
+ float min_delay = p->min_delay;
+ float mod_depth = p->mod_depth;
+ float wet_dry = p->wet_dry;
+ int i, c;
+ int mask = MAX_CHORUS_LENGTH - 1;
+ uint32_t sphase = (uint32_t)(p->sphase * 65536.0 * 65536.0 / 360);
+ uint32_t dphase = (uint32_t)(p->lfo_freq * m->tp32dsr);
+ const int fracbits = 32 - 11;
+ const int fracscale = 1 << fracbits;
+
+ for (c = 0; c < 2; c++)
+ {
+ int pos = m->pos;
+ uint32_t phase = m->phase + c * sphase;
+ for (i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ float dry = inputs[c][i];
+ float v0 = sine_table[phase >> fracbits];
+ float v1 = sine_table[1 + (phase >> fracbits)];
+ float lfo = v0 + (v1 - v0) * ((phase & (fracscale - 1)) * (1.0 / fracscale));
+
+ m->storage[pos & mask][c] = dry;
+
+ float dva = min_delay + mod_depth * lfo;
+ int dv = (int)dva;
+ float frac = dva - dv;
+ float smp0 = m->storage[(pos - dv) & mask][c];
+ float smp1 = m->storage[(pos - dv - 1) & mask][c];
+
+ float smp = smp0 + (smp1 - smp0) * frac;
+
+ outputs[c][i] = sanef(dry + (smp - dry) * wet_dry);
+
+ pos++;
+ phase += dphase;
+ }
+ }
+
+ m->phase += CBOX_BLOCK_SIZE * dphase;
+ m->pos += CBOX_BLOCK_SIZE;
+}
+
+MODULE_SIMPLE_DESTROY_FUNCTION(chorus)
+
+MODULE_CREATE_FUNCTION(chorus)
+{
+ static int inited = 0;
+ int i;
+ if (!inited)
+ {
+ inited = 1;
+ for (i = 0; i < 2049; i++)
+ sine_table[i] = 1 + sin(i * M_PI / 1024);
+ }
+
+ struct chorus_module *m = malloc(sizeof(struct chorus_module));
+ CALL_MODULE_INIT(m, 2, 2, chorus);
+ m->module.process_event = chorus_process_event;
+ m->module.process_block = chorus_process_block;
+ m->pos = 0;
+ m->phase = 0;
+ m->tp32dsr = 65536.0 * 65536.0 * m->module.srate_inv;
+ struct chorus_params *p = malloc(sizeof(struct chorus_params));
+ m->params = p;
+ p->sphase = cbox_config_get_float(cfg_section, "stereo_phase", 90.f);
+ p->lfo_freq = cbox_config_get_float(cfg_section, "lfo_freq", 1.f);
+ p->min_delay = cbox_config_get_float(cfg_section, "min_delay", 20.f);
+ p->mod_depth = cbox_config_get_float(cfg_section, "mod_depth", 15.f);
+ p->wet_dry = cbox_config_get_float(cfg_section, "wet_dry", 0.5f);
+ for (i = 0; i < MAX_CHORUS_LENGTH; i++)
+ m->storage[i][0] = m->storage[i][1] = 0.f;
+
+ return &m->module;
+}
+
+
+struct cbox_module_keyrange_metadata chorus_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata chorus_controllers[] = {
+};
+
+DEFINE_MODULE(chorus, 2, 2)
+
diff --git a/template/calfbox/cleanpythonbuild.sh b/template/calfbox/cleanpythonbuild.sh
new file mode 100755
index 0000000..de408d9
--- /dev/null
+++ b/template/calfbox/cleanpythonbuild.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+make clean
+make distclean
+rm build -rf
+set -e
+sh autogen.sh
+./configure --prefix=/usr --without-python
+make
+python3 setup.py build
+sudo python3 setup.py install
+sudo make install
diff --git a/template/calfbox/cmd.c b/template/calfbox/cmd.c
new file mode 100644
index 0000000..775b24e
--- /dev/null
+++ b/template/calfbox/cmd.c
@@ -0,0 +1,208 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "blob.h"
+#include "cmd.h"
+#include "dom.h"
+#include "errors.h"
+#include
+#include
+#include
+#include
+#include
+#include
+
+void cbox_command_target_init(struct cbox_command_target *ct, cbox_process_cmd cmd, void *user_data)
+{
+ ct->process_cmd = cmd;
+ ct->user_data = user_data;
+}
+
+gboolean cbox_execute_on(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd_name, const char *args, GError **error, ...)
+{
+ va_list av;
+
+ va_start(av, error);
+ gboolean res = cbox_execute_on_v(ct, fb, cmd_name, args, av, error);
+ va_end(av);
+ return res;
+}
+
+gboolean cbox_execute_on_v(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd_name, const char *args, va_list av, GError **error)
+{
+ int argcount = 0;
+ struct cbox_osc_command cmd;
+ uint8_t *extra_data;
+ // XXXKF might be not good enough for weird platforms
+ uint32_t unit_size = sizeof(double);
+ // this must be a power of 2 to guarantee proper alignment
+ assert(unit_size >= sizeof(int) && (unit_size == 4 || unit_size == 8));
+ cmd.command = cmd_name;
+ cmd.arg_types = args;
+ for (int i = 0; args[i]; i++)
+ argcount = i + 1;
+ // contains pointers to all the values, plus values themselves in case of int/double
+ // (casting them to pointers is ugly, and va_arg does not return a lvalue)
+ cmd.arg_values = malloc(sizeof(void *) * argcount + unit_size * argcount);
+ extra_data = (uint8_t *)&cmd.arg_values[argcount];
+
+ for (int i = 0; i < argcount; i++)
+ {
+ int iv;
+ double fv;
+ void *pv = extra_data + unit_size * i;
+ switch(args[i])
+ {
+ case 's':
+ cmd.arg_values[i] = va_arg(av, char *);
+ break;
+ case 'i':
+ iv = va_arg(av, int);
+ memcpy(pv, &iv, sizeof(int));
+ cmd.arg_values[i] = pv;
+ break;
+ case 'f': // double really
+ fv = (double)va_arg(av, double);
+ memcpy(pv, &fv, sizeof(double));
+ cmd.arg_values[i] = pv;
+ break;
+ case 'b':
+ cmd.arg_values[i] = va_arg(av, struct cbox_blob *);
+ break;
+ case 'o':
+ cmd.arg_values[i] = va_arg(av, struct cbox_objhdr *);
+ break;
+ case 'u':
+ cmd.arg_values[i] = va_arg(av, struct cbox_uuid *);
+ break;
+ default:
+ g_error("Invalid format character '%c' for command '%s'", args[i], cmd_name);
+ assert(0);
+ }
+ }
+ gboolean result = ct->process_cmd(ct, fb, &cmd, error);
+ free(cmd.arg_values);
+ return result;
+}
+
+gboolean cbox_osc_command_dump(const struct cbox_osc_command *cmd)
+{
+ g_message("Command = %s, args = %s", cmd->command, cmd->arg_types);
+ for (int i = 0; cmd->arg_types[i]; i++)
+ {
+ switch(cmd->arg_types[i])
+ {
+ case 's':
+ g_message("Args[%d] = '%s'", i, (const char *)cmd->arg_values[i]);
+ break;
+ case 'o':
+ {
+ struct cbox_objhdr *oh = cmd->arg_values[i];
+ char buf[40];
+ uuid_unparse(oh->instance_uuid.uuid, buf);
+ g_message("Args[%d] = uuid:'%s'", i, buf);
+ break;
+ }
+ case 'i':
+ g_message("Args[%d] = %d", i, *(int *)cmd->arg_values[i]);
+ break;
+ case 'f':
+ g_message("Args[%d] = %f", i, *(double *)cmd->arg_values[i]);
+ break;
+ case 'b':
+ {
+ struct cbox_blob *b = cmd->arg_values[i];
+ g_message("Args[%d] = (%p, %d)", i, b->data, (int)b->size);
+ break;
+ }
+ default:
+ g_error("Invalid format character '%c' for command '%s'", cmd->arg_types[i], cmd->command);
+ assert(0);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+gboolean cbox_check_fb_channel(struct cbox_command_target *fb, const char *command, GError **error)
+{
+ if (fb)
+ return TRUE;
+
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Feedback channel required for command '%s'", command);
+ return FALSE;
+}
+
+
+gboolean cbox_execute_sub(struct cbox_command_target *ct, struct cbox_command_target *fb, const struct cbox_osc_command *cmd, const char *new_command, GError **error)
+{
+ struct cbox_osc_command subcmd;
+ subcmd.command = new_command;
+ subcmd.arg_types = cmd->arg_types;
+ subcmd.arg_values = cmd->arg_values;
+ return ct->process_cmd(ct, fb, &subcmd, error);
+}
+
+gboolean cbox_parse_path_part_int(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, int *index, int min_index, int max_index, GError **error)
+{
+ char *numcopy = NULL;
+ if (!cbox_parse_path_part_str(cmd, path, subcommand, &numcopy, error))
+ return FALSE;
+ if (!*subcommand)
+ return TRUE;
+ char *endptr = NULL;
+ *index = strtol(numcopy, &endptr, 10);
+ if (!*numcopy && *endptr)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid index %s for command %s", numcopy, cmd->command);
+ g_free(numcopy);
+ *subcommand = NULL;
+ return TRUE;
+ }
+ if (*index < min_index || *index > max_index)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Index %s out of range [%d, %d] for command %s", numcopy, min_index, max_index, cmd->command);
+ g_free(numcopy);
+ *subcommand = NULL;
+ return TRUE;
+ }
+ g_free(numcopy);
+ return TRUE;
+}
+
+gboolean cbox_parse_path_part_str(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, char **path_element, GError **error)
+{
+ *path_element = NULL;
+ *subcommand = NULL;
+ int plen = strlen(path);
+ if (!strncmp(cmd->command, path, plen))
+ {
+ const char *num = cmd->command + plen;
+ const char *slash = strchr(num, '/');
+ if (!slash)
+ {
+ cbox_set_command_error_with_msg(error, cmd, "needs at least one extra path element");
+ return TRUE;
+ }
+
+ *path_element = g_strndup(num, slash-num);
+ *subcommand = slash;
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/template/calfbox/cmd.h b/template/calfbox/cmd.h
new file mode 100644
index 0000000..fa34cdb
--- /dev/null
+++ b/template/calfbox/cmd.h
@@ -0,0 +1,64 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_CMD_H
+#define CBOX_CMD_H
+
+#include
+#include
+#include
+
+#define CBOX_ARG_I(cmd, idx) (*(int *)(cmd)->arg_values[(idx)])
+#define CBOX_ARG_S(cmd, idx) ((const char *)(cmd)->arg_values[(idx)])
+#define CBOX_ARG_B(cmd, idx) ((const struct cbox_blob *)(cmd)->arg_values[(idx)])
+#define CBOX_ARG_F(cmd, idx) (*(double *)(cmd)->arg_values[(idx)])
+#define CBOX_ARG_O(cmd, idx, src, class, error) cbox_document_get_object_by_text_uuid(CBOX_GET_DOCUMENT(src), (const char *)(cmd)->arg_values[(idx)], &CBOX_CLASS(class), (error))
+#define CBOX_ARG_S_ISNULL(cmd, idx) (0 == (const char *)(cmd)->arg_values[(idx)])
+
+struct cbox_command_target;
+
+struct cbox_osc_command
+{
+ const char *command;
+ const char *arg_types;
+ void **arg_values;
+};
+
+typedef gboolean (*cbox_process_cmd)(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
+
+struct cbox_command_target
+{
+ void *user_data;
+ cbox_process_cmd process_cmd;
+};
+
+void cbox_command_target_init(struct cbox_command_target *ct, cbox_process_cmd cmd, void *user_data);
+
+extern gboolean cbox_check_fb_channel(struct cbox_command_target *fb, const char *command, GError **error);
+
+extern gboolean cbox_execute_sub(struct cbox_command_target *ct, struct cbox_command_target *fb, const struct cbox_osc_command *cmd, const char *new_command, GError **error);
+extern gboolean cbox_execute_on(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd, const char *args, GError **error, ...);
+extern gboolean cbox_execute_on_v(struct cbox_command_target *ct, struct cbox_command_target *fb, const char *cmd, const char *args, va_list va, GError **error);
+
+extern gboolean cbox_osc_command_dump(const struct cbox_osc_command *cmd);
+
+// Note: this sets *subcommand to NULL on parse error; requires "/path/" as path
+extern gboolean cbox_parse_path_part_int(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, int *index, int min_index, int max_index, GError **error);
+extern gboolean cbox_parse_path_part_str(const struct cbox_osc_command *cmd, const char *path, const char **subcommand, char **path_element, GError **error);
+
+#endif
diff --git a/template/calfbox/compressor.c b/template/calfbox/compressor.c
new file mode 100644
index 0000000..891b27b
--- /dev/null
+++ b/template/calfbox/compressor.c
@@ -0,0 +1,155 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include "onepole-float.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MODULE_PARAMS compressor_params
+
+struct compressor_params
+{
+ float threshold;
+ float ratio;
+ float attack;
+ float release;
+ float makeup;
+};
+
+struct compressor_module
+{
+ struct cbox_module module;
+
+ struct compressor_params *params, *old_params;
+ struct cbox_onepolef_coeffs attack_lp, release_lp, fast_attack_lp;
+ struct cbox_onepolef_state tracker;
+ struct cbox_onepolef_state tracker2;
+};
+
+MODULE_PROCESSCMD_FUNCTION(compressor)
+{
+ struct compressor_module *m = (struct compressor_module *)ct->user_data;
+
+ EFFECT_PARAM("/makeup", "f", makeup, double, dB2gain_simple, -100, 100) else
+ EFFECT_PARAM("/threshold", "f", threshold, double, dB2gain_simple, -100, 100) else
+ EFFECT_PARAM("/ratio", "f", ratio, double, , 1, 100) else
+ EFFECT_PARAM("/attack", "f", attack, double, , 1, 1000) else
+ EFFECT_PARAM("/release", "f", release, double, , 1, 1000) else
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ return cbox_execute_on(fb, NULL, "/makeup", "f", error, gain2dB_simple(m->params->makeup))
+ && cbox_execute_on(fb, NULL, "/threshold", "f", error, gain2dB_simple(m->params->threshold))
+ && cbox_execute_on(fb, NULL, "/ratio", "f", error, m->params->ratio)
+ && cbox_execute_on(fb, NULL, "/attack", "f", error, m->params->attack)
+ && cbox_execute_on(fb, NULL, "/release", "f", error, m->params->release)
+ && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error)
+ ;
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void compressor_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct compressor_module *m = module->user_data;
+}
+
+void compressor_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct compressor_module *m = module->user_data;
+
+ if (m->params != m->old_params)
+ {
+ float scale = M_PI * 1000 / m->module.srate;
+ cbox_onepolef_set_lowpass(&m->fast_attack_lp, 2 * scale / m->params->attack);
+ cbox_onepolef_set_lowpass(&m->attack_lp, scale / m->params->attack);
+ cbox_onepolef_set_lowpass(&m->release_lp, scale / m->params->release);
+ m->old_params = m->params;
+ }
+
+ float threshold = m->params->threshold, invratio = 1.0 / m->params->ratio;
+ for (int i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ float left = inputs[0][i], right = inputs[1][i];
+ float sig = 0.5 * (fabs(left) > fabs(right) ? fabs(left) : fabs(right));
+
+ int falling = sig < m->tracker.y1 && sig < m->tracker.x1;
+ int rising_fast = sig > 4 * m->tracker.y1 && sig > 4 * m->tracker.x1;
+ sig = cbox_onepolef_process_sample(&m->tracker, falling ? &m->release_lp : (rising_fast && m->tracker.y1 ? &m->fast_attack_lp : &m->attack_lp), sig);
+ sig = cbox_onepolef_process_sample(&m->tracker2, falling ? &m->release_lp : (rising_fast && m->tracker2.y1 ? &m->fast_attack_lp : &m->attack_lp), sig);
+ float gain = 1.0;
+ if (sig > threshold)
+ gain = threshold * powf(sig / threshold, invratio) / sig;
+ gain *= m->params->makeup;
+
+ outputs[0][i] = left * gain;
+ outputs[1][i] = right * gain;
+ }
+}
+
+MODULE_SIMPLE_DESTROY_FUNCTION(compressor)
+
+MODULE_CREATE_FUNCTION(compressor)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ struct compressor_module *m = malloc(sizeof(struct compressor_module));
+ CALL_MODULE_INIT(m, 2, 2, compressor);
+ m->module.process_event = compressor_process_event;
+ m->module.process_block = compressor_process_block;
+
+ struct compressor_params *p = malloc(sizeof(struct compressor_params));
+ p->threshold = cbox_config_get_gain_db(cfg_section, "threshold", -12.0);
+ p->ratio = cbox_config_get_float(cfg_section, "ratio", 2.0);
+ p->attack = cbox_config_get_float(cfg_section, "attack", 5.0);
+ p->release = cbox_config_get_float(cfg_section, "release", 100.0);
+ p->makeup = cbox_config_get_gain_db(cfg_section, "makeup", 6.0);
+ m->params = p;
+ m->old_params = NULL;
+
+ cbox_onepolef_reset(&m->tracker);
+ cbox_onepolef_reset(&m->tracker2);
+
+ return &m->module;
+}
+
+
+struct cbox_module_keyrange_metadata compressor_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata compressor_controllers[] = {
+};
+
+DEFINE_MODULE(compressor, 2, 2)
+
diff --git a/template/calfbox/config-api.c b/template/calfbox/config-api.c
new file mode 100644
index 0000000..885635f
--- /dev/null
+++ b/template/calfbox/config-api.c
@@ -0,0 +1,357 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config-api.h"
+
+#include
+#include
+#include
+#include
+#include
+
+static GKeyFile *config_keyfile;
+static gchar *keyfile_name;
+static GStringChunk *cfg_strings = NULL;
+static GHashTable *config_sections_hash = NULL;
+
+void cbox_config_init(const char *override_file)
+{
+ const gchar *keyfiledirs[3];
+ const gchar *keyfilename = ".cboxrc";
+ GKeyFileFlags flags = G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS;
+ GError *error = NULL;
+ if (config_keyfile)
+ return;
+
+ config_sections_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+ cfg_strings = g_string_chunk_new(100);
+ config_keyfile = g_key_file_new();
+
+ // Allow virtual (in-memory) config by passing empty string
+ if (override_file && !*override_file)
+ {
+ keyfile_name = g_strdup("");
+ return;
+ }
+
+ keyfiledirs[0] = getenv("HOME");
+ keyfiledirs[1] = NULL;
+ // XXXKF add proper error handling
+
+ if (override_file)
+ {
+ if (!g_key_file_load_from_file(config_keyfile, override_file, flags, &error))
+ {
+ g_warning("Could not read user config: %s", error->message);
+ g_error_free(error);
+ }
+ else
+ {
+ keyfile_name = g_strdup(override_file);
+ g_message("User config pathname is %s", keyfile_name);
+ return;
+ }
+ }
+
+ if (!g_key_file_load_from_dirs(config_keyfile, keyfilename, keyfiledirs, &keyfile_name, flags, &error))
+ {
+ g_warning("Could not read cboxrc: %s, search dir = %s, filename = %s", error->message, keyfiledirs[0], keyfilename);
+ g_error_free(error);
+ }
+ else
+ {
+ g_message("Config pathname is %s", keyfile_name);
+ }
+}
+
+int cbox_config_has_section(const char *section)
+{
+ return section && g_key_file_has_group(config_keyfile, section);
+}
+
+char *cbox_config_get_string(const char *section, const char *key)
+{
+ return cbox_config_get_string_with_default(section, key, NULL);
+}
+
+void cbox_config_set_string(const char *section, const char *key, const char *value)
+{
+ g_key_file_set_string(config_keyfile, section, key, value);
+}
+
+char *cbox_config_permify(const char *temporary)
+{
+ return g_string_chunk_insert(cfg_strings, temporary);
+}
+
+char *cbox_config_get_string_with_default(const char *section, const char *key, char *def_value)
+{
+ if (section && key && g_key_file_has_key(config_keyfile, section, key, NULL))
+ {
+ gchar *tmp = g_key_file_get_string(config_keyfile, section, key, NULL);
+ gchar *perm = g_string_chunk_insert(cfg_strings, tmp);
+ g_free(tmp);
+ return perm;
+ }
+ else
+ {
+ return def_value ? g_string_chunk_insert(cfg_strings, def_value) : NULL;
+ }
+}
+
+int cbox_config_get_int(const char *section, const char *key, int def_value)
+{
+ GError *error = NULL;
+ int result;
+
+ if (!section || !key)
+ return def_value;
+ result = g_key_file_get_integer(config_keyfile, section, key, &error);
+ if (error)
+ {
+ g_error_free(error);
+ return def_value;
+ }
+ return result;
+}
+
+void cbox_config_set_int(const char *section, const char *key, int value)
+{
+ g_key_file_set_integer(config_keyfile, section, key, value);
+}
+
+float cbox_config_get_float(const char *section, const char *key, float def_value)
+{
+ GError *error = NULL;
+ float result;
+
+ if (!section || !key)
+ return def_value;
+ result = g_key_file_get_double(config_keyfile, section, key, &error);
+ if (error)
+ {
+ g_error_free(error);
+ return def_value;
+ }
+ return result;
+}
+
+void cbox_config_set_float(const char *section, const char *key, double value)
+{
+ g_key_file_set_double(config_keyfile, section, key, value);
+}
+
+float cbox_config_get_gain(const char *section, const char *key, float def_value)
+{
+ GError *error = NULL;
+ float result;
+
+ if (!section || !key)
+ return def_value;
+ result = g_key_file_get_double(config_keyfile, section, key, &error);
+ if (error)
+ {
+ g_error_free(error);
+ return def_value;
+ }
+ return pow(2.0, result / 6.0);
+}
+
+float cbox_config_get_gain_db(const char *section, const char *key, float def_value)
+{
+ return cbox_config_get_gain(section, key, pow(2.0, def_value / 6.0));
+}
+
+void cbox_config_foreach_section(void (*process)(void *user_data, const char *section), void *user_data)
+{
+ gsize i, length = 0;
+ gchar **groups = g_key_file_get_groups (config_keyfile, &length);
+ if (!groups)
+ return;
+ for (i = 0; i < length; i++)
+ {
+ process(user_data, groups[i]);
+ }
+ g_strfreev(groups);
+}
+
+void cbox_config_foreach_key(void (*process)(void *user_data, const char *key), const char *section, void *user_data)
+{
+ gsize i, length = 0;
+ gchar **keys = g_key_file_get_keys (config_keyfile, section, &length, NULL);
+ if (!keys)
+ return;
+ for (i = 0; i < length; i++)
+ {
+ process(user_data, keys[i]);
+ }
+ g_strfreev(keys);
+}
+
+int cbox_config_remove_section(const char *section)
+{
+ return 0 != g_key_file_remove_group(config_keyfile, section, NULL);
+}
+
+int cbox_config_remove_key(const char *section, const char *key)
+{
+ return 0 != g_key_file_remove_key(config_keyfile, section, key, NULL);
+}
+
+gboolean cbox_config_save(const char *filename, GError **error)
+{
+ gsize len = 0;
+ gchar *data = g_key_file_to_data(config_keyfile, &len, error);
+ if (!data)
+ return FALSE;
+
+ if (filename == NULL)
+ filename = keyfile_name;
+
+ gboolean ok = g_file_set_contents(filename, data, len, error);
+ g_free(data);
+ return ok;
+}
+
+struct cbox_cfgfile
+{
+ gchar *libname;
+ gchar *filename;
+ GKeyFile *keyfile;
+};
+
+struct cbox_cfgfile *cbox_cfgfile_get_by_libname(const char *name)
+{
+ // XXXKF implement
+ return NULL;
+}
+
+struct cbox_sectref
+{
+ struct cbox_cfgfile *cfgfile;
+ gchar *section;
+};
+
+static struct cbox_sectref *cbox_sectref_lookup(const char *name)
+{
+ struct cbox_sectref *sr = g_hash_table_lookup(config_sections_hash, name);
+ return sr;
+}
+
+static void cbox_sectref_keep(struct cbox_sectref *sect)
+{
+ gchar *tmp = g_strdup_printf("%s@%s", sect->section, sect->cfgfile->libname);
+ g_hash_table_insert(config_sections_hash, tmp, sect);
+ g_free(tmp);
+}
+
+static void cbox_sectref_set_from_string(struct cbox_sectref *sr, const gchar *refname, struct cbox_cfgfile *default_lib)
+{
+ const gchar *p = strchr(refname, '@');
+ if (p)
+ {
+ sr->section = g_strndup(refname, p - refname);
+ sr->cfgfile = cbox_cfgfile_get_by_libname(p + 1);
+ }
+ else
+ {
+ sr->section = g_strndup(refname, p - refname);
+ sr->cfgfile = default_lib;
+ }
+}
+
+// find the section 'prefix+refname.section' in the config file - either the one referenced by sect, or refname.file
+struct cbox_sectref *cbox_config_sectref(struct cbox_sectref *sect, const char *prefix, const char *refname)
+{
+ if (!prefix)
+ prefix = "";
+ gchar *tmpsect = NULL;
+ const char *p = strchr(refname, '@');
+ if (p)
+ tmpsect = g_strdup_printf("%s%s", prefix, refname);
+ else
+ tmpsect = g_strdup_printf("%s%s@%s", prefix, refname, sect->cfgfile->libname);
+ struct cbox_sectref *sr = cbox_sectref_lookup(tmpsect);
+ if (sr)
+ {
+ g_free(tmpsect);
+ return sr;
+ }
+ sr = malloc(sizeof(struct cbox_sectref));
+ cbox_sectref_set_from_string(sr, tmpsect, sect ? sect->cfgfile : NULL);
+ g_free(tmpsect);
+ cbox_sectref_keep(sr);
+ return sr;
+}
+
+struct cbox_sectref *cbox_config_get_sectref(struct cbox_sectref *sect, const char *prefix, const char *key)
+{
+ if (!key || !sect)
+ return NULL;
+ //const char *sectname = cbox_config_get_string(sect, key);
+ const char *sectname = cbox_config_get_string(sect->section, key);
+ if (!sectname)
+ return NULL;
+ return cbox_config_sectref(sect, prefix, sectname);
+}
+
+struct cbox_sectref *cbox_config_get_sectref_n(struct cbox_sectref *sect, const char *prefix, const char *key, int index)
+{
+ if (!key || !sect)
+ return NULL;
+ gchar *tmp = g_strdup_printf("%s%d", key, index);
+ struct cbox_sectref *sr = cbox_config_get_sectref(sect, prefix, tmp);
+ g_free(tmp);
+ return sr;
+}
+
+struct cbox_sectref *cbox_config_get_sectref_suffix(struct cbox_sectref *sect, const char *prefix, const char *key, const char *suffix)
+{
+ if (!key || !sect)
+ return NULL;
+ gchar *tmp = g_strdup_printf("%s%s", key, suffix ? suffix : "");
+ struct cbox_sectref *sr = cbox_config_get_sectref(sect, prefix, tmp);
+ g_free(tmp);
+ return sr;
+}
+
+void cbox_config_close()
+{
+ if (config_sections_hash)
+ {
+ g_hash_table_destroy(config_sections_hash);
+ config_sections_hash = NULL;
+ }
+ if (config_keyfile)
+ {
+ g_key_file_free(config_keyfile);
+ config_keyfile = NULL;
+ }
+ if (cfg_strings)
+ {
+ g_string_chunk_free(cfg_strings);
+ cfg_strings = NULL;
+ }
+ if (keyfile_name)
+ {
+ g_free(keyfile_name);
+ keyfile_name = NULL;
+ }
+}
+
diff --git a/template/calfbox/config-api.h b/template/calfbox/config-api.h
new file mode 100644
index 0000000..d021cd5
--- /dev/null
+++ b/template/calfbox/config-api.h
@@ -0,0 +1,53 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_CONFIG_API_H
+#define CBOX_CONFIG_API_H
+
+#include
+
+struct cbox_sectref;
+
+extern void cbox_config_init(const char *override_file);
+extern int cbox_config_has_section(const char *section);
+extern char *cbox_config_get_string(const char *section, const char *key);
+extern char *cbox_config_get_string_with_default(const char *section, const char *key, char *def_value);
+extern int cbox_config_get_int(const char *section, const char *key, int def_value);
+extern float cbox_config_get_float(const char *section, const char *key, float def_value);
+extern float cbox_config_get_gain(const char *section, const char *key, float def_value);
+extern float cbox_config_get_gain_db(const char *section, const char *key, float def_value);
+extern void cbox_config_foreach_section(void (*process)(void *user_data, const char *section), void *user_data);
+extern void cbox_config_foreach_key(void (*process)(void *user_data, const char *key), const char *section, void *user_data);
+extern char *cbox_config_permify(const char *temporary);
+
+extern void cbox_config_set_string(const char *section, const char *key, const char *value);
+extern void cbox_config_set_int(const char *section, const char *key, int value);
+extern void cbox_config_set_float(const char *section, const char *key, double value);
+extern int cbox_config_remove_section(const char *section);
+extern int cbox_config_remove_key(const char *section, const char *key);
+
+extern gboolean cbox_config_save(const char *filename, GError **error);
+
+extern struct cbox_sectref *cbox_config_sectref(struct cbox_sectref *def_sect, const char *prefix, const char *refname);
+extern struct cbox_sectref *cbox_config_get_sectref(struct cbox_sectref *sect, const char *prefix, const char *key);
+extern struct cbox_sectref *cbox_config_get_sectref_n(struct cbox_sectref *sect, const char *prefix, const char *key, int index);
+extern struct cbox_sectref *cbox_config_get_sectref_suffix(struct cbox_sectref *sect, const char *prefix, const char *key, const char *suffix);
+
+extern void cbox_config_close(void);
+
+#endif
diff --git a/template/calfbox/configure.ac b/template/calfbox/configure.ac
new file mode 100644
index 0000000..d6bdbaa
--- /dev/null
+++ b/template/calfbox/configure.ac
@@ -0,0 +1,160 @@
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.63)
+AC_INIT([calfbox],[0.0.3],[wdev@foltman.com])
+AC_CONFIG_HEADER([config.h])
+LT_INIT([])
+LT_LANG([C])
+
+AM_INIT_AUTOMAKE(1.8)
+
+if test "x$prefix" = "xNONE"; then
+ prefix=$ac_default_prefix
+fi
+
+# Checks for programs.
+AC_PROG_CC_C99
+AC_PROG_INSTALL
+PKG_PROG_PKG_CONFIG
+
+# Checks for headers.
+AC_HEADER_STDC
+
+# Set initial parameters
+PYTHON_ENABLED="yes"
+NCURSES_ENABLED="yes"
+JACK_ENABLED="yes"
+FLUIDSYNTH_ENABLED="yes"
+LIBSMF_ENABLED="yes"
+LIBUSB_ENABLED="yes"
+SSE_ENABLED="no"
+NEON_ENABLED="no"
+
+# Check options
+
+AC_MSG_CHECKING([whether to enable Python embedding])
+AC_ARG_WITH(python,
+AC_HELP_STRING([--without-python],[disable Python embedding]),
+ [if test "$withval" = "no"; then PYTHON_ENABLED="no"; fi],[])
+AC_MSG_RESULT($PYTHON_ENABLED)
+
+AC_MSG_CHECKING([whether to enable ncurses UI])
+AC_ARG_WITH(ncurses,
+AC_HELP_STRING([--without-ncurses],[disable ncurses ui]),
+ [if test "$withval" = "no"; then NCURSES_ENABLED="no"; fi],[])
+AC_MSG_RESULT($NCURSES_ENABLED)
+
+AC_MSG_CHECKING([whether to enable JACK I/O])
+AC_ARG_WITH(jack,
+AC_HELP_STRING([--without-jack],[disable JACK audio and MIDI]),
+ [if test "$withval" = "no"; then JACK_ENABLED="no"; fi],[])
+AC_MSG_RESULT($JACK_ENABLED)
+
+AC_MSG_CHECKING([whether to enable Fluidsynth])
+AC_ARG_WITH(fluidsynth,
+AC_HELP_STRING([--without-fluidsynth],[disable use of Fluidsynth]),
+ [if test "$withval" = "no"; then FLUIDSYNTH_ENABLED="no"; fi],[])
+AC_MSG_RESULT($FLUIDSYNTH_ENABLED)
+
+AC_MSG_CHECKING([whether to enable libsmf])
+AC_ARG_WITH(libsmf,
+AC_HELP_STRING([--without-libsmf],[disable use of libsmf]),
+ [if test "$withval" = "no"; then LIBSMF_ENABLED="no"; fi],[])
+AC_MSG_RESULT($LIBSMF_ENABLED)
+
+AC_MSG_CHECKING([whether to enable libusb])
+AC_ARG_WITH(libusb,
+AC_HELP_STRING([--without-libusb],[disable use of libusb]),
+ [if test "$withval" = "no"; then LIBUSB_ENABLED="no"; fi],[])
+AC_MSG_RESULT($LIBUSB_ENABLED)
+
+AC_MSG_CHECKING([whether to enable SSE (x86 family only)])
+AC_ARG_WITH(sse,
+AC_HELP_STRING([--with-sse],[enable use of SSE]),
+ [if test "$withval" = "yes"; then SSE_ENABLED="yes"; fi],[])
+AC_MSG_RESULT($SSE_ENABLED)
+
+AC_MSG_CHECKING([whether to enable NEON (ARM family only)])
+AC_ARG_WITH(neon,
+AC_HELP_STRING([--with-neon],[enable use of NEON]),
+ [if test "$withval" = "yes"; then NEON_ENABLED="yes"; fi],[])
+AC_MSG_RESULT($NEON_ENABLED)
+
+# Check dependencies
+AC_CHECK_HEADER(uuid/uuid.h, true, AC_MSG_ERROR([libuuid header (uuid/uuid.h) is required]))
+AC_CHECK_LIB(uuid, uuid_unparse, true, AC_MSG_ERROR([libuuid is required]))
+PKG_CHECK_MODULES(GLIB_DEPS, glib-2.0 >= 2.6, true, AC_MSG_ERROR([libglib-2.0 is required]))
+if test "$LIBUSB_ENABLED" = "yes"; then
+ PKG_CHECK_MODULES(LIBUSB_DEPS, libusb-1.0 >= 1.0, true, AC_MSG_ERROR([libusb-1.0 is required]))
+fi
+PKG_CHECK_MODULES(LIBSNDFILE_DEPS, sndfile, true, AC_MSG_ERROR([libsndfile is required]))
+if test "$NCURSES_ENABLED" = "yes"; then
+ PKG_CHECK_MODULES(NCURSES_DEPS, ncurses, true, AC_MSG_ERROR([libncurses is required]))
+fi
+
+if test "$FLUIDSYNTH_ENABLED" = "yes"; then
+ PKG_CHECK_MODULES(FLUIDSYNTH_DEPS, fluidsynth >= 1.0.8, true, AC_MSG_ERROR([fluidsynth 1.0.8 is required]))
+fi
+
+if test "$LIBSMF_ENABLED" = "yes"; then
+ PKG_CHECK_MODULES(LIBSMF_DEPS, smf >= 1.3, true, AC_MSG_ERROR([libsmf 1.3 is required (libsmf.sourceforge.net)]))
+fi
+
+if test "$JACK_ENABLED" = "yes"; then
+ PKG_CHECK_MODULES(JACK_DEPS, jack >= 0.116.0, true, AC_MSG_ERROR([JACK is required (or use --without-jack)]))
+ AC_CHECK_HEADER(jack/jack.h, true, AC_MSG_ERROR([JACK is required (or use --without-jack)]))
+ PKG_CHECK_MODULES(JACK_RENAME_PORT, jack >= 0.124.2 jack < 1.9.0, JACK_HAS_RENAME="yes", JACK_HAS_RENAME="no")
+ PKG_CHECK_MODULES(JACK2_RENAME_PORT, jack >= 1.9.11, JACK_HAS_RENAME="yes", JACK_HAS_RENAME_DUMMY="no")
+fi
+
+if test "$PYTHON_ENABLED" = "yes"; then
+ PKG_CHECK_MODULES(PYTHON_DEPS, python3 >= 3.0, true, AC_MSG_ERROR([python 3.0 or newer is required (or use --without-python)]))
+fi
+
+# Generate Automake conditionals
+AM_CONDITIONAL(USE_PYTHON, test "$PYTHON_ENABLED" = "yes")
+AM_CONDITIONAL(USE_NCURSES, test "$NCURSES_ENABLED" = "yes")
+AM_CONDITIONAL(USE_JACK, test "$JACK_ENABLED" = "yes")
+AM_CONDITIONAL(USE_FLUIDSYNTH, test "$FLUIDSYNTH_ENABLED" = "yes")
+AM_CONDITIONAL(USE_LIBSMF, test "$LIBSMF_ENABLED" = "yes")
+AM_CONDITIONAL(USE_LIBUSB, test "$LIBUSB_ENABLED" = "yes")
+AM_CONDITIONAL(USE_SSE, test "$SSE_ENABLED" = "yes")
+AM_CONDITIONAL(USE_NEON, test "$NEON_ENABLED" = "yes")
+
+# Generate config.h conditionals
+if test "$PYTHON_ENABLED" = "yes"; then
+ AC_DEFINE(USE_PYTHON, 1, [Python will be included])
+fi
+if test "$NCURSES_ENABLED" = "yes"; then
+ AC_DEFINE(USE_NCURSES, 1, [ncurses will be included])
+fi
+if test "$JACK_ENABLED" = "yes"; then
+ AC_DEFINE(USE_JACK, 1, [JACK I/O will be included])
+fi
+if test "$JACK_HAS_RENAME" = "yes"; then
+ AC_DEFINE(JACK_HAS_RENAME, 1, [JACK function jack_port_rename should be used instead of jack_port_set_name])
+fi
+if test "$FLUIDSYNTH_ENABLED" = "yes"; then
+ AC_DEFINE(USE_FLUIDSYNTH, 1, [Fluidsynth will be included])
+fi
+if test "$LIBSMF_ENABLED" = "yes"; then
+ AC_DEFINE(USE_LIBSMF, 1, [libsmf will be used])
+fi
+if test "$LIBUSB_ENABLED" = "yes"; then
+ AC_DEFINE(USE_LIBUSB, 1, [libusb will be used])
+fi
+if test "$LIBUSB_ENABLED" = "no" && test "$JACK_ENABLED" = "no"; then
+ AC_MSG_ERROR([Neither JACK nor libusb are enabled, at least one is required])
+fi
+if test "$SSE_ENABLED" = "yes"; then
+ AC_DEFINE(USE_SSE, 1, [x86 Streaming SIMD Extensions will be used])
+fi
+if test "$NEON_ENABLED" = "yes"; then
+ AC_DEFINE(USE_NEON, 1, [ARM NEON SIMD Extensions will be used])
+fi
+
+# Generate files
+AC_CONFIG_FILES([Makefile])
+
+AC_OUTPUT
diff --git a/template/calfbox/delay.c b/template/calfbox/delay.c
new file mode 100644
index 0000000..c48ad7f
--- /dev/null
+++ b/template/calfbox/delay.c
@@ -0,0 +1,139 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_DELAY_LENGTH 65536
+
+struct delay_params
+{
+ float time;
+ float wet_dry, fb_amt;
+};
+
+struct delay_module
+{
+ struct cbox_module module;
+
+ float storage[MAX_DELAY_LENGTH][2];
+ struct delay_params *params;
+ int pos;
+};
+
+#define MODULE_PARAMS delay_params
+
+MODULE_PROCESSCMD_FUNCTION(delay)
+{
+ struct delay_module *m = (struct delay_module *)ct->user_data;
+
+ EFFECT_PARAM("/time", "f", time, double, , 1, 1000) else
+ EFFECT_PARAM("/fb_amt", "f", fb_amt, double, , 0, 1) else
+ EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ return cbox_execute_on(fb, NULL, "/time", "f", error, m->params->time)
+ && cbox_execute_on(fb, NULL, "/fb_amt", "f", error, m->params->fb_amt)
+ && cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry)
+ && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error)
+ ;
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void delay_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct delay_module *m = (struct delay_module *)module;
+}
+
+void delay_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct delay_module *m = (struct delay_module *)module;
+
+ int pos = m->pos;
+ int dv = m->params->time * m->module.srate / 1000.0;
+ float dryamt = 1 - m->params->wet_dry;
+ float wetamt = m->params->wet_dry;
+ float fbamt = m->params->fb_amt;
+
+ for (int i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ float dry[2] = { inputs[0][i], inputs[1][i] };
+ float *delayed = &m->storage[pos & (MAX_DELAY_LENGTH - 1)][0];
+
+ float wet[2] = { dryamt * dry[0] + wetamt * delayed[0], dryamt * dry[1] + wetamt * delayed[1] };
+ float fb[2] = { dry[0] + fbamt * delayed[0], dry[1] + fbamt * delayed[1] };
+ outputs[0][i] = sanef(wet[0]);
+ outputs[1][i] = sanef(wet[1]);
+
+ float *wcell = &m->storage[(pos + dv) & (MAX_DELAY_LENGTH - 1)][0];
+ wcell[0] = sanef(fb[0]);
+ wcell[1] = sanef(fb[1]);
+ pos++;
+ }
+ m->pos = pos;
+}
+
+MODULE_SIMPLE_DESTROY_FUNCTION(delay)
+
+MODULE_CREATE_FUNCTION(delay)
+{
+ static int inited = 0;
+ int i;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ struct delay_module *m = malloc(sizeof(struct delay_module));
+ CALL_MODULE_INIT(m, 2, 2, delay);
+ struct delay_params *p = malloc(sizeof(struct delay_params));
+ m->params = p;
+ m->module.process_event = delay_process_event;
+ m->module.process_block = delay_process_block;
+ m->pos = 0;
+ p->time = cbox_config_get_float(cfg_section, "delay", 250);
+ p->wet_dry = cbox_config_get_float(cfg_section, "wet_dry", 0.3);
+ p->fb_amt = cbox_config_get_gain_db(cfg_section, "feedback_gain", -12.f);
+ for (i = 0; i < MAX_DELAY_LENGTH; i++)
+ m->storage[i][0] = m->storage[i][1] = 0.f;
+
+ return &m->module;
+}
+
+struct cbox_module_keyrange_metadata delay_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata delay_controllers[] = {
+};
+
+DEFINE_MODULE(delay, 2, 2)
+
diff --git a/template/calfbox/distortion.c b/template/calfbox/distortion.c
new file mode 100644
index 0000000..8251e89
--- /dev/null
+++ b/template/calfbox/distortion.c
@@ -0,0 +1,138 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MODULE_PARAMS distortion_params
+
+struct distortion_params
+{
+ float drive;
+ float shape;
+};
+
+struct distortion_module
+{
+ struct cbox_module module;
+
+ struct distortion_params *params, *old_params;
+};
+
+gboolean distortion_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct distortion_module *m = (struct distortion_module *)ct->user_data;
+
+ EFFECT_PARAM("/drive", "f", drive, double, dB2gain_simple, -36, 36) else
+ EFFECT_PARAM("/shape", "f", shape, double, , -1, 2) else
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ return cbox_execute_on(fb, NULL, "/drive", "f", error, gain2dB_simple(m->params->drive))
+ && cbox_execute_on(fb, NULL, "/shape", "f", error, m->params->shape)
+ && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error)
+ ;
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void distortion_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct distortion_module *m = module->user_data;
+}
+
+void distortion_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct distortion_module *m = module->user_data;
+
+ if (m->params != m->old_params)
+ {
+ // update calculated values
+ }
+
+ float drive = m->params->drive;
+ float shape = m->params->shape;
+
+ float a0 = shape;
+ float a1 = -2 * shape - 0.5;
+ float a2 = 1.5 + shape;
+
+ float post = pow(drive, -0.7);
+
+ for (int i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ for (int c = 0; c < 2; c++)
+ {
+ float val = inputs[c][i];
+
+ val *= drive;
+
+ if (fabs(val) > 1.0)
+ val = (val > 0) ? 1 : -1;
+ else
+ val = a0 * val * val * val * val * val + a1 * val * val * val + a2 * val;
+
+ outputs[c][i] = val * post;
+ }
+ }
+}
+
+MODULE_SIMPLE_DESTROY_FUNCTION(distortion)
+
+MODULE_CREATE_FUNCTION(distortion)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ struct distortion_module *m = malloc(sizeof(struct distortion_module));
+ CALL_MODULE_INIT(m, 2, 2, distortion);
+ m->module.process_event = distortion_process_event;
+ m->module.process_block = distortion_process_block;
+ struct distortion_params *p = malloc(sizeof(struct distortion_params));
+ p->drive = cbox_config_get_gain_db(cfg_section, "drive", 0.f);
+ p->shape = cbox_config_get_gain_db(cfg_section, "shape", 0.f);
+ m->params = p;
+ m->old_params = NULL;
+
+ return &m->module;
+}
+
+
+struct cbox_module_keyrange_metadata distortion_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata distortion_controllers[] = {
+};
+
+DEFINE_MODULE(distortion, 2, 2)
+
diff --git a/template/calfbox/dom.c b/template/calfbox/dom.c
new file mode 100644
index 0000000..b512623
--- /dev/null
+++ b/template/calfbox/dom.c
@@ -0,0 +1,372 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2012 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "cmd.h"
+#include "errors.h"
+#include "dom.h"
+
+#include
+#include
+#include
+#include
+
+static GHashTable *class_name_hash = NULL;
+
+struct cbox_class_per_document
+{
+ GList *instances;
+};
+
+struct cbox_document
+{
+ GHashTable *classes_per_document;
+ GHashTable *services_per_document;
+ GHashTable *uuids_per_document;
+ struct cbox_command_target cmd_target;
+ int item_ctr;
+ uint64_t generation_ctr;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void cbox_dom_init()
+{
+ class_name_hash = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+void cbox_dom_close()
+{
+ g_hash_table_destroy(class_name_hash);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+struct cbox_class *cbox_class_find_by_name(const char *name)
+{
+ assert(class_name_hash != NULL);
+ return g_hash_table_lookup(class_name_hash, name);
+}
+
+void cbox_class_register(struct cbox_class *class_ptr)
+{
+ assert(class_name_hash != NULL);
+ g_hash_table_insert(class_name_hash, (gpointer)class_ptr->name, class_ptr);
+}
+
+static struct cbox_class_per_document *get_cpd_for_class(struct cbox_document *doc, struct cbox_class *class_ptr)
+{
+ struct cbox_class_per_document *p = g_hash_table_lookup(doc->classes_per_document, class_ptr);
+ if (p != NULL)
+ return p;
+ p = malloc(sizeof(struct cbox_class_per_document));
+ p->instances = NULL;
+ g_hash_table_insert(doc->classes_per_document, class_ptr, p);
+ return p;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void cbox_uuid_clear(struct cbox_uuid *uuid)
+{
+ uuid_clear(uuid->uuid);
+}
+
+guint cbox_uuid_hash(gconstpointer v)
+{
+ char buf[40];
+ uuid_unparse_lower(((struct cbox_uuid *)v)->uuid, buf);
+ return g_str_hash(buf);
+}
+
+gboolean cbox_uuid_equal(gconstpointer v1, gconstpointer v2)
+{
+ const struct cbox_uuid *p1 = v1;
+ const struct cbox_uuid *p2 = v2;
+
+ return !uuid_compare(p1->uuid, p2->uuid);
+}
+
+void cbox_uuid_copy(struct cbox_uuid *vto, const struct cbox_uuid *vfrom)
+{
+ uuid_copy(vto->uuid, vfrom->uuid);
+}
+
+gboolean cbox_uuid_report_as(struct cbox_uuid *uuid, const char *cmd, struct cbox_command_target *fb, GError **error)
+{
+ if (!fb)
+ return TRUE;
+ return cbox_execute_on(fb, NULL, cmd, "u", error, uuid->uuid);
+}
+
+gboolean cbox_uuid_report(struct cbox_uuid *uuid, struct cbox_command_target *fb, GError **error)
+{
+ return cbox_uuid_report_as(uuid, "/uuid", fb, error);
+}
+
+gboolean cbox_uuid_fromstring(struct cbox_uuid *uuid, const char *str, GError **error)
+{
+ if (uuid_parse(str, uuid->uuid))
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Malformed UUID: '%s'", str);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void cbox_uuid_tostring(struct cbox_uuid *uuid, char str[40])
+{
+ uuid_unparse(uuid->uuid, str);
+}
+
+void cbox_uuid_generate(struct cbox_uuid *uuid)
+{
+ uuid_generate(uuid->uuid);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void cbox_object_register_instance(struct cbox_document *doc, struct cbox_objhdr *obj)
+{
+ assert(obj != NULL);
+
+ struct cbox_class_per_document *cpd = get_cpd_for_class(doc, obj->class_ptr);
+ cpd->instances = g_list_prepend(cpd->instances, obj);
+ obj->owner = doc;
+ obj->link_in_document = cpd->instances;
+ g_hash_table_insert(obj->owner->uuids_per_document, &obj->instance_uuid, obj);
+}
+
+struct cbox_command_target *cbox_object_get_cmd_target(struct cbox_objhdr *hdr_ptr)
+{
+ if (!hdr_ptr->class_ptr->getcmdtargetfunc)
+ return NULL;
+ return hdr_ptr->class_ptr->getcmdtargetfunc(hdr_ptr);
+}
+
+gboolean cbox_object_try_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, gboolean *result, GError **error)
+{
+ // XXXKF this assumes objhdr ptr == object ptr - needs to add the header offset in cmd target?
+ struct cbox_objhdr *obj = ct->user_data;
+ if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_object_default_status(obj, fb, error))
+ {
+ *result = FALSE;
+ return TRUE;
+ }
+ return FALSE;
+ }
+ if (!strcmp(subcmd, "/delete") && !strcmp(cmd->arg_types, ""))
+ {
+ cbox_object_destroy(obj);
+ *result = TRUE;
+ return TRUE;
+ }
+ if (!strcmp(subcmd, "/get_uuid") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ {
+ *result = FALSE;
+ return TRUE;
+ }
+
+ *result = cbox_uuid_report(&obj->instance_uuid, fb, error);
+ return TRUE;
+ }
+ if (!strcmp(subcmd, "/get_class_name") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ {
+ *result = FALSE;
+ return TRUE;
+ }
+ *result = cbox_execute_on(fb, NULL, "/class_name", "s", error, obj->class_ptr->name);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean cbox_object_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ gboolean result = FALSE;
+ if (cbox_object_try_default_process_cmd(ct, fb, cmd, cmd->command, &result, error))
+ return result;
+ struct cbox_objhdr *obj = ct->user_data;
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s' for object class '%s'", cmd->command, cmd->arg_types, obj->class_ptr->name);
+ return FALSE;
+}
+
+gboolean cbox_object_default_status(struct cbox_objhdr *objhdr, struct cbox_command_target *fb, GError **error)
+{
+ char buf[40];
+ uuid_unparse(objhdr->instance_uuid.uuid, buf);
+ return cbox_execute_on(fb, NULL, "/uuid", "s", error, buf);
+}
+
+void cbox_object_destroy(struct cbox_objhdr *hdr_ptr)
+{
+ struct cbox_class_per_document *cpd = get_cpd_for_class(hdr_ptr->owner, hdr_ptr->class_ptr);
+ cpd->instances = g_list_delete_link(cpd->instances, hdr_ptr->link_in_document);
+ hdr_ptr->link_in_document = NULL;
+ g_hash_table_remove(hdr_ptr->owner->uuids_per_document, &hdr_ptr->instance_uuid);
+
+ hdr_ptr->class_ptr->destroyfunc(hdr_ptr);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+static gboolean document_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ char *uuid;
+ const char *subcommand;
+ if (!strcmp(cmd->command, "/dump") && !strcmp(cmd->arg_types, ""))
+ {
+ struct cbox_document *doc = ct->user_data;
+ cbox_document_dump(doc);
+ return TRUE;
+ }
+ if (cbox_parse_path_part_str(cmd, "/uuid/", &subcommand, &uuid, error))
+ {
+ struct cbox_document *doc = ct->user_data;
+ if (!subcommand)
+ return FALSE;
+ struct cbox_objhdr *obj = cbox_document_get_object_by_text_uuid(doc, uuid, NULL, error);
+ g_free(uuid);
+ if (!obj)
+ return FALSE;
+ struct cbox_command_target *ct2 = cbox_object_get_cmd_target(obj);
+ return cbox_execute_sub(ct2, fb, cmd, subcommand, error);
+ }
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types);
+ return FALSE;
+}
+
+struct cbox_document *cbox_document_new()
+{
+ struct cbox_document *res = malloc(sizeof(struct cbox_document));
+ res->classes_per_document = g_hash_table_new_full(NULL, NULL, NULL, g_free);
+ res->services_per_document = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+ res->uuids_per_document = g_hash_table_new(cbox_uuid_hash, cbox_uuid_equal);
+ res->cmd_target.process_cmd = document_process_cmd;
+ res->cmd_target.user_data = res;
+ res->item_ctr = 0;
+ res->generation_ctr = 1000; // start with non-zero value just to spot invalid values more easily
+
+ return res;
+}
+
+struct cbox_command_target *cbox_document_get_cmd_target(struct cbox_document *doc)
+{
+ return &doc->cmd_target;
+}
+
+struct cbox_objhdr *cbox_document_get_service(struct cbox_document *document, const char *name)
+{
+ return g_hash_table_lookup(document->services_per_document, name);
+}
+
+void cbox_document_set_service(struct cbox_document *document, const char *name, struct cbox_objhdr *obj)
+{
+ g_hash_table_insert(document->services_per_document, g_strdup(name), obj);
+}
+
+struct cbox_objhdr *cbox_document_get_object_by_uuid(struct cbox_document *doc, const struct cbox_uuid *uuid)
+{
+ return g_hash_table_lookup(doc->uuids_per_document, uuid);
+}
+
+struct cbox_objhdr *cbox_document_get_object_by_text_uuid(struct cbox_document *doc, const char *uuid, const struct cbox_class *class_ptr, GError **error)
+{
+ struct cbox_uuid uuidv;
+ if (!cbox_uuid_fromstring(&uuidv, uuid, error))
+ return NULL;
+ struct cbox_objhdr *obj = cbox_document_get_object_by_uuid(doc, &uuidv);
+ if (!obj)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "UUID not found: '%s'", uuid);
+ return NULL;
+ }
+ if (class_ptr && !cbox_class_is_a(obj->class_ptr, class_ptr))
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unexpected object type '%s' for UUID '%s' (expected '%s')", obj->class_ptr->name, uuid, class_ptr->name);
+ return NULL;
+ }
+ return obj;
+}
+
+static void iter_func(gpointer key, gpointer value, gpointer doc_)
+{
+#ifndef NDEBUG
+ struct cbox_document *doc = (struct cbox_document *)doc_;
+#endif
+ struct cbox_class *class_ptr = key;
+ struct cbox_class_per_document *cpd = value;
+ int first = 1;
+ printf("Class %s: ", class_ptr->name);
+ GList *l = cpd->instances;
+ while(l) {
+ if (!first)
+ printf(", ");
+ printf("%p", l->data);
+ fflush(stdout);
+ struct cbox_objhdr *hdr = (struct cbox_objhdr *)l->data;
+ char buf[40];
+ uuid_unparse(hdr->instance_uuid.uuid, buf);
+ printf("[%s]", buf);
+ fflush(stdout);
+ assert(cbox_document_get_object_by_uuid(doc, &hdr->instance_uuid));
+ l = l->next;
+ first = 0;
+ }
+ if (first)
+ printf("");
+ printf("\n");
+}
+
+static void iter_func2(gpointer key, gpointer value, gpointer document)
+{
+ struct cbox_objhdr *oh = value;
+ char buf[40];
+ uuid_unparse(oh->instance_uuid.uuid, buf);
+ printf("Service %s: %p", (const char *)key, value);
+ fflush(stdout);
+ printf("[%s]", buf);
+ fflush(stdout);
+ printf(" (%s)\n", oh->class_ptr->name);
+}
+
+void cbox_document_dump(struct cbox_document *document)
+{
+ g_hash_table_foreach(document->classes_per_document, iter_func, document);
+ g_hash_table_foreach(document->services_per_document, iter_func2, document);
+}
+
+uint64_t cbox_document_get_next_stamp(struct cbox_document *document)
+{
+ return document->generation_ctr;
+}
+
+void cbox_document_destroy(struct cbox_document *document)
+{
+ g_hash_table_destroy(document->classes_per_document);
+ g_hash_table_destroy(document->services_per_document);
+ g_hash_table_destroy(document->uuids_per_document);
+ free(document);
+}
+
+
diff --git a/template/calfbox/dom.h b/template/calfbox/dom.h
new file mode 100644
index 0000000..4f270bf
--- /dev/null
+++ b/template/calfbox/dom.h
@@ -0,0 +1,155 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2012 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_DOM_H
+#define CBOX_DOM_H
+
+#include
+#include
+#include
+
+struct cbox_command_target;
+struct cbox_osc_command;
+struct cbox_objhdr;
+struct cbox_document;
+struct GList;
+
+struct cbox_uuid
+{
+ uuid_t uuid;
+};
+
+extern void cbox_uuid_clear(struct cbox_uuid *uuid);
+extern guint cbox_uuid_hash(gconstpointer v);
+extern void cbox_uuid_copy(struct cbox_uuid *vto, const struct cbox_uuid *vfrom);
+extern gboolean cbox_uuid_equal(gconstpointer v1, gconstpointer v2);
+extern gboolean cbox_uuid_report(struct cbox_uuid *uuid, struct cbox_command_target *fb, GError **error);
+extern gboolean cbox_uuid_report_as(struct cbox_uuid *uuid, const char *cmd, struct cbox_command_target *fb, GError **error);
+extern gboolean cbox_uuid_fromstring(struct cbox_uuid *uuid, const char *str, GError **error);
+extern void cbox_uuid_tostring(struct cbox_uuid *uuid, char str[40]);
+extern void cbox_uuid_generate(struct cbox_uuid *uuid);
+
+struct cbox_class
+{
+ struct cbox_class *parent;
+ const char *name;
+ int hdr_offset;
+ void (*destroyfunc)(struct cbox_objhdr *objhdr);
+ struct cbox_command_target *(*getcmdtargetfunc)(struct cbox_objhdr *objhdr);
+};
+
+extern struct cbox_class *cbox_class_find_by_name(const char *name);
+extern void cbox_class_register(struct cbox_class *class_ptr);
+
+struct cbox_objhdr
+{
+ struct cbox_class *class_ptr;
+ struct cbox_document *owner;
+ void *link_in_document;
+ struct cbox_uuid instance_uuid;
+ uint64_t stamp;
+};
+
+static inline int cbox_class_is_a(const struct cbox_class *c1, const struct cbox_class *c2)
+{
+ while(c1 != c2 && c1->parent)
+ c1 = c1->parent;
+ return c1 == c2;
+}
+
+extern void cbox_object_register_instance(struct cbox_document *doc, struct cbox_objhdr *obj);
+extern struct cbox_command_target *cbox_object_get_cmd_target(struct cbox_objhdr *hdr_ptr);
+extern gboolean cbox_object_try_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, gboolean *result, GError **error);
+extern gboolean cbox_object_default_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
+extern gboolean cbox_object_default_status(struct cbox_objhdr *objhdr, struct cbox_command_target *fb, GError **error);
+
+extern void cbox_object_destroy(struct cbox_objhdr *hdr_ptr);
+
+extern struct cbox_document *cbox_document_new(void);
+extern void cbox_document_dump(struct cbox_document *);
+extern struct cbox_command_target *cbox_document_get_cmd_target(struct cbox_document *);
+extern struct cbox_objhdr *cbox_document_get_service(struct cbox_document *doc, const char *name);
+extern void cbox_document_set_service(struct cbox_document *doc, const char *name, struct cbox_objhdr *hdr_ptr);
+extern struct cbox_objhdr *cbox_document_get_object_by_uuid(struct cbox_document *doc, const struct cbox_uuid *uuid);
+extern struct cbox_objhdr *cbox_document_get_object_by_text_uuid(struct cbox_document *doc, const char *uuid, const struct cbox_class *class_ptr, GError **error);
+extern uint64_t cbox_document_get_next_stamp(struct cbox_document *doc);
+extern void cbox_document_destroy(struct cbox_document *);
+
+extern void cbox_dom_init(void);
+extern void cbox_dom_close(void);
+
+// must be the first field in the object-compatible struct
+#define CBOX_OBJECT_HEADER() \
+ struct cbox_objhdr _obj_hdr;
+
+#define CBOX_CLASS(class) CBOX_CLASS_##class
+
+#define CBOX_EXTERN_CLASS(class) \
+ extern struct cbox_class CBOX_CLASS(class);
+
+#define CBOX_GET_DOCUMENT(obj) \
+ ((obj)->_obj_hdr.owner)
+
+#define CBOX_STAMP(obj) \
+ ((obj)->_obj_hdr.stamp = cbox_document_get_next_stamp(CBOX_GET_DOCUMENT(obj)))
+
+#define CBOX_DELETE(obj) \
+ ((obj) && (cbox_object_destroy(&(obj)->_obj_hdr), 1))
+
+#define CBOX_IS_A(obj, class) \
+ ((obj) && cbox_class_is_a((obj)->_obj_hdr.class_ptr, &CBOX_CLASS(class)))
+
+#define CBOX_OBJECT_HEADER_INIT(self, class, document) \
+ do { \
+ (self)->_obj_hdr.class_ptr = &CBOX_CLASS_##class; \
+ (self)->_obj_hdr.owner = (document); \
+ (self)->_obj_hdr.link_in_document = NULL; \
+ (self)->_obj_hdr.stamp = cbox_document_get_next_stamp(document); \
+ uuid_generate((self)->_obj_hdr.instance_uuid.uuid); \
+ } while(0)
+
+#define CBOX_OBJECT_REGISTER(self) \
+ (cbox_object_register_instance((self)->_obj_hdr.owner, &(self)->_obj_hdr))
+
+#define CBOX_OBJECT_DEFAULT_STATUS(self, fb, error) \
+ (cbox_object_default_status(&(self)->_obj_hdr, (fb), (error)))
+
+#define CBOX_CLASS_DEFINITION_ROOT(class) \
+ static void class##_destroyfunc(struct cbox_objhdr *hdr_ptr); \
+ static struct cbox_command_target *class##_getcmdtarget(struct cbox_objhdr *hdr) { \
+ return &(((struct class *)hdr)->cmd_target);\
+ }; \
+ struct cbox_class CBOX_CLASS_##class = { \
+ .parent = NULL, \
+ .name = #class, \
+ .hdr_offset = offsetof(struct class, _obj_hdr), \
+ .destroyfunc = class##_destroyfunc, \
+ .getcmdtargetfunc = class##_getcmdtarget \
+ }; \
+
+#define CBOX_RETURN_OBJECT(result) \
+ return &(result)->_obj_hdr
+
+// Convert header to object, regardless of the relative position of the header.
+#define CBOX_H2O(hdr) \
+ (void *)(((char *)(hdr)) - (hdr)->class_ptr->hdr_offset)
+
+#define CBOX_O2H(obj) \
+ (&(*(obj))._obj_hdr)
+
+#endif
diff --git a/template/calfbox/drvjunk/miditest.py b/template/calfbox/drvjunk/miditest.py
new file mode 100644
index 0000000..a670167
--- /dev/null
+++ b/template/calfbox/drvjunk/miditest.py
@@ -0,0 +1,152 @@
+import array
+import binascii
+import usb.core
+import usb.util
+import time
+
+class USBMIDIConfiguration:
+ def __init__(self, cfg, ifno, ifalt):
+ self.cfg = cfg
+ self.ifno = ifno
+ self.ifalt = ifalt
+ def __str__(self):
+ return "cfg=%d ifno=%d ifalt=%d" % (self.cfg, self.ifno, self.ifalt)
+ def __repr__(self):
+ return "USBMIDIConfiguration(%d,%d,%d)" % (self.cfg, self.ifno, self.ifalt)
+
+class USBMIDIDeviceDescriptor:
+ def __init__(self, vendorID, productID, interfaces = None):
+ self.vendorID = vendorID
+ self.productID = productID
+ if interfaces is None:
+ self.interfaces = []
+ else:
+ self.interfaces = interfaces
+ def add_interface(self, config, ifno, ifalt):
+ self.interfaces.append(USBMIDIConfiguration(config, ifno, ifalt))
+ def has_interfaces(self):
+ return len(self.interfaces)
+ def __str__(self):
+ return "vid=%04x pid=%04x" % (self.vendorID, self.productID)
+ def __repr__(self):
+ return "USBMIDIDeviceDescriptor(0x%04x, 0x%04x, %s)" % (self.vendorID, self.productID, self.interfaces)
+
+class USBMIDI:
+ cin_sizes = [None, None, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1]
+ def __init__(self, mididev, midicfg, debug = False):
+ dev = usb.core.find(idVendor = mididev.vendorID, idProduct = mididev.productID)
+ self.dev = dev
+ intf = None
+ for cfgo in dev:
+ if cfgo.bConfigurationValue == midicfg.cfg:
+ cfgo.set()
+ intf = cfgo[(midicfg.ifno, midicfg.ifalt)]
+ if not intf:
+ raise ValueError, "Configuration %d not found" % midicfg.cfg
+ print intf.bNumEndpoints
+ self.epIn = None
+ self.epOut = None
+ for ep in intf:
+ if debug:
+ print "endpoint %x" % ep.bEndpointAddress
+ if ep.bEndpointAddress > 0x80:
+ if self.epIn is None:
+ self.epIn = ep
+ else:
+ if self.epOut is None:
+ self.epOut = ep
+
+ def read(self):
+ try:
+ data = self.epIn.read(self.epIn.wMaxPacketSize)
+ if data is None:
+ return None
+ return array.array('B', data)
+ except usb.core.USBError, e:
+ return None
+
+ def encode(self, port, msg):
+ a = array.array('B')
+ a.append(16 * port + (msg[0] >> 4))
+ a.fromlist(msg)
+ return a
+
+ def write(self, data):
+ self.epOut.write(data)
+
+ def parse(self, data):
+ i = 0
+ msgs = []
+ while i < len(data):
+ if data[i] == 0:
+ break
+ cin, cable_id = data[i] & 15, data[i] >> 4
+ msgs.append(data[i + 1 : i + 1 + KeyRig25.cin_sizes[cin]])
+ i += 4
+ return msgs
+
+ @staticmethod
+ def findall(vendorID = None, productID = None, debug = False):
+ dev_list = []
+ devices = usb.core.find(find_all = True)
+ for dev in devices:
+ if vendorID is not None and dev.idVendor != vendorID:
+ continue
+ if productID is not None and dev.idProduct != productID:
+ continue
+ thisdev = USBMIDIDeviceDescriptor(dev.idVendor, dev.idProduct)
+ if debug:
+ print "Device %04x:%04x, class %d" % (dev.idVendor, dev.idProduct, dev.bDeviceClass)
+ if dev.bDeviceClass == 0: # device defined at interface level
+ for cfg in dev:
+ if debug:
+ print "Configuration ", cfg.bConfigurationValue
+ for intf in cfg:
+ if debug:
+ print "Interface %d alternate-setting %d" % (intf.bInterfaceNumber, intf.bAlternateSetting)
+ print "Class %d subclass %d" % (intf.bInterfaceClass, intf.bInterfaceSubClass)
+ if intf.bInterfaceClass == 1 and intf.bInterfaceSubClass == 3:
+ if debug:
+ print "(%d,%d,%d): This is USB MIDI" % (cfg.bConfigurationValue, intf.bInterfaceNumber, intf.bAlternateSetting)
+ thisdev.add_interface(cfg.bConfigurationValue, intf.bInterfaceNumber, intf.bAlternateSetting)
+ if thisdev.has_interfaces():
+ dev_list.append(thisdev)
+ return dev_list
+
+ #print devices
+
+class KnownUSBMIDI(USBMIDI):
+ def __init__(self, vendorID, productID):
+ devlist = USBMIDI.findall(vendorID, productID, debug = False)
+ if not devlist:
+ raise ValueError
+ USBMIDI.__init__(self, devlist[0], devlist[0].interfaces[0])
+
+class KeyRig25(KnownUSBMIDI):
+ def __init__(self):
+ KnownUSBMIDI.__init__(self, vendorID = 0x763, productID = 0x115)
+
+class XMidi2x2(KnownUSBMIDI):
+ def __init__(self):
+ KnownUSBMIDI.__init__(self, vendorID = 0x41e, productID = 0x3f08)
+
+class LexiconOmega(KnownUSBMIDI):
+ def __init__(self):
+ KnownUSBMIDI.__init__(self, vendorID = 0x1210, productID = 2)
+
+print USBMIDI.findall()
+xmidi = XMidi2x2()
+xmidi.write(xmidi.encode(1, [0x90, 36, 100]))
+xmidi.write(xmidi.encode(1, [0x80, 36, 100]))
+
+#krig = KeyRig25()
+krig = LexiconOmega()
+while True:
+ data = krig.read()
+ if data is not None:
+ decoded = krig.parse(data)
+ reencoded = array.array('B')
+ for msg in decoded:
+ reencoded.extend(xmidi.encode(1, list(msg)))
+ xmidi.write(reencoded)
+ print decoded
diff --git a/template/calfbox/drvjunk/omega.c b/template/calfbox/drvjunk/omega.c
new file mode 100644
index 0000000..3611bb1
--- /dev/null
+++ b/template/calfbox/drvjunk/omega.c
@@ -0,0 +1,255 @@
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+// interface 1 altsetting 1 endpoint 01 (out) bits 16 channels 2 mps 192
+// interface 2 altsetting 1 endpoint 83 (in) bits 16 channels 2 mps 192
+
+int samples_captured = 0;
+int samples_played = 0;
+
+static struct libusb_context *usbctx;
+static int omega_timeout = 1000;
+
+int epOUT = 0x01, epIN = 0x83;
+
+void init_usb()
+{
+ libusb_init(&usbctx);
+ libusb_set_debug(usbctx, 3);
+}
+
+struct libusb_device_handle *open_omega()
+{
+ struct libusb_device_handle *handle;
+ handle = libusb_open_device_with_vid_pid(usbctx, 0x1210, 0x0002);
+ if (!handle)
+ {
+ printf("Lexicon Omega not found after reset.\n");
+ return NULL;
+ }
+ if (libusb_set_configuration(handle, 1))
+ {
+ libusb_close(handle);
+ return NULL;
+ }
+
+ if (libusb_claim_interface(handle, 1))
+ goto error;
+ if (libusb_claim_interface(handle, 2))
+ goto error;
+ if (libusb_claim_interface(handle, 7))
+ goto error;
+ if (libusb_set_interface_alt_setting(handle, 1, 1))
+ goto error;
+ if (libusb_set_interface_alt_setting(handle, 2, 1))
+ goto error;
+
+ return handle;
+
+error:
+ libusb_close(handle);
+ return NULL;
+}
+
+#define EP_CONTROL_UNDEFINED 0
+#define SAMPLING_FREQ_CONTROL 1
+#define PITCH_CONTROL 2
+
+#define SET_CUR 0x01
+#define GET_CUR 0x81
+#define SET_MIN 0x02
+#define GET_MIN 0x82
+#define SET_MAX 0x03
+#define GET_MAX 0x83
+#define SET_RES 0x04
+#define GET_RES 0x84
+#define SET_MEM 0x05
+#define GET_MEM 0x85
+#define GET_STAT 0xFF
+
+int configure_omega(struct libusb_device_handle *h, int sample_rate)
+{
+ uint8_t freq_data[3];
+ freq_data[0] = sample_rate & 0xFF;
+ freq_data[1] = (sample_rate & 0xFF00) >> 8;
+ freq_data[2] = (sample_rate & 0xFF0000) >> 16;
+ if (libusb_control_transfer(h, 0x22, 0x01, 256, epOUT, freq_data, 3, omega_timeout) != 3)
+ return -1;
+ if (libusb_control_transfer(h, 0x22, 0x01, 256, epIN, freq_data, 3, omega_timeout) != 3)
+ return -1;
+// libusb_control_transfer(dev, 0x22, 0x01,
+ return 0;
+}
+
+// 192 bytes = 1ms@48000 (48 samples)
+
+#define NUM_PLAY_BUFS 2
+#define PLAY_PACKET_COUNT 2
+#define PLAY_PACKET_SIZE 192
+
+static float phase = 0;
+static int phase2 = 0;
+
+static int desync = 0;
+static int samples_sent = 0;
+
+static int srate = 48000;
+
+void play_callback(struct libusb_transfer *transfer)
+{
+ int i;
+ //printf("Play Callback! %d %p status %d\n", (int)transfer->length, transfer->buffer, (int)transfer->status);
+
+ samples_played += transfer->length / 4;
+ int nsamps = srate / 1000;
+ if (desync >= 1000 * transfer->num_iso_packets && nsamps < PLAY_PACKET_SIZE/4)
+ nsamps++;
+ // printf("desync = %d nsamps = %d!\n", desync, nsamps);
+ int tlen = 0;
+ for (i = 0; i < transfer->num_iso_packets; i++)
+ {
+ tlen += transfer->iso_packet_desc[i].actual_length;
+ if (transfer->iso_packet_desc[i].status)
+ printf("ISO error: index = %d i = %d status = %d\n", (int)transfer->user_data, i, transfer->iso_packet_desc[i].status);
+ }
+ // printf("actual length = %d!\n", tlen);
+
+ transfer->length = nsamps * transfer->num_iso_packets * 4;
+ libusb_set_iso_packet_lengths(transfer, nsamps * 4);
+
+ desync += transfer->num_iso_packets * srate;
+ desync -= tlen / 4 * 1000;
+
+ int16_t *data = (int16_t*)transfer->buffer;
+ for (i = 0; i < transfer->length / 4; i ++)
+ {
+ float v = 16000 * sin(phase);
+ //phase += (phase2 & 4096) ? 0.02 : 0.04;
+ phase += (phase2 & 16384) ? 0.04: 0.02;
+ //phase += 0.2 * cos(phase2 / 16384.0);
+ phase2++;
+ if (phase > 2 * M_PI)
+ phase -= 2 * M_PI;
+ int vi = (int)v;
+ data[i * 2] = vi;
+ data[i * 2 + 1] = vi;
+ }
+ libusb_submit_transfer(transfer);
+}
+
+void play_stuff(struct libusb_device_handle *h, int index)
+{
+ struct libusb_transfer *t;
+ int i;
+ int packets = PLAY_PACKET_COUNT;
+ t = libusb_alloc_transfer(packets);
+ int tsize = srate * 4 / 1000;
+ uint8_t *buf = (uint8_t *)malloc(PLAY_PACKET_SIZE*packets);
+ //int ssf = 0;
+
+ for (i = 0; i < tsize * packets; i++)
+ buf[i] = 0;
+
+ libusb_fill_iso_transfer(t, h, epOUT, buf, tsize * packets, packets, play_callback, (void *)index, 20000);
+ libusb_set_iso_packet_lengths(t, tsize);
+ libusb_submit_transfer(t);
+}
+
+#define NUM_RECORD_BUFS 2
+#define NUM_REC_PACKETS 2
+#define REC_PACKET_SIZE 192
+
+static uint8_t *record_buffers[NUM_RECORD_BUFS];
+// struct libusb_transfer *record_transfers[NUM_RECORD_BUFS];
+
+float filt = 0;
+
+void record_callback(struct libusb_transfer *transfer)
+{
+ int i;
+ // printf("Record callback! %p index %d len %d\n", transfer, (int)transfer->user_data, transfer->length);
+ samples_captured += transfer->length / 4;
+
+ float avg = 0;
+ int16_t *bufz = (int16_t*)transfer->buffer;
+ int items = transfer->length / 4;
+ for (i = 0; i < items; i ++)
+ {
+ int16_t *buf = &bufz[i * 2];
+ if (fabs(buf[0]) > avg)
+ avg = fabs(buf[0]);
+ if (fabs(buf[1]) > avg)
+ avg = fabs(buf[1]);
+ }
+ if (avg)
+ printf("%12.6f dBFS\r", 6 * log(avg / 32767 / items) / log(2.0));
+ libusb_submit_transfer(transfer);
+}
+
+void record_stuff(struct libusb_device_handle *h, int index)
+{
+ // 0x02
+ struct libusb_transfer *t;
+
+ record_buffers[index] = (uint8_t*)malloc(NUM_REC_PACKETS * REC_PACKET_SIZE);
+ memset(record_buffers[index], 0, NUM_REC_PACKETS * REC_PACKET_SIZE);
+
+ t = libusb_alloc_transfer(NUM_REC_PACKETS);
+ libusb_fill_iso_transfer(t, h, epIN, record_buffers[index], NUM_REC_PACKETS * REC_PACKET_SIZE, NUM_REC_PACKETS, record_callback, (void *)index, 1000);
+ libusb_set_iso_packet_lengths(t, REC_PACKET_SIZE);
+ if (libusb_submit_transfer(t))
+ goto error;
+ return;
+error:
+ printf("Record setup failed for index=%d\n", index);
+}
+
+void usb_main_loop()
+{
+ struct sched_param p;
+ p.sched_priority = 10;
+ sched_setscheduler(0, SCHED_FIFO, &p);
+ while(1) {
+ struct timeval tv = {
+ .tv_sec = 0,
+ .tv_usec = 100
+ };
+ libusb_handle_events_timeout(usbctx, &tv);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct libusb_device_handle *h;
+ int i;
+
+ init_usb();
+ h = open_omega();
+ if (!h)
+ {
+ printf("Lexicon Omega could not be opened.\n");
+ return 2;
+ }
+
+ // 10: 4 3 3 - 16 bit
+ // 30: 2 2 1 2 2 2 1 - 24 bit
+ // 50: 4 3 3
+ // 70: 2 2 1 2 2 2 1
+ printf("Error = %d\n", configure_omega(h, srate));
+ usleep(1);
+ for (i = 0; i < NUM_PLAY_BUFS; i++)
+ play_stuff(h, i);
+ for (i = 0; i < NUM_RECORD_BUFS; i++)
+ record_stuff(h, i);
+ usb_main_loop();
+ return 0;
+}
+
diff --git a/template/calfbox/dspmath.h b/template/calfbox/dspmath.h
new file mode 100644
index 0000000..81c7ed5
--- /dev/null
+++ b/template/calfbox/dspmath.h
@@ -0,0 +1,238 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+#ifndef CBOX_DSPMATH_H
+#define CBOX_DSPMATH_H
+
+#define CBOX_BLOCK_SIZE 16
+
+#include
+#include
+#include
+#include
+#include
+
+#ifndef M_PI
+#include
+#define M_PI G_PI
+#endif
+
+typedef float cbox_sample_t;
+
+struct cbox_sincos
+{
+ float sine;
+ float cosine;
+ float prewarp;
+ float prewarp2;
+};
+
+static inline float hz2w(float hz, float sr)
+{
+ return M_PI * hz / (2 * sr);
+}
+
+static inline float cerp_naive(float v0, float v1, float v2, float v3, float f)
+{
+ float x0 = -1;
+ float x1 = 0;
+ float x2 = 1;
+ float x3 = 2;
+
+ float l0 = ((f - x1) * (f - x2) * (f - x3)) / ( (x0 - x1) * (x0 - x2) * (x0 - x3));
+ float l1 = ((f - x0) * (f - x2) * (f - x3)) / ((x1 - x0) * (x1 - x2) * (x1 - x3));
+ float l2 = ((f - x0) * (f - x1) * (f - x3)) / ((x2 - x0) * (x2 - x1) * (x2 - x3));
+ float l3 = ((f - x0) * (f - x1) * (f - x2)) / ((x3 - x0) * (x3 - x1) * (x3 - x2) );
+
+ return v0 * l0 + v1 * l1 + v2 * l2 + v3 * l3;
+}
+
+static inline float cerp(float v0, float v1, float v2, float v3, float f)
+{
+ f += 1;
+
+ float d0 = (f - 0);
+ float d1 = (f - 1);
+ float d2 = (f - 2);
+ float d3 = (f - 3);
+
+ float d03 = (d0 * d3) * (1.0 / 2.0);
+ float d12 = (d03 + 1) * (1.0 / 3.0);
+
+ float l0 = -d12 * d3;
+ float l1 = d03 * d2;
+ float l2 = -d03 * d1;
+ float l3 = d12 * d0;
+
+ float y = v0 * l0 + v1 * l1 + v2 * l2 + v3 * l3;
+ // printf("%f\n", y - cerp_naive(v0, v1, v2, v3, f - 1));
+ return y;
+}
+
+static inline float sanef(float v)
+{
+ if (fabs(v) < (1.0 / (65536.0 * 65536.0)))
+ return 0;
+ return v;
+}
+
+static inline void sanebf(float *buf)
+{
+ int i;
+ for (i = 0; i < CBOX_BLOCK_SIZE; i++)
+ buf[i] = sanef(buf[i]);
+}
+
+static inline void copybf(float *to, float *from)
+{
+ memcpy(to, from, sizeof(float) * CBOX_BLOCK_SIZE);
+}
+
+static inline float cent2factor(float cent)
+{
+ return powf(2.0, cent * (1.f / 1200.f)); // I think this may be optimised using exp()
+}
+
+static inline float dB2gain(float dB)
+{
+ return powf(2.f, dB * (1.f / 6.f));
+}
+
+static inline float dB2gain_simple(float dB)
+{
+ if (dB <= -96)
+ return 0;
+ return powf(2.f, dB * (1.f / 6.f));
+}
+
+static inline float gain2dB_simple(float gain)
+{
+ static const float sixoverlog2 = 8.656170245333781; // 6.0 / logf(2.f);
+ if (gain < (1.f / 65536.f))
+ return -96.f;
+ return sixoverlog2 * logf(gain);
+}
+
+static inline float deg2rad(float deg)
+{
+ return deg * (float)(M_PI / 180.f);
+}
+
+static inline float rad2deg(float rad)
+{
+ return rad * (float)(180.f / M_PI);
+}
+
+// Do a butterfly operation:
+// dst1 = src1 + e^iw_1*src2
+// dst2 = src1 + e^iw_2*src2 (w = phase * 2pi / ANALYSIS_BUFFER_SIZE)
+static inline void butterfly(complex float *dst1, complex float *dst2, complex float src1, complex float src2, complex float eiw1, complex float eiw2)
+{
+ *dst1 = src1 + eiw1 * src2;
+ *dst2 = src1 + eiw2 * src2;
+}
+
+struct cbox_gain
+{
+ float db_gain;
+ float lin_gain;
+ float old_lin_gain;
+ float pos;
+ float delta;
+};
+
+static inline void cbox_gain_init(struct cbox_gain *gain)
+{
+ gain->db_gain = 0;
+ gain->lin_gain = 1;
+ gain->old_lin_gain = 1;
+ gain->pos = 1;
+ gain->delta = 1 / (44100 * 0.1); // XXXKF ballpark
+}
+
+static inline void cbox_gain_set_db(struct cbox_gain *gain, float db)
+{
+ if (gain->db_gain == db)
+ return;
+ gain->db_gain = db;
+ gain->old_lin_gain = gain->old_lin_gain + (gain->lin_gain - gain->old_lin_gain) * gain->pos;
+ gain->lin_gain = dB2gain(db);
+ gain->pos = 0;
+}
+
+#define CBOX_GAIN_APPLY_LOOP(gain, nsamples, code) \
+{ \
+ double pos = (gain)->pos; \
+ double span = (gain)->lin_gain - (gain)->old_lin_gain; \
+ double start = (gain)->old_lin_gain; \
+ double step = (gain)->delta; \
+ if (pos >= 1) { \
+ double tgain = gain->lin_gain; \
+ for (uint32_t i = 0; i < (nsamples); ++i) { \
+ code(i, tgain) \
+ } \
+ } else { \
+ if (pos + (nsamples) * step < 1.0) { \
+ for (uint32_t i = 0; i < (nsamples); ++i) { \
+ double tgain = start + (pos + i * step) * span; \
+ code(i, tgain) \
+ } \
+ gain->pos += (nsamples) * step; \
+ } \
+ else { \
+ for (uint32_t i = 0; i < (nsamples); ++i) { \
+ code(i, (start + pos * span)) \
+ pos = (pos + step < 1.0 ? pos + step : 1.0); \
+ } \
+ gain->pos = 1.0; \
+ } \
+ } \
+}
+
+#define CBOX_GAIN_ADD_MONO(i, gain) \
+ dest1[i] += src1[i] * gain;
+
+static inline void cbox_gain_add_mono(struct cbox_gain *gain, float *dest1, const float *src1, uint32_t nsamples)
+{
+ CBOX_GAIN_APPLY_LOOP(gain, nsamples, CBOX_GAIN_ADD_MONO);
+}
+
+#define CBOX_GAIN_ADD_STEREO(i, gain) \
+ dest1[i] += src1[i] * gain, dest2[i] += src2[i] * gain;
+
+static inline void cbox_gain_add_stereo(struct cbox_gain *gain, float *dest1, const float *src1, float *dest2, const float *src2, uint32_t nsamples)
+{
+ CBOX_GAIN_APPLY_LOOP(gain, nsamples, CBOX_GAIN_ADD_STEREO);
+}
+
+#define CBOX_GAIN_COPY_MONO(i, gain) \
+ dest1[i] = src1[i] * gain;
+
+static inline void cbox_gain_copy_mono(struct cbox_gain *gain, float *dest1, const float *src1, uint32_t nsamples)
+{
+ CBOX_GAIN_APPLY_LOOP(gain, nsamples, CBOX_GAIN_COPY_MONO);
+}
+
+#define CBOX_GAIN_COPY_STEREO(i, gain) \
+ dest1[i] = src1[i] * gain, dest2[i] = src2[i] * gain;
+
+static inline void cbox_gain_copy_stereo(struct cbox_gain *gain, float *dest1, const float *src1, float *dest2, const float *src2, uint32_t nsamples)
+{
+ CBOX_GAIN_APPLY_LOOP(gain, nsamples, CBOX_GAIN_COPY_STEREO);
+}
+
+#endif
\ No newline at end of file
diff --git a/template/calfbox/engine.c b/template/calfbox/engine.c
new file mode 100644
index 0000000..7d969d6
--- /dev/null
+++ b/template/calfbox/engine.c
@@ -0,0 +1,445 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2013 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "blob.h"
+#include "dom.h"
+#include "engine.h"
+#include "instr.h"
+#include "io.h"
+#include "layer.h"
+#include "midi.h"
+#include "mididest.h"
+#include "module.h"
+#include "rt.h"
+#include "scene.h"
+#include "seq.h"
+#include "song.h"
+#include "stm.h"
+#include "track.h"
+#include
+#include
+#include
+
+CBOX_CLASS_DEFINITION_ROOT(cbox_engine)
+
+static gboolean cbox_engine_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
+
+struct cbox_engine *cbox_engine_new(struct cbox_document *doc, struct cbox_rt *rt)
+{
+ struct cbox_engine *engine = malloc(sizeof(struct cbox_engine));
+ CBOX_OBJECT_HEADER_INIT(engine, cbox_engine, doc);
+
+ engine->rt = rt;
+ engine->scenes = NULL;
+ engine->scene_count = 0;
+ engine->effect = NULL;
+ engine->master = cbox_master_new(engine);
+ engine->master->song = cbox_song_new(doc);
+ engine->spb = NULL;
+ engine->spb_lock = 0;
+ engine->spb_retry = 0;
+
+ if (rt)
+ cbox_io_env_copy(&engine->io_env, &rt->io_env);
+ else
+ {
+ engine->io_env.srate = 0; // must be overridden
+ engine->io_env.buffer_size = 256;
+ engine->io_env.input_count = 0;
+ engine->io_env.output_count = 2;
+ }
+
+ cbox_midi_buffer_init(&engine->midibuf_aux);
+ cbox_midi_buffer_init(&engine->midibuf_jack);
+ cbox_midi_buffer_init(&engine->midibuf_song);
+ engine->stmap = malloc(sizeof(struct cbox_song_time_mapper));
+ cbox_song_time_mapper_init(engine->stmap, engine);
+ cbox_midi_appsink_init(&engine->appsink, rt, &engine->stmap->tmap);
+
+ cbox_command_target_init(&engine->cmd_target, cbox_engine_process_cmd, engine);
+ CBOX_OBJECT_REGISTER(engine);
+
+ return engine;
+}
+
+struct cbox_objhdr *cbox_engine_newfunc(struct cbox_class *class_ptr, struct cbox_document *doc)
+{
+ return NULL;
+}
+
+void cbox_engine_destroyfunc(struct cbox_objhdr *obj_ptr)
+{
+ struct cbox_engine *engine = (struct cbox_engine *)obj_ptr;
+ while(engine->scene_count)
+ CBOX_DELETE(engine->scenes[0]);
+ if (engine->master->song)
+ {
+ CBOX_DELETE(engine->master->song);
+ engine->master->song = NULL;
+ }
+ cbox_master_destroy(engine->master);
+ engine->master = NULL;
+ free(engine->stmap);
+ engine->stmap = NULL;
+
+ free(engine);
+}
+
+static gboolean cbox_engine_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct cbox_engine *engine = ct->user_data;
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ for (uint32_t i = 0; i < engine->scene_count; i++)
+ {
+ if (!cbox_execute_on(fb, NULL, "/scene", "o", error, engine->scenes[i]))
+ return FALSE;
+ }
+ return CBOX_OBJECT_DEFAULT_STATUS(engine, fb, error);
+ }
+ else if (!strcmp(cmd->command, "/render_stereo") && !strcmp(cmd->arg_types, "i"))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ if (engine->rt && engine->rt->io)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot use render function in real-time mode.");
+ return FALSE;
+ }
+ struct cbox_midi_buffer midibuf_song;
+ cbox_midi_buffer_init(&midibuf_song);
+ int nframes = CBOX_ARG_I(cmd, 0);
+ float *data = malloc(2 * nframes * sizeof(float));
+ float *data_i = malloc(2 * nframes * sizeof(float));
+ float *buffers[2] = { data, data + nframes };
+ for (int i = 0; i < nframes; i++)
+ {
+ buffers[0][i] = 0.f;
+ buffers[1][i] = 0.f;
+ }
+ cbox_engine_process(engine, NULL, nframes, buffers, 2);
+ for (int i = 0; i < nframes; i++)
+ {
+ data_i[i * 2] = buffers[0][i];
+ data_i[i * 2 + 1] = buffers[1][i];
+ }
+ free(data);
+
+ if (!cbox_execute_on(fb, NULL, "/data", "b", error, cbox_blob_new_acquire_data(data_i, nframes * 2 * sizeof(float))))
+ return FALSE;
+ return TRUE;
+ }
+ else if (!strncmp(cmd->command, "/master_effect/",15))
+ {
+ return cbox_module_slot_process_cmd(&engine->effect, fb, cmd, cmd->command + 14, CBOX_GET_DOCUMENT(engine), engine->rt, engine, error);
+ }
+ else if (!strcmp(cmd->command, "/new_scene") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ struct cbox_scene *s = cbox_scene_new(CBOX_GET_DOCUMENT(engine), engine);
+
+ return s ? cbox_execute_on(fb, NULL, "/uuid", "o", error, s) : FALSE;
+ }
+ else if (!strcmp(cmd->command, "/new_recorder") && !strcmp(cmd->arg_types, "s"))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ struct cbox_recorder *rec = cbox_recorder_new_stream(engine, engine->rt, CBOX_ARG_S(cmd, 0));
+
+ return rec ? cbox_execute_on(fb, NULL, "/uuid", "o", error, rec) : FALSE;
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+}
+
+void cbox_engine_process(struct cbox_engine *engine, struct cbox_io *io, uint32_t nframes, float **output_buffers, uint32_t output_channels)
+{
+ struct cbox_module *effect = engine->effect;
+ uint32_t i, j;
+
+ cbox_midi_buffer_clear(&engine->midibuf_aux);
+ cbox_midi_buffer_clear(&engine->midibuf_song);
+ if (io)
+ cbox_io_get_midi_data(io, &engine->midibuf_jack);
+ else
+ cbox_midi_buffer_clear(&engine->midibuf_jack);
+
+ // Copy MIDI input to the app-sink
+ cbox_midi_appsink_supply(&engine->appsink, &engine->midibuf_jack, io->free_running_frame_counter);
+
+ // Clear external track outputs
+ if (engine->spb)
+ cbox_song_playback_prepare_render(engine->spb);
+
+ if (engine->rt)
+ cbox_rt_handle_rt_commands(engine->rt);
+
+ // Combine various sources of events (song, non-RT thread, JACK input)
+ if (engine->spb)
+ {
+ engine->frame_start_song_pos = engine->spb->song_pos_samples;
+ cbox_song_playback_render(engine->spb, &engine->midibuf_song, nframes);
+ }
+
+ for (uint32_t i = 0; i < engine->scene_count; i++)
+ cbox_scene_render(engine->scenes[i], nframes, output_buffers, output_channels);
+
+ // Process "master" effect
+ if (effect)
+ {
+ for (i = 0; i < nframes; i += CBOX_BLOCK_SIZE)
+ {
+ cbox_sample_t left[CBOX_BLOCK_SIZE], right[CBOX_BLOCK_SIZE];
+ cbox_sample_t *in_bufs[2] = {output_buffers[0] + i, output_buffers[1] + i};
+ cbox_sample_t *out_bufs[2] = {left, right};
+ (*effect->process_block)(effect, in_bufs, out_bufs);
+ for (j = 0; j < CBOX_BLOCK_SIZE; j++)
+ {
+ output_buffers[0][i + j] = left[j];
+ output_buffers[1][i + j] = right[j];
+ }
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void cbox_engine_add_scene(struct cbox_engine *engine, struct cbox_scene *scene)
+{
+ assert(scene->engine == engine);
+
+ cbox_rt_array_insert(engine->rt, (void ***)&engine->scenes, &engine->scene_count, -1, scene);
+}
+
+void cbox_engine_remove_scene(struct cbox_engine *engine, struct cbox_scene *scene)
+{
+ assert(scene->engine == engine);
+ cbox_rt_array_remove_by_value(engine->rt, (void ***)&engine->scenes, &engine->scene_count, scene);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+#define cbox_engine_set_song_playback_args(ARG) ARG(struct cbox_song_playback *, old_song) ARG(struct cbox_song_playback *, new_song) ARG(uint32_t, new_time_ppqn)
+
+DEFINE_ASYNC_RT_FUNC(cbox_engine, engine, cbox_engine_set_song_playback)
+{
+ // If there's no new song, silence all ongoing notes. Otherwise, copy the
+ // ongoing notes to the new playback structure so that the notes get released
+ // when playback is stopped (or possibly earlier).
+ if (engine->spb)
+ {
+ if (new_song)
+ cbox_song_playback_apply_old_state(new_song);
+
+ if (cbox_song_playback_active_notes_release(engine->spb, new_song, new_time_ppqn == (uint32_t)-1 ? old_song->song_pos_ppqn : new_time_ppqn, &engine->midibuf_aux) < 0)
+ {
+ RT_CALL_AGAIN_LATER();
+ return;
+ }
+ }
+ engine->spb = new_song;
+ engine->master->spb = new_song;
+ if (new_song)
+ {
+ if (new_time_ppqn == (uint32_t)-1)
+ {
+ int old_time_ppqn = old_song ? old_song->song_pos_ppqn : 0;
+ cbox_song_playback_seek_samples(engine->master->spb, old_song ? old_song->song_pos_samples : 0);
+ // If tempo change occurred anywhere before playback point, then
+ // sample-based position corresponds to a completely different location.
+ // So, if new sample-based position corresponds to different PPQN
+ // position, seek again using PPQN position.
+ if (old_song && abs(new_song->song_pos_ppqn - old_time_ppqn) > 1)
+ cbox_song_playback_seek_ppqn(engine->master->spb, old_time_ppqn, FALSE);
+ }
+ else
+ cbox_song_playback_seek_ppqn(engine->master->spb, new_time_ppqn, FALSE);
+ }
+}
+
+ASYNC_PREPARE_FUNC(cbox_engine, engine, cbox_engine_set_song_playback)
+{
+ // If update is already in progress, reschedule another at the end of it
+ if (engine->spb_lock)
+ {
+ engine->spb_retry = 1;
+ return 1;
+ }
+ ++engine->spb_lock;
+ args->old_song = engine->spb;
+ args->new_song = cbox_song_playback_new(engine->master->song, engine->master, engine, args->old_song);
+
+ return 0;
+}
+
+ASYNC_CLEANUP_FUNC(cbox_engine, engine, cbox_engine_set_song_playback)
+{
+ --engine->spb_lock;
+ assert(!engine->spb_lock);
+ if (args->old_song)
+ cbox_song_playback_destroy(args->old_song);
+ // If another update was requested while this one was in progress, repeat
+ // the operation
+ if (engine->spb_retry) {
+ engine->spb_retry = 0;
+ cbox_engine_set_song_playback(engine, NULL, NULL, args->new_time_ppqn);
+ }
+}
+
+void cbox_engine_update_song(struct cbox_engine *engine, int new_pos_ppqn)
+{
+ cbox_engine_set_song_playback(engine, NULL, NULL, new_pos_ppqn);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void cbox_engine_update_song_playback(struct cbox_engine *engine)
+{
+ cbox_engine_update_song(engine, -1);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+uint32_t cbox_engine_current_pos_samples(struct cbox_engine *engine)
+{
+ uint32_t pos = engine->frame_start_song_pos + engine->song_pos_offset;
+ if (engine->spb && engine->spb->loop_start_ppqn < engine->spb->loop_end_ppqn)
+ pos = cbox_song_playback_correct_for_looping(engine->spb, pos);
+ return pos;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void cbox_engine_update_input_connections(struct cbox_engine *engine)
+{
+ for (uint32_t i = 0; i < engine->scene_count; i++)
+ cbox_scene_update_connected_inputs(engine->scenes[i]);
+}
+
+void cbox_engine_update_output_connections(struct cbox_engine *engine)
+{
+ for (uint32_t i = 0; i < engine->scene_count; i++)
+ cbox_scene_update_connected_outputs(engine->scenes[i]);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void cbox_engine_send_events_to(struct cbox_engine *engine, struct cbox_midi_merger *merger, struct cbox_midi_buffer *buffer)
+{
+ if (!engine || !buffer)
+ return;
+ if (merger)
+ cbox_midi_merger_push(merger, buffer, engine->rt);
+ else
+ {
+ for (uint32_t i = 0; i < engine->scene_count; i++)
+ cbox_midi_merger_push(&engine->scenes[i]->scene_input_merger, buffer, engine->rt);
+ if (!engine->rt || !engine->rt->io)
+ return;
+ for (GSList *p = engine->rt->io->midi_outputs; p; p = p->next)
+ {
+ struct cbox_midi_output *midiout = p->data;
+ cbox_midi_merger_push(&midiout->merger, buffer, engine->rt);
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+void cbox_engine_on_tempo_sync(struct cbox_engine *engine, double beats_per_minute)
+{
+ if (!engine->master)
+ return;
+ if (beats_per_minute && beats_per_minute != engine->master->tempo && beats_per_minute != engine->master->new_tempo) {
+ engine->master->new_tempo = beats_per_minute;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+gboolean cbox_engine_on_transport_sync(struct cbox_engine *engine, enum cbox_transport_state state, uint32_t frame)
+{
+ if (state == ts_stopping)
+ {
+ if (engine->master->state == CMTS_ROLLING)
+ engine->master->state = engine->spb ? CMTS_STOPPING : CMTS_STOP;
+
+ return engine->master->state == CMTS_STOP;
+ }
+ if (state == ts_starting)
+ {
+ if (engine->master->state == CMTS_STOPPING)
+ return FALSE;
+ if (engine->master->state == CMTS_ROLLING)
+ {
+ if (engine->spb->song_pos_samples == frame)
+ return TRUE;
+ engine->master->state = CMTS_STOPPING;
+ return FALSE;
+ }
+ if (engine->spb && engine->spb->song_pos_samples != frame)
+ {
+ cbox_song_playback_seek_samples(engine->spb, frame);
+ }
+ engine->frame_start_song_pos = frame;
+ return TRUE;
+ }
+ if (state == ts_rolling)
+ {
+ // When starting with JACK transport rolling, there is no
+ // ts_starting message in first place (because there can't be without
+ // interfering with other applications). Seek immediately.
+ if (engine->spb && engine->spb->song_pos_samples != frame)
+ {
+ cbox_song_playback_seek_samples(engine->spb, frame);
+ }
+ else
+ engine->frame_start_song_pos = frame;
+ engine->master->state = CMTS_ROLLING;
+ return TRUE;
+ }
+ if (state == ts_stopped)
+ {
+ if (engine->master->state == CMTS_ROLLING)
+ engine->master->state = CMTS_STOPPING;
+
+ if (engine->master->state == CMTS_STOP && engine->spb && engine->spb->song_pos_samples != frame)
+ {
+ cbox_song_playback_seek_samples(engine->spb, frame);
+ }
+
+ return engine->master->state == CMTS_STOP;
+ }
+ return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////
+
+struct cbox_midi_merger *cbox_engine_get_midi_output(struct cbox_engine *engine, struct cbox_uuid *uuid)
+{
+ struct cbox_objhdr *objhdr = cbox_document_get_object_by_uuid(CBOX_GET_DOCUMENT(engine), uuid);
+ if (!objhdr)
+ return NULL;
+ struct cbox_scene *scene = (struct cbox_scene *)objhdr;
+ if (!CBOX_IS_A(scene, cbox_scene))
+ return NULL;
+ return &scene->scene_input_merger;
+}
diff --git a/template/calfbox/engine.h b/template/calfbox/engine.h
new file mode 100644
index 0000000..d9b560c
--- /dev/null
+++ b/template/calfbox/engine.h
@@ -0,0 +1,72 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2013 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_ENGINE_H
+#define CBOX_ENGINE_H
+
+#include "cmd.h"
+#include "dom.h"
+#include "io.h"
+#include "midi.h"
+#include "rt.h"
+
+CBOX_EXTERN_CLASS(cbox_engine)
+
+#define GET_RT_FROM_cbox_engine(ptr) ((ptr)->rt)
+
+struct cbox_engine
+{
+ CBOX_OBJECT_HEADER()
+ struct cbox_command_target cmd_target;
+ struct cbox_io_env io_env;
+ struct cbox_rt *rt;
+ struct cbox_scene **scenes;
+ uint32_t scene_count;
+ struct cbox_song_playback *spb;
+ struct cbox_module *effect;
+ struct cbox_master *master;
+ struct cbox_midi_buffer midibuf_aux, midibuf_jack, midibuf_song;
+ struct cbox_song_time_mapper *stmap;
+ struct cbox_midi_appsink appsink;
+
+ int spb_lock, spb_retry;
+
+ uint32_t frame_start_song_pos, song_pos_offset; // samples
+};
+
+// These use an RT command internally
+extern struct cbox_engine *cbox_engine_new(struct cbox_document *doc, struct cbox_rt *rt);
+extern void cbox_engine_update_song_playback(struct cbox_engine *engine);
+extern void cbox_engine_update_input_connections(struct cbox_engine *engine);
+extern void cbox_engine_update_output_connections(struct cbox_engine *engine);
+extern void cbox_engine_add_scene(struct cbox_engine *engine, struct cbox_scene *scene);
+void cbox_engine_remove_scene(struct cbox_engine *engine, struct cbox_scene *scene);
+extern struct cbox_song *cbox_engine_set_song(struct cbox_engine *engine, struct cbox_song *song, int new_pos);
+extern struct cbox_song *cbox_engine_set_pattern(struct cbox_engine *engine, struct cbox_midi_pattern *pattern, int new_pos);
+extern void cbox_engine_set_pattern_and_destroy(struct cbox_engine *engine, struct cbox_midi_pattern *pattern);
+extern void cbox_engine_send_events_to(struct cbox_engine *engine, struct cbox_midi_merger *merger, struct cbox_midi_buffer *buffer);
+extern void cbox_engine_process(struct cbox_engine *engine, struct cbox_io *io, uint32_t nframes, float **output_buffers, uint32_t output_channels);
+extern gboolean cbox_engine_on_transport_sync(struct cbox_engine *engine, enum cbox_transport_state state, uint32_t frame);
+extern void cbox_engine_on_tempo_sync(struct cbox_engine *engine, double beats_per_minute);
+extern struct cbox_midi_merger *cbox_engine_get_midi_output(struct cbox_engine *engine, struct cbox_uuid *uuid);
+extern uint32_t cbox_engine_current_pos_samples(struct cbox_engine *engine);
+
+extern int cbox_engine_get_sample_rate(struct cbox_engine *engine);
+extern int cbox_engine_get_buffer_size(struct cbox_engine *engine);
+
+#endif
diff --git a/template/calfbox/envelope.h b/template/calfbox/envelope.h
new file mode 100644
index 0000000..58522ff
--- /dev/null
+++ b/template/calfbox/envelope.h
@@ -0,0 +1,322 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_ENVELOPE_H
+#define CBOX_ENVELOPE_H
+
+#include
+#include
+
+struct cbox_envstage
+{
+ double end_value;
+ int time;
+ int next_if_pressed, next_if_released, keep_last_value, break_on_release, is_exp;
+};
+
+#define MAX_ENV_STAGES 16
+#define EXP_NOISE_FLOOR (100.0 / 16384.0)
+
+struct cbox_envelope_shape
+{
+ double start_value;
+ struct cbox_envstage stages[MAX_ENV_STAGES];
+};
+
+struct cbox_envelope
+{
+ struct cbox_envelope_shape *shape;
+ double stage_start_value, cur_value, exp_factor, inv_time, cur_time, orig_time, orig_target;
+ int cur_stage;
+};
+
+static inline void cbox_envelope_init_stage(struct cbox_envelope *env)
+{
+ struct cbox_envstage *es = &env->shape->stages[env->cur_stage];
+ env->orig_time = es->time;
+ env->orig_target = es->end_value;
+ env->inv_time = es->time > 0 ? 1.0 / es->time : 1e6;
+ if (es->is_exp)
+ {
+ if (env->stage_start_value < EXP_NOISE_FLOOR)
+ env->stage_start_value = EXP_NOISE_FLOOR;
+ double ev = es->end_value;
+ if (ev < EXP_NOISE_FLOOR)
+ ev = EXP_NOISE_FLOOR;
+ env->exp_factor = log(ev / env->stage_start_value);
+ }
+}
+
+static inline void cbox_envelope_go_to(struct cbox_envelope *env, int stage)
+{
+ env->stage_start_value = env->cur_value;
+ env->cur_stage = stage;
+ env->cur_time = 0;
+ cbox_envelope_init_stage(env);
+}
+
+static inline void cbox_envelope_reset(struct cbox_envelope *env)
+{
+ env->cur_value = 0;
+ env->cur_stage = 0;
+ env->cur_time = 0;
+ cbox_envelope_init_stage(env);
+}
+
+static inline void cbox_envelope_update_shape(struct cbox_envelope *env, struct cbox_envelope_shape *shape)
+{
+ struct cbox_envelope_shape *old_shape = env->shape;
+ env->shape = shape;
+ if (env->cur_stage < 0)
+ return;
+ struct cbox_envstage *ns = &env->shape->stages[env->cur_stage];
+ struct cbox_envstage *os = &old_shape->stages[env->cur_stage];
+ if (os->time > 0)
+ env->cur_time = env->cur_time * ns->time / os->time;
+ if (env->cur_time > ns->time)
+ env->cur_time = ns->time;
+}
+
+static inline float cbox_envelope_get_value(struct cbox_envelope *env, const struct cbox_envelope_shape *shape)
+{
+ if (env->cur_stage < 0)
+ return env->cur_value;
+ const struct cbox_envstage *es = &shape->stages[env->cur_stage];
+ double pos = es->time > 0 ? env->cur_time * env->inv_time : 0;
+ if (pos > 1)
+ pos = 1;
+ if (es->is_exp)
+ {
+ // instead of exp, may use 2**x which can be factored
+ // into a shift and a table lookup
+ env->cur_value = env->stage_start_value * expf(pos * env->exp_factor);
+ if (env->cur_value <= EXP_NOISE_FLOOR)
+ env->cur_value = 0;
+ }
+ else
+ env->cur_value = env->stage_start_value + (es->end_value - env->stage_start_value) * pos;
+ return env->cur_value;
+}
+
+#define DEBUG_UPDATE_SHAPE(...)
+
+static inline void cbox_envelope_update_shape_after_modify(struct cbox_envelope *env, struct cbox_envelope_shape *shape, double sr)
+{
+ struct cbox_envstage *es = &shape->stages[env->cur_stage];
+ if (es->time != env->orig_time)
+ {
+ // Scale cur_time to reflect the same relative position within the stage
+ env->cur_time = env->cur_time * es->time / (env->orig_time > 0 ? env->orig_time : 1);
+ env->orig_time = es->time;
+ env->inv_time = es->time > 0 ? 1.0 / es->time : 1e6;
+ }
+ if (es->end_value != env->orig_target)
+ {
+ // Adjust the start value to keep the current value intact given the change in the slope
+ double pos = es->time > 0 ? env->cur_time * env->inv_time : 1;
+ if (pos < 1)
+ {
+ if (es->is_exp)
+ env->stage_start_value /= pow(es->end_value / (env->orig_target >= EXP_NOISE_FLOOR ? env->orig_target : EXP_NOISE_FLOOR), pos / (1 - pos)); // untested, likely never used
+ else
+ env->stage_start_value -= (es->end_value - env->orig_target) * pos / (1 - pos);
+ }
+ env->orig_target = es->end_value;
+ }
+}
+
+static inline void cbox_envelope_advance(struct cbox_envelope *env, int released, const struct cbox_envelope_shape *shape)
+{
+ if (env->cur_stage < 0)
+ return;
+ const struct cbox_envstage *es = &shape->stages[env->cur_stage];
+ double pos = es->time > 0 ? env->cur_time * env->inv_time : 1;
+ env->cur_time++;
+ if (pos >= 1 || (es->break_on_release && released))
+ {
+ int next_stage = released ? es->next_if_released : es->next_if_pressed;
+ if (!es->keep_last_value || pos >= 1 || (es->keep_last_value == 2 && !released) || next_stage == env->cur_stage)
+ env->stage_start_value = es->end_value;
+ else
+ env->stage_start_value = env->cur_value;
+ env->cur_stage = next_stage;
+ env->cur_time = 0;
+ cbox_envelope_init_stage(env);
+ }
+}
+
+struct cbox_adsr
+{
+ float attack;
+ float decay;
+ float sustain;
+ float release;
+};
+
+static inline void cbox_envelope_init_adsr(struct cbox_envelope_shape *env, const struct cbox_adsr *adsr, int sr)
+{
+ env->start_value = 0;
+ env->stages[0].end_value = 1;
+ env->stages[0].time = adsr->attack * sr;
+ env->stages[0].next_if_pressed = 1;
+ env->stages[0].next_if_released = 3;
+ env->stages[0].keep_last_value = 1;
+ env->stages[0].break_on_release = 0;
+ env->stages[0].is_exp = 0;
+
+ env->stages[1].end_value = adsr->sustain;
+ env->stages[1].time = adsr->decay * sr;
+ env->stages[1].next_if_pressed = 2;
+ env->stages[1].next_if_released = 3;
+ env->stages[1].keep_last_value = 1;
+ env->stages[1].break_on_release = 0;
+ env->stages[1].is_exp = 0;
+
+ env->stages[2].end_value = adsr->sustain;
+ env->stages[2].time = 1 * sr;
+ env->stages[2].next_if_pressed = 2;
+ env->stages[2].next_if_released = 3;
+ env->stages[2].keep_last_value = 0;
+ env->stages[2].break_on_release = 1;
+ env->stages[2].is_exp = 0;
+
+ env->stages[3].end_value = 0;
+ env->stages[3].time = adsr->release * sr;
+ env->stages[3].next_if_pressed = -1;
+ env->stages[3].next_if_released = -1;
+ env->stages[3].keep_last_value = 0;
+ env->stages[3].break_on_release = 0;
+ env->stages[3].is_exp = 1;
+
+ env->stages[15].end_value = 0;
+ env->stages[15].time = 0.01 * sr;
+ env->stages[15].next_if_pressed = -1;
+ env->stages[15].next_if_released = -1;
+ env->stages[15].keep_last_value = 0;
+ env->stages[15].break_on_release = 0;
+ env->stages[15].is_exp = 0;
+}
+
+struct cbox_dahdsr
+{
+ float start;
+ float delay;
+ float attack;
+ float hold;
+ float decay;
+ float sustain;
+ float release;
+};
+
+static inline void cbox_dahdsr_init(struct cbox_dahdsr *dahdsr, float top_value)
+{
+ dahdsr->start = 0.f;
+ dahdsr->delay = 0.f;
+ dahdsr->attack = 0.f;
+ dahdsr->hold = 0.f;
+ dahdsr->decay = 0.f;
+ dahdsr->sustain = top_value;
+ dahdsr->release = 0.05f;
+}
+
+static inline void cbox_envelope_init_dahdsr(struct cbox_envelope_shape *env, const struct cbox_dahdsr *dahdsr, int sr, float top_value, gboolean is_release_exp)
+{
+ env->start_value = dahdsr->start;
+ env->stages[0].end_value = dahdsr->start;
+ env->stages[0].time = dahdsr->delay * sr;
+ env->stages[0].next_if_pressed = 1;
+ env->stages[0].next_if_released = 5;
+ env->stages[0].keep_last_value = 1;
+ env->stages[0].break_on_release = 0;
+ env->stages[0].is_exp = 0;
+
+ env->stages[1].end_value = top_value;
+ env->stages[1].time = dahdsr->attack * sr;
+ env->stages[1].next_if_pressed = 2;
+ env->stages[1].next_if_released = 5;
+ env->stages[1].keep_last_value = 2;
+ env->stages[1].break_on_release = 1;
+ env->stages[1].is_exp = 0;
+
+ env->stages[2].end_value = top_value;
+ env->stages[2].time = dahdsr->hold * sr;
+ env->stages[2].next_if_pressed = 3;
+ env->stages[2].next_if_released = 5;
+ env->stages[2].keep_last_value = 2;
+ env->stages[2].break_on_release = 1;
+ env->stages[2].is_exp = 0;
+
+ env->stages[3].end_value = dahdsr->sustain;
+ env->stages[3].time = dahdsr->decay * sr;
+ env->stages[3].next_if_pressed = 4;
+ env->stages[3].next_if_released = 5;
+ env->stages[3].keep_last_value = 1;
+ env->stages[3].break_on_release = 1;
+ env->stages[3].is_exp = 0;
+
+ env->stages[4].end_value = dahdsr->sustain;
+ env->stages[4].time = 1 * sr;
+ env->stages[4].next_if_pressed = 4;
+ env->stages[4].next_if_released = 5;
+ env->stages[4].keep_last_value = 1;
+ env->stages[4].break_on_release = 1;
+ env->stages[4].is_exp = 0;
+
+ env->stages[5].end_value = 0;
+ env->stages[5].time = dahdsr->release * sr;
+ env->stages[5].next_if_pressed = -1;
+ env->stages[5].next_if_released = -1;
+ env->stages[5].keep_last_value = 0;
+ env->stages[5].break_on_release = 0;
+ env->stages[5].is_exp = is_release_exp;
+
+ env->stages[15].end_value = 0;
+ env->stages[15].time = 0.01 * sr;
+ env->stages[15].next_if_pressed = -1;
+ env->stages[15].next_if_released = -1;
+ env->stages[15].keep_last_value = 0;
+ env->stages[15].break_on_release = 0;
+ env->stages[15].is_exp = 0;
+}
+
+static inline void cbox_envelope_modify_dahdsr(struct cbox_envelope_shape *env, int part, float value, int sr)
+{
+ switch(part)
+ {
+ case 0: // delay
+ case 1: // attack
+ case 2: // hold
+ case 3: // decay
+ case 5: // release
+ env->stages[part].time += value * sr;
+ // Allow negative times (deal with them in get_next) to make multiple signed modulations work correctly
+ break;
+ case 4: // sustain
+ env->stages[3].end_value += value;
+ env->stages[4].end_value += value;
+ env->stages[4].time = 0.02 * sr; // more rapid transition
+ break;
+ case 6: // start
+ env->stages[0].end_value += value;
+ env->start_value += value;
+ break;
+ }
+}
+
+
+#endif
diff --git a/template/calfbox/eq.c b/template/calfbox/eq.c
new file mode 100644
index 0000000..f53c7ae
--- /dev/null
+++ b/template/calfbox/eq.c
@@ -0,0 +1,194 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "biquad-float.h"
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "eq.h"
+#include "module.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MODULE_PARAMS parametric_eq_params
+#define MAX_EQ_BANDS 4
+
+struct parametric_eq_params
+{
+ struct eq_band bands[MAX_EQ_BANDS];
+};
+
+struct parametric_eq_module
+{
+ struct cbox_module module;
+
+ struct parametric_eq_params *params, *old_params;
+
+ struct cbox_biquadf_state state[MAX_EQ_BANDS][2];
+ struct cbox_biquadf_coeffs coeffs[MAX_EQ_BANDS];
+};
+
+static void redo_filters(struct parametric_eq_module *m)
+{
+ for (int i = 0; i < MAX_EQ_BANDS; i++)
+ {
+ struct eq_band *band = &m->params->bands[i];
+ if (band->active)
+ {
+ cbox_biquadf_set_peakeq_rbj(&m->coeffs[i], band->center, band->q, band->gain, m->module.srate);
+ }
+ }
+ m->old_params = m->params;
+}
+
+gboolean parametric_eq_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct parametric_eq_module *m = (struct parametric_eq_module *)ct->user_data;
+
+ EFFECT_PARAM_ARRAY("/active", "i", bands, active, int, , 0, 1) else
+ EFFECT_PARAM_ARRAY("/center", "f", bands, center, double, , 10, 20000) else
+ EFFECT_PARAM_ARRAY("/q", "f", bands, q, double, , 0.01, 100) else
+ EFFECT_PARAM_ARRAY("/gain", "f", bands, gain, double, dB2gain_simple, -100, 100) else
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ for (int i = 0; i < MAX_EQ_BANDS; i++)
+ {
+ if (!cbox_execute_on(fb, NULL, "/active", "ii", error, i, (int)m->params->bands[i].active))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/center", "if", error, i, m->params->bands[i].center))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/q", "if", error, i, m->params->bands[i].q))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/gain", "if", error, i, gain2dB_simple(m->params->bands[i].gain)))
+ return FALSE;
+ }
+ // return cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry);
+ return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error);
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void parametric_eq_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct parametric_eq_module *m = (struct parametric_eq_module *)module;
+}
+
+void parametric_eq_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct parametric_eq_module *m = (struct parametric_eq_module *)module;
+
+ if (m->params != m->old_params)
+ redo_filters(m);
+
+ for (int c = 0; c < 2; c++)
+ {
+ gboolean first = TRUE;
+ for (int i = 0; i < MAX_EQ_BANDS; i++)
+ {
+ if (!m->params->bands[i].active)
+ continue;
+ if (first)
+ {
+ cbox_biquadf_process_to(&m->state[i][c], &m->coeffs[i], inputs[c], outputs[c]);
+ first = FALSE;
+ }
+ else
+ {
+ cbox_biquadf_process(&m->state[i][c], &m->coeffs[i], outputs[c]);
+ }
+ }
+ if (first)
+ memcpy(outputs[c], inputs[c], sizeof(float) * CBOX_BLOCK_SIZE);
+ }
+}
+
+float cbox_eq_get_band_param(const char *cfg_section, int band, const char *param, float defvalue)
+{
+ gchar *s = g_strdup_printf("band%d_%s", band + 1, param);
+ float v = cbox_config_get_float(cfg_section, s, defvalue);
+ g_free(s);
+
+ return v;
+}
+
+float cbox_eq_get_band_param_db(const char *cfg_section, int band, const char *param, float defvalue)
+{
+ gchar *s = g_strdup_printf("band%d_%s", band + 1, param);
+ float v = cbox_config_get_gain_db(cfg_section, s, defvalue);
+ g_free(s);
+
+ return v;
+}
+
+void cbox_eq_reset_bands(struct cbox_biquadf_state state[1][2], int bands)
+{
+ for (int b = 0; b < MAX_EQ_BANDS; b++)
+ for (int c = 0; c < 2; c++)
+ cbox_biquadf_reset(&state[b][c]);
+}
+
+MODULE_SIMPLE_DESTROY_FUNCTION(parametric_eq)
+
+MODULE_CREATE_FUNCTION(parametric_eq)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ struct parametric_eq_module *m = malloc(sizeof(struct parametric_eq_module));
+ CALL_MODULE_INIT(m, 2, 2, parametric_eq);
+ m->module.process_event = parametric_eq_process_event;
+ m->module.process_block = parametric_eq_process_block;
+ struct parametric_eq_params *p = malloc(sizeof(struct parametric_eq_params));
+ m->params = p;
+ m->old_params = NULL;
+
+ for (int b = 0; b < MAX_EQ_BANDS; b++)
+ {
+ p->bands[b].active = cbox_eq_get_band_param(cfg_section, b, "active", 0) > 0;
+ p->bands[b].center = cbox_eq_get_band_param(cfg_section, b, "center", 50 * pow(4.0, b));
+ p->bands[b].q = cbox_eq_get_band_param(cfg_section, b, "q", 0.707);
+ p->bands[b].gain = cbox_eq_get_band_param_db(cfg_section, b, "gain", 0);
+ }
+
+ cbox_eq_reset_bands(m->state, MAX_EQ_BANDS);
+
+ return &m->module;
+}
+
+
+struct cbox_module_keyrange_metadata parametric_eq_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata parametric_eq_controllers[] = {
+};
+
+DEFINE_MODULE(parametric_eq, 2, 2)
+
diff --git a/template/calfbox/eq.h b/template/calfbox/eq.h
new file mode 100644
index 0000000..2a5e070
--- /dev/null
+++ b/template/calfbox/eq.h
@@ -0,0 +1,31 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include
+
+struct eq_band
+{
+ gboolean active;
+ float center;
+ float q;
+ float gain;
+};
+
+extern float cbox_eq_get_band_param(const char *cfg_section, int band, const char *param, float defvalue);
+extern float cbox_eq_get_band_param_db(const char *cfg_section, int band, const char *param, float defvalue);
+extern void cbox_eq_reset_bands(struct cbox_biquadf_state (*state)[2], int bands);
diff --git a/template/calfbox/errors.c b/template/calfbox/errors.c
new file mode 100644
index 0000000..0fec3e0
--- /dev/null
+++ b/template/calfbox/errors.c
@@ -0,0 +1,70 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "errors.h"
+
+GQuark cbox_module_error_quark()
+{
+ return g_quark_from_string("cbox-module-error-quark");
+}
+
+void cbox_force_error(GError **error)
+{
+ if (error && !*error)
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "unknown error");
+}
+
+void cbox_print_error(GError *error)
+{
+ if (!error)
+ {
+ g_warning("Unspecified error");
+ return;
+ }
+ g_warning("%s", error->message);
+ g_error_free(error);
+}
+
+void cbox_print_error_if(GError *error)
+{
+ if (!error)
+ return;
+ g_warning("%s", error->message);
+ g_error_free(error);
+}
+
+gboolean cbox_set_command_error(GError **error, const struct cbox_osc_command *cmd)
+{
+ if (error && !*error)
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_OUT_OF_RANGE, "Invalid command '%s' with args '%s'", cmd->command, cmd->arg_types);
+ return FALSE;
+}
+
+gboolean cbox_set_command_error_with_msg(GError **error, const struct cbox_osc_command *cmd, const char *extra_msg)
+{
+ if (error && !*error)
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_OUT_OF_RANGE, "Invalid command '%s' with args '%s': %s", cmd->command, cmd->arg_types, extra_msg);
+ return FALSE;
+}
+
+gboolean cbox_set_range_error(GError **error, const char *param, double minv, double maxv)
+{
+ if (error && !*error)
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_OUT_OF_RANGE, "Parameter %s not within a valid range of [%f, %f]", param, minv, maxv);
+ return FALSE;
+}
diff --git a/template/calfbox/errors.h b/template/calfbox/errors.h
new file mode 100644
index 0000000..ae8206d
--- /dev/null
+++ b/template/calfbox/errors.h
@@ -0,0 +1,44 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_ERRORS_H
+#define CBOX_ERRORS_H
+
+#include
+#include "cmd.h"
+
+#define CBOX_MODULE_ERROR cbox_module_error_quark()
+
+enum CboxModuleError
+{
+ CBOX_MODULE_ERROR_FAILED,
+ CBOX_MODULE_ERROR_INVALID_COMMAND,
+ CBOX_MODULE_ERROR_OUT_OF_RANGE,
+};
+
+struct cbox_osc_command;
+
+extern GQuark cbox_module_error_quark(void);
+extern void cbox_force_error(GError **error);
+extern void cbox_print_error(GError *error);
+extern void cbox_print_error_if(GError *error);
+extern gboolean cbox_set_command_error(GError **error, const struct cbox_osc_command *cmd);
+extern gboolean cbox_set_command_error_with_msg(GError **error, const struct cbox_osc_command *cmd, const char *extra_msg);
+extern gboolean cbox_set_range_error(GError **error, const char *param, double minv, double maxv);
+
+#endif
diff --git a/template/calfbox/example.py b/template/calfbox/example.py
new file mode 100644
index 0000000..c50284c
--- /dev/null
+++ b/template/calfbox/example.py
@@ -0,0 +1,554 @@
+import sys
+sys.argv = []
+import gi
+gi.require_version('Gdk', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import GObject, Gdk, Gtk
+import math
+
+sys.path = ["./py"] + sys.path
+
+import cbox
+from gui_tools import *
+import fx_gui
+import instr_gui
+import drumkit_editor
+#import drum_pattern_editor
+
+class SceneDialog(SelectObjectDialog):
+ title = "Select a scene"
+ def __init__(self, parent):
+ SelectObjectDialog.__init__(self, parent=parent)
+ def update_model(self, model):
+ for s in cbox.Config.sections("scene:"):
+ title = s["title"]
+ model.append((s.name[6:], "Scene", s.name, title))
+ for s in cbox.Config.sections("instrument:"):
+ title = s["title"]
+ model.append((s.name[11:], "Instrument", s.name, title))
+ for s in cbox.Config.sections("layer:"):
+ title = s["title"]
+ model.append((s.name[6:], "Layer", s.name, title))
+
+class NewLayerDialog(SelectObjectDialog):
+ title = "Create a layer"
+ def __init__(self, parent):
+ SelectObjectDialog.__init__(self, parent=parent)
+ def update_model(self, model):
+ for engine_name, wclass in instr_gui.instrument_window_map.items():
+ model.append((engine_name, "Engine", engine_name, ""))
+
+class LoadLayerDialog(SelectObjectDialog):
+ title = "Load a layer"
+ def __init__(self, parent):
+ SelectObjectDialog.__init__(self, parent=parent)
+ def update_model(self, model):
+ for s in cbox.Config.sections("instrument:"):
+ title = s["title"]
+ model.append((s.name[11:], "Instrument", s.name, title))
+ for s in cbox.Config.sections("layer:"):
+ title = s["title"]
+ model.append((s.name[6:], "Layer", s.name, title))
+
+class PlayPatternDialog(SelectObjectDialog):
+ title = "Play a drum pattern"
+ def __init__(self, parent):
+ SelectObjectDialog.__init__(self, parent)
+ def update_model(self, model):
+ model.append((None, "Stop", "", ""))
+ for s in cbox.Config.sections("drumpattern:"):
+ title = s["title"]
+ model.append((s.name[12:], "Pattern", s.name, title))
+ for s in cbox.Config.sections("drumtrack:"):
+ title = s["title"]
+ model.append((s.name[10:], "Track", s.name, title))
+
+in_channels_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING)
+in_channels_ls.append((0, "All"))
+out_channels_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING)
+out_channels_ls.append((0, "Same"))
+for i in range(1, 17):
+ in_channels_ls.append((i, str(i)))
+ out_channels_ls.append((i, str(i)))
+notes_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING)
+opt_notes_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING)
+opt_notes_ls.append((-1, "N/A"))
+for i in range(0, 128):
+ notes_ls.append((i, note_to_name(i)))
+ opt_notes_ls.append((i, note_to_name(i)))
+transpose_ls = Gtk.ListStore(GObject.TYPE_INT, GObject.TYPE_STRING)
+for i in range(-60, 61):
+ transpose_ls.append((i, str(i)))
+
+class SceneLayersModel(Gtk.ListStore):
+ def __init__(self):
+ Gtk.ListStore.__init__(self, GObject.TYPE_STRING, GObject.TYPE_BOOLEAN,
+ GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_BOOLEAN,
+ GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_INT, GObject.TYPE_STRING)
+ #def make_row_item(self, opath, tree_path):
+ # return opath % self[(1 + int(tree_path))]
+ def make_row_item(self, opath, tree_path):
+ return cbox.Document.uuid_cmd(self[int(tree_path)][-1], opath)
+ def refresh(self, scene_status):
+ self.clear()
+ for layer in scene_status.layers:
+ ls = layer.status()
+ self.append((ls.instrument_name, ls.enable != 0, ls.in_channel, ls.out_channel, ls.consume != 0, ls.low_note, ls.high_note, ls.fixed_note, ls.transpose, layer.uuid))
+
+class SceneLayersView(Gtk.TreeView):
+ def __init__(self, model):
+ Gtk.TreeView.__init__(self, model=model)
+ self.enable_model_drag_source(Gdk.ModifierType.BUTTON1_MASK, [("text/plain", 0, 1)], Gdk.DragAction.MOVE)
+ self.enable_model_drag_dest([("text/plain", Gtk.TargetFlags.SAME_APP | Gtk.TargetFlags.SAME_WIDGET, 1)], Gdk.DragAction.MOVE)
+ self.connect('drag_data_get', self.drag_data_get)
+ self.connect('drag_data_received', self.drag_data_received)
+ self.insert_column_with_attributes(0, "On?", standard_toggle_renderer(model, "/enable", 1), active=1)
+ self.insert_column_with_attributes(1, "Name", Gtk.CellRendererText(), text=0)
+ self.insert_column_with_data_func(2, "In Ch#", standard_combo_renderer(model, in_channels_ls, "/in_channel", 2), lambda column, cell, model, iter, data: cell.set_property('text', "%s" % model[iter][2] if model[iter][2] != 0 else 'All'), None)
+ self.insert_column_with_data_func(3, "Out Ch#", standard_combo_renderer(model, out_channels_ls, "/out_channel", 3), lambda column, cell, model, iter, data: cell.set_property('text', "%s" % model[iter][3] if model[iter][3] != 0 else 'Same'), None)
+ self.insert_column_with_attributes(4, "Eat?", standard_toggle_renderer(model, "/consume", 4), active=4)
+ self.insert_column_with_data_func(5, "Lo N#", standard_combo_renderer(model, notes_ls, "/low_note", 5), lambda column, cell, model, iter, data: cell.set_property('text', note_to_name(model[iter][5])), None)
+ self.insert_column_with_data_func(6, "Hi N#", standard_combo_renderer(model, notes_ls, "/high_note", 6), lambda column, cell, model, iter, data: cell.set_property('text', note_to_name(model[iter][6])), None)
+ self.insert_column_with_data_func(7, "Fix N#", standard_combo_renderer(model, opt_notes_ls, "/fixed_note", 7), lambda column, cell, model, iter, data: cell.set_property('text', note_to_name(model[iter][7])), None)
+ self.insert_column_with_attributes(8, "Transpose", standard_combo_renderer(model, transpose_ls, "/transpose", 8), text=8)
+ def drag_data_get(self, treeview, context, selection, target_id, etime):
+ cursor = treeview.get_cursor()
+ if cursor is not None:
+ selection.set('text/plain', 8, str(cursor[0][0]))
+ def drag_data_received(self, treeview, context, x, y, selection, info, etime):
+ src_row = int(selection.data)
+ dest_row_info = treeview.get_dest_row_at_pos(x, y)
+ if dest_row_info is not None:
+ dest_row = dest_row_info[0][0]
+ #print src_row, dest_row, dest_row_info[1]
+ scene = cbox.Document.get_scene()
+ scene.move_layer(src_row, dest_row)
+ self.get_model().refresh(scene.status())
+
+class SceneAuxBusesModel(Gtk.ListStore):
+ def __init__(self):
+ Gtk.ListStore.__init__(self, GObject.TYPE_STRING, GObject.TYPE_STRING)
+ def refresh(self, scene_status):
+ self.clear()
+ for aux_name, aux_obj in scene_status.auxes.items():
+ slot = aux_obj.slot.status()
+ self.append((slot.insert_preset, slot.insert_engine))
+
+class SceneAuxBusesView(Gtk.TreeView):
+ def __init__(self, model):
+ Gtk.TreeView.__init__(self, model=model)
+ self.insert_column_with_attributes(0, "Name", Gtk.CellRendererText(), text=0)
+ self.insert_column_with_attributes(1, "Engine", Gtk.CellRendererText(), text=1)
+ def get_current_row(self):
+ if self.get_cursor()[0] is None:
+ return None, None
+ row = self.get_cursor()[0][0]
+ return row + 1, self.get_model()[row]
+
+class StatusBar(Gtk.Statusbar):
+ def __init__(self):
+ Gtk.Statusbar.__init__(self)
+ self.sample_rate_label = Gtk.Label(label="")
+ self.pack_start(self.sample_rate_label, False, False, 2)
+ self.status = self.get_context_id("Status")
+ self.sample_rate = self.get_context_id("Sample rate")
+ self.push(self.status, "")
+ self.push(self.sample_rate, "-")
+ def update(self, status, sample_rate):
+ self.pop(self.status)
+ self.push(self.status, status)
+ self.sample_rate_label.set_text("%s Hz" % sample_rate)
+
+class MainWindow(Gtk.Window):
+ def __init__(self):
+ Gtk.Window.__init__(self, type = Gtk.WindowType.TOPLEVEL)
+ self.vbox = Gtk.VBox(spacing = 5)
+ self.add(self.vbox)
+ self.create()
+ set_timer(self, 30, self.update)
+ #self.drum_pattern_editor = None
+ self.drumkit_editor = None
+
+ def create(self):
+ self.menu_bar = Gtk.MenuBar()
+
+ self.menu_bar.append(create_menu("_Scene", [
+ ("_Load", self.load_scene),
+ ("_Quit", self.quit),
+ ]))
+ self.menu_bar.append(create_menu("_Layer", [
+ ("_New", self.layer_new),
+ ("_Load", self.layer_load),
+ ("_Remove", self.layer_remove),
+ ]))
+ self.menu_bar.append(create_menu("_AuxBus", [
+ ("_Add", self.aux_bus_add),
+ ("_Edit", self.aux_bus_edit),
+ ("_Remove", self.aux_bus_remove),
+ ]))
+ self.menu_bar.append(create_menu("_Tools", [
+ ("_Drum Kit Editor", self.tools_drumkit_editor),
+ ("_Play Drum Pattern", self.tools_play_drum_pattern),
+ #("_Edit Drum Pattern", self.tools_drum_pattern_editor),
+ ("_Un-zombify", self.tools_unzombify),
+ ("_Object list", self.tools_object_list),
+ ("_Wave bank dump", self.tools_wave_bank_dump),
+ ]))
+
+ self.vbox.pack_start(self.menu_bar, False, False, 0)
+ rt_status = cbox.Document.get_rt().status()
+ scene = cbox.Document.get_scene()
+ self.nb = Gtk.Notebook()
+ self.vbox.add(self.nb)
+ self.nb.append_page(self.create_master(scene), Gtk.Label(label="Master"))
+ self.status_bar = StatusBar()
+ self.vbox.pack_start(self.status_bar, False, False, 0)
+ self.create_instrument_pages(scene.status(), rt_status)
+
+ def create_master(self, scene):
+ scene_status = scene.status()
+ self.master_info = left_label("")
+ self.timesig_info = left_label("")
+
+ t = Gtk.Grid()
+ t.set_column_spacing(5)
+ t.set_row_spacing(5)
+
+ t.attach(bold_label("Scene"), 0, 0, 1, 1)
+ self.scene_label = left_label(scene_status.name)
+ t.attach(self.scene_label, 1, 0, 2, 1)
+
+ self.title_label = left_label(scene_status.title)
+ t.attach(bold_label("Title"), 0, 1, 1, 1)
+ t.attach(self.title_label, 1, 1, 2, 1)
+
+ t.attach(bold_label("Play pos"), 0, 2, 1, 1)
+ t.attach(self.master_info, 1, 2, 2, 1)
+
+ t.attach(bold_label("Time sig"), 0, 3, 1, 1)
+ t.attach(self.timesig_info, 1, 3, 2, 1)
+ hb = Gtk.HButtonBox()
+ b = Gtk.Button(label="Play")
+ b.connect('clicked', lambda w: cbox.Transport.play())
+ hb.pack_start(b, False, False, 5)
+ b = Gtk.Button(label="Stop")
+ b.connect('clicked', lambda w: cbox.Transport.stop())
+ hb.pack_start(b, False, False, 5)
+ b = Gtk.Button(label="Rewind")
+ b.connect('clicked', lambda w: cbox.Transport.seek_ppqn(0))
+ hb.pack_start(b, False, False, 5)
+ b = Gtk.Button(label="Panic")
+ b.connect('clicked', lambda w: cbox.Transport.panic())
+ hb.pack_start(b, False, False, 5)
+ t.attach(hb, 2, 3, 1, 1)
+
+ t.attach(bold_label("Tempo"), 0, 4, 1, 1)
+ self.tempo_adj = Gtk.Adjustment(value=40, lower=40, upper=300, step_increment=1, page_increment=5, page_size=0)
+ self.tempo_adj.connect('value_changed', adjustment_changed_float, cbox.VarPath("/master/set_tempo"))
+ t.attach(standard_hslider(self.tempo_adj), 1, 4, 2, 1)
+
+ t.attach(bold_label("Transpose"), 0, 5, 1, 1)
+ self.transpose_adj = Gtk.Adjustment(value=scene_status.transpose, lower=-24, upper=24, step_increment=1, page_increment=5, page_size=0)
+ self.transpose_adj.connect('value_changed', adjustment_changed_int, cbox.VarPath('/scene/transpose'))
+ t.attach(standard_align(Gtk.SpinButton(adjustment = self.transpose_adj), 0, 0, 0, 0), 1, 5, 2, 1)
+
+ self.layers_model = SceneLayersModel()
+ self.layers_view = SceneLayersView(self.layers_model)
+ t.attach(standard_vscroll_window(-1, 160, self.layers_view), 0, 7, 3, 1)
+
+ self.auxes_model = SceneAuxBusesModel()
+ self.auxes_view = SceneAuxBusesView(self.auxes_model)
+ t.attach(standard_vscroll_window(-1, 120, self.auxes_view), 0, 8, 3, 1)
+
+ me = cbox.Document.get_engine().master_effect
+ me_status = me.status()
+
+ hb = Gtk.HBox(spacing = 5)
+ self.master_chooser = fx_gui.InsertEffectChooser(me.path, "slot", me_status.insert_engine, me_status.insert_preset, me_status.bypass, self)
+ hb.pack_start(self.master_chooser.fx_engine, True, True, 0)
+ hb.pack_start(self.master_chooser.fx_preset, True, True, 5)
+ hb.pack_start(self.master_chooser.fx_edit, False, False, 5)
+ hb.pack_start(self.master_chooser.fx_bypass, False, False, 5)
+
+ t.attach(bold_label("Master effect"), 0, 6, 1, 1)
+ t.attach(standard_align(hb, 0, 0, 0, 0), 1, 6, 3, 1)
+
+ self.layers_model.refresh(scene_status)
+ self.auxes_model.refresh(scene_status)
+
+ return t
+
+ def quit(self, w):
+ self.destroy()
+
+ def load_scene(self, w):
+ d = SceneDialog(self)
+ response = d.run()
+ try:
+ if response == Gtk.ResponseType.OK:
+ scene = cbox.Document.get_scene()
+ item_name, item_type, item_key, item_label = d.get_selected_object()
+ if item_type == 'Scene':
+ scene.load(item_name)
+ elif item_type == 'Layer':
+ scene.clear()
+ scene.add_layer(item_name)
+ elif item_type == 'Instrument':
+ scene.clear()
+ scene.add_instrument_layer(item_name)
+ scene_status = scene.status()
+ self.scene_label.set_text(scene_status.name)
+ self.title_label.set_text(scene_status.title)
+ self.refresh_instrument_pages(scene_status)
+ finally:
+ d.destroy()
+
+ def layer_load(self, w):
+ d = LoadLayerDialog(self)
+ response = d.run()
+ try:
+ if response == Gtk.ResponseType.OK:
+ scene = cbox.Document.get_scene()
+ item_name, item_type, item_key, item_label = d.get_selected_object()
+ if item_type == 'Layer':
+ scene.add_layer(item_name)
+ elif item_type == 'Instrument':
+ scene.add_instrument_layer(item_name)
+ self.refresh_instrument_pages()
+ finally:
+ d.destroy()
+
+ def layer_new(self, w):
+ d = NewLayerDialog(self)
+ response = d.run()
+ try:
+ if response == Gtk.ResponseType.OK:
+ scene = cbox.Document.get_scene()
+ keys = scene.status().instruments.keys()
+ engine_name = d.get_selected_object()[0]
+ for i in range(1, 1001):
+ name = "%s%s" % (engine_name, i)
+ if name not in keys:
+ break
+ scene.add_new_instrument_layer(name, engine_name)
+ self.refresh_instrument_pages()
+ finally:
+ d.destroy()
+
+ def layer_remove(self, w):
+ if self.layers_view.get_cursor()[0] is not None:
+ pos = self.layers_view.get_cursor()[0][0]
+ cbox.Document.get_scene().delete_layer(pos)
+ self.refresh_instrument_pages()
+
+ def aux_bus_add(self, w):
+ d = fx_gui.LoadEffectDialog(self)
+ response = d.run()
+ try:
+ cbox.do_cmd("/scene/load_aux", None, [d.get_selected_object()[0]])
+ self.refresh_instrument_pages()
+ finally:
+ d.destroy()
+ def aux_bus_remove(self, w):
+ rowid, row = self.auxes_view.get_current_row()
+ if rowid is None:
+ return
+ cbox.do_cmd("/scene/delete_aux", None, [row[0]])
+ self.refresh_instrument_pages()
+
+ def aux_bus_edit(self, w):
+ rowid, row = self.auxes_view.get_current_row()
+ if rowid is None:
+ return
+ wclass = fx_gui.effect_window_map[row[1]]
+ popup = wclass("Aux: %s" % row[0], self, "/scene/aux/%s/slot/engine" % row[0])
+ popup.show_all()
+ popup.present()
+
+ def tools_unzombify(self, w):
+ cbox.do_cmd("/rt/cycle", None, [])
+
+ def tools_drumkit_editor(self, w):
+ if self.drumkit_editor is None:
+ self.drumkit_editor = drumkit_editor.EditorDialog(self)
+ self.refresh_instrument_pages()
+ self.drumkit_editor.connect('destroy', self.on_drumkit_editor_destroy)
+ self.drumkit_editor.show_all()
+ self.drumkit_editor.present()
+
+ def on_drumkit_editor_destroy(self, w):
+ self.drumkit_editor = None
+
+ def tools_object_list(self, w):
+ cbox.Document.dump()
+
+ def tools_wave_bank_dump(self, w):
+ for w in cbox.get_thing('/waves/list', '/waveform', [str]):
+ info = cbox.GetThings("/waves/info", ["filename", "name", "bytes", "loop"], [w])
+ print("%s: %d bytes, loop = %s" % (info.filename, info.bytes, info.loop))
+
+ def tools_play_drum_pattern(self, w):
+ d = PlayPatternDialog(self)
+ response = d.run()
+ try:
+ if response == Gtk.ResponseType.OK:
+ row = d.get_selected_object()
+ if row[1] == 'Pattern':
+ song = cbox.Document().get_song()
+ song.loop_single_pattern(lambda: song.load_drum_pattern(row[0]))
+ elif row[1] == 'Track':
+ song = cbox.Document().get_song()
+ song.loop_single_pattern(lambda: song.load_drum_track(row[0]))
+ elif row[1] == 'Stop':
+ song = cbox.Document().get_song()
+ song.clear()
+ song.update_playback()
+ tracks = song.status().tracks
+ if len(tracks):
+ for track_item in tracks:
+ track_item.track.set_external_output(cbox.Document.get_scene().uuid)
+ song.update_playback()
+
+ finally:
+ d.destroy()
+
+ def tools_drum_pattern_editor(self, w):
+ if self.drum_pattern_editor is None:
+ length = drum_pattern_editor.PPQN * 4
+ pat_data = cbox.Pattern.get_pattern()
+ if pat_data is not None:
+ pat_data, length = pat_data
+ self.drum_pattern_editor = drum_pattern_editor.DrumSeqWindow(length, pat_data)
+ self.drum_pattern_editor.set_title("Drum pattern editor")
+ self.drum_pattern_editor.show_all()
+ self.drum_pattern_editor.connect('destroy', self.on_drum_pattern_editor_destroy)
+ self.drum_pattern_editor.pattern.connect('changed', self.on_drum_pattern_changed)
+ self.drum_pattern_editor.pattern.changed()
+ self.drum_pattern_editor.present()
+
+ def on_drum_pattern_changed(self, pattern):
+ data = bytearray()
+ for i in pattern.items():
+ ch = i.channel - 1
+ data += cbox.Pattern.serialize_event(int(i.pos), 0x90 + ch, int(i.row), int(i.vel))
+ if i.len > 1:
+ data += cbox.Pattern.serialize_event(int(i.pos + i.len - 1), 0x80 + ch, int(i.row), int(i.vel))
+ else:
+ data += cbox.Pattern.serialize_event(int(i.pos + 1), 0x80 + ch, int(i.row), int(i.vel))
+
+ length = pattern.get_length()
+
+ song = cbox.Document().get_song()
+ song.loop_single_pattern(lambda: song.pattern_from_blob(data, length))
+
+ def on_drum_pattern_editor_destroy(self, w):
+ self.drum_pattern_editor = None
+
+ def refresh_instrument_pages(self, scene_status = None):
+ self.delete_instrument_pages()
+ rt_status = cbox.Document.get_rt().status()
+ if scene_status is None:
+ scene_status = cbox.Document.get_scene().status()
+ self.layers_model.refresh(scene_status)
+ self.auxes_model.refresh(scene_status)
+ self.create_instrument_pages(scene_status, rt_status)
+ self.nb.show_all()
+ self.title_label.set_text(scene_status.title)
+
+ def create_instrument_pages(self, scene_status, rt_status):
+ self.path_widgets = {}
+ self.path_popups = {}
+ self.fx_choosers = {}
+
+ outputs_ls = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_INT)
+ for out in range(0, rt_status.audio_channels[1]//2):
+ outputs_ls.append(("Out %s/%s" % (out * 2 + 1, out * 2 + 2), out))
+
+ auxbus_ls = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_STRING)
+ auxbus_ls.append(("", ""))
+ for bus_name in scene_status.auxes.keys():
+ auxbus_ls.append(("Aux: %s" % bus_name, bus_name))
+
+ for iname, (iengine, iobj) in scene_status.instruments.items():
+ ipath = "/scene/instr/%s" % iname
+ idata = iobj.status()
+ #attribs = cbox.GetThings("/scene/instr_info", ['engine', 'name'], [i])
+ #markup += 'Instrument %d: engine %s, name %s\n' % (i, attribs.engine, attribs.name)
+ b = Gtk.VBox(spacing = 5)
+ b.set_border_width(5)
+ b.pack_start(Gtk.Label(label="Engine: %s" % iengine), False, False, 5)
+ b.pack_start(Gtk.HSeparator(), False, False, 5)
+ t = Gtk.Table(n_rows=1 + idata.outputs, n_columns=7)
+ t.attach(bold_label("Instr. output", 0.5), 0, 1, 0, 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
+ t.attach(bold_label("Send to", 0.5), 1, 2, 0, 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
+ t.attach(bold_label("Gain [dB]", 0.5), 2, 3, 0, 1, 0, Gtk.AttachOptions.SHRINK)
+ t.attach(bold_label("Effect", 0.5), 3, 4, 0, 1, 0, Gtk.AttachOptions.SHRINK)
+ t.attach(bold_label("Preset", 0.5), 4, 7, 0, 1, 0, Gtk.AttachOptions.SHRINK)
+ b.pack_start(t, False, False, 5)
+
+ y = 1
+ for o in range(1, idata.outputs + 1):
+ is_aux = o >= idata.aux_offset
+ if not is_aux:
+ opath = "%s/output/%s" % (ipath, o)
+ output_name = "Out %s" % o
+ else:
+ opath = "%s/aux/%s" % (ipath, o - idata.aux_offset + 1)
+ output_name = "Aux %s" % (o - idata.aux_offset + 1)
+ odata = cbox.GetThings(opath + "/status", ['gain', 'output', 'bus', 'insert_engine', 'insert_preset', 'bypass'], [])
+ engine = odata.insert_engine
+ preset = odata.insert_preset
+ bypass = odata.bypass
+
+ t.attach(Gtk.Label(label=output_name), 0, 1, y, y + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
+
+ if not is_aux:
+ cb = standard_combo(outputs_ls, odata.output - 1)
+ cb.connect('changed', combo_value_changed, cbox.VarPath(opath + '/output'), 1)
+ else:
+ cb = standard_combo(auxbus_ls, ls_index(auxbus_ls, odata.bus, 1))
+ cb.connect('changed', combo_value_changed_use_column, cbox.VarPath(opath + '/bus'), 1)
+ t.attach(cb, 1, 2, y, y + 1, Gtk.AttachOptions.SHRINK, Gtk.AttachOptions.SHRINK)
+
+ adj = Gtk.Adjustment(value=odata.gain, lower=-96, upper=24, step_increment=1, page_increment=6, page_size=0)
+ adj.connect('value_changed', adjustment_changed_float, cbox.VarPath(opath + '/gain'))
+ t.attach(standard_hslider(adj), 2, 3, y, y + 1, Gtk.AttachOptions.EXPAND | Gtk.AttachOptions.FILL, Gtk.AttachOptions.SHRINK)
+
+ chooser = fx_gui.InsertEffectChooser(opath, "%s: %s" % (iname, output_name), engine, preset, bypass, self)
+ self.fx_choosers[opath] = chooser
+ t.attach(chooser.fx_engine, 3, 4, y, y + 1, 0, Gtk.AttachOptions.SHRINK)
+ t.attach(chooser.fx_preset, 4, 5, y, y + 1, 0, Gtk.AttachOptions.SHRINK)
+ t.attach(chooser.fx_edit, 5, 6, y, y + 1, 0, Gtk.AttachOptions.SHRINK)
+ t.attach(chooser.fx_bypass, 6, 7, y, y + 1, 0, Gtk.AttachOptions.SHRINK)
+ y += 1
+ if iengine in instr_gui.instrument_window_map:
+ b.pack_start(Gtk.HSeparator(), False, False, 5)
+ b.pack_start(instr_gui.instrument_window_map[iengine](iname, iobj), True, True, 5)
+ self.nb.append_page(b, Gtk.Label(label=iname))
+ self.update()
+
+ def delete_instrument_pages(self):
+ while self.nb.get_n_pages() > 1:
+ self.nb.remove_page(self.nb.get_n_pages() - 1)
+
+ def update(self):
+ cbox.call_on_idle()
+ master = cbox.Transport.status()
+ if master.tempo is not None:
+ self.master_info.set_markup('%s (%s)' % (master.pos, master.pos_ppqn))
+ self.timesig_info.set_markup("%s/%s" % tuple(master.timesig))
+ self.tempo_adj.set_value(master.tempo)
+ state = cbox.Document.get_rt().status().state
+ self.status_bar.update(state[1], master.sample_rate)
+ return True
+
+def do_quit(window):
+ Gtk.main_quit()
+
+w = MainWindow()
+w.set_title("My UI")
+w.show_all()
+w.connect('destroy', do_quit)
+
+Gtk.main()
+
diff --git a/template/calfbox/experiments/interactive.py b/template/calfbox/experiments/interactive.py
new file mode 100755
index 0000000..88d3ed7
--- /dev/null
+++ b/template/calfbox/experiments/interactive.py
@@ -0,0 +1,57 @@
+#! /usr/bin/env -S python3 -i
+# -*- coding: utf-8 -*-
+"""
+This is a minimal calfbox python example. It is meant as a starting
+point to find bugs and test performance.
+
+Copyright 2019, Nils Hilbricht, Germany ( https://www.hilbricht.net )
+
+This code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+import atexit
+from pprint import pprint
+from calfbox import cbox
+
+
+cbox.init_engine("")
+cbox.Config.set("io", "outputs", 8)
+NAME = "Cbox Interactive"
+cbox.Config.set("io", "client_name", NAME)
+
+cbox.start_audio()
+scene = cbox.Document.get_engine().new_scene()
+scene.clear()
+
+trackName = "trackOne"
+cboxMidiOutUuid = cbox.JackIO.create_midi_output(trackName)
+calfboxTrack = cbox.Document.get_song().add_track()
+
+
+pblob = bytes()
+pblob += cbox.Pattern.serialize_event(0, 0x90, 60, 100) # note on
+pblob += cbox.Pattern.serialize_event(383, 0x80, 60, 100) # note off
+pattern = cbox.Document.get_song().pattern_from_blob(pblob, 384)
+calfboxTrack.add_clip(0, 0, 384, pattern) #pos, offset, length(and not end-position, but is the same for the complete track), pattern
+cbox.Document.get_song().set_loop(384, 384) #set playback length for the entire score. Why is the first value not zero? That would create an actual loop from the start to end. We want the song to play only once. The cbox way of doing that is to set the loop range to zero at the end of the track. Zero length is stop.
+cbox.Document.get_song().update_playback()
+
+print()
+
+def exit_handler():
+ #Restore initial state and stop the engine
+ cbox.Transport.stop()
+ cbox.stop_audio()
+ cbox.shutdown_engine()
+atexit.register(exit_handler)
diff --git a/template/calfbox/experiments/meta.py b/template/calfbox/experiments/meta.py
new file mode 100644
index 0000000..cd98241
--- /dev/null
+++ b/template/calfbox/experiments/meta.py
@@ -0,0 +1,602 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net )
+
+This code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+
+import re
+from calfbox import cbox #use the globally installed calfbox
+from asyncio import get_event_loop
+from sys import stdout, maxsize
+import os, signal
+
+D1024 =210 * 2**0 # = 210. The lcm of 2, 3, 5, 7 . according to www.informatics.indiana.edu/donbyrd/CMNExtremes.htm this is the real world limit.
+D512 = 210 * 2**1
+D256 = 210 * 2**2
+D128 = 210 * 2**3
+D64 = 210 * 2**4
+D32 = 210 * 2**5
+D16 = 210 * 2**6 #16th 13440 ticks
+D8 = 210 * 2**7 #eigth 26880 ticks
+D4 = 210 * 2**8 #quarter 53760 ticks
+D2 = 210 * 2**9 #half 107520 ticks
+D1 = 210 * 2**10 #whole 215040 ticks
+DB = 210 * 2**11 #brevis 430080 ticks
+DL = 210 * 2**12 #longa
+DM = 210 * 2**13 #maxima
+#MAXIMUM = 0x7FFFFFFF # 31bit. maximum number of calfbox ticks allowed for its timeline, for example for the song duration
+MAXIMUM = 100 * D1
+#max_pulses = min(2**31, 2**31 * ppqn * bpm / (60 * sample_rate))
+
+ly2pitch = {
+ "ceses,,," : 00,
+ "ces,,," : 10,
+ "c,,," : 20,
+ "cis,,," : 30,
+ "cisis,,," : 40,
+ "deses,,," : 50,
+ "des,,," : 60,
+ "d,,," : 70,
+ "dis,,," : 80,
+ "disis,,," : 90,
+ "eeses,,," : 100,
+ "ees,,," : 110,
+ "e,,," : 120,
+ "eis,,," : 130,
+ "eisis,,," : 140,
+ "feses,,," : 150,
+ "fes,,," : 160,
+ "f,,," : 170,
+ "fis,,," : 180,
+ "fisis,,," : 190,
+ "geses,,," : 200,
+ "ges,,," : 210,
+ "g,,," : 220,
+ "gis,,," : 230,
+ "gisis,,," : 240,
+ "aeses,,," : 250,
+ "aes,,," : 260,
+ "a,,," : 270,
+ "ais,,," : 280,
+ "aisis,,," : 290,
+ "beses,,," : 300,
+ "bes,,," : 310,
+ "b,,," : 320,
+ "bis,,," : 330,
+ "bisis,,," : 340,
+ "ceses,," : 350,
+ "ces,," : 360,
+ "c,," : 370,
+ "cis,," : 380,
+ "cisis,," : 390,
+ "deses,," : 400,
+ "des,," : 410,
+ "d,," : 420,
+ "dis,," : 430,
+ "disis,," : 440,
+ "eeses,," : 450,
+ "ees,," : 460,
+ "e,," : 470,
+ "eis,," : 480,
+ "eisis,," : 490,
+ "feses,," : 500,
+ "fes,," : 510,
+ "f,," : 520,
+ "fis,," : 530,
+ "fisis,," : 540,
+ "geses,," : 550,
+ "ges,," : 560,
+ "g,," : 570,
+ "gis,," : 580,
+ "gisis,," : 590,
+ "aeses,," : 600,
+ "aes,," : 610,
+ "a,," : 620,
+ "ais,," : 630,
+ "aisis,," : 640,
+ "beses,," : 650,
+ "bes,," : 660,
+ "b,," : 670,
+ "bis,," : 680,
+ "bisis,," : 690,
+ "ceses," : 700,
+ "ces," : 710,
+ "c," : 720,
+ "cis," : 730,
+ "cisis," : 740,
+ "deses," : 750,
+ "des," : 760,
+ "d," : 770,
+ "dis," : 780,
+ "disis," : 790,
+ "eeses," : 800,
+ "ees," : 810,
+ "e," : 820,
+ "eis," : 830,
+ "eisis," : 840,
+ "feses," : 850,
+ "fes," : 860,
+ "f," : 870,
+ "fis," : 880,
+ "fisis," : 890,
+ "geses," : 900,
+ "ges," : 910,
+ "g," : 920,
+ "gis," : 930,
+ "gisis," : 940,
+ "aeses," : 950,
+ "aes," : 960,
+ "a," : 970,
+ "ais," : 980,
+ "aisis," : 990,
+ "beses," : 1000,
+ "bes," : 1010,
+ "b," : 1020,
+ "bis," : 1030,
+ "bisis," : 1040,
+ "ceses" : 1050,
+ "ces" : 1060,
+ "c" : 1070,
+ "cis" : 1080,
+ "cisis" : 1090,
+ "deses" : 1100,
+ "des" : 1110,
+ "d" : 1120,
+ "dis" : 1130,
+ "disis" : 1140,
+ "eeses" : 1150,
+ "ees" : 1160,
+ "e" : 1170,
+ "eis" : 1180,
+ "eisis" : 1190,
+ "feses" : 1200,
+ "fes" : 1210,
+ "f" : 1220,
+ "fis" : 1230,
+ "fisis" : 1240,
+ "geses" : 1250,
+ "ges" : 1260,
+ "g" : 1270,
+ "gis" : 1280,
+ "gisis" : 1290,
+ "aeses" : 1300,
+ "aes" : 1310,
+ "a" : 1320,
+ "ais" : 1330,
+ "aisis" : 1340,
+ "beses" : 1350,
+ "bes" : 1360,
+ "b" : 1370,
+ "bis" : 1380,
+ "bisis" : 1390,
+ "ceses'" : 1400,
+ "ces'" : 1410,
+ "c'" : 1420,
+ "cis'" : 1430,
+ "cisis'" : 1440,
+ "deses'" : 1450,
+ "des'" : 1460,
+ "d'" : 1470,
+ "dis'" : 1480,
+ "disis'" : 1490,
+ "eeses'" : 1500,
+ "ees'" : 1510,
+ "e'" : 1520,
+ "eis'" : 1530,
+ "eisis'" : 1540,
+ "feses'" : 1550,
+ "fes'" : 1560,
+ "f'" : 1570,
+ "fis'" : 1580,
+ "fisis'" : 1590,
+ "geses'" : 1600,
+ "ges'" : 1610,
+ "g'" : 1620,
+ "gis'" : 1630,
+ "gisis'" : 1640,
+ "aeses'" : 1650,
+ "aes'" : 1660,
+ "a'" : 1670,
+ "ais'" : 1680,
+ "aisis'" : 1690,
+ "beses'" : 1700,
+ "bes'" : 1710,
+ "b'" : 1720,
+ "bis'" : 1730,
+ "bisis'" : 1740,
+ "ceses''" : 1750,
+ "ces''" : 1760,
+ "c''" : 1770,
+ "cis''" : 1780,
+ "cisis''" : 1790,
+ "deses''" : 1800,
+ "des''" : 1810,
+ "d''" : 1820,
+ "dis''" : 1830,
+ "disis''" : 1840,
+ "eeses''" : 1850,
+ "ees''" : 1860,
+ "e''" : 1870,
+ "eis''" : 1880,
+ "eisis''" : 1890,
+ "feses''" : 1900,
+ "fes''" : 1910,
+ "f''" : 1920,
+ "fis''" : 1930,
+ "fisis''" : 1940,
+ "geses''" : 1950,
+ "ges''" : 1960,
+ "g''" : 1970,
+ "gis''" : 1980,
+ "gisis''" : 1990,
+ "aeses''" : 2000,
+ "aes''" : 2010,
+ "a''" : 2020,
+ "ais''" : 2030,
+ "aisis''" : 2040,
+ "beses''" : 2050,
+ "bes''" : 2060,
+ "b''" : 2070,
+ "bis''" : 2080,
+ "bisis''" : 2090,
+ "ceses'''" : 2100,
+ "ces'''" : 2110,
+ "c'''" : 2120,
+ "cis'''" : 2130,
+ "cisis'''" : 2140,
+ "deses'''" : 2150,
+ "des'''" : 2160,
+ "d'''" : 2170,
+ "dis'''" : 2180,
+ "disis'''" : 2190,
+ "eeses'''" : 2200,
+ "ees'''" : 2210,
+ "e'''" : 2220,
+ "eis'''" : 2230,
+ "eisis'''" : 2240,
+ "feses'''" : 2250,
+ "fes'''" : 2260,
+ "f'''" : 2270,
+ "fis'''" : 2280,
+ "fisis'''" : 2290,
+ "geses'''" : 2300,
+ "ges'''" : 2310,
+ "g'''" : 2320,
+ "gis'''" : 2330,
+ "gisis'''" : 2340,
+ "aeses'''" : 2350,
+ "aes'''" : 2360,
+ "a'''" : 2370,
+ "ais'''" : 2380,
+ "aisis'''" : 2390,
+ "beses'''" : 2400,
+ "bes'''" : 2410,
+ "b'''" : 2420,
+ "bis'''" : 2430,
+ "bisis'''" : 2440,
+ "ceses''''" : 2450,
+ "ces''''" : 2460,
+ "c''''" : 2470,
+ "cis''''" : 2480,
+ "cisis''''" : 2490,
+ "deses''''" : 2500,
+ "des''''" : 2510,
+ "d''''" : 2520,
+ "dis''''" : 2530,
+ "disis''''" : 2540,
+ "eeses''''" : 2550,
+ "ees''''" : 2560,
+ "e''''" : 2570,
+ "eis''''" : 2580,
+ "eisis''''" : 2590,
+ "feses''''" : 2600,
+ "fes''''" : 2610,
+ "f''''" : 2620,
+ "fis''''" : 2630,
+ "fisis''''" : 2640,
+ "geses''''" : 2650,
+ "ges''''" : 2660,
+ "g''''" : 2670,
+ "gis''''" : 2680,
+ "gisis''''" : 2690,
+ "aeses''''" : 2700,
+ "aes''''" : 2710,
+ "a''''" : 2720,
+ "ais''''" : 2730,
+ "aisis''''" : 2740,
+ "beses''''" : 2750,
+ "bes''''" : 2760,
+ "b''''" : 2770,
+ "bis''''" : 2780,
+ "bisis''''" : 2790,
+ "ceses'''''" : 2800,
+ "ces'''''" : 2810,
+ "c'''''" : 2820,
+ "cis'''''" : 2830,
+ "cisis'''''" : 2840,
+ "deses'''''" : 2850,
+ "des'''''" : 2860,
+ "d'''''" : 2870,
+ "dis'''''" : 2880,
+ "disis'''''" : 2890,
+ "eeses'''''" : 2900,
+ "ees'''''" : 2910,
+ "e'''''" : 2920,
+ "eis'''''" : 2930,
+ "eisis'''''" : 2940,
+ "feses'''''" : 2950,
+ "fes'''''" : 2960,
+ "f'''''" : 2970,
+ "fis'''''" : 2980,
+ "fisis'''''" : 2990,
+ "geses'''''" : 3000,
+ "ges'''''" : 3010,
+ "g'''''" : 3020,
+ "gis'''''" : 3030,
+ "gisis'''''" : 3040,
+ "aeses'''''" : 3050,
+ "aes'''''" : 3060,
+ "a'''''" : 3070,
+ "ais'''''" : 3080,
+ "aisis'''''" : 3090,
+ "beses'''''" : 3100,
+ "bes'''''" : 3110,
+ "b'''''" : 3120,
+ "bis'''''" : 3130,
+ "bisis'''''" : 3140,
+ #"r" : float('inf'), a rest is not a pitch
+ }
+
+def plain(pitch):
+ """ Extract the note from a note-number, without any octave but with the tailing zero.
+ This means we double-use the lowest octave as abstract version."""
+ #Dividing through the octave, 350, results in the number of the octave and the note as remainder.
+ return divmod(pitch, 350)[1]
+
+def octave(pitch):
+ """Return the octave of given note. Lowest 0 is X,,,"""
+ return divmod(pitch, 350)[0]
+
+def halfToneDistanceFromC(pitch):
+ """Return the half-tone step distance from C. The "sounding" interval"""
+ return {
+ #00 : 10, # ceses,,, -> bes
+ #10 : 11, # ces,,, -> b
+
+ 00 : -2, # ceses,,, -> bes
+ 10 : -1, # ces,,, -> b
+ 20 : 0, # c,,,
+ 30 : 1, # cis,,,
+ 40 : 2, # cisis,,, -> d ...
+ 50 : 0, # deses,,,
+ 60 : 1, # des,,,
+ 70 : 2, # d,,,
+ 80 : 3, # dis,,,
+ 90 : 4, # disis,,,
+ 100 : 2, # eeses,,,
+ 110 : 3, # ees,,,
+ 120 : 4, # e,,,
+ 130 : 5, # eis,,,
+ 140 : 6, # eisis,,,
+ 150 : 3, # feses,,,
+ 160 : 4, # fes,,,
+ 170 : 5, # f,,,
+ 180 : 6, # fis,,,
+ 190 : 7, # fisis,,,
+ 200 : 5, # geses,,,
+ 210 : 6, # ges,,,
+ 220 : 7, # g,,,
+ 230 : 8, # gis,,,
+ 240 : 9, # gisis,,,
+ 250 : 7, # aeses,,,
+ 260 : 8, # aes,,,
+ 270 : 9, # a,,,
+ 280 : 10, # ais,,,
+ 290 : 11, # aisis,,,
+ 300 : 9, # beses,,,
+ 310 : 10, # bes,,,
+ 320 : 11, # b,,,
+ 330 : 12, # bis,,,
+ 340 : 13, # bisis,,,
+ #330 : 0, # bis,,,
+ #340 : 1, # bisis,,,
+ }[plain(pitch)]
+
+
+lyToMidi = {} #filled for all pitches on startup, below
+for ly, pitch in ly2pitch.items():
+ octOffset = (octave(pitch) +1) * 12 #twelve tones per midi octave
+ lyToMidi[ly] = octOffset + halfToneDistanceFromC(pitch)
+
+
+lyToTicks = {
+ "16" : D16,
+ "8" : D8,
+ "4" : D4,
+ "2" : D2,
+ "1" : D1,
+ }
+
+
+
+def ly(lilypondString):
+ """Take string of simple lilypond notes, return midi pitches as generator of (pitch, ticks)"""
+ lastDur = "4"
+ for lyNote in lilypondString.split(" "):
+ try:
+ lyPitch, lyDur = re.split(r'(\d+)', lyNote)[0:2]
+ lastDur = lyDur
+ except ValueError:
+ lyPitch = re.split(r'(\d+)', lyNote)[0]
+ lyDur = lastDur
+
+ yield (lyToMidi[lyPitch], lyToTicks[lyDur])
+
+
+def ly2cbox(lilypondString):
+ """Return (pbytes, durationInTicks)
+ a python byte data type with midi data for cbox"""
+ #cbox.Pattern.serialize_event(position, midibyte1 (noteon), midibyte2(pitch), midibyte3(velocity))
+ pblob = bytes()
+ startTick = 0
+ for midiPitch, durationInTicks in ly(lilypondString):
+ endTick = startTick + durationInTicks - 1 #-1 ticks to create a small logical gap. This is nothing compared to our tick value dimensions, but it is enough for the midi protocol to treat two notes as separate ones. Imporant to say that this does NOT affect the next note on. This will be mathematically correct anyway.
+ pblob += cbox.Pattern.serialize_event(startTick, 0x90, midiPitch, 100) # note on
+ pblob += cbox.Pattern.serialize_event(endTick , 0x80, midiPitch, 100) # note off
+ startTick = startTick + durationInTicks #no -1 for the next note
+ return pblob, startTick
+
+
+cboxTracks = {} #trackName:(cboxTrack,cboxMidiOutUuid)
+def cboxSetTrack(trackName, durationInTicks, pattern):
+ """Creates or resets calfbox tracks including jack connections
+ Keeps jack connections alive.
+
+ pattern is most likely a single pattern created through cbox.Document.get_song().pattern_from_blob
+ But it can also be a list of such patterns. In this case all patterns must be the same duration
+ and the parameter durationInTicks is the length of ONE pattern.
+ """
+
+ if not trackName in cboxTracks:
+ cboxMidiOutUuid = cbox.JackIO.create_midi_output(trackName)
+ calfboxTrack = cbox.Document.get_song().add_track()
+ cboxTracks[trackName] = (calfboxTrack, cboxMidiOutUuid)
+ else:
+ calfboxTrack, cboxMidiOutUuid = cboxTracks[trackName]
+ calfboxTrack.delete()
+
+ calfboxTrack = cbox.Document.get_song().add_track()
+ calfboxTrack.set_external_output(cboxMidiOutUuid)
+ cbox.JackIO.rename_midi_output(cboxMidiOutUuid, trackName)
+ calfboxTrack.set_name(trackName)
+
+ if type(pattern) is cbox.DocPattern:
+ calfboxTrack.add_clip(0, 0, durationInTicks, pattern) #pos, offset, length(and not end-position, but is the same for the complete track), pattern
+ else: #iterable
+ assert iter(pattern)
+ #durationInTicks is the length of ONE pattern.
+ for i, pat in enumerate(pattern):
+ calfboxTrack.add_clip(i*durationInTicks, 0, durationInTicks, pat) #pos, offset, length, pattern.
+ calfboxTrack.add_clip(i*durationInTicks, 0, durationInTicks, pat) #pos, offset, length, pattern.
+
+ return calfboxTrack
+
+
+def ly2Track(trackName, lyString):
+ """Convert a simple string of lilypond notes to a cbox track and add that to the score"""
+ music = lyString
+ cboxBlob, durationInTicks = ly2cbox(music)
+ pattern = cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks)
+ cboxSetTrack(trackName, durationInTicks, pattern)
+
+def getLongestTrackDuationInTicks():
+ return max( max(clip.pos + clip.length for clip in cboxTrack.track.status().clips) for cboxTrack in cbox.Document.get_song().status().tracks if cboxTrack.track.status().clips)
+ #for cboxTrack in cbox.Document.get_song().status().tracks:
+ # print (cboxTrack.track)
+ #TODO: These are different than the one above. It is more. Why?
+ #for cboxTrack, cboxMidiOutUuid in cboxTracks.values():
+ # print (cboxTrack.status())
+
+
+def cboxLoop(eventLoop):
+ cbox.call_on_idle()
+ assert eventLoop.is_running()
+
+ #it is not that simple. status = "[Running]" if cbox.Transport.status().playing else "[Stopped]"
+ if cbox.Transport.status().playing == 1:
+ status = "[Running]"
+ elif cbox.Transport.status().playing == 0:
+ status = "[Stopped]"
+ elif cbox.Transport.status().playing == 2:
+ status = "[Stopping]"
+ elif cbox.Transport.status().playing is None:
+ status = "[Uninitialized]"
+ else:
+ raise ValueError("Unknown playback status: {}".format(cbox.Transport.status().playing))
+
+ stdout.write(" \r") #it is a hack but it cleans the line from old artefacts
+ stdout.write('{}: {}\r'.format(status, cbox.Transport.status().pos_ppqn))
+ stdout.flush()
+ eventLoop.call_later(0.1, cboxLoop, eventLoop) #100ms delay
+
+eventLoop = get_event_loop()
+def initCbox(clientName, internalEventProcessor=True, commonMidiInput=True):
+ cbox.init_engine("")
+ cbox.Config.set("io", "client_name", clientName)
+ cbox.Config.set("io", "enable_common_midi_input", commonMidiInput) #the default "catch all" midi input
+ cbox.start_audio()
+ scene = cbox.Document.get_engine().new_scene()
+ scene.clear()
+ cbox.do_cmd("/master/set_ppqn_factor", None, [D4]) #quarter note has how many ticks?
+
+ cbox.Transport.stop()
+ cbox.Transport.seek_ppqn(0)
+ cbox.Transport.set_tempo(120.0) #must be float
+
+ if internalEventProcessor:
+ eventLoop.call_soon(cboxLoop, eventLoop)
+ return scene, cbox, eventLoop
+
+
+def connectPhysicalKeyboards(port="midi"):
+ midiKeyboards = cbox.JackIO.get_ports(".*", cbox.JackIO.MIDI_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL)
+ ourMidiInPort = cbox.Config.get("io", "client_name",) + ":" + port
+ for keyboard in midiKeyboards:
+ cbox.JackIO.port_connect(keyboard, ourMidiInPort)
+
+def start(autoplay = False, userfunction = None, tempo = 120):
+ def ask_exit():
+ print()
+ eventLoop.stop()
+ shutdownCbox()
+
+ try:
+ dur = getLongestTrackDuationInTicks()
+ print("Starting with supplied music data. Setting sond duration to longest track")
+ assert dur > 0
+ cbox.Document.get_song().set_loop(dur, dur) #set playback length for the entire score.
+ except ValueError:
+ print ("Starting without a track. Setting song duration to a high value to generate recording space")
+ cbox.Document.get_song().set_loop(MAXIMUM, MAXIMUM) #set playback length for the entire score.
+
+ cbox.Transport.set_tempo(float(tempo))
+ cbox.Document.get_song().update_playback()
+
+ for signame in ('SIGINT', 'SIGTERM'):
+ eventLoop.add_signal_handler(getattr(signal, signame), ask_exit)
+
+ if userfunction:
+ print ("Send SIGUSR1 with following command to trigger user function")
+ print ("kill -10 {}".format(os.getpid()))
+ print ()
+ eventLoop.add_signal_handler(getattr(signal, "SIGUSR1"), userfunction)
+
+ print ("Use jack transport to control playback")
+ print ("Press Ctrl+C to abort")
+ print("pid %s: send SIGINT or SIGTERM to exit." % os.getpid())
+
+ try:
+ eventLoop.run_forever()
+ finally:
+ eventLoop.close()
+
+
+def shutdownCbox():
+ cbox.Transport.stop()
+ cbox.Transport.seek_ppqn(0)
+ cbox.stop_audio()
+ cbox.shutdown_engine()
diff --git a/template/calfbox/experiments/playPatternsAsMeasures.py b/template/calfbox/experiments/playPatternsAsMeasures.py
new file mode 100755
index 0000000..6a2368a
--- /dev/null
+++ b/template/calfbox/experiments/playPatternsAsMeasures.py
@@ -0,0 +1,51 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net )
+
+This code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+from meta import ly2cbox, cboxSetTrack, initCbox, start, D4
+
+scene, cbox, eventLoop = initCbox("test02")
+
+#Generate Music
+music = "c'4 d' e' f'"
+cboxBlob, durationInTicks = ly2cbox(music)
+#cboxSetTrack("someInstrument", durationInTicks, pattern)
+
+oneMeasureInTicks = D4 * 4
+patternList = [
+ cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks),
+ cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks),
+ cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks),
+ cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks),
+ cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks),
+ cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks),
+ cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks),
+ cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks),
+]
+
+cboxSetTrack("metronome", oneMeasureInTicks, patternList)
+
+def userfunction():
+ D4 = 210 * 2**8
+ MEASURE = 4 * D4
+ cbox.Transport.stop()
+ cbox.Document.get_song().update_playback()
+ cbox.Transport.seek_ppqn(4 * MEASURE + 2 * D4 ) #4th measure in the middle
+ cbox.Transport.play()
+
+start(userfunction=userfunction)
diff --git a/template/calfbox/experiments/printAllMidiEvents.py b/template/calfbox/experiments/printAllMidiEvents.py
new file mode 100755
index 0000000..408f795
--- /dev/null
+++ b/template/calfbox/experiments/printAllMidiEvents.py
@@ -0,0 +1,32 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net )
+
+This code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+from meta import initCbox, start, connectPhysicalKeyboards
+
+def processMidiIn(eventLoop):
+ eventList = cbox.get_new_events()
+ if eventList:
+ for (address, uninterestingStuff, event) in eventList: #we are only interested in the event, which is a list again.
+ print (address, "event:", event, "playback:", cbox.Transport.status().playing)
+ eventLoop.call_later(0.1, processMidiIn, eventLoop)
+
+scene, cbox, eventLoop = initCbox("test01", internalEventProcessor=False)
+eventLoop.call_later(0.1, processMidiIn, eventLoop) #100ms
+connectPhysicalKeyboards()
+start()
diff --git a/template/calfbox/experiments/printAllMidiEventsSpecificPort.py b/template/calfbox/experiments/printAllMidiEventsSpecificPort.py
new file mode 100755
index 0000000..8638c3d
--- /dev/null
+++ b/template/calfbox/experiments/printAllMidiEventsSpecificPort.py
@@ -0,0 +1,35 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net )
+
+This code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+from meta import initCbox, start, connectPhysicalKeyboards
+
+def processMidiIn(eventLoop):
+ eventList = cbox.JackIO.get_new_events(cboxMidiPortUid)
+ if eventList:
+ for (address, uninterestingStuff, event) in eventList: #we are only interested in the event, which is a list again.
+ print (address, "event:", event, "playback:", cbox.Transport.status().playing)
+ eventLoop.call_later(0.1, processMidiIn, eventLoop)
+
+scene, cbox, eventLoop = initCbox("test01", internalEventProcessor=False, commonMidiInput=False)
+cboxMidiPortUid = cbox.JackIO.create_midi_input("customInput")
+cbox.JackIO.set_appsink_for_midi_input(cboxMidiPortUid, True) #This sounds like a program wide sink, but it is needed for every port.
+cbox.JackIO.route_midi_input(cboxMidiPortUid, scene.uuid)
+eventLoop.call_later(0.1, processMidiIn, eventLoop) #100ms
+connectPhysicalKeyboards(port="customInput")
+start()
diff --git a/template/calfbox/experiments/printNotesOnlyDuringPlayback.py b/template/calfbox/experiments/printNotesOnlyDuringPlayback.py
new file mode 100755
index 0000000..29a5715
--- /dev/null
+++ b/template/calfbox/experiments/printNotesOnlyDuringPlayback.py
@@ -0,0 +1,79 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+from meta import ly2Track, initCbox, start, connectPhysicalKeyboards
+
+def processMidiIn(eventLoop):
+ """cbox event: ("event address", None, [firstByte, pitch, velocit])
+
+ Cbox event come in pairs
+
+ first is the normal midi-event + channel
+ channel = first & 0x0F
+ mode = first & 0xF0
+
+ Of course it doesn't need to be pitch and velocity.
+ But this is easier to document than "data2, data3"
+
+ Either call cbox.call_on_idle or cbox.get_new_events.
+ Both will clean the event queue but only the latter will give us
+ the results as python data.
+ """
+ eventList = cbox.get_new_events()
+ lenList = len(eventList)
+ if lenList >= 2 and lenList % 2 == 0:
+ for (address, uninterestingStuff, event) in eventList: #we are only interested in the event, which is a list again.
+ #print (address, "event:", event, "playback:", cbox.Transport.status().playing)
+ if address in ("/io/midi/event_time_samples", "/io/midi/event_time_ppqn", ):
+ assert len(event) == 1
+ #We are very strict at the moment. A timestamp is only allowed if there wasn't another timestamp waiting. It is strict because only simple_event with len==3 eat up the timestamp. Any other event will trigger an error.
+ if processMidiIn.lastTimestamp:
+ raise NotImplementedError("the previous event didn't eat up the timestamp")
+ else:
+ if address == "/io/midi/event_time_ppqn":
+ assert cbox.Transport.status().playing == 1
+ during_recording = True
+ else:
+ assert not cbox.Transport.status().playing == 1
+ during_recording = False
+ processMidiIn.lastTimestamp = event[0]
+
+ elif address == "/io/midi/simple_event" and len(event) == 3: #we can only unpack the event after knowing its length.
+ assert processMidiIn.lastTimestamp # Midi events are always preceded by timestamps
+ first, second, third = event
+ channel = first & 0x0F
+ mode = first & 0xF0 #0x90 note on, 0x80 note off and so on.
+
+ if mode == 0x90: #Note On. 144 in decimal
+ midipitch = second
+ velocity = third
+ if during_recording:
+ print("ON: {}, Channel: {}, Pitch: {}, Velocity: {}".format(processMidiIn.lastTimestamp, channel, midipitch, velocity))
+ #else:
+ # print("ON Time-Samples: {}, Channel: {}, Pitch: {}, Velocity: {}".format(processMidiIn.lastTimestamp, channel, midipitch, velocity))
+
+ elif mode == 0x80: #Note Off. 128 in decimal
+ midipitch = second
+ velocity = third
+ if during_recording:
+ print("OFF: {}, Channel: {}, Pitch: {}, Velocity: {}".format(processMidiIn.lastTimestamp, channel, midipitch, velocity))
+
+ #elif mode == 0xB0: #CC
+ # ccNumber = second
+ # ccValue = third
+ #else:
+ #discard the events
+
+ processMidiIn.lastTimestamp = None
+
+ else:
+ raise NotImplementedError("Address type {} unknown".format(address))
+
+
+ eventLoop.call_later(0.1, processMidiIn, eventLoop)
+processMidiIn.lastTimestamp = None
+
+scene, cbox, eventLoop = initCbox("test01", internalEventProcessor=False)
+#ly2Track(trackName="doWeNeedThis", lyString="c8")
+eventLoop.call_later(0.1, processMidiIn, eventLoop) #100ms
+connectPhysicalKeyboards()
+start()
diff --git a/template/calfbox/experiments/simplePlayback.py b/template/calfbox/experiments/simplePlayback.py
new file mode 100755
index 0000000..d36ef77
--- /dev/null
+++ b/template/calfbox/experiments/simplePlayback.py
@@ -0,0 +1,30 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net )
+
+This code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+from meta import ly2cbox, cboxSetTrack, initCbox, start
+
+scene, cbox, eventLoop = initCbox("test01")
+
+#Generate Music
+music = "c'4 d' e' f'2 g' c''"
+cboxBlob, durationInTicks = ly2cbox(music)
+pattern = cbox.Document.get_song().pattern_from_blob(cboxBlob, durationInTicks)
+cboxSetTrack("someInstrument", durationInTicks, pattern)
+
+start()
diff --git a/template/calfbox/experiments/simplerPlayback.py b/template/calfbox/experiments/simplerPlayback.py
new file mode 100755
index 0000000..1bf8d14
--- /dev/null
+++ b/template/calfbox/experiments/simplerPlayback.py
@@ -0,0 +1,25 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+Copyright 2017, Nils Hilbricht, Germany ( https://www.hilbricht.net )
+
+This code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+from meta import ly2Track, initCbox, start
+
+scene, cbox, eventLoop = initCbox("test01")
+
+ly2Track(trackName="someInstrument", lyString="c'4 d' e' f'2 g' c''")
+start()
diff --git a/template/calfbox/experiments/testmetadata.py b/template/calfbox/experiments/testmetadata.py
new file mode 100755
index 0000000..5954144
--- /dev/null
+++ b/template/calfbox/experiments/testmetadata.py
@@ -0,0 +1,82 @@
+#! /usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+This is a minimal calfbox python example. It is meant as a starting
+point to find bugs and test performance.
+
+Copyright 2018, Nils Hilbricht, Germany ( https://www.hilbricht.net )
+
+This code is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+
+import atexit
+from pprint import pprint
+from calfbox import cbox
+
+
+cbox.init_engine("")
+cbox.Config.set("io", "outputs", 8)
+NAME = "Cbox Interactive"
+cbox.Config.set("io", "client_name", NAME)
+
+cbox.start_audio()
+scene = cbox.Document.get_engine().new_scene()
+scene.clear()
+
+print("Setting nonsense meta data to our first two ports and midi port")
+cbox.JackIO.Metadata.set_property("Cbox Interactive:out_1", "foo", "bar")
+cbox.JackIO.Metadata.set_property("Cbox Interactive:out_1", "faz", "baz")
+cbox.JackIO.Metadata.set_property("Cbox Interactive:out_2", "rolf", "hello")
+cbox.JackIO.Metadata.set_property("Cbox Interactive:out_2", "rolf", "hello")
+cbox.JackIO.Metadata.set_property("Cbox Interactive:midi", "wolf", "world", "stryng")
+cbox.JackIO.Metadata.set_property("Cbox Interactive:midi", "asd", "qwe", "")
+
+
+print ("Setting port order for all 8 ports")
+
+portOrderDict = {
+ "Cbox Interactive:out_1": 50,
+ "Cbox Interactive:out_2": 40,
+ "Cbox Interactive:out_3": 3,
+ "Cbox Interactive:out_4": 5,
+ "Cbox Interactive:out_5": 7,
+ "Cbox Interactive:out_6": 999,
+ "Cbox Interactive:out_7": 4,
+ "Cbox Interactive:out_8": 4,
+ }
+
+try:
+ cbox.JackIO.Metadata.set_all_port_order(portOrderDict)
+ print ("Test to catch non-unique indices failed!. Quitting")
+ quit()
+except ValueError as e:
+ print ("Caught expected ValueError for double index entry.\nAdjusting value and try again to set all ports.")
+
+portOrderDict["Cbox Interactive:out_8"] = 0
+cbox.JackIO.Metadata.set_all_port_order(portOrderDict)
+
+print("List of all metadata follows")
+pprint (cbox.JackIO.Metadata.get_all_properties())
+
+print()
+print ("Now check your port order in QJackCtl or similar. Press [Return] to quit")
+input() #wait for key to confirm order visually in qjackctl
+quit()
+
+def exit_handler():
+ #Restore initial state and stop the engine
+ cbox.Transport.stop()
+ cbox.stop_audio()
+ cbox.shutdown_engine()
+atexit.register(exit_handler)
diff --git a/template/calfbox/fbr.c b/template/calfbox/fbr.c
new file mode 100644
index 0000000..f4bd803
--- /dev/null
+++ b/template/calfbox/fbr.c
@@ -0,0 +1,373 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "biquad-float.h"
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "eq.h"
+#include "module.h"
+#include "rt.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MODULE_PARAMS feedback_reducer_params
+
+#define MAX_FBR_BANDS 16
+
+#define ANALYSIS_BUFFER_SIZE 8192
+#define ANALYSIS_BUFFER_BITS 13
+
+// Sine table
+static complex float euler_table[ANALYSIS_BUFFER_SIZE];
+
+// Bit reversal table
+static int map_table[ANALYSIS_BUFFER_SIZE];
+
+// Bit-reversed von Hann window
+static float von_hann_window_transposed[ANALYSIS_BUFFER_SIZE];
+
+struct feedback_reducer_params
+{
+ struct eq_band bands[MAX_FBR_BANDS];
+};
+
+struct feedback_reducer_module
+{
+ struct cbox_module module;
+
+ struct feedback_reducer_params *params, *old_params;
+
+ struct cbox_biquadf_coeffs coeffs[MAX_FBR_BANDS];
+ struct cbox_biquadf_state state[MAX_FBR_BANDS][2];
+
+ float analysis_buffer[ANALYSIS_BUFFER_SIZE];
+ float *wrptr;
+ int analysed;
+
+ complex float fft_buffers[2][ANALYSIS_BUFFER_SIZE];
+};
+
+// Trivial implementation of Cooley-Tukey (+ my own mistakes) + von Hann window
+static int do_fft(struct feedback_reducer_module *m)
+{
+ // Copy + bit reversal addressing
+ for (int i = 0; i < ANALYSIS_BUFFER_SIZE; i++)
+ {
+ m->fft_buffers[0][i] = von_hann_window_transposed[i] * m->analysis_buffer[map_table[i]] * (2.0 / ANALYSIS_BUFFER_SIZE);
+ }
+
+ for (int i = 0; i < ANALYSIS_BUFFER_BITS; i++)
+ {
+ complex float *src = m->fft_buffers[i & 1];
+ complex float *dst = m->fft_buffers[(~i) & 1];
+ int invi = ANALYSIS_BUFFER_BITS - i - 1;
+ int disp = 1 << i;
+ int mask = disp - 1;
+
+ for (int j = 0; j < ANALYSIS_BUFFER_SIZE / 2; j++)
+ {
+ int jj1 = (j & mask) + ((j & ~mask) << 1); // insert 0 at i'th bit to get the left arm of the butterfly
+ int jj2 = jj1 + disp; // insert 1 at i'th bit to get the right arm
+
+ // e^iw
+ complex float eiw1 = euler_table[(jj1 << invi) & (ANALYSIS_BUFFER_SIZE - 1)];
+ complex float eiw2 = euler_table[(jj2 << invi) & (ANALYSIS_BUFFER_SIZE - 1)];
+
+ // printf("%d -> %d, %d\n", j, jj, jj + disp);
+ butterfly(&dst[jj1], &dst[jj2], src[jj1], src[jj2], eiw1, eiw2);
+ }
+ }
+ return ANALYSIS_BUFFER_BITS & 1;
+}
+
+#define PEAK_REGION_RADIUS 3
+
+struct potential_peak_info
+{
+ int bin;
+ float avg;
+ float centre;
+ float peak;
+ float dist;
+ float points;
+};
+
+static int peak_compare(const void *peak1, const void *peak2)
+{
+ const struct potential_peak_info *pi1 = peak1;
+ const struct potential_peak_info *pi2 = peak2;
+
+ if (pi1->points < pi2->points)
+ return +1;
+ if (pi1->points > pi2->points)
+ return -1;
+ return 0;
+}
+
+static int find_peaks(complex float *spectrum, float srate, float peak_freqs[16])
+{
+ struct potential_peak_info pki[ANALYSIS_BUFFER_SIZE / 2 + 1];
+ for (int i = 0; i <= ANALYSIS_BUFFER_SIZE / 2; i++)
+ {
+ pki[i].bin = i;
+ pki[i].points = 0.f;
+ }
+ float gmax = 0;
+ for (int i = PEAK_REGION_RADIUS; i <= ANALYSIS_BUFFER_SIZE / 2 - PEAK_REGION_RADIUS; i++)
+ {
+ struct potential_peak_info *pi = &pki[i];
+ float sum = 0;
+ float sumf = 0;
+ float peak = 0;
+ for (int j = -PEAK_REGION_RADIUS; j <= PEAK_REGION_RADIUS; j++)
+ {
+ float f = (i + j);
+ float bin = cabs(spectrum[i + j]);
+ if (bin > peak)
+ peak = bin;
+ sum += bin;
+ sumf += f * bin;
+ }
+ pi->avg = sum / (2 * PEAK_REGION_RADIUS + 1);
+ pi->peak = peak;
+ pi->centre = sumf / sum;
+ pi->dist = (sumf / sum - i);
+ if (peak > gmax)
+ gmax = peak;
+ // printf("Bin %d sumf/sum %f avg %f peak %f p/a %f dist %f val %f\n", i, sumf / sum, pki[i].avg, peak, peak / pki[i].avg, sumf/sum - i, cabs(spectrum[i]));
+ }
+ for (int i = PEAK_REGION_RADIUS; i <= ANALYSIS_BUFFER_SIZE / 2 - PEAK_REGION_RADIUS; i++)
+ {
+ struct potential_peak_info *tpi = &pki[i];
+ // ignore peaks below -40dB of the max bin
+ if (pki[(int)tpi->centre].peak < gmax * 0.01)
+ continue;
+ pki[(int)tpi->centre].points += 1;
+ }
+ #if 0
+ for (int i = 0; i <= ANALYSIS_BUFFER_SIZE / 2; i++)
+ {
+ float freq = i * srate / ANALYSIS_BUFFER_SIZE;
+ printf("Bin %d freq %f points %f\n", i, freq, pki[i].points);
+ }
+ #endif
+ qsort(pki, ANALYSIS_BUFFER_SIZE / 2 + 1, sizeof(struct potential_peak_info), peak_compare);
+
+ float peaks[16];
+ int peak_count = 0;
+ for (int i = 0; i <= ANALYSIS_BUFFER_SIZE / 2; i++)
+ {
+ if (pki[i].points <= 1)
+ break;
+ if (pki[i].peak <= 0.0001)
+ break;
+ gboolean dupe = FALSE;
+ for (int j = 0; j < peak_count; j++)
+ {
+ if (fabs(peaks[j] - pki[i].centre) < PEAK_REGION_RADIUS)
+ {
+ dupe = TRUE;
+ break;
+ }
+ }
+ if (dupe)
+ continue;
+ peak_freqs[peak_count] = pki[i].centre * srate / ANALYSIS_BUFFER_SIZE;
+ peaks[peak_count++] = pki[i].centre;
+ printf("Mul %f freq %f points %f peak %f\n", pki[i].centre, pki[i].centre * srate / ANALYSIS_BUFFER_SIZE, pki[i].points, pki[i].peak);
+ if (peak_count == 4)
+ break;
+ }
+ return peak_count;
+}
+
+static void redo_filters(struct feedback_reducer_module *m)
+{
+ for (int i = 0; i < MAX_FBR_BANDS; i++)
+ {
+ struct eq_band *band = &m->params->bands[i];
+ if (band->active)
+ {
+ cbox_biquadf_set_peakeq_rbj(&m->coeffs[i], band->center, band->q, band->gain, m->module.srate);
+ }
+ }
+ m->old_params = m->params;
+}
+
+gboolean feedback_reducer_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct feedback_reducer_module *m = (struct feedback_reducer_module *)ct->user_data;
+
+ EFFECT_PARAM_ARRAY("/active", "i", bands, active, int, , 0, 1) else
+ EFFECT_PARAM_ARRAY("/center", "f", bands, center, double, , 10, 20000) else
+ EFFECT_PARAM_ARRAY("/q", "f", bands, q, double, , 0.01, 100) else
+ EFFECT_PARAM_ARRAY("/gain", "f", bands, gain, double, dB2gain_simple, -100, 100) else
+ if (!strcmp(cmd->command, "/start") && !strcmp(cmd->arg_types, ""))
+ {
+ m->analysed = 0;
+ cbox_rt_swap_pointers(m->module.rt, (void **)&m->wrptr, m->analysis_buffer);
+ }
+ else if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ if (m->wrptr == m->analysis_buffer + ANALYSIS_BUFFER_SIZE && m->analysed == 0)
+ {
+ float freqs[16];
+ int count = find_peaks(m->fft_buffers[do_fft(m)], m->module.srate, freqs);
+ struct feedback_reducer_params *p = malloc(sizeof(struct feedback_reducer_params));
+ memcpy(p->bands + count, &m->params->bands[0], sizeof(struct eq_band) * (MAX_FBR_BANDS - count));
+ for (int i = 0; i < count; i++)
+ {
+ p->bands[i].active = TRUE;
+ p->bands[i].center = freqs[i];
+ p->bands[i].q = freqs[i] / 50; // each band ~100 Hz (not really sure about filter Q vs bandwidth)
+ p->bands[i].gain = 0.125;
+ }
+ free(cbox_rt_swap_pointers(m->module.rt, (void **)&m->params, p)); \
+ m->analysed = 1;
+ if (!cbox_execute_on(fb, NULL, "/refresh", "i", error, 1))
+ return FALSE;
+ }
+ if (!cbox_execute_on(fb, NULL, "/finished", "i", error, m->analysed))
+ return FALSE;
+ for (int i = 0; i < MAX_FBR_BANDS; i++)
+ {
+ if (!cbox_execute_on(fb, NULL, "/active", "ii", error, i, (int)m->params->bands[i].active))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/center", "if", error, i, m->params->bands[i].center))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/q", "if", error, i, m->params->bands[i].q))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/gain", "if", error, i, gain2dB_simple(m->params->bands[i].gain)))
+ return FALSE;
+ }
+ // return cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry);
+ return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error);
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void feedback_reducer_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct feedback_reducer_module *m = module->user_data;
+}
+
+void feedback_reducer_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct feedback_reducer_module *m = module->user_data;
+
+ if (m->params != m->old_params)
+ redo_filters(m);
+
+ if (m->wrptr && m->wrptr != m->analysis_buffer + ANALYSIS_BUFFER_SIZE)
+ {
+ for (int i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ if (m->wrptr == m->analysis_buffer + ANALYSIS_BUFFER_SIZE)
+ break;
+ *m->wrptr++ = inputs[0][i] + inputs[1][i];
+ }
+ }
+ for (int c = 0; c < 2; c++)
+ {
+ gboolean first = TRUE;
+ for (int i = 0; i < MAX_FBR_BANDS; i++)
+ {
+ if (!m->params->bands[i].active)
+ continue;
+ if (first)
+ {
+ cbox_biquadf_process_to(&m->state[i][c], &m->coeffs[i], inputs[c], outputs[c]);
+ first = FALSE;
+ }
+ else
+ {
+ cbox_biquadf_process(&m->state[i][c], &m->coeffs[i], outputs[c]);
+ }
+ }
+ if (first)
+ memcpy(outputs[c], inputs[c], sizeof(float) * CBOX_BLOCK_SIZE);
+ }
+}
+
+MODULE_SIMPLE_DESTROY_FUNCTION(feedback_reducer)
+
+MODULE_CREATE_FUNCTION(feedback_reducer)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ for (int i = 0; i < ANALYSIS_BUFFER_SIZE; i++)
+ {
+ euler_table[i] = cos(i * 2 * M_PI / ANALYSIS_BUFFER_SIZE) + I * sin(i * 2 * M_PI / ANALYSIS_BUFFER_SIZE);
+ int ni = 0;
+ for (int j = 0; j < ANALYSIS_BUFFER_BITS; j++)
+ {
+ if (i & (1 << (ANALYSIS_BUFFER_BITS - 1 - j)))
+ ni = ni | (1 << j);
+ }
+ map_table[i] = ni;
+ von_hann_window_transposed[i] = 0.5 * (1 - cos (ni * 2 * M_PI / (ANALYSIS_BUFFER_SIZE - 1)));
+ }
+
+ inited = 1;
+ }
+
+ struct feedback_reducer_module *m = malloc(sizeof(struct feedback_reducer_module));
+ CALL_MODULE_INIT(m, 2, 2, feedback_reducer);
+ m->module.process_event = feedback_reducer_process_event;
+ m->module.process_block = feedback_reducer_process_block;
+ struct feedback_reducer_params *p = malloc(sizeof(struct feedback_reducer_params));
+ m->params = p;
+ m->old_params = NULL;
+ m->analysed = 0;
+ m->wrptr = NULL;
+
+ for (int b = 0; b < MAX_FBR_BANDS; b++)
+ {
+ p->bands[b].active = cbox_eq_get_band_param(cfg_section, b, "active", 0) > 0;
+ p->bands[b].center = cbox_eq_get_band_param(cfg_section, b, "center", 50 * pow(2.0, b / 2.0));
+ p->bands[b].q = cbox_eq_get_band_param(cfg_section, b, "q", 0.707 * 2);
+ p->bands[b].gain = cbox_eq_get_band_param_db(cfg_section, b, "gain", 0);
+ }
+ redo_filters(m);
+ cbox_eq_reset_bands(m->state, MAX_FBR_BANDS);
+
+ return &m->module;
+}
+
+
+struct cbox_module_keyrange_metadata feedback_reducer_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata feedback_reducer_controllers[] = {
+};
+
+DEFINE_MODULE(feedback_reducer, 2, 2)
+
diff --git a/template/calfbox/fifo.c b/template/calfbox/fifo.c
new file mode 100644
index 0000000..5c9852b
--- /dev/null
+++ b/template/calfbox/fifo.c
@@ -0,0 +1,22 @@
+#include "fifo.h"
+#include
+
+struct cbox_fifo *cbox_fifo_new(uint32_t size)
+{
+ struct cbox_fifo *fifo = calloc(1, sizeof(struct cbox_fifo) + size);
+ if (!fifo)
+ return NULL;
+ fifo->data = (uint8_t *)(fifo + 1);
+ fifo->size = size;
+ fifo->write_count = 0;
+ fifo->write_offset= 0;
+ fifo->read_count = 0;
+ fifo->read_offset = 0;
+ return fifo;
+}
+
+void cbox_fifo_destroy(struct cbox_fifo *fifo)
+{
+ free(fifo);
+}
+
diff --git a/template/calfbox/fifo.h b/template/calfbox/fifo.h
new file mode 100644
index 0000000..d9446cd
--- /dev/null
+++ b/template/calfbox/fifo.h
@@ -0,0 +1,136 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2013 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_FIFO_H
+#define CBOX_FIFO_H
+
+#include
+#include
+#include
+#include
+
+struct cbox_fifo
+{
+ uint8_t *data;
+ uint32_t size;
+ uint64_t pad; // ensure the write-related and read-related structs are on 64 bit boundary
+ uint32_t write_count;
+ uint32_t write_offset;
+ uint32_t read_count;
+ uint32_t read_offset;
+};
+
+extern struct cbox_fifo *cbox_fifo_new(uint32_t size);
+
+static inline uint32_t cbox_fifo_readsize(struct cbox_fifo *fifo);
+static inline uint32_t cbox_fifo_writespace(struct cbox_fifo *fifo);
+static inline gboolean cbox_fifo_read_atomic(struct cbox_fifo *fifo, void *dest, uint32_t bytes);
+static inline gboolean cbox_fifo_write_atomic(struct cbox_fifo *fifo, const void *src, uint32_t bytes);
+static inline gboolean cbox_fifo_peek(struct cbox_fifo *fifo, void *dest, uint32_t bytes);
+static inline gboolean cbox_fifo_consume(struct cbox_fifo *fifo, uint32_t bytes);
+
+extern void cbox_fifo_destroy(struct cbox_fifo *fifo);
+
+
+static inline uint32_t cbox_fifo_readsize(struct cbox_fifo *fifo)
+{
+ return fifo->write_count - fifo->read_count;
+}
+
+static inline uint32_t cbox_fifo_writespace(struct cbox_fifo *fifo)
+{
+ return fifo->size - (fifo->write_count - fifo->read_count);
+}
+
+static inline gboolean cbox_fifo_read_impl(struct cbox_fifo *fifo, void *dest, uint32_t bytes, gboolean advance)
+{
+ __sync_synchronize();
+ if (fifo->write_count - fifo->read_count < bytes)
+ return FALSE;
+
+ if (dest)
+ {
+ uint32_t ofs = fifo->read_count - fifo->read_offset;
+ assert(ofs >= 0 && ofs < fifo->size);
+ if (ofs + bytes > fifo->size)
+ {
+ uint8_t *dstb = dest;
+ uint32_t firstpart = fifo->size - ofs;
+ memcpy(dstb, fifo->data + ofs, firstpart);
+ memcpy(dstb + firstpart, fifo->data, bytes - firstpart);
+ }
+ else
+ memcpy(dest, fifo->data + ofs, bytes);
+ }
+
+ if (advance)
+ {
+ __sync_synchronize();
+ // Make sure data are copied before signalling that they can be overwritten
+ fifo->read_count += bytes;
+ if (fifo->read_count - fifo->read_offset >= fifo->size)
+ fifo->read_offset += fifo->size;
+ }
+ __sync_synchronize();
+
+ return TRUE;
+}
+
+static inline gboolean cbox_fifo_read_atomic(struct cbox_fifo *fifo, void *dest, uint32_t bytes)
+{
+ return cbox_fifo_read_impl(fifo, dest, bytes, TRUE);
+}
+
+static inline gboolean cbox_fifo_peek(struct cbox_fifo *fifo, void *dest, uint32_t bytes)
+{
+ return cbox_fifo_read_impl(fifo, dest, bytes, FALSE);
+}
+
+static inline gboolean cbox_fifo_consume(struct cbox_fifo *fifo, uint32_t bytes)
+{
+ return cbox_fifo_read_impl(fifo, NULL, bytes, TRUE);
+}
+
+static inline gboolean cbox_fifo_write_atomic(struct cbox_fifo *fifo, const void *src, uint32_t bytes)
+{
+ if (fifo->size - (fifo->write_count - fifo->read_count) < bytes)
+ return FALSE;
+
+ uint32_t ofs = fifo->write_count - fifo->write_offset;
+ assert(ofs >= 0 && ofs < fifo->size);
+ if (ofs + bytes > fifo->size)
+ {
+ const uint8_t *srcb = src;
+ uint32_t firstpart = fifo->size - ofs;
+ memcpy(fifo->data + ofs, srcb, firstpart);
+ memcpy(fifo->data, srcb + firstpart, bytes - firstpart);
+ }
+ else
+ memcpy(fifo->data + ofs, src, bytes);
+
+ // Make sure data are in the buffer before announcing the availability
+ __sync_synchronize();
+ fifo->write_count += bytes;
+ if (fifo->write_count - fifo->write_offset >= fifo->size)
+ fifo->write_offset += fifo->size;
+
+ return TRUE;
+}
+
+
+#endif
diff --git a/template/calfbox/fluid.c b/template/calfbox/fluid.c
new file mode 100644
index 0000000..7ac37d8
--- /dev/null
+++ b/template/calfbox/fluid.c
@@ -0,0 +1,412 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+
+#if USE_FLUIDSYNTH
+
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if FLUIDSYNTH_VERSION_MAJOR < 2
+
+typedef unsigned int cbox_fluidsynth_id_t;
+#define fluid_sfont_iteration_setup() fluid_preset_t sfont_iterator;
+#define fluid_sfont_iteration_start(sfont) (sfont)->iteration_start(sfont)
+#define fluid_sfont_iteration_next(sfont) (sfont)->iteration_next(sfont, &sfont_iterator) ? &sfont_iterator : NULL
+#define fluid_preset_get_num(preset) (preset)->get_num(preset)
+#define fluid_preset_get_banknum(preset) (preset)->get_banknum(preset)
+#define fluid_preset_get_name(preset) (preset)->get_name(preset)
+
+#else
+
+typedef int cbox_fluidsynth_id_t;
+#define fluid_sfont_iteration_setup()
+
+#endif
+
+#define CBOX_FLUIDSYNTH_ERROR cbox_fluidsynth_error_quark()
+
+enum CboxFluidsynthError
+{
+ CBOX_FLUIDSYNTH_ERROR_FAILED,
+};
+
+GQuark cbox_fluidsynth_error_quark(void)
+{
+ return g_quark_from_string("cbox-fluidsynth-error-quark");
+}
+
+static void fluidsynth_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs);
+static void fluidsynth_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len);
+static gboolean fluidsynth_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
+static void fluidsynth_destroyfunc(struct cbox_module *module);
+
+struct fluidsynth_module
+{
+ struct cbox_module module;
+
+ fluid_settings_t *settings;
+ fluid_synth_t *synth;
+ char *bank_name;
+ int sfid;
+ int output_pairs;
+ int is_multi;
+ float **left_outputs, **right_outputs;
+};
+
+static gboolean select_patch_by_name(struct fluidsynth_module *m, int channel, const gchar *preset, GError **error)
+{
+ fluid_sfont_t* sfont = fluid_synth_get_sfont(m->synth, 0);
+ fluid_preset_t* tmp;
+ fluid_sfont_iteration_setup();
+
+ fluid_sfont_iteration_start(sfont);
+ while((tmp = fluid_sfont_iteration_next(sfont)) != NULL)
+ {
+ // trailing spaces are common in some SF2s
+ const char *pname = fluid_preset_get_name(tmp);
+ int len = strlen(pname);
+ while (len > 0 && pname[len - 1] == ' ')
+ len--;
+
+ if (!strncmp(pname, preset, len) && preset[len] == '\0')
+ {
+ fluid_synth_program_select(m->synth, channel, m->sfid, fluid_preset_get_banknum(tmp), fluid_preset_get_num(tmp));
+ return TRUE;
+ }
+ }
+
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Preset not found: %s", preset);
+ return FALSE;
+}
+
+MODULE_CREATE_FUNCTION(fluidsynth)
+{
+ int result = 0;
+ int i;
+ const char *bankname = cbox_config_get_string(cfg_section, "sf2");
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ struct fluidsynth_module *m = malloc(sizeof(struct fluidsynth_module));
+ int pairs = cbox_config_get_int(cfg_section, "output_pairs", 0);
+ m->output_pairs = pairs ? pairs : 1;
+ m->is_multi = pairs > 0;
+ if (m->output_pairs < 1 || m->output_pairs > 16)
+ {
+ free(m);
+ g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "Invalid number of output pairs (found %d, supported range 1-16)", m->output_pairs);
+ return NULL;
+ }
+ if (pairs == 0)
+ {
+ CALL_MODULE_INIT(m, 0, 2 * m->output_pairs, fluidsynth);
+ m->left_outputs = NULL;
+ m->right_outputs = NULL;
+ }
+ else
+ {
+ g_message("Multichannel mode enabled, %d output pairs, 2 effects", m->output_pairs);
+ CALL_MODULE_INIT(m, 0, 2 * m->output_pairs + 4, fluidsynth);
+ m->left_outputs = malloc(sizeof(float *) * (m->output_pairs + 2));
+ m->right_outputs = malloc(sizeof(float *) * (m->output_pairs + 2));
+ }
+ m->module.process_event = fluidsynth_process_event;
+ m->module.process_block = fluidsynth_process_block;
+ m->module.aux_offset = 2 * m->output_pairs;
+ m->settings = new_fluid_settings();
+ fluid_settings_setnum(m->settings, "synth.sample-rate", m->module.srate);
+ fluid_settings_setint(m->settings, "synth.audio-channels", m->output_pairs);
+ fluid_settings_setint(m->settings, "synth.audio-groups", m->output_pairs);
+ m->synth = new_fluid_synth(m->settings);
+ fluid_synth_set_reverb_on(m->synth, cbox_config_get_int(cfg_section, "reverb", 1));
+ fluid_synth_set_chorus_on(m->synth, cbox_config_get_int(cfg_section, "chorus", 1));
+
+ //Log levels in Fluidsynth are handled as settable functions
+ fluid_set_log_function(FLUID_PANIC, NULL, NULL);
+ fluid_set_log_function(FLUID_ERR, NULL, NULL);
+ fluid_set_log_function(FLUID_WARN, NULL, NULL);
+ fluid_set_log_function(FLUID_DBG, NULL, NULL);
+ fluid_set_log_function(FLUID_INFO, NULL, NULL);
+
+
+ m->bank_name = NULL;
+ m->sfid = -1;
+ if (bankname)
+ {
+ m->bank_name = g_strdup(bankname);
+ g_message("Loading soundfont %s", bankname);
+ result = fluid_synth_sfload(m->synth, bankname, 1);
+ if (result == FLUID_FAILED)
+ {
+ g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "Failed to load the default bank %s: %s", bankname, fluid_synth_error(m->synth));
+ return NULL;
+ }
+ m->sfid = result;
+ g_message("Soundfont %s loaded", bankname);
+ }
+ if (bankname)
+ {
+ for (i = 0; i < 16; i++)
+ {
+ gchar *key = g_strdup_printf("channel%d", i + 1);
+ gchar *preset = cbox_config_get_string(cfg_section, key);
+ fluid_synth_sfont_select(m->synth, i, m->sfid);
+ if (preset)
+ {
+ if (!select_patch_by_name(m, i, preset, error))
+ {
+ CBOX_DELETE(&m->module);
+ return NULL;
+ }
+ }
+ g_free(key);
+ }
+ }
+
+ return &m->module;
+}
+
+void fluidsynth_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct fluidsynth_module *m = (struct fluidsynth_module *)module;
+ if (!m->is_multi)
+ fluid_synth_write_float(m->synth, CBOX_BLOCK_SIZE, outputs[0], 0, 1, outputs[1], 0, 1);
+ else
+ {
+ for (int i = 0; i < 2 + m->output_pairs; i++)
+ {
+ m->left_outputs[i] = outputs[2 * i];
+ m->right_outputs[i] = outputs[2 * i + 1];
+ }
+
+ fluid_synth_nwrite_float(m->synth, CBOX_BLOCK_SIZE, m->left_outputs, m->right_outputs, m->left_outputs + m->output_pairs, m->right_outputs + m->output_pairs);
+ }
+}
+
+void fluidsynth_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ struct fluidsynth_module *m = (struct fluidsynth_module *)module;
+ if (len > 0)
+ {
+ int cmd = data[0] >> 4;
+ int chn = data[0] & 15;
+ switch(cmd)
+ {
+ case 8:
+ fluid_synth_noteoff(m->synth, chn, data[1]);
+ break;
+
+ case 9:
+ fluid_synth_noteon(m->synth, chn, data[1], data[2]);
+ break;
+
+ case 10:
+ // polyphonic pressure not handled
+ break;
+
+ case 11:
+ fluid_synth_cc(m->synth, chn, data[1], data[2]);
+ break;
+
+ case 12:
+ fluid_synth_program_change(m->synth, chn, data[1]);
+ break;
+
+ case 13:
+ fluid_synth_channel_pressure(m->synth, chn, data[1]);
+ break;
+
+ case 14:
+ fluid_synth_pitch_bend(m->synth, chn, data[1] + 128 * data[2]);
+ break;
+
+ }
+ }
+}
+
+gboolean fluidsynth_process_load_patch(struct fluidsynth_module *m, const char *bank_name, GError **error)
+{
+ if (bank_name && !*bank_name)
+ bank_name = NULL;
+ int old_sfid = m->sfid;
+ char *old_bank_name = m->bank_name;
+ if (bank_name)
+ {
+ int result = fluid_synth_sfload(m->synth, bank_name, 1);
+ if (result == FLUID_FAILED)
+ {
+ g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "Failed to load the bank %s: %s", bank_name, fluid_synth_error(m->synth));
+ return FALSE;
+ }
+ g_message("Soundfont %s loaded at ID %d", bank_name, result);
+ m->sfid = result;
+ }
+ else
+ m->sfid = -1;
+ if (old_sfid != -1)
+ {
+ free(old_bank_name);
+ fluid_synth_sfunload(m->synth, old_sfid, 1);
+ }
+ if (m->sfid != -1)
+ {
+ for (int i = 0; i < 16; i++)
+ fluid_synth_sfont_select(m->synth, i, m->sfid);
+ }
+ m->bank_name = bank_name ? g_strdup(bank_name) : NULL;
+ return TRUE;
+}
+
+gboolean fluidsynth_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct fluidsynth_module *m = (struct fluidsynth_module *)ct->user_data;
+
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/polyphony", "i", error, fluid_synth_get_polyphony(m->synth)))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/soundfont", "s", error, m->bank_name ? m->bank_name : ""))
+ return FALSE;
+ for (int i = 0; i < 16; i++)
+ {
+ cbox_fluidsynth_id_t sfont_id, bank_num, preset_num;
+ fluid_synth_get_program(m->synth, i, &sfont_id, &bank_num, &preset_num);
+ fluid_preset_t *preset = fluid_synth_get_channel_preset(m->synth, i);
+ if (!cbox_execute_on(fb, NULL, "/patch", "iis", error, 1 + i, preset_num + 128 * bank_num, preset ? fluid_preset_get_name(preset) : "(unknown)"))
+ return FALSE;
+ }
+ return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error);
+ }
+ else if (!strcmp(cmd->command, "/patches") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ if (m->sfid == -1)
+ return TRUE;
+ fluid_sfont_t* sfont = fluid_synth_get_sfont(m->synth, 0);
+ fluid_preset_t *tmp;
+
+ fluid_sfont_iteration_setup();
+ fluid_sfont_iteration_start(sfont);
+ while((tmp = fluid_sfont_iteration_next(sfont)) != NULL)
+ {
+ const char *pname = fluid_preset_get_name(tmp);
+ if (!cbox_execute_on(fb, NULL, "/patch", "is", error, (int)(fluid_preset_get_num(tmp) + 128 * fluid_preset_get_banknum(tmp)), pname))
+ return FALSE;
+ }
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/set_patch") && !strcmp(cmd->arg_types, "ii"))
+ {
+ if (m->sfid == -1)
+ {
+ g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "No soundfont loaded");
+ return FALSE;
+ }
+ int channel = CBOX_ARG_I(cmd, 0);
+ if (channel < 1 || channel > 16)
+ {
+ g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "Invalid channel %d", channel);
+ return FALSE;
+ }
+ int value = CBOX_ARG_I(cmd, 1);
+ return fluid_synth_program_select(m->synth, channel - 1, m->sfid, value >> 7, value & 127) == FLUID_OK;
+ }
+ else if (!strcmp(cmd->command, "/polyphony") && !strcmp(cmd->arg_types, "i"))
+ {
+ int polyphony = CBOX_ARG_I(cmd, 0);
+ if (polyphony < 2 || polyphony > 256)
+ {
+ g_set_error(error, CBOX_FLUIDSYNTH_ERROR, CBOX_FLUIDSYNTH_ERROR_FAILED, "Invalid polyphony %d (must be between 2 and 256)", polyphony);
+ return FALSE;
+ }
+ return fluid_synth_set_polyphony(m->synth, polyphony) == FLUID_OK;
+ }
+ else if (!strcmp(cmd->command, "/load_soundfont") && !strcmp(cmd->arg_types, "s"))
+ {
+ return fluidsynth_process_load_patch(m, CBOX_ARG_S(cmd, 0), error);
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void fluidsynth_destroyfunc(struct cbox_module *module)
+{
+ struct fluidsynth_module *m = (struct fluidsynth_module *)module;
+
+ if (m->output_pairs)
+ {
+ free(m->left_outputs);
+ free(m->right_outputs);
+ }
+ free(m->bank_name);
+
+ delete_fluid_settings(m->settings);
+ delete_fluid_synth(m->synth);
+}
+
+struct cbox_module_livecontroller_metadata fluidsynth_controllers[] = {
+ { -1, cmlc_continuouscc, 1, "Modulation", NULL},
+ { -1, cmlc_continuouscc, 7, "Volume", NULL},
+ { -1, cmlc_continuouscc, 10, "Pan", NULL},
+ { -1, cmlc_continuouscc, 91, "Reverb", NULL},
+ { -1, cmlc_continuouscc, 93, "Chorus", NULL},
+ { -1, cmlc_onoffcc, 64, "Hold", NULL},
+ { -1, cmlc_onoffcc, 66, "Sostenuto", NULL},
+};
+
+struct cbox_module_keyrange_metadata fluidsynth_keyranges[] = {
+ { 1, 0, 127, "Channel 1" },
+ { 2, 0, 127, "Channel 2" },
+ { 3, 0, 127, "Channel 3" },
+ { 4, 0, 127, "Channel 4" },
+ { 5, 0, 127, "Channel 5" },
+ { 6, 0, 127, "Channel 6" },
+ { 7, 0, 127, "Channel 7" },
+ { 8, 0, 127, "Channel 8" },
+ { 9, 0, 127, "Channel 9" },
+ { 10, 0, 127, "Channel 10" },
+ { 11, 0, 127, "Channel 11" },
+ { 12, 0, 127, "Channel 12" },
+ { 13, 0, 127, "Channel 13" },
+ { 14, 0, 127, "Channel 14" },
+ { 15, 0, 127, "Channel 15" },
+ { 16, 0, 127, "Channel 16" },
+};
+
+DEFINE_MODULE(fluidsynth, 0, 2)
+
+#endif
diff --git a/template/calfbox/fuzz.c b/template/calfbox/fuzz.c
new file mode 100644
index 0000000..677d3d0
--- /dev/null
+++ b/template/calfbox/fuzz.c
@@ -0,0 +1,173 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "biquad-float.h"
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MODULE_PARAMS fuzz_params
+
+struct fuzz_params
+{
+ float drive;
+ float wet_dry;
+ float rectify;
+ float band;
+ float bandwidth;
+ float band2;
+ float bandwidth2;
+};
+
+struct fuzz_module
+{
+ struct cbox_module module;
+
+ struct fuzz_params *params, *old_params;
+
+ struct cbox_biquadf_coeffs split_coeffs;
+ struct cbox_biquadf_coeffs post_coeffs;
+ struct cbox_biquadf_state split_state[2];
+ struct cbox_biquadf_state post_state[2];
+};
+
+gboolean fuzz_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct fuzz_module *m = (struct fuzz_module *)ct->user_data;
+
+ EFFECT_PARAM("/drive", "f", drive, double, dB2gain_simple, -36, 36) else
+ EFFECT_PARAM("/wet_dry", "f", wet_dry, double, , 0, 1) else
+ EFFECT_PARAM("/rectify", "f", rectify, double, , 0, 1) else
+ EFFECT_PARAM("/band", "f", band, double, , 100, 5000) else
+ EFFECT_PARAM("/bandwidth", "f", bandwidth, double, , 0.25, 4) else
+ EFFECT_PARAM("/band2", "f", band2, double, , 100, 5000) else
+ EFFECT_PARAM("/bandwidth2", "f", bandwidth2, double, , 0.25, 4) else
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ return cbox_execute_on(fb, NULL, "/drive", "f", error, gain2dB_simple(m->params->drive))
+ && cbox_execute_on(fb, NULL, "/wet_dry", "f", error, m->params->wet_dry)
+ && cbox_execute_on(fb, NULL, "/rectify", "f", error, m->params->rectify)
+ && cbox_execute_on(fb, NULL, "/band", "f", error, m->params->band)
+ && cbox_execute_on(fb, NULL, "/bandwidth", "f", error, m->params->bandwidth)
+ && cbox_execute_on(fb, NULL, "/band2", "f", error, m->params->band2)
+ && cbox_execute_on(fb, NULL, "/bandwidth2", "f", error, m->params->bandwidth2)
+ && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error)
+ ;
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void fuzz_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct fuzz_module *m = module->user_data;
+}
+
+void fuzz_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct fuzz_module *m = module->user_data;
+
+ if (m->params != m->old_params)
+ {
+ // update calculated values
+ }
+
+ cbox_biquadf_set_bp_rbj(&m->split_coeffs, m->params->band, 0.7 / m->params->bandwidth, m->module.srate);
+ cbox_biquadf_set_bp_rbj(&m->post_coeffs, m->params->band2, 0.7 / m->params->bandwidth2, m->module.srate);
+
+ float splitbuf[2][CBOX_BLOCK_SIZE];
+ float drive = m->params->drive;
+ float sdrive = pow(drive, -0.7);
+ for (int c = 0; c < 2; c++)
+ {
+ cbox_biquadf_process_to(&m->split_state[c], &m->split_coeffs, inputs[c], splitbuf[c]);
+ for (int i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ float in = inputs[c][i];
+
+ float val = splitbuf[c][i];
+
+ val *= drive;
+
+ val += m->params->rectify;
+ if (fabs(val) > 1.0)
+ val = (val > 0) ? 1 : -1;
+ else
+ val = val * (3 - val * val) * 0.5;
+
+ val *= sdrive;
+
+ val = cbox_biquadf_process_sample(&m->post_state[c], &m->post_coeffs, val);
+
+ outputs[c][i] = in + (val - in) * m->params->wet_dry;
+ }
+ }
+}
+
+MODULE_SIMPLE_DESTROY_FUNCTION(fuzz)
+
+MODULE_CREATE_FUNCTION(fuzz)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ struct fuzz_module *m = malloc(sizeof(struct fuzz_module));
+ CALL_MODULE_INIT(m, 2, 2, fuzz);
+ m->module.process_event = fuzz_process_event;
+ m->module.process_block = fuzz_process_block;
+ struct fuzz_params *p = malloc(sizeof(struct fuzz_params));
+ p->drive = cbox_config_get_gain_db(cfg_section, "drive", 0.f);
+ p->wet_dry = cbox_config_get_float(cfg_section, "wet_dry", 0.5f);
+ p->rectify = cbox_config_get_float(cfg_section, "rectify", 0.5f);
+ p->band = cbox_config_get_float(cfg_section, "band", 1000.f);
+ p->bandwidth = cbox_config_get_float(cfg_section, "bandwidth", 1);
+ p->band2 = cbox_config_get_float(cfg_section, "band2", 2000.f);
+ p->bandwidth2 = cbox_config_get_float(cfg_section, "bandwidth2", 1);
+ m->params = p;
+ m->old_params = NULL;
+ cbox_biquadf_reset(&m->split_state[0]);
+ cbox_biquadf_reset(&m->split_state[1]);
+ cbox_biquadf_reset(&m->post_state[0]);
+ cbox_biquadf_reset(&m->post_state[1]);
+
+ return &m->module;
+}
+
+
+struct cbox_module_keyrange_metadata fuzz_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata fuzz_controllers[] = {
+};
+
+DEFINE_MODULE(fuzz, 2, 2)
+
diff --git a/template/calfbox/fxchain.c b/template/calfbox/fxchain.c
new file mode 100644
index 0000000..5cd123d
--- /dev/null
+++ b/template/calfbox/fxchain.c
@@ -0,0 +1,231 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include "rt.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct fxchain_module
+{
+ struct cbox_module module;
+
+ struct cbox_module **modules;
+ uint32_t module_count;
+};
+
+void fxchain_move(struct fxchain_module *m, unsigned int oldpos, unsigned int newpos)
+{
+ if (oldpos == newpos)
+ return;
+ struct cbox_module **modules = malloc(sizeof(struct cbox_module *) * m->module_count);
+ for (uint32_t i = 0; i < m->module_count; i++)
+ {
+ int s;
+ if (i == newpos)
+ s = oldpos;
+ else
+ {
+ if (oldpos < newpos)
+ s = (i < oldpos || i > newpos) ? i : i + 1;
+ else
+ s = (i < newpos || i > oldpos) ? i : i - 1;
+ }
+ modules[i] = m->modules[s];
+ }
+ free(cbox_rt_swap_pointers(m->module.rt, (void **)&m->modules, modules));
+}
+
+gboolean fxchain_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct fxchain_module *m = (struct fxchain_module *)ct->user_data;
+ const char *subcommand = NULL;
+ int index = 0;
+
+ //EFFECT_PARAM("/module_count", "i", stages, int, , 1, 12) else
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ for (uint32_t i = 0; i < m->module_count; i++)
+ {
+ gboolean res = FALSE;
+ if (m->modules[i])
+ res = cbox_execute_on(fb, NULL, "/module", "ss", error, m->modules[i]->engine_name, m->modules[i]->instance_name);
+ else
+ res = cbox_execute_on(fb, NULL, "/module", "ss", error, "", "");
+ if (!res)
+ return FALSE;
+ res = cbox_execute_on(fb, NULL, "/bypass", "ii", error, i + 1, m->modules[i] ? m->modules[i]->bypass : 0);
+ }
+ return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error);
+ }
+ else if (cbox_parse_path_part_int(cmd, "/module/", &subcommand, &index, 1, m->module_count, error))
+ {
+ if (!subcommand)
+ return FALSE;
+ return cbox_module_slot_process_cmd(&m->modules[index - 1], fb, cmd, subcommand, CBOX_GET_DOCUMENT(&m->module), m->module.rt, m->module.engine, error);
+ }
+ else if (!strcmp(cmd->command, "/insert") && !strcmp(cmd->arg_types, "i"))
+ {
+ int pos = CBOX_ARG_I(cmd, 0) - 1;
+ struct cbox_module **new_modules = malloc((m->module_count + 1) * sizeof(struct cbox_module *));
+ memcpy(new_modules, m->modules, pos * sizeof(struct cbox_module *));
+ new_modules[pos] = NULL;
+ memcpy(new_modules + pos + 1, m->modules + pos, (m->module_count - pos) * sizeof(struct cbox_module *));
+ void *old_modules = cbox_rt_swap_pointers_and_update_count(m->module.rt, (void **)&m->modules, new_modules, &m->module_count, m->module_count + 1);
+ free(old_modules);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/delete") && !strcmp(cmd->arg_types, "i"))
+ {
+ int pos = CBOX_ARG_I(cmd, 0) - 1;
+ struct cbox_module **new_modules = malloc((m->module_count + 1) * sizeof(struct cbox_module *));
+ memcpy(new_modules, m->modules, pos * sizeof(struct cbox_module *));
+ memcpy(new_modules + pos, m->modules + pos + 1, (m->module_count - pos - 1) * sizeof(struct cbox_module *));
+ struct cbox_module *deleted_module = m->modules[pos];
+ void *old_modules = cbox_rt_swap_pointers_and_update_count(m->module.rt, (void **)&m->modules, new_modules, &m->module_count, m->module_count - 1);
+ free(old_modules);
+ if (deleted_module)
+ CBOX_DELETE(deleted_module);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/move") && !strcmp(cmd->arg_types, "ii"))
+ {
+ int oldpos = CBOX_ARG_I(cmd, 0) - 1;
+ int newpos = CBOX_ARG_I(cmd, 1) - 1;
+ fxchain_move(m, oldpos, newpos);
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void fxchain_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct fxchain_module *m = module->user_data;
+}
+
+void fxchain_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct fxchain_module *m = module->user_data;
+
+ float bufs[2][2][CBOX_BLOCK_SIZE];
+
+ for (uint32_t i = 0; i < m->module_count; i++)
+ {
+ float *input_bufs[2], *output_bufs[2];
+ for (int c = 0; c < 2; c++)
+ {
+ input_bufs[c] = i == 0 ? inputs[c] : bufs[i & 1][c];
+ output_bufs[c] = i == m->module_count - 1 ? outputs[c] : bufs[(i + 1) & 1][c];
+ }
+ if (m->modules[i] && !m->modules[i]->bypass)
+ m->modules[i]->process_block(m->modules[i]->user_data, input_bufs, output_bufs);
+ else
+ {
+ // this is not eficient at all, but empty modules aren't likely to be used except
+ // when setting up a chain.
+ for (int c = 0; c < 2; c++)
+ memcpy(output_bufs[c], input_bufs[c], CBOX_BLOCK_SIZE * sizeof(float));
+ }
+ }
+
+}
+
+static void fxchain_destroyfunc(struct cbox_module *module)
+{
+ struct fxchain_module *m = module->user_data;
+ for (uint32_t i = 0; i < m->module_count; i++)
+ {
+ CBOX_DELETE(m->modules[i]);
+ m->modules[i] = NULL;
+ }
+ free(m->modules);
+}
+
+MODULE_CREATE_FUNCTION(fxchain)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ int i, fx_count = 0;
+ for (i = 0; ; i++)
+ {
+ gchar *name = g_strdup_printf("effect%d", i + 1);
+ const char *fx_name = cbox_config_get_string(cfg_section, name);
+ g_free(name);
+ if (!fx_name)
+ break;
+ }
+ fx_count = i;
+ if (cfg_section && !fx_count)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "No effects defined");
+ return NULL;
+ }
+
+ struct fxchain_module *m = malloc(sizeof(struct fxchain_module));
+ CALL_MODULE_INIT(m, 2, 2, fxchain);
+ m->module.process_event = fxchain_process_event;
+ m->module.process_block = fxchain_process_block;
+ m->modules = malloc(sizeof(struct cbox_module *) * fx_count);
+ m->module_count = fx_count;
+
+ for (i = 0; i < fx_count; i++)
+ m->modules[i] = NULL;
+
+ for (i = 0; i < fx_count; i++)
+ {
+ gchar *name = g_strdup_printf("effect%d", i + 1);
+ const char *fx_preset_name = cbox_config_get_string(cfg_section, name);
+ g_free(name);
+ m->modules[i] = cbox_module_new_from_fx_preset(fx_preset_name, doc, rt, engine, error);
+ if (!m->modules[i])
+ goto failed;
+ }
+ fx_count = i;
+
+ return &m->module;
+
+failed:
+ m->module_count = i;
+ CBOX_DELETE(&m->module);
+ return NULL;
+}
+
+
+struct cbox_module_keyrange_metadata fxchain_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata fxchain_controllers[] = {
+};
+
+DEFINE_MODULE(fxchain, 0, 2)
+
diff --git a/template/calfbox/gate.c b/template/calfbox/gate.c
new file mode 100644
index 0000000..fac233f
--- /dev/null
+++ b/template/calfbox/gate.c
@@ -0,0 +1,181 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2012 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include "onepole-float.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MODULE_PARAMS gate_params
+
+struct gate_params
+{
+ float threshold;
+ float ratio;
+ float attack;
+ float hold;
+ float release;
+};
+
+struct gate_module
+{
+ struct cbox_module module;
+
+ struct gate_params *params, *old_params;
+ struct cbox_onepolef_coeffs attack_lp, release_lp, shifter_lp;
+ struct cbox_onepolef_state shifter1, shifter2;
+ struct cbox_onepolef_state tracker;
+ int hold_time, hold_threshold;
+};
+
+gboolean gate_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct gate_module *m = (struct gate_module *)ct->user_data;
+
+ EFFECT_PARAM("/threshold", "f", threshold, double, dB2gain_simple, -100, 100) else
+ EFFECT_PARAM("/ratio", "f", ratio, double, , 1, 100) else
+ EFFECT_PARAM("/attack", "f", attack, double, , 1, 1000) else
+ EFFECT_PARAM("/hold", "f", hold, double, , 1, 1000) else
+ EFFECT_PARAM("/release", "f", release, double, , 1, 1000) else
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ return cbox_execute_on(fb, NULL, "/threshold", "f", error, gain2dB_simple(m->params->threshold))
+ && cbox_execute_on(fb, NULL, "/ratio", "f", error, m->params->ratio)
+ && cbox_execute_on(fb, NULL, "/attack", "f", error, m->params->attack)
+ && cbox_execute_on(fb, NULL, "/hold", "f", error, m->params->hold)
+ && cbox_execute_on(fb, NULL, "/release", "f", error, m->params->release)
+ && CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error)
+ ;
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void gate_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct gate_module *m = module->user_data;
+}
+
+void gate_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct gate_module *m = module->user_data;
+
+ if (m->params != m->old_params)
+ {
+ float scale = M_PI * 1000 / m->module.srate;
+ cbox_onepolef_set_lowpass(&m->attack_lp, scale / m->params->attack);
+ cbox_onepolef_set_lowpass(&m->release_lp, scale / m->params->release);
+ cbox_onepolef_set_allpass(&m->shifter_lp, M_PI * 100 / m->module.srate);
+ m->hold_threshold = (int)(m->module.srate * m->params->hold * 0.001);
+ m->old_params = m->params;
+ }
+
+ float threshold = m->params->threshold;
+ float threshold2 = threshold * threshold * 1.73;
+ for (int i = 0; i < CBOX_BLOCK_SIZE; i++)
+ {
+ float left = inputs[0][i], right = inputs[1][i];
+ float sig = fabs(left) > fabs(right) ? fabs(left) : fabs(right);
+
+ // Primitive envelope detector - may not work so well with more interesting stereo signals
+ float shf1 = cbox_onepolef_process_sample(&m->shifter1, &m->shifter_lp, 0.5 * (left + right));
+ float shf2 = cbox_onepolef_process_sample(&m->shifter2, &m->shifter_lp, shf1);
+ sig = sig*sig + shf1*shf1 + shf2 * shf2;
+
+ // attack - hold - release logic based on signal envelope
+ int release = 1;
+ float gain = 1.0;
+ if (sig < threshold2)
+ {
+ // hold vs release
+ if (m->hold_time >= m->hold_threshold)
+ {
+ gain = powf(sig / threshold2, 0.5 * (m->params->ratio - 1));
+ // gain = powf(sqrt(sig) / threshold, (m->params->ratio - 1));
+ }
+ else
+ m->hold_time++;
+ }
+ else
+ {
+ // attack - going to 1 using attack rate
+ m->hold_time = 0;
+ gain = 1.0;
+ release = 0;
+ }
+
+ gain = cbox_onepolef_process_sample(&m->tracker, release ? &m->release_lp : &m->attack_lp, gain);
+
+ outputs[0][i] = left * gain;
+ outputs[1][i] = right * gain;
+ }
+}
+
+MODULE_SIMPLE_DESTROY_FUNCTION(gate)
+
+MODULE_CREATE_FUNCTION(gate)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ struct gate_module *m = malloc(sizeof(struct gate_module));
+ CALL_MODULE_INIT(m, 2, 2, gate);
+ m->module.process_event = gate_process_event;
+ m->module.process_block = gate_process_block;
+ m->hold_time = 0;
+ m->hold_threshold = 0;
+
+ struct gate_params *p = malloc(sizeof(struct gate_params));
+ p->threshold = cbox_config_get_gain_db(cfg_section, "threshold", -28.0);
+ p->ratio = cbox_config_get_float(cfg_section, "ratio", 3.0);
+ p->attack = cbox_config_get_float(cfg_section, "attack", 3.0);
+ p->hold = cbox_config_get_float(cfg_section, "hold", 100.0);
+ p->release = cbox_config_get_float(cfg_section, "release", 100.0);
+ m->params = p;
+ m->old_params = NULL;
+
+ cbox_onepolef_reset(&m->tracker);
+ cbox_onepolef_reset(&m->shifter1);
+ cbox_onepolef_reset(&m->shifter2);
+
+ return &m->module;
+}
+
+
+struct cbox_module_keyrange_metadata gate_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata gate_controllers[] = {
+};
+
+DEFINE_MODULE(gate, 2, 2)
+
diff --git a/template/calfbox/hwcfg.c b/template/calfbox/hwcfg.c
new file mode 100644
index 0000000..b86cb7b
--- /dev/null
+++ b/template/calfbox/hwcfg.c
@@ -0,0 +1,172 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+
+#if USE_JACK
+
+#include "config-api.h"
+#include "io.h"
+
+#include
+#include
+#include
+#include
+
+static char *cfg(const char *section, const char *name, char *defvalue)
+{
+ char *value = cbox_config_get_string(section, name);
+ if (value)
+ return value;
+ return cbox_config_get_string_with_default("autojack", name, defvalue);
+}
+
+static void generate_jack_config(const char *section, const char *id)
+{
+ char *rcfile = cbox_config_get_string("autojack", "jackdrc");
+ FILE *f;
+ if (!rcfile)
+ {
+ rcfile = g_strdup_printf("%s/.jackdrc", getenv("HOME"));
+ g_message("Generating JACK config: %s\n", rcfile);
+ f = fopen(rcfile, "w");
+ if (!f)
+ {
+ g_error("Cannot open file %s", rcfile);
+ return;
+ }
+ g_free(rcfile);
+ }
+ else
+ {
+ g_message("Generating JACK config: %s\n", rcfile);
+ f = fopen(rcfile, "w");
+ if (!f)
+ {
+ g_error("Cannot open file %s", rcfile);
+ return;
+ }
+ }
+
+ fprintf(f, "%s %s -d alsa -d hw:%s -r 44100 %s\n",
+ cfg(section, "jackd", "/usr/bin/jackd"),
+ cfg(section, "jack_options", "-R -T"),
+ id,
+ cfg(section, "alsa_options", ""));
+ fclose(f);
+}
+
+static int try_soundcard(const char *name)
+{
+ gchar *id;
+ if (!cbox_config_has_section(name))
+ return 0;
+
+ g_message("Trying section %s", name);
+
+ id = cbox_config_get_string(name, "device");
+ if (id != NULL)
+ {
+ struct stat s;
+ int result;
+ gchar *fn = g_strdup_printf("/proc/asound/%s", id);
+ result = stat(fn, &s);
+ if (!result)
+ generate_jack_config(name, id);
+ g_free(fn);
+ return !result;
+ }
+
+ id = cbox_config_get_string(name, "usbid");
+ if (id != NULL)
+ {
+ int vid, pid;
+ if (sscanf(id, "%x:%x\n", &vid, &pid) !=2)
+ {
+ g_error("Invalid VID:PID value: %s", id);
+ return 0;
+ }
+ for (int i = 0; ; i++)
+ {
+ struct stat s;
+ int result;
+ FILE *f = NULL;
+ int tvid, tpid;
+
+ // check if it's not beyond the last soundcard index
+ gchar *fn = g_strdup_printf("/proc/asound/card%d", i);
+ result = stat(fn, &s);
+ g_free(fn);
+ if (result)
+ break;
+
+ // check if it has a USB ID
+ fn = g_strdup_printf("/proc/asound/card%d/usbid", i);
+ f = fopen(fn, "r");
+ g_free(fn);
+
+ if (!f)
+ continue;
+
+ if (fscanf(f, "%x:%x", &tvid, &tpid) == 2)
+ {
+ if (vid == tvid && pid == tpid)
+ {
+ gchar *fn = g_strdup_printf("%d", i);
+ generate_jack_config(name, fn);
+ g_free(fn);
+ fclose(f);
+ return 1;
+ }
+ }
+ fclose(f);
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
+int cbox_hwcfg_setup_jack(void)
+{
+ int i;
+ if (!cbox_config_has_section("autojack"))
+ return 0;
+
+ for (i = 0; ; i++)
+ {
+ int result;
+ gchar *cardnum = g_strdup_printf("soundcard%d", i);
+ char *secname = cbox_config_get_string("autojack", cardnum);
+ g_free(cardnum);
+
+ if (!secname)
+ break;
+
+ secname = g_strdup_printf("soundcard:%s", secname);
+ result = try_soundcard(secname);
+ g_free(secname);
+
+ if (result)
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/template/calfbox/hwcfg.h b/template/calfbox/hwcfg.h
new file mode 100644
index 0000000..a5061b2
--- /dev/null
+++ b/template/calfbox/hwcfg.h
@@ -0,0 +1,28 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_HWCFG_H
+#define CBOX_HWCFG_H
+
+/**
+ * Autodetect JACK config based on cbox configuration vs ALSA devices present.
+ * @retval 1 if OK
+ */
+extern int cbox_hwcfg_setup_jack(void);
+
+#endif
diff --git a/template/calfbox/instr.c b/template/calfbox/instr.c
new file mode 100644
index 0000000..d030c7c
--- /dev/null
+++ b/template/calfbox/instr.c
@@ -0,0 +1,251 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "auxbus.h"
+#include "config-api.h"
+#include "instr.h"
+#include "module.h"
+#include "io.h"
+#include "rt.h"
+#include "scene.h"
+#include
+#include
+
+CBOX_CLASS_DEFINITION_ROOT(cbox_instrument)
+
+static gboolean cbox_instrument_output_process_cmd(struct cbox_instrument *instr, struct cbox_instrument_output *output, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, GError **error)
+{
+ if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!(cbox_execute_on(fb, NULL, "/gain_linear", "f", error, output->gain_obj.lin_gain) &&
+ cbox_execute_on(fb, NULL, "/gain", "f", error, output->gain_obj.db_gain) &&
+ cbox_execute_on(fb, NULL, "/output", "i", error, output->output_bus + 1)))
+ return FALSE;
+ return cbox_module_slot_process_cmd(&output->insert, fb, cmd, subcmd, CBOX_GET_DOCUMENT(instr->scene), instr->scene->rt, instr->scene->engine, error);
+ }
+ if (!strcmp(subcmd, "/gain") && !strcmp(cmd->arg_types, "f"))
+ {
+ // XXXKF this needs proper handling of concurrency/race conditions, might
+ // be built into the gain class in future.
+ cbox_gain_set_db(&output->gain_obj, CBOX_ARG_F(cmd, 0));
+ return TRUE;
+ }
+ if (!strcmp(subcmd, "/output") && !strcmp(cmd->arg_types, "i"))
+ {
+ int obus = CBOX_ARG_I(cmd, 0);
+ int max_outputs = instr->scene->rt->io ? instr->scene->rt->io->io_env.output_count : 2;
+ int max_obus = 1 + (max_outputs - 1) / 2;
+ if (obus < 0 || obus > max_obus) {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid output %d (must be between 1 and %d, or 0 for none)", obus, max_obus);
+ return FALSE;
+ }
+ output->output_bus = obus - 1;
+ return TRUE;
+ }
+ if (!strncmp(subcmd, "/rec_dry/", 9))
+ return cbox_execute_sub(&output->rec_dry.cmd_target, fb, cmd, subcmd + 8, error);
+ if (!strncmp(subcmd, "/rec_wet/", 9))
+ return cbox_execute_sub(&output->rec_wet.cmd_target, fb, cmd, subcmd + 8, error);
+ return cbox_module_slot_process_cmd(&output->insert, fb, cmd, subcmd, CBOX_GET_DOCUMENT(instr->scene), instr->scene->rt, instr->scene->engine, error);
+}
+
+static gboolean cbox_instrument_aux_process_cmd(struct cbox_instrument *instr, struct cbox_instrument_output *output, int id, struct cbox_command_target *fb, struct cbox_osc_command *cmd, const char *subcmd, GError **error)
+{
+ if (!strcmp(subcmd, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ if (!(cbox_execute_on(fb, NULL, "/gain_linear", "f", error, output->gain_obj.lin_gain) &&
+ cbox_execute_on(fb, NULL, "/gain", "f", error, output->gain_obj.db_gain) &&
+ cbox_execute_on(fb, NULL, "/bus", "s", error, instr->aux_output_names[id] ? instr->aux_output_names[id] : "")))
+ return FALSE;
+ return cbox_module_slot_process_cmd(&output->insert, fb, cmd, subcmd, CBOX_GET_DOCUMENT(instr->scene), instr->scene->rt, instr->scene->engine, error);
+ }
+ else if (!strcmp(subcmd, "/bus") && !strcmp(cmd->arg_types, "s"))
+ {
+ struct cbox_scene *scene = instr->scene;
+ if (!CBOX_ARG_S(cmd, 0))
+ {
+ struct cbox_aux_bus *old_bus = cbox_rt_swap_pointers(instr->module->rt, (void **)&instr->aux_outputs[id], NULL);
+ if (old_bus)
+ cbox_aux_bus_unref(old_bus);
+ return TRUE;
+ }
+ for (uint32_t i = 0; i < scene->aux_bus_count; i++)
+ {
+ if (!scene->aux_buses[i])
+ continue;
+ if (!strcmp(scene->aux_buses[i]->name, CBOX_ARG_S(cmd, 0)))
+ {
+ g_free(instr->aux_output_names[id]);
+ instr->aux_output_names[id] = g_strdup(scene->aux_buses[i]->name);
+ cbox_aux_bus_ref(scene->aux_buses[i]);
+ struct cbox_aux_bus *old_bus = cbox_rt_swap_pointers(instr->module->rt, (void **)&instr->aux_outputs[id], scene->aux_buses[i]);
+ if (old_bus)
+ cbox_aux_bus_unref(old_bus);
+ return TRUE;
+ }
+ }
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown aux bus: %s", CBOX_ARG_S(cmd, 0));
+ return FALSE;
+ }
+ else if (!strcmp(subcmd, "/output") && !strcmp(cmd->arg_types, "i")) // not supported
+ {
+ cbox_set_command_error(error, cmd);
+ return FALSE;
+ }
+ else // otherwise, treat just like an command on normal (non-aux) output
+ return cbox_instrument_output_process_cmd(instr, output, fb, cmd, subcmd, error);
+}
+
+gboolean cbox_instrument_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct cbox_instrument *instr = ct->user_data;
+ const char *subcommand = NULL;
+ int index = 0;
+ int aux_offset = instr->module->aux_offset / 2;
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/engine", "s", error, instr->module->engine_name))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/aux_offset", "i", error, instr->module->aux_offset / 2 + 1))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/outputs", "i", error, instr->module->outputs / 2))
+ return FALSE;
+ return CBOX_OBJECT_DEFAULT_STATUS(instr, fb, error);
+ }
+ else if (cbox_parse_path_part_int(cmd, "/output/", &subcommand, &index, 1, aux_offset, error))
+ {
+ if (!subcommand)
+ return FALSE;
+ if (index < 1 || index > (int)(1 + instr->module->aux_offset))
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", index, instr->module->aux_offset);
+ return FALSE;
+ }
+ return cbox_instrument_output_process_cmd(instr, &instr->outputs[index - 1], fb, cmd, subcommand, error);
+ }
+ else if (cbox_parse_path_part_int(cmd, "/aux/", &subcommand, &index, 1, instr->aux_output_count, error))
+ {
+ if (!subcommand)
+ return FALSE;
+ int acount = 1 + instr->module->outputs - instr->module->aux_offset;
+ if (index < 1 || index > acount)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d)", index, acount);
+ return FALSE;
+ }
+ return cbox_instrument_aux_process_cmd(instr, &instr->outputs[aux_offset + index - 1], index - 1, fb, cmd, subcommand, error);
+ }
+ else
+ if (!strncmp(cmd->command, "/engine/",8))
+ {
+ if (!instr->module->cmd_target.process_cmd)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "The engine %s has no command target defined", instr->module->engine_name);
+ return FALSE;
+ }
+ return cbox_execute_sub(&instr->module->cmd_target, fb, cmd, cmd->command + 7, error);
+ }
+ else if (!strcmp(cmd->command, "/move_to") && !strcmp(cmd->arg_types, "si"))
+ {
+ struct cbox_scene *new_scene = (struct cbox_scene *)CBOX_ARG_O(cmd, 0, instr->scene, cbox_scene, error);
+ if (!new_scene)
+ return FALSE;
+ int dstpos = CBOX_ARG_I(cmd, 1) - 1;
+
+ if (dstpos < 0 || (uint32_t)dstpos > new_scene->layer_count)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Invalid position %d (valid are 1..%d or 0 for append)", dstpos + 1, 1 + new_scene->layer_count);
+ return FALSE;
+ }
+
+ return cbox_scene_move_instrument_to(instr->scene, instr, new_scene, dstpos, error);
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+}
+
+void cbox_instrument_destroy_if_unused(struct cbox_instrument *instrument)
+{
+ if (instrument->refcount == 0)
+ CBOX_DELETE(instrument);
+}
+
+void cbox_instrument_destroyfunc(struct cbox_objhdr *objhdr)
+{
+ struct cbox_instrument *instrument = CBOX_H2O(objhdr);
+ assert(instrument->refcount == 0);
+ for (uint32_t i = 0; i < (uint32_t)instrument->module->outputs >> 1; i ++)
+ {
+ cbox_instrument_output_uninit(&instrument->outputs[i]);
+ }
+ free(instrument->outputs);
+ for (uint32_t i = 0; i < instrument->aux_output_count; i++)
+ {
+ g_free(instrument->aux_output_names[i]);
+ }
+ free(instrument->aux_output_names);
+ free(instrument->aux_outputs);
+ CBOX_DELETE(instrument->module);
+ free(instrument);
+}
+
+void cbox_instrument_unref_aux_buses(struct cbox_instrument *instrument)
+{
+ for (uint32_t j = 0; j < instrument->aux_output_count; j++)
+ {
+ if (instrument->aux_outputs[j])
+ cbox_aux_bus_unref(instrument->aux_outputs[j]);
+ }
+}
+
+void cbox_instrument_disconnect_aux_bus(struct cbox_instrument *instrument, struct cbox_aux_bus *bus)
+{
+ for (uint32_t j = 0; j < instrument->aux_output_count; j++)
+ {
+ if (instrument->aux_outputs[j] == bus)
+ {
+ cbox_aux_bus_unref(instrument->aux_outputs[j]);
+ instrument->aux_outputs[j] = NULL;
+ }
+ }
+}
+
+void cbox_instrument_output_init(struct cbox_instrument_output *output, struct cbox_scene *scene, uint32_t max_numsamples)
+{
+ cbox_recording_source_init(&output->rec_dry, scene, max_numsamples, 2);
+ cbox_recording_source_init(&output->rec_wet, scene, max_numsamples, 2);
+ output->insert = NULL;
+ output->output_bus = 0;
+ cbox_gain_init(&output->gain_obj);
+}
+
+
+void cbox_instrument_output_uninit(struct cbox_instrument_output *output)
+{
+ cbox_recording_source_uninit(&output->rec_dry);
+ cbox_recording_source_uninit(&output->rec_wet);
+ if (output->insert)
+ {
+ CBOX_DELETE(output->insert);
+ output->insert = NULL;
+ }
+}
diff --git a/template/calfbox/instr.h b/template/calfbox/instr.h
new file mode 100644
index 0000000..a8b4eb8
--- /dev/null
+++ b/template/calfbox/instr.h
@@ -0,0 +1,61 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_INSTR_H
+#define CBOX_INSTR_H
+
+#include "dspmath.h"
+#include "recsrc.h"
+
+CBOX_EXTERN_CLASS(cbox_instrument)
+
+struct cbox_module;
+struct cbox_rt;
+struct cbox_scene;
+struct cbox_instruments;
+
+struct cbox_instrument_output
+{
+ struct cbox_module *insert;
+ int output_bus;
+ struct cbox_gain gain_obj;
+ struct cbox_recording_source rec_dry, rec_wet;
+};
+
+struct cbox_instrument
+{
+ CBOX_OBJECT_HEADER()
+ struct cbox_command_target cmd_target;
+ struct cbox_module *module;
+ struct cbox_instrument_output *outputs;
+ struct cbox_scene *scene;
+ int refcount;
+ gchar **aux_output_names;
+ struct cbox_aux_bus **aux_outputs;
+ uint32_t aux_output_count;
+};
+
+extern void cbox_instrument_unref_aux_buses(struct cbox_instrument *instrument);
+extern void cbox_instrument_disconnect_aux_bus(struct cbox_instrument *instrument, struct cbox_aux_bus *bus);
+extern void cbox_instrument_destroy_if_unused(struct cbox_instrument *instrument);
+extern gboolean cbox_instrument_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
+
+extern void cbox_instrument_output_init(struct cbox_instrument_output *output, struct cbox_scene *scene, uint32_t max_numsamples);
+extern void cbox_instrument_output_uninit(struct cbox_instrument_output *output);
+
+#endif
diff --git a/template/calfbox/io.c b/template/calfbox/io.c
new file mode 100644
index 0000000..9ffa7df
--- /dev/null
+++ b/template/calfbox/io.c
@@ -0,0 +1,625 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "app.h"
+#include "config.h"
+#include "config-api.h"
+#include "engine.h"
+#include "errors.h"
+#include "hwcfg.h"
+#include "io.h"
+#include "meter.h"
+#include "midi.h"
+#include "mididest.h"
+#include "recsrc.h"
+#include "seq.h"
+
+#include
+#include
+#include
+#include
+
+const char *cbox_io_section = "io";
+
+gboolean cbox_io_init(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error)
+{
+#if USE_JACK
+#if USE_LIBUSB
+ if (cbox_config_get_int(cbox_io_section, "use_usb", 0))
+ return cbox_io_init_usb(io, params, fb, error);
+#endif
+ return cbox_io_init_jack(io, params, fb, error);
+#else
+#if USE_LIBUSB
+ return cbox_io_init_usb(io, params, fb, error);
+#endif
+#endif
+}
+
+int cbox_io_get_sample_rate(struct cbox_io *io)
+{
+ return io->impl->getsampleratefunc(io->impl);
+}
+
+void cbox_io_poll_ports(struct cbox_io *io, struct cbox_command_target *fb)
+{
+ io->impl->pollfunc(io->impl, fb);
+}
+
+int cbox_io_get_midi_data(struct cbox_io *io, struct cbox_midi_buffer *destination)
+{
+ return io->impl->getmidifunc(io->impl, destination);
+}
+
+int cbox_io_start(struct cbox_io *io, struct cbox_io_callbacks *cb, struct cbox_command_target *fb)
+{
+ io->cb = cb;
+ return io->impl->startfunc(io->impl, fb, NULL);
+}
+
+gboolean cbox_io_get_disconnect_status(struct cbox_io *io, GError **error)
+{
+ return io->impl->getstatusfunc(io->impl, error);
+}
+
+gboolean cbox_io_cycle(struct cbox_io *io, struct cbox_command_target *fb, GError **error)
+{
+ return io->impl->cyclefunc(io->impl, fb, error);
+}
+
+int cbox_io_stop(struct cbox_io *io)
+{
+ int result = io->impl->stopfunc(io->impl, NULL);
+ if (io->cb && io->cb->on_stopped)
+ io->cb->on_stopped(io->cb->user_data);
+ io->cb = NULL;
+ return result;
+}
+
+struct cbox_midi_output *cbox_io_get_midi_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid)
+{
+ if (uuid)
+ {
+ for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
+ {
+ struct cbox_midi_output *midiout = p->data;
+ if (!midiout->removing && cbox_uuid_equal(&midiout->uuid, uuid))
+ return midiout;
+ }
+ }
+ if (name)
+ {
+ for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
+ {
+ struct cbox_midi_output *midiout = p->data;
+ if (!midiout->removing && !strcmp(midiout->name, name))
+ return midiout;
+ }
+ }
+ return NULL;
+}
+
+struct cbox_midi_input *cbox_io_get_midi_input(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid)
+{
+ if (uuid)
+ {
+ for (GSList *p = io->midi_inputs; p; p = g_slist_next(p))
+ {
+ struct cbox_midi_input *midiin = p->data;
+ if (!midiin->removing && cbox_uuid_equal(&midiin->uuid, uuid))
+ return midiin;
+ }
+ }
+ if (name)
+ {
+ for (GSList *p = io->midi_inputs; p; p = g_slist_next(p))
+ {
+ struct cbox_midi_input *midiin = p->data;
+ if (!midiin->removing && !strcmp(midiin->name, name))
+ return midiin;
+ }
+ }
+ return NULL;
+}
+
+struct cbox_audio_output *cbox_io_get_audio_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid)
+{
+ if (uuid)
+ {
+ for (GSList *p = io->audio_outputs; p; p = g_slist_next(p))
+ {
+ struct cbox_audio_output *audioout = p->data;
+ if (!audioout->removing && cbox_uuid_equal(&audioout->uuid, uuid))
+ return audioout;
+ }
+ }
+ if (name)
+ {
+ for (GSList *p = io->audio_outputs; p; p = g_slist_next(p))
+ {
+ struct cbox_audio_output *audioout = p->data;
+ if (!audioout->removing && !strcmp(audioout->name, name))
+ return audioout;
+ }
+ }
+ return NULL;
+}
+
+struct cbox_midi_output *cbox_io_create_midi_output(struct cbox_io *io, const char *name, GError **error)
+{
+ struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, name, NULL);
+ if (midiout)
+ return midiout;
+
+ midiout = io->impl->createmidioutfunc(io->impl, name, error);
+ if (!midiout)
+ return NULL;
+
+ io->midi_outputs = g_slist_prepend(io->midi_outputs, midiout);
+
+ // Notify client code to connect to new outputs if needed
+ if (io->cb->on_midi_outputs_changed)
+ io->cb->on_midi_outputs_changed(io->cb->user_data);
+ return midiout;
+}
+
+void cbox_io_destroy_midi_output(struct cbox_io *io, struct cbox_midi_output *midiout)
+{
+ midiout->removing = TRUE;
+
+ // This is not a very efficient way to do it. However, in this case,
+ // the list will rarely contain more than 10 elements, so simplicity
+ // and correctness may be more important.
+ GSList *copy = g_slist_copy(io->midi_outputs);
+ copy = g_slist_remove(copy, midiout);
+
+ GSList *old = io->midi_outputs;
+ io->midi_outputs = copy;
+
+ cbox_midi_merger_close(&midiout->merger, app.rt);
+ assert(!midiout->merger.inputs);
+
+ // Notify client code to disconnect the output and to make sure the RT code
+ // is not using the old list anymore
+ if (io->cb->on_midi_outputs_changed)
+ io->cb->on_midi_outputs_changed(io->cb->user_data);
+
+ assert(!midiout->merger.inputs);
+
+ g_slist_free(old);
+ io->impl->destroymidioutfunc(io->impl, midiout);
+}
+
+struct cbox_midi_input *cbox_io_create_midi_input(struct cbox_io *io, const char *name, GError **error)
+{
+ struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, name, NULL);
+ if (midiin)
+ return midiin;
+
+ midiin = io->impl->createmidiinfunc(io->impl, name, error);
+ if (!midiin)
+ return NULL;
+
+ io->midi_inputs = g_slist_prepend(io->midi_inputs, midiin);
+
+ // Notify client code to connect to new inputs if needed
+ if (io->cb->on_midi_inputs_changed)
+ io->cb->on_midi_inputs_changed(io->cb->user_data);
+ return midiin;
+}
+
+void cbox_io_destroy_midi_input(struct cbox_io *io, struct cbox_midi_input *midiin)
+{
+ midiin->removing = TRUE;
+
+ // This is not a very efficient way to do it. However, in this case,
+ // the list will rarely contain more than 10 elements, so simplicity
+ // and correctness may be more important.
+ GSList *copy = g_slist_copy(io->midi_inputs);
+ copy = g_slist_remove(copy, midiin);
+
+ GSList *old = io->midi_inputs;
+ io->midi_inputs = copy;
+
+ // Notify client code to disconnect the input and to make sure the RT code
+ // is not using the old list anymore
+ if (io->cb->on_midi_inputs_changed)
+ io->cb->on_midi_inputs_changed(io->cb->user_data);
+
+ g_slist_free(old);
+ io->impl->destroymidiinfunc(io->impl, midiin);
+}
+
+void cbox_io_destroy_all_midi_ports(struct cbox_io *io)
+{
+ for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
+ {
+ struct cbox_midi_output *midiout = p->data;
+ midiout->removing = TRUE;
+ }
+ for (GSList *p = io->midi_inputs; p; p = g_slist_next(p))
+ {
+ struct cbox_midi_output *midiin = p->data;
+ midiin->removing = TRUE;
+ }
+
+ GSList *old_i = io->midi_inputs, *old_o = io->midi_outputs;
+ io->midi_outputs = NULL;
+ io->midi_inputs = NULL;
+ // Notify client code to disconnect the output and to make sure the RT code
+ // is not using the old list anymore
+ if (io->cb && io->cb->on_midi_outputs_changed)
+ io->cb->on_midi_outputs_changed(io->cb->user_data);
+ if (io->cb && io->cb->on_midi_inputs_changed)
+ io->cb->on_midi_inputs_changed(io->cb->user_data);
+
+ while(old_o)
+ {
+ struct cbox_midi_output *midiout = old_o->data;
+ cbox_midi_merger_close(&midiout->merger, app.rt);
+ assert(!midiout->merger.inputs);
+ io->impl->destroymidioutfunc(io->impl, midiout);
+ old_o = g_slist_remove(old_o, midiout);
+ }
+ g_slist_free(old_o);
+
+ while(old_i)
+ {
+ struct cbox_midi_input *midiin = old_i->data;
+ io->impl->destroymidiinfunc(io->impl, midiin);
+ old_i = g_slist_remove(old_i, midiin);
+ }
+ g_slist_free(old_i);
+}
+
+static void cbox_audio_output_router_record_block(struct cbox_recorder *handler, const float **buffers, uint32_t offset, uint32_t numsamples)
+{
+ struct cbox_audio_output_router *router = handler->user_data;
+ if (router->left->removing || router->right->removing)
+ return;
+ cbox_gain_add_stereo(&router->gain, &router->left->buffer[offset], buffers[0], &router->right->buffer[offset], buffers[1], numsamples);
+}
+
+static gboolean cbox_audio_output_router_attach(struct cbox_recorder *handler, struct cbox_recording_source *src, GError **error)
+{
+ struct cbox_audio_output_router *router = handler->user_data;
+ if (router->attached)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Router already attached");
+ return FALSE;
+ }
+ router->source = src;
+ router->attached++;
+ return TRUE;
+}
+
+static gboolean cbox_audio_output_router_detach(struct cbox_recorder *handler, GError **error)
+{
+ struct cbox_audio_output_router *router = handler->user_data;
+ if (router->attached != 1)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Router not yet attached");
+ return FALSE;
+ }
+ assert (router->source);
+ --router->attached;
+ assert (router->attached == 0);
+ router->source = NULL;
+ return TRUE;
+}
+
+static void cbox_audio_output_router_destroy(struct cbox_recorder *handler)
+{
+ struct cbox_audio_output_router *router = handler->user_data;
+ if (router->attached)
+ cbox_recording_source_detach(router->source, &router->recorder, NULL);
+ assert(!router->attached);
+ router->left->users--;
+ router->right->users--;
+}
+
+static gboolean cbox_audio_output_router_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct cbox_audio_output_router *router = ct->user_data;
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ if (!cbox_execute_on(fb, NULL, "/gain", "f", error, router->gain.db_gain))
+ return FALSE;
+ return CBOX_OBJECT_DEFAULT_STATUS(&router->recorder, fb, error);
+ }
+ if (!strcmp(cmd->command, "/gain") && !strcmp(cmd->arg_types, "f"))
+ {
+ cbox_gain_set_db(&router->gain, CBOX_ARG_F(cmd, 0));
+ return TRUE;
+ }
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+}
+
+struct cbox_audio_output_router *cbox_io_create_audio_output_router(struct cbox_io *io, struct cbox_engine *engine, struct cbox_audio_output *left, struct cbox_audio_output *right)
+{
+ struct cbox_audio_output_router *router = calloc(1, sizeof(struct cbox_audio_output_router));
+ CBOX_OBJECT_HEADER_INIT(&router->recorder, cbox_recorder, CBOX_GET_DOCUMENT(engine));
+ cbox_command_target_init(&router->recorder.cmd_target, cbox_audio_output_router_process_cmd, &router->recorder);
+ router->recorder.user_data = router;
+ router->recorder.attach = cbox_audio_output_router_attach;
+ router->recorder.record_block = cbox_audio_output_router_record_block;
+ router->recorder.detach = cbox_audio_output_router_detach;
+ router->recorder.destroy = cbox_audio_output_router_destroy;
+ router->source = NULL;
+ router->left = left;
+ router->right = right;
+ router->attached = 0;
+ cbox_gain_init(&router->gain);
+ cbox_gain_set_db(&router->gain, 12.0);
+ left->users++;
+ right->users++;
+ CBOX_OBJECT_REGISTER(&router->recorder);
+ return router;
+}
+
+struct cbox_audio_output *cbox_io_create_audio_output(struct cbox_io *io, const char *name, GError **error)
+{
+ struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, name, NULL);
+ if (audioout)
+ return audioout;
+
+ audioout = io->impl->createaudiooutfunc(io->impl, name, error);
+ if (!audioout)
+ return NULL;
+
+ io->audio_outputs = g_slist_prepend(io->audio_outputs, audioout);
+
+ // Notify client code to connect to new outputs if needed
+ if (io->cb->on_audio_outputs_changed)
+ io->cb->on_audio_outputs_changed(io->cb->user_data);
+ return audioout;
+}
+
+struct cbox_audio_output *cbox_io_get_audio_output_by_uuid_string(struct cbox_io *io, const char *uuidstr, GError **error)
+{
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return NULL;
+ struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, NULL, &uuid);
+ if (!audioout)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return NULL;
+ }
+ return audioout;
+}
+
+gboolean cbox_io_destroy_audio_output(struct cbox_io *io, struct cbox_audio_output *audioout, GError **error)
+{
+ if (audioout->users)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' is in use", audioout->name);
+ return FALSE;
+ }
+ audioout->removing = TRUE;
+
+ // This is not a very efficient way to do it. However, in this case,
+ // the list will rarely contain more than 10 elements, so simplicity
+ // and correctness may be more important.
+ GSList *copy = g_slist_copy(io->audio_outputs);
+ copy = g_slist_remove(copy, audioout);
+
+ GSList *old = io->audio_outputs;
+ io->audio_outputs = copy;
+
+ // Notify client code to disconnect the output and to make sure the RT code
+ // is not using the old list anymore
+ if (io->cb->on_audio_outputs_changed)
+ io->cb->on_audio_outputs_changed(io->cb->user_data);
+
+ g_slist_free(old);
+ io->impl->destroyaudiooutfunc(io->impl, audioout);
+ return TRUE;
+}
+
+gboolean cbox_io_process_cmd(struct cbox_io *io, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error, gboolean *cmd_handled)
+{
+ *cmd_handled = FALSE;
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ *cmd_handled = TRUE;
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ for (GSList *p = io->midi_inputs; p; p = g_slist_next(p))
+ {
+ struct cbox_midi_input *midiin = p->data;
+ if (!midiin->removing)
+ {
+ if (!cbox_execute_on(fb, NULL, "/midi_input", "su", error, midiin->name, &midiin->uuid))
+ return FALSE;
+ }
+ }
+ for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
+ {
+ struct cbox_midi_output *midiout = p->data;
+ if (!midiout->removing)
+ {
+ if (!cbox_execute_on(fb, NULL, "/midi_output", "su", error, midiout->name, &midiout->uuid))
+ return FALSE;
+ }
+ }
+ return cbox_execute_on(fb, NULL, "/audio_inputs", "i", error, io->io_env.input_count) &&
+ cbox_execute_on(fb, NULL, "/audio_outputs", "i", error, io->io_env.output_count) &&
+ cbox_execute_on(fb, NULL, "/sample_rate", "i", error, io->io_env.srate) &&
+ cbox_execute_on(fb, NULL, "/buffer_size", "i", error, io->io_env.buffer_size);
+ }
+ else if (io->impl->createmidiinfunc && !strcmp(cmd->command, "/create_midi_input") && !strcmp(cmd->arg_types, "s"))
+ {
+ *cmd_handled = TRUE;
+ struct cbox_midi_input *midiin;
+ midiin = cbox_io_create_midi_input(io, CBOX_ARG_S(cmd, 0), error);
+ if (!midiin)
+ return FALSE;
+ cbox_midi_appsink_init(&midiin->appsink, app.rt, &app.engine->stmap->tmap);
+ return cbox_uuid_report(&midiin->uuid, fb, error);
+ }
+ else if (!strcmp(cmd->command, "/route_midi_input") && !strcmp(cmd->arg_types, "ss"))
+ {
+ *cmd_handled = TRUE;
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
+ if (!midiin)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ if (*CBOX_ARG_S(cmd, 1))
+ {
+ if (cbox_uuid_fromstring(&midiin->output, CBOX_ARG_S(cmd, 1), error))
+ midiin->output_set = TRUE;
+ }
+ else
+ midiin->output_set = FALSE;
+ if (io->impl->updatemidiinroutingfunc)
+ io->impl->updatemidiinroutingfunc(io->impl);
+ if (io->cb->on_midi_inputs_changed)
+ io->cb->on_midi_inputs_changed(io->cb->user_data);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/set_appsink_for_midi_input") && !strcmp(cmd->arg_types, "si"))
+ {
+ *cmd_handled = TRUE;
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
+ if (!midiin)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ midiin->enable_appsink = CBOX_ARG_I(cmd, 1);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/get_new_events") && !strcmp(cmd->arg_types, "s"))
+ {
+ *cmd_handled = TRUE;
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
+ if (!midiin)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ if (!midiin->enable_appsink)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "App sink not enabled for port '%s'", uuidstr);
+ return FALSE;
+ }
+ return cbox_midi_appsink_send_to(&midiin->appsink, fb, error);
+ }
+ else if (io->impl->createmidioutfunc && !strcmp(cmd->command, "/create_midi_output") && !strcmp(cmd->arg_types, "s"))
+ {
+ *cmd_handled = TRUE;
+ struct cbox_midi_output *midiout;
+ midiout = cbox_io_create_midi_output(io, CBOX_ARG_S(cmd, 0), error);
+ if (!midiout)
+ return FALSE;
+ return cbox_uuid_report(&midiout->uuid, fb, error);
+ }
+ else if (io->impl->destroymidiinfunc && !strcmp(cmd->command, "/delete_midi_input") && !strcmp(cmd->arg_types, "s"))
+ {
+ *cmd_handled = TRUE;
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
+ if (!midiin)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ cbox_io_destroy_midi_input(io, midiin);
+ return TRUE;
+ }
+ else if (io->impl->destroymidioutfunc && !strcmp(cmd->command, "/delete_midi_output") && !strcmp(cmd->arg_types, "s"))
+ {
+ *cmd_handled = TRUE;
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, NULL, &uuid);
+ if (!midiout)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ cbox_io_destroy_midi_output(io, midiout);
+ return TRUE;
+ }
+ else if (io->impl->createaudiooutfunc && !strcmp(cmd->command, "/create_audio_output") && !strcmp(cmd->arg_types, "s"))
+ {
+ *cmd_handled = TRUE;
+ struct cbox_audio_output *audioout;
+ audioout = cbox_io_create_audio_output(io, CBOX_ARG_S(cmd, 0), error);
+ if (!audioout)
+ return FALSE;
+ return cbox_uuid_report(&audioout->uuid, fb, error);
+ }
+ else if (io->impl->destroyaudiooutfunc && !strcmp(cmd->command, "/delete_audio_output") && !strcmp(cmd->arg_types, "s"))
+ {
+ *cmd_handled = TRUE;
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ struct cbox_audio_output *audioout = cbox_io_get_audio_output_by_uuid_string(io, uuidstr, error);
+ if (!audioout)
+ return FALSE;
+ return cbox_io_destroy_audio_output(io, audioout, error);
+ }
+ else if (io->impl->createmidioutfunc && !strcmp(cmd->command, "/create_audio_output_router") && !strcmp(cmd->arg_types, "ss"))
+ {
+ *cmd_handled = TRUE;
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ struct cbox_audio_output *left = cbox_io_get_audio_output_by_uuid_string(io, uuidstr, error);
+ if (!left)
+ return FALSE;
+ uuidstr = CBOX_ARG_S(cmd, 1);
+ struct cbox_audio_output *right = cbox_io_get_audio_output_by_uuid_string(io, uuidstr, error);
+ if (!right)
+ return FALSE;
+ // XXXKF hack alert
+ struct cbox_audio_output_router *router = cbox_io_create_audio_output_router(io, app.engine, left, right);
+ return cbox_uuid_report(&router->recorder._obj_hdr.instance_uuid, fb, error);
+ }
+ return FALSE;
+}
+
+void cbox_io_close(struct cbox_io *io)
+{
+ io->impl->destroyfunc(io->impl);
+ io->impl = NULL;
+}
+
diff --git a/template/calfbox/io.h b/template/calfbox/io.h
new file mode 100644
index 0000000..f200c63
--- /dev/null
+++ b/template/calfbox/io.h
@@ -0,0 +1,194 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_IO_H
+#define CBOX_IO_H
+
+#include
+#include "config.h"
+#if USE_JACK
+#include
+#endif
+#include "dspmath.h"
+#include "dom.h"
+#include "ioenv.h"
+#include "master.h"
+#include "mididest.h"
+#include "recsrc.h"
+
+struct cbox_io;
+struct cbox_io_callbacks;
+struct cbox_recording_source;
+struct cbox_meter;
+struct cbox_midi_buffer;
+struct cbox_scene;
+
+struct cbox_open_params
+{
+};
+
+struct cbox_io_impl
+{
+ struct cbox_io *pio;
+
+ int (*getsampleratefunc)(struct cbox_io_impl *ioi);
+ gboolean (*startfunc)(struct cbox_io_impl *ioi, struct cbox_command_target *fb, GError **error);
+ gboolean (*stopfunc)(struct cbox_io_impl *ioi, GError **error);
+ gboolean (*cyclefunc)(struct cbox_io_impl *ioi, struct cbox_command_target *fb, GError **error);
+ gboolean (*getstatusfunc)(struct cbox_io_impl *ioi, GError **error);
+ void (*pollfunc)(struct cbox_io_impl *ioi, struct cbox_command_target *fb);
+ int (*getmidifunc)(struct cbox_io_impl *ioi, struct cbox_midi_buffer *destination);
+ struct cbox_midi_output *(*createmidioutfunc)(struct cbox_io_impl *ioi, const char *name, GError **error);
+ void (*destroymidioutfunc)(struct cbox_io_impl *ioi, struct cbox_midi_output *midiout);
+ struct cbox_midi_input *(*createmidiinfunc)(struct cbox_io_impl *ioi, const char *name, GError **error);
+ void (*destroymidiinfunc)(struct cbox_io_impl *ioi, struct cbox_midi_input *midiout);
+ void (*updatemidiinroutingfunc)(struct cbox_io_impl *ioi);
+ struct cbox_audio_output *(*createaudiooutfunc)(struct cbox_io_impl *ioi, const char *name, GError **error);
+ void (*destroyaudiooutfunc)(struct cbox_io_impl *ioi, struct cbox_audio_output *audioout);
+ void (*controltransportfunc)(struct cbox_io_impl *ioi, gboolean roll, uint32_t pos); // (uint32_t)-1 if no change
+ gboolean (*getsynccompletedfunc)(struct cbox_io_impl *ioi);
+ void (*destroyfunc)(struct cbox_io_impl *ioi);
+};
+
+struct cbox_io
+{
+ struct cbox_io_impl *impl;
+ struct cbox_command_target cmd_target;
+
+ float **input_buffers; // only valid inside jack_rt_process
+ float **output_buffers; // only valid inside jack_rt_process
+ struct cbox_io_env io_env;
+
+ struct cbox_io_callbacks *cb;
+ GSList *midi_inputs;
+ GSList *midi_outputs;
+ GSList *audio_outputs;
+ uint32_t free_running_frame_counter;
+};
+
+enum cbox_transport_state
+{
+ ts_stopping,
+ ts_stopped,
+ ts_starting,
+ ts_rolling,
+};
+
+struct cbox_transport_position
+{
+ uint32_t bar;
+ uint32_t beat;
+ uint32_t tick;
+ uint32_t offset;
+ double tempo;
+ double ticks_per_beat;
+ double bar_start_tick;
+ uint32_t timesig_num;
+ uint32_t timesig_denom;
+};
+
+struct cbox_io_callbacks
+{
+ void *user_data;
+
+ void (*process)(void *user_data, struct cbox_io *io, uint32_t nframes);
+ void (*on_started)(void *user_data);
+ void (*on_stopped)(void *user_data);
+ void (*on_disconnected)(void *user_data);
+ void (*on_reconnected)(void *user_data);
+ void (*on_midi_inputs_changed)(void *user_data);
+ void (*on_midi_outputs_changed)(void *user_data);
+ void (*on_audio_outputs_changed)(void *user_data);
+ gboolean (*on_transport_sync)(void *user_data, enum cbox_transport_state state, uint32_t frame);
+ void (*get_transport_data)(void *user_data, gboolean explicit_pos, uint32_t time_samples, struct cbox_transport_position *tp);
+ gboolean (*on_tempo_sync)(void *user_data, double beats_per_minute);
+};
+
+struct cbox_midi_input
+{
+ gchar *name;
+ struct cbox_uuid uuid;
+ struct cbox_midi_buffer buffer;
+ gboolean removing;
+ gboolean output_set;
+ struct cbox_uuid output;
+ gboolean enable_appsink;
+ struct cbox_midi_appsink appsink;
+};
+
+struct cbox_midi_output
+{
+ gchar *name;
+ struct cbox_uuid uuid;
+ struct cbox_midi_buffer buffer;
+ struct cbox_midi_merger merger;
+ // This is set if the output is in process of being removed and should not
+ // be used for output.
+ gboolean removing;
+};
+
+struct cbox_audio_output
+{
+ gchar *name;
+ struct cbox_uuid uuid;
+ // This is set if the output is in process of being removed and should not
+ // be used for output.
+ gboolean removing;
+ float *buffer;
+ uint32_t users;
+};
+
+struct cbox_audio_output_router
+{
+ struct cbox_recorder recorder;
+ struct cbox_recording_source *source;
+ struct cbox_audio_output *left, *right;
+ struct cbox_gain gain;
+ int attached;
+};
+
+extern gboolean cbox_io_init(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error);
+#if USE_JACK
+extern gboolean cbox_io_init_jack(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error);
+#endif
+extern gboolean cbox_io_init_usb(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error);
+
+extern int cbox_io_start(struct cbox_io *io, struct cbox_io_callbacks *cb, struct cbox_command_target *fb);
+extern int cbox_io_stop(struct cbox_io *io);
+extern int cbox_io_get_sample_rate(struct cbox_io *io);
+extern int cbox_io_get_midi_data(struct cbox_io *io, struct cbox_midi_buffer *destination);
+extern gboolean cbox_io_get_disconnect_status(struct cbox_io *io, GError **error);
+extern gboolean cbox_io_cycle(struct cbox_io *io, struct cbox_command_target *fb, GError **error);
+extern void cbox_io_poll_ports(struct cbox_io *io, struct cbox_command_target *fb);
+extern struct cbox_midi_input *cbox_io_get_midi_input(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid);
+extern struct cbox_midi_output *cbox_io_get_midi_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid);
+extern struct cbox_audio_output *cbox_io_get_audio_output(struct cbox_io *io, const char *name, const struct cbox_uuid *uuid);
+extern struct cbox_audio_output *cbox_io_get_audio_output_by_uuid_string(struct cbox_io *io, const char *uuidstr, GError **error);
+extern struct cbox_midi_output *cbox_io_create_midi_output(struct cbox_io *io, const char *name, GError **error);
+extern void cbox_io_destroy_midi_output(struct cbox_io *io, struct cbox_midi_output *midiout);
+extern struct cbox_audio_output *cbox_io_create_audio_output(struct cbox_io *io, const char *name, GError **error);
+extern gboolean cbox_io_destroy_audio_output(struct cbox_io *io, struct cbox_audio_output *audioout, GError **error);
+extern struct cbox_midi_input *cbox_io_create_midi_input(struct cbox_io *io, const char *name, GError **error);
+extern void cbox_io_destroy_midi_input(struct cbox_io *io, struct cbox_midi_input *midiin);
+extern void cbox_io_destroy_all_midi_ports(struct cbox_io *io);
+extern gboolean cbox_io_process_cmd(struct cbox_io *io, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error, gboolean *cmd_handled);
+extern void cbox_io_close(struct cbox_io *io);
+
+extern const char *cbox_io_section;
+
+#endif
diff --git a/template/calfbox/ioenv.h b/template/calfbox/ioenv.h
new file mode 100644
index 0000000..9b0181f
--- /dev/null
+++ b/template/calfbox/ioenv.h
@@ -0,0 +1,46 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2013 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_IOENV_H
+#define CBOX_IOENV_H
+
+struct cbox_io_env
+{
+ int srate;
+ uint32_t buffer_size;
+ uint32_t input_count, output_count;
+};
+
+static inline void cbox_io_env_clear(struct cbox_io_env *env)
+{
+ env->srate = 0;
+ env->buffer_size = 0;
+ env->input_count = 0;
+ env->output_count = 0;
+}
+
+static inline void cbox_io_env_copy(struct cbox_io_env *dest, const struct cbox_io_env *src)
+{
+ dest->srate = src->srate;
+ dest->buffer_size = src->buffer_size;
+ dest->input_count = src->input_count;
+ dest->output_count = src->output_count;
+}
+
+#endif
+
diff --git a/template/calfbox/jack_api_example.py b/template/calfbox/jack_api_example.py
new file mode 100644
index 0000000..be40d58
--- /dev/null
+++ b/template/calfbox/jack_api_example.py
@@ -0,0 +1,177 @@
+from calfbox import cbox
+import time
+
+def cmd_dumper(cmd, fb, args):
+ print ("%s(%s)" % (cmd, ",".join(list(map(repr,args)))))
+
+cbox.init_engine()
+
+cbox.Config.add_section("drumpattern:pat1", """
+title=Straight - Verse
+beats=4
+track1=bd
+track2=sd
+track3=hh
+track4=ho
+bd_note=c1
+sd_note=d1
+hh_note=f#1
+ho_note=a#1
+bd_trigger=9... .... 9.6. ....
+sd_trigger=.... 9..5 .2.. 9...
+hh_trigger=9353 7353 7353 73.3
+ho_trigger=.... .... .... ..3.
+""")
+
+cbox.Config.set("io", "use_usb", 0)
+cbox.start_audio(cmd_dumper)
+
+global Document
+Document = cbox.Document
+
+status = cbox.JackIO.status()
+client_name = status.client_name
+print ("Client name: %s" % client_name)
+print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs))
+print ("Sample rate: %d frames/sec" % (status.sample_rate))
+print ("JACK period: %d frames" % (status.buffer_size))
+uuid_bad = cbox.JackIO.create_midi_output('bad')
+uuid_bad2 = cbox.JackIO.create_midi_input('bad2')
+cbox.JackIO.autoconnect_midi_output(uuid_bad, '%s:bad2' % client_name)
+print (cbox.JackIO.get_connected_ports('%s:bad' % client_name))
+try:
+ cbox.JackIO.disconnect_midi_input(uuid_bad)
+ assert False
+except:
+ pass
+try:
+ cbox.JackIO.disconnect_midi_output(uuid_bad2)
+ assert False
+except:
+ pass
+assert len(cbox.JackIO.get_connected_ports('%s:bad' % client_name)) == 1
+assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad)
+assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad2)
+cbox.JackIO.disconnect_midi_output(uuid_bad)
+assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == []
+assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == []
+assert cbox.JackIO.get_connected_ports(uuid_bad) == []
+assert cbox.JackIO.get_connected_ports(uuid_bad2) == []
+
+cbox.JackIO.autoconnect_midi_output(uuid_bad2, '%s:bad' % client_name)
+assert len(cbox.JackIO.get_connected_ports('%s:bad' % client_name)) == 1
+assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad)
+assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad2)
+cbox.JackIO.disconnect_midi_input(uuid_bad2)
+assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == []
+assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == []
+assert cbox.JackIO.get_connected_ports(uuid_bad) == []
+assert cbox.JackIO.get_connected_ports(uuid_bad2) == []
+
+cbox.JackIO.autoconnect_midi_output(uuid_bad2, '%s:bad' % client_name)
+assert len(cbox.JackIO.get_connected_ports('%s:bad' % client_name)) == 1
+assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad)
+assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad2)
+cbox.JackIO.disconnect_midi_port(uuid_bad)
+assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == []
+assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == []
+assert cbox.JackIO.get_connected_ports(uuid_bad) == []
+assert cbox.JackIO.get_connected_ports(uuid_bad2) == []
+
+cbox.JackIO.autoconnect_midi_output(uuid_bad2, '%s:bad' % client_name)
+assert len(cbox.JackIO.get_connected_ports('%s:bad' % client_name)) == 1
+assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad)
+assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == cbox.JackIO.get_connected_ports(uuid_bad2)
+cbox.JackIO.disconnect_midi_port(uuid_bad2)
+assert cbox.JackIO.get_connected_ports('%s:bad' % client_name) == []
+assert cbox.JackIO.get_connected_ports('%s:bad2' % client_name) == []
+assert cbox.JackIO.get_connected_ports(uuid_bad) == []
+assert cbox.JackIO.get_connected_ports(uuid_bad2) == []
+
+cbox.JackIO.delete_midi_output(uuid_bad)
+cbox.JackIO.delete_midi_input(uuid_bad2)
+
+uuid = cbox.JackIO.create_midi_output('drums')
+cbox.JackIO.autoconnect_midi_output(uuid, '*alsa_pcm:.*')
+cbox.JackIO.rename_midi_output(uuid, 'kettles')
+
+uuid_in = cbox.JackIO.create_midi_input('extra')
+cbox.JackIO.autoconnect_midi_input(uuid_in, '*alsa_pcm:.*')
+cbox.JackIO.rename_midi_input(uuid_in, 'extra_port')
+
+uuid2 = cbox.JackIO.create_midi_output('violins')
+
+print (cbox.JackIO.jack_transport_position())
+status = cbox.JackIO.status()
+print ("Before deleting, MIDI outputs: %s" % status.midi_output)
+
+cbox.JackIO.delete_midi_output(uuid2)
+status = cbox.JackIO.status()
+print ("After deleting, MIDI outputs: %s" % status.midi_output)
+
+print ("Physical audio inputs: %s" % (",".join(cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL))))
+print ("Physical audio outputs: %s" % (",".join(cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SINK | cbox.JackIO.PORT_IS_PHYSICAL))))
+print ("Physical MIDI inputs: %s" % (",".join(cbox.JackIO.get_ports(".*", cbox.JackIO.MIDI_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL))))
+print ("Physical MIDI outputs: %s" % (",".join(cbox.JackIO.get_ports(".*", cbox.JackIO.MIDI_TYPE, cbox.JackIO.PORT_IS_SINK | cbox.JackIO.PORT_IS_PHYSICAL))))
+
+inputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL)
+outputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SINK | cbox.JackIO.PORT_IS_PHYSICAL)
+cbox.JackIO.port_connect(inputs[0], outputs[0])
+cbox.JackIO.port_connect(inputs[1], outputs[1])
+#assert "cbox:in_3" in cbox.JackIO.get_connected_ports(inputs[0])
+cbox.JackIO.port_disconnect(inputs[0], outputs[0])
+cbox.JackIO.port_disconnect(inputs[1], outputs[1])
+
+scene = Document.get_scene()
+scene.clear()
+instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument()
+pgm_no = instrument.engine.get_unused_program()
+pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass')
+instrument.engine.set_patch(1, pgm_no)
+instrument.engine.set_patch(10, pgm_no)
+
+song = Document.get_song()
+track = song.add_track()
+track.set_external_output(uuid)
+print ("Track outputs to: %s:%s" % (client_name, track.status().external_output))
+pattern = song.load_drum_pattern("pat1")
+track.add_clip(0, 0, pattern.status().loop_end, pattern)
+song.set_loop(0, pattern.status().loop_end)
+song.update_playback()
+cbox.Transport.play()
+cbox.JackIO.transport_mode(True, False)
+while not cbox.JackIO.jack_transport_position().is_master:
+ print ("Waiting to become the master")
+ time.sleep(0.01)
+cbox.JackIO.transport_mode(False)
+while cbox.JackIO.jack_transport_position().is_master:
+ print ("Waiting to stop being the master")
+ time.sleep(0.01)
+cbox.JackIO.external_tempo(False)
+assert cbox.JackIO.status().external_tempo == False
+cbox.JackIO.external_tempo(True)
+assert cbox.JackIO.status().external_tempo == True
+
+uuid3 = cbox.JackIO.create_audio_output('noises')
+assert "cbox:noises" in cbox.JackIO.get_ports(".*:noises", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE)
+cbox.JackIO.rename_audio_output(uuid3, "silence")
+router = cbox.JackIO.create_audio_output_router(uuid3, uuid3)
+assert type(router) is cbox.DocRecorder
+try:
+ cbox.JackIO.delete_audio_output(uuid3)
+ assert False
+except Exception as e:
+ assert 'is in use' in str(e)
+router.delete()
+assert "cbox:noises" not in cbox.JackIO.get_ports(".*:noises", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE)
+assert "cbox:silence" in cbox.JackIO.get_ports(".*:silence", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE)
+cbox.JackIO.delete_audio_output(uuid3)
+assert "cbox:silence" not in cbox.JackIO.get_ports(".*:silence", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE)
+
+print("Ready!")
+
+while True:
+ events = cbox.get_new_events()
+ if events:
+ print (events)
+ time.sleep(0.05)
diff --git a/template/calfbox/jack_audio_routing.py b/template/calfbox/jack_audio_routing.py
new file mode 100644
index 0000000..17ebb94
--- /dev/null
+++ b/template/calfbox/jack_audio_routing.py
@@ -0,0 +1,65 @@
+from calfbox import cbox
+import time
+
+def cmd_dumper(cmd, fb, args):
+ print ("%s(%s)" % (cmd, ",".join(list(map(repr,args)))))
+
+cbox.init_engine()
+cbox.Config.set("io", "use_usb", 0)
+cbox.start_audio(cmd_dumper)
+
+global Document
+Document = cbox.Document
+
+status = cbox.JackIO.status()
+client_name = status.client_name
+print ("Client name: %s" % client_name)
+print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs))
+print ("Sample rate: %d frames/sec" % (status.sample_rate))
+print ("JACK period: %d frames" % (status.buffer_size))
+
+inputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SOURCE | cbox.JackIO.PORT_IS_PHYSICAL)
+outputs = cbox.JackIO.get_ports(".*", cbox.JackIO.AUDIO_TYPE, cbox.JackIO.PORT_IS_SINK | cbox.JackIO.PORT_IS_PHYSICAL)
+
+scene = Document.get_scene()
+scene.clear()
+instrument = scene.add_new_instrument_layer("test_sampler", "sampler").get_instrument()
+pgm_no = instrument.engine.get_unused_program()
+pgm = instrument.engine.load_patch_from_file(pgm_no, 'synthbass.sfz', 'SynthBass')
+instrument.engine.set_patch(1, pgm_no)
+instrument.engine.set_patch(10, pgm_no)
+
+print ("Connecting")
+
+uuid = cbox.JackIO.create_audio_output('noises')
+router = cbox.JackIO.create_audio_output_router(uuid, uuid)
+assert type(router) is cbox.DocRecorder
+router2 = cbox.JackIO.create_audio_output_router(uuid, uuid)
+assert type(router2) is cbox.DocRecorder
+instrument.get_output_slot(0).rec_wet.attach(router)
+instrument.get_output_slot(0).rec_wet.attach(router2)
+
+exc = None
+try:
+ instrument.get_output_slot(0).rec_wet.attach(router2)
+except Exception as e:
+ exc = e
+assert "Router already attached" in str(exc)
+
+instrument.get_output_slot(0).rec_wet.detach(router2)
+
+try:
+ instrument.get_output_slot(0).rec_wet.detach(router2)
+except Exception as e:
+ exc = e
+assert "Recorder is not attached" in str(exc)
+
+router.delete()
+
+print("Ready!")
+
+while True:
+ events = cbox.get_new_events()
+ if events:
+ print (events)
+ time.sleep(0.05)
diff --git a/template/calfbox/jack_output_routing.py b/template/calfbox/jack_output_routing.py
new file mode 100644
index 0000000..ba69bc7
--- /dev/null
+++ b/template/calfbox/jack_output_routing.py
@@ -0,0 +1,50 @@
+from calfbox import cbox
+import time
+
+def cmd_dumper(cmd, fb, args):
+ print ("%s(%s)" % (cmd, ",".join(list(map(repr,args)))))
+
+cbox.init_engine()
+
+cbox.Config.set("io", "use_usb", 0)
+cbox.Config.set("io", "midi", "*.*")
+
+cbox.start_audio(cmd_dumper)
+
+global Document
+Document = cbox.Document
+
+status = cbox.JackIO.status()
+client_name = status.client_name
+print ("Client name: %s" % client_name)
+print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs))
+print ("Sample rate: %d frames/sec" % (status.sample_rate))
+print ("JACK period: %d frames" % (status.buffer_size))
+
+left_dry = cbox.JackIO.create_audio_output('left_dry')
+right_dry = cbox.JackIO.create_audio_output('right_dry')
+left_wet = cbox.JackIO.create_audio_output('left_wet', '#1')
+right_wet = cbox.JackIO.create_audio_output('right_wet', '#2')
+router_dry = cbox.JackIO.create_audio_output_router(left_dry, right_dry)
+assert type(router_dry) is cbox.DocRecorder
+router_wet = cbox.JackIO.create_audio_output_router(left_wet, right_wet)
+assert type(router_wet) is cbox.DocRecorder
+
+scene = Document.get_scene()
+scene.clear()
+instrument = scene.add_new_instrument_layer("test_sampler", "tonewheel_organ").get_instrument()
+instrument.get_output_slot(0).set_insert_engine("delay")
+instrument.get_output_slot(0).rec_dry.attach(router_dry)
+instrument.get_output_slot(0).rec_wet.attach(router_wet)
+assert router_dry.uuid == instrument.get_output_slot(0).rec_dry.status().handler[0].uuid
+assert router_wet.uuid == instrument.get_output_slot(0).rec_wet.status().handler[0].uuid
+router_wet.set_gain(-3.0)
+assert router_wet.status().gain == -3
+
+print("Ready!")
+
+while True:
+ events = cbox.get_new_events()
+ if events:
+ print (events)
+ time.sleep(0.05)
diff --git a/template/calfbox/jack_scene_routing.py b/template/calfbox/jack_scene_routing.py
new file mode 100644
index 0000000..0ff0b8d
--- /dev/null
+++ b/template/calfbox/jack_scene_routing.py
@@ -0,0 +1,36 @@
+from calfbox import cbox
+import time
+
+def cmd_dumper(cmd, fb, args):
+ print ("%s(%s)" % (cmd, ",".join(list(map(repr,args)))))
+
+cbox.init_engine()
+
+cbox.Config.set("io", "use_usb", 0)
+cbox.start_audio(cmd_dumper)
+
+global Document
+Document = cbox.Document
+
+status = cbox.JackIO.status()
+client_name = status.client_name
+print ("Client name: %s" % client_name)
+print ("Audio inputs: %d, outputs: %d" % (status.audio_inputs, status.audio_outputs))
+print ("Sample rate: %d frames/sec" % (status.sample_rate))
+print ("JACK period: %d frames" % (status.buffer_size))
+
+uuid_ext1 = cbox.JackIO.create_midi_output('ext1')
+uuid_ext2 = cbox.JackIO.create_midi_output('ext2')
+
+scene = Document.get_scene()
+scene.clear()
+layer = scene.add_new_midi_layer(uuid_ext2)
+#layer.set_external_output(uuid_ext1)
+
+print("Ready!")
+
+while True:
+ events = cbox.get_new_events()
+ if events:
+ print (events)
+ time.sleep(0.05)
diff --git a/template/calfbox/jackinput.c b/template/calfbox/jackinput.c
new file mode 100644
index 0000000..8db3b9c
--- /dev/null
+++ b/template/calfbox/jackinput.c
@@ -0,0 +1,153 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "app.h"
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if USE_JACK
+
+struct jack_input_module
+{
+ struct cbox_module module;
+
+ int inputs[2];
+ int offset;
+};
+
+void jack_input_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct jack_input_module *m = module->user_data;
+}
+
+void jack_input_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct jack_input_module *m = module->user_data;
+
+ for (int i = 0; i < 2; i++)
+ {
+ if (m->inputs[i] < 0)
+ {
+ for (int j = 0; j < CBOX_BLOCK_SIZE; j++)
+ outputs[i][j] = 0;
+ }
+ else
+ {
+ float *src = module->rt->io->input_buffers[m->inputs[i]] + m->offset;
+ for (int j = 0; j < CBOX_BLOCK_SIZE; j++)
+ outputs[i][j] = src[j];
+ }
+ }
+ m->offset = (m->offset + CBOX_BLOCK_SIZE) % app.io.io_env.buffer_size;
+}
+
+static gboolean validate_input_index(int input, const char *cfg_section, const char *type, GError **error)
+{
+ if ((input < 1 || input > (int)app.io.io_env.input_count) && input != -1)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_OUT_OF_RANGE, "%s: invalid value for %s (%d), allowed values are 1..%d or -1 for unconnected", cfg_section, type, input, app.io.io_env.input_count);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static void jack_input_destroyfunc(struct cbox_module *module)
+{
+}
+
+static int to_base1(int val)
+{
+ if (val < 0)
+ return val;
+ return 1 + val;
+}
+
+gboolean jack_input_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct jack_input_module *m = ct->user_data;
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ if (!cbox_execute_on(fb, NULL, "/inputs", "ii", error, to_base1(m->inputs[0]), to_base1(m->inputs[1])))
+ return FALSE;
+ return CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error);
+ }
+ else if (!strcmp(cmd->command, "/inputs") && !strcmp(cmd->arg_types, "ii"))
+ {
+ int left_input = CBOX_ARG_I(cmd, 0);
+ int right_input = CBOX_ARG_I(cmd, 1);
+ if (!validate_input_index(left_input, "script", "left input", error))
+ return FALSE;
+ if (!validate_input_index(right_input, "script", "right input", error))
+ return FALSE;
+ m->inputs[0] = left_input < 0 ? -1 : left_input - 1;
+ m->inputs[1] = right_input < 0 ? -1 : right_input - 1;
+ return TRUE;
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+MODULE_CREATE_FUNCTION(jack_input)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ int left_input = cbox_config_get_int(cfg_section, "left_input", 1);
+ int right_input = cbox_config_get_int(cfg_section, "right_input", 2);
+ if (!validate_input_index(left_input, cfg_section, "left_input", error))
+ return NULL;
+ if (!validate_input_index(right_input, cfg_section, "right_input", error))
+ return NULL;
+
+ struct jack_input_module *m = malloc(sizeof(struct jack_input_module));
+ CALL_MODULE_INIT(m, 0, 2, jack_input);
+ m->module.process_event = jack_input_process_event;
+ m->module.process_block = jack_input_process_block;
+
+ m->inputs[0] = left_input - 1;
+ m->inputs[1] = right_input - 1;
+ m->offset = 0;
+
+ return &m->module;
+}
+
+
+struct cbox_module_keyrange_metadata jack_input_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata jack_input_controllers[] = {
+};
+
+DEFINE_MODULE(jack_input, 0, 2)
+
+#endif
diff --git a/template/calfbox/jackio.c b/template/calfbox/jackio.c
new file mode 100644
index 0000000..4643b9a
--- /dev/null
+++ b/template/calfbox/jackio.c
@@ -0,0 +1,1408 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2012 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+
+#if USE_JACK
+
+#include "app.h"
+#include "config-api.h"
+#include "errors.h"
+#include "hwcfg.h"
+#include "io.h"
+#include "meter.h"
+#include "midi.h"
+#include "mididest.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+struct cbox_jack_io_impl
+{
+ struct cbox_io_impl ioi;
+
+ jack_client_t *client;
+ jack_port_t **inputs;
+ jack_port_t **outputs;
+ jack_port_t *midi;
+ char *error_str; // set to non-NULL if client has been booted out by JACK
+ char *client_name;
+ gboolean enable_common_midi_input;
+ jack_transport_state_t last_transport_state;
+ gboolean debug_transport;
+ gboolean external_tempo;
+ uint32_t master_detect_bits;
+
+ jack_ringbuffer_t *rb_autoconnect;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct cbox_jack_midi_input
+{
+ struct cbox_midi_input hdr;
+ gchar *autoconnect_spec;
+ jack_port_t *port;
+ struct cbox_jack_io_impl *jii;
+};
+
+struct cbox_jack_midi_output
+{
+ struct cbox_midi_output hdr;
+ gchar *autoconnect_spec;
+ jack_port_t *port;
+ struct cbox_jack_io_impl *jii;
+};
+
+struct cbox_jack_audio_output
+{
+ struct cbox_audio_output hdr;
+ gchar *autoconnect_spec;
+ jack_port_t *port;
+ struct cbox_jack_io_impl *jii;
+};
+
+static struct cbox_midi_input *cbox_jackio_create_midi_in(struct cbox_io_impl *impl, const char *name, GError **error);
+static struct cbox_midi_output *cbox_jackio_create_midi_out(struct cbox_io_impl *impl, const char *name, GError **error);
+static struct cbox_audio_output *cbox_jackio_create_audio_out(struct cbox_io_impl *impl, const char *name, GError **error);
+static void cbox_jackio_destroy_midi_in(struct cbox_io_impl *ioi, struct cbox_midi_input *midiin);
+static void cbox_jackio_destroy_midi_out(struct cbox_io_impl *ioi, struct cbox_midi_output *midiout);
+static void cbox_jackio_destroy_audio_out(struct cbox_io_impl *ioi, struct cbox_audio_output *audioout);
+static void cbox_jack_midi_output_set_autoconnect(struct cbox_jack_midi_output *jmo, const gchar *autoconnect_spec);
+static void cbox_jack_audio_output_set_autoconnect(struct cbox_jack_audio_output *jao, const gchar *autoconnect_spec);
+
+static const char *transport_state_names[] = {"Stopped", "Rolling", "Looping?", "Starting", "Unknown/invalid#4", "Unknown/invalid#5", "Unknown/invalid#6" };
+
+void cbox_jack_midi_input_destroy(struct cbox_jack_midi_input *jmi)
+{
+ if (jmi->port)
+ {
+ jack_port_unregister(jmi->jii->client, jmi->port);
+ jmi->port = NULL;
+ }
+ g_free(jmi->hdr.name);
+ g_free(jmi->autoconnect_spec);
+ free(jmi);
+}
+
+void cbox_jack_midi_output_destroy(struct cbox_jack_midi_output *jmo)
+{
+ if (jmo->port)
+ {
+ jack_port_unregister(jmo->jii->client, jmo->port);
+ jmo->port = NULL;
+ }
+ g_free(jmo->hdr.name);
+ g_free(jmo->autoconnect_spec);
+ assert(!jmo->hdr.merger.inputs);
+ free(jmo);
+}
+
+void cbox_jack_audio_output_destroy(struct cbox_jack_audio_output *jao)
+{
+ if (jao->port)
+ {
+ jack_port_unregister(jao->jii->client, jao->port);
+ jao->port = NULL;
+ }
+ g_free(jao->hdr.name);
+ g_free(jao->autoconnect_spec);
+ free(jao);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int copy_midi_data_to_buffer(jack_port_t *port, int buffer_size, struct cbox_midi_buffer *destination)
+{
+ void *midi = jack_port_get_buffer(port, buffer_size);
+ if (!midi)
+ return 0;
+ uint32_t event_count = jack_midi_get_event_count(midi);
+
+ cbox_midi_buffer_clear(destination);
+ for (uint32_t i = 0; i < event_count; i++)
+ {
+ jack_midi_event_t event;
+
+ if (!jack_midi_event_get(&event, midi, i))
+ {
+ if (!cbox_midi_buffer_write_event(destination, event.time, event.buffer, event.size))
+ return -i;
+ }
+ else
+ return -i;
+ }
+
+ return event_count;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int process_cb(jack_nframes_t nframes, void *arg)
+{
+ struct cbox_jack_io_impl *jii = arg;
+ struct cbox_io *io = jii->ioi.pio;
+ struct cbox_io_callbacks *cb = io->cb;
+
+ io->io_env.buffer_size = nframes;
+ for (uint32_t i = 0; i < io->io_env.input_count; i++)
+ io->input_buffers[i] = jack_port_get_buffer(jii->inputs[i], nframes);
+ for (uint32_t i = 0; i < io->io_env.output_count; i++)
+ {
+ io->output_buffers[i] = jack_port_get_buffer(jii->outputs[i], nframes);
+ if (!io->output_buffers[i])
+ continue;
+ for (uint32_t j = 0; j < nframes; j ++)
+ io->output_buffers[i][j] = 0.f;
+ }
+ if (cb->on_transport_sync || (jii->external_tempo && cb->on_tempo_sync)) {
+ jack_position_t pos;
+ memset(&pos, 0, sizeof(pos));
+ jack_transport_state_t state = jack_transport_query(jii->client, &pos);
+ if (jii->external_tempo && cb->on_tempo_sync && (pos.valid & JackPositionBBT) && pos.beats_per_minute > 0) {
+ cb->on_tempo_sync(cb->user_data, pos.beats_per_minute);
+ }
+ if (cb->on_transport_sync)
+ {
+ if (state != jii->last_transport_state)
+ {
+ jack_position_t pos;
+ jack_transport_query(jii->client, &pos);
+ if (jii->debug_transport)
+ g_message("JACK transport: incoming state change, state = %s, last state = %s, pos = %d\n", transport_state_names[state], transport_state_names[(int)jii->last_transport_state], (int)pos.frame);
+ if (state == JackTransportStopped)
+ {
+ if (cb->on_transport_sync(cb->user_data, ts_stopping, pos.frame))
+ jii->last_transport_state = state;
+ }
+ else
+ if (state == JackTransportRolling && jii->last_transport_state == JackTransportStarting)
+ {
+ if (cb->on_transport_sync(cb->user_data, ts_rolling, pos.frame))
+ jii->last_transport_state = state;
+ }
+ else
+ jii->last_transport_state = state;
+ }
+ }
+ }
+ for (GSList *p = io->midi_inputs; p; p = p->next)
+ {
+ struct cbox_jack_midi_input *input = p->data;
+ if (input->hdr.output_set || input->hdr.enable_appsink)
+ {
+ copy_midi_data_to_buffer(input->port, io->io_env.buffer_size, &input->hdr.buffer);
+ if (input->hdr.enable_appsink)
+ cbox_midi_appsink_supply(&input->hdr.appsink, &input->hdr.buffer, io->free_running_frame_counter);
+ }
+ else
+ cbox_midi_buffer_clear(&input->hdr.buffer);
+ }
+ for (GSList *p = io->audio_outputs; p; p = p->next)
+ {
+ struct cbox_jack_audio_output *output = p->data;
+ float *buffer = jack_port_get_buffer(output->port, nframes);
+ output->hdr.buffer = buffer;
+ for (uint32_t j = 0; j < nframes; j ++)
+ buffer[j] = 0.f;
+ }
+ cb->process(cb->user_data, io, nframes);
+ for (uint32_t i = 0; i < io->io_env.input_count; i++)
+ io->input_buffers[i] = NULL;
+ for (uint32_t i = 0; i < io->io_env.output_count; i++)
+ io->output_buffers[i] = NULL;
+ for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
+ {
+ struct cbox_jack_midi_output *midiout = p->data;
+
+ void *pbuf = jack_port_get_buffer(midiout->port, nframes);
+ jack_midi_clear_buffer(pbuf);
+
+ cbox_midi_merger_render(&midiout->hdr.merger);
+ if (midiout->hdr.buffer.count)
+ {
+ uint8_t tmp_data[4];
+ for (uint32_t i = 0; i < midiout->hdr.buffer.count; i++)
+ {
+ const struct cbox_midi_event *event = cbox_midi_buffer_get_event(&midiout->hdr.buffer, i);
+ const uint8_t *pdata = cbox_midi_event_get_data(event);
+ if ((pdata[0] & 0xF0) == 0x90 && !pdata[2] && event->size == 3)
+ {
+ tmp_data[0] = pdata[0] & ~0x10;
+ tmp_data[1] = pdata[1];
+ tmp_data[2] = pdata[2];
+ pdata = tmp_data;
+ }
+ if (jack_midi_event_write(pbuf, event->time, pdata, event->size))
+ {
+ g_warning("MIDI buffer overflow on JACK output port '%s'", midiout->hdr.name);
+ break;
+ }
+ }
+ }
+ }
+ io->free_running_frame_counter += nframes;
+ jii->master_detect_bits <<= 1;
+ return 0;
+}
+
+static void autoconnect_port(jack_client_t *client, const char *port, const char *use_name, int is_cbox_input, const jack_port_t *only_connect_port, struct cbox_command_target *fb)
+{
+ int res;
+ if (only_connect_port)
+ {
+ jack_port_t *right;
+ right = jack_port_by_name(client, use_name);
+ if (only_connect_port != right)
+ return;
+ }
+
+ const char *pfrom = is_cbox_input ? use_name : port;
+ const char *pto = !is_cbox_input ? use_name : port;
+
+ res = jack_connect(client, pfrom, pto);
+ if (res == EEXIST)
+ res = 0;
+ gboolean suppressed = FALSE;
+ if (fb)
+ {
+ if (!res)
+ suppressed = cbox_execute_on(fb, NULL, "/io/jack/connected", "ss", NULL, pfrom, pto);
+ else
+ suppressed = cbox_execute_on(fb, NULL, "/io/jack/connect_failed", "sss", NULL, pfrom, pto, (res == EEXIST ? "already connected" : "failed"));
+ }
+ if (!suppressed)
+ g_message("Connect: %s %s %s (%s)", port, is_cbox_input ? "<-" : "->", use_name, res == 0 ? "success" : (res == EEXIST ? "already connected" : "failed"));
+}
+
+static void autoconnect_by_spec(jack_client_t *client, const char *port, const char *orig_spec, int is_cbox_input, int is_midi, const jack_port_t *only_connect_port, struct cbox_command_target *fb)
+{
+ char *name, *copy_spec, *dpos;
+ const char *use_name;
+
+ copy_spec = g_strdup(orig_spec);
+ name = copy_spec;
+ do {
+ dpos = strchr(name, ';');
+ if (dpos)
+ *dpos = '\0';
+
+ use_name = name;
+ if (use_name[0] == '#')
+ {
+ char *endptr = NULL;
+ long portidx = strtol(use_name + 1, &endptr, 10) - 1;
+ if (endptr == use_name + strlen(use_name))
+ {
+ const char **names = jack_get_ports(client, ".*", is_midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE, is_cbox_input ? JackPortIsOutput : JackPortIsInput);
+ // Client killed by JACK
+ if (!names) {
+ g_free(copy_spec);
+ return;
+ }
+ int i;
+ for (i = 0; i < portidx && names[i]; i++)
+ ;
+
+ if (names[i])
+ autoconnect_port(client, port, names[i], is_cbox_input, only_connect_port, fb);
+ else
+ g_message("Connect: unmatched port index %d for autoconnecting %s", (int)portidx, port);
+
+ jack_free(names);
+ }
+ }
+ else if (use_name[0] == '~' || use_name[0] == '*')
+ {
+ const char **names = jack_get_ports(client, use_name + 1, is_midi ? JACK_DEFAULT_MIDI_TYPE : JACK_DEFAULT_AUDIO_TYPE, is_cbox_input ? JackPortIsOutput : JackPortIsInput);
+ // Client killed by JACK
+ if (!names) {
+ g_free(copy_spec);
+ return;
+ }
+
+ if (names && names[0])
+ {
+ if (use_name[0] == '*')
+ {
+ int i;
+ for (i = 0; names[i]; i++)
+ autoconnect_port(client, port, names[i], is_cbox_input, only_connect_port, fb);
+ }
+ else
+ autoconnect_port(client, port, names[0], is_cbox_input, only_connect_port, fb);
+ }
+ else
+ g_message("Connect: unmatched port regexp %s", use_name);
+ jack_free(names);
+ }
+ else
+ autoconnect_port(client, port, use_name, is_cbox_input, only_connect_port, fb);
+
+ if (dpos)
+ name = dpos + 1;
+ } while(dpos);
+ g_free(copy_spec);
+}
+
+static void autoconnect_by_var(jack_client_t *client, const char *port, const char *config_var, int is_cbox_input, int is_midi, const jack_port_t *only_connect_port, struct cbox_command_target *fb)
+{
+ const char *orig_spec = cbox_config_get_string(cbox_io_section, config_var);
+ if (orig_spec)
+ autoconnect_by_spec(client, port, orig_spec, is_cbox_input, is_midi, only_connect_port, fb);
+}
+
+static void port_connect_cb(jack_port_id_t port, int registered, void *arg)
+{
+ struct cbox_jack_io_impl *jii = arg;
+ if (registered)
+ {
+ jack_port_t *portobj = jack_port_by_id(jii->client, port);
+
+ jack_ringbuffer_write(jii->rb_autoconnect, (char *)&portobj, sizeof(portobj));
+ }
+}
+
+static void port_autoconnect(struct cbox_jack_io_impl *jii, jack_port_t *portobj, struct cbox_command_target *fb)
+{
+ struct cbox_io *io = jii->ioi.pio;
+
+ for (uint32_t i = 0; i < io->io_env.output_count; i++)
+ {
+ gchar *cbox_port = g_strdup_printf("%s:out_%d", jii->client_name, 1 + i);
+ gchar *config_key = g_strdup_printf("out_%d", 1 + i);
+ autoconnect_by_var(jii->client, cbox_port, config_key, 0, 0, portobj, fb);
+ g_free(cbox_port);
+ g_free(config_key);
+ }
+ for (uint32_t i = 0; i < io->io_env.input_count; i++)
+ {
+ gchar *cbox_port = g_strdup_printf("%s:in_%d", jii->client_name, 1 + i);
+ gchar *config_key = g_strdup_printf("in_%d", 1 + i);
+ autoconnect_by_var(jii->client, cbox_port, config_key, 1, 0, portobj, fb);
+ g_free(cbox_port);
+ g_free(config_key);
+ }
+ for (GSList *p = io->midi_outputs; p; p = g_slist_next(p))
+ {
+ struct cbox_jack_midi_output *midiout = p->data;
+ if (midiout->autoconnect_spec)
+ {
+ gchar *cbox_port = g_strdup_printf("%s:%s", jii->client_name, midiout->hdr.name);
+ autoconnect_by_spec(jii->client, cbox_port, midiout->autoconnect_spec, FALSE, TRUE, portobj, fb);
+ g_free(cbox_port);
+ }
+ }
+ for (GSList *p = io->midi_inputs; p; p = g_slist_next(p))
+ {
+ struct cbox_jack_midi_input *midiin = p->data;
+ if (midiin->autoconnect_spec)
+ {
+ gchar *cbox_port = g_strdup_printf("%s:%s", jii->client_name, midiin->hdr.name);
+ autoconnect_by_spec(jii->client, cbox_port, midiin->autoconnect_spec, TRUE, TRUE, portobj, fb);
+ g_free(cbox_port);
+ }
+ }
+ for (GSList *p = io->audio_outputs; p; p = g_slist_next(p))
+ {
+ struct cbox_jack_audio_output *audioout = p->data;
+ if (audioout->autoconnect_spec)
+ {
+ gchar *cbox_port = g_strdup_printf("%s:%s", jii->client_name, audioout->hdr.name);
+ autoconnect_by_spec(jii->client, cbox_port, audioout->autoconnect_spec, FALSE, FALSE, portobj, fb);
+ g_free(cbox_port);
+ }
+ }
+ gchar *cbox_port = g_strdup_printf("%s:midi", jii->client_name);
+ autoconnect_by_var(jii->client, cbox_port, "midi", 1, 1, portobj, fb);
+ g_free(cbox_port);
+}
+
+int cbox_jackio_get_sample_rate(struct cbox_io_impl *impl)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+
+ return jack_get_sample_rate(jii->client);
+}
+
+gboolean cbox_jackio_get_status(struct cbox_io_impl *impl, GError **error)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+ if (!jii->error_str)
+ return TRUE;
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s", jii->error_str);
+ return FALSE;
+}
+
+static void client_shutdown_cb(jack_status_t code, const char *reason, void *arg)
+{
+ struct cbox_jack_io_impl *jii = arg;
+ struct cbox_io *io = jii->ioi.pio;
+ jii->error_str = g_strdup(reason);
+ if (io->cb && io->cb->on_disconnected)
+ (io->cb->on_disconnected)(io->cb->user_data);
+}
+
+static void timebase_cb(jack_transport_state_t state, jack_nframes_t nframes,
+ jack_position_t *pos, int new_pos, void *arg)
+{
+ struct cbox_jack_io_impl *jii = arg;
+ struct cbox_io *io = jii->ioi.pio;
+
+ jii->master_detect_bits |= 1;
+ if (io->cb->get_transport_data) {
+ struct cbox_transport_position tpos;
+
+ io->cb->get_transport_data(io->cb->user_data, new_pos, pos->frame, &tpos);
+ pos->valid = JackPositionBBT;
+ pos->bar = tpos.bar;
+ pos->beat = tpos.beat;
+ pos->tick = tpos.tick;
+ pos->bar_start_tick = tpos.bar_start_tick;
+ pos->ticks_per_beat = tpos.ticks_per_beat;
+ pos->beats_per_minute = tpos.tempo;
+ pos->beats_per_bar = tpos.timesig_num;
+ pos->beat_type = tpos.timesig_denom;
+ }
+}
+
+static int sync_cb(jack_transport_state_t state, jack_position_t *pos, void *arg)
+{
+ struct cbox_jack_io_impl *jii = arg;
+ struct cbox_io *io = jii->ioi.pio;
+ gboolean result = TRUE;
+ int last_state = jii->last_transport_state;
+ switch(state)
+ {
+ case JackTransportStopped:
+ result = io->cb->on_transport_sync(io->cb->user_data, ts_stopped, pos->frame);
+ break;
+ case JackTransportStarting:
+ jii->last_transport_state = JackTransportStarting;
+ result = io->cb->on_transport_sync(io->cb->user_data, ts_starting, pos->frame);
+ break;
+ case JackTransportRolling:
+ result = io->cb->on_transport_sync(io->cb->user_data, ts_rolling, pos->frame);
+ break;
+ default:
+ // assume the client is ready
+ result = TRUE;
+ }
+ if (jii->debug_transport)
+ g_message("JACK transport: incoming sync callback, state = %s, last state = %s, pos = %d, result = %d\n", transport_state_names[state], transport_state_names[last_state], (int)pos->frame, result);
+ return result;
+}
+
+gboolean cbox_jackio_set_master_mode(struct cbox_jack_io_impl *jii, int mode, GError **error)
+{
+ if (jii->ioi.pio->cb->get_transport_data)
+ {
+ if (mode) {
+ switch(jack_set_timebase_callback(jii->client, mode == 1, timebase_cb, jii))
+ {
+ case 0:
+ return TRUE;
+ case EBUSY:
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Timebase master already set");
+ return FALSE;
+ default:
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Timebase master could not be set");
+ return FALSE;
+
+ }
+ } else {
+ switch(jack_release_timebase(jii->client))
+ {
+ case 0:
+ return TRUE;
+ case EINVAL:
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Not a current timebase master");
+ return FALSE;
+ default:
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Timebase master could not be unset");
+ return FALSE;
+ }
+ }
+ }
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Transport not supported by engine");
+ return FALSE;
+}
+
+gboolean cbox_jackio_start(struct cbox_io_impl *impl, struct cbox_command_target *fb, GError **error)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+ struct cbox_io *io = jii->ioi.pio;
+
+ if (io->cb->on_transport_sync)
+ jack_set_sync_callback(jii->client, sync_cb, jii);
+ jack_set_process_callback(jii->client, process_cb, jii);
+ jack_set_port_registration_callback(jii->client, port_connect_cb, jii);
+ jack_on_info_shutdown(jii->client, client_shutdown_cb, jii);
+
+ if (io->cb->on_started)
+ io->cb->on_started(io->cb->user_data);
+
+ jack_activate(jii->client);
+
+ if (cbox_config_has_section(cbox_io_section))
+ port_autoconnect(jii, NULL, fb);
+
+ return TRUE;
+}
+
+gboolean cbox_jackio_stop(struct cbox_io_impl *impl, GError **error)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+
+ if (jii->error_str)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "%s", jii->error_str);
+ return FALSE;
+ }
+ int result = jack_deactivate(jii->client);
+ if (result)
+ g_warning("jack_deactivate has failed, result = %d", result);
+ jack_release_timebase(jii->client);
+ jack_set_process_callback(jii->client, NULL, 0);
+ return TRUE;
+}
+
+void cbox_jackio_poll_ports(struct cbox_io_impl *impl, struct cbox_command_target *fb)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+
+ while (jack_ringbuffer_read_space(jii->rb_autoconnect) >= sizeof(jack_port_t *))
+ {
+ jack_port_t *portobj;
+ jack_ringbuffer_read(jii->rb_autoconnect, (char *)&portobj, sizeof(portobj));
+ port_autoconnect(jii, portobj, fb);
+ }
+}
+
+int cbox_jackio_get_midi_data(struct cbox_io_impl *impl, struct cbox_midi_buffer *destination)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+ if (!jii->enable_common_midi_input)
+ {
+ cbox_midi_buffer_clear(destination);
+ return 1;
+ }
+
+ return copy_midi_data_to_buffer(jii->midi, jii->ioi.pio->io_env.buffer_size, destination);
+}
+
+void cbox_jackio_destroy(struct cbox_io_impl *impl)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+ struct cbox_io *io = impl->pio;
+ if (jii->client)
+ {
+ if (jii->error_str)
+ {
+ g_free(jii->error_str);
+ jii->error_str = NULL;
+ }
+ else
+ {
+ for (uint32_t i = 0; i < io->io_env.input_count; i++)
+ jack_port_unregister(jii->client, jii->inputs[i]);
+ free(jii->inputs);
+ for (uint32_t i = 0; i < io->io_env.output_count; i++)
+ jack_port_unregister(jii->client, jii->outputs[i]);
+ free(jii->outputs);
+ if (jii->midi)
+ jack_port_unregister(jii->client, jii->midi);
+ }
+ if (jii->client_name)
+ {
+ free(jii->client_name);
+ jii->client_name = NULL;
+ }
+ cbox_io_destroy_all_midi_ports(io);
+
+ jack_ringbuffer_free(jii->rb_autoconnect);
+ jack_client_close(jii->client);
+ }
+ free(jii);
+}
+
+gboolean cbox_jackio_cycle(struct cbox_io_impl *impl, struct cbox_command_target *fb, GError **error)
+{
+ struct cbox_io *io = impl->pio;
+ struct cbox_io_callbacks *cb = io->cb;
+ cbox_io_close(io);
+
+ // XXXKF use params structure some day
+ if (!cbox_io_init_jack(io, NULL, fb, error))
+ return FALSE;
+
+ cbox_io_start(io, cb, fb);
+ if (cb->on_reconnected)
+ (cb->on_reconnected)(cb->user_data);
+ return TRUE;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct cbox_midi_input *cbox_jackio_create_midi_in(struct cbox_io_impl *impl, const char *name, GError **error)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+ jack_port_t *port = jack_port_register(jii->client, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
+ if (!port)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create input MIDI port '%s'", name);
+ return FALSE;
+ }
+ struct cbox_jack_midi_input *input = calloc(1, sizeof(struct cbox_jack_midi_input));
+ input->hdr.name = g_strdup(name);
+ input->hdr.removing = FALSE;
+ input->port = port;
+ input->jii = jii;
+ cbox_uuid_generate(&input->hdr.uuid);
+ cbox_midi_buffer_init(&input->hdr.buffer);
+
+ return (struct cbox_midi_input *)input;
+}
+
+struct cbox_midi_output *cbox_jackio_create_midi_out(struct cbox_io_impl *impl, const char *name, GError **error)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+ jack_port_t *port = jack_port_register(jii->client, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
+ if (!port)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create output MIDI port '%s'", name);
+ return FALSE;
+ }
+ struct cbox_jack_midi_output *output = calloc(1, sizeof(struct cbox_jack_midi_output));
+ output->hdr.name = g_strdup(name);
+ output->hdr.removing = FALSE;
+ output->port = port;
+ output->jii = jii;
+ cbox_uuid_generate(&output->hdr.uuid);
+ cbox_midi_buffer_init(&output->hdr.buffer);
+ cbox_midi_merger_init(&output->hdr.merger, &output->hdr.buffer);
+
+ return (struct cbox_midi_output *)output;
+}
+
+struct cbox_audio_output *cbox_jackio_create_audio_out(struct cbox_io_impl *impl, const char *name, GError **error)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+ jack_port_t *port = jack_port_register(jii->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ if (!port)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create output audio port '%s'", name);
+ return FALSE;
+ }
+ struct cbox_jack_audio_output *output = calloc(1, sizeof(struct cbox_jack_audio_output));
+ output->hdr.name = g_strdup(name);
+ output->hdr.removing = FALSE;
+ output->hdr.users = 0;
+ output->port = port;
+ output->jii = jii;
+ cbox_uuid_generate(&output->hdr.uuid);
+
+ return (struct cbox_audio_output *)output;
+}
+
+void cbox_jack_port_set_autoconnect(gchar **spec_ptr, const gchar *autoconnect_spec, struct cbox_jack_io_impl *jii, const gchar *port_name, gboolean is_cbox_input, gboolean is_midi)
+{
+ if (*spec_ptr)
+ g_free(*spec_ptr);
+ *spec_ptr = autoconnect_spec && *autoconnect_spec ? g_strdup(autoconnect_spec) : NULL;
+ if (*spec_ptr)
+ {
+ gchar *cbox_port = g_strdup_printf("%s:%s", jii->client_name, port_name);
+ autoconnect_by_spec(jii->client, cbox_port, *spec_ptr, is_cbox_input, is_midi, NULL, NULL);
+ g_free(cbox_port);
+ }
+}
+
+void cbox_jack_midi_input_set_autoconnect(struct cbox_jack_midi_input *jmi, const gchar *autoconnect_spec)
+{
+ cbox_jack_port_set_autoconnect(&jmi->autoconnect_spec, autoconnect_spec, jmi->jii, jmi->hdr.name, TRUE, TRUE);
+}
+
+void cbox_jack_midi_output_set_autoconnect(struct cbox_jack_midi_output *jmo, const gchar *autoconnect_spec)
+{
+ cbox_jack_port_set_autoconnect(&jmo->autoconnect_spec, autoconnect_spec, jmo->jii, jmo->hdr.name, FALSE, TRUE);
+}
+
+void cbox_jack_audio_output_set_autoconnect(struct cbox_jack_audio_output *jao, const gchar *autoconnect_spec)
+{
+ cbox_jack_port_set_autoconnect(&jao->autoconnect_spec, autoconnect_spec, jao->jii, jao->hdr.name, FALSE, FALSE);
+}
+
+void cbox_jackio_destroy_midi_in(struct cbox_io_impl *ioi, struct cbox_midi_input *midiin)
+{
+ cbox_jack_midi_input_destroy((struct cbox_jack_midi_input *)midiin);
+}
+
+void cbox_jackio_destroy_midi_out(struct cbox_io_impl *ioi, struct cbox_midi_output *midiout)
+{
+ cbox_jack_midi_output_destroy((struct cbox_jack_midi_output *)midiout);
+}
+
+void cbox_jackio_destroy_audio_out(struct cbox_io_impl *ioi, struct cbox_audio_output *audioout)
+{
+ cbox_jack_audio_output_destroy((struct cbox_jack_audio_output *)audioout);
+}
+
+#if JACK_HAS_RENAME
+#define jack_port_rename_fn jack_port_rename
+#else
+#define jack_port_rename_fn(client, handle, name) jack_port_set_name(handle, name)
+#endif
+
+
+static gboolean cbox_jack_io_get_jack_uuid_from_name(struct cbox_jack_io_impl *jii, const char *name, jack_uuid_t *uuid, GError **error)
+{
+ jack_port_t *port = NULL;
+ port = jack_port_by_name(jii->client, name);
+ if (!port)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", name);
+ return FALSE;
+ }
+ assert(uuid);
+ jack_uuid_t temp_uuid = jack_port_uuid(port);
+ if (!temp_uuid)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "JACK uuid for port '%s' not found", name);
+ return FALSE;
+ }
+ *uuid = temp_uuid;
+ return TRUE;
+}
+
+static gboolean cbox_jack_io_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)ct->user_data;
+ struct cbox_io *io = jii->ioi.pio;
+ gboolean handled = FALSE;
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ return cbox_execute_on(fb, NULL, "/client_type", "s", error, "JACK") &&
+ cbox_execute_on(fb, NULL, "/client_name", "s", error, jii->client_name) &&
+ cbox_execute_on(fb, NULL, "/external_tempo", "i", error, jii->external_tempo) &&
+ cbox_io_process_cmd(io, fb, cmd, error, &handled);
+ }
+ else if (!strcmp(cmd->command, "/jack_transport_position") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ jack_position_t pos;
+ memset(&pos, 0, sizeof(pos));
+ jack_transport_state_t state = jack_transport_query(jii->client, &pos);
+ if (!(cbox_execute_on(fb, NULL, "/state", "i", error, (int)state) &&
+ cbox_execute_on(fb, NULL, "/is_master", "i", error, jii->master_detect_bits & 2 ? 1 : 0) &&
+ cbox_execute_on(fb, NULL, "/unique_lo", "i", error, (int)pos.unique_1) &&
+ cbox_execute_on(fb, NULL, "/unique_hi", "i", error, (int)(pos.unique_1 >> 32)) &&
+ cbox_execute_on(fb, NULL, "/usecs_lo", "i", error, (int)pos.usecs) &&
+ cbox_execute_on(fb, NULL, "/usecs_hi", "i", error, (int)(pos.usecs >> 32)) &&
+ cbox_execute_on(fb, NULL, "/frame_rate", "i", error, (int)(pos.frame_rate)) &&
+ cbox_execute_on(fb, NULL, "/frame", "i", error, (int)(pos.frame))))
+ return FALSE;
+ if ((pos.valid & JackPositionBBT) && !(
+ cbox_execute_on(fb, NULL, "/bar", "i", error, (int)pos.bar) &&
+ cbox_execute_on(fb, NULL, "/beat", "i", error, (int)pos.beat) &&
+ cbox_execute_on(fb, NULL, "/tick", "i", error, (int)pos.tick) &&
+ cbox_execute_on(fb, NULL, "/bar_start_tick", "f", error, (int)pos.bar_start_tick) &&
+ cbox_execute_on(fb, NULL, "/beats_per_bar", "f", error, (double)pos.beats_per_bar) &&
+ cbox_execute_on(fb, NULL, "/beat_type", "f", error, (double)pos.beat_type) &&
+ cbox_execute_on(fb, NULL, "/ticks_per_beat", "f", error, (double)pos.ticks_per_beat) &&
+ cbox_execute_on(fb, NULL, "/beats_per_minute", "f", error, (double)pos.beats_per_minute)))
+ return FALSE;
+ if ((pos.valid & JackBBTFrameOffset) && !(
+ cbox_execute_on(fb, NULL, "/bbt_frame_offset", "i", error, (int)pos.bbt_offset)))
+ return FALSE;
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/transport_mode") && !strcmp(cmd->arg_types, "i"))
+ {
+ return cbox_jackio_set_master_mode(jii, CBOX_ARG_I(cmd, 0), error);
+ }
+ else if (!strcmp(cmd->command, "/jack_transport_locate") && !strcmp(cmd->arg_types, "i"))
+ {
+ jack_transport_locate(jii->client, (uint32_t)CBOX_ARG_I(cmd, 0));
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/rename_midi_port") && !strcmp(cmd->arg_types, "ss"))
+ {
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ const char *new_name = CBOX_ARG_S(cmd, 1);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
+ struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, NULL, &uuid);
+ if (!midiout && !midiin)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ jack_port_t *port = midiout ? ((struct cbox_jack_midi_output *)midiout)->port
+ : ((struct cbox_jack_midi_input *)midiin)->port;
+ char **pname = midiout ? &midiout->name : &midiin->name;
+ if (0 != jack_port_rename_fn(jii->client, port, new_name))
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot set port name to '%s'", new_name);
+ return FALSE;
+ }
+ g_free(*pname);
+ *pname = g_strdup(new_name);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/rename_audio_port") && !strcmp(cmd->arg_types, "ss"))
+ {
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ const char *new_name = CBOX_ARG_S(cmd, 1);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, NULL, &uuid);
+ if (!audioout)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ if (0 != jack_port_rename_fn(jii->client, ((struct cbox_jack_audio_output *)audioout)->port, new_name))
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot set port name to '%s'", new_name);
+ return FALSE;
+ }
+ g_free(audioout->name);
+ audioout->name = g_strdup(new_name);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/autoconnect") && !strcmp(cmd->arg_types, "ss"))
+ {
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ const char *spec = CBOX_ARG_S(cmd, 1);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, NULL, &uuid);
+ if (midiout)
+ {
+ cbox_jack_midi_output_set_autoconnect((struct cbox_jack_midi_output *)midiout, spec);
+ return TRUE;
+ }
+ struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
+ if (midiin)
+ {
+ cbox_jack_midi_input_set_autoconnect((struct cbox_jack_midi_input *)midiin, spec);
+ return TRUE;
+ }
+ struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, NULL, &uuid);
+ if (audioout)
+ {
+ cbox_jack_audio_output_set_autoconnect((struct cbox_jack_audio_output *)audioout, spec);
+ return TRUE;
+ }
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ else if (!strcmp(cmd->command, "/disconnect_audio_output") && !strcmp(cmd->arg_types, "s"))
+ {
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_audio_output *audioout = cbox_io_get_audio_output(io, NULL, &uuid);
+ if (!audioout)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ jack_port_disconnect(jii->client, ((struct cbox_jack_audio_output *)audioout)->port);
+ return TRUE;
+ }
+ else if (!strncmp(cmd->command, "/disconnect_midi_", 17) && !strcmp(cmd->arg_types, "s"))
+ {
+ bool is_both = !strcmp(cmd->command + 17, "port");
+ bool is_in = is_both || !strcmp(cmd->command + 17, "input");
+ bool is_out = is_both || !strcmp(cmd->command + 17, "output");
+ if (is_in || is_out) {
+ const char *uuidstr = CBOX_ARG_S(cmd, 0);
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, uuidstr, error))
+ return FALSE;
+ struct cbox_midi_input *midiin = is_in ? cbox_io_get_midi_input(io, NULL, &uuid) : NULL;
+ struct cbox_midi_output *midiout = is_out ? cbox_io_get_midi_output(io, NULL, &uuid) : NULL;
+ if (!midiout && !midiin)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", uuidstr);
+ return FALSE;
+ }
+ jack_port_t *port = midiout ? ((struct cbox_jack_midi_output *)midiout)->port
+ : ((struct cbox_jack_midi_input *)midiin)->port;
+ jack_port_disconnect(jii->client, port);
+ return TRUE;
+ }
+ }
+
+ if (!strcmp(cmd->command, "/port_connect") && !strcmp(cmd->arg_types, "ss"))
+ {
+ const char *port_from = CBOX_ARG_S(cmd, 0);
+ const char *port_to = CBOX_ARG_S(cmd, 1);
+ int res = jack_connect(jii->client, port_from, port_to);
+ if (res == EEXIST)
+ res = 0;
+ if (res)
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot connect port '%s' to '%s'", port_from, port_to);
+ return res == 0;
+ }
+ else if (!strcmp(cmd->command, "/port_disconnect") && !strcmp(cmd->arg_types, "ss"))
+ {
+ const char *port_from = CBOX_ARG_S(cmd, 0);
+ const char *port_to = CBOX_ARG_S(cmd, 1);
+ int res = jack_disconnect(jii->client, port_from, port_to);
+ if (res)
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot disconnect port '%s' from '%s'", port_from, port_to);
+ return res == 0;
+ }
+ else if (!strcmp(cmd->command, "/get_connected_ports") && !strcmp(cmd->arg_types, "s"))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ const char *name = CBOX_ARG_S(cmd, 0);
+ jack_port_t *port = NULL;
+ if (!strchr(name, ':')) {
+ // try UUID
+ struct cbox_uuid uuid;
+ if (!cbox_uuid_fromstring(&uuid, name, error))
+ return FALSE;
+ struct cbox_midi_output *midiout = cbox_io_get_midi_output(io, NULL, &uuid);
+ if (midiout)
+ port = ((struct cbox_jack_midi_output *)midiout)->port;
+ else {
+ struct cbox_midi_input *midiin = cbox_io_get_midi_input(io, NULL, &uuid);
+ if (midiin)
+ port = ((struct cbox_jack_midi_input *)midiin)->port;
+ }
+ }
+ else
+ port = jack_port_by_name(jii->client, name);
+ if (!port)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' not found", name);
+ return FALSE;
+ }
+ const char** ports = jack_port_get_all_connections(jii->client, port);
+ for (int i = 0; ports && ports[i]; i++)
+ {
+ if (!cbox_execute_on(fb, NULL, "/port", "s", error, ports[i]))
+ return FALSE;
+ }
+ jack_free(ports);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/get_ports") && !strcmp(cmd->arg_types, "ssi"))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ const char *mask = CBOX_ARG_S(cmd, 0);
+ const char *type = CBOX_ARG_S(cmd, 1);
+ uint32_t flags = CBOX_ARG_I(cmd, 2);
+ const char** ports = jack_get_ports(jii->client, mask, type, flags);
+ for (int i = 0; ports && ports[i]; i++)
+ {
+ if (!cbox_execute_on(fb, NULL, "/port", "s", error, ports[i]))
+ return FALSE;
+ }
+ jack_free(ports);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/external_tempo") && !strcmp(cmd->arg_types, "i"))
+ {
+ jii->external_tempo = CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+
+ //Metadata
+
+ else if (!strcmp(cmd->command, "/set_property") && !strcmp(cmd->arg_types, "ssss"))
+ //parameters: "client:port", key, value, type according to jack_property_t (empty or NULL for string)
+ {
+ const char *name = CBOX_ARG_S(cmd, 0);
+ const char *key = CBOX_ARG_S(cmd, 1);
+ const char *value = CBOX_ARG_S(cmd, 2);
+ const char *type = CBOX_ARG_S(cmd, 3);
+
+ jack_uuid_t subject;
+ if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside
+ return FALSE;
+
+ if (jack_set_property(jii->client, subject, key, value, type)) // 0 on success
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Set property key:'%s' value: '%s' to port '%s' was not successful", key, value, name);
+ return FALSE;
+ }
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/get_property") && !strcmp(cmd->arg_types, "ss"))
+ //parameters: "client:port", key
+ //returns python key, value and type as strings
+ {
+ const char *name = CBOX_ARG_S(cmd, 0);
+ const char *key = CBOX_ARG_S(cmd, 1);
+
+ jack_uuid_t subject;
+ if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside
+ return FALSE;
+
+ char* value = NULL;
+ char* type = NULL;
+
+ if (jack_get_property(subject, key, &value, &type)) // 0 on success, -1 if the subject has no key property.
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' does not have key '%s'", name, key);
+ return FALSE;
+ }
+
+ char* returntype; //We need to call jack_free on type in any case so it can't be our own data.
+ if (type == NULL)
+ returntype = "";
+ else
+ returntype = type;
+
+ if (!cbox_execute_on(fb, NULL, "/value", "ss", error, value, returntype)) //send return values to Python.
+ return FALSE;
+
+ jack_free(value);
+ jack_free(type);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/get_properties") && !strcmp(cmd->arg_types, "s"))
+ //parameters: "client:port"
+ {
+ const char *name = CBOX_ARG_S(cmd, 0);
+
+ jack_uuid_t subject;
+ if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside
+ return FALSE;
+
+ jack_description_t desc;
+ if (!jack_get_properties(subject, &desc)) // 0 on success, -1 if no subject with any properties exists.
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Port '%s' with uuid '%lli' does not have any properties", name, (long long)subject);
+ return FALSE;
+ }
+
+ const char *returntype;
+ for (uint32_t i = 0; icommand, "/get_all_properties") && !strcmp(cmd->arg_types, ""))
+ {
+ jack_description_t *descs;
+ int counter;
+ counter = jack_get_all_properties(&descs);
+ const char *returntype;
+ for (int j = 0; j < counter; j++)
+ {
+ jack_description_t *one_desc = &descs[j];
+ for (uint32_t i = 0; i < one_desc->property_cnt ; i++)
+ {
+ if (one_desc->properties[i].type == NULL)
+ returntype = "";
+ else
+ returntype = one_desc->properties[i].type;
+
+ /*
+ index = jack_uuid_to_index(one_desc->subject)
+ portid = jack_port_by_id(jii->client, index);
+ portname = jack_port_name(port);
+ */
+ if (!cbox_execute_on(fb, NULL, "/all_properties", "ssss",
+ error,
+ jack_port_name(jack_port_by_id(jii->client, jack_uuid_to_index(one_desc->subject))),
+ one_desc->properties[i].key,
+ one_desc->properties[i].data,
+ returntype))
+ return FALSE;
+ }
+ jack_free_description(one_desc, 0); //if non-zero one_desc will also be passed to free()
+ }
+ jack_free(descs);
+ return TRUE;
+ }
+
+
+ else if (!strcmp(cmd->command, "/remove_property") && !strcmp(cmd->arg_types, "ss"))
+ {
+ const char *name = CBOX_ARG_S(cmd, 0);
+ const char *key = CBOX_ARG_S(cmd, 1);
+
+ jack_uuid_t subject;
+ if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside
+ return FALSE;
+
+ if (jack_remove_property(jii->client, subject, key)) // 0 on success, -1 otherwise
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Could not remove port '%s' key '%s'", name, key);
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ else if (!strcmp(cmd->command, "/remove_properties") && !strcmp(cmd->arg_types, "s"))
+ {
+ const char *name = CBOX_ARG_S(cmd, 0);
+
+ jack_uuid_t subject;
+ if (!cbox_jack_io_get_jack_uuid_from_name(jii, name, &subject, error)) //error message set inside
+ return FALSE;
+
+ if (jack_remove_properties(jii->client, subject) == -1) // number of removed properties returned, -1 on error
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Could not remove properties of port '%s'", name);
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+
+ else if (!strcmp(cmd->command, "/remove_all_properties") && !strcmp(cmd->arg_types, ""))
+ {
+ if (jack_remove_all_properties(jii->client)) // 0 on success, -1 otherwise
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Remove all JACK properties was not successful");
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ else
+ {
+ gboolean result = cbox_io_process_cmd(io, fb, cmd, error, &handled);
+ if (!handled)
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Unknown combination of target path and argument: '%s', '%s'", cmd->command, cmd->arg_types);
+ return result;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void cbox_jackio_control_transport(struct cbox_io_impl *impl, gboolean roll, uint32_t pos)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+
+ if (jii->debug_transport)
+ g_message("JACK transport: control(op=%s, pos=%d)\n", roll ? "roll" : "stop", (int)pos);
+
+ jack_transport_state_t state = jack_transport_query(jii->client, NULL);
+ if (roll && pos != (uint32_t)-1)
+ jack_transport_locate(jii->client, pos);
+ if (roll && state == JackTransportStopped)
+ jack_transport_start(jii->client);
+ if (!roll && state != JackTransportStopped)
+ jack_transport_stop(jii->client);
+ if (!roll && pos != (uint32_t)-1)
+ jack_transport_locate(jii->client, pos);
+}
+
+static gboolean cbox_jackio_get_sync_completed(struct cbox_io_impl *impl)
+{
+ struct cbox_jack_io_impl *jii = (struct cbox_jack_io_impl *)impl;
+ return jack_transport_query(jii->client, NULL) != JackTransportStarting;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void cbox_jackio_update_midi_in_routing(struct cbox_io_impl *impl)
+{
+ // XXXKF slow and wasteful, but that's okay for now
+ for (GSList *p = impl->pio->midi_inputs; p; p = g_slist_next(p))
+ {
+ struct cbox_midi_input *midiin = p->data;
+ for (GSList *q = impl->pio->midi_outputs; q; q = g_slist_next(q))
+ {
+ struct cbox_midi_output *midiout = q->data;
+
+ bool add = midiin->output_set && !midiout->removing && cbox_uuid_equal(&midiout->uuid, &midiin->output);
+ if (add)
+ cbox_midi_merger_connect(&midiout->merger, &midiin->buffer, app.rt, NULL);
+ else
+ cbox_midi_merger_disconnect(&midiout->merger, &midiin->buffer, app.rt);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+gboolean cbox_io_init_jack(struct cbox_io *io, struct cbox_open_params *const params, struct cbox_command_target *fb, GError **error)
+{
+ const char *client_name = cbox_config_get_string_with_default("io", "client_name", "cbox");
+
+ jack_client_t *client = NULL;
+ jack_status_t status = 0;
+ client = jack_client_open(client_name, JackNoStartServer, &status);
+ if (client == NULL)
+ {
+ if (!cbox_hwcfg_setup_jack())
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot set up JACK server configuration based on current hardware");
+ return FALSE;
+ }
+
+ status = 0;
+ client = jack_client_open(client_name, 0, &status);
+ }
+ if (client == NULL)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create JACK instance");
+ return FALSE;
+ }
+
+ // XXXKF would use a callback instead
+ io->io_env.buffer_size = jack_get_buffer_size(client);
+ io->cb = NULL;
+ io->io_env.input_count = cbox_config_get_int("io", "inputs", 0);
+ io->input_buffers = malloc(sizeof(float *) * io->io_env.input_count);
+ io->io_env.output_count = cbox_config_get_int("io", "outputs", 2);
+ io->output_buffers = malloc(sizeof(float *) * io->io_env.output_count);
+
+ struct cbox_jack_io_impl *jii = malloc(sizeof(struct cbox_jack_io_impl));
+ io->impl = &jii->ioi;
+ jii->enable_common_midi_input = cbox_config_get_int("io", "enable_common_midi_input", 1);
+ jii->debug_transport = cbox_config_get_int("debug", "jack_transport", 0);
+ jii->last_transport_state = JackTransportStopped;
+ jii->external_tempo = FALSE;
+
+ cbox_command_target_init(&io->cmd_target, cbox_jack_io_process_cmd, jii);
+ jii->ioi.pio = io;
+ jii->ioi.getsampleratefunc = cbox_jackio_get_sample_rate;
+ jii->ioi.startfunc = cbox_jackio_start;
+ jii->ioi.stopfunc = cbox_jackio_stop;
+ jii->ioi.getstatusfunc = cbox_jackio_get_status;
+ jii->ioi.pollfunc = cbox_jackio_poll_ports;
+ jii->ioi.cyclefunc = cbox_jackio_cycle;
+ jii->ioi.getmidifunc = cbox_jackio_get_midi_data;
+ jii->ioi.createmidiinfunc = cbox_jackio_create_midi_in;
+ jii->ioi.destroymidiinfunc = cbox_jackio_destroy_midi_in;
+ jii->ioi.createmidioutfunc = cbox_jackio_create_midi_out;
+ jii->ioi.destroymidioutfunc = cbox_jackio_destroy_midi_out;
+ jii->ioi.updatemidiinroutingfunc = cbox_jackio_update_midi_in_routing;
+ jii->ioi.createaudiooutfunc = cbox_jackio_create_audio_out;
+ jii->ioi.destroyaudiooutfunc = cbox_jackio_destroy_audio_out;
+ jii->ioi.controltransportfunc = cbox_jackio_control_transport;
+ jii->ioi.getsynccompletedfunc = cbox_jackio_get_sync_completed;
+ jii->ioi.destroyfunc = cbox_jackio_destroy;
+
+ jii->client_name = g_strdup(jack_get_client_name(client));
+ jii->client = client;
+ jii->rb_autoconnect = jack_ringbuffer_create(sizeof(jack_port_t *) * 128);
+ jii->error_str = NULL;
+ io->io_env.srate = jack_get_sample_rate(client);
+
+ jii->inputs = malloc(sizeof(jack_port_t *) * io->io_env.input_count);
+ jii->outputs = malloc(sizeof(jack_port_t *) * io->io_env.output_count);
+ for (uint32_t i = 0; i < io->io_env.input_count; i++)
+ jii->inputs[i] = NULL;
+ for (uint32_t i = 0; i < io->io_env.output_count; i++)
+ jii->outputs[i] = NULL;
+ for (uint32_t i = 0; i < io->io_env.input_count; i++)
+ {
+ gchar *name = g_strdup_printf("in_%d", 1 + i);
+ jii->inputs[i] = jack_port_register(jii->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
+ if (!jii->inputs[i])
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create input port %d (%s)", i, name);
+ g_free(name);
+ goto cleanup;
+ }
+ g_free(name);
+ }
+ for (uint32_t i = 0; i < io->io_env.output_count; i++)
+ {
+ gchar *name = g_strdup_printf("out_%d", 1 + i);
+ jii->outputs[i] = jack_port_register(jii->client, name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ if (!jii->outputs[i])
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create output port %d (%s)", i, name);
+ g_free(name);
+ goto cleanup;
+ }
+ g_free(name);
+ }
+ if (jii->enable_common_midi_input)
+ {
+ jii->midi = jack_port_register(jii->client, "midi", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
+ if (!jii->midi)
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Cannot create MIDI port");
+ return FALSE;
+ }
+ }
+ else
+ jii->midi = NULL;
+
+ if (fb)
+ cbox_execute_on(fb, NULL, "/io/jack_client_name", "s", NULL, jii->client_name);
+
+ cbox_io_poll_ports(io, fb);
+
+ return TRUE;
+
+cleanup:
+ if (jii->inputs)
+ {
+ for (uint32_t i = 0; i < io->io_env.input_count; i++)
+ free(jii->inputs[i]);
+ free(jii->inputs);
+ }
+ if (jii->outputs)
+ {
+ for (uint32_t i = 0; i < io->io_env.output_count; i++)
+ free(jii->outputs[i]);
+ free(jii->outputs);
+ }
+ cbox_io_destroy_all_midi_ports(io);
+ if (jii->client_name)
+ free(jii->client_name);
+ jack_client_close(jii->client);
+ free(jii);
+ io->impl = NULL;
+ return FALSE;
+};
+
+#endif
diff --git a/template/calfbox/layer.c b/template/calfbox/layer.c
new file mode 100644
index 0000000..304e4d6
--- /dev/null
+++ b/template/calfbox/layer.c
@@ -0,0 +1,293 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config-api.h"
+#include "errors.h"
+#include "instr.h"
+#include "layer.h"
+#include "midi.h"
+#include "module.h"
+#include "rt.h"
+#include "scene.h"
+#include
+
+gboolean cbox_layer_load(struct cbox_layer *layer, const char *name, GError **error)
+{
+ const char *cv = NULL;
+ struct cbox_instrument *instr = NULL;
+ gchar *section = g_strdup_printf("layer:%s", name);
+
+ if (!cbox_config_has_section(section))
+ {
+ g_set_error(error, CBOX_MODULE_ERROR, CBOX_MODULE_ERROR_FAILED, "Missing section for layer %s", name);
+ goto error;
+ }
+
+ cv = cbox_config_get_string(section, "instrument");
+ if (cv)
+ {
+ instr = cbox_scene_get_instrument_by_name(layer->scene, cv, TRUE, error);
+ if (!instr)
+ {
+ cbox_force_error(error);
+ g_prefix_error(error, "Cannot get instrument %s for layer %s: ", cv, name);
+ goto error;
+ }
+ }
+
+ layer->enabled = cbox_config_get_int(section, "enabled", TRUE);
+ layer->low_note = 0;
+ layer->high_note = 127;
+
+ cv = cbox_config_get_string(section, "low_note");
+ if (cv)
+ layer->low_note = note_from_string(cv);
+
+ cv = cbox_config_get_string(section, "high_note");
+ if (cv)
+ layer->high_note = note_from_string(cv);
+
+ layer->transpose = cbox_config_get_int(section, "transpose", 0);
+ layer->fixed_note = cbox_config_get_int(section, "fixed_note", -1);
+ layer->in_channel = cbox_config_get_int(section, "in_channel", 0) - 1;
+ layer->out_channel = cbox_config_get_int(section, "out_channel", 0) - 1;
+ layer->disable_aftertouch = !cbox_config_get_int(section, "aftertouch", TRUE);
+ layer->invert_sustain = cbox_config_get_int(section, "invert_sustain", FALSE);
+ layer->consume = cbox_config_get_int(section, "consume", FALSE);
+ layer->ignore_scene_transpose = cbox_config_get_int(section, "ignore_scene_transpose", FALSE);
+ layer->ignore_program_changes = cbox_config_get_int(section, "ignore_program_changes", FALSE);
+ layer->external_output_set = FALSE;
+ g_free(section);
+
+ cbox_layer_set_instrument(layer, instr);
+
+ return 1;
+
+error:
+ if (instr)
+ cbox_instrument_destroy_if_unused(instr);
+
+ g_free(section);
+ return 0;
+}
+
+void cbox_layer_set_instrument(struct cbox_layer *layer, struct cbox_instrument *instrument)
+{
+ if (layer->instrument)
+ {
+ layer->instrument->refcount--;
+ cbox_instrument_destroy_if_unused(layer->instrument);
+ layer->instrument = NULL;
+ }
+ layer->instrument = instrument;
+ if (layer->instrument)
+ layer->instrument->refcount++;
+}
+
+static gboolean cbox_layer_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error);
+
+static void cbox_layer_destroyfunc(struct cbox_objhdr *objhdr)
+{
+ struct cbox_layer *layer = CBOX_H2O(objhdr);
+ if (layer->instrument && !--(layer->instrument->refcount))
+ {
+ if (layer->instrument->scene)
+ cbox_scene_remove_instrument(layer->instrument->scene, layer->instrument);
+
+ cbox_instrument_destroy_if_unused(layer->instrument);
+ }
+ if (layer->external_merger) {
+ cbox_midi_merger_disconnect(layer->external_merger, &layer->output_buffer, layer->scene->rt);
+ }
+ free(layer);
+}
+
+gboolean cbox_layer_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct cbox_layer *layer = ct->user_data;
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+
+ if (!(cbox_execute_on(fb, NULL, "/enable", "i", error, (int)layer->enabled) &&
+ (layer->instrument
+ ? cbox_execute_on(fb, NULL, "/instrument_name", "s", error, layer->instrument->module->instance_name) &&
+ cbox_execute_on(fb, NULL, "/instrument_uuid", "o", error, layer->instrument)
+ : (layer->external_output_set
+ ? cbox_uuid_report_as(&layer->external_output, "/external_output", fb, error)
+ : TRUE)) &&
+ cbox_execute_on(fb, NULL, "/consume", "i", error, (int)layer->consume) &&
+ cbox_execute_on(fb, NULL, "/ignore_scene_transpose", "i", error, (int)layer->ignore_scene_transpose) &&
+ cbox_execute_on(fb, NULL, "/ignore_program_changes", "i", error, (int)layer->ignore_program_changes) &&
+ cbox_execute_on(fb, NULL, "/disable_aftertouch", "i", error, (int)layer->disable_aftertouch) &&
+ cbox_execute_on(fb, NULL, "/transpose", "i", error, (int)layer->transpose) &&
+ cbox_execute_on(fb, NULL, "/fixed_note", "i", error, (int)layer->fixed_note) &&
+ cbox_execute_on(fb, NULL, "/low_note", "i", error, (int)layer->low_note) &&
+ cbox_execute_on(fb, NULL, "/high_note", "i", error, (int)layer->high_note) &&
+ cbox_execute_on(fb, NULL, "/in_channel", "i", error, layer->in_channel + 1) &&
+ cbox_execute_on(fb, NULL, "/out_channel", "i", error, layer->out_channel + 1) &&
+ CBOX_OBJECT_DEFAULT_STATUS(layer, fb, error)))
+ return FALSE;
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/enable") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->enabled = 0 != CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/consume") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->consume = 0 != CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/ignore_scene_transpose") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->ignore_scene_transpose = 0 != CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/ignore_program_changes") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->ignore_program_changes = 0 != CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/disable_aftertouch") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->disable_aftertouch = 0 != CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/transpose") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->transpose = CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/fixed_note") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->fixed_note = CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/low_note") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->low_note = CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/high_note") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->high_note = CBOX_ARG_I(cmd, 0);
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/in_channel") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->in_channel = CBOX_ARG_I(cmd, 0) - 1;
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/out_channel") && !strcmp(cmd->arg_types, "i"))
+ {
+ layer->out_channel = CBOX_ARG_I(cmd, 0) - 1;
+ return TRUE;
+ }
+ else if (!strcmp(cmd->command, "/external_output") && !strcmp(cmd->arg_types, "s"))
+ {
+ if (*CBOX_ARG_S(cmd, 0))
+ {
+ if (cbox_uuid_fromstring(&layer->external_output, CBOX_ARG_S(cmd, 0), error)) {
+ layer->external_output_set = TRUE;
+ }
+ }
+ else {
+ layer->external_output_set = FALSE;
+ }
+ cbox_scene_update_connected_outputs(layer->scene);
+ return TRUE;
+ }
+ else // otherwise, treat just like an command on normal (non-aux) output
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+}
+
+CBOX_CLASS_DEFINITION_ROOT(cbox_layer)
+
+struct cbox_layer *cbox_layer_new(struct cbox_scene *scene)
+{
+ struct cbox_document *doc = CBOX_GET_DOCUMENT(scene);
+ struct cbox_layer *l = malloc(sizeof(struct cbox_layer));
+
+ CBOX_OBJECT_HEADER_INIT(l, cbox_layer, doc);
+ cbox_command_target_init(&l->cmd_target, cbox_layer_process_cmd, l);
+ l->enabled = TRUE;
+ l->instrument = NULL;
+ l->low_note = 0;
+ l->high_note = 127;
+
+ l->transpose = 0;
+ l->fixed_note = -1;
+ l->in_channel = -1;
+ l->out_channel = -1;
+ l->disable_aftertouch = FALSE;
+ l->invert_sustain = FALSE;
+ l->consume = FALSE;
+ l->ignore_scene_transpose = FALSE;
+ l->ignore_program_changes = FALSE;
+ l->scene = scene;
+ cbox_uuid_clear(&l->external_output);
+ l->external_output_set = FALSE;
+ l->external_merger = NULL;
+ CBOX_OBJECT_REGISTER(l);
+ return l;
+}
+
+struct cbox_layer *cbox_layer_new_with_instrument(struct cbox_scene *scene, const char *instrument_name, GError **error)
+{
+ struct cbox_layer *layer = cbox_layer_new(scene);
+ struct cbox_instrument *instr = NULL;
+ if (!layer) goto error;
+ instr = cbox_scene_get_instrument_by_name(scene, instrument_name, TRUE, error);
+ if (!instr)
+ {
+ cbox_force_error(error);
+ g_prefix_error(error, "Cannot get instrument %s for new layer: ", instrument_name);
+ CBOX_DELETE(layer);
+ return NULL;
+ }
+ cbox_layer_set_instrument(layer, instr);
+ return layer;
+
+error:
+ CBOX_DELETE(layer);
+ if (instr)
+ cbox_instrument_destroy_if_unused(instr);
+ return NULL;
+}
+
+struct cbox_layer *cbox_layer_new_from_config(struct cbox_scene *scene, const char *layer_name, GError **error)
+{
+ struct cbox_layer *layer = cbox_layer_new(scene);
+ if (!layer)
+ goto error;
+
+ layer->scene = scene;
+ if (!cbox_layer_load(layer, layer_name, error))
+ goto error;
+
+ return layer;
+
+error:
+ CBOX_DELETE(layer);
+ return NULL;
+}
+
+
diff --git a/template/calfbox/layer.h b/template/calfbox/layer.h
new file mode 100644
index 0000000..3f4c9e8
--- /dev/null
+++ b/template/calfbox/layer.h
@@ -0,0 +1,63 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#ifndef CBOX_LAYER_H
+#define CBOX_LAYER_H
+
+#include "dom.h"
+#include "midi.h"
+#include
+#include
+
+struct cbox_module;
+struct cbox_rt;
+
+CBOX_EXTERN_CLASS(cbox_layer)
+
+struct cbox_layer
+{
+ CBOX_OBJECT_HEADER()
+ struct cbox_scene *scene;
+ struct cbox_instrument *instrument;
+ struct cbox_command_target cmd_target;
+ gboolean enabled;
+ int8_t in_channel; // -1 for Omni
+ int8_t out_channel; // -1 for Omni
+ uint8_t low_note;
+ uint8_t high_note;
+ int8_t transpose;
+ int8_t fixed_note;
+ gboolean disable_aftertouch;
+ gboolean invert_sustain;
+ gboolean consume;
+ gboolean ignore_scene_transpose;
+ gboolean ignore_program_changes;
+ gboolean external_output_set;
+ struct cbox_uuid external_output;
+ struct cbox_midi_buffer output_buffer;
+ struct cbox_midi_merger *external_merger;
+};
+
+extern struct cbox_layer *cbox_layer_new(struct cbox_scene *scene);
+extern struct cbox_layer *cbox_layer_new_with_instrument(struct cbox_scene *scene, const char *instrument_name, GError **error);
+extern struct cbox_layer *cbox_layer_new_from_config(struct cbox_scene *scene, const char *instrument_name, GError **error);
+extern gboolean cbox_layer_load(struct cbox_layer *layer, const char *name, GError **error);
+extern void cbox_layer_set_instrument(struct cbox_layer *layer, struct cbox_instrument *instrument);
+extern void cbox_layer_destroy(struct cbox_layer *layer);
+
+#endif
diff --git a/template/calfbox/limiter.c b/template/calfbox/limiter.c
new file mode 100644
index 0000000..401dfc5
--- /dev/null
+++ b/template/calfbox/limiter.c
@@ -0,0 +1,154 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "config.h"
+#include "config-api.h"
+#include "dspmath.h"
+#include "module.h"
+#include "onepole-float.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MODULE_PARAMS limiter_params
+
+struct limiter_params
+{
+ float threshold;
+ float attack;
+ float release;
+};
+
+struct limiter_module
+{
+ struct cbox_module module;
+
+ struct limiter_params *params, *old_params;
+
+ double cur_gain;
+ double atk_coeff, rel_coeff;
+};
+
+gboolean limiter_process_cmd(struct cbox_command_target *ct, struct cbox_command_target *fb, struct cbox_osc_command *cmd, GError **error)
+{
+ struct limiter_module *m = (struct limiter_module *)ct->user_data;
+
+ EFFECT_PARAM("/threshold", "f", threshold, double, , -100, 12) else
+ EFFECT_PARAM("/attack", "f", attack, double, , 1, 1000) else
+ EFFECT_PARAM("/release", "f", release, double, , 1, 5000) else
+ if (!strcmp(cmd->command, "/status") && !strcmp(cmd->arg_types, ""))
+ {
+ if (!cbox_check_fb_channel(fb, cmd->command, error))
+ return FALSE;
+ return
+ cbox_execute_on(fb, NULL, "/threshold", "f", error, m->params->threshold) &&
+ cbox_execute_on(fb, NULL, "/attack", "f", error, m->params->attack) &&
+ cbox_execute_on(fb, NULL, "/release", "f", error, m->params->release) &&
+ CBOX_OBJECT_DEFAULT_STATUS(&m->module, fb, error);
+ }
+ else
+ return cbox_object_default_process_cmd(ct, fb, cmd, error);
+ return TRUE;
+}
+
+void limiter_process_event(struct cbox_module *module, const uint8_t *data, uint32_t len)
+{
+ // struct limiter_module *m = module->user_data;
+}
+
+void limiter_process_block(struct cbox_module *module, cbox_sample_t **inputs, cbox_sample_t **outputs)
+{
+ struct limiter_module *m = module->user_data;
+ struct limiter_params *mp = m->params;
+
+ if (m->params != m->old_params)
+ {
+ m->atk_coeff = 1 - exp(-1000.0 / (mp->attack * m->module.srate));
+ m->rel_coeff = 1 - exp(-1000.0 / (mp->release * m->module.srate));
+ // update calculated values
+ }
+ const double minval = pow(2.0, -110.0);
+ for (int i = 0; i < CBOX_BLOCK_SIZE; ++i)
+ {
+ float left = inputs[0][i], right = inputs[1][i];
+
+ float level = fabs(left);
+ if (fabs(right) > level)
+ level = fabs(right);
+ if (level < minval)
+ level = minval;
+ level = log(level);
+
+ float gain = 0.0;
+
+ if (level > mp->threshold * 0.11552)
+ gain = mp->threshold * 0.11552 - level;
+
+ // instantaneous attack + slow release
+ if (gain >= m->cur_gain)
+ m->cur_gain += m->rel_coeff * (gain - m->cur_gain);
+ else
+ m->cur_gain += m->atk_coeff * (gain - m->cur_gain);
+
+ gain = exp(m->cur_gain);
+ //if (gain < 1)
+ // printf("level = %f gain = %f\n", m->cur_level, gain);
+
+ outputs[0][i] = left * gain;
+ outputs[1][i] = right * gain;
+ }
+}
+
+MODULE_SIMPLE_DESTROY_FUNCTION(limiter)
+
+MODULE_CREATE_FUNCTION(limiter)
+{
+ static int inited = 0;
+ if (!inited)
+ {
+ inited = 1;
+ }
+
+ struct limiter_module *m = malloc(sizeof(struct limiter_module));
+ CALL_MODULE_INIT(m, 2, 2, limiter);
+ m->module.process_event = limiter_process_event;
+ m->module.process_block = limiter_process_block;
+ struct limiter_params *p = malloc(sizeof(struct limiter_params));
+ p->threshold = -1;
+ p->attack = 10.f;
+ p->release = 2000.f;
+ m->params = p;
+ m->old_params = NULL;
+ m->cur_gain = 0.f;
+
+ return &m->module;
+}
+
+
+struct cbox_module_keyrange_metadata limiter_keyranges[] = {
+};
+
+struct cbox_module_livecontroller_metadata limiter_controllers[] = {
+};
+
+DEFINE_MODULE(limiter, 0, 2)
+
diff --git a/template/calfbox/main.c b/template/calfbox/main.c
new file mode 100644
index 0000000..ed9ad48
--- /dev/null
+++ b/template/calfbox/main.c
@@ -0,0 +1,450 @@
+/*
+Calf Box, an open source musical instrument.
+Copyright (C) 2010-2011 Krzysztof Foltman
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+#include "app.h"
+#include "config.h"
+#include "config-api.h"
+#include "dom.h"
+#include "engine.h"
+#include "instr.h"
+#include "io.h"
+#include "layer.h"
+#include "menu.h"
+#include "menuitem.h"
+#include "midi.h"
+#include "module.h"
+#include "pattern.h"
+#include "rt.h"
+#include "scene.h"
+#include "scripting.h"
+#include "song.h"
+#include "tarfile.h"
+#include "ui.h"
+#include "wavebank.h"
+
+#include
+#include
+#include
+#include
+#include
+#include
+#if USE_NCURSES
+#include
+#endif
+#include
+#include
+#include