This is fugue.c in view mode; [Download] [Up]
/*
* FILE: fugue.c
* BY: Christopher Lee Fraley (cf0v@spice.cs.cmu.edu)
* DESC: Basic sound manipulation functions for an X-Lisp sound type.
*
* 1.0 (13-FEB-89) - Created from "sound.c". Moved all of the sound
* manipulation functions here, leaving the primative
* constructors and desctructors in "sound.c", as well
* as a few other misc. primitives, such as s_copy(),
* s_flatten(), etc. (cf0v)
* 1.1 (16-FEB-89) - I noticed I make the assumption that (int)double (i.e.
* type-casting a double into an integer) does the same
* as (int)(floor(double)). That is, type casting must
* not round, but should just drop the fractional part.
* 1.2 (25-FEB-89) - MidiToHz() added. (cf0v)
* 1.3 (20-APR-89) - logicalTo() added. (cf0v)
* 1.4 ( 5-MAY-89) - renamed MidiToHz() to step_to_hz(); added instrument key
* to s_osc(). (cf0v)
* (29-OCT-89) - added s_from, s_dur, s_to, s_logicalto (rbd)
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <math.h>
#include "xlisp.h"
#include "sound.h"
#include "fugue.h"
/* compute_phase -- given a phase in radians, a wavetable specified as
* the nominal pitch (in half steps), the table length, and the sample
* rate, compute the sample number corresponding to the phase. This
* routine makes it easy to initialize the table pointer at the beginning
* of various oscillator implementations in Fugue. Note that the table
* may represent several periods, in which case phase 2PI is not the same
* as 0. Also note that the phase increment is also computed and returned
* through incr_ptr.
*/
double compute_phase(phase, key, n, srate, new_srate, freq, incr_ptr)
double phase; /* phase in radians */
double key; /* the semitone number of the table played at srate */
long n; /* number of samples */
double srate; /* the sample rate of the table */
double new_srate; /* sample rate of the result */
double freq; /* the desired frequency */
double *incr_ptr; /* the sample increment */
{
double period = 1.0 / step_to_hz(key);
/* convert phase to sample units */
phase = srate * period * (phase / (double) ANGLEBASE);
/* phase is now in sample units; if phase is less than zero, then increase
it by some number of sLength's to make it positive:
*/
if (phase < 0)
phase += (((int) ((-phase) / n)) + 1) * n;
/* if phase is longer than the sample length, wrap it by subtracting the
integer part of the division by sLength:
*/
if (phase > n)
phase -= ((int) (phase / n)) * n;
/* Now figure the phase increment: to reproduce original pitch
required incr = srate / new_srate. To get the new frequency,
scale by freq / nominal_freq = freq * period:
*/
*incr_ptr = (srate / new_srate) * freq * period;
return phase;
}
double step_to_hz(pitch)
double pitch;
{
return (16.351597830 * pow(2.0, pitch / 12.0));
}
double hz_to_step(hz)
double hz;
{
return 57.0 + (12 / log(2.0)) * (log(hz) - log(440.0));
}
double s_logicalTo(s)
SoundPtr s;
{
return (s->logicalTo);
}
SoundPtr s_setLogicalTo(t, s)
double t;
SoundPtr s;
{
s = s_copy(s);
s->logicalTo = t;
return (s);
}
float s_maxSample(s1)
SoundPtr s1;
{
SDataType max;
SDataPtr data, end;
SamplePtr sample;
switch (s1->tag)
{
case SUMNODES:
(void) s_flatten(s1);
/* FALL-THROUGH TO SAMPLES: */
case SAMPLES:
sample = s1->ptr.sample;
data = sample->data + 1 + (int)(s1->from * sample->srate + 0.5);
data = MAX(data, sample->data);
end = sample->data + 1 + (int)(s1->to * sample->srate + 0.5);
end = MIN(end, sample->data + sample->length);
max = 0;
for (; data < end; data++)
max = MAX(max, ABS(*data));
return (max * s1->scale);
case SILENCE:
default:
break;
}
return (-1.0);
}
SoundPtr s_clip(s1, from, to)
SoundPtr s1;
double from, to;
{
SoundPtr s;
s = s_copy(s1);
s->from = MAX(s1->from, from);
s->to = MIN(s1->to, to);
if (s->from >= s->to)
s_silence(s);
return (s);
}
SoundPtr s_lclip(s1, deltaFrom)
SoundPtr s1;
double deltaFrom;
{
SoundPtr s;
if (deltaFrom <= 0.0)
return (s1);
s = s_copy(s1);
s->from += deltaFrom;
if (s->from >= s->to)
s_silence(s);
return (s);
}
SoundPtr s_rclip(s1, deltaTo)
SoundPtr s1;
double deltaTo;
{
SoundPtr s;
if (deltaTo <= 0.0)
return (s1);
s = s_copy(s1);
s->to -= deltaTo;
if (s->from >= s->to)
s_silence(s);
return (s);
}
SoundPtr s_add(s1, s2)
SoundPtr s1, s2;
{
SoundPtr s;
int valid1, valid2;
double beg, end, beg2, end2;
valid1 = extent(s1, &beg, &end);
valid2 = extent(s2, &beg2, &end2);
beg = MIN(beg, beg2);
end = MAX(end, end2);
if (!valid1)
return (s2);
else if (!valid2)
return (s1);
s = s_create();
s->from = beg;
s->to = end;
s->logicalTo = MAX(s1->logicalTo, s2->logicalTo);
s->srate = MAX(s1->srate, s2->srate);
s->tag = SUMNODES;
s->ptr.node = n_create(s1);
s->ptr.node->next = n_create(s2);
return (s);
}
SoundPtr s_scale(s1, f)
SoundPtr s1;
double f;
{
SoundPtr s;
s = s_copy(s1);
s->scale *= f;
if (f == 0.0)
s_silence(s);
return (s);
}
SoundPtr s_shift(s1, f)
SoundPtr s1;
double f;
{
SoundPtr s;
s = s_copy(s1);
s->shift += f;
s->logicalTo += f;
return (s);
}
SoundPtr s_stretch(s1, f)
SoundPtr s1;
double f;
{
SoundPtr s;
s = s_copy(s1);
s->stretch *= f;
s->logicalTo *= f;
if (f == 0.0)
s_silence(s);
return (s);
}
float s_from(s)
SoundPtr s;
{
return s->from;
}
float s_to(s)
SoundPtr s;
{
return s->to;
}
float s_dur(s)
SoundPtr s;
{
return s->to - s->from;
}
/* s_samples -- return samples in a lisp array */
/**/
LVAL s_samples(s, limit)
SoundPtr s;
int limit;
{
int len;
SDataPtr data;
LVAL v;
int i;
int valid;
double minimum, maximum;
if (s->tag != SAMPLES) {
xlerror("expected flattened samples", s);
}
valid = extent(s, &minimum, &maximum);
if (!valid) return newvector(0);
/* protect the pointer to the vector */
xlsave1(v);
len = (maximum - minimum) * s->srate + 0.5;
if (len > limit) len = limit;
data = locate_sample(s, minimum);
v = newvector(len);
for (i = 0; i < len; i++) {
setelement(v, i, cvflonum(data[i]));
}
/* restore the stack */
xlpop();
return v;
}
/* show_indent -- indentation level for s_stats */
int show_indent = 0;
/* indent -- print spaces to indent by stat_indent */
int indent()
{
int i;
for (i = 0; i < show_indent; i++) printf(" ");
}
char *tag_name[] = {"SILENCE", "SAMPLES", "SUMNODES"};
/* s_show -- print sound structure for debugging */
/**/
SoundPtr s_show(s)
SoundPtr s;
{
indent();
printf("{%x} tag:%s\n", s, tag_name[s->tag]);
indent();
printf(" from:%g to:%g shift:%g stretch:%g\n",
s->from, s->to, s->shift, s->stretch);
indent();
printf(" logTo:%g scale:%g srate:%g refCount:%d\n",
s->logicalTo, s->scale, s->srate, s->refCount);
if (s->tag == SAMPLES) {
int i, len;
SDataPtr data;
len = s->ptr.sample->length;
data = s->ptr.sample->data;
indent();
printf(" sound: {%x}\n", s->ptr.sample);
indent();
printf(" refCount: %d len:%d srate:%g data:{%x}\n",
s->ptr.sample->refCount, len, s->ptr.sample->srate, data);
} else if (s->tag == SUMNODES) {
NodePtr n;
show_indent++;
for (n = s->ptr.node; n != NULL; n = n->next) {
indent();
printf("{%x}->\n", n);
s_show(n->sound);
}
show_indent--;
putchar('\n');
}
return (s);
}
float s_srate(s)
SoundPtr s;
{
return s->srate;
}
SoundPtr s_stats(s)
SoundPtr s;
{
printf("{%x} tag:%s\n", s, tag_name[s->tag]);
printf(" from:%g to:%g shift:%g stretch:%g logTo:%g\n",
s->from, s->to, s->shift, s->stretch, s->logicalTo);
printf(" scale:%g srate:%g refCount:%d\n",
s->scale, s->srate, s->refCount);
if (s->tag == SAMPLES) {
int i, len;
SDataPtr data;
len = s->ptr.sample->length;
data = s->ptr.sample->data;
printf(" len:%d [", len);
for (i = 1; i <= 6 && i < len; i++)
printf("%g, ", data[i]);
if (i < len) printf("..., ");
i = MAX(i, len - 3);
for (; i < len; i++)
printf("%g, ", data[i]);
printf("%g]\n", data[len]);
}
putchar('\n');
return (s);
}
/* s_access -- lookup the value at a particular time point */
/**/
float s_access(s, t)
SoundPtr s; /* the sound */
double t; /* the time */
{
double min_time, max_time;
int valid;
double exact_offset, frac;
SDataPtr ptr;
/* must be samples before we can access it */
(void) s_flatten(s);
if (s->tag == SILENCE) return 0.0;
valid = extent(s, &min_time, &max_time);
if (!valid || t < min_time || t > max_time) return 0.0;
exact_offset = (t - s->shift) * s->srate;
ptr = s->ptr.sample->data + 1 + (long) exact_offset;
frac = exact_offset - floor(exact_offset);
return ptr[0] * (1.0 - frac) + ptr[1] * frac;
}
SoundPtr s_apply(s1, sc, from, to, sh, str, srate)
SoundPtr s1; /* the sound to be modified */
double sc; /* scale factor */
double from; /* clip from */
double to; /* clip to */
double sh; /* shift by */
double str; /* stretch by */
double srate;/* sample rate */
{
SoundPtr s;
/* printf(
"s_apply: snd %x scale %g from %g to %g shift %g stretch %g srate %g\n",
s1, sc, from, to, sh, str, srate); */
s = s_copy(s1);
/* first scale samples */
s->scale *= sc;
/* then extract the period from start to stop */
/* notice that since s->from and s->to are shifted by s-shift, we
* have to shift from and to parameters by s->shift before clipping */
s->from = MAX(s->from, from - s->shift);
s->to = MIN(s->to, to - s->shift);
/* shift the result by sh */
s->shift += sh;
/* and stretch by str */
s->stretch *= str;
/* shift and stretch s->logicalTo */
s->logicalTo = (s->logicalTo + sh) * str;
if (srate != 0.0)
s->srate = srate;
if ((s->scale == 0.0) || (s->to <= s->from) || (s->stretch == 0.0))
s_silence(s);
return (s);
}
SoundPtr s_mult(s1, s2)
SoundPtr s1, s2;
{
int len;
SoundPtr s, temp;
SamplePtr sample;
SDataPtr data, data1, data2;
double begin, end, srate;
double min1, min2, max1, max2;
int valid1, valid2;
s = s_create();
(void) s_flatten(s1);
(void) s_flatten(s2);
if ((s1->tag == SILENCE) || (s2->tag == SILENCE))
return (s);
srate = MAX(s1->srate, s2->srate);
valid1 = extent(s1, &min1, &max1);
valid2 = extent(s2, &min2, &max2);
begin = MAX(min1, min2);
end = MIN(max1, max2);
len = (end - begin) * srate + 0.5;
if ((len <= 1) || !valid1 || !valid2)
return (s);
data = sdata_create(len);
sample = spl_create(data, len, srate);
data++; /* skip over initial zero */
s->to = end - begin;
s->shift = begin;
s->logicalTo = MIN(s1->logicalTo, s2->logicalTo);
s->srate = srate;
s->tag = SAMPLES;
s->ptr.sample = sample;
if (APPROX(s1->srate, s2->srate)) {
data1 = locate_sample(s1, begin);
data2 = locate_sample(s2, begin);
for (; len != 0; len--)
*(data++) = *(data1++) * (*(data2++));
return (s);
} else {
double step2, index2, d, f;
int index1;
if (s1->srate < s2->srate) {
temp = s1;
s1 = s2;
s2 = temp;
}
/* srate1 > srate2 */
step2 = s2->srate / s1->srate;
/* why did Chris add the 0.5 to only one of these? */
index1 = 1 + (begin - s1->shift) * s1->srate + 0.5;
index2 = 1 + (begin - s2->shift) * s2->srate;
data1 = data + len; /* use this as a termination marker */
for (; data < data1; data++, index1++, index2 += step2) {
register int i2 = floor(index2);
d = index2 - i2;
f = 1.0 - d;
*data = s1->ptr.sample->data[index1] *
(f * s2->ptr.sample->data[i2] +
d * s2->ptr.sample->data[i2 + 1]);
}
return (s);
}
}
SoundPtr sf_load(file, srate)
char *file;
double srate;
{
int fin;
SoundPtr s;
SamplePtr sample;
SDataPtr data;
SFDataPtr inbuff, temp;
char error[80];
struct stat sbuff;
unsigned int nbytes;
int size;
if (stat(file, &sbuff) < 0)
xlabort(sprintf(error, "SF-LOAD: Cannot stat file '%s'", file));
if (0 > (fin = open(file, 0)))
xlabort(sprintf(error, "SF-LOAD: Cannot open file '%s'", file));
s = s_create();
nbytes = sbuff.st_size;
if (NULL == (inbuff=(SFDataPtr)malloc(nbytes)))
xlabort("SF-LOAD: insufficient memory - extern");
/* fprintf(stderr, "inbuff=%x\n", inbuff); */
if (nbytes != read(fin, (char *)inbuff, (int)nbytes))
xlabort(sprintf(error, "SF-LOAD: error reading file '%s'", file));
size = nbytes / sizeof(SFDataType);
(void)close(fin);
if (size <= 1)
return (s);
data = sdata_create(size);
sample = spl_create(data, size, srate);
s->to = s->logicalTo = (double) size / srate;
s->srate = srate;
s->tag = SAMPLES;
s->ptr.sample = sample;
temp = inbuff;
data++; /* skip over initial zero */
for (; size!=0; size--)
*(data++) = *(inbuff++);
free((char *)temp);
return (s);
}
SoundPtr sf_save(s1, file)
SoundPtr s1;
char *file;
{
double f;
int fout;
int i, len;
SFDataPtr buff, ptr;
SDataType max;
SDataPtr data;
char error[80];
(void) s_flatten(s1);
if (!(max = s_maxSample(s1)))
{
printf("WARNING: sf_save() did not save SILENCE\n");
return (s1);
}
if (max != 0.0)
f = 32767 / max;
else
f = 1.0;
if (0 > (fout = creat(file, PERMS)))
xlabort(sprintf(error, "SF-SAVE: Cannot create file '%s'", file));
data = s1->ptr.sample->data + 1; /* ignore initial zero */
len = s1->ptr.sample->length;
if (NULL == (buff = (SFDataPtr)malloc((unsigned)len * sizeof(SFDataType))))
xlabort("SF-SAVE: Cannot malloc room for output buffer.");
for (ptr=buff, i=0; i<len; i++)
*(ptr++) = *(data++) * f + 0.5;
(void)write(fout, (char *)buff, len*sizeof(SFDataType));
(void)close(fout);
free((char *)buff);
return (s1);
}
SoundPtr s_env(srate, t1, t2, t3, t4, l1, l2, l3)
float srate; /* the sample rate */
float t1, t2, t3, t4; /* the duration of each phase */
float l1, l2, l3; /* the level of each phase endpoint */
{
float t[5]; /* time in each phase of the envelope */
float l[5]; /* level at each endpoint */
float len[5];/* the length in samples of each phase */
float time;
unsigned int size; /* total length of the envelope */
register double lvl; /* envelope level */
register double incr; /* envelope slope */
register SDataPtr ptr; /* pointer to resulting signal samples */
SDataPtr beg, end;
SoundPtr s;
int i;
register int samp; /* sample counter */
l[0] = 0.0;
l[1] = l1; t[1] = ABS(t1);
l[2] = l2; t[2] = ABS(t2);
l[3] = l3; t[3] = ABS(t3);
l[4] = 0.0; t[4] = ABS(t4);
s = s_create();
size = 0;
/* compute the number of samples for each phase and the total */
for (i=1; i<=4; i++)
size += len[i] = (int)(srate * t[i]);
if (size <= 1) return (s);
beg = ptr = sdata_create(size);
ptr++; /* skip past initial zero */
end = ptr + size - 1;
for (i=1; i<=4; i++) {
lvl = l[i - 1];
incr = (l[i] - l[i - 1]) / len[i];
for (samp = 0; samp < len[i]; samp++) {
if (ptr > end) {
fprintf(stderr, "Fatal error in s_env!");
exit(1);
}
*ptr++ = lvl;
lvl += incr;
}
}
s->to = s->logicalTo = size / srate;
s->srate = srate;
s->tag = SAMPLES;
s->ptr.sample = spl_create(beg, size, srate);
return (s);
}
SoundPtr s_osc(s, sKey, srate, freq, duration, phase, oneShot)
SoundPtr s;
double sKey, srate, freq, duration, phase;
int oneShot;
{
SDataPtr src; /* Ptr to sample data of s */
int sLength; /* Len of s in samples */
double sPeriod; /* Period corresponding to sKey */
SoundPtr newS; /* The newly created sound */
unsigned int size; /* Size of return sample */
SDataPtr ptr; /* Ptr to return data */
double dt; /* dTime per sample */
double d, f; /* Interpolation variables */
int fp;
if (oneShot) {
newS = s_copy(s);
if ((newS->stretch == 0.0) || (newS->tag == SILENCE))
return (s_silence(newS));
newS->to = MIN(newS->to, (duration / newS->stretch)-newS->shift);
newS->logicalTo = duration;
if (newS->from >= newS->to)
return (s_silence(newS));
return (newS);
}
newS = s_create();
s_flatten(s);
src = s->ptr.sample->data;
size = duration * srate;
if (size <= 1) return (newS);
ptr = sdata_create(size);
newS->from = 0.0;
newS->to = newS->logicalTo = floor(duration * srate + 0.5) / srate;
newS->stretch = 1.0;
newS->srate = srate;
newS->tag = SAMPLES;
newS->ptr.sample = spl_create(ptr, size, srate);
sPeriod = 1.0 / step_to_hz(sKey); /* convert canon sKey to period */
/* warning: this is WRONG! */
sLength = s->ptr.sample->length;
phase = compute_phase(phase, sKey, sLength, s->srate, srate, freq, &dt);
ptr++; /* skip past initial zero */
while (size--) {
if (phase >= sLength) phase -= sLength;
fp = floor(phase);
d = phase - fp;
f = 1.0 - d;
*ptr++ = (f * src[fp]) +
d * (fp + 1 == sLength ? (*src) : src[fp + 1]);
phase += dt;
}
#ifdef OLDCODE
for (;size && (int)phase<sLength; size--)
{
d = phase - floor(phase);
f = 1.0 - d;
if ((int)phase == sLength-1)
*ptr++ = (f * (*(src+(int)phase)) + d * (*src));
else
*ptr++ = (f * (*(src+(int)phase)) +
d * (*(src+1+(int)phase)));
phase += dt;
}
phase -= sLength;
}
#endif
return (newS);
}
/* s_pwl -- build a piece-wise linear envelope function */
/**/
SoundPtr s_pwl(srate, lis)
float srate; /* the sample rate */
LVAL lis; /* list of points: x1 y1 x2 y2 ... xn */
{
float time;
unsigned int size; /* total length of the envelope */
register double lvl; /* envelope level */
register double incr; /* envelope slope */
register SDataPtr ptr; /* pointer to resulting signal samples */
double x, y;
SDataPtr beg, end;
SoundPtr s;
int i;
register int samp, dest; /* sample counter */
LVAL lval;
/* check validity first */
lval = lis;
/* printf("pwl arg check\n"); */
while (TRUE) {
LVAL flo;
if (!consp(lval)) {
xlerror("s-pwl: time value missing from list", lis);
}
flo = car(lval);
if (!floatp(flo) ||
((x = getflonum(flo)) <= 0.0)) {
xlerror("s-pwl: bad time value", flo);
}
/* printf(" %g ", x); */
lval = cdr(lval);
if (null(lval)) {
/* printf("\n");*/
break;
}
if (!consp(lval)) {
xlerror("s-pwl: bad breakpoint list", lis);
}
flo = car(lval);
if (!floatp(flo)) xlerror("s-pwl: non-float breakpoint value", flo);
/* printf("%g\n", y = getflonum(flo));*/
lval = cdr(lval);
}
s = s_create();
size = (int) (srate * x);
if (size <= 1) return (s);
beg = ptr = sdata_create(size);
ptr++; /* skip past initial zero */
end = ptr + size - 1;
lval = lis;
lvl = 0.0;
samp = 0;
while (!null(lval)) {
x = getflonum(car(lval));
lval = cdr(lval);
if (null(lval)) y = 0.0;
else {
y = getflonum(car(lval));
lval = cdr(lval);
}
dest = (srate * x);
if (dest > samp) incr = (y - lvl) / (dest - samp);
/* printf("incr %g for %d\n", incr, dest - samp); */
while (samp < dest) {
if (ptr > end) {
fprintf(stderr, "Fatal error in s_pwl");
exit(1);
}
*ptr++ = lvl;
lvl += incr;
samp++;
}
}
s->to = s->logicalTo = size / srate;
s->srate = srate;
s->tag = SAMPLES;
s->ptr.sample = spl_create(beg, size, srate);
return (s);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.