GEN routines

GEN routines

Overview

This document outlines a set of useful GEN routines.

What exactly is a GEN routine?

A GEN routine is used to describe a function that works on table (a SKFLT array in C). These are usually used to generate wavetables.

The term "GEN routine" (yes, all caps), is borrowed from Csound and MUSIC-N languages as GENerators for function tables, affectionately referred to as "f-tables", which are essentially just "tables" in the sndkit world.

Tangled Files

gen.c and gen.h.

<<gen.h>>=
#ifndef SK_GEN_SINE
#define SK_GEN_SINE

#ifndef SKFLT
#define SKFLT float
#endif

<<funcdefs>>
#endif

<<gen.c>>=
#include <math.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "gen.h"

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

<<funcs>>

Sine

The function sk_gen_sine generates a sampled period of a sine wave.

<<funcdefs>>=
void sk_gen_sine(SKFLT *tab, unsigned long sz);

<<funcs>>=
void sk_gen_sine(SKFLT *tab, unsigned long sz)
{
    int i;
    SKFLT step;

    if (sz <= 0) return;

    step = 2 * M_PI / sz;

    for (i = 0; i < sz; i++) {
        tab[i] = sin(i * step);
    }
}

Saw

Samples a single period of a simple sawtooth waveform. Note that this will produce aliasing artifacts. For specialized sawtooth oscillators with alias supression, consider using blep.

<<funcdefs>>=
void sk_gen_saw(SKFLT *tab, unsigned long sz);

<<funcs>>=
void sk_gen_saw(SKFLT *tab, unsigned long sz)
{
    int i;
    SKFLT step;

    if (sz <= 0) return;

    step = 1.0 / sz;

    for (i = 0; i < sz; i++) {
        tab[i] = 2.0*(i*step) - 1.0;
    }
}

String Values

Generates a table from a string of values separated by spaces. Extracted from the Soundpipe library.

This will actually re-allocate the table if need be, which is why the arguments are pointers.

<<funcdefs>>=
void sk_gen_vals(SKFLT **ptab, int *psz, const char *str);

<<funcs>>=
static char * tokenize(char **next, int *size)
{
    char *token;
    char *str;
    char *peak;

    if (*size <= 0) return NULL;
    token = *next;
    str = *next;

    peak = str + 1;

    while ((*size)--) {
        if (*str == ' ') {
            *str = 0;
            if (*peak != ' ') break;
        }
        str = str + 1;
        peak = str + 1;
    }
    *next = peak;
    return token;
}

void sk_gen_vals(SKFLT **ptab, int *psz, const char *string)
{
    int size;
    char *str;
    char *out;
    char *ptr;
    int j;
    int sz;
    SKFLT *tab;

    size = strlen(string);
    str = malloc(sizeof(char) * size + 1);
    strcpy(str, string);
    ptr = str;
    j = 0;

    tab = *ptab;
    sz = *psz;

    while (size > 0) {
        out = tokenize(&str, &size);
        if (sz < j + 1) {
            tab = realloc(tab, sizeof(SKFLT) * (sz + 2));
            /* zero out new tables */
            tab[sz] = 0;
            tab[sz + 1] = 0;
            sz++;
        }
        tab[j] = atof(out);
        j++;
    }

    *ptab = tab;
    *psz = sz;
    free(ptr);
}

Sine Sum

<<funcdefs>>=
void sk_gen_sinesum(SKFLT *tab,
                    int sz,
                    const char *argstring,
                    int normalize);

<<funcs>>=
void sk_gen_sinesum(SKFLT *tab,
                    int sz,
                    const char *argstring,
                    int normalize)
{
    SKFLT *args;
    int argsz;
    int phs;
    SKFLT amp;
    int flen;
    SKFLT tpdlen;
    int i, n;
    SKFLT ampsum;

    args = malloc(sizeof(SKFLT));
    args[0] = 0;
    argsz = 1;

    ampsum = 0;

    sk_gen_vals(&args, &argsz, argstring);
    flen = sz;
    tpdlen = 2.0 * M_PI / (SKFLT) flen;

    for (i = argsz; i > 0; i--) {
        amp = args[i - 1];
        if (amp > 0) {
            ampsum += amp;
            for (phs = 0, n = 0; n < sz; n++) {
                tab[n] += sin(phs * tpdlen) * amp;
                phs += i;
                phs %= flen;
            }
        }
    }

    if (normalize) {
        SKFLT norm;
        norm = 1.0 / ampsum;
        for (n = 0; n < sz; n++) {
            tab[n] *= norm;
        }
    }

    free(args);
}