aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHanspeter Portner <dev@open-music-kontrollers.ch>2015-09-10 19:13:53 +0200
committerHanspeter Portner <dev@open-music-kontrollers.ch>2015-09-10 19:13:53 +0200
commit0c9aec8f2f8426eb1a46c3a2485f19d692ffeedc (patch)
tree0b0eced86b36e34730c9d16baebf024d221539e6
parent5c6e57373f6e706987ebf75398b81fb47cea4e47 (diff)
downloadsynthpod-0c9aec8f2f8426eb1a46c3a2485f19d692ffeedc.tar.xz
implement nominalBlockLength and optionsInterface.
* notify plugins via optionsInterfaces about changed: * minBlockLength * nominalBlockLength * reinitialize plugins upon new maxBlockLength * resize Audio/CV buffers upon new maxBlockLength * add JACK callback for block length changes * add JACK callback for sample range changes
-rw-r--r--bin/synthpod_jack.c33
-rw-r--r--lib/synthpod_app.c311
-rw-r--r--lib/synthpod_app.h6
-rw-r--r--lib/synthpod_private.h3
-rw-r--r--plugins/synthpod.ttl3
-rw-r--r--plugins/synthpod_stereo.c24
6 files changed, 324 insertions, 56 deletions
diff --git a/bin/synthpod_jack.c b/bin/synthpod_jack.c
index 4aa89356..9489f5da 100644
--- a/bin/synthpod_jack.c
+++ b/bin/synthpod_jack.c
@@ -870,6 +870,33 @@ _session(jack_session_event_t *ev, void *data)
ecore_main_loop_thread_safe_call_async(_session_async, data);
}
+// rt, but can do non-rt stuff, as process won't be called
+static int
+_buffer_size(jack_nframes_t block_size, void *data)
+{
+ prog_t *handle = data;
+
+ //printf("JACK: new buffer size: %p %u %u\n",
+ // handle->app, handle->app_driver.max_block_size, block_size);
+
+ if(handle->app)
+ return sp_app_nominal_block_length(handle->app, block_size);
+
+ return 0;
+}
+
+// non-rt
+static int
+_sample_rate(jack_nframes_t sample_rate, void *data)
+{
+ prog_t *handle = data;
+
+ if(handle->app && (sample_rate != handle->app_driver.sample_rate) )
+ fprintf(stderr, "synthpod does not support dynamic sample rate changes\n");
+
+ return 0;
+}
+
#if defined(BUILD_UI)
// rt
static void *
@@ -1228,6 +1255,10 @@ _jack_init(prog_t *handle, const char *id)
return -1;
if(jack_set_session_callback(handle->client, _session, handle))
return -1;
+ if(jack_set_sample_rate_callback(handle->client, _sample_rate, handle))
+ return -1;
+ if(jack_set_buffer_size_callback(handle->client, _buffer_size, handle))
+ return -1;
jack_on_shutdown(handle->client, _shutdown, handle);
jack_set_xrun_callback(handle->client, _xrun, handle);
@@ -1271,7 +1302,7 @@ _open(const char *path, const char *name, const char *id, void *data)
// synthpod init
handle->app_driver.sample_rate = jack_get_sample_rate(handle->client);
handle->app_driver.max_block_size = jack_get_buffer_size(handle->client);
- handle->app_driver.min_block_size = jack_get_buffer_size(handle->client);
+ handle->app_driver.min_block_size = 1;
handle->app_driver.seq_size = MAX(handle->seq_size,
jack_port_type_get_buffer_size(handle->client, JACK_DEFAULT_MIDI_TYPE));
diff --git a/lib/synthpod_app.c b/lib/synthpod_app.c
index b4e1b963..08e62b48 100644
--- a/lib/synthpod_app.c
+++ b/lib/synthpod_app.c
@@ -47,6 +47,7 @@ typedef struct _port_t port_t;
typedef struct _work_t work_t;
typedef struct _job_t job_t;
typedef struct _source_t source_t;
+typedef struct _pool_t pool_t;
enum _bypass_state_t {
BYPASS_STATE_PAUSED = 0,
@@ -86,6 +87,11 @@ struct _work_t {
uint8_t payload [0];
};
+struct _pool_t {
+ size_t size;
+ void *buf;
+};
+
struct _mod_t {
sp_app_t *app;
u_id_t uid;
@@ -119,7 +125,7 @@ struct _mod_t {
// opts
struct {
- LV2_Options_Option options [4];
+ LV2_Options_Option options [5];
const LV2_Options_Interface *iface;
} opts;
@@ -138,8 +144,7 @@ struct _mod_t {
uint32_t num_ports;
port_t *ports;
- size_t size;
- void *buf;
+ pool_t pools [PORT_TYPE_NUM];
};
struct _source_t {
@@ -520,6 +525,71 @@ _mod_make_path(LV2_State_Make_Path_Handle instance, const char *abstract_path)
return absolute_path;
}
+static inline int
+_mod_alloc_pool(pool_t *pool)
+{
+#if defined(_WIN32)
+ pool->buf = _aligned_malloc(pool->size, 8);
+ if(pool->buf)
+ {
+#else
+ posix_memalign(&pool->buf, 8, pool->size);
+ if(pool->buf)
+ {
+ mlock(pool->buf, pool->size);
+#endif
+ memset(pool->buf, 0x0, pool->size);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+static inline void
+_mod_free_pool(pool_t *pool)
+{
+ if(pool->buf)
+ {
+#if !defined(_WIN32)
+ munlock(pool->buf, pool->size);
+#endif
+ free(pool->buf);
+ pool->buf = NULL;
+ }
+}
+
+static void
+_mod_slice_pool(mod_t *mod, port_type_t type)
+{
+ // set ptr to pool buffer
+ void *ptr = mod->pools[type].buf;
+
+ for(port_direction_t dir=0; dir<PORT_DIRECTION_NUM; dir++)
+ {
+ for(uint32_t i=0; i<mod->num_ports; i++)
+ {
+ port_t *tar = &mod->ports[i];
+
+ if( (tar->type != type) || (tar->direction != dir) )
+ continue; //skip
+
+ // define buffer slice
+ tar->buf = ptr;
+
+ // initialize control buffers to default value
+ if(tar->type == PORT_TYPE_CONTROL)
+ {
+ float *buf_ptr = PORT_BUF_ALIGNED(tar);
+ *buf_ptr = tar->dflt;
+ }
+
+ ptr += lv2_atom_pad_size(tar->size);
+ }
+ }
+}
+
+
// non-rt worker-thread
static inline mod_t *
_sp_app_mod_add(sp_app_t *app, const char *uri, uint32_t uid)
@@ -580,8 +650,15 @@ _sp_app_mod_add(sp_app_t *app, const char *uri, uint32_t uid)
mod->opts.options[2].type = app->forge.Int;
mod->opts.options[2].value = &app->driver->seq_size;
- mod->opts.options[3].key = 0; // sentinel
- mod->opts.options[3].value = NULL; // sentinel
+ mod->opts.options[3].context = LV2_OPTIONS_INSTANCE;
+ mod->opts.options[3].subject = 0;
+ mod->opts.options[3].key = app->regs.bufsz.nominal_block_length.urid;
+ mod->opts.options[3].size = sizeof(int32_t);
+ mod->opts.options[3].type = app->forge.Int;
+ mod->opts.options[3].value = &app->driver->max_block_size; // set to max by default
+
+ mod->opts.options[4].key = 0; // sentinel
+ mod->opts.options[4].value = NULL; // sentinel
// populate feature list
int nfeatures = 0;
@@ -700,7 +777,7 @@ _sp_app_mod_add(sp_app_t *app, const char *uri, uint32_t uid)
mod->zero.iface = lilv_instance_get_extension_data(mod->inst,
ZERO_WORKER__interface);
mod->opts.iface = lilv_instance_get_extension_data(mod->inst,
- LV2_OPTIONS__interface); //TODO actually use this for something?
+ LV2_OPTIONS__interface);
mod->sys.iface = lilv_instance_get_extension_data(mod->inst,
SYSTEM_PORT__interface);
@@ -711,7 +788,9 @@ _sp_app_mod_add(sp_app_t *app, const char *uri, uint32_t uid)
else if(!strcmp(uri, SYNTHPOD_PREFIX"sink"))
system_sink = 1;
- mod->size = 0;
+ // clear pool sizes
+ for(port_type_t pool=0; pool<PORT_TYPE_NUM; pool++)
+ mod->pools[pool].size = 0;
mod->ports = calloc(mod->num_ports, sizeof(port_t));
for(uint32_t i=0; i<mod->num_ports; i++)
@@ -823,60 +902,44 @@ _sp_app_mod_add(sp_app_t *app, const char *uri, uint32_t uid)
lilv_node_free(minsize);
}
- mod->size += lv2_atom_pad_size(tar->size);
+ // increase pool sizes
+ mod->pools[tar->type].size += lv2_atom_pad_size(tar->size);
}
- // allocate single 8-byte aligned buffer per plugin
-#if defined(_WIN32)
- mod->buf = _aligned_malloc(mod->size, 8);
- if(mod->buf)
- {
-#else
- posix_memalign(&mod->buf, 8, mod->size);
- if(mod->buf)
+ // allocate 8-byte aligned buffer per plugin and port type pool
+ int alloc_failed = 0;
+ for(port_type_t pool=0; pool<PORT_TYPE_NUM; pool++)
{
- mlock(mod->buf, mod->size);
-#endif
- memset(mod->buf, 0x0, mod->size);
+ if(_mod_alloc_pool(&mod->pools[pool]))
+ {
+ alloc_failed = 1;
+ break;
+ }
}
- else
+
+ if(alloc_failed)
{
+ for(port_type_t pool=0; pool<PORT_TYPE_NUM; pool++)
+ _mod_free_pool(&mod->pools[pool]);
+
free(mod->uri_str);
free(mod->ports);
free(mod);
+
return NULL;
}
// slice plugin buffer into per-port-type-and-direction regions for
// efficient dereference in plugin instance
- void *ptr = mod->buf;
- for(port_type_t type=0; type<PORT_TYPE_NUM; type++)
- {
- for(port_direction_t dir=0; dir<PORT_DIRECTION_NUM; dir++)
- {
- for(uint32_t i=0; i<mod->num_ports; i++)
- {
- port_t *tar = &mod->ports[i];
+ for(port_type_t pool=0; pool<PORT_TYPE_NUM; pool++)
+ _mod_slice_pool(mod, pool);
- if( (tar->type != type) || (tar->direction != dir) )
- continue; //skip
-
- // define buffer slice
- tar->buf = ptr;
-
- // initialize control buffers to default value
- if(tar->type == PORT_TYPE_CONTROL)
- {
- float *buf_ptr = PORT_BUF_ALIGNED(tar);
- *buf_ptr = tar->dflt;
- }
-
- // set port buffer
- lilv_instance_connect_port(mod->inst, i, tar->buf);
+ for(uint32_t i=0; i<mod->num_ports; i++)
+ {
+ port_t *tar = &mod->ports[i];
- ptr += lv2_atom_pad_size(tar->size);
- }
- }
+ // set port buffer
+ lilv_instance_connect_port(mod->inst, i, tar->buf);
}
// load presets
@@ -900,13 +963,8 @@ _sp_app_mod_del(sp_app_t *app, mod_t *mod)
lilv_instance_free(mod->inst);
// free memory
- if(mod->buf)
- {
-#if !defined(_WIN32)
- munlock(mod->buf, mod->size);
-#endif
- free(mod->buf);
- }
+ for(port_type_t pool=0; pool<PORT_TYPE_NUM; pool++)
+ _mod_free_pool(&mod->pools[pool]);
// unregister system ports
for(uint32_t i=0; i<mod->num_ports; i++)
@@ -2684,7 +2742,7 @@ sp_app_free(sp_app_t *app)
// free mods
for(int m=0; m<app->num_mods; m++)
_sp_app_mod_del(app, app->mods[m]);
-
+
sp_regs_deinit(&app->regs);
if(!app->embedded)
@@ -3225,3 +3283,148 @@ sp_app_paused(sp_app_t *app)
return 2;
}
}
+
+uint32_t
+sp_app_options_set(sp_app_t *app, const LV2_Options_Option *options)
+{
+ LV2_Options_Status status = LV2_OPTIONS_SUCCESS;
+
+ for(int m=0; m<app->num_mods; m++)
+ {
+ mod_t *mod = app->mods[m];
+
+ if(mod->opts.iface && mod->opts.iface->set)
+ status |= mod->opts.iface->set(mod->handle, options);
+ }
+
+ return status;
+}
+
+// non-rt
+static void
+_sp_app_reinitialize(sp_app_t *app)
+{
+ for(int m=0; m<app->num_mods; m++)
+ {
+ mod_t *mod = app->mods[m];
+
+ // reinitialize all modules,
+ lilv_instance_deactivate(mod->inst);
+ lilv_instance_free(mod->inst);
+ mod->inst = NULL;
+ mod->handle = NULL;
+
+ // mod->features should be up-to-date
+ mod->inst = lilv_plugin_instantiate(mod->plug, app->driver->sample_rate, mod->features);
+ mod->handle = lilv_instance_get_handle(mod->inst),
+
+ //TODO should we re-get extension_data?
+
+ // resize sample based buffers only (e.g. AUDIO and CV)
+ _mod_free_pool(&mod->pools[PORT_TYPE_AUDIO]);
+ _mod_free_pool(&mod->pools[PORT_TYPE_CV]);
+
+ mod->pools[PORT_TYPE_AUDIO].size = 0;
+ mod->pools[PORT_TYPE_CV].size = 0;
+
+ for(uint32_t i=0; i<mod->num_ports; i++)
+ {
+ port_t *tar = &mod->ports[i];
+
+ if( (tar->type == PORT_TYPE_AUDIO)
+ || (tar->type == PORT_TYPE_CV) )
+ {
+ tar->size = app->driver->max_block_size * sizeof(float);
+ mod->pools[tar->type].size += lv2_atom_pad_size(tar->size);
+ }
+ }
+
+ _mod_alloc_pool(&mod->pools[PORT_TYPE_AUDIO]);
+ _mod_alloc_pool(&mod->pools[PORT_TYPE_CV]);
+
+ _mod_slice_pool(mod, PORT_TYPE_AUDIO);
+ _mod_slice_pool(mod, PORT_TYPE_CV);
+ }
+
+ // refresh all connections
+ for(int m=0; m<app->num_mods; m++)
+ {
+ mod_t *mod = app->mods[m];
+
+ for(uint32_t i=0; i<mod->num_ports; i++)
+ {
+ port_t *tar = &mod->ports[i];
+
+ // set port buffer
+ lilv_instance_connect_port(mod->inst, i,
+ tar->direction == PORT_DIRECTION_INPUT
+ ? PORT_SINK_ALIGNED(tar)
+ : PORT_BUF_ALIGNED(tar));
+ }
+
+ lilv_instance_activate(mod->inst);
+ }
+}
+
+// non-rt
+int
+sp_app_nominal_block_length(sp_app_t *app, uint32_t nsamples)
+{
+ if(nsamples <= app->driver->max_block_size)
+ {
+ for(int m=0; m<app->num_mods; m++)
+ {
+ mod_t *mod = app->mods[m];
+
+ if(mod->opts.iface && mod->opts.iface->set)
+ {
+ if(nsamples < app->driver->min_block_size)
+ {
+ // update driver struct
+ app->driver->min_block_size = nsamples;
+
+ const LV2_Options_Option options [2] = {{
+ .context = LV2_OPTIONS_INSTANCE,
+ .subject = 0, // is ignored
+ .key = app->regs.bufsz.min_block_length.urid,
+ .size = sizeof(int32_t),
+ .type = app->forge.Int,
+ .value = &app->driver->min_block_size
+ }, {
+ .key = 0, // sentinel
+ .value =NULL // sentinel
+ }};
+
+ // notify new minimalBlockLength
+ uint32_t ret = mod->opts.iface->set(mod->handle, options); // TODO check
+ }
+
+ const int32_t nominal_block_length = nsamples;
+
+ const LV2_Options_Option options [2] = {{
+ .context = LV2_OPTIONS_INSTANCE,
+ .subject = 0, // is ignored
+ .key = app->regs.bufsz.nominal_block_length.urid,
+ .size = sizeof(int32_t),
+ .type = app->forge.Int,
+ .value = &nominal_block_length
+ }, {
+ .key = 0, // sentinel
+ .value =NULL // sentinel
+ }};
+
+ // notify new nominalBlockLength
+ uint32_t ret = mod->opts.iface->set(mod->handle, options); // TODO check
+ }
+ }
+ }
+ else // nsamples > max_block_size
+ {
+ // update driver struct
+ app->driver->max_block_size = nsamples;
+
+ _sp_app_reinitialize(app);
+ }
+
+ return 0;
+}
diff --git a/lib/synthpod_app.h b/lib/synthpod_app.h
index 137bfa20..2f5e1696 100644
--- a/lib/synthpod_app.h
+++ b/lib/synthpod_app.h
@@ -137,4 +137,10 @@ sp_app_restore(sp_app_t *app, LV2_State_Retrieve_Function retrieve,
SYNTHPOD_SYMBOL_EXTERN int
sp_app_paused(sp_app_t *app);
+SYNTHPOD_SYMBOL_EXTERN uint32_t
+sp_app_options_set(sp_app_t *app, const LV2_Options_Option *options);
+
+SYNTHPOD_SYMBOL_EXTERN int
+sp_app_nominal_block_length(sp_app_t *app, uint32_t nsamples);
+
#endif // _SYNTHPOD_APP_H
diff --git a/lib/synthpod_private.h b/lib/synthpod_private.h
index b30c02c3..43233aa1 100644
--- a/lib/synthpod_private.h
+++ b/lib/synthpod_private.h
@@ -177,6 +177,7 @@ struct _reg_t {
} core;
struct {
+ reg_item_t nominal_block_length;
reg_item_t max_block_length;
reg_item_t min_block_length;
reg_item_t sequence_size;
@@ -330,6 +331,7 @@ sp_regs_init(reg_t *regs, LilvWorld *world, LV2_URID_Map *map)
_register(&regs->core.symbol, world, map, LV2_CORE__symbol);
_register(&regs->core.index, world, map, LV2_CORE__index);
+ _register(&regs->bufsz.nominal_block_length, world, map, LV2_BUF_SIZE_PREFIX "nominalBlockLength");
_register(&regs->bufsz.max_block_length, world, map, LV2_BUF_SIZE__maxBlockLength);
_register(&regs->bufsz.min_block_length, world, map, LV2_BUF_SIZE__minBlockLength);
_register(&regs->bufsz.sequence_size, world, map, LV2_BUF_SIZE__sequenceSize);
@@ -456,6 +458,7 @@ sp_regs_deinit(reg_t *regs)
_unregister(&regs->core.symbol);
_unregister(&regs->core.index);
+ _unregister(&regs->bufsz.nominal_block_length);
_unregister(&regs->bufsz.max_block_length);
_unregister(&regs->bufsz.min_block_length);
_unregister(&regs->bufsz.sequence_size);
diff --git a/plugins/synthpod.ttl b/plugins/synthpod.ttl
index 60d75b6c..d8b1bb18 100644
--- a/plugins/synthpod.ttl
+++ b/plugins/synthpod.ttl
@@ -34,6 +34,7 @@
@prefix inst: <http://lv2plug.in/ns/ext/instance-access> .
@prefix urid: <http://lv2plug.in/ns/ext/urid#> .
@prefix patch: <http://lv2plug.in/ns/ext/patch#> .
+@prefix opts: <http://lv2plug.in/ns/ext/options#> .
@prefix lic: <http://opensource.org/licenses/> .
@prefix omk: <http://open-music-kontrollers.ch/ventosus#> .
@@ -130,7 +131,7 @@ synthpod:stereo
doap:name "Synthpod Stereo" ;
doap:license lic:Artistic-2.0 ;
lv2:project proj:synthpod ;
- lv2:extensionData work:interface , state:interface, zwork:interface ;
+ lv2:extensionData work:interface , state:interface, zwork:interface, opts:interface ;
lv2:requiredFeature work:schedule , bufsz:boundedBlockLength, urid:map, urid:unmap ;
lv2:optionalFeature lv2:isLive , lv2:hardRTCapable , log:log, zwork:schedule ;
diff --git a/plugins/synthpod_stereo.c b/plugins/synthpod_stereo.c
index 23403437..39ac71b6 100644
--- a/plugins/synthpod_stereo.c
+++ b/plugins/synthpod_stereo.c
@@ -806,6 +806,28 @@ cleanup(LV2_Handle instance)
eina_shutdown();
}
+static uint32_t
+_opts_get(LV2_Handle instance, LV2_Options_Option *options)
+{
+ // we have no options
+
+ return LV2_OPTIONS_ERR_BAD_KEY;
+}
+
+static uint32_t
+_opts_set(LV2_Handle instance, const LV2_Options_Option *options)
+{
+ plughandle_t *handle = instance;
+
+ // route options to all plugins
+ return sp_app_options_set(handle->app, options);
+}
+
+static const LV2_Options_Interface opts_iface = {
+ .get = _opts_get,
+ .set = _opts_set
+};
+
static const void*
extension_data(const char* uri)
{
@@ -815,6 +837,8 @@ extension_data(const char* uri)
return &state_iface;
else if(!strcmp(uri, ZERO_WORKER__interface))
return &zero_iface;
+ else if(!strcmp(uri, LV2_OPTIONS__interface))
+ return &opts_iface;
else
return NULL;
}