diff options
| -rw-r--r-- | doc/reference.doxygen.in | 1 | ||||
| -rw-r--r-- | lv2/lv2plug.in/ns/ext/atom/forge.h | 1 | ||||
| -rw-r--r-- | lv2/lv2plug.in/ns/ext/urid/urid.ttl | 4 | ||||
| l--------- | lv2/lv2plug.in/ns/ext/worker/ext.pc.in | 1 | ||||
| -rw-r--r-- | lv2/lv2plug.in/ns/ext/worker/manifest.ttl | 8 | ||||
| l--------- | lv2/lv2plug.in/ns/ext/worker/waf | 1 | ||||
| -rw-r--r-- | lv2/lv2plug.in/ns/ext/worker/worker.h | 138 | ||||
| -rw-r--r-- | lv2/lv2plug.in/ns/ext/worker/worker.ttl | 58 | ||||
| l--------- | lv2/lv2plug.in/ns/ext/worker/wscript | 1 | ||||
| -rw-r--r-- | plugins/eg-sampler.lv2/sampler.c | 259 | ||||
| -rw-r--r-- | plugins/eg-sampler.lv2/sampler.ttl | 12 | ||||
| -rw-r--r-- | plugins/eg-sampler.lv2/wscript | 8 | ||||
| -rw-r--r-- | plugins/eg-sampler.lv2/zix/common.h | 83 | ||||
| -rw-r--r-- | plugins/eg-sampler.lv2/zix/ring.c | 231 | ||||
| -rw-r--r-- | plugins/eg-sampler.lv2/zix/ring.h | 136 | ||||
| -rw-r--r-- | plugins/eg-sampler.lv2/zix/sem.h | 236 | ||||
| -rw-r--r-- | plugins/eg-sampler.lv2/zix/thread.h | 133 | 
17 files changed, 325 insertions, 986 deletions
diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in index f97a93f..9ed3968 100644 --- a/doc/reference.doxygen.in +++ b/doc/reference.doxygen.in @@ -587,6 +587,7 @@ INPUT =  \  	@LV2_SRCDIR@/lv2/lv2plug.in/ns/ext/ui-resize/ui-resize.h \  	@LV2_SRCDIR@/lv2/lv2plug.in/ns/ext/uri-map/uri-map.h \  	@LV2_SRCDIR@/lv2/lv2plug.in/ns/ext/urid/urid.h \ +	@LV2_SRCDIR@/lv2/lv2plug.in/ns/ext/worker/worker.h \  	@LV2_SRCDIR@/lv2/lv2plug.in/ns/extensions/ui/ui.h \  	@LV2_SRCDIR@/lv2/lv2plug.in/ns/lv2core/lv2.h diff --git a/lv2/lv2plug.in/ns/ext/atom/forge.h b/lv2/lv2plug.in/ns/ext/atom/forge.h index 322e14e..ea5977e 100644 --- a/lv2/lv2plug.in/ns/ext/atom/forge.h +++ b/lv2/lv2plug.in/ns/ext/atom/forge.h @@ -120,7 +120,6 @@ static inline void  lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map)  {  	lv2_atom_forge_set_buffer(forge, NULL, 0); -	forge->stack    = NULL;  	forge->Blank    = map->map(map->handle, LV2_ATOM__Blank);  	forge->Bool     = map->map(map->handle, LV2_ATOM__Bool);  	forge->Double   = map->map(map->handle, LV2_ATOM__Double); diff --git a/lv2/lv2plug.in/ns/ext/urid/urid.ttl b/lv2/lv2plug.in/ns/ext/urid/urid.ttl index 121ffaf..d080107 100644 --- a/lv2/lv2plug.in/ns/ext/urid/urid.ttl +++ b/lv2/lv2plug.in/ns/ext/urid/urid.ttl @@ -68,7 +68,7 @@ urid:map  	lv2:documentation """  <p>A feature which is used to map URIs to integers.  To support this feature,  the host must pass an LV2_Feature to LV2_Descriptor::instantiate() with URI -LV2_URID_MAP_URI and data pointed to an instance of LV2_URID_Map.</p> +LV2_URID__map and data pointed to an instance of LV2_URID_Map.</p>  """ .  urid:unmap @@ -76,6 +76,6 @@ urid:unmap  	lv2:documentation """  <p>A feature which is used to unmap URIs previously mapped to integers by  urid:map.  To support this feature, the host must pass an LV2_Feature to -LV2_Descriptor::instantiate() with URI LV2_URID_UNMAP_URI and data pointed to +LV2_Descriptor::instantiate() with URI LV2_URID__unmapl and data pointed to  an instance of LV2_URID_Unmap.</p>  """ . diff --git a/lv2/lv2plug.in/ns/ext/worker/ext.pc.in b/lv2/lv2plug.in/ns/ext/worker/ext.pc.in new file mode 120000 index 0000000..03dd044 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/ext.pc.in @@ -0,0 +1 @@ +../../../../../ext.pc.in
\ No newline at end of file diff --git a/lv2/lv2plug.in/ns/ext/worker/manifest.ttl b/lv2/lv2plug.in/ns/ext/worker/manifest.ttl new file mode 100644 index 0000000..af46f01 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/manifest.ttl @@ -0,0 +1,8 @@ +@prefix lv2:  <http://lv2plug.in/ns/lv2core#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + +<http://lv2plug.in/ns/ext/worker> +	a lv2:Specification ; +	lv2:minorVersion 0 ; +	lv2:microVersion 1 ; +	rdfs:seeAlso <worker.ttl> . diff --git a/lv2/lv2plug.in/ns/ext/worker/waf b/lv2/lv2plug.in/ns/ext/worker/waf new file mode 120000 index 0000000..5235032 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/waf @@ -0,0 +1 @@ +../../../../../waf
\ No newline at end of file diff --git a/lv2/lv2plug.in/ns/ext/worker/worker.h b/lv2/lv2plug.in/ns/ext/worker/worker.h new file mode 100644 index 0000000..612a24c --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/worker.h @@ -0,0 +1,138 @@ +/* +  Copyright 2012 David Robillard <http://drobilla.net> + +  Permission to use, copy, modify, and/or distribute this software for any +  purpose with or without fee is hereby granted, provided that the above +  copyright notice and this permission notice appear in all copies. + +  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** +   @file worker.h C header for the LV2 Worker extension +   <http://lv2plug.in/ns/ext/worker>. +*/ + +#ifndef LV2_WORKER_H +#define LV2_WORKER_H + +#include <stdint.h> + +#include "lv2/lv2plug.in/ns/lv2core/lv2.h" + +#define LV2_WORKER_URI    "http://lv2plug.in/ns/ext/worker" +#define LV2_WORKER_PREFIX LV2_WORKER_URI "#" + +#define LV2_WORKER__Interface LV2_WORKER_PREFIX "Interface" +#define LV2_WORKER__schedule  LV2_WORKER_PREFIX "schedule" + +/** +   A status code for worker functions. +*/ +typedef enum { +	LV2_WORKER_SUCCESS       = 0,  /**< Completed successfully. */ +	LV2_WORKER_ERR_UNKNOWN   = 1,  /**< Unknown error. */ +	LV2_WORKER_ERR_NO_SPACE  = 2   /**< Failed due to lack of space. */ +} LV2_Worker_Status; + +typedef void* LV2_Worker_Respond_Handle; + +/** +   A function to respond to run() from the worker method. + +   The @p data MUST be safe for the host to copy and later pass to +   work_response(), and the host MUST guarantee that it will be eventually +   passed to work_response() if this function returns LV2_WORKER_SUCCESS. +*/ +typedef LV2_Worker_Status (*LV2_Worker_Respond_Function)( +	LV2_Worker_Respond_Handle handle, +	uint32_t                  size, +	const void*               data); + +/** +   LV2 Plugin Worker Interface. + +   This is the interface provided by the plugin to implement a worker method. +   The plugin's extension_data() method should return an LV2_Worker_Interface +   when called with LV2_WORKER__Interface as its argument. +*/ +typedef struct _LV2_Worker_Interface { +	/** +	   The worker method.  This is called by the host in a non-realtime context +	   as requested, possibly with an arbitrary message to handle. + +	   A response can be sent to run() using @p respond.  The plugin MUST NOT +	   make any assumptions about which thread calls this method, other than +	   the fact that there are no real-time requirements. + +	   @param instance The LV2 instance this is a method on. +	   @param respond  A function for sending a response to run(). +	   @param handle   Must be passed to @p respond if it is called. +	   @param size     The size of @p data. +	   @param data     Data from run(), or NULL. +	*/ +	LV2_Worker_Status (*work)(LV2_Handle                  instance, +	                          LV2_Worker_Respond_Function respond, +	                          LV2_Worker_Respond_Handle   handle, +	                          uint32_t                    size, +	                          const void*                 data); + +	/** +	   Handle a response from the worker.  This is called by the host in the +	   run() context when a response from the worker is ready. + +	   @param instance The LV2 instance this is a method on. +	   @param size     The size of @p body. +	   @param body     Message body, or NULL. +	*/ +	LV2_Worker_Status (*work_response)(LV2_Handle  instance, +	                                   uint32_t    size, +	                                   const void* body); +} LV2_Worker_Interface; + +typedef void* LV2_Worker_Schedule_Handle; + +typedef struct _LV2_Worker_Schedule { +	/** +	   Opaque host data. +	*/ +	LV2_Worker_Schedule_Handle handle; + +	/** +	   Request from run() that the host call the worker. + +	   This function is in the audio threading class.  It should be called from +	   run() to request that the host call the work() method in a non-realtime +	   context with the given arguments. + +	   This function is always safe to call from run(), but it is not +	   guaranteed that the worker is actually called from a different thread. +	   In particular, when free-wheeling (e.g. for offline rendering), the +	   worker may be executed immediately.  This allows single-threaded +	   processing with sample accuracy and avoids timing problems when run() is +	   executing much faster or slower than real-time. + +	   Plugins SHOULD be written in such a way that if the worker runs +	   immediately, and responses from the worker are delivered immediately, +	   the effect of the work takes place immediately with sample accuracy. + +	   The @p data MUST be safe for the host to copy and later pass to work(), +	   and the host MUST guarantee that it will be eventually passed to work() +	   if this function returns LV2_WORKER_SUCCESS. + +	   @param handle The handle field of this struct. +	   @param size   The size of @p body. +	   @param data   Message to pass to work(), or NULL. +	*/ +	LV2_Worker_Status (*schedule_work)(LV2_Worker_Schedule_Handle handle, +	                                   uint32_t                   size, +	                                   const void*                data); +} LV2_Worker_Schedule; + +#endif  /* LV2_WORKER_H */ diff --git a/lv2/lv2plug.in/ns/ext/worker/worker.ttl b/lv2/lv2plug.in/ns/ext/worker/worker.ttl new file mode 100644 index 0000000..ea49056 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/worker.ttl @@ -0,0 +1,58 @@ +# LV2 Worker Extension +# Copyright 2012 David Robillard <d@drobilla.net> +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +@prefix work: <http://lv2plug.in/ns/ext/worker#> . +@prefix lv2:  <http://lv2plug.in/ns/lv2core#> . +@prefix rdf:  <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> . +@prefix doap: <http://usefulinc.com/ns/doap#> . +@prefix foaf: <http://xmlns.com/foaf/0.1/> . + +<http://lv2plug.in/ns/ext/worker> +	a lv2:Specification , +		lv2:Feature ; +	doap:name "LV2 Worker" ; +	doap:shortdesc "Support for a non-realtime plugin worker method." ; +	lv2:documentation """ +""" . + +work:Interface +	a rdfs:Class ; +	rdfs:subClassOf lv2:ExtensionData ; +	lv2:documentation """ +<p>A structure (LV2_Worker_Interface) which contains the worker method to be +called by the host.  In order to support this extension, the plugin must return +a valid LV2_Worker_Interface from LV2_Descriptor::extension_data() when it is +called with URI LV2_WORKER__Interface.</p> + +<p>The plugin data file should describe this like so:</p> +<pre class="turtle-code"> +@prefix work: <http://lv2plug.in/ns/ext/worker#> . + +<plugin> +    a lv2:Plugin ; +    lv2:extensionData work:Interface . +</pre> +""" . + +work:schedule +	a lv2:Feature ; +	lv2:documentation """ +<p>A feature which provides functions for use by the plugin to implement a +worker method.  To support this feature, the host must pass an LV2_Feature to +LV2_Descriptor::instantiate() with URI LV2_WORKER__schedule and data pointed to +an instance of LV2_Worker_Schedule.</p> +""" . diff --git a/lv2/lv2plug.in/ns/ext/worker/wscript b/lv2/lv2plug.in/ns/ext/worker/wscript new file mode 120000 index 0000000..7e2c01b --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/wscript @@ -0,0 +1 @@ +../../../../../ext.wscript
\ No newline at end of file diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c index ec59ef3..229762e 100644 --- a/plugins/eg-sampler.lv2/sampler.c +++ b/plugins/eg-sampler.lv2/sampler.c @@ -44,16 +44,11 @@  #include "lv2/lv2plug.in/ns/ext/patch/patch.h"  #include "lv2/lv2plug.in/ns/ext/state/state.h"  #include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "lv2/lv2plug.in/ns/ext/worker/worker.h"  #include "lv2/lv2plug.in/ns/lv2core/lv2.h" -#include "zix/sem.h" -#include "zix/thread.h" -#include "zix/ring.h" -  #include "./uris.h" -#define RING_SIZE 4096 -  enum {  	SAMPLER_CONTROL  = 0,  	SAMPLER_RESPONSE = 1, @@ -71,39 +66,38 @@ typedef struct {  typedef struct {  	/* Features */ -	LV2_URID_Map* map; +	LV2_URID_Map*        map; +	LV2_Worker_Schedule* schedule;  	/* Forge for creating atoms */  	LV2_Atom_Forge forge; -	/* Worker thread, communication, and sync */ -	ZixThread worker_thread; -	ZixSem    signal; -	ZixRing*  to_worker; -	ZixRing*  from_worker; -	bool      exit; -  	/* Sample */  	Sample* sample;  	/* Ports */ -	float*             output_port; -	LV2_Atom_Sequence* control_port; -	LV2_Atom_Sequence* notify_port; +	float*               output_port; +	LV2_Atom_Sequence*   control_port; +	LV2_Atom_Sequence*   notify_port; +	LV2_Atom_Forge_Frame notify_frame;  	/* URIs */  	SamplerURIs uris; +	/* Current position in run() */ +	uint32_t frame_offset; +  	/* Playback state */  	sf_count_t frame;  	bool       play;  } Sampler; -/** An atom-like message used internally to apply/free samples. - * - * This is only used internally via ringbuffers, since it is not POD and - * therefore not strictly an Atom. - */ +/** +   An atom-like message used internally to apply/free samples. +  +   This is only used internally to communicate with the worker, it is not an +   Atom because it is not POD. +*/  typedef struct {  	LV2_Atom atom;  	Sample*  sample; @@ -120,7 +114,7 @@ load_sample(Sampler* plugin, const char* path)  	SNDFILE* const sndfile = sf_open(path, SFM_READ, info);  	if (!sndfile || !info->frames || (info->channels != 1)) { -		fprintf(stderr, "failed to open sample '%s'.\n", path); +		fprintf(stderr, "Failed to open sample '%s'.\n", path);  		free(sample);  		return NULL;  	} @@ -128,7 +122,7 @@ load_sample(Sampler* plugin, const char* path)  	/* Read data */  	float* const data = malloc(sizeof(float) * info->frames);  	if (!data) { -		fprintf(stderr, "failed to allocate memory for sample.\n"); +		fprintf(stderr, "Failed to allocate memory for sample.\n");  		return NULL;  	}  	sf_seek(sndfile, 0ul, SEEK_SET); @@ -144,32 +138,7 @@ load_sample(Sampler* plugin, const char* path)  	return sample;  } -static bool -handle_set_message(Sampler*               plugin, -                   const LV2_Atom_Object* obj) -{ -	/* Get file path from message */ -	const LV2_Atom* file_path = read_set_file(&plugin->uris, obj); -	if (!file_path) { -		return false; -	} - -	/* Load sample. */ -	Sample* sample = load_sample(plugin, LV2_ATOM_BODY(file_path)); -	if (sample) { -		/* Loaded sample, send it to run() to be applied. */ -		const SampleMessage msg = { -			{ sizeof(sample), plugin->uris.eg_applySample }, -			sample -		}; -		zix_ring_write( -			plugin->from_worker, &msg, lv2_atom_pad_size(sizeof(msg))); -	} - -	return true; -} - -void +static void  free_sample(Sample* sample)  {  	if (sample) { @@ -180,33 +149,65 @@ free_sample(Sample* sample)  	}  } -void* -worker_thread_main(void* arg) +/** Handle work (load or free a sample) in a non-realtime thread. */ +static LV2_Worker_Status +work(LV2_Handle                  instance, +     LV2_Worker_Respond_Function respond, +     LV2_Worker_Respond_Handle   handle, +     uint32_t                    size, +     const void*                 data)  { -	Sampler* plugin = (Sampler*)arg; - -	while (!zix_sem_wait(&plugin->signal) && !plugin->exit) { -		/* Peek message header to see how much we need to read. */ -		LV2_Atom head; -		zix_ring_peek(plugin->to_worker, &head, sizeof(head)); - -		/* Read message. */ -		const uint32_t size = lv2_atom_pad_size(sizeof(LV2_Atom) + head.size); -		uint8_t        buf[size]; -		LV2_Atom*      obj  = (LV2_Atom*)buf; -		zix_ring_read(plugin->to_worker, buf, size); - -		if (obj->type == plugin->uris.eg_freeSample) { -			/* Free old sample */ -			SampleMessage* msg = (SampleMessage*)obj; -			free_sample(msg->sample); -		} else { -			/* Handle set message (load sample). */ -			handle_set_message(plugin, (LV2_Atom_Object*)obj); +	Sampler*  self = (Sampler*)instance; +	LV2_Atom* atom = (LV2_Atom*)data; +	if (atom->type == self->uris.eg_freeSample) { +		/* Free old sample */ +		SampleMessage* msg = (SampleMessage*)data; +		free_sample(msg->sample); +	} else { +		/* Handle set message (load sample). */ +		LV2_Atom_Object* obj = (LV2_Atom_Object*)data; + +		/* Get file path from message */ +		const LV2_Atom* file_path = read_set_file(&self->uris, obj); +		if (!file_path) { +			return LV2_WORKER_ERR_UNKNOWN; +		} + +		/* Load sample. */ +		Sample* sample = load_sample(self, LV2_ATOM_BODY(file_path)); +		if (sample) { +			/* Loaded sample, send it to run() to be applied. */ +			respond(handle, sizeof(sample), &sample);  		}  	} -	return 0; +	return LV2_WORKER_SUCCESS; +} + +/** Handle a response from work() in the audio thread. */ +static LV2_Worker_Status +work_response(LV2_Handle  instance, +              uint32_t    size, +              const void* data) +{ +	Sampler* self = (Sampler*)instance; + +	SampleMessage msg = { { sizeof(Sample*), self->uris.eg_freeSample }, +	                      self->sample }; + +	/* Send a message to the worker to free the current sample */ +	self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg); +	                          +	/* Install the new sample */ +	self->sample = *(Sample**)data; + +	/* Send a notification that we're using a new sample. */ +	lv2_atom_forge_frame_time(&self->forge, self->frame_offset); +	write_set_file(&self->forge, &self->uris, +	               self->sample->path, +	               self->sample->path_len); + +	return LV2_WORKER_SUCCESS;  }  static void @@ -242,52 +243,34 @@ instantiate(const LV2_Descriptor*     descriptor,  		return NULL;  	} +	memset(plugin, 0, sizeof(Sampler));  	plugin->sample = (Sample*)malloc(sizeof(Sample));  	if (!plugin->sample) {  		return NULL;  	}  	memset(plugin->sample, 0, sizeof(Sample)); -	memset(&plugin->uris, 0, sizeof(plugin->uris)); -	/* Scan host features for URID map */ -	LV2_URID_Map* map = NULL; +	/* Scan and store host features */  	for (int i = 0; features[i]; ++i) { -		if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { -			map = (LV2_URID_Map*)features[i]->data; +		if (!strcmp(features[i]->URI, LV2_URID__map)) { +			plugin->map = (LV2_URID_Map*)features[i]->data; +		} else if (!strcmp(features[i]->URI, LV2_WORKER__schedule)) { +			plugin->schedule = (LV2_Worker_Schedule*)features[i]->data;  		}  	} -	if (!map) { +	if (!plugin->map) {  		fprintf(stderr, "Host does not support urid:map.\n");  		goto fail; +	} else if (!plugin->schedule) { +		fprintf(stderr, "Host does not support work:schedule.\n"); +		goto fail;  	}  	/* Map URIS and initialise forge */ -	plugin->map = map;  	map_sampler_uris(plugin->map, &plugin->uris);  	lv2_atom_forge_init(&plugin->forge, plugin->map); -	/* Create signal for waking up worker thread */ -	if (zix_sem_init(&plugin->signal, 0)) { -		fprintf(stderr, "Could not initialize semaphore.\n"); -		goto fail; -	} - -	/* Create worker thread */ -	plugin->exit = false; -	if (zix_thread_create( -		    &plugin->worker_thread, 1024, worker_thread_main, plugin)) { -		fprintf(stderr, "Could not initialize worker thread.\n"); -		goto fail; -	} - -	/* Create ringbuffers for communicating with worker thread */ -	plugin->to_worker   = zix_ring_new(RING_SIZE); -	plugin->from_worker = zix_ring_new(RING_SIZE); - -	zix_ring_mlock(plugin->to_worker); -	zix_ring_mlock(plugin->from_worker); -  	/* Load the default sample file */  	const size_t path_len    = strlen(path);  	const size_t file_len    = strlen(default_sample_file); @@ -308,12 +291,6 @@ cleanup(LV2_Handle instance)  {  	Sampler* plugin = (Sampler*)instance; -	plugin->exit = true; -	zix_sem_post(&plugin->signal); -	zix_thread_join(plugin->worker_thread, 0); -	zix_sem_destroy(&plugin->signal); -	zix_ring_free(plugin->to_worker); -	zix_ring_free(plugin->from_worker);  	free_sample(plugin->sample);  	free(plugin);  } @@ -328,9 +305,19 @@ run(LV2_Handle instance,  	sf_count_t   pos         = 0;  	float*       output      = plugin->output_port; +	/* Set up forge to write directly to notify output port. */ +	const uint32_t notify_capacity = plugin->notify_port->atom.size; +	lv2_atom_forge_set_buffer(&plugin->forge, +	                          (uint8_t*)plugin->notify_port, +	                          notify_capacity); + +	/* Start a sequence in the notify output port. */ +	lv2_atom_forge_sequence_head(&plugin->forge, &plugin->notify_frame, 0); +  	/* Read incoming events */  	LV2_SEQUENCE_FOREACH(plugin->control_port, i) {  		LV2_Atom_Event* const ev = lv2_sequence_iter_get(i); +		plugin->frame_offset = ev->time.frames;  		if (ev->body.type == uris->midi_Event) {  			uint8_t* const data = (uint8_t* const)(ev + 1);  			if ((data[0] & 0xF0) == 0x90) { @@ -341,13 +328,11 @@ run(LV2_Handle instance,  		} else if (is_object_type(uris, ev->body.type)) {  			const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;  			if (obj->body.otype == uris->patch_Set) { -				/* Received a set message, send it to the worker thread. */ +				/* Received a set message, send it to the worker. */  				fprintf(stderr, "Queueing set message\n"); -				zix_ring_write(plugin->to_worker, -				               obj, -				               lv2_atom_pad_size( -					               lv2_atom_total_size(&obj->atom))); -				zix_sem_post(&plugin->signal); +				plugin->schedule->schedule_work(plugin->schedule->handle, +				                                lv2_atom_total_size(&ev->body), +				                                &ev->body);  			} else {  				fprintf(stderr, "Unknown object type %d\n", obj->body.otype);  			} @@ -380,47 +365,6 @@ run(LV2_Handle instance,  	for (; pos < sample_count; ++pos) {  		output[pos] = 0.0f;  	} - -	/* Set up forge to write directly to notify output port buffer */ -	const uint32_t notify_capacity = plugin->notify_port->atom.size; - -	lv2_atom_forge_set_buffer(&plugin->forge, -	                          (uint8_t*)plugin->notify_port, -	                          notify_capacity); - -	LV2_Atom_Forge_Frame seq_frame; -	lv2_atom_forge_sequence_head(&plugin->forge, &seq_frame, 0); - -	/* Read messages from worker thread */ -	SampleMessage  m; -	const uint32_t msize = lv2_atom_pad_size(sizeof(m)); -	while (zix_ring_read(plugin->from_worker, &m, msize) == msize) { -		if (m.atom.type == uris->eg_applySample) { -			/* Send a message to the worker to free the current sample */ -			SampleMessage free_msg = { -				{ sizeof(plugin->sample), uris->eg_freeSample }, -				plugin->sample -			}; -			zix_ring_write(plugin->to_worker, -			               &free_msg, -			               lv2_atom_pad_size(sizeof(free_msg))); -			zix_sem_post(&plugin->signal); - -			/* Install the new sample */ -			plugin->sample = m.sample; - -			/* Send a notification that we're using a new sample. */ -			lv2_atom_forge_frame_time(&plugin->forge, 0); -			write_set_file(&plugin->forge, uris, -			               plugin->sample->path, -			               plugin->sample->path_len); - -		} else { -			fprintf(stderr, "Unknown message from worker\n"); -		} -	} - -	lv2_atom_forge_pop(&plugin->forge, &seq_frame);  }  static void @@ -477,12 +421,15 @@ restore(LV2_Handle                  instance,  	}  } -const void* +static const void*  extension_data(const char* uri)  { -	static const LV2_State_Interface state = { save, restore }; +	static const LV2_State_Interface  state  = { save, restore }; +	static const LV2_Worker_Interface worker = { work, work_response };  	if (!strcmp(uri, LV2_STATE__Interface)) {  		return &state; +	} else if (!strcmp(uri, LV2_WORKER__Interface)) { +		return &worker;  	}  	return NULL;  } diff --git a/plugins/eg-sampler.lv2/sampler.ttl b/plugins/eg-sampler.lv2/sampler.ttl index fa93ba4..a70be7b 100644 --- a/plugins/eg-sampler.lv2/sampler.ttl +++ b/plugins/eg-sampler.lv2/sampler.ttl @@ -21,12 +21,14 @@  @prefix lv2:  <http://lv2plug.in/ns/lv2core#> .  @prefix ui:   <http://lv2plug.in/ns/extensions/ui#> .  @prefix urid: <http://lv2plug.in/ns/ext/urid#> . +@prefix work: <http://lv2plug.in/ns/ext/worker#> .  <http://lv2plug.in/plugins/eg-sampler>  	a lv2:Plugin ;  	doap:name "Example Sampler" ;  	doap:license <http://opensource.org/licenses/isc> ; -	lv2:requiredFeature urid:map ; +	lv2:requiredFeature urid:map , +		work:schedule ;  	lv2:optionalFeature lv2:hardRTCapable ;  	lv2:extensionData <http://lv2plug.in/ns/ext/state#Interface> ;  	ui:ui <http://lv2plug.in/plugins/eg-sampler#ui> ; @@ -38,7 +40,7 @@  			<http://lv2plug.in/ns/ext/patch#Message> ;  		lv2:index 0 ;  		lv2:symbol "control" ; -		lv2:name "Control" +		lv2:name "Control" ;  	] , [  		a lv2:OutputPort ,  			atom:MessagePort ; @@ -46,13 +48,13 @@  		atom:supports <http://lv2plug.in/ns/ext/patch#Message> ;  		lv2:index 1 ;  		lv2:symbol "notify" ; -		lv2:name "Notify" +		lv2:name "Notify" ;  	] , [  		a lv2:AudioPort ,  			lv2:OutputPort ;  		lv2:index 2 ;  		lv2:symbol "out" ; -		lv2:name "Out" +		lv2:name "Out" ;  	] .  <http://lv2plug.in/plugins/eg-sampler#ui> @@ -61,5 +63,5 @@  	ui:portNotification [  		ui:plugin <http://lv2plug.in/plugins/eg-sampler> ;  		lv2:symbol "notify" ; -        ui:notifyType atom:Blank ; +		ui:notifyType atom:Blank ;  	] . diff --git a/plugins/eg-sampler.lv2/wscript b/plugins/eg-sampler.lv2/wscript index c25b28d..1f3ec04 100644 --- a/plugins/eg-sampler.lv2/wscript +++ b/plugins/eg-sampler.lv2/wscript @@ -31,6 +31,8 @@ def configure(conf):                            uselib_store='LV2_PATCH')          autowaf.check_pkg(conf, 'lv2-lv2plug.in-ns-ext-state',                            uselib_store='LV2_STATE') +        autowaf.check_pkg(conf, 'lv2-lv2plug.in-ns-ext-worker', +                          uselib_store='LV2_WORKER')      conf.check(function_name='mlock',                 header_name='sys/mman.h', @@ -85,11 +87,11 @@ def build(bld):      # Build plugin library      obj = bld(features     = 'c cshlib',                env          = penv, -              source       = ['sampler.c', 'zix/ring.c'], +              source       = ['sampler.c'],                name         = 'sampler',                target       = '%s/sampler' % bundle,                install_path = '${LV2DIR}/%s' % bundle, -              use          = 'SNDFILE LV2CORE LV2_URID LV2_ATOM LV2_STATE LV2_MESSAGE', +              use          = 'SNDFILE LV2CORE LV2_URID LV2_ATOM LV2_STATE LV2_PATCH LV2_WORKER',                includes     = includes)      # Build UI library @@ -100,6 +102,6 @@ def build(bld):                    name         = 'sampler_ui',                    target       = '%s/sampler_ui' % bundle,                    install_path = '${LV2DIR}/%s' % bundle, -                  use          = 'GTK2 LV2CORE LV2_URID LV2_ATOM LV2_STATE LV2_MESSAGE', +                  use          = 'GTK2 LV2CORE LV2_URID LV2_ATOM LV2_PATCH',                    includes     = includes) diff --git a/plugins/eg-sampler.lv2/zix/common.h b/plugins/eg-sampler.lv2/zix/common.h deleted file mode 100644 index f113cfe..0000000 --- a/plugins/eg-sampler.lv2/zix/common.h +++ /dev/null @@ -1,83 +0,0 @@ -/* -  Copyright 2011-2012 David Robillard <http://drobilla.net> - -  Permission to use, copy, modify, and/or distribute this software for any -  purpose with or without fee is hereby granted, provided that the above -  copyright notice and this permission notice appear in all copies. - -  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#ifndef ZIX_COMMON_H -#define ZIX_COMMON_H - -/** -   @addtogroup zix -   @{ -*/ - -/** @cond */ -#ifdef ZIX_SHARED -#    ifdef _WIN32 -#        define ZIX_LIB_IMPORT __declspec(dllimport) -#        define ZIX_LIB_EXPORT __declspec(dllexport) -#    else -#        define ZIX_LIB_IMPORT __attribute__((visibility("default"))) -#        define ZIX_LIB_EXPORT __attribute__((visibility("default"))) -#    endif -#    ifdef ZIX_INTERNAL -#        define ZIX_API ZIX_LIB_EXPORT -#    else -#        define ZIX_API ZIX_LIB_IMPORT -#    endif -#else -#    define ZIX_API -#endif -/** @endcond */ - -#ifdef __cplusplus -extern "C" { -#else -#    include <stdbool.h> -#endif - -typedef enum { -	ZIX_STATUS_SUCCESS, -	ZIX_STATUS_ERROR, -	ZIX_STATUS_NO_MEM, -	ZIX_STATUS_NOT_FOUND, -	ZIX_STATUS_EXISTS, -	ZIX_STATUS_BAD_ARG, -	ZIX_STATUS_BAD_PERMS, -} ZixStatus; - -/** -   Function for comparing two elements. -*/ -typedef int (*ZixComparator)(const void* a, const void* b, void* user_data); - -/** -   Function for testing equality of two elements. -*/ -typedef bool (*ZixEqualFunc)(const void* a, const void* b); - -/** -   Function to destroy an element. -*/ -typedef void (*ZixDestroyFunc)(void* ptr); - -/** -   @} -*/ - -#ifdef __cplusplus -}  /* extern "C" */ -#endif - -#endif  /* ZIX_COMMON_H */ diff --git a/plugins/eg-sampler.lv2/zix/ring.c b/plugins/eg-sampler.lv2/zix/ring.c deleted file mode 100644 index 29d415c..0000000 --- a/plugins/eg-sampler.lv2/zix/ring.c +++ /dev/null @@ -1,231 +0,0 @@ -/* -  Copyright 2011-2012 David Robillard <http://drobilla.net> - -  Permission to use, copy, modify, and/or distribute this software for any -  purpose with or without fee is hereby granted, provided that the above -  copyright notice and this permission notice appear in all copies. - -  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#ifdef HAVE_MLOCK -#    include <sys/mman.h> -#    define ZIX_MLOCK(ptr, size) mlock((ptr), (size)) -#elif defined(_WIN32) -#    include <windows.h> -#    define ZIX_MLOCK(ptr, size) VirtualLock((ptr), (size)) -#else -#    pragma message("warning: No memory locking, possible RT violations") -#    define ZIX_MLOCK(ptr, size) -#endif - -#if defined(__APPLE__) -#    include <libkern/OSAtomic.h> -#    define ZIX_FULL_BARRIER() OSMemoryBarrier() -#elif defined(_WIN32) -#    include <windows.h> -#    define ZIX_FULL_BARRIER() MemoryBarrier() -#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) -#    define ZIX_FULL_BARRIER() __sync_synchronize() -#else -#    pragma message("warning: No memory barriers, possible SMP bugs") -#    define ZIX_FULL_BARRIER() -#endif - -/* No support for any systems with separate read and write barriers */ -#define ZIX_READ_BARRIER() ZIX_FULL_BARRIER() -#define ZIX_WRITE_BARRIER() ZIX_FULL_BARRIER() - -#include "zix/ring.h" - -struct ZixRingImpl { -	uint32_t write_head;  ///< Read index into buf -	uint32_t read_head;   ///< Write index into buf -	uint32_t size;        ///< Size (capacity) in bytes -	uint32_t size_mask;   ///< Mask for fast modulo -	char*    buf;         ///< Contents -}; - -static inline uint32_t -next_power_of_two(uint32_t size) -{ -	// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 -	size--; -	size |= size >> 1; -	size |= size >> 2; -	size |= size >> 4; -	size |= size >> 8; -	size |= size >> 16; -	size++; -	return size; -} - -ZixRing* -zix_ring_new(uint32_t size) -{ -	ZixRing* ring = (ZixRing*)malloc(sizeof(ZixRing)); -	ring->write_head = 0; -	ring->read_head  = 0; -	ring->size       = next_power_of_two(size); -	ring->size_mask  = ring->size - 1; -	ring->buf        = (char*)malloc(ring->size); -	return ring; -} - -void -zix_ring_free(ZixRing* ring) -{ -	free(ring->buf); -	free(ring); -} - -void -zix_ring_mlock(ZixRing* ring) -{ -	ZIX_MLOCK(ring, sizeof(ZixRing)); -	ZIX_MLOCK(ring->buf, ring->size); -} - -void -zix_ring_reset(ZixRing* ring) -{ -	ring->write_head = 0; -	ring->read_head  = 0; -} - -static inline uint32_t -read_space_internal(const ZixRing* ring, uint32_t r, uint32_t w) -{ -	if (r < w) { -		return w - r; -	} else { -		return (w - r + ring->size) & ring->size_mask; -	} -} - -uint32_t -zix_ring_read_space(const ZixRing* ring) -{ -	return read_space_internal(ring, ring->read_head, ring->write_head); -} - -static inline uint32_t -write_space_internal(const ZixRing* ring, uint32_t r, uint32_t w) -{ -	if (r == w) { -		return ring->size - 1; -	} else if (r < w) { -		return ((r - w + ring->size) & ring->size_mask) - 1; -	} else { -		return (r - w) - 1; -	} -} - -uint32_t -zix_ring_write_space(const ZixRing* ring) -{ -	return write_space_internal(ring, ring->read_head, ring->write_head); -} - -uint32_t -zix_ring_capacity(const ZixRing* ring) -{ -	return ring->size - 1; -} - -static inline uint32_t -peek_internal(const ZixRing* ring, uint32_t r, uint32_t w, -              uint32_t size, void* dst) -{ -	if (read_space_internal(ring, r, w) < size) { -		return 0; -	} - -	if (r + size < ring->size) { -		memcpy(dst, &ring->buf[r], size); -	} else { -		const uint32_t first_size = ring->size - r; -		memcpy(dst, &ring->buf[r], first_size); -		memcpy((char*)dst + first_size, &ring->buf[0], size - first_size); -	} - -	return size; -} - -uint32_t -zix_ring_peek(ZixRing* ring, void* dst, uint32_t size) -{ -	const uint32_t r = ring->read_head; -	const uint32_t w = ring->write_head; - -	return peek_internal(ring, r, w, size, dst); -} - -uint32_t -zix_ring_read(ZixRing* ring, void* dst, uint32_t size) -{ -	const uint32_t r = ring->read_head; -	const uint32_t w = ring->write_head; - -	if (peek_internal(ring, r, w, size, dst)) { -		ZIX_READ_BARRIER(); -		ring->read_head = (r + size) & ring->size_mask; -		return size; -	} else { -		return 0; -	} -} - -uint32_t -zix_ring_skip(ZixRing* ring, uint32_t size) -{ -	const uint32_t r = ring->read_head; -	const uint32_t w = ring->write_head; -	if (read_space_internal(ring, r, w) < size) { -		return 0; -	} - -	ZIX_READ_BARRIER(); -	ring->read_head = (r + size) & ring->size_mask; -	return size; -} - -uint32_t -zix_ring_write(ZixRing* ring, const void* src, uint32_t size) -{ -	const uint32_t r = ring->read_head; -	const uint32_t w = ring->write_head; -	if (write_space_internal(ring, r, w) < size) { -		return 0; -	} - -	if (w + size <= ring->size) { -		memcpy(&ring->buf[w], src, size); -		ZIX_WRITE_BARRIER(); -		ring->write_head = (w + size) & ring->size_mask; -	} else { -		const uint32_t this_size = ring->size - w; -		memcpy(&ring->buf[w], src, this_size); -		memcpy(&ring->buf[0], (char*)src + this_size, size - this_size); -		ZIX_WRITE_BARRIER(); -		ring->write_head = size - this_size; -	} - -	return size; -} - -void* -zix_ring_write_head(ZixRing* ring) -{ -	return &ring->buf[ring->write_head]; -} diff --git a/plugins/eg-sampler.lv2/zix/ring.h b/plugins/eg-sampler.lv2/zix/ring.h deleted file mode 100644 index dd45769..0000000 --- a/plugins/eg-sampler.lv2/zix/ring.h +++ /dev/null @@ -1,136 +0,0 @@ -/* -  Copyright 2011-2012 David Robillard <http://drobilla.net> - -  Permission to use, copy, modify, and/or distribute this software for any -  purpose with or without fee is hereby granted, provided that the above -  copyright notice and this permission notice appear in all copies. - -  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#ifndef ZIX_RING_H -#define ZIX_RING_H - -#include <stdint.h> - -#include "zix/common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** -   @addtogroup zix -   @{ -   @name Ring -   @{ -*/ - -/** -   A lock-free ring buffer. - -   Thread-safe with a single reader and single writer, and realtime safe -   on both ends. -*/ -typedef struct ZixRingImpl ZixRing; - -/** -   Create a new ring. -   @param size Size in bytes (note this may be rounded up). - -   At most @c size - 1 bytes may be stored in the ring at once. -*/ -ZixRing* -zix_ring_new(uint32_t size); - -/** -   Destroy a ring. -*/ -void -zix_ring_free(ZixRing* ring); - -/** -   Lock the ring data into physical memory. - -   This function is NOT thread safe or real-time safe, but it should be called -   after zix_ring_new() to lock all ring memory to avoid page faults while -   using the ring (i.e. this function MUST be called first in order for the -   ring to be truly real-time safe). - -*/ -void -zix_ring_mlock(ZixRing* ring); - -/** -   Reset (empty) a ring. - -   This function is NOT thread-safe, it may only be called when there are no -   readers or writers. -*/ -void -zix_ring_reset(ZixRing* ring); - -/** -   Return the number of bytes of space available for reading. -*/ -uint32_t -zix_ring_read_space(const ZixRing* ring); - -/** -   Return the number of bytes of space available for writing. -*/ -uint32_t -zix_ring_write_space(const ZixRing* ring); - -/** -   Return the capacity (i.e. total write space when empty). -*/ -uint32_t -zix_ring_capacity(const ZixRing* ring); - -/** -   Read from the ring without advancing the read head. -*/ -uint32_t -zix_ring_peek(ZixRing* ring, void* dst, uint32_t size); - -/** -   Read from the ring and advance the read head. -*/ -uint32_t -zix_ring_read(ZixRing* ring, void* dst, uint32_t size); - -/** -   Skip data in the ring (advance read head without reading). -*/ -uint32_t -zix_ring_skip(ZixRing* ring, uint32_t size); - -/** -   Write data to the ring. -*/ -uint32_t -zix_ring_write(ZixRing* ring, const void* src, uint32_t size); - -/** -   Return a pointer to the current position of the write head. -*/ -void* -zix_ring_write_head(ZixRing* ring); - -/** -   @} -   @} -*/ - -#ifdef __cplusplus -}  /* extern "C" */ -#endif - -#endif  /* ZIX_RING_H */ diff --git a/plugins/eg-sampler.lv2/zix/sem.h b/plugins/eg-sampler.lv2/zix/sem.h deleted file mode 100644 index d536c99..0000000 --- a/plugins/eg-sampler.lv2/zix/sem.h +++ /dev/null @@ -1,236 +0,0 @@ -/* -  Copyright 2012-2012 David Robillard <http://drobilla.net> - -  Permission to use, copy, modify, and/or distribute this software for any -  purpose with or without fee is hereby granted, provided that the above -  copyright notice and this permission notice appear in all copies. - -  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#ifndef ZIX_SEM_H -#define ZIX_SEM_H - -#ifdef __APPLE__ -#    include <mach/mach.h> -#elif defined(_WIN32) -#    include <windows.h> -#else -#    include <semaphore.h> -#    include <errno.h> -#endif - -#include "zix/common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** -   @addtogroup zix -   @{ -   @name Semaphore -   @{ -*/ - -/** -   A counting semaphore. - -   This is an integer that is always positive, and has two main operations: -   increment (post) and decrement (wait).  If a decrement can not be performed -   (i.e. the value is 0) the caller will be blocked until another thread posts -   and the operation can succeed. - -   Semaphores can be created with any starting value, but typically this will -   be 0 so the semaphore can be used as a simple signal where each post -   corresponds to one wait. - -   Semaphores are very efficient (much moreso than a mutex/cond pair).  In -   particular, at least on Linux, post is async-signal-safe, which means it -   does not block and will not be interrupted.  If you need to signal from -   a realtime thread, this is the most appropriate primitive to use. -*/ -typedef struct ZixSemImpl ZixSem; - -/** -   Create and initialize @c sem to @c initial. -*/ -static inline ZixStatus -zix_sem_init(ZixSem* sem, unsigned initial); - -/** -   Destroy @c sem. -*/ -static inline void -zix_sem_destroy(ZixSem* sem); - -/** -   Increment (and signal any waiters). -   Realtime safe. -*/ -static inline void -zix_sem_post(ZixSem* sem); - -/** -   Wait until count is > 0, then decrement. -   Obviously not realtime safe. -*/ -static inline ZixStatus -zix_sem_wait(ZixSem* sem); - -/** -   Non-blocking version of wait(). - -   @return true if decrement was successful (lock was acquired). -*/ -static inline bool -zix_sem_try_wait(ZixSem* sem); - -/** -   @cond -*/ - -#ifdef __APPLE__ - -struct ZixSemImpl { -	semaphore_t sem; -}; - -static inline ZixStatus -zix_sem_init(ZixSem* sem, unsigned initial) -{ -	return semaphore_create(mach_task_self(), &sem->sem, SYNC_POLICY_FIFO, 0) -		? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -static inline void -zix_sem_destroy(ZixSem* sem) -{ -	semaphore_destroy(mach_task_self(), sem->sem); -} - -static inline void -zix_sem_post(ZixSem* sem) -{ -	semaphore_signal(sem->sem); -} - -static inline ZixStatus -zix_sem_wait(ZixSem* sem) -{ -	if (semaphore_wait(sem->sem) != KERN_SUCCESS) { -		return ZIX_STATUS_ERROR; -	} -	return ZIX_STATUS_SUCCESS; -} - -static inline bool -zix_sem_try_wait(ZixSem* sem) -{ -	const mach_timespec_t zero = { 0, 0 }; -	return semaphore_timedwait(sem->sem, zero) == KERN_SUCCESS; -} - -#elif defined(_WIN32) - -struct ZixSemImpl { -	HANDLE sem; -}; - -static inline ZixStatus -zix_sem_init(ZixSem* sem, unsigned initial) -{ -	sem->sem = CreateSemaphore(NULL, initial, LONG_MAX, NULL); -	return (sem->sem) ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -static inline void -zix_sem_destroy(ZixSem* sem) -{ -	CloseHandle(sem->sem); -} - -static inline void -zix_sem_post(ZixSem* sem) -{ -	ReleaseSemaphore(sem->sem, 1, NULL); -} - -static inline ZixStatus -zix_sem_wait(ZixSem* sem) -{ -	if (WaitForSingleObject(sem->sem, INFINITE) != WAIT_OBJECT_0) { -		return ZIX_STATUS_ERROR; -	} -	return ZIX_STATUS_SUCCESS; -} - -static inline bool -zix_sem_try_wait(ZixSem* sem) -{ -	WaitForSingleObject(sem->sem, 0); -} - -#else  /* !defined(__APPLE__) && !defined(_WIN32) */ - -struct ZixSemImpl { -	sem_t sem; -}; - -static inline ZixStatus -zix_sem_init(ZixSem* sem, unsigned initial) -{ -	return sem_init(&sem->sem, 0, initial) -		? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -static inline void -zix_sem_destroy(ZixSem* sem) -{ -	sem_destroy(&sem->sem); -} - -static inline void -zix_sem_post(ZixSem* sem) -{ -	sem_post(&sem->sem); -} - -static inline ZixStatus -zix_sem_wait(ZixSem* sem) -{ -	while (sem_wait(&sem->sem)) { -		if (errno != EINTR) { -			return ZIX_STATUS_ERROR; -		} -		/* Otherwise, interrupted, so try again. */ -	} - -	return ZIX_STATUS_SUCCESS; -} - -static inline bool -zix_sem_try_wait(ZixSem* sem) -{ -	return (sem_trywait(&sem->sem) == 0); -} - -#endif - -/** -   @endcond -   @} -   @} -*/ - -#ifdef __cplusplus -}  /* extern "C" */ -#endif - -#endif  /* ZIX_SEM_H */ diff --git a/plugins/eg-sampler.lv2/zix/thread.h b/plugins/eg-sampler.lv2/zix/thread.h deleted file mode 100644 index 602b701..0000000 --- a/plugins/eg-sampler.lv2/zix/thread.h +++ /dev/null @@ -1,133 +0,0 @@ -/* -  Copyright 2012-2012 David Robillard <http://drobilla.net> - -  Permission to use, copy, modify, and/or distribute this software for any -  purpose with or without fee is hereby granted, provided that the above -  copyright notice and this permission notice appear in all copies. - -  THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ - -#ifndef ZIX_THREAD_H -#define ZIX_THREAD_H - -#ifdef _WIN32 -#    include <windows.h> -#else -#    include <errno.h> -#    include <pthread.h> -#endif - -#include "zix/common.h" - -#ifdef __cplusplus -extern "C" { -#else -#    include <stdbool.h> -#endif - -/** -   @addtogroup zix -   @{ -   @name Thread -   @{ -*/ - -#ifdef _WIN32 -typedef HANDLE ZixThread; -#else -typedef pthread_t ZixThread; -#endif - -/** -   Initialize @c thread to a new thread. - -   The thread will immediately be launched, calling @c function with @c arg -   as the only parameter. -*/ -static inline ZixStatus -zix_thread_create(ZixThread* thread, -                  size_t     stack_size, -                  void*      (*function)(void*), -                  void*      arg); - -/** -   Join @c thread (block until @c thread exits). -*/ -static inline ZixStatus -zix_thread_join(ZixThread thread, void** retval); - -#ifdef _WIN32 - -static inline ZixStatus -zix_thread_create(ZixThread* thread, -                  size_t     stack_size, -                  void*      (*function)(void*), -                  void*      arg) -{ -	*thread = CreateThread(NULL, stack_size, -	                       (LPTHREAD_START_ROUTINE)function, arg, -	                       0, NULL); -	return *thread ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR; -} - -static inline ZixStatus -zix_thread_join(ZixThread thread, void** retval) -{ -	return WaitForSingleObject(thread, INFINITE) -		? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR; -} - -#else  /* !defined(_WIN32) */ - -static inline ZixStatus -zix_thread_create(ZixThread* thread, -                  size_t     stack_size, -                  void*      (*function)(void*), -                  void*      arg) -{ -	pthread_attr_t attr; -	pthread_attr_init(&attr); -	pthread_attr_setstacksize(&attr, stack_size); - -	const int ret = pthread_create(thread, NULL, function, arg); -	pthread_attr_destroy(&attr); - -	if (ret == EAGAIN) { -		return ZIX_STATUS_NO_MEM; -	} else if (ret == EINVAL) { -		return ZIX_STATUS_BAD_ARG; -	} else if (ret == EPERM) { -		return ZIX_STATUS_BAD_PERMS; -	} else if (ret) { -		return ZIX_STATUS_ERROR; -	} - -	return ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_thread_join(ZixThread thread, void** retval) -{ -	return pthread_join(thread, retval) -		? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -#endif - -/** -   @} -   @} -*/ - -#ifdef __cplusplus -}  /* extern "C" */ -#endif - -#endif  /* ZIX_THREAD_H */  |