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