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.