diff options
| author | David Robillard <d@drobilla.net> | 2013-02-18 01:51:40 +0000 | 
|---|---|---|
| committer | David Robillard <d@drobilla.net> | 2013-02-18 01:51:40 +0000 | 
| commit | 881da03ad49b987975ba293a4d0a51c561309470 (patch) | |
| tree | 5ae1553dbb1eee0c36cd62f41ac84c47f1e6a985 /plugins/eg03-metro.lv2 | |
| parent | bc8f4e16826abcc9a22466a151e1c4d8daf1a365 (diff) | |
| download | lv2-881da03ad49b987975ba293a4d0a51c561309470.tar.xz | |
Rework metro comments to be a bit more palatable in book form.
Diffstat (limited to 'plugins/eg03-metro.lv2')
| -rw-r--r-- | plugins/eg03-metro.lv2/metro.c | 83 | 
1 files changed, 39 insertions, 44 deletions
diff --git a/plugins/eg03-metro.lv2/metro.c b/plugins/eg03-metro.lv2/metro.c index d2ac982..9048e21 100644 --- a/plugins/eg03-metro.lv2/metro.c +++ b/plugins/eg03-metro.lv2/metro.c @@ -63,7 +63,17 @@ typedef enum {  	STATE_OFF      // Silent  } State; -/** The plugin instance structure: */ +/** +   This plugin must keep track of more state than previous examples to be able +   to render audio.  The basic idea is to generate a single cycle of a sine +   wave which is conceptually played continuously.  The 'tick' is generated by +   enveloping the amplitude so there is a short attack/decay peak around a +   tick, and silence the rest of the time. +    +   This example uses a simple AD envelope with fixed parameters.  A more +   sophisticated implementation might use a more advanced envelope and allow +   the user to modify these parameters, the frequency of the wave, and so on. +   */  typedef struct {  	LV2_URID_Map* map;   // URID map feature  	MetroURIs     uris;  // Cache of mapped URIDs @@ -74,33 +84,20 @@ typedef struct {  		float*             output;  	} ports; -	/** The rate, bpm, and speed are the basic information sent by the host. */ -	double   rate; -	float    bpm; -	float    speed; +	// Variables to keep track of the tempo information sent by the host +	double rate;   // Sample rate +	float  bpm;    // Beats per minute (tempo) +	float  speed;  // Transport speed (usually 0=stop, 1=play) -	/** To keep track of when to play the next click, we need to keep track of -	    a few pieces of information: */ +	uint32_t elapsed_len;  // Frames since the start of the last click +	uint32_t wave_offset;  // Current play offset in the wave +	State    state;        // Current play state -	/** - The frames since the start of the last click is stored */ -	uint32_t elapsed_len; - -	/** - The current play offset in the wave */ -	uint32_t wave_offset; - -	/** - The current play state (attack, decay, or off) */ -	State state; - -	/** The wave to play is a simple sine wave generated at instantiation time -	    based on the sample rate.  The length in frames is stored in order to -	    continuously play the wave in a cycle to avoid discontinuity clicks. */ +	// One cycle of a sine wave  	float*   wave;  	uint32_t wave_len; -	/** The continuously playing sine wave is enveloped to provide an actual -	    metronome tick.  This plugin uses a simple AD envelope with fixed -	    parameters.  A more sophisticated implementation might use a more -	    advanced envelope and allow the user to modify these parameters. */ +	// Envelope parameters  	uint32_t attack_len;  	uint32_t decay_len;  } Metro; @@ -148,7 +145,7 @@ instantiate(const LV2_Descriptor*     descriptor,  		return NULL;  	} -	/** Scan host features for URID map */ +	// Scan host features for URID map  	LV2_URID_Map* map = NULL;  	for (int i = 0; features[i]; ++i) {  		if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { @@ -161,7 +158,7 @@ instantiate(const LV2_Descriptor*     descriptor,  		return NULL;  	} -	/** Map URIS */ +	// Map URIS  	MetroURIs* const uris = &self->uris;  	self->map = map;  	uris->atom_Blank          = map->map(map->handle, LV2_ATOM__Blank); @@ -174,14 +171,14 @@ instantiate(const LV2_Descriptor*     descriptor,  	uris->time_beatsPerMinute = map->map(map->handle, LV2_TIME__beatsPerMinute);  	uris->time_speed          = map->map(map->handle, LV2_TIME__speed); -	/** Initialise fields */ +	// Initialise instance fields  	self->rate       = rate;  	self->bpm        = 120.0f;  	self->attack_len = attack_s * rate;  	self->decay_len  = decay_s * rate;  	self->state      = STATE_OFF; -	/** Generate one cycle of a sine wave at the desired frequency. */ +	// Generate one cycle of a sine wave at the desired frequency  	const double freq = 440.0 * 2.0;  	const double amp  = 0.5;  	self->wave_len = rate / freq; @@ -213,7 +210,7 @@ play(Metro* self, uint32_t begin, uint32_t end)  	for (uint32_t i = begin; i < end; ++i) {  		switch (self->state) {  		case STATE_ATTACK: -			/** Amplitude increases from 0..1 until attack_len */ +			// Amplitude increases from 0..1 until attack_len  			output[i] = self->wave[self->wave_offset] *  				self->elapsed_len / (float)self->attack_len;  			if (self->elapsed_len >= self->attack_len) { @@ -221,7 +218,7 @@ play(Metro* self, uint32_t begin, uint32_t end)  			}  			break;  		case STATE_DECAY: -			/** Amplitude decreases from 1..0 until attack_len + decay_len */ +			// Amplitude decreases from 1..0 until attack_len + decay_len  			output[i] = 0.0f;  			output[i] = self->wave[self->wave_offset] *  				(1 - ((self->elapsed_len - self->attack_len) / @@ -234,10 +231,10 @@ play(Metro* self, uint32_t begin, uint32_t end)  			output[i] = 0.0f;  		} -		/** We continuously play the sine wave regardless of envelope */ +		// We continuously play the sine wave regardless of envelope  		self->wave_offset = (self->wave_offset + 1) % self->wave_len; -		/** Update elapsed time and start attack if necessary */ +		// Update elapsed time and start attack if necessary  		if (++self->elapsed_len == frames_per_beat) {  			self->state       = STATE_ATTACK;  			self->elapsed_len = 0; @@ -250,7 +247,7 @@ update_position(Metro* self, const LV2_Atom_Object* obj)  {  	const MetroURIs* uris = &self->uris; -	/** Received new transport position/speed */ +	// Received new transport position/speed  	LV2_Atom *beat = NULL, *bpm = NULL, *speed = NULL;  	lv2_atom_object_get(obj,  	                    uris->time_barBeat, &beat, @@ -258,18 +255,16 @@ update_position(Metro* self, const LV2_Atom_Object* obj)  	                    uris->time_speed, &speed,  	                    NULL);  	if (bpm && bpm->type == uris->atom_Float) { -		/** Tempo changed, update BPM */ +		// Tempo changed, update BPM  		self->bpm = ((LV2_Atom_Float*)bpm)->body;  	}  	if (speed && speed->type == uris->atom_Float) { -		/** Speed changed, e.g. 0 (stop) to 1 (play) */ +		// Speed changed, e.g. 0 (stop) to 1 (play)  		self->speed = ((LV2_Atom_Float*)speed)->body;  	}  	if (beat && beat->type == uris->atom_Float) { -		/** Received a beat position, synchronise. -		    This is a simple hard sync that may cause clicks. -		    A real plugin would do something more graceful. -		*/ +		// Received a beat position, synchronise +		// This hard sync may cause clicks, a real plugin would be more graceful  		const float frames_per_beat = 60.0f / self->bpm * self->rate;  		const float bar_beats       = ((LV2_Atom_Float*)beat)->body;  		const float beat_beats      = bar_beats - floorf(bar_beats); @@ -290,35 +285,35 @@ run(LV2_Handle instance, uint32_t sample_count)  	Metro*           self = (Metro*)instance;  	const MetroURIs* uris = &self->uris; -	/** Empty notify output for now */ +	// Empty notify output for now  	LV2_Atom_Sequence* notify = self->ports.notify;  	notify->atom.type = self->uris.atom_Sequence;  	notify->atom.size = sizeof(LV2_Atom_Sequence_Body);  	notify->body.unit = notify->body.pad = 0; -	/** Work forwards in time frame by frame, handling events as we go */ +	// Work forwards in time frame by frame, handling events as we go  	const LV2_Atom_Sequence* in     = self->ports.control;  	uint32_t                 last_t = 0;  	for (LV2_Atom_Event* ev = lv2_atom_sequence_begin(&in->body);  	     !lv2_atom_sequence_is_end(&in->body, in->atom.size, ev);  	     ev = lv2_atom_sequence_next(ev)) { -		/** Play the click for the time slice from last_t until now */ +		// Play the click for the time slice from last_t until now  		play(self, last_t, ev->time.frames);  		if (ev->body.type == uris->atom_Blank) {  			const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body;  			if (obj->body.otype == uris->time_Position) { -				/** Received position information, update */ +				// Received position information, update  				update_position(self, obj);  			}  		} -		/** Update time for next iteration and move to next event*/ +		// Update time for next iteration and move to next event  		last_t = ev->time.frames;  	} -	/** Play for remainder of cycle */ +	// Play for remainder of cycle  	play(self, last_t, sample_count);  }  |