home *** CD-ROM | disk | FTP | other *** search
- #include <stdio.h>
- #include <stdlib.h>
- #include <errno.h>
- #include <string.h>
- #include <malloc.h>
-
- #include <conf.h>
- #include "clstandardtypes.h"
- #include "sysdeps.h"
- #include "log.h"
- #include "mix_server.h"
- #include "xmalloc.h"
-
- #define _L LOG_SOUND | LOG_INFO
-
- /* MIXER SERVER */
-
- #if defined(UNDER_UNIX)
- #define WRITE_TO_FILE 1
- #endif
-
- #if WRITE_TO_FILE
- #include <fcntl.h>
- int snd_file;
- #endif
-
- /* This is a OS-generic module for mixing digitized samples together. */
- void
- mix_init(mix_context * m, u32 hertz, u32 bufsize,
- bool issigned, bool eightbit, bool bigendian)
- {
- u16 x;
-
- #if WRITE_TO_FILE
- snd_file = open("digital.raw", O_CREAT|O_TRUNC|O_WRONLY, 0666);
- if (snd_file < 0) snd_file = 0;
- #endif
-
- x = ('A' << 8) + 'B';
- m->soundhz = hertz < 4000 ? 4000 : hertz;
- m->issigned = issigned;
- m->eightbit = eightbit;
- m->swapendian = (*(u8 *) & x == 'B') == bigendian;
-
- logger(_L | L_1, "mix_init: swapendian=%d\n\n", m->swapendian);
- m->buffer = (s32 *) xmalloc(sizeof(s32) * bufsize);
- m->bufsize = bufsize;
- }
-
- void
- mix_term(mix_context * m)
- {
- if (m->buffer) {
- xfree(m->buffer);
- m->buffer = NULL;
- }
- #if WRITE_TO_FILE
- if (snd_file) close(snd_file);
- #endif
- }
-
- void
- mix_restart(mix_context * m)
- {
- memset(m->voices, 0, sizeof(m->voices));
- m->voices[0].clock = m->voices[1].clock = m->voices[2].clock =
- m->voices[3].clock = m->voices[4].clock = m->voices[5].clock =
- m->soundhz;
- m->voices[3].ns1 = 0xaaaaaaaa;
- m->voices[3].ns2 = 1;
- }
-
- // Step a tone voice by one sample and return contrib.
- INLINE void
- step_tone(sample * v, s32 * chn, int * active)
- {
- v->div += v->delta;
- if (v->div >= v->clock) {
- /*if (v->vol) */ {
- *chn += v->vol;
- (*active)++;
- }
- while (v->div >= v->clock)
- v->div -= v->clock;
- } else {
- //*chn += -v->vol;
- //(*active)++;
- }
- }
-
- // Advance a tone voice by X samples.
- INLINE void
- advance_tone(sample * v, u32 samples)
- {
- v->div = (v->div + v->delta * samples) % v->clock;
- }
-
-
- // Step white noise by one sample and update dat.
-
- #define NOISE_GATE(x,y) \
- do { \
- x = (x<<1) | (x>>31); \
- x ^= y; \
- if ((y += x)==0) y++; \
- } while (0)
-
- INLINE void
- step_white(sample * v, s32 * chn, int * active)
- {
- v->div += v->delta;
- while (v->div >= v->clock) {
- NOISE_GATE(v->ns1, v->ns2);
- v->div -= v->clock;
- }
- if (v->ns1 & 1) {
- /*if (v->vol) */ {
- *chn += v->vol;
- (*active)++;
- }
- } else {
-
- // *chn -= v->vol;
- // (*active)++;
- }
- }
-
- // Advance white noise by X samples
- INLINE void
- advance_white(sample * v, u32 samples)
- {
- u32 chg;
- u32 steps;
-
- chg = v->delta * samples;
- steps = (v->div + chg) / v->clock;
- v->div = (v->div + chg) % v->clock;
- while (steps--) {
- NOISE_GATE(v->ns1, v->ns2);
- }
- }
-
- // Step periodic noise by one sample and update dat.
- #define PERIODMULT 16
- INLINE void
- step_periodic(sample * v, s32 * chn, int * active)
- {
- v->div += v->delta;
- if (v->div >= v->clock) {
- /*if (v->vol) */ {
- *chn += v->vol;
- (*active)++;
- }
- while (v->div >= v->clock)
- v->div -= v->clock;
- } else {
- //*chn -= v->vol;
- //(*active)++;
- }
- }
-
- // Advance periodic noise by X samples
- INLINE void
- advance_periodic(sample * v, u32 samples)
- {
- v->div = (v->div + v->delta * samples) % v->clock;
- }
-
- // Step speech by one sample and update dat.
- // Sample is finished when s->used==0. Caller should free memory.
- INLINE void
- step_digital(sample * v, s32 * chn, int * active)
- {
- if (v->used) {
- *chn += v->data[v->st] * (signed) v->vol / (signed) 256;
- (*active)++;
-
- v->div += v->delta;
- while (v->div >= v->clock) {
- v->div -= v->clock;
- v->st++;
- v->used--;
- if (v->used == 0) {
- v->st = v->en = 0;
- } else if (v->st >= v->len) {
- v->st -= v->len;
- }
- }
- }
- }
-
- // Advance digital data by X samples
- INLINE void
- advance_digital(sample * v, u32 samples)
- {
- u32 chg = v->delta * samples;
- u32 steps = (v->div + chg) / v->clock;
-
- v->div = (v->div + chg) % v->clock;
- v->st = (v->st + steps) % v->len;
-
- // unless we loop [we don't],
- // we are done when we've stepped through
- // the whole sample.
- if (v->used <= steps || v->used == 0) {
- v->st = v->en = 0;
- // don't free memory (there's a bug here)
- // xfree(v->data);
- // v->data = NULL;
- v->used = 0;
- v->len = 0;
- } else {
- v->used -= steps;
- }
- }
-
- #if 0
- // Step audio gate
- INLINE void
- step_audiogate(sample * v, s32 * chn, int * active)
- {
- // since this can change so fast, we set v->clock
- // to indicate something should happen
- if (v->clock & 1) {
- *chn += v->vol;
- } else if (v->clock) {
- *chn -= 0x7fffff;
- }
- (*active)++;
- }
-
- // Advance audio gate
- INLINE void
- advance_audiogate(sample * v, u32 samples)
- {
- if (v->clock > samples)
- v->clock -= samples;
- else
- v->clock = 0;
- }
- #endif
-
- static int had_silent_frame = 0;
- static int
- mix_silence(mix_context * m)
- {
- // check that something is on
- return (
- (m->voices[0].vol | m->voices[1].vol | m->voices[2].
- vol | m->voices[3].vol | m->voices[4].vol | m->voices[5].vol) ==
- 0 ||
- // and that nothing is illegal
- (m->voices[0].clock && m->voices[1].clock && m->voices[2].clock
- && m->voices[3].clock && m->voices[4].clock
- && m->voices[5].clock) == 0);
- }
-
- /*
- Mix the channels together and generate a segment of
- sound. Does not advance the mixer's idea of time;
- use mix_advance() to do that.
- */
- void
- mix_mixit(mix_context * m, u32 advance, u32 samples)
- {
- s32 *out = m->buffer, *end = out + samples;
- s32 dat = 0;
- int div = 0;
- int silent;
- sample myvoices[6];
-
- // work on local copy of m->voices.
- memcpy(myvoices, m->voices, sizeof(myvoices));
-
- if (advance)
- mix_advance(m, advance);
-
- silent = mix_silence(m);
- if (!silent || !had_silent_frame) {
- had_silent_frame = silent;
-
- while (out < end) {
- dat = 0;
- div = 0;
-
- // tones
- if (myvoices[0].vol)
- step_tone(&myvoices[0], &dat, &div);
- if (myvoices[1].vol)
- step_tone(&myvoices[1], &dat, &div);
- if (myvoices[2].vol)
- step_tone(&myvoices[2], &dat, &div);
-
- // noise
- if (myvoices[3].vol) {
- sample *n = &myvoices[3];
-
- if (n->iswhite) {
- step_white(n, &dat, &div);
- } else {
- step_periodic(n, &dat, &div);
- }
-
- }
- // speech
- if (myvoices[4].used) {
- step_digital(&myvoices[4], &dat, &div);
- }
- // audio gate
- if (myvoices[5].used) {
- step_digital(&myvoices[5], &dat, &div);
- }
-
- if (div) {
- //dat /= div;
- dat >>= 1;
- if (dat)
- logger(_L | L_3, "dat[%d]=%08X \n", div, dat);
- }
-
- *out++ = dat <= -0x00800000 ? -0x007fffff :
- dat >= 0x00800000 ? 0x007fffff : dat;
- }
-
- } else {
- memset(m->buffer, 0, samples * sizeof(s32));
- had_silent_frame = true;
- }
-
- /* Convert sample */
-
- if (m->eightbit) {
- int idx;
- s8 *ptr = (s8 *) m->buffer;
-
- for (idx = 0; idx < samples; idx++)
- *ptr++ = m->buffer[idx] >> 16; /* 24 -> 8 */
- } else {
- int idx;
- s16 *ptr = (s16 *) m->buffer;
-
- for (idx = 0; idx < samples; idx++)
- *ptr++ = m->buffer[idx] >> 8; /* 24 -> 16 */
- }
-
- if (!m->issigned) {
- int idx;
- int step = (m->eightbit ? 1 : 2);
- s8 *ptr = (s8 *) m->buffer;
-
- for (idx = (m->swapendian ? step - 1 : 0); idx < samples; idx += step)
- ptr[idx] ^= 0x80;
- }
-
- if (m->swapendian && !m->eightbit) {
- swab((char *) m->buffer, (const char *) m->buffer,
- samples * sizeof(u16));
- }
-
- }
-
- /* Advance mixer time by so many samples. */
- void
- mix_advance(mix_context * m, int samples)
- {
- // tones
- if (m->voices[0].clock)
- advance_tone(&m->voices[0], samples);
- if (m->voices[1].clock)
- advance_tone(&m->voices[1], samples);
- if (m->voices[2].clock)
- advance_tone(&m->voices[2], samples);
-
- // noise
- if (m->voices[3].iswhite) {
- if (m->voices[3].clock)
- advance_white(&m->voices[3], samples);
- } else {
- if (m->voices[3].clock)
- advance_periodic(&m->voices[3], samples);
- }
-
- // speech
- if (m->voices[4].used) {
- advance_digital(&m->voices[4], samples);
- }
- // audio gate
- if (m->voices[5].used) {
- advance_digital(&m->voices[5], samples);
- }
- }
-
- static void
- stackdata(sample * s, s8 * bytes, int size)
- {
- int cnt;
-
- /* This routine is apt to occur during a speech
- interrupt and lead to inconsistencies. We do all
- our work on a copy of the sample. The worst that
- can happen, it appears, is for a part of the
- sample to be repeated when this routine resets
- the s->st and s->en pointers. */
-
- sample in = *s;
-
- if (bytes == NULL)
- return;
-
- #if 0
- // Simplest algorithm, but very bad on memory.
-
- in.data = (u8 *) xrealloc(in.data, size + in.len);
- memcpy(in.data + in.en, bytes, size);
- in.len += size;
- in.en += size;
- in.used += size;
- *s = in;
- return;
- #endif
-
- logger(_L | L_1, "IN: in.data=%p, in.len=%d, in.used=%d, in.st=%d, in.en=%d\n",
- in.data, in.len, in.used, in.st, in.en);
-
- if (in.st > in.len || in.en > in.len ||
- (in.st >= in.en ?
- (in.used != (in.len - in.st + in.en)) :
- (in.used != (in.en - in.st)))) {
- logger(_L | LOG_ERROR | LOG_USER,
- "consistency error: in.len=%d, in.st=%d, in.en=%d, in.used=%d\n",
- in.len, in.st, in.en, in.used);
- if (in.data) xfree(in.data); in.data = NULL;
- in.used = in.len = in.st = in.en = 0;
- }
-
- /* need to shrink the ring? */
- if (in.data != NULL && (in.used < in.len) && (in.used + size < in.len)) {
- /* two cases: (1) all data is contiguous: move to beginning,
- reset pointers, and continue.
- (2) data wraps. Move [0,...) part up, move end part to beginning. */
-
- logger(_L | L_1, "shrinking block");
- /* non-wrapping case */
- if (in.used > 0 && in.st < in.en) {
- memmove(in.data, in.data + in.st, in.used);
- in.st = 0;
- in.en = in.used;
- in.data = (s8 *) xrealloc(in.data, in.used);
- in.len = in.used;
- }
- /* wrapping case */
- else
- if (in.used > 0 && in.st > in.en
- && (in.len - in.st + in.en < in.st)) {
- int endsize = in.len - in.st;
-
- memmove(in.data + endsize, in.data, in.en);
- memmove(in.data, in.data + in.st, endsize);
- in.st = 0;
- in.en += endsize;
- in.data = (s8 *) xrealloc(in.data, in.en);
- in.len = in.en;
- }
- }
-
- /* need to grow the ring? */
- if (in.data == NULL || in.used + size > in.len) {
- int nw;
-
- logger(_L | L_1, "resizing: in.used+size=%d, in.len=%d, in.data=%p\n\n",
- in.used + size, in.len, in.data);
- nw = in.used + size;
- in.data = (s8 *) xrealloc(in.data, nw);
-
- /* if we grew the buffer and the used part was wrapping,
- move the end of the old buffer to the end of the
- new buffer */
- if (in.used > 0 && in.en <= in.st && in.st > 0) {
- int endsize = in.len - in.st;
-
- memmove(in.data + nw - endsize, in.data + in.st, endsize);
- in.st = nw - endsize;
- }
- if (in.en == in.st && in.en == 0)
- in.en = in.len;
- in.len = nw;
- }
-
- /* paste on the end of the current block, don't wrap */
- cnt = (in.len - in.en <= size) ? in.len - in.en : size;
- memcpy(in.data + in.en, bytes, cnt);
-
- /* copy the rest to the beginning of the ring */
- memcpy(in.data, bytes + cnt, size - cnt);
-
- in.used += size;
- in.en += size;
- if (in.en >= in.len)
- in.en -= in.len;
-
- *s = in;
-
- logger(_L | L_1, "OUT: s->data=%p, s->len=%d, s->used=%d, s->st=%d, s->en=%d\n",
- s->data, s->len, s->used, s->st, s->en);
-
- }
-
- ///////////////////////////////////////////////////////
-
- /*
- #include <math.h>
- #include <stdio.h>
-
- int main(void)
- {
- int x;
- for (x=0; x<16; x++)
- {
- double f = exp((x/15.0) * log(17)) ;
- printf("\t%08X,\n", (int)(f * 0x080000));
-
- }
- }
- */
-
- static u32 atten[16] = {
- 0x00000000,
- // 0x00080000,
- 0x0009A9C5,
- 0x000BAC10,
- 0x000E1945,
- 0x001107A1,
- 0x001491FC,
- 0x0018D8C4,
- 0x001E0327,
- 0x00244075,
- 0x002BC9D6,
- 0x0034E454,
- 0x003FE353,
- 0x004D2B8C,
- 0x005D36AB,
- 0x007097A5,
- 0x007FFFFF
- };
-
- void
- mix_handle_voice(mix_context * m, u8 channel, u32 hertz, u8 volume)
- {
- sample *s;
-
- logger(_L | L_2, "mix_handle_voice: channel %d, hertz = 0x%x, volume = %d\n",
- channel, hertz, volume);
-
- if (channel >= mix_CHN0 && channel <= mix_CHN2)
- {
- s = &m->voices[channel];
-
- // sounds this high-pitched won't
- // work at all.
- if (hertz * 2 >= m->soundhz) {
- s->delta = 0;
- s->vol = 0;
- s->clock = m->soundhz; // assure no zero divides
- } else {
- s->clock = m->soundhz;
- s->delta = hertz;
- s->div = volume ? s->div : 0;
- s->vol = atten[volume];
- }
- }
- }
-
- void
- mix_handle_noise(mix_context * m, u8 channel, u32 hertz, u8 volume,
- int iswhite)
- {
- sample *s;
-
- logger(_L | L_2, "mix_handle_noise: channel %d, hertz = 0x%x, volume = %d, iswhite = %d\n",
- channel, hertz, volume, iswhite);
-
- if (channel == mix_Noise)
- {
- s = &m->voices[channel];
-
- if (iswhite) {
- s->clock = m->soundhz;
- s->delta = hertz;
- s->div = volume ? s->div : 0;
- s->vol = atten[volume];
- s->iswhite = 1;
- } else {
- s->clock = m->soundhz * PERIODMULT;
- s->delta = hertz;
- s->div = volume ? s->div : 0;
- s->vol = atten[volume];
- s->iswhite = 0;
- }
- }
- }
-
- void
- mix_handle_data(mix_context * m, u8 channel, u32 hertz, u8 volume, u32 length,
- s8 * data)
- {
- logger(_L | L_1, "mix_handle_data: using %d bytes of %d Hz data on channel %d\n",
- length, hertz, channel);
-
- #if WRITE_TO_FILE
- if (snd_file) write(snd_file, data, length);
- #endif
-
- switch (channel) {
- case mix_Speech:
- {
- sample *s = &m->voices[4];
- if (data && length) {
- s->clock = m->soundhz;
- s->delta = hertz;
- s->div = 0;
- s->vol = volume << 15;
- stackdata(s, data, length);
- } else {
- // flush
- if (s->delta > 0)
- while (s->len > s->delta)
- {
-
- }
- /* xfree(s->data);
- s->data = 0L;
- s->len = s->used = s->st = s->en = 0;*/
- }
-
- break;
- }
-
- case mix_AudioGate:
- {
- /* for the audio gate, we only use the volume;
- no data need be passed. This is because we interpret
- 'length' as a repeat count for 'vol'. */
-
- if (length) {
- s8 *tmp = (s8 *) alloca(length);
- sample *s = &m->voices[5];
-
- // int x;
- logger(_L | L_2, "writing %d bytes of %d as audio gate\n", length, volume);
- // for (x=0; x<length; x++)
- // tmp[x] = ((volume ^ !!(x&1)) ? 0x7f : 0) ;
- memset(tmp, volume, length);
- s->clock = m->soundhz;
- s->delta = hertz;
- s->div = 0;
- s->vol = volume << 15;
- stackdata(s, tmp, length);
- } else {
- sample *s = &m->voices[5];
-
- if (s->used > s->delta) {
- // if (s->data) xfree(s->data); s->data = NULL;
- s->st = s->en = s->len = s->used = 0;
- }
- }
- break;
- }
- }
- }
-
- /*********************************/
-