summaryrefslogtreecommitdiff
path: root/plugins/dumb/dumb-kode54/src/sigtypes/sample.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/dumb/dumb-kode54/src/sigtypes/sample.c')
-rw-r--r--plugins/dumb/dumb-kode54/src/sigtypes/sample.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/plugins/dumb/dumb-kode54/src/sigtypes/sample.c b/plugins/dumb/dumb-kode54/src/sigtypes/sample.c
new file mode 100644
index 00000000..4695de91
--- /dev/null
+++ b/plugins/dumb/dumb-kode54/src/sigtypes/sample.c
@@ -0,0 +1,340 @@
+/* _______ ____ __ ___ ___
+ * \ _ \ \ / \ / \ \ / / ' ' '
+ * | | \ \ | | || | \/ | . .
+ * | | | | | | || ||\ /| |
+ * | | | | | | || || \/ | | ' ' '
+ * | | | | | | || || | | . .
+ * | |_/ / \ \__// || | |
+ * /_______/ynamic \____/niversal /__\ /____\usic /| . . ibliotheque
+ * / \
+ * / . \
+ * sample.c - The sample (SAMP) signal type. / / \ \
+ * | < / \_
+ * By entheh. | \/ /\ /
+ * \_ / > /
+ * This only supports monaural samples. For | \ / /
+ * multiple channels, use multiple samples and | ' /
+ * a combining signal (COMB). \__/
+ */
+
+/* NOTE: filters need not be credited yet, as they will be moved elsewhere. */
+/** WARNING don't forget to move these filters somewhere */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dumb.h"
+
+
+/** WARNING move these things somewhere useful, for DUH writing - this applies to other signal types too */
+#define SIGTYPE_SAMPLE DUMB_ID('S','A','M','P')
+
+
+
+#define SAMPFLAG_16BIT 1 /* sample in file is 16 bit, rather than 8 bit */
+#define SAMPFLAG_LOOP 2 /* loop indefinitely */
+#define SAMPFLAG_XLOOP 4 /* loop x times; only relevant if LOOP not set */
+#define SAMPFLAG_PINGPONG 8 /* loop back and forth, if LOOP or XLOOP set */
+
+
+/* SAMPPARAM_N_LOOPS: add 'value' iterations to the loop. 'value' is assumed
+ * to be positive.
+ */
+#define SAMPPARAM_N_LOOPS 0
+
+
+
+typedef struct SAMPLE_SIGDATA
+{
+ long size;
+ int flags;
+ long loop_start;
+ long loop_end;
+ sample_t *samples;
+}
+SAMPLE_SIGDATA;
+
+
+
+typedef struct SAMPLE_SIGRENDERER
+{
+ SAMPLE_SIGDATA *sigdata;
+ int n_channels;
+ DUMB_RESAMPLER r;
+ int n_loops;
+}
+SAMPLE_SIGRENDERER;
+
+
+
+static sigdata_t *sample_load_sigdata(DUH *duh, DUMBFILE *file)
+{
+ SAMPLE_SIGDATA *sigdata;
+ long size;
+ long n;
+ int flags;
+
+ (void)duh;
+
+ size = dumbfile_igetl(file);
+
+ if (dumbfile_error(file) || size <= 0)
+ return NULL;
+
+ flags = dumbfile_getc(file);
+ if (flags < 0)
+ return NULL;
+
+ sigdata = malloc(sizeof(*sigdata));
+ if (!sigdata)
+ return NULL;
+
+ sigdata->samples = malloc(size * sizeof(sample_t));
+ if (!sigdata->samples) {
+ free(sigdata);
+ return NULL;
+ }
+
+ sigdata->size = size;
+ sigdata->flags = flags;
+
+ if (sigdata->flags & (SAMPFLAG_LOOP | SAMPFLAG_XLOOP)) {
+ sigdata->loop_start = dumbfile_igetl(file);
+
+ if (dumbfile_error(file) || (unsigned long)sigdata->loop_start >= (unsigned long)size) {
+ free(sigdata->samples);
+ free(sigdata);
+ return NULL;
+ }
+
+ if (sigdata->flags & SAMPFLAG_LOOP)
+ sigdata->loop_end = size;
+ else {
+ sigdata->loop_end = dumbfile_igetl(file);
+
+ if (dumbfile_error(file) || sigdata->loop_end <= sigdata->loop_start || sigdata->loop_end > size) {
+ free(sigdata->samples);
+ free(sigdata);
+ return NULL;
+ }
+ }
+ } else {
+ sigdata->loop_start = 0;
+ sigdata->loop_end = size;
+ }
+
+ if (sigdata->flags & SAMPFLAG_16BIT) {
+ for (n = 0; n < size; n++) {
+ int m = dumbfile_igetw(file);
+ if (m < 0) {
+ free(sigdata->samples);
+ free(sigdata);
+ return NULL;
+ }
+ sigdata->samples[n] = (int)(signed short)m << 8;
+ }
+ } else {
+ for (n = 0; n < size; n++) {
+ int m = dumbfile_getc(file);
+ if (m < 0) {
+ free(sigdata->samples);
+ free(sigdata);
+ return NULL;
+ }
+ sigdata->samples[n] = (int)(signed char)m << 16;
+ }
+ }
+
+ return sigdata;
+}
+
+
+
+static void sample_pickup(DUMB_RESAMPLER *r, void *data)
+{
+ SAMPLE_SIGRENDERER *sigrenderer = data;
+
+ if (!(sigrenderer->sigdata->flags & (SAMPFLAG_LOOP | SAMPFLAG_XLOOP))) {
+ r->dir = 0;
+ return;
+ }
+
+ if (!(sigrenderer->sigdata->flags & SAMPFLAG_LOOP) && sigrenderer->n_loops == 0) {
+ r->dir = 0;
+ return;
+ }
+
+ if (sigrenderer->sigdata->flags & SAMPFLAG_PINGPONG) {
+ if (r->dir < 0) {
+ r->pos = (r->start << 1) - 1 - r->pos;
+ r->subpos ^= 65535;
+ r->dir = 1;
+ } else {
+ r->pos = (r->end << 1) - 1 - r->pos;
+ r->subpos ^= 65535;
+ r->dir = -1;
+ }
+ } else
+ r->pos -= r->end - r->start;
+
+ if (!(sigrenderer->sigdata->flags & SAMPFLAG_LOOP)) {
+ if (sigrenderer->n_loops > 0) {
+ sigrenderer->n_loops--;
+ if (sigrenderer->n_loops == 0) {
+ r->start = 0;
+ r->end = sigrenderer->sigdata->size;
+ }
+ }
+ }
+}
+
+
+
+static sigrenderer_t *sample_start_sigrenderer(DUH *duh, sigdata_t *data, int n_channels, long pos)
+{
+ SAMPLE_SIGDATA *sigdata = data;
+ SAMPLE_SIGRENDERER *sigrenderer;
+
+ (void)duh;
+
+ sigrenderer = malloc(sizeof(*sigrenderer));
+ if (!sigrenderer) return NULL;
+
+ sigrenderer->sigdata = data;
+ sigrenderer->n_channels = n_channels;
+ dumb_reset_resampler(&sigrenderer->r, sigdata->samples, pos, 0, sigdata->size);
+ sigrenderer->r.pickup = &sample_pickup;
+ sigrenderer->r.pickup_data = sigrenderer;
+ sigrenderer->n_loops = 0;
+
+ return sigrenderer;
+}
+
+
+
+#if 0
+/* The name says it all ;-) */
+static void sample_cheap_low_pass_filter(sample *src, long size, float max_freq) {
+
+ long i;
+ float fact = max_freq / 44100.0f;
+
+ for (i = 0; i < size-1; i++) {
+ float d = src[i+1] - src[i];
+ if (d > fact)
+ src[i+1] += fact - d;
+ else if (d < -fact)
+ src[i+1] += -d - fact;
+ }
+
+ return;
+}
+
+
+
+/* Dithering with noise shaping filter. Set shape = 0 for no shaping. */
+static void sample_dither_filter(float *src, long size, float shape) {
+ float r1 = 0, r2 = 0;
+ float s1 = 0, s2 = 0; /* Feedback buffer */
+ float o = 0.5f / 255;
+ float tmp;
+ int i;
+
+ for (i = 0; i < size; i++) {
+ r2 = r1;
+ r1 = rand() / (float)RAND_MAX;
+
+ tmp = src[i] + shape * (s1 + s1 - s2);
+ src[i] = tmp + o * (r1 - r2);
+ src[i] = MID(-1.0f, src[i], 1.0f);
+
+ s2 = s1;
+ s1 = tmp - src[i];
+ }
+ return;
+}
+#endif
+
+
+
+static void sample_sigrenderer_set_sigparam(sigrenderer_t *data, unsigned char id, long value)
+{
+ SAMPLE_SIGRENDERER *sigrenderer = data;
+
+ if (id == SAMPPARAM_N_LOOPS) {
+ if ((sigrenderer->sigdata->flags & (SAMPFLAG_LOOP | SAMPFLAG_XLOOP)) == SAMPFLAG_XLOOP) {
+ sigrenderer->n_loops += value;
+ sigrenderer->r.start = sigrenderer->n_loops ? sigrenderer->sigdata->loop_start : 0;
+ sigrenderer->r.end = sigrenderer->n_loops ? sigrenderer->sigdata->loop_end : sigrenderer->sigdata->size;
+ }
+ }
+}
+
+
+
+static long sample_sigrenderer_get_samples(
+ sigrenderer_t *data,
+ float volume, float delta,
+ long size, sample_t **samples
+)
+{
+ SAMPLE_SIGRENDERER *sigrenderer = data;
+
+ DUMB_RESAMPLER initial_r = sigrenderer->r;
+
+ long s = dumb_resample(&sigrenderer->r, samples[0], size, volume, delta);
+
+ int n;
+ for (n = 1; n < sigrenderer->n_channels; n++) {
+ sigrenderer->r = initial_r;
+ dumb_resample(&sigrenderer->r, samples[n], size, volume, delta);
+ }
+
+ return s;
+}
+
+
+
+static void sample_sigrenderer_get_current_sample(sigrenderer_t *data, float volume, sample_t *samples)
+{
+ SAMPLE_SIGRENDERER *sigrenderer = data;
+ int n;
+ for (n = 0; n < sigrenderer->n_channels; n++)
+ samples[n] = dumb_resample_get_current_sample(&sigrenderer->r, volume);
+}
+
+
+
+static void sample_end_sigrenderer(sigrenderer_t *sigrenderer)
+{
+ free(sigrenderer);
+}
+
+
+
+static void sample_unload_sigdata(sigdata_t *data)
+{
+ SAMPLE_SIGDATA *sigdata = data;
+ free(sigdata->samples);
+ free(data);
+}
+
+
+
+static DUH_SIGTYPE_DESC sigtype_sample = {
+ SIGTYPE_SAMPLE,
+ &sample_load_sigdata,
+ &sample_start_sigrenderer,
+ &sample_sigrenderer_set_sigparam,
+ &sample_sigrenderer_get_samples,
+ &sample_sigrenderer_get_current_sample,
+ &sample_end_sigrenderer,
+ &sample_unload_sigdata
+};
+
+
+
+void dumb_register_sigtype_sample(void)
+{
+ dumb_register_sigtype(&sigtype_sample);
+}