Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/openal-0.0.8/src/al_filter.c @ 17

Last change on this file since 17 was 17, checked in by landauf, 16 years ago

added openal

File size: 45.2 KB
Line 
1/* -*- mode: C; tab-width:8; c-basic-offset:8 -*-
2 * vi:set ts=8:
3 *
4 * al_filter.c
5 *
6 * Contains filters.
7 *
8 *
9 * Short guide to filters:
10 *
11 * Filters all have the prefix alf_<something>.  Each filter
12 * defined in software_time_filters or software_frequency_filters
13 * is applied to each source that finds its way into the mixer.
14 *
15 * ApplyFilters takes a chunk of data from the original buffer
16 * associated with the passed source.
17 *
18 * This chunk is understood to be that block of data samp->_orig_buffer
19 * offset src->soundpos to src->soundpos + bufsiz, where src is the
20 * passed AL_source, samp is the AL_buffer associated with src, and
21 * bufsiz is the length of the chunk of data that we want.  It is usually
22 * set to _AL_DEF_BUFSIZE, unless specified by ALC_BUFFERSIZE in the
23 * application.
24 *
25 * Applying filters to a source does not (should not) change the original
26 * pcm data.  ApplyFilters will split the original pcm data prior to
27 * calling each filter, and filters should restrict themselves to
28 * manipulating the passed data.
29 *
30 * time domain filters (those defines in software_time_filters) are
31 * passed:
32 *      ALuint cid
33 *              identifier for the context that this source belongs to
34 *      AL_source *src
35 *              source that the filter should be applied to
36 *      AL_buffer *samp
37 *              buffer that the source is associated with
38 *      ALshort **buffers
39 *              arrays of points to PCM data, one element per
40 *              channel(left/right/etc)
41 *      ALuint nc
42 *              number of elements in buffers
43 *      ALuint len
44 *              byte length of each element in buffers
45 *
46 * Filters are expected to alter buffers[0..nc-1] in place.  After
47 * the ApplyFilter iteration is over, the resulting data is mixed into
48 * the main mix buffer and forgotten.  The data altered is cumulative,
49 * that is to say, if two filters alf_f and alf_g occur in sequential
50 * order, alf_g will see the pcm data after alf_f has altered it.
51 *
52 * FINER POINTS:
53 *
54 * A lot of the filters make effects by modulating amplitude and delay.
55 * Because these changes are cumulative, we can reduce the application
56 * of amplitude and delay changes to one operation.  This is the point
57 * of SourceParamApply --- filters can make changes to srcParams.gain
58 * and srcParams.delay in a source and have those changes applied at
59 * the end of the ApplyFilters call for the source.  These values are
60 * reset to their defaults at the top of the ApplyFilters call.
61 *
62 */
63#include "al_siteconfig.h"
64
65#include <AL/alext.h>
66
67#include <math.h>
68#include <stdlib.h>
69#include <string.h>
70#include <float.h>
71
72#include "al_buffer.h"
73#include "al_debug.h"
74#include "al_error.h"
75#include "al_filter.h"
76#include "al_listen.h"
77#include "al_main.h"
78#include "al_mixer.h"
79#include "al_source.h"
80#include "al_queue.h"
81#include "al_vector.h"
82
83#include "alc/alc_context.h"
84#include "alc/alc_speaker.h"
85
86#define MIN(a,b) (((a) < (b)) ? (a) : (b))
87#define MAX(a,b) (((a) < (b)) ? (b) : (a))
88
89#define MIN_PITCH 0.25f
90
91#define USE_TPITCH_LOOKUP 1 /* icculus change here JIV FIXME */
92
93/*
94 * _AL_CUTTOFF_ATTENUATION is the value below which, sounds are not further
95 * distance attenuated.  The purpose of this culling is to avoid pop-off
96 * artifacts.
97 *
98 * Elias: This has been found to cause insufficient distance attenuation
99 * and has therefore been effectively disabled by setting it to 0. If no
100 * problems show up, the value should be completely removed.
101 * The original was value 0.01
102 */
103#define _AL_CUTTOFF_ATTENUATION 0.00
104
105/*
106 * TPITCH_MAX sets the number of discrete values for AL_PITCH we can have.
107 * You can set AL_PITCH to anything, but integer rounding will ensure that
108 * it will fall beween MIN_SCALE and 2.0.
109 *
110 * 2.0 is an arbitrary constant, and likely to be changed.
111 */
112#define TPITCH_MAX       256
113
114/*
115 * The default software time domain filters.
116 *
117 * I wish I could say that the order of these does not matter,
118 * but it does.  Namely, tdoppler and tpitch must occur in that
119 * order, and they must occur before any other filter.  listenergain must
120 * occur last.
121 */
122static time_filter_set software_time_filters[] = {
123        { "tdoppler",      alf_tdoppler }, /* time-domain doppler filter */
124        { "tpitch",        alf_tpitch   }, /* time-domain pitch filter */
125        { "da",            alf_da },
126        { "reverb",        alf_reverb },
127        { "coning",        alf_coning },
128        { "panning",       alf_panning },
129        { "minmax",        alf_minmax },
130        { "listenergain",  alf_listenergain },
131        { { 0 },     NULL }
132};
133
134/*
135 * compute_sa( ALfloat *source_pos, ALfloat source_max,
136 *             ALfloat source_ref, ALfloat source_gain,
137 *             ALfloat source_rolloff,
138 *             ALfloat *speaker_pos,
139 *             ALfloat (*df)( ALfloat dist, ALfloat rolloff,
140 *                            ALfloat ref, ALfloat max))
141 *
142 * computes distance attenuation with respect to a speaker position.
143 *
144 * This is some normalized value which gets expotenially closer to 1.0
145 * as the source approaches the listener.  The minimum attenuation is
146 * AL_CUTTOFF_ATTENUATION, which approached when the source approaches
147 * the max distance.
148 *
149 * source_pos = source position   [x/y/z]
150 * source_max = source specific max distance
151 * speaker_pos = speaker position [x/y/z]
152 * ref        = source's reference distance
153 * df         = distance model function
154 * max        = maximum distance, beyond which everything is clamped at
155 *              some small value near, but not equal to, zero.
156 */
157static ALfloat compute_sa( ALfloat *source_pos, ALfloat source_max,
158                           ALfloat source_ref, ALfloat source_gain,
159                           ALfloat source_rolloff,
160                           ALfloat *speaker_pos,
161                           ALfloat df( ALfloat dist, ALfloat rolloff, ALfloat ref, ALfloat max ));
162
163#if USE_TPITCH_LOOKUP
164/*
165 * our quick lookup table for our time domain pitch filter.
166 *
167 *
168 * We initialize each element in offsets to be a set of offsets,
169 * such that
170 *
171 *      offset[x][y]     = int portion of y * pitch
172 *      fractinoal[x][y] = fractional portion of y * pitch
173 *
174 *      Where x is the discrete integer value of pitch, and y is
175 *      any value between 0 and the length of a buffer.
176 *
177 * What's the point?  To save the pain of float->int conversion at
178 * runtime, which is needed to map the original PCM data to a
179 * "pitch modified" mapping of the same data.
180 *
181 */
182static struct {
183        int *offsets[TPITCH_MAX]; /* use int instead of ALint because
184                                   * these are array indexes
185                                   */
186        float *fractionals[TPITCH_MAX];
187        ALuint max;
188        ALuint middle; /* the index which pitch == 1.0 corresponds to */
189        ALuint len; /* length of offsets[0...TPITCH_MAX] in samples */
190} tpitch_lookup = { { NULL }, { NULL }, 0, 0, 0 };
191
192/* func associated with our tpitch lookup */
193static void init_tpitch_lookup(ALuint len);
194#endif
195
196static ALfloat compute_doppler_pitch(ALfloat *object1, ALfloat *o1_vel,
197                               ALfloat *object2, ALfloat *o2_vel,
198                               ALfloat factor, ALfloat speed);
199
200/*
201 * _alInitTimeFilters( time_filter_set *tf_ptr_ref )
202 *
203 * Initializes tf_ptr_ref to the current set of time filters, and initialize
204 * tpitch_lookup_max if it hasn't been initialized before.
205 */
206void _alInitTimeFilters( time_filter_set *tf_ptr_ref ) {
207        ALuint i = 0;
208
209        do {
210                tf_ptr_ref[i] = software_time_filters[i];
211        } while(software_time_filters[i++].filter != NULL);
212
213#if USE_TPITCH_LOOKUP
214        /*
215         * init tpitch_loopup only if it hasn't been initialized
216         * yet.
217         */
218        if(tpitch_lookup.max != TPITCH_MAX) {
219                tpitch_lookup.max    = TPITCH_MAX;
220                tpitch_lookup.middle = TPITCH_MAX / 2;
221
222                for(i = 0; i < tpitch_lookup.max; i++)
223                {
224                        free(tpitch_lookup.offsets[i]);
225                        free(tpitch_lookup.fractionals[i]);
226                        tpitch_lookup.offsets[i] = 0;
227                        tpitch_lookup.fractionals[i] = 0;
228                }
229        }
230#endif
231
232        return;
233}
234
235/*
236 * _alDestroyFilters( void )
237 *
238 * Deallocates data structures used by the filters and helper functions.
239 */
240void _alDestroyFilters( void ) {
241#if USE_TPITCH_LOOKUP
242        ALuint i;
243
244        for(i = 0; i < TPITCH_MAX; i++)
245        {
246                free(tpitch_lookup.offsets[i]);
247                free(tpitch_lookup.fractionals[i]);
248
249                tpitch_lookup.offsets[i] = 0;
250                tpitch_lookup.fractionals[i] = 0;
251        }
252        tpitch_lookup.len = 0;
253#endif
254
255        return;
256}
257
258/*
259 * _alApplyFilters( ALuint cid, ALuint sid )
260 *
261 * _alApplyFilters is called from the mixing function, and is passed
262 * a source id and the context where this sourceid has meaning.
263 *
264 * The filters that are applied to the source are determined by the
265 * context.  Each context is initialized such that it contains a table
266 * of the software filters.  Extensions and plugins can be later loaded
267 * to override the default functionality.  The point being, each context's
268 * filter "signature" may be different.
269 *
270 * assumes locked source sid
271 */
272void _alApplyFilters( ALuint cid, ALuint sid ) {
273        AL_source *src;
274        AL_buffer *samp;
275        time_filter_set *cc_tfilters;
276        time_filter *tf;
277        ALuint mixbuflen; /* byte size of total data to compose (all channels) */
278        ALint len;        /* byte size of one channel's worth of data to compose */
279        ALint filterlen;  /* filterlen is adjusted below to take into account looping, etc */
280        int ic;           /* internal (canon) chans   */
281        int mc;           /* mixer chans (==speakers) */
282        ALboolean *boolp;       /* for determining bool flags */
283        int i;
284
285        /* initialize */
286        ic = _alGetChannelsFromFormat( _ALC_CANON_FMT );
287
288        _alcLockContext( cid );
289
290        mc = _alcGetNumSpeakers( cid );
291        mixbuflen = _alcGetWriteBufsiz( cid );
292
293        samp = _alGetBufferFromSid( cid, sid );
294        if(samp == NULL) {
295                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
296                        "_alFilter: null samp, sid == %d", sid);
297
298                _alcUnlockContext( cid );
299                return;
300        }
301
302        _alcUnlockContext( cid );
303
304        len = mixbuflen * ((float) ic / mc);
305        filterlen = len;
306
307        /*
308         * Allocate scratch space to hold enough data for the source
309         * about to be split.  We allocate more space in case of a
310         * multichannel source.
311         */
312        if(f_buffers.len < len / sizeof (ALshort))
313        {
314                void *temp;
315                ALuint newlen = len * _alGetChannelsFromFormat(samp->format);
316
317                for(i = 0; i < mc; i++)
318                {
319                        temp = realloc(f_buffers.data[i], newlen);
320                        if(temp == NULL) {
321                                /* FIXME: do something */
322                        }
323
324                        f_buffers.data[i] = temp;
325                }
326
327                f_buffers.len = newlen;
328        }
329
330#if USE_TPITCH_LOOKUP
331        if(tpitch_lookup.len < (ALuint) len)
332        {
333                init_tpitch_lookup(len);
334        }
335#endif
336
337        src  = _alGetSource(cid, sid);
338        if(src == NULL) {
339                _alDebug(ALD_MAXIMUS, __FILE__, __LINE__,
340                        "_alFilter: null src, sid == %d", sid);
341                return;
342        }
343
344        /* streaming buffer?  set soundpos */
345        if(samp->flags & ALB_STREAMING) {
346                src->srcParams.soundpos = samp->streampos;
347
348                if(samp->streampos > samp->size) {
349                        memset(src->srcParams.outbuf, 0, len);
350
351#ifdef DEBUG_MAXIMUS
352                        fprintf(stderr, "underflow!!!!!!!!!!!!!!!!\n");
353#endif
354                        return; /* underflow */
355                }
356        }
357
358        _alSourceParamReset(src); /* reset srcParam settings */
359
360        _alSplitSources(cid, sid, mc, len, samp, (ALshort **) f_buffers.data);
361
362        /*
363         * translate head relative sources
364         */
365        boolp = _alGetSourceParam(src, AL_SOURCE_RELATIVE);
366
367        if(boolp != NULL) {
368                _alDebug(ALD_SOURCE, __FILE__, __LINE__,
369                "_alApplyFilters: sid %d relative boolp = %d", sid, *boolp );
370
371                if(*boolp == AL_TRUE) {
372                        /* This is a RELATIVE source, which means we must
373                         * translate it before applying any sort of positional
374                         * filter to it.
375                         */
376                        AL_context *cc;
377
378                        _alcLockContext( cid );
379
380                        cc = _alcGetContext(cid);
381                        if(cc != NULL) {
382                                _alSourceTranslate(src, cc->listener.position );
383                        }
384
385                        _alcUnlockContext( cid );
386                }
387        }
388
389        /*
390         * adjust len to account for end of sample, looping, etc
391         */
392        if(filterlen > _alSourceBytesLeft(src, samp))
393        {
394                /* John Quigley's patch, check it out -- jiv */
395               if((_alSourceIsLooping( src ) == AL_FALSE)
396                        && (src->srcParams.new_readindex == -1))
397                {
398                        /* Non looping source */
399                        filterlen = _alSourceBytesLeft(src, samp);
400                }
401        }
402
403        if(filterlen > 0)
404        {
405                cc_tfilters = _alcGetTimeFilters(cid);
406
407                /* apply time domain filters */
408                for(i = 0; cc_tfilters[i].filter != NULL; i++)
409                {
410                        tf = cc_tfilters[i].filter;
411
412                        tf(cid, src, samp, (ALshort **) f_buffers.data,
413                           mc, filterlen);
414                }
415
416                /*
417                 *  Apply gain and delay for filters that don't actually touch
418                 *  the data ( alf_da).
419                 */
420                _alSourceParamApply(src, mc, filterlen,
421                                    (ALshort **) f_buffers.data);
422        }
423
424
425        /*
426         * Take the resulting pcm data in f_buffers, and mix these into
427         * the source's temporary output buffer.
428         */
429        _alCollapseSource(cid, sid, mc, mixbuflen, (ALshort **) f_buffers.data);
430
431        /*
432         * head RELATIVE sources need to be untranslated, lest their
433         * position become weird.
434         */
435        if((boolp != NULL) && (*boolp == AL_TRUE)) {
436                AL_context *cc;
437                ALfloat ipos[3]; /* inverse listener position */
438
439                _alcLockContext( cid );
440
441                cc = _alcGetContext(cid);
442
443                if(cc != NULL) {
444                        _alVectorInverse(ipos, cc->listener.position);
445                        _alSourceTranslate(src, ipos);
446                }
447
448                _alcUnlockContext( cid );
449        }
450
451        return;
452}
453
454/*
455 * alf_coning
456 *
457 * Implements the coning filter, which is used when CONE_INNER_ANGLE
458 * or CONE_OUTER_ANGLE is set.  This is used for directional sounds.
459 *
460 * The spec is vague as to the actual requirements of directional sounds,
461 * and Carlo has suggested that we maintain the DirectSound meaning for
462 * directional sounds, namely (in my interpretation):
463 *
464 *    The inner, outer cone define three zones: inside inner cone
465 *    (INSIDE), between inner and outer cone (BETWEEN, outside outer cone,
466 *    (OUTSIDE).
467 *
468 *    In INSIDE, the gain of the sound is attenuated as a normal
469 *    positional source.
470 *
471 *    In OUTSIDE, the gain is set to some value specified by the user.
472 *
473 *    In BETWEEN, the gain is transitionally set to some value between
474 *    what it would be in INSIDE and OUTSIDE.
475 *
476 * This requires an additional source paramter, like CONE_OUTSIDE_ATTENUATION,
477 * and quite frankly seems goofy.  This implementation implements the
478 * following convention:
479 *
480 *    In INSIDE, the gain of the sound is attenuated as a normal
481 *    positional source.
482 *
483 *    In OUTSIDE, the gain is set to _AL_CUTTOFF_ATTENUATION
484 *
485 *    In BETWEEN, the gain is transitionally set to some value between
486 *    what it would be in INSIDE and OUTSIDE.
487 *
488 * Well, okay that's still pretty goofy.  Folks who want to set a
489 * minimum attenuation can stil do so using AL_SOURCE_ATTENUATION_MIN.
490 *
491 * IMPLEMENTATION:
492 * okay, we check the angle between the speaker position and
493 * the source's direction vector, using the source's position
494 * as the origin.  This angle we call theta.
495 *
496 * Then, we compare theta with the outer cone angle.  If it's greater,
497 * we use the min attenuation.  If it's less, we compare theta with
498 * the inner cone angle.  If it's greater, we attenuate as normal.
499 * Otherwise, we don't attenuate at all (full volume, pitch etc).
500 *
501 * assumes locked source
502 *
503 * FIXME: please check my math.
504 *        - with an AL_NONE distance model, should this do anything
505 *          at all?
506 */
507void alf_coning( ALuint cid,
508                 AL_source *src,
509                 UNUSED(AL_buffer *samp),
510                 UNUSED(ALshort **buffers),
511                 ALuint nc,
512                 UNUSED(ALuint len)) {
513        AL_context *cc;
514        ALfloat sa;  /* speaker attenuation */
515        ALfloat *sp; /* source position  */
516        ALfloat *sd; /* source direction */
517        ALfloat lp[3]; /* listener position */
518        ALfloat theta; /* angle between listener and source's direction
519                        * vector, with the source's position as origin.
520                        */
521        ALfloat srcDir[3];
522        ALfloat icone;      /* inner cone angle. */
523        ALfloat ocone;      /* outer cone angle.  */
524        ALfloat (*df)( ALfloat dist, ALfloat rolloff, ALfloat ref, ALfloat max ); /* distance model func */
525        ALfloat smax;       /* source specific max distance */
526        ALfloat ref;        /* source specific reference distance */
527        ALfloat gain;       /* source specific gain */
528        ALfloat outergain;  /* source specific outer gain */
529        ALfloat rolloff;    /* source specific rolloff factor */
530        void *temp;
531        ALuint i;
532
533        _alcLockContext( cid );
534        cc = _alcGetContext( cid );
535        if(cc == NULL) {
536                /* ugh.  bad context id */
537
538                _alcUnlockContext( cid );
539                return;
540        }
541
542        /*
543         * The source specific max is set to max at this point, but may be
544         * altered below of the application has set it.
545         */
546        smax = FLT_MAX;
547        df = cc->distance_func;
548
549        _alcUnlockContext( cid );
550
551        alGetListenerfv(AL_POSITION, lp);
552
553        /* If no direction set, return */
554        sd = _alGetSourceParam( src, AL_DIRECTION );
555        if(sd == NULL) {
556                /*
557                 * source has no direction (normal).  leave it for alf_da
558                 */
559                return;
560        }
561
562        sp = _alGetSourceParam( src, AL_POSITION );
563        if(sp == NULL) {
564                /* If no position set, return */
565
566                return;
567        }
568
569        /* get source specific ref distance */
570        temp = _alGetSourceParam( src, AL_REFERENCE_DISTANCE );
571        if( temp != NULL ) {
572                ref = * (ALfloat *) temp;
573        } else {
574                _alSourceGetParamDefault( AL_REFERENCE_DISTANCE, &ref );
575        }
576
577        /* get source specific gain */
578        temp = _alGetSourceParam( src, AL_GAIN );
579        if( temp != NULL ) {
580                gain =  * (ALfloat *) temp;
581        } else {
582                _alSourceGetParamDefault( AL_GAIN, &gain );
583        }
584
585        /* get source specific max distance */
586        temp = _alGetSourceParam( src, AL_MAX_DISTANCE );
587        if( temp != NULL ) {
588                smax =  * (ALfloat *) temp;
589        } else {
590                _alSourceGetParamDefault( AL_MAX_DISTANCE, &smax );
591        }
592
593        /* get source specific rolloff factor */
594        temp = _alGetSourceParam( src, AL_ROLLOFF_FACTOR );
595        if( temp != NULL ) {
596                rolloff =  * (ALfloat *) temp;
597        } else {
598                _alSourceGetParamDefault( AL_ROLLOFF_FACTOR, &rolloff );
599        }
600
601        srcDir[0] = sp[0] + sd[0];
602        srcDir[1] = sp[1] + sd[1];
603        srcDir[2] = sp[2] + sd[2];
604
605        /*
606         * Get CONE settings.
607         *
608         * If unset, use 360.0 degrees
609         */
610        temp = _alGetSourceParam( src, AL_CONE_INNER_ANGLE );
611        if(temp != NULL) {
612                icone = _alDegreeToRadian(* (ALfloat *) temp);
613        } else {
614                _alSourceGetParamDefault( AL_CONE_INNER_ANGLE, &icone );
615        }
616
617        temp = _alGetSourceParam( src, AL_CONE_OUTER_ANGLE );
618        if(temp != NULL) {
619                ocone = _alDegreeToRadian(* (ALfloat *) temp);
620        } else {
621                _alSourceGetParamDefault( AL_CONE_OUTER_ANGLE, &ocone );
622        }
623
624        temp = _alGetSourceParam( src, AL_CONE_OUTER_GAIN );
625        if(temp != NULL) {
626                outergain = * (ALfloat *) temp;
627        } else {
628                _alSourceGetParamDefault( AL_CONE_OUTER_GAIN, &outergain );
629        }
630
631        _alDebug(ALD_SOURCE, __FILE__, __LINE__,
632                "alf_coning: sid %d icone %f ocone %f", src->sid, icone, ocone );
633
634        theta = _alVectorAngleBetween(sp, lp, srcDir);
635
636        if( theta <= (icone / 2.0f) ) {
637                /*
638                 * INSIDE:
639                 *
640                 * attenuate normally
641                 */
642                _alDebug(ALD_SOURCE, __FILE__, __LINE__,
643                        "alf_coning: sid %d theta %f INSIDE",
644                        src->sid, theta );
645
646                /*
647                 * speaker[i] is in inner cone, don't do
648                 * anything.
649                 */
650                sa = compute_sa( sp, smax, ref, gain, rolloff, lp, df );
651        } else if( theta <= ( ocone / 2.0f) ) {
652                /*
653                 * BETWEEN:
654                 *
655                 * kind of cheesy, but we average the INSIDE
656                 * and OUTSIDE attenuation.
657                 */
658                _alDebug(ALD_SOURCE, __FILE__, __LINE__,
659                        "alf_coning: sid %d theta %f BETWEEN",
660                        src->sid, theta);
661
662                sa = compute_sa( sp, smax, ref, gain, rolloff, lp, df );
663
664                sa += _AL_CUTTOFF_ATTENUATION;
665                sa /= 2;
666        } else {
667                /*
668                 * OUTSIDE:
669                 *
670                 * Set to attenuation_min
671                 */
672                _alDebug(ALD_SOURCE, __FILE__, __LINE__,
673                        "alf_coning: sid %d theta %f OUTSIDE",
674                        src->sid, theta );
675
676                if( outergain < _AL_CUTTOFF_ATTENUATION ) {
677                        sa = _AL_CUTTOFF_ATTENUATION;
678                } else {
679                        sa = outergain;
680                }
681        }
682
683        for(i = 0; i < nc; i++) {
684                /* set gain, to be applied in SourceParamApply */
685                src->srcParams.gain[i] *= sa;
686        }
687
688        return;
689}
690
691/*
692 * alf_reverb
693 *
694 * As far as reverb implementations go, this sucks.  Should be
695 * frequency based?
696 *
697 * Should be able to be applied in sequence for second order
698 * approximations.
699 *
700 * FIXME: this is so ugly!  And consumes a ton of memory.
701 */
702void alf_reverb( UNUSED(ALuint cid),
703                 AL_source *src,
704                 AL_buffer *samp,
705                 ALshort **buffers,
706                 ALuint nc,
707                 ALuint len ) {
708        ALshort *bpt; /* pointer to passed buffers */
709        ALshort *rpt; /* pointer to reverb buffers */
710        ALuint i;
711        ALfloat scale = src->reverb_scale;
712        ALuint delay  = src->reverb_delay;
713        ALuint k;
714        int sample;
715
716        /* with a delay of 0.0, no reverb possible or needed */
717        if(!(src->flags & ALS_REVERB)) {
718                return;
719        }
720
721        /*
722         * initialize persistent reverb buffers if they haven't been
723         * done before
724         */
725        for(i = 0; i < nc; i++) {
726                if(src->reverb_buf[i] == NULL) {
727                        src->reverb_buf[i] = malloc(samp->size);
728                        memset(src->reverb_buf[i], 0, samp->size);
729                }
730        }
731
732        if(src->srcParams.soundpos > delay) {
733                int revoffset = ((src->srcParams.soundpos - delay) / sizeof(ALshort));
734
735                for(i = 0; i < nc; i++) {
736                        bpt  = buffers[i];
737                        rpt  = src->reverb_buf[i];
738                        rpt += revoffset;
739
740                        for(k = 0; k < len / sizeof(ALshort); k++) {
741                                sample = bpt[k] + rpt[k] * scale;
742
743                                if(sample > canon_max) {
744                                        sample = canon_max;
745                                } else if (sample < canon_min) {
746                                        sample = canon_min;
747                                }
748
749                                bpt[k] = sample;
750                        }
751                }
752        }
753
754        _alBuffersAppend(src->reverb_buf,
755                        (void **) buffers, len, src->reverbpos, nc);
756
757        src->reverbpos += len;
758
759        return;
760}
761
762/*
763 * alf_da
764 *
765 * alf_da implements distance attenuation.  We call compute_sa to get the
766 * per-speaker attenuation for each channel, and manipulate the srcParam gain
767 * settings to effect that computation.
768 *
769 * alf_da returns early if we discover that the source has
770 * either CONE_INNER_ANGLE or CONE_OUTER_ANGLE set (ie, is a
771 * directional source).  In those cases, alf_coning should do
772 * the distance attenuation.
773 *
774 * assumes locked source
775 *
776 * FIXME:
777 *    Remind me to clean this up.
778 */
779void alf_da( ALuint cid,
780             AL_source *src,
781             UNUSED(AL_buffer *samp),
782             UNUSED(ALshort **buffers),
783             ALuint nc,
784             UNUSED(ALuint len)) {
785        AL_context *cc;
786        ALfloat *sp; /* source position */
787        ALfloat sa;  /* speaker attenuation */
788        ALfloat listener_position[3];
789        ALfloat *temp;
790        ALuint i;
791        ALfloat (*df)( ALfloat dist, ALfloat rolloff, ALfloat ref, ALfloat max ); /* distance model func */
792        ALfloat gain; /* source specific gain */
793        ALfloat ref;  /* source specific ref distance */
794        ALfloat smax;        /* source specific max distance */
795        ALfloat rolloff; /* source specific rolloff factor */
796
797        /* get distance scale */
798        _alcLockContext( cid );
799        cc = _alcGetContext(cid);
800        if(cc == NULL) {
801                _alcUnlockContext( cid );
802
803                /* ugh.  bad context id */
804                return;
805        }
806
807        df = cc->distance_func;
808
809        _alcUnlockContext( cid );
810
811        /*
812         *
813         * The source specific max is set to max at this point, but may be
814         * altered below of the application has set it.
815         */
816        smax = FLT_MAX;
817
818        /*
819         * if coning is enabled for this source, then we want to
820         * let the coning filter take care of attenuating since
821         * it has more information then we do.
822         *
823         * We check the direction flag because coning may not
824         * be set (ie, they use defaults)
825         */
826        temp = _alGetSourceParam(src, AL_DIRECTION);
827        if(temp != NULL) {
828                /*
829                 * This sound has it's direction set, so leave it
830                 * to the coning filter.
831                 */
832                _alDebug( ALD_SOURCE, __FILE__, __LINE__,
833                        "Directional sound, probably not right" );
834
835                return;
836        }
837
838        /* ambient near listener */
839        alGetListenerfv(AL_POSITION, listener_position);
840
841        sp = _alGetSourceParam( src, AL_POSITION );
842        if(sp == NULL) {
843                /*
844                 * As an optimization, don't do any attenuation if
845                 * the source is relative and there's no position.
846                 */
847                ALboolean *isrel;
848
849                isrel = _alGetSourceParam( src, AL_SOURCE_RELATIVE );
850                if ( isrel && *isrel ) {
851                        ALfloat *gp = _alGetSourceParam(src, AL_GAIN);
852
853                        if(gp)
854                        {
855                                for(i = 0; i < _ALC_MAX_CHANNELS; i++)
856                                {
857                                        src->srcParams.gain[i] *= *gp;
858                                }
859                        }
860
861                        return;
862                }
863
864                /*
865                 * no position set, so set it to the listener
866                 * postition.  We should probably set it to
867                 * 0.0, 0.0, 0.0 instead.
868                 *
869                 * We fall through to get the MIN/MAX
870                 */
871                sp = listener_position;
872
873                _alDebug(ALD_SOURCE, __FILE__, __LINE__,
874                        "alf_da: setting to listener pos, probably not right");
875        }
876
877        /* set reference distance */
878        temp = _alGetSourceParam( src, AL_REFERENCE_DISTANCE );
879        if( temp != NULL ) {
880                ref = * (ALfloat *) temp;
881        } else {
882                _alSourceGetParamDefault( AL_REFERENCE_DISTANCE, &ref );
883        }
884
885        /* set source specific gain */
886        temp = _alGetSourceParam( src, AL_GAIN );
887        if( temp != NULL ) {
888                gain = * (ALfloat *) temp;
889        } else {
890                _alSourceGetParamDefault( AL_GAIN, &gain );
891        }
892
893        /* set source specific max distance */
894        temp = _alGetSourceParam( src, AL_MAX_DISTANCE );
895        if( temp != NULL ) {
896                smax =  * (ALfloat *) temp;
897        } else {
898                _alSourceGetParamDefault( AL_MAX_DISTANCE, &smax );
899        }
900
901        /* get source specific rolloff factor */
902        temp = _alGetSourceParam( src, AL_ROLLOFF_FACTOR );
903        if( temp != NULL ) {
904                rolloff =  * (ALfloat *) temp;
905        } else {
906                _alSourceGetParamDefault( AL_ROLLOFF_FACTOR, &rolloff );
907        }
908
909        sa = compute_sa( sp, smax, ref, gain, rolloff,
910                         listener_position, df );
911
912        for(i = 0; i < nc; i++) {
913                src->srcParams.gain[i] *= sa;
914        }
915
916        return;
917}
918
919#if USE_TPITCH_LOOKUP
920/*
921 * init_tpitch_lookup( ALuint len )
922 *
923 * Initializes the tpitch lookup table.  See declaration of tpitch_lookup for
924 * information on the layout and meaning of tpitch_lookup_init.
925 */
926static void init_tpitch_lookup( ALuint len ) {
927        ALfloat scale;
928        ALuint i;
929
930        if(tpitch_lookup.len >= len) {
931                /* We only go through the main loop if we
932                 * haven't been initialized, or have been
933                 * initialized with less memory than needed.
934                 */
935                return;
936        }
937        tpitch_lookup.len = len;
938
939        /*
940         * initialize time domain pitch filter lookup table
941         */
942
943        /*
944         * For pitch < 1.0, we lower the frequency such that a pitch of
945         * 0.5 corresponds to 1 octave drop.  Is this just a linear
946         * application of the step?
947         */
948        for(i = 0; i < tpitch_lookup.max; i++) {
949                ALfloat pitch;
950                ALuint j;
951
952                /* set offset part */
953                free(tpitch_lookup.offsets[i]);
954                tpitch_lookup.offsets[i] = malloc(sizeof *tpitch_lookup.offsets * len);
955                /* set fractional part */
956                free(tpitch_lookup.fractionals[i]);
957                tpitch_lookup.fractionals[i] = malloc(sizeof *tpitch_lookup.fractionals * len);
958
959                /* set iterate pitch */
960                pitch = 2.0 * ((float)i / (float)tpitch_lookup.max);
961
962                /* initialize offset table */
963                scale = 0.0f;
964
965                for(j = 0; j < len; j++)
966                {
967                        float foffset = j * pitch;
968                        ALuint offset = (int) foffset;
969                        float frac = foffset - offset;
970
971                        tpitch_lookup.offsets[i][j] = offset;
972                        tpitch_lookup.fractionals[i][j] = frac;
973                }
974        }
975
976        return;
977}
978#endif
979
980/*
981 * alf_tdoppler
982 *
983 * This filter acts out the doppler effects, in the time domain as
984 * opposed to frequency domain.  This filter works by computing the pitch
985 * required to represent the doppler shift, and setting the AL_PITCH attribute
986 * of the source directly.
987 *
988 * FIXME:
989 *    It's not a good idea to mess with src's pitch.  Some method of
990 *    expressing this computation without changing the source's attributes
991 *    should be used.
992 *
993 */
994void alf_tdoppler( ALuint cid,
995                   AL_source *src,
996                   UNUSED(AL_buffer *samp),
997                   UNUSED(ALshort **buffers),
998                   UNUSED(ALuint nc),
999                   UNUSED(ALuint len) ) {
1000        AL_context *cc;
1001        ALfloat *sv; /* source velocity */
1002        ALfloat *sp; /* source position */
1003        ALfloat lv[3]; /* listener velocity */
1004        ALfloat lp[3]; /* listener position */
1005        ALfloat relative_velocity;  /* speed of source wrt listener */
1006#if 0
1007        ALfloat zeros[] = { 0.0, 0.0, 0.0 };
1008#endif
1009        AL_sourcestate *srcstate;
1010        ALfloat doppler_factor;
1011        ALfloat doppler_velocity;
1012        ALfloat doppler_pitch;
1013
1014        /* initialize the mixrate */
1015        if(src->pitch.isset == AL_TRUE)
1016        {
1017                src->mixrate = src->pitch.data;
1018        }
1019        else
1020        {
1021                src->mixrate = 1.0;
1022        }
1023
1024        /* lock context, get context specific stuff */
1025        _alcLockContext( cid );
1026
1027        cc = _alcGetContext(cid);
1028        if( cc == NULL ) {
1029                /* cid is an invalid context id. */
1030                _alcUnlockContext( cid );
1031
1032                return;
1033        }
1034
1035        doppler_factor   = cc->doppler_factor;
1036        doppler_velocity = cc->doppler_velocity;
1037
1038        _alcUnlockContext( cid );
1039
1040        alGetListenerfv(AL_VELOCITY, lv);
1041        alGetListenerfv(AL_POSITION, lp);
1042
1043        sp = _alGetSourceParam(src, AL_POSITION );
1044        sv = _alGetSourceParam(src, AL_VELOCITY );
1045
1046        if(sp == NULL) {
1047                return;
1048        }
1049
1050        if(sv == NULL) {
1051                /* no velocity set, no doppler effect */
1052                return;
1053        }
1054
1055        if (fabs(doppler_factor) < 1.0E-6f) {
1056                /* doppler factor set to zero, no doppler effect */
1057                return;
1058        }
1059
1060#if 0
1061        /* ToDo: duplicate test */
1062        if(sv == NULL) {
1063                /*
1064                 * if unset, set to the velocity to the
1065                 * zero vector.
1066                 */
1067                sv = zeros;
1068        }
1069#endif
1070
1071        relative_velocity = _alVectorMagnitude(sv, lv);
1072        if(relative_velocity == 0.0) {
1073                /*
1074                 * no relative velocity, no doppler
1075                 *
1076                 * FIXME: use epsilon
1077                 */
1078
1079                return;
1080        }
1081
1082
1083        srcstate = _alSourceQueueGetCurrentState(src);
1084        if(srcstate == NULL) {
1085                fprintf(stderr, "weird\n");
1086        }
1087
1088        doppler_pitch = compute_doppler_pitch(lp, lv, sp, sv,
1089                                        doppler_factor, doppler_velocity);
1090
1091        src->mixrate *= doppler_pitch;
1092
1093#ifdef DEBUG
1094        if(src->mixrate < MIN_PITCH)
1095        {
1096                _alDebug(ALD_FILTER, __FILE__, __LINE__,
1097                         "Clamping src->mixrate %f\n",
1098                         src->mixrate);
1099        }
1100#endif
1101
1102        src->mixrate = MAX(src->mixrate, MIN_PITCH);
1103        src->mixrate = MIN(src->mixrate, 2.0f);
1104
1105        return;
1106}
1107
1108/*
1109 * alf_minmax
1110 *
1111 * Implements min/max gain.  First min is applied, then max.
1112 */
1113void alf_minmax( UNUSED(ALuint cid),
1114                 AL_source *src,
1115                 UNUSED(AL_buffer *samp),
1116                 UNUSED(ALshort **buffers),
1117                 ALuint nc,
1118                 UNUSED(ALuint len) ) {
1119        ALfloat *amaxp = _alGetSourceParam( src, AL_MAX_GAIN );
1120        ALfloat *aminp = _alGetSourceParam( src, AL_MIN_GAIN );
1121        ALfloat attenuation_min;
1122        ALfloat attenuation_max;
1123        ALuint i;
1124
1125        /*
1126         * if min or max are set, use them.  Otherwise, keep defaults
1127         */
1128        if(aminp != NULL) {
1129                attenuation_min = *aminp;
1130        } else {
1131                _alSourceGetParamDefault( AL_MIN_GAIN, &attenuation_min );
1132        }
1133
1134        if(amaxp != NULL) {
1135                attenuation_max = *amaxp;
1136        } else {
1137                _alSourceGetParamDefault( AL_MAX_GAIN, &attenuation_max );
1138        }
1139
1140        for(i = 0; i < nc; i++) {
1141                if( src->srcParams.gain[i] > attenuation_max ) {
1142                        src->srcParams.gain[i] = attenuation_max;
1143                } else if( src->srcParams.gain[i] < attenuation_min ) {
1144                        src->srcParams.gain[i] = attenuation_min;
1145                }
1146        }
1147
1148        return;
1149}
1150
1151/*
1152 * alf_listenergain
1153 *
1154 * Implements listener gain.
1155 */
1156void
1157alf_listenergain( UNUSED(ALuint cid),
1158                  AL_source *src,
1159                  UNUSED(AL_buffer *samp),
1160                  UNUSED(ALshort **buffers),
1161                  ALuint nc,
1162                  UNUSED(ALuint len) )
1163{
1164        ALfloat gain;
1165        ALuint i;
1166        alGetListenerfv(AL_GAIN, &gain);
1167        for(i = 0; i < nc; i++) {
1168                src->srcParams.gain[i] *= gain;
1169        }
1170}
1171
1172/*
1173 * compute_doppler_pitch( ALfloat *object1, ALfloat *o1_vel,
1174 *                        ALfloat *object2, ALfloat *o2_vel,
1175 *                        ALfloat factor,
1176 *                        ALfloat speed )
1177 *
1178 * compute_doppler_pitch is meant to return a value spanning 0.5 to 1.5,
1179 * which is meant to simulate the frequency shift undergone by sources
1180 * in relative movement wrt the listener.
1181 */
1182static ALfloat compute_doppler_pitch( ALfloat *object1, ALfloat *o1_vel,
1183                                      ALfloat *object2, ALfloat *o2_vel,
1184                                      ALfloat factor,  /* doppler_factor */
1185                                      ALfloat speed ) { /* propagation_speed */
1186        ALfloat between[3];       /* Unit vector pointing in the direction
1187                                   * from one object to the other
1188                                   */
1189        ALfloat obj1V, obj2V;     /* Relative scalar velocity components */
1190        ALfloat ratio;            /* Ratio of relative velocities */
1191        ALfloat retval;           /* final doppler shift */
1192
1193        /*
1194         * Set up the "between" vector which points from one object to the
1195         * other
1196         */
1197        between[0] = object2[0] - object1[0];
1198        between[1] = object2[1] - object1[1];
1199        between[2] = object2[2] - object1[2];
1200
1201        _alVectorNormalize( between, between );
1202
1203        /*
1204         * Compute the dot product of the velocity vector and the "between"
1205         * vector.
1206         *
1207         * The _alVectorDotp function is not set up for computing dot products
1208         * for actual vectors (it works for three points that define two
1209         * vectors from a common origin), so I'll do it here.
1210         */
1211        obj1V  = o1_vel[0] * between[0];
1212        obj1V += o1_vel[1] * between[1];
1213        obj1V += o1_vel[2] * between[2];
1214
1215        /* Now compute the dot product for the second object */
1216        obj2V  = o2_vel[0] * between[0];
1217        obj2V += o2_vel[1] * between[1];
1218        obj2V += o2_vel[2] * between[2];
1219
1220        /*
1221         * Apply the Doppler factor by modifying the source and listener
1222         * velocities.  This will exaggerate or reduce the Doppler
1223         * effect as expected.
1224         */
1225        obj1V *= factor;
1226        obj2V *= factor;
1227
1228        /*
1229         * Now compute the obj1/obj2 velocity ratio, taking into account
1230         * the propagation speed.  This formula is straight from the spec.
1231         */
1232        obj1V = speed - obj1V;
1233        obj2V = speed + obj2V;
1234        ratio = obj1V / obj2V;
1235
1236        /* Finally, return the ratio */
1237        retval = ratio;
1238
1239        return retval;
1240}
1241
1242#if USE_TPITCH_LOOKUP
1243/*
1244 * alf_tpitch
1245 *
1246 * this filter acts out AL_PITCH.
1247 *
1248 * This filter is implements AL_PITCH, but - oh-ho! - in the
1249 * time domain.  All that good fft mojo going to waste.
1250 */
1251void alf_tpitch( UNUSED(ALuint cid),
1252                 AL_source *src,
1253                 AL_buffer *samp,
1254                 ALshort **buffers,
1255                 ALuint nc,
1256                 ALuint len ) {
1257        ALshort *obufptr = NULL; /* pointer to unmolested buffer data */
1258        ALshort *bufptr  = NULL;  /* pointer to buffers[0..nc-1] */
1259        ALuint l_index;   /* index into lookup table */
1260        ALint ipos = 0;   /* used to store offsets temporarily */
1261        ALuint i;
1262        int *offsets;        /* pointer to set of offsets in lookup table */
1263        float *fractionals;  /* pointer to set of fractionals in lookup table */
1264        int bufchans;
1265        ALfloat pitch;
1266
1267        pitch = src->mixrate;
1268       
1269        if (pitch == 1.0 && !(src->flags & ALS_NEEDPITCH)) {
1270                /*
1271                 * mixrate is at the default, so changing pitch is unnecessary.
1272                 */
1273                return;
1274        }
1275
1276        bufchans = _alGetChannelsFromFormat(samp->format); /* we need bufchans to
1277                                                      * scale our increment
1278                                                      * of the soundpos,
1279                                                      * because of
1280                                                      * multichannel format
1281                                                      * buffers.
1282                                                      */
1283        /*
1284         * if pitch is out of range, return.
1285         */
1286        if(pitch <= 0.0f)
1287        {
1288                _alDebug(ALD_FILTER, __FILE__, __LINE__,
1289                        "pitch out of range: %f, clamping", pitch);
1290                pitch = 0.05f;
1291        }
1292        else if (pitch > 2.0f)
1293        {
1294                _alDebug(ALD_FILTER, __FILE__, __LINE__,
1295                        "pitch out of range: %f, clamping", pitch);
1296                pitch = 2.0f;
1297        }
1298
1299        if(_alBufferIsCallback(samp) == AL_TRUE) {
1300                /* just debugging here, remove this block */
1301
1302                _alDebug(ALD_BUFFER, __FILE__, __LINE__,
1303                      "No tpitch support for callbacks yet");
1304
1305                /* _alSetError(cid, AL_INVALID_OPERATION); */
1306                return;
1307        }
1308
1309        /*
1310         *  We need len in samples, not bytes.
1311         */
1312        len /= sizeof(ALshort);
1313
1314        /* convert pitch into index in our lookup table */
1315        l_index = (pitch / 2.0) * tpitch_lookup.max;
1316
1317        /*
1318         * sanity check.
1319         */
1320        if(l_index >= tpitch_lookup.max) {
1321                l_index = tpitch_lookup.max - 1;
1322        }
1323
1324        _alDebug(ALD_FILTER, __FILE__, __LINE__,
1325              "pitch %f l_index %d", pitch, l_index);
1326
1327        /*
1328         * offsets is our set of pitch-scaled offsets, 0...pitch * len.
1329         *
1330         * Well, sort of.  0...pitch * len, but with len scaled such
1331         * that we don't suffer a overrun if the buffer's original
1332         * data is too short.
1333         */
1334        offsets = tpitch_lookup.offsets[ l_index ];
1335
1336#ifdef DEBUG_MEM
1337        assert(l_index < TPITCH_MAX);
1338#endif
1339
1340        /*
1341         * Iterate over each buffers[0..nc-1]
1342         */
1343        for(i = 0; i < nc; i++) {
1344                ALint clen = len;
1345                int j;
1346
1347                /*
1348                 * Kind of breaking convention here and actually using
1349                 * the original buffer data instead of just resampling
1350                 * inside the passed buffer data.  This is because we
1351                 * won't have enough data to resample pitch > 1.0.
1352                 *
1353                 * We offset our original buffer pointer by the source's
1354                 * current position, but in samples, not in bytes
1355                 * (which is what src->srcParams.soundpos is in).
1356                 */
1357                obufptr  = samp->orig_buffers[i];
1358                obufptr += src->srcParams.soundpos / sizeof *obufptr;
1359
1360#ifdef DEBUG_MEM
1361                assert(samp->orig_buffers[i]);
1362                assert(src->srcParams.soundpos < samp->size);
1363#endif
1364
1365                if(l_index == tpitch_lookup.middle ) {
1366                        /* when this predicate is true, the pitch is
1367                         * equal to 1, which means there is no change.
1368                         * Therefore, we short circuit.
1369                         *
1370                         * Because we're incrementing the soundpos here,
1371                         * we can't just return.
1372                         */
1373
1374                        continue;
1375                }
1376
1377                /*
1378                 * set bufptr to the pcm channel that we
1379                 * are about to change in-place.
1380                 */
1381                bufptr = buffers[i];
1382
1383                /*
1384                 * We mess with offsets in the loop below, so reset it
1385                 * after each iteration.
1386                 */
1387                offsets = tpitch_lookup.offsets[ l_index ];
1388                fractionals = tpitch_lookup.fractionals[ l_index ];
1389
1390                /* don't run past end */
1391                if(((clen + 1) * pitch * sizeof(ALshort)) >=
1392                   (samp->size - src->srcParams.soundpos))
1393                {
1394                        clen = samp->size - src->srcParams.soundpos;
1395                        clen /= pitch;
1396                        clen /= sizeof(ALshort);
1397                        clen -= 1;
1398                }
1399
1400                /*
1401                 * this is where the "resampling" takes place.  We do a
1402                 * very little bit on unrolling here, and it shouldn't
1403                 * be necessary, but seems to improve performance quite
1404                 * a bit.
1405                 */
1406                for(j = 0; j < clen; j++)
1407                {
1408#if USE_LRINT
1409                        {
1410                                int offset = offsets[j];
1411                                int nextoffset = offsets[j+1];
1412                                float frac = fractionals[j];
1413                                float firstsample = obufptr[offset];
1414                                float nextsample = obufptr[nextoffset];
1415                                int finalsample;
1416
1417                                /* do a little interpolation */
1418                                finalsample = lrintf(firstsample +
1419                                            frac * (nextsample - firstsample));
1420
1421                                finalsample = MIN(finalsample, canon_max);
1422                                bufptr[j] =   MAX(finalsample, canon_min);
1423                        }
1424#else
1425                        {
1426                                int offset = offsets[j];
1427                                int nextoffset = offsets[j+1];
1428                                float frac = fractionals[j];
1429                                int firstsample = obufptr[offset];
1430                                int nextsample = obufptr[nextoffset];
1431                                int finalsample;
1432
1433                                /* do a little interpolation */
1434                                finalsample = firstsample +
1435                                            frac * (nextsample - firstsample);
1436
1437                                finalsample = MIN(finalsample, canon_max);
1438                                bufptr[j] =   MAX(finalsample, canon_min);
1439                        }
1440#endif
1441                }
1442
1443                /* zero off end */
1444                memset(&bufptr[j], 0, (len-j)*sizeof *bufptr);
1445        }
1446
1447        /*
1448         *  Set offsets to a known good state.
1449         */
1450        offsets = tpitch_lookup.offsets[l_index];
1451
1452        /*
1453         *  AL_PITCH (well, alf_tpitch actually) require that the
1454         *  main mixer func does not increment the source's soundpos,
1455         *  so we must increment it here.  If we detect an overrun, we
1456         *  must reset the src's soundpos to something reasonable.
1457         */
1458        ipos = (int) (len * pitch);
1459        src->srcParams.soundpos += bufchans * ipos * sizeof(ALshort);
1460
1461        if(src->srcParams.soundpos > samp->size)
1462        {
1463                /*
1464                 * we've reached the end of this sample.
1465                 *
1466                 * Since we're handling the soundpos incrementing for
1467                 * this source (usually done in _alMixSources), we have
1468                 * to handle all the special cases here instead of
1469                 * delegating them.
1470                 *
1471                 * These include callback, looping, and streaming
1472                 * sources.  For now, we just handle looping and
1473                 * normal sources, as callback sources will probably
1474                 * require added some special case logic to _alSplitSources
1475                 * to give up a little more breathing room.
1476                 */
1477                if( _alSourceIsLooping( src ) == AL_TRUE ) {
1478                        /*
1479                         * looping source
1480                         *
1481                         * FIXME:
1482                         *      This isn't right.  soundpos should be set to
1483                         *      something different, and we may need to carry
1484                         *      over info so that the sound loops properly.
1485                         */
1486
1487                        /* FIXME: kind of kludgy */
1488                        src->srcParams.soundpos = 0;
1489                } else {
1490                        /*
1491                         * let _alMixSources know it's time for this source
1492                         * to die.
1493                         */
1494                        _alDebug(ALD_FILTER, __FILE__, __LINE__,
1495                                 "tpitch: source ending");
1496                        src->srcParams.soundpos = samp->size;
1497                }
1498        }
1499
1500        return;
1501}
1502#else
1503/*
1504 * alf_tpitch
1505 *
1506 * this filter acts out AL_PITCH.
1507 *
1508 * This filter is implements AL_PITCH, but - oh-ho! - in the
1509 * time domain.  All that good fft mojo going to waste.
1510 */
1511void alf_tpitch( UNUSED(ALuint cid),
1512                 AL_source *src,
1513                 AL_buffer *samp,
1514                 ALshort **buffers,
1515                 ALuint nc,
1516                 ALuint len )
1517{
1518        ALshort *obufptr = NULL; /* pointer to unmolested buffer data */
1519        ALshort *bufptr  = NULL;  /* pointer to buffers[0..nc-1] */
1520        ALint ipos = 0;   /* used to store offsets temporarily */
1521        ALuint i;
1522        ALuint clen;
1523        int bufchans;
1524        ALfloat pitch;
1525
1526        pitch = src->mixrate;
1527       
1528        if (pitch == 1.0 && !(src->flags & ALS_NEEDPITCH)) {
1529                /*
1530                 * mixrate is at the default, so changing pitch is unnecessary.
1531                 */
1532                return;
1533        }
1534
1535        bufchans = _alGetChannelsFromFormat(samp->format); /* we need bufchans to
1536                                                      * scale our increment
1537                                                      * of the soundpos,
1538                                                      * because of
1539                                                      * multichannel format
1540                                                      * buffers.
1541                                                      */
1542        /*
1543         * if pitch is out of range, clamp.
1544         */
1545        pitch = MIN(pitch, 2.0f);
1546        pitch = MAX(pitch, MIN_PITCH);
1547
1548        /*
1549         *  We need len in samples, not bytes.
1550         */
1551        len /= sizeof(ALshort);
1552
1553        _alDebug(ALD_FILTER, __FILE__, __LINE__, "pitch %f", pitch);
1554
1555        /*
1556         * Iterate over each buffers[0..nc-1]
1557         */
1558        for(i = 0; i < nc; i++) {
1559                ALuint j;
1560
1561                if(pitch == 1.0f)
1562                {
1563                        continue;
1564                }
1565
1566                /*
1567                 * Kind of breaking convention here and actually using
1568                 * the original buffer data instead of just resampling
1569                 * inside the passed buffer data.  This is because we
1570                 * won't have enough data to resample pitch > 1.0.
1571                 *
1572                 * We offset our original buffer pointer by the source's
1573                 * current position, but in samples, not in bytes
1574                 * (which is what src->srcParams.soundpos is in).
1575                 */
1576                obufptr  = samp->orig_buffers[i];
1577                obufptr += src->srcParams.soundpos / sizeof *obufptr;
1578
1579                /*
1580                 * set bufptr to the pcm channel that we
1581                 * are about to change in-place.
1582                 */
1583                bufptr = buffers[i];
1584
1585                clen = len;
1586
1587                /* don't run past end */
1588                if(((clen + 1) * pitch * sizeof(ALshort)) >=
1589                   (samp->size - src->srcParams.soundpos))
1590                {
1591                        clen = samp->size - src->srcParams.soundpos;
1592                        clen /= pitch;
1593                        clen /= sizeof(ALshort);
1594                        clen -= 1;
1595                }
1596
1597                /*
1598                 * this is where the "resampling" takes place.  We do a
1599                 * very little bit on unrolling here, and it shouldn't
1600                 * be necessary, but seems to improve performance quite
1601                 * a bit.
1602                 */
1603                for(j = 0; j < clen; j++)
1604                {
1605                        /* make sure we don't go past end of last source */
1606#ifdef DEBUG_FILTER
1607                        assert(((j+1)*pitch)*2 <
1608                                samp->size - src->srcParams.soundpos);
1609#endif
1610                        {
1611                                float foffset = j * pitch;
1612                                int offset = (int) foffset;
1613                                float frac = foffset - offset;
1614                                int firstsample = obufptr[(int) (j * pitch)];
1615                                int nextsample = obufptr[(int)((j+1) * pitch)];
1616                                int finalsample;
1617
1618                                /* do a little interpolation */
1619                                finalsample = firstsample +
1620                                            frac * (nextsample - firstsample);
1621
1622                                finalsample = MIN(finalsample, canon_max);
1623                                bufptr[j] =   MAX(finalsample, canon_min);
1624                        }
1625                }
1626
1627                /* JIV FIXME: use memset */
1628                for( ; j < len; j++)
1629                {
1630                        bufptr[j] = 0;
1631                }
1632        }
1633
1634        /*
1635         *  AL_PITCH (well, alf_tpitch actually) require that the
1636         *  main mixer func does not increment the source's soundpos,
1637         *  so we must increment it here.  If we detect an overrun, we
1638         *  must reset the src's soundpos to something reasonable.
1639         */
1640        ipos = (int) (len * pitch);
1641        src->srcParams.soundpos += bufchans * ipos * sizeof(ALshort);
1642
1643        if(src->srcParams.soundpos > samp->size)
1644        {
1645                /*
1646                 * we've reached the end of this sample.
1647                 *
1648                 * Since we're handling the soundpos incrementing for
1649                 * this source (usually done in _alMixSources), we have
1650                 * to handle all the special cases here instead of
1651                 * delegating them.
1652                 *
1653                 * These include callback, looping, and streaming
1654                 * sources.  For now, we just handle looping and
1655                 * normal sources, as callback sources will probably
1656                 * require added some special case logic to _alSplitSources
1657                 * to give up a little more breathing room.
1658                 */
1659                if( _alSourceIsLooping( src ) == AL_TRUE ) {
1660                        /*
1661                         * looping source
1662                         *
1663                         * FIXME:
1664                         *      This isn't right.  soundpos should be set to
1665                         *      something different, and we may need to carry
1666                         *      over info so that the sound loops properly.
1667                         */
1668
1669                        /* FIXME: kind of kludgy */
1670                        src->srcParams.soundpos = 0;
1671                } else {
1672                        /*
1673                         * let _alMixSources know it's time for this source
1674                         * to die.
1675                         */
1676                        _alDebug(ALD_FILTER, __FILE__, __LINE__,
1677                                 "tpitch: source ending");
1678
1679                        src->srcParams.soundpos = samp->size;
1680                }
1681        }
1682
1683        return;
1684}
1685#endif
1686
1687
1688/*
1689 * compute_sa( ALfloat *source_pos, ALfloat source_max,
1690 *             ALfloat source_ref, ALfloat source_gain,
1691 *             ALfloat source_rolloff,
1692 *             ALfloat *speaker_pos,
1693 *             ALfloat (*df)( ALfloat dist, ALfloat rolloff,
1694 *                            ALfloat ref, ALfloat max))
1695 *
1696 * computes distance attenuation with respect to a speaker position.
1697 *
1698 * This is some normalized value which gets expotenially closer to 1.0
1699 * as the source approaches the listener.  The minimum attenuation is
1700 * AL_CUTTOFF_ATTENUATION, which approached when the source approaches
1701 * the max distance.
1702 *
1703 * source_pos = source position   [x/y/z]
1704 * source_max = source specific max distance
1705 * speaker_pos = speaker position [x/y/z]
1706 * ref        = source's reference distance
1707 * df         = distance model function
1708 * max        = maximum distance, beyond which everything is clamped at
1709 *              some small value near, but not equal to, zero.
1710 */
1711static ALfloat
1712compute_sa( ALfloat *source_pos, ALfloat source_max,
1713            ALfloat source_ref, ALfloat source_gain,
1714            ALfloat source_rolloff,
1715            ALfloat *speaker_pos,
1716            ALfloat (*df)( ALfloat dist, ALfloat rolloff, ALfloat ref, ALfloat max))
1717{
1718        ALfloat retval;
1719
1720        /* "Optimize" for rolloff == 0.0 */
1721        if (source_rolloff > 0.0) {
1722                ALfloat distance;
1723                distance = _alVectorMagnitude( source_pos, speaker_pos );
1724                retval = source_gain * df( distance, source_rolloff, source_ref, source_max );
1725        } else {
1726                retval = source_gain;
1727        }
1728
1729        if( retval > 1.0 ) {
1730                return 1.0;
1731        }
1732
1733        if(retval < _AL_CUTTOFF_ATTENUATION) {
1734                return _AL_CUTTOFF_ATTENUATION;
1735        }
1736
1737        return retval;
1738}
1739
1740/*
1741 * alf_panning
1742 *
1743 */
1744
1745void alf_panning( ALuint cid,
1746                 AL_source *src,
1747                 UNUSED(AL_buffer *samp),
1748                 UNUSED(ALshort **buffers),
1749                 ALuint nc,
1750                 UNUSED(ALuint len) ) {
1751        ALfloat lp[3]; /* listener position */
1752        ALfloat *sp; /* source position */
1753        ALfloat *sd; /* speaker position */
1754        ALfloat m;
1755        ALfloat sa;
1756        ALuint i;
1757
1758        alGetListenerfv(AL_POSITION, lp);
1759        sp = _alGetSourceParam(src, AL_POSITION );
1760
1761        if ((sp == NULL) || (lp == NULL)) {
1762                return;
1763        }
1764
1765        m = _alVectorMagnitude(lp, sp);
1766        if (m == 0) {
1767                /* should this use epsilon? */
1768                return;
1769        }
1770
1771        for (i = 0; i < nc; i++) {
1772                sd = _alcGetSpeakerPosition(cid, i);
1773                sa = _alVectorDotp(lp, sp, sd) / m;
1774                sa += 1.0;
1775
1776                src->srcParams.gain[i] *= sa;
1777        }
1778}
Note: See TracBrowser for help on using the repository browser.