aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/eg03-metro.lv2
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/eg03-metro.lv2')
-rw-r--r--plugins/eg03-metro.lv2/metro.c83
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);
}