summaryrefslogtreecommitdiff
path: root/plugins/dumb/dumb-kode54/src/sigtypes/sequence.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/dumb/dumb-kode54/src/sigtypes/sequence.c')
-rw-r--r--plugins/dumb/dumb-kode54/src/sigtypes/sequence.c592
1 files changed, 592 insertions, 0 deletions
diff --git a/plugins/dumb/dumb-kode54/src/sigtypes/sequence.c b/plugins/dumb/dumb-kode54/src/sigtypes/sequence.c
new file mode 100644
index 00000000..cfade4d6
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/sigtypes/sequence.c
@@ -0,0 +1,592 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * sequence.c - The sequence (SEQU) signal type. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * | \ / /
+ * | ' /
+ * \__/
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+
+
+
+#define SIGTYPE_SEQUENCE DUMB_ID('S','E','Q','U')
+
+
+
+/* We have 256 intervals per semitone, 12 * 256 per octave
+ 2 ** (1 / (12 * 256)) = 1.000225659305069791926712241547647863626
+
+ pow(DUMB_PITCH_BASE, x) = 1.5
+ x = log2(1.5) / log2(DUMB_PITCH_BASE)
+ x = log2(1.5) * 12 * 256
+ x = 1797.004802
+ cf.
+ x = 7 * 256 = 1792
+
+ so, for the perfect fifth temperament, use an interval of 1797.
+*/
+
+
+
+/* Sequencing format
+ * -----------------
+ *
+ * NOTE: A LOT OF THIS IS NOW REDUNDANT. PLEASE REFER TO duhspecs.txt.
+ *
+ * When a signal is initiated, it claims a reference number. If any other
+ * currently playing signal has the same number, that signal becomes
+ * anonymous and inaccessible; that is, if multiple signals were initiated
+ * with the same reference, the reference belongs to the most recent.
+ *
+ * Signals can be stopped, or have their pitch, volume or parameters changed,
+ * using the reference number. A signal may stop prematurely if it runs out
+ * of data, in which case the reference number becomes void, and operations
+ * on it will be ignored. Such a situation will not flag any kind of warning,
+ * since it may be the result of inaccuracies when resampling.
+ *
+ * The sequence consists of a series of commands. All commands begin with a
+ * long int, which is the time to wait after the last command (or the
+ * beginning of the sequence) before executing this command. 65536 represents
+ * one second. A time of -1 (or in fact any negative time) terminates the
+ * sequence, but any currently playing signals will continue until they run
+ * out. Make sure no non-terminating signals are playing when the sequence
+ * ends!
+ *
+ * The time, if nonnegative, is followed by one byte indicating the type of
+ * command. This byte can have the following values:
+ *
+ * SEQUENCE_START_SIGNAL
+ * unsigned char ref; - Reference. Need more than 256? Use two sequences,
+ * and get your brain seen to.
+ * int sig; - The index of the signal to initiate.
+ * long pos; - The position at which to start. 65536 represents one second.
+ * unsigned short volume; - Volume. 65535 represents the maximum volume, so
+ * you will want to go lower than this if you are
+ * playing more than one signal at once.
+ * signed short pitch; - Pitch. 0 represents a frequency of 65536 Hz. Scale
+ * is logarithmic. Add 256 to increase pitch by one
+ * semitone in the even temperament, or add 12*256 to
+ * increase pitch by one octave in any temperament
+ * (i.e. double the frequency).
+ *
+ * SEQUENCE_SET_VOLUME
+ * unsigned char ref;
+ * unsigned short volume;
+ *
+ * SEQUENCE_SET_PITCH
+ * unsigned char ref;
+ * signed short pitch;
+ *
+ * SEQUENCE_SET_PARAMETER
+ * unsigned char ref;
+ * unsigned char id;
+ * long value;
+ * - see the description of the set_parameter function pointer for
+ * information on 'id' and 'value'.
+ *
+ * SEQUENCE_STOP_SIGNAL
+ * unsigned char ref;
+ */
+
+
+
+#define SEQUENCE_START_SIGNAL 0
+#define SEQUENCE_SET_VOLUME 1
+#define SEQUENCE_SET_PITCH 2
+#define SEQUENCE_SET_PARAMETER 3
+#define SEQUENCE_STOP_SIGNAL 4
+
+
+
+typedef struct SEQUENCE_PLAYING
+{
+ struct SEQUENCE_PLAYING *next;
+
+ DUH_SIGNAL_SAMPINFO *sampinfo;
+
+ int ref;
+
+ int pitch;
+ int volume;
+}
+SEQUENCE_PLAYING;
+
+
+
+typedef struct SEQUENCE_SAMPINFO
+{
+ DUH *duh;
+
+ int n_channels;
+
+ unsigned char *signal;
+
+ long time_left;
+ int sub_time_left;
+
+ SEQUENCE_PLAYING *playing;
+}
+SEQUENCE_SAMPINFO;
+
+
+
+#define sequence_c(signal) ((int)*((*(signal))++))
+
+
+static int sequence_w(unsigned char **signal)
+{
+ int v = (*signal)[0] | ((*signal)[1] << 8);
+ *signal += 2;
+ return v;
+}
+
+
+static long sequence_l(unsigned char **signal)
+{
+ long v = (*signal)[0] | ((*signal)[1] << 8) | ((*signal)[2] << 16) | ((*signal)[3] << 24);
+ *signal += 4;
+ return v;
+}
+
+
+static long sequence_cl(unsigned char **signal)
+{
+ long v = sequence_c(signal);
+ if (v & 0x80) {
+ v &= 0x7F;
+ v |= sequence_c(signal) << 7;
+ if (v & 0x4000) {
+ v &= 0x3FFF;
+ v |= sequence_c(signal) << 14;
+ if (v & 0x200000) {
+ v &= 0x1FFFFF;
+ v |= sequence_c(signal) << 21;
+ if (v & 0x10000000) {
+ v &= 0x0FFFFFFF;
+ v |= sequence_c(signal) << 28;
+ }
+ }
+ }
+ }
+ return v;
+}
+
+
+
+static void *sequence_load_signal(DUH *duh, DUMBFILE *file)
+{
+ long size;
+ unsigned char *signal;
+
+ (void)duh;
+
+ size = dumbfile_igetl(file);
+ if (dumbfile_error(file) || size <= 0)
+ return NULL;
+
+ signal = malloc(size);
+ if (!signal)
+ return NULL;
+
+ if (dumbfile_getnc((char *)signal, size, file) < size) {
+ free(signal);
+ return NULL;
+ }
+
+ return signal;
+}
+
+
+
+static long render(
+ SEQUENCE_SAMPINFO *sampinfo,
+ float volume, float delta,
+ long pos, long size, sample_t **samples
+)
+{
+ sample_t **splptr;
+
+ SEQUENCE_PLAYING **playing_p = &sampinfo->playing;
+
+ long max_size = 0;
+ long part_size;
+
+ int n;
+ long i;
+
+ for (n = 0; n < sampinfo->n_channels; n++)
+ memset(samples[n] + pos, 0, size * sizeof(sample_t));
+
+ splptr = malloc(sampinfo->n_channels * sizeof(*splptr));
+ if (!splptr)
+ return 0;
+
+ splptr[0] = malloc(sampinfo->n_channels * size * sizeof(sample_t));
+ if (!splptr[0]) {
+ free(splptr);
+ return 0;
+ }
+
+ for (n = 1; n < sampinfo->n_channels; n++)
+ splptr[n] = splptr[n - 1] + size;
+
+ while (*playing_p) {
+ SEQUENCE_PLAYING *playing = *playing_p;
+
+ part_size = duh_signal_render_samples(
+ playing->sampinfo,
+ volume * (float)playing->volume * (1.0f / 65535.0f),
+ (float)(pow(DUMB_PITCH_BASE, playing->pitch) * delta),
+ size, splptr
+ );
+
+ for (n = 0; n < sampinfo->n_channels; n++)
+ for (i = 0; i < part_size; i++)
+ samples[n][pos+i] += splptr[n][i];
+
+ if (part_size > max_size)
+ max_size = part_size;
+
+ if (part_size < size) {
+ *playing_p = playing->next;
+ duh_signal_end_samples(playing->sampinfo);
+ free(playing);
+ } else
+ playing_p = &playing->next;
+ }
+
+ free(splptr[0]);
+ free(splptr);
+
+ return max_size;
+}
+
+
+
+/* 'offset' is added to the position at which the signal should start. It is
+ * currently assumed to be positive, and is currently only used when seeking
+ * forwards in the sequence.
+ */
+static void sequence_command(SEQUENCE_SAMPINFO *sampinfo, long offset)
+{
+ int command = sequence_c(&sampinfo->signal);
+
+ if (command == SEQUENCE_START_SIGNAL) {
+
+ int ref = sequence_c(&sampinfo->signal);
+ int sig = sequence_cl(&sampinfo->signal);
+ long pos = sequence_cl(&sampinfo->signal);
+ int volume = sequence_w(&sampinfo->signal);
+ int pitch = (int)(signed short)sequence_w(&sampinfo->signal);
+
+ SEQUENCE_PLAYING *playing = sampinfo->playing;
+
+ while (playing) {
+ if (playing->ref == ref)
+ playing->ref = -1;
+ playing = playing->next;
+ }
+
+ playing = malloc(sizeof(SEQUENCE_PLAYING));
+
+ if (playing) {
+ playing->sampinfo = duh_signal_start_samples(sampinfo->duh, sig, sampinfo->n_channels, pos + offset);
+
+ if (playing->sampinfo) {
+ playing->ref = ref;
+ playing->pitch = pitch;
+ playing->volume = volume;
+
+ playing->next = sampinfo->playing;
+ sampinfo->playing = playing;
+ } else
+ free(playing);
+ }
+
+ } else if (command == SEQUENCE_SET_VOLUME) {
+
+ int ref = sequence_c(&sampinfo->signal);
+ int volume = sequence_w(&sampinfo->signal);
+
+ SEQUENCE_PLAYING *playing = sampinfo->playing;
+
+ while (playing) {
+ if (playing->ref == ref) {
+ playing->volume = volume;
+ break;
+ }
+ playing = playing->next;
+ }
+
+ } else if (command == SEQUENCE_SET_PITCH) {
+
+ int ref = sequence_c(&sampinfo->signal);
+ int pitch = (int)(signed short)sequence_w(&sampinfo->signal);
+
+ SEQUENCE_PLAYING *playing = sampinfo->playing;
+
+ while (playing) {
+ if (playing->ref == ref) {
+ playing->pitch = pitch;
+ break;
+ }
+ playing = playing->next;
+ }
+
+ } else if (command == SEQUENCE_SET_PARAMETER) {
+
+ int ref = sequence_c(&sampinfo->signal);
+ unsigned char id = sequence_c(&sampinfo->signal);
+ long value = sequence_l(&sampinfo->signal);
+
+ SEQUENCE_PLAYING *playing = sampinfo->playing;
+
+ while (playing) {
+ if (playing->ref == ref) {
+ duh_signal_set_parameter(playing->sampinfo, id, value);
+ break;
+ }
+ playing = playing->next;
+ }
+
+ } else if (command == SEQUENCE_STOP_SIGNAL) {
+
+ int ref = sequence_c(&sampinfo->signal);
+
+ SEQUENCE_PLAYING **playing_p = &sampinfo->playing;
+
+ while (*playing_p) {
+ SEQUENCE_PLAYING *playing = *playing_p;
+
+ if (playing->ref == ref) {
+ duh_signal_end_samples(playing->sampinfo);
+ *playing_p = playing->next;
+ free(playing);
+ break;
+ }
+
+ playing_p = &playing->next;
+ }
+
+ } else {
+
+ TRACE("Error in sequence: unknown command %d.\n", command);
+ sampinfo->signal = NULL;
+
+ }
+}
+
+
+
+static void *sequence_start_samples(DUH *duh, void *signal, int n_channels, long pos)
+{
+ SEQUENCE_SAMPINFO *sampinfo;
+ long time = sequence_cl((unsigned char **)&signal);
+
+ if (time < 0)
+ return NULL;
+
+ sampinfo = malloc(sizeof(SEQUENCE_SAMPINFO));
+ if (!sampinfo)
+ return NULL;
+
+ sampinfo->duh = duh;
+ sampinfo->n_channels = n_channels;
+ sampinfo->signal = signal;
+ sampinfo->playing = NULL;
+
+ /* Seek to 'pos'. */
+ while (time < pos) {
+ pos -= time;
+
+ sequence_command(sampinfo, pos);
+
+ time = sequence_cl(&sampinfo->signal);
+
+ if (time < 0) {
+ sampinfo->signal = NULL;
+ return sampinfo;
+ }
+ }
+
+ sampinfo->time_left = time - pos;
+ sampinfo->sub_time_left = 0;
+
+ return sampinfo;
+}
+
+
+
+static long sequence_render_samples(
+ void *sampinfo,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+
+#define sampinfo ((SEQUENCE_SAMPINFO *)sampinfo)
+
+ long pos = 0;
+
+ int dt = (int)(delta * 65536.0f + 0.5f);
+
+ long todo;
+ LONG_LONG t;
+
+ while (sampinfo->signal) {
+ todo = (long)((((LONG_LONG)sampinfo->time_left << 16) | sampinfo->sub_time_left) / dt);
+
+ if (todo >= size)
+ break;
+
+ if (todo) {
+ render(sampinfo, volume, delta, pos, todo, samples);
+
+ pos += todo;
+ size -= todo;
+
+ todo = (long)((((LONG_LONG)sampinfo->time_left << 16) | sampinfo->sub_time_left) / dt);
+ t = sampinfo->sub_time_left - (LONG_LONG)todo * dt;
+ sampinfo->sub_time_left = (long)t & 65535;
+ sampinfo->time_left += (long)(t >> 16);
+ }
+
+ sequence_command(sampinfo, 0);
+
+ todo = sequence_cl(&sampinfo->signal);
+
+ if (todo >= 0)
+ sampinfo->time_left += todo;
+ else
+ sampinfo->signal = NULL;
+ }
+
+ if (sampinfo->signal) {
+ render(sampinfo, volume, delta, pos, size, samples);
+
+ pos += size;
+
+ t = sampinfo->sub_time_left - (LONG_LONG)size * dt;
+ sampinfo->sub_time_left = (long)t & 65535;
+ sampinfo->time_left += (long)(t >> 16);
+ } else
+ pos += render(sampinfo, volume, delta, pos, size, samples);
+
+ return pos;
+
+
+/** WARNING - remove this... */
+#if 0
+ float size_unified = size * delta;
+
+ int n;
+
+ sample_t **samples2 = malloc(sampinfo->n_channels * sizeof(*samples2));
+ memcpy(samples2, samples, sampinfo->n_channels * sizeof(*samples2));
+
+ while (sampinfo->signal && sampinfo->time < size_unified) {
+
+ {
+ long sz = (long)(sampinfo->time / delta);
+
+ if (sz)
+ render(sampinfo, volume, delta, sz, samples2);
+
+ for (n = 0; n < sampinfo->n_channels; n++)
+ samples2[n] += sz;
+
+ size -= sz;
+ pos += sz;
+
+ sampinfo->time -= sz * delta;
+ }
+
+ sequence_command(sampinfo, 0);
+
+ {
+ long time = sequence_cl(&sampinfo->signal);
+
+ if (time >= 0)
+ sampinfo->time += (float)time;
+ else
+ sampinfo->signal = NULL;
+ }
+
+ size_unified = size * delta;
+ }
+
+ if (sampinfo->signal) {
+ render(sampinfo, volume, delta, size, samples2);
+ sampinfo->time -= size_unified;
+ pos += size;
+ } else
+ pos += render(sampinfo, volume, delta, size, samples2);
+
+ free(samples2);
+
+ return pos;
+#endif
+
+#undef sampinfo
+
+}
+
+
+
+static void sequence_end_samples(void *sampinfo)
+{
+ SEQUENCE_PLAYING *playing = ((SEQUENCE_SAMPINFO *)sampinfo)->playing;
+
+ while (playing) {
+ SEQUENCE_PLAYING *next = playing->next;
+
+ duh_signal_end_samples(playing->sampinfo);
+ free(playing);
+
+ playing = next;
+ }
+
+ free(sampinfo);
+}
+
+
+
+static void sequence_unload_signal(void *signal)
+{
+ free(signal);
+}
+
+
+
+static DUH_SIGTYPE_DESC sigtype_sequence = {
+ SIGTYPE_SEQUENCE,
+ &sequence_load_signal,
+ &sequence_start_samples,
+ NULL,
+ &sequence_render_samples,
+ &sequence_end_samples,
+ &sequence_unload_signal
+};
+
+
+
+void dumb_register_sigtype_sequence(void)
+{
+ dumb_register_sigtype(&sigtype_sequence);
+}