Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre
5 : * Copyright (c) Telecom ParisTech 2000-2012
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / Scene Compositor sub-project
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC is distributed in the hope that it will be useful,
16 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : * GNU Lesser General Public License for more details.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 :
27 :
28 : #include "nodes_stacks.h"
29 : #include "visual_manager.h"
30 :
31 : #ifndef GPAC_DISABLE_VRML
32 :
33 : typedef struct
34 : {
35 : GF_SoundInterface snd_ifce;
36 : SFVec3f pos;
37 : } Sound2DStack;
38 :
39 : /*sound2D wraper - spacialization is not supported yet*/
40 2978 : static void TraverseSound2D(GF_Node *node, void *rs, Bool is_destroy)
41 : {
42 : GF_TraverseState *tr_state = (GF_TraverseState*) rs;
43 : M_Sound2D *snd = (M_Sound2D *)node;
44 2978 : Sound2DStack *st = (Sound2DStack *)gf_node_get_private(node);
45 :
46 2978 : if (is_destroy) {
47 228 : gf_free(st);
48 228 : return;
49 : }
50 2750 : if (!snd->source) return;
51 :
52 : /*this implies no DEF/USE for real location...*/
53 2750 : st->pos.x = snd->location.x;
54 2750 : st->pos.y = snd->location.y;
55 2750 : st->pos.z = 0;
56 : #ifndef GPAC_DISABLE_3D
57 2750 : if (tr_state->visual->type_3d)
58 242 : gf_mx_apply_vec(&tr_state->model_matrix, &st->pos);
59 : else
60 : #endif
61 2508 : gf_mx2d_apply_coords(&tr_state->transform, &st->pos.x, &st->pos.y);
62 :
63 :
64 2750 : tr_state->sound_holder = &st->snd_ifce;
65 2750 : gf_node_traverse((GF_Node *) snd->source, tr_state);
66 2750 : tr_state->sound_holder = NULL;
67 : /*never cull Sound2d*/
68 2750 : tr_state->disable_cull = 1;
69 : }
70 18221 : static Bool SND2D_GetChannelVolume(GF_Node *node, Fixed *vol)
71 : {
72 : u32 i;
73 18221 : Fixed volume = ((M_Sound2D *)node)->intensity;
74 455525 : for (i=0; i<GF_AUDIO_MIXER_MAX_CHANNELS; i++) {
75 437304 : vol[i] = volume;
76 : }
77 18221 : return (volume==FIX_ONE) ? 0 : 1;
78 : }
79 :
80 :
81 228 : void compositor_init_sound2d(GF_Compositor *compositor, GF_Node *node)
82 : {
83 : Sound2DStack *snd;
84 228 : GF_SAFEALLOC(snd, Sound2DStack);
85 228 : if (!snd) {
86 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate sound 2d stack\n"));
87 : return;
88 : }
89 228 : snd->snd_ifce.GetChannelVolume = SND2D_GetChannelVolume;
90 228 : snd->snd_ifce.owner = node;
91 228 : gf_node_set_private(node, snd);
92 228 : gf_node_set_callback_function(node, TraverseSound2D);
93 : }
94 :
95 : #ifndef GPAC_DISABLE_3D
96 :
97 2 : static Fixed snd_compute_gain(Fixed min_b, Fixed min_f, Fixed max_b, Fixed max_f, SFVec3f pos)
98 : {
99 : Fixed sqpos_x, sqpos_z;
100 : Fixed y_pos, x_pos, dist_ellip, viewp_dist, dist_from_foci_min, dist_from_foci_max, d_min, d_max, sqb_min, sqb_max;
101 2 : Fixed a_in = (min_f+min_b)/2;
102 2 : Fixed b_in = gf_sqrt(gf_mulfix(min_b, min_f));
103 2 : Fixed alpha_min = (min_f-min_b)/2;
104 : Fixed dist_foci_min = (min_f-min_b);
105 2 : Fixed a_out = (max_f+max_b)/2; //first ellipse axis
106 2 : Fixed b_out = gf_sqrt(gf_mulfix(max_b, max_f));
107 2 : Fixed alpha_max = (max_f-max_b)/2; //origo from focus
108 : Fixed dist_foci_max = (max_f-max_b);
109 : Fixed x_min = 0;
110 : Fixed x_max = 0;
111 : Fixed y_min = 0;
112 : Fixed y_max = 0;
113 2 : Fixed k = (ABS(pos.z) >= FIX_EPSILON) ? gf_divfix(pos.x, pos.z) : 0;
114 :
115 2 : sqpos_x = gf_mulfix(pos.x, pos.x);
116 2 : sqpos_z = gf_mulfix(pos.z, pos.z);
117 :
118 2 : dist_from_foci_min = gf_sqrt(sqpos_z + sqpos_x) + gf_sqrt( gf_mulfix(pos.z - dist_foci_min, pos.z - dist_foci_min) + sqpos_x);
119 2 : dist_from_foci_max = gf_sqrt(sqpos_z + sqpos_x) + gf_sqrt( gf_mulfix(pos.z - dist_foci_max, pos.z - dist_foci_max) + sqpos_x);
120 : d_min = min_f+min_b;
121 : d_max = max_f+max_b;
122 2 : if(dist_from_foci_max > d_max) return 0;
123 2 : else if (dist_from_foci_min <= d_min) return FIX_ONE;
124 :
125 2 : sqb_min = gf_mulfix(b_in, b_in);
126 2 : sqb_max = gf_mulfix(b_out, b_out);
127 :
128 2 : if (ABS(pos.z) > FIX_ONE/10000) {
129 2 : s32 sign = (pos.z>0) ? 1 : -1;
130 : Fixed a_in_k_sq, a_out_k_sq;
131 2 : a_in_k_sq = gf_mulfix(a_in, k);
132 2 : a_in_k_sq = gf_mulfix(a_in_k_sq, a_in_k_sq);
133 :
134 2 : x_min = gf_mulfix(alpha_min, sqb_min) + sign*gf_mulfix( gf_mulfix(a_in, b_in), gf_sqrt(a_in_k_sq + sqb_min - gf_mulfix( gf_mulfix(alpha_min, k), gf_mulfix(alpha_min, k))));
135 2 : x_min = gf_divfix(x_min, sqb_min + a_in_k_sq);
136 2 : y_min = gf_mulfix(k, x_min);
137 :
138 2 : a_out_k_sq = gf_mulfix(a_out, k);
139 2 : a_out_k_sq = gf_mulfix(a_out_k_sq, a_out_k_sq);
140 :
141 2 : x_max = gf_mulfix(alpha_max, sqb_max) + sign*gf_mulfix( gf_mulfix(a_out, b_out), gf_sqrt( a_out_k_sq + sqb_max - gf_mulfix( gf_mulfix(alpha_max, k), gf_mulfix(alpha_max, k))));
142 2 : x_max = gf_divfix(x_max, sqb_max + a_out_k_sq);
143 2 : y_max = gf_mulfix(k, x_max);
144 : } else {
145 : x_min = x_max = 0;
146 0 : y_min = gf_mulfix(b_in, gf_sqrt(FIX_ONE - gf_mulfix( gf_divfix(alpha_min,a_in), gf_divfix(alpha_min,a_in)) ) );
147 0 : y_max = gf_mulfix(b_out, gf_sqrt(FIX_ONE - gf_mulfix( gf_divfix(alpha_max,a_out), gf_divfix(alpha_max,a_out)) ) );
148 : }
149 :
150 2 : y_pos = gf_sqrt(sqpos_x) - y_min;
151 2 : x_pos = pos.z - x_min;
152 2 : x_max -= x_min;
153 2 : y_max -= y_min;
154 2 : dist_ellip = gf_sqrt( gf_mulfix(y_max, y_max) + gf_mulfix(x_max, x_max));
155 2 : viewp_dist = gf_sqrt( gf_mulfix(y_pos, y_pos) + gf_mulfix(x_pos, x_pos));
156 2 : viewp_dist = gf_divfix(viewp_dist, dist_ellip);
157 :
158 2 : return FLT2FIX ( (Float) pow(10.0,- FIX2FLT(viewp_dist)));
159 : }
160 :
161 :
162 : typedef struct
163 : {
164 : GF_SoundInterface snd_ifce;
165 : GF_Matrix mx;
166 : SFVec3f last_pos;
167 : Bool identity;
168 : /*local system*/
169 : Fixed intensity;
170 : Fixed lgain, rgain;
171 : } SoundStack;
172 :
173 652 : static void TraverseSound(GF_Node *node, void *rs, Bool is_destroy)
174 : {
175 : GF_TraverseState *tr_state = (GF_TraverseState*) rs;
176 : M_Sound *snd = (M_Sound *)node;
177 652 : SoundStack *st = (SoundStack *)gf_node_get_private(node);
178 :
179 652 : if (is_destroy) {
180 2 : gf_free(st);
181 2 : return;
182 : }
183 650 : if (!snd->source) return;
184 :
185 650 : tr_state->sound_holder = &st->snd_ifce;
186 :
187 : /*forward in case we're switched off*/
188 650 : if (tr_state->switched_off) {
189 0 : gf_node_traverse((GF_Node *) snd->source, tr_state);
190 : }
191 650 : else if (tr_state->traversing_mode==TRAVERSE_GET_BOUNDS) {
192 : /*we can't cull sound since*/
193 2 : tr_state->disable_cull = 1;
194 648 : } else if (tr_state->traversing_mode==TRAVERSE_SORT) {
195 : GF_Matrix mx;
196 : SFVec3f usr, snd_dir, pos;
197 : Fixed mag, ang;
198 : /*this implies no DEF/USE for real location...*/
199 302 : gf_mx_copy(st->mx, tr_state->model_matrix);
200 : gf_mx_copy(mx, tr_state->model_matrix);
201 302 : gf_mx_inverse(&mx);
202 :
203 302 : snd_dir = snd->direction;
204 302 : gf_vec_norm(&snd_dir);
205 :
206 : /*get user location*/
207 302 : usr = tr_state->camera->position;
208 302 : gf_mx_apply_vec(&mx, &usr);
209 :
210 : /*recenter to ellipse focal*/
211 302 : gf_vec_diff(usr, usr, snd->location);
212 302 : mag = gf_vec_len(usr);
213 302 : if (!mag) mag = FIX_ONE/10;
214 302 : ang = gf_divfix(gf_vec_dot(snd_dir, usr), mag);
215 :
216 302 : usr.z = gf_mulfix(ang, mag);
217 302 : usr.x = gf_sqrt(gf_mulfix(mag, mag) - gf_mulfix(usr.z, usr.z));
218 302 : usr.y = 0;
219 302 : if (!gf_vec_equal(usr, st->last_pos)) {
220 2 : st->intensity = snd_compute_gain(snd->minBack, snd->minFront, snd->maxBack, snd->maxFront, usr);
221 2 : st->intensity = gf_mulfix(st->intensity, snd->intensity);
222 2 : st->last_pos = usr;
223 : }
224 302 : st->identity = (st->intensity==FIX_ONE) ? 1 : 0;
225 :
226 302 : if (snd->spatialize) {
227 : Fixed sign;
228 : SFVec3f cross;
229 151 : pos = snd->location;
230 151 : gf_mx_apply_vec(&tr_state->model_matrix, &pos);
231 151 : gf_vec_diff(pos, pos, tr_state->camera->position);
232 151 : gf_vec_diff(usr, tr_state->camera->target, tr_state->camera->position);
233 151 : gf_vec_norm(&pos);
234 151 : gf_vec_norm(&usr);
235 :
236 151 : ang = gf_acos(gf_vec_dot(usr, pos));
237 : /*get orientation*/
238 151 : cross = gf_vec_cross(usr, pos);
239 151 : sign = gf_vec_dot(cross, tr_state->camera->up);
240 151 : if (sign>0) ang *= -1;
241 151 : ang = (FIX_ONE + gf_sin(ang)) / 2;
242 151 : st->lgain = (FIX_ONE - gf_mulfix(ang, ang));
243 151 : st->rgain = FIX_ONE - gf_mulfix(FIX_ONE - ang, FIX_ONE - ang);
244 : /*renorm between 0 and 1*/
245 151 : st->lgain = gf_mulfix(st->lgain, 4*st->intensity/3);
246 151 : st->rgain = gf_mulfix(st->rgain, 4*st->intensity/3);
247 :
248 151 : if (st->identity && ((st->lgain!=FIX_ONE) || (st->rgain!=FIX_ONE))) st->identity = 0;
249 : } else {
250 151 : st->lgain = st->rgain = FIX_ONE;
251 : }
252 302 : gf_node_traverse((GF_Node *) snd->source, tr_state);
253 : }
254 :
255 650 : tr_state->sound_holder = NULL;
256 : }
257 15798 : static Bool SND_GetChannelVolume(GF_Node *node, Fixed *vol)
258 : {
259 : u32 i;
260 : M_Sound *snd = (M_Sound *)node;
261 15798 : SoundStack *st = (SoundStack *)gf_node_get_private(node);
262 363354 : for (i=2; i<GF_AUDIO_MIXER_MAX_CHANNELS; i++)
263 347556 : vol[i] = st->intensity;
264 :
265 15798 : if (snd->spatialize) {
266 7899 : vol[0] = st->lgain;
267 7899 : vol[1] = st->rgain;
268 : } else {
269 7899 : vol[0] = vol[1] = st->intensity;
270 : }
271 15798 : return !st->identity;
272 : }
273 :
274 2 : void compositor_init_sound(GF_Compositor *compositor, GF_Node *node)
275 : {
276 : SoundStack *snd;
277 2 : GF_SAFEALLOC(snd, SoundStack);
278 2 : if (!snd) {
279 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_COMPOSE, ("[Compositor] Failed to allocate sound stack\n"));
280 : return;
281 : }
282 2 : snd->snd_ifce.GetChannelVolume = SND_GetChannelVolume;
283 2 : snd->snd_ifce.owner = node;
284 2 : gf_node_set_private(node, snd);
285 2 : gf_node_set_callback_function(node, TraverseSound);
286 : }
287 :
288 : #endif /*GPAC_DISABLE_3D*/
289 :
290 : #endif /*GPAC_DISABLE_VRML*/
|