1 #include "audio/nebu_Source3D.h"
2 
3 #include <assert.h>
4 #include <string.h>
5 
6 Uint8 tmp[65536];
7 
8 #define USOUND 50
9 #define EPSILON 0.1f
10 #define SOUND_VOL_THRESHOLD 0.1
11 #define VOLSCALE_BASE 1000
12 
13 #define MAX(x,y) (( (x) > (y) ) ? (x) : (y))
14 
15 /*!
16   \fn int fxShift(float shift, Uint8 *target, Uint8 *source, int len)
17 
18   \param shift   Shift of the frequency (new_frequency = shift * old_frequency)
19   \param target  Target buffer to mix into; format: Sint16 LR stereo
20   \param source  The buffer to mix from
21   \param len     The amount of bytes to mix into the target buffer
22 
23   \return        The amount of samples read from the source
24 */
25 
fxComputeShiftLen(float shift,int len)26 int fxComputeShiftLen(float shift, int len) {
27   return (int) ( (len - 1) * shift + 1 );
28 }
29 
fxShift(float shift,Uint8 * target,Uint8 * source,int len)30 int fxShift(float shift, Uint8 *target, Uint8 *source, int len) {
31   int i, j;
32 
33   // amount of samples to mix/write
34   len /= 4;
35 
36   for(i = 0; i < len; i++) { // LR pairs
37     for(j = 0; j < 2; j++) { // channels
38       Sint32 result = 0;
39       int pa;
40       float t;
41 
42       pa = (int) (i * shift);
43       t = (i * shift) - pa;
44 
45       result = (Sint32) (
46 	*( (Sint16*) target + j + 2 * i) * 1.0f +
47 	*( (Sint16*) source + j + 2 * (pa + 0) ) * (1.0f - t) +
48 	*( (Sint16*) source + j + 2 * (pa + 1) ) * (t) );
49 
50 #define MAX_SINT16 ((1 << 15) - 1)
51 #define MIN_SINT16 (- (1 << 15) )
52       if(result > MAX_SINT16) {
53 	result = MAX_SINT16;
54 	fprintf(stderr, "overflow\n");
55       } else if(result < MIN_SINT16) {
56 	result = MIN_SINT16;
57 	fprintf(stderr, "underflow\n");
58       }
59 
60       *( (Sint16*) target + j + 2 * i ) = (Sint16) result;
61       /* *(Sint16*) (target + 2 * j + 4 * i) += (Sint16)
62 	 ( *(Sint16*) (source + 2 * j + 4 * (k + 0) ) * ( 1 - l ) +
63 	 *(Sint16*) (source + 2 * j + 4 * (k + 2) ) * ( l ) ); */
64 
65     }
66   }
67   return 4 * fxComputeShiftLen(shift, len);
68 }
69 
70 /*!
71   \fn void fxPan(float pan, float vol, Uint8 *buf, int len)
72 
73   \param vol   Volume (for distance attenuation), should be between 0 and 1
74   \param pan   Panning, -1.0 is left, 1.0 is right, 0.0 is center
75   \param buf   The sample to be modified in-place, format: Sint16 LR stereo
76   \param len   Number of bytes
77 */
78 
fxPan(float pan,float vol,Uint8 * buf,int len)79 void fxPan(float pan, float vol, Uint8 *buf, int len) {
80   int i;
81 
82   float left_vol =  - vol * ( -1.0f + pan ) / 2.0f;
83   float right_vol = vol * ( 1.0f + pan ) / 2.0f;
84 
85   for(i = 0; i < len; i += 4) {
86     *(Sint16*) (buf + i) = // *= left_vol
87       (Sint16) (left_vol * *(Sint16*) (buf + i) );
88     *(Sint16*) (buf + i + 2) = // *= right_vol
89       (Sint16) (right_vol * *(Sint16*) (buf + i + 2) );
90   }
91 }
92 
93 namespace Sound {
94 
GetModifiers(float & fPan,float & fVolume,float & fShift)95   void Source3D::GetModifiers(float& fPan, float& fVolume, float& fShift) {
96 
97     Vector3& vSourceLocation = _location;
98     Vector3& vSourceVelocity = _velocity;
99 
100     Listener listener = _system->GetListener();
101     Vector3& vListenerLocation = listener._location;
102     Vector3 vListenerVelocity = listener._velocity;
103     Vector3 vListenerDirection = listener._direction;
104     Vector3 vListenerUp = listener._up;
105 
106     if( (vSourceLocation - vListenerLocation).Length() < EPSILON  ) {
107       fPan = 0;
108       fVolume = 1.0f;
109       fShift = 1.0f;
110       return;
111     }
112 
113     vListenerDirection.Normalize();
114     vListenerUp.Normalize();
115     Vector3 vListenerLeft = vListenerDirection.Cross( vListenerUp );
116     vListenerLeft.Normalize();
117 
118     /* panning */
119     Vector3 vTarget = vSourceLocation - vListenerLocation;
120 		Vector3 v1 = vListenerLeft * ( vTarget * vListenerLeft );
121 		Vector3 v2 = vListenerDirection * (vTarget * vListenerDirection );
122     Vector3 vTargetPlanar = v1 + v2;
123 
124     float cosPhi =
125       vTargetPlanar.Normalize() *
126       vListenerDirection;
127 
128     fPan = 1 - (float)fabs(cosPhi);
129 
130     if( vTargetPlanar * vListenerLeft < 0 )
131       fPan = -fPan;
132 
133   /* done panning */
134 
135   /* attenuation */
136     // float fallOff = vTarget.Length2();
137     float fallOff = (float)pow(vTarget.Length(), 1.8f);
138     fVolume = (fallOff > VOLSCALE_BASE) ?
139       (VOLSCALE_BASE / fallOff) : (1.0f);
140 
141     /* done attenuation */
142 
143   /* doppler */
144 
145     fShift =
146       (USOUND + ( vListenerVelocity * vTarget ) / vTarget.Length() ) /
147       (USOUND + ( vSourceVelocity * vTarget ) / vTarget.Length() );
148 		if(fShift < 0.5) {
149 			printf("clamping fShift from %.2f to 0.5\n", fShift);
150 			fShift = 0.5f;
151 		}
152 		if(fShift > 1.5) {
153 			printf("clamping fShift from %.2f to 1.5\n", fShift);
154 			fShift = 1.5f;
155 		}
156 
157     /* done doppler */
158   }
159 
Mix(Uint8 * data,int len)160   int Source3D::Mix(Uint8 *data, int len) {
161     if(_source->_buffer == NULL) return 0;
162 
163     if(_source->IsPlaying()) {
164       int volume = (int)(_source->GetVolume() * SDL_MIX_MAXVOLUME);
165       float pan = 0, shift = 1.0f, vol = 1.0f;
166       int clen, shifted_len;
167 
168       GetModifiers(pan, vol, shift);
169       // printf("received: volume: %.4f, panning: %.4f, shift: %.4f\n", vol, pan, shift);
170 
171       shifted_len = 4 * fxComputeShiftLen( shift, len / 4 );
172       clen = MAX(len, shifted_len) + 32; // safety distance
173 
174       assert(clen < _source->_buffersize);
175 
176       if(vol > SOUND_VOL_THRESHOLD) {
177 				// copy clen bytes from the buffer to a temporary buffer
178 				if(clen <= _source->_buffersize - _position) {
179 					memcpy(tmp, _source->_buffer + _position, clen);
180 				} else {
181 					memcpy(tmp, _source->_buffer + _position,
182 								 _source->_buffersize - _position);
183 					memcpy(tmp + _source->_buffersize - _position, _source->_buffer,
184 								 clen - (_source->_buffersize - _position));
185 				}
186 
187 				fxPan(pan, vol, tmp, clen);
188 				// fxshift mixes the data to the stream
189 				_position += fxShift(shift, data, tmp, len);
190 
191 				if(_position > _source->_buffersize)
192 					_position -= _source->_buffersize;
193 
194 				return 1; // mixed something
195       }
196     }
197     return 0; // didn't mix anything to the stream
198   }
199 }
200