1 /*
2  * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/>
3  *           (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com>
4  *
5  * This file is part of lsp-plugins
6  * Created on: 25 апр. 2019 г.
7  *
8  * lsp-plugins is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * any later version.
12  *
13  * lsp-plugins is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <dsp/dsp.h>
23 
24 #include <core/files/Model3DFile.h>
25 
26 #include <plugins/room_builder.h>
27 #include <dsp/endian.h>
28 #include <core/fade.h>
29 #include <core/stdlib/math.h>
30 #include <core/files/lspc/LSPCAudioWriter.h>
31 #include <core/files/AudioFile.h>
32 
33 #define TMP_BUF_SIZE            4096
34 #define CONV_RANK               10
35 #define TRACE_PORT(p)           lsp_trace("  port id=%s", (p)->metadata()->id);
36 
37 namespace lsp
38 {
39     static const float band_freqs[] =
40     {
41         73.0f,
42         156.0f,
43         332.0f,
44         707.0f,
45         1507.0f,
46         3213.0f,
47         6849.0f
48     };
49 
50     //-------------------------------------------------------------------------
51     // 3D Scene loader
~SceneLoader()52     room_builder_base::SceneLoader::~SceneLoader()
53     {
54     }
55 
56 
init(room_builder_base * base)57     void room_builder_base::SceneLoader::init(room_builder_base *base)
58     {
59         pCore   = base;
60         sScene.clear();
61     }
62 
destroy()63     void room_builder_base::SceneLoader::destroy()
64     {
65         sScene.destroy();
66     }
67 
run()68     status_t room_builder_base::RenderLauncher::run()
69     {
70         return pBuilder->start_rendering();
71     }
72 
73     template <class T>
kvt_deploy(KVTStorage * s,const char * base,const char * branch,T value,size_t flags)74         static bool kvt_deploy(KVTStorage *s, const char *base, const char *branch, T value, size_t flags)
75         {
76             char name[0x100]; // Should be enough
77             size_t len = ::strlen(base) + ::strlen(branch) + 2;
78             if (len >= 0x100)
79                 return false;
80 
81             char *tail = ::stpcpy(name, base);
82             *(tail++)  = '/';
83             stpcpy(tail, branch);
84 
85             return s->put(name, value, flags) == STATUS_OK;
86         }
87 
run()88     status_t room_builder_base::SceneLoader::run()
89     {
90         // Clear scene
91         sScene.clear();
92 
93         // Check state
94         size_t nobjs = 0;
95         status_t res = STATUS_UNSPECIFIED;
96 
97         // Load the scene file
98         if (pCore->p3DFile == NULL)
99             res = STATUS_UNKNOWN_ERR;
100         else if (::strlen(sPath) > 0)
101         {
102             res = Model3DFile::load(&sScene, sPath, true);
103             if (res == STATUS_OK)
104             {
105                 // Initialize object properties
106                 nobjs = sScene.num_objects();
107             }
108         }
109 
110         // Get KVT storage and deploy new values
111         KVTStorage *kvt = pCore->kvt_lock();
112         if (kvt == NULL)
113             return STATUS_UNKNOWN_ERR;
114 
115         // Now initialize object properties
116         lsp_trace("Extra loading flags=0x%x", int(nFlags));
117         size_t f_extra  = (nFlags & (PF_STATE_IMPORT | PF_PRESET_IMPORT | PF_STATE_RESTORE)) ? KVT_KEEP | KVT_TX : KVT_TX;
118         size_t f_hue    = (nFlags & (PF_STATE_IMPORT | PF_STATE_RESTORE)) ? KVT_KEEP | KVT_TX : KVT_TX;
119 
120         char base[128];
121         kvt_deploy(kvt, "/scene", "objects", int32_t(nobjs), KVT_TX);
122         kvt_deploy(kvt, "/scene", "selected", 0.0f, f_extra);
123 
124         for (size_t i=0; i<nobjs; ++i)
125         {
126             Object3D *obj       = sScene.object(i);
127             if (obj == NULL)
128                 return STATUS_UNKNOWN_ERR;
129             const point3d_t *c  = obj->center();
130 
131             sprintf(base, "/scene/object/%d", int(i));
132             lsp_trace("Deploying KVT parameters for %s", base);
133 
134             kvt_deploy(kvt, base, "name", obj->get_name(), KVT_TX); // Always overwrite name
135 
136             kvt_deploy(kvt, base, "enabled", 1.0f, f_extra);
137             kvt_deploy(kvt, base, "center/x", c->x, KVT_TX | KVT_TRANSIENT); // Always overwrite, do not save in state
138             kvt_deploy(kvt, base, "center/y", c->y, KVT_TX | KVT_TRANSIENT); // Always overwrite, do not save in state
139             kvt_deploy(kvt, base, "center/z", c->z, KVT_TX | KVT_TRANSIENT); // Always overwrite, do not save in state
140             kvt_deploy(kvt, base, "position/x", 0.0f, f_extra);
141             kvt_deploy(kvt, base, "position/y", 0.0f, f_extra);
142             kvt_deploy(kvt, base, "position/z", 0.0f, f_extra);
143             kvt_deploy(kvt, base, "rotation/yaw", 0.0f, f_extra);
144             kvt_deploy(kvt, base, "rotation/pitch", 0.0f, f_extra);
145             kvt_deploy(kvt, base, "rotation/roll", 0.0f, f_extra);
146             kvt_deploy(kvt, base, "scale/x", 100.0f, f_extra);
147             kvt_deploy(kvt, base, "scale/y", 100.0f, f_extra);
148             kvt_deploy(kvt, base, "scale/z", 100.0f, f_extra);
149             kvt_deploy(kvt, base, "color/hue", float(i) / float(nobjs), f_hue); // Always overwrite hue
150 
151             kvt_deploy(kvt, base, "material/absorption/outer", 1.5f, f_extra); // Absorption of concrete material
152             kvt_deploy(kvt, base, "material/dispersion/outer", 1.0f, f_extra);
153             kvt_deploy(kvt, base, "material/diffusion/outer", 1.0f, f_extra);
154             kvt_deploy(kvt, base, "material/transparency/outer", 48.0f, f_extra);
155 
156             kvt_deploy(kvt, base, "material/absorption/inner", 1.5f, f_extra);
157             kvt_deploy(kvt, base, "material/dispersion/inner", 1.0f, f_extra);
158             kvt_deploy(kvt, base, "material/diffusion/inner", 1.0f, f_extra);
159             kvt_deploy(kvt, base, "material/transparency/inner", 52.0f, f_extra);
160 
161             kvt_deploy(kvt, base, "material/absorption/link", 1.0f, f_extra);
162             kvt_deploy(kvt, base, "material/dispersion/link", 1.0f, f_extra);
163             kvt_deploy(kvt, base, "material/diffusion/link", 1.0f, f_extra);
164             kvt_deploy(kvt, base, "material/transparency/link", 1.0f, f_extra);
165 
166             kvt_deploy(kvt, base, "material/sound_speed", 4250.0f, f_extra);  // Sound speed in concrete material
167         }
168 
169         // Drop rare (unused) objects
170         kvt_cleanup_objects(kvt, nobjs);
171 
172         pCore->kvt_release();
173 
174         return res;
175     }
176 
kvt_cleanup_objects(KVTStorage * kvt,size_t objects)177     void room_builder_base::kvt_cleanup_objects(KVTStorage *kvt, size_t objects)
178     {
179         KVTIterator *it = kvt->enum_branch("/scene/object");
180         while (it->next() == STATUS_OK)
181         {
182             const char *id = it->id();
183             if (id == NULL)
184                 continue;
185 
186             // Must be a pure object identifier
187             errno = 0;
188             char *endptr;
189             long value = ::strtol(id, &endptr, 10);
190             if ((errno != 0) || (size_t(endptr - id) != size_t(::strlen(id))))
191                 continue;
192 
193             // Remove the object
194             if ((value < 0) || (value >= ssize_t(objects)))
195             {
196                 lsp_trace("Removing KVT parameters from %s", it->name());
197                 it->remove_branch();
198             }
199         }
200     }
201 
202     template <class T>
kvt_fetch(KVTStorage * s,const char * base,const char * branch,T * value,T dfl)203         static bool kvt_fetch(KVTStorage *s, const char *base, const char *branch, T *value, T dfl)
204         {
205             char name[0x100]; // Should be enough;
206             size_t len = ::strlen(base) + ::strlen(branch) + 2;
207             if (len >= 0x100)
208                 return false;
209 
210             char *tail = ::stpcpy(name, base);
211             *(tail++)  = '/';
212             stpcpy(tail, branch);
213 
214             return s->get_dfl(name, value, dfl);
215         }
216 
read_object_properties(obj_props_t * props,const char * base,KVTStorage * kvt)217     void room_builder_base::read_object_properties(obj_props_t *props, const char *base, KVTStorage *kvt)
218     {
219         float enabled;
220 
221         kvt_fetch(kvt, base, "name", &props->sName, "unnamed");
222         kvt_fetch(kvt, base, "enabled", &enabled, 1.0f);
223         kvt_fetch(kvt, base, "center/x", &props->sCenter.x, 0.0f);
224         kvt_fetch(kvt, base, "center/y", &props->sCenter.y, 0.0f);
225         kvt_fetch(kvt, base, "center/z", &props->sCenter.z, 0.0f);
226         kvt_fetch(kvt, base, "position/x", &props->sMove.dx, 0.0f);
227         kvt_fetch(kvt, base, "position/y", &props->sMove.dy, 0.0f);
228         kvt_fetch(kvt, base, "position/z", &props->sMove.dz, 0.0f);
229         kvt_fetch(kvt, base, "rotation/yaw", &props->fYaw, 0.0f);
230         kvt_fetch(kvt, base, "rotation/pitch", &props->fPitch, 0.0f);
231         kvt_fetch(kvt, base, "rotation/roll", &props->fRoll, 0.0f);
232         kvt_fetch(kvt, base, "scale/x", &props->sScale.dx, 1.0f);
233         kvt_fetch(kvt, base, "scale/y", &props->sScale.dy, 1.0f);
234         kvt_fetch(kvt, base, "scale/z", &props->sScale.dz, 1.0f);
235         kvt_fetch(kvt, base, "color/hue", &props->fHue, 0.0f);
236 
237         kvt_fetch(kvt, base, "material/absorption/outer", &props->fAbsorption[0], 1.5f);
238         kvt_fetch(kvt, base, "material/dispersion/outer", &props->fDispersion[0], 1.0f);
239         kvt_fetch(kvt, base, "material/dissipation/outer", &props->fDiffusion[0], 1.0f);
240         kvt_fetch(kvt, base, "material/transparency/outer", &props->fTransparency[0], 48.0f);
241 
242         kvt_fetch(kvt, base, "material/absorption/inner", &props->fAbsorption[1], 1.5f);
243         kvt_fetch(kvt, base, "material/dispersion/inner", &props->fDispersion[1], 1.0f);
244         kvt_fetch(kvt, base, "material/diffusion/inner", &props->fDiffusion[1], 1.0f);
245         kvt_fetch(kvt, base, "material/transparency/inner", &props->fTransparency[1], 52.0f);
246 
247         kvt_fetch(kvt, base, "material/absorption/link", &props->lnkAbsorption, 1.0f);
248         kvt_fetch(kvt, base, "material/dispersion/link", &props->lnkDispersion, 1.0f);
249         kvt_fetch(kvt, base, "material/diffusion/link", &props->lnkDiffusion, 1.0f);
250         kvt_fetch(kvt, base, "material/transparency/link", &props->lnkTransparency, 1.0f);
251 
252         kvt_fetch(kvt, base, "material/sound_speed", &props->fSndSpeed, 4250.0f);
253 
254         props->bEnabled = (enabled >= 0.5f);
255     }
256 
build_object_matrix(matrix3d_t * m,const obj_props_t * props,const matrix3d_t * world)257     void room_builder_base::build_object_matrix(matrix3d_t *m, const obj_props_t *props, const matrix3d_t *world)
258     {
259         matrix3d_t tmp;
260 
261         // Copy world matrix
262         *m  = *world;
263 
264         // Apply translation
265         dsp::init_matrix3d_translate(&tmp,
266                 props->sCenter.x + props->sMove.dx,
267                 props->sCenter.y + props->sMove.dy,
268                 props->sCenter.z + props->sMove.dz
269         );
270         dsp::apply_matrix3d_mm1(m, &tmp);
271 
272         // Apply rotation
273         dsp::init_matrix3d_rotate_z(&tmp, props->fYaw * M_PI / 180.0f);
274         dsp::apply_matrix3d_mm1(m, &tmp);
275 
276         dsp::init_matrix3d_rotate_y(&tmp, props->fPitch * M_PI / 180.0f);
277         dsp::apply_matrix3d_mm1(m, &tmp);
278 
279         dsp::init_matrix3d_rotate_x(&tmp, props->fRoll * M_PI / 180.0f);
280         dsp::apply_matrix3d_mm1(m, &tmp);
281 
282         // Apply scale
283         dsp::init_matrix3d_scale(&tmp, props->sScale.dx * 0.01f, props->sScale.dy * 0.01f, props->sScale.dz * 0.01f);
284         dsp::apply_matrix3d_mm1(m, &tmp);
285 
286         // Move center to (0, 0, 0) point
287         dsp::init_matrix3d_translate(&tmp, -props->sCenter.x, -props->sCenter.y, -props->sCenter.z);
288         dsp::apply_matrix3d_mm1(m, &tmp);
289     }
290 
run()291     status_t room_builder_base::Renderer::run()
292     {
293         // Perform processing
294         lsp_trace("Launching process() method");
295         pBuilder->enRenderStatus    = STATUS_IN_PROCESS;
296         status_t res    = pRT->process(nThreads, 1.0f);
297 
298         // Deploy success result
299         if (res == STATUS_OK)
300             res = pBuilder->commit_samples(vSamples);
301 
302         // Free all resources
303         if (lkTerminate.lock())
304         {
305             pRT->destroy(true);
306             delete pRT;
307             pRT = NULL;
308             lkTerminate.unlock();
309         }
310 
311         room_builder_base::destroy_samples(vSamples);
312 
313         return pBuilder->enRenderStatus = res;
314     }
315 
terminate()316     void room_builder_base::Renderer::terminate()
317     {
318         if (lkTerminate.lock())
319         {
320             if (pRT != NULL)
321                 pRT->cancel();
322             lkTerminate.unlock();
323         }
324     }
325 
run()326     status_t room_builder_base::Configurator::run()
327     {
328         return pBuilder->reconfigure(&sConfig);
329     }
330 
bind(size_t sample_id,capture_t * capture)331     void room_builder_base::SampleSaver::bind(size_t sample_id, capture_t *capture)
332     {
333         nSampleID   = sample_id;
334         IPort *p    = capture->pOutFile;
335         if (p == NULL)
336             return;
337         path_t *path = p->getBuffer<path_t>();
338         if (path == NULL)
339             return;
340         const char *spath = path->get_path();
341         if (spath != NULL)
342         {
343             ::strncpy(sPath, spath, PATH_MAX);
344             sPath[PATH_MAX] = '\0';
345         }
346         else
347             sPath[0] = '\0';
348     }
349 
run()350     status_t room_builder_base::SampleSaver::run()
351     {
352         return pBuilder->save_sample(sPath, nSampleID);
353     }
354 
355     //-------------------------------------------------------------------------
room_builder_base(const plugin_metadata_t & metadata,size_t inputs)356     room_builder_base::room_builder_base(const plugin_metadata_t &metadata, size_t inputs):
357         plugin_t(metadata),
358         s3DLauncher(this),
359         sConfigurator(this),
360         sSaver(this)
361     {
362         nInputs         = inputs;
363         nReconfigReq    = 0;
364         nReconfigResp   = 0;
365 
366         nRenderThreads  = 0;
367         fRenderQuality  = 0.5f;
368         bRenderNormalize= true;
369         enRenderStatus  = STATUS_OK;
370         fRenderProgress = 0.0f;
371         fRenderCmd      = 0.0f;
372         nFftRank        = 0;
373 
374         nSceneStatus    = STATUS_UNSPECIFIED;
375         fSceneProgress  = 0.0f;
376         nSync           = 0;
377 
378         pBypass         = NULL;
379         pRank           = NULL;
380         pDry            = NULL;
381         pWet            = NULL;
382         pRenderThreads  = NULL;
383         pRenderQuality  = NULL;
384         pRenderStatus   = NULL;
385         pRenderProgress = NULL;
386         pRenderNormalize= NULL;
387         pRenderCmd      = NULL;
388         pOutGain        = NULL;
389         pPredelay       = NULL;
390         p3DFile         = NULL;
391         p3DProgress     = NULL;
392         p3DStatus       = NULL;
393         p3DOrientation  = NULL;
394         pScaleX         = NULL;
395         pScaleY         = NULL;
396         pScaleZ         = NULL;
397         pRenderer       = NULL;
398 
399         pData           = NULL;
400         pExecutor       = NULL;
401 
402         dsp::init_vector_dxyz(&sScale, 1.0f, 1.0f, 1.0f);
403     }
404 
~room_builder_base()405     room_builder_base::~room_builder_base()
406     {
407     }
408 
init(IWrapper * wrapper)409     void room_builder_base::init(IWrapper *wrapper)
410     {
411         // Pass wrapper
412         plugin_t::init(wrapper);
413 
414         // Remember executor service
415         pExecutor       = wrapper->get_executor();
416         lsp_trace("Executor = %p", pExecutor);
417 
418         // Allocate memory
419         size_t tmp_buf_size = TMP_BUF_SIZE * sizeof(float);
420         size_t thumb_size   = room_builder_base_metadata::MESH_SIZE *
421                               room_builder_base_metadata::TRACKS_MAX * sizeof(float);
422         size_t alloc        = tmp_buf_size * (room_builder_base_metadata::CONVOLVERS + 2) +
423                               thumb_size * room_builder_base_metadata::CAPTURES;
424         uint8_t *ptr        = alloc_aligned<uint8_t>(pData, alloc);
425         if (pData == NULL)
426             return;
427 
428         // Initialize 3D loader
429         s3DLoader.init(this);
430 
431         // Initialize inputs
432         for (size_t i=0; i<2; ++i)
433         {
434             input_t *in     = &vInputs[i];
435             in->vIn         = NULL;
436             in->pIn         = NULL;
437             in->pPan        = NULL;
438         }
439 
440         // Initialize output channels
441         for (size_t i=0; i<2; ++i)
442         {
443             channel_t *c    = &vChannels[i];
444 
445             if (!c->sPlayer.init(room_builder_base_metadata::CAPTURES, 32))
446                 return;
447             if (!c->sEqualizer.init(room_builder_base_metadata::EQ_BANDS + 2, CONV_RANK))
448                 return;
449             c->sEqualizer.set_mode(EQM_BYPASS);
450 
451             c->fDryPan[0]   = 0.0f;
452             c->fDryPan[1]   = 0.0f;
453 
454             c->vOut         = NULL;
455             c->vBuffer      = reinterpret_cast<float *>(ptr);
456             ptr            += tmp_buf_size;
457 
458             c->pOut         = NULL;
459 
460             c->pWetEq       = NULL;
461             c->pLowCut      = NULL;
462             c->pLowFreq     = NULL;
463             c->pHighCut     = NULL;
464             c->pHighFreq    = NULL;
465 
466             for (size_t j=0; j<room_builder_base_metadata::EQ_BANDS; ++j)
467                 c->pFreqGain[j]     = NULL;
468         }
469 
470         // Initialize sources
471         for (size_t i=0; i<room_builder_base_metadata::SOURCES; ++i)
472         {
473             source_t *src   = &vSources[i];
474 
475             src->bEnabled       = false;
476             src->enType         = RT_AS_TRIANGLE;
477             dsp::init_point_xyz(&src->sPos, 0.0f, -1.0f, 0.0f);
478             src->fYaw           = 0.0f;
479             src->fPitch         = 0.0f;
480             src->fRoll          = 0.0f;
481             src->fSize          = 0.0f;
482             src->fHeight        = 0.0f;
483             src->fAngle         = 0.0f;
484             src->fCurvature     = 1.0f;
485             src->fAmplitude     = 1.0f;
486 
487             src->pEnabled       = NULL;
488             src->pType          = NULL;
489             src->pPhase         = NULL;
490             src->pPosX          = NULL;
491             src->pPosY          = NULL;
492             src->pPosZ          = NULL;
493             src->pYaw           = NULL;
494             src->pPitch         = NULL;
495             src->pRoll          = NULL;
496             src->pSize          = NULL;
497             src->pHeight        = NULL;
498             src->pAngle         = NULL;
499             src->pCurvature     = NULL;
500         }
501 
502         // Initialize captures
503         for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
504         {
505             capture_t *cap  = &vCaptures[i];
506 
507             dsp::init_point_xyz(&cap->sPos, 0.0f, 1.0f, 0.0f);
508             cap->fYaw           = 0.0f;
509             cap->fPitch         = 0.0f;
510             cap->fRoll          = 0.0f;
511             cap->fCapsule       = room_builder_base_metadata::CAPSULE_DFL;
512             cap->sConfig        = RT_CC_XY;
513             cap->fAngle         = room_builder_base_metadata::ANGLE_DFL;
514             cap->fDistance      = room_builder_base_metadata::DISTANCE_DFL;
515             cap->enDirection    = RT_AC_OMNI;
516             cap->enSide         = RT_AC_BIDIR;
517 
518             cap->bEnabled       = (i == 0);
519             cap->nRMin          = 1;
520             cap->nRMax          = -1;
521 
522             cap->fHeadCut       = 0.0f;
523             cap->fTailCut       = 0.0f;
524             cap->fFadeIn        = 0.0f;
525             cap->fFadeOut       = 0.0f;
526             cap->bReverse       = false;
527             cap->fMakeup        = 1.0f;
528             cap->nLength        = 0;
529             cap->nStatus        = STATUS_NO_DATA;
530             cap->fCurrLen       = 0.0f;
531             cap->fMaxLen        = 0.0f;
532 
533             cap->nChangeReq     = 0;
534             cap->nChangeResp    = 0;
535             cap->bCommit        = false;
536             cap->bSync          = false;
537             cap->bExport        = false;
538 
539             cap->pCurr          = NULL;
540             cap->pSwap          = NULL;
541 
542             for (size_t j=0; j<room_builder_base_metadata::TRACKS_MAX; ++j)
543             {
544                 cap->vThumbs[j]     = reinterpret_cast<float *>(ptr);
545                 ptr                += room_builder_base_metadata::MESH_SIZE * sizeof(float);
546             }
547 
548             cap->pEnabled       = NULL;
549             cap->pRMin          = NULL;
550             cap->pRMax          = NULL;
551             cap->pPosX          = NULL;
552             cap->pPosY          = NULL;
553             cap->pPosZ          = NULL;
554             cap->pYaw           = NULL;
555             cap->pPitch         = NULL;
556             cap->pRoll          = NULL;
557             cap->pCapsule       = NULL;
558             cap->pConfig        = NULL;
559             cap->pAngle         = NULL;
560             cap->pDistance      = NULL;
561             cap->pDirection     = NULL;
562             cap->pSide          = NULL;
563 
564             cap->pHeadCut       = NULL;
565             cap->pTailCut       = NULL;
566             cap->pFadeIn        = NULL;
567             cap->pFadeOut       = NULL;
568             cap->pListen        = NULL;
569             cap->pReverse       = NULL;
570             cap->pMakeup        = NULL;
571             cap->pStatus        = NULL;
572             cap->pLength        = NULL;
573             cap->pCurrLen       = NULL;
574             cap->pMaxLen        = NULL;
575             cap->pThumbs        = NULL;
576 
577             cap->pOutFile       = NULL;
578             cap->pSaveCmd       = NULL;
579             cap->pSaveStatus    = NULL;
580             cap->pSaveProgress  = NULL;
581         }
582 
583         // Initialize convolvers
584         for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
585         {
586             lsp_trace("Binding convolution #%d ports", int(i));
587             convolver_t *c  = &vConvolvers[i];
588 
589             c->pCurr            = NULL;
590             c->pSwap            = NULL;
591 
592             c->nSampleID        = 0;
593             c->nTrackID         = 0;
594 
595             c->vBuffer          = reinterpret_cast<float *>(ptr);
596             ptr                += tmp_buf_size;
597 
598             c->fPanIn[0]        = 0.0f;
599             c->fPanIn[1]        = 0.0f;
600             c->fPanOut[0]       = 0.0f;
601             c->fPanOut[1]       = 0.0f;
602 
603             c->pMakeup          = NULL;
604             c->pPanIn           = NULL;
605             c->pPanOut          = NULL;
606             c->pSample          = NULL;
607             c->pTrack           = NULL;
608             c->pPredelay        = NULL;
609             c->pMute            = NULL;
610             c->pActivity        = NULL;
611         }
612 
613         // Bind ports
614         size_t port_id = 0;
615 
616         lsp_trace("Binding audio ports");
617         for (size_t i=0; i<nInputs; ++i)
618         {
619             TRACE_PORT(vPorts[port_id]);
620             vInputs[i].pIn      = vPorts[port_id++];
621         }
622         for (size_t i=0; i<2; ++i)
623         {
624             TRACE_PORT(vPorts[port_id]);
625             vChannels[i].pOut   = vPorts[port_id++];
626         }
627 
628         // Bind controlling ports
629         lsp_trace("Binding common ports");
630         TRACE_PORT(vPorts[port_id]);
631         pBypass         = vPorts[port_id++];
632         TRACE_PORT(vPorts[port_id]);            // Skip view selector
633         port_id++;
634         TRACE_PORT(vPorts[port_id]);            // Skip editor selector
635         port_id++;
636         TRACE_PORT(vPorts[port_id]);            // Skip processor selector
637         port_id++;
638         TRACE_PORT(vPorts[port_id]);            // FFT rank
639         pRank           = vPorts[port_id++];
640         TRACE_PORT(vPorts[port_id]);            // Pre-delay
641         pPredelay       = vPorts[port_id++];
642 
643         for (size_t i=0; i<nInputs; ++i)        // Panning ports
644         {
645             TRACE_PORT(vPorts[port_id]);
646             vInputs[i].pPan     = vPorts[port_id++];
647         }
648 
649         TRACE_PORT(vPorts[port_id]);
650         pDry            = vPorts[port_id++];
651         TRACE_PORT(vPorts[port_id]);
652         pWet            = vPorts[port_id++];
653         TRACE_PORT(vPorts[port_id]);
654         pOutGain        = vPorts[port_id++];
655 
656         TRACE_PORT(vPorts[port_id]);
657         pRenderThreads  = vPorts[port_id++];
658         TRACE_PORT(vPorts[port_id]);
659         pRenderQuality  = vPorts[port_id++];
660         TRACE_PORT(vPorts[port_id]);
661         pRenderStatus   = vPorts[port_id++];
662         TRACE_PORT(vPorts[port_id]);
663         pRenderProgress = vPorts[port_id++];
664         TRACE_PORT(vPorts[port_id]);
665         pRenderNormalize= vPorts[port_id++];
666         TRACE_PORT(vPorts[port_id]);
667         pRenderCmd      = vPorts[port_id++];
668 
669         TRACE_PORT(vPorts[port_id]);
670         p3DFile         = vPorts[port_id++];
671         TRACE_PORT(vPorts[port_id]);
672         p3DStatus       = vPorts[port_id++];
673         TRACE_PORT(vPorts[port_id]);
674         p3DProgress     = vPorts[port_id++];
675         TRACE_PORT(vPorts[port_id]);
676         p3DOrientation  = vPorts[port_id++];
677         TRACE_PORT(vPorts[port_id]);
678         pScaleX         = vPorts[port_id++];
679         TRACE_PORT(vPorts[port_id]);
680         pScaleY         = vPorts[port_id++];
681         TRACE_PORT(vPorts[port_id]);
682         pScaleZ         = vPorts[port_id++];
683 
684         // Skip camera settings
685         TRACE_PORT(vPorts[port_id]);            // Skip camera x
686         port_id++;
687         TRACE_PORT(vPorts[port_id]);            // Skip camera y
688         port_id++;
689         TRACE_PORT(vPorts[port_id]);            // Skip camera z
690         port_id++;
691         TRACE_PORT(vPorts[port_id]);            // Skip camera yaw
692         port_id++;
693         TRACE_PORT(vPorts[port_id]);            // Skip camera pitch
694         port_id++;
695 
696         // Bind sources
697         TRACE_PORT(vPorts[port_id]);            // Skip source selector
698         port_id++;
699 
700         for (size_t i=0; i<room_builder_base_metadata::SOURCES; ++i)
701         {
702             source_t *src   = &vSources[i];
703 
704             TRACE_PORT(vPorts[port_id]);
705             src->pEnabled       = vPorts[port_id++];
706             TRACE_PORT(vPorts[port_id]);
707             src->pType          = vPorts[port_id++];
708             TRACE_PORT(vPorts[port_id]);
709             src->pPhase         = vPorts[port_id++];
710             TRACE_PORT(vPorts[port_id]);
711             src->pPosX          = vPorts[port_id++];
712             TRACE_PORT(vPorts[port_id]);
713             src->pPosY          = vPorts[port_id++];
714             TRACE_PORT(vPorts[port_id]);
715             src->pPosZ          = vPorts[port_id++];
716             TRACE_PORT(vPorts[port_id]);
717             src->pYaw           = vPorts[port_id++];
718             TRACE_PORT(vPorts[port_id]);
719             src->pPitch         = vPorts[port_id++];
720             TRACE_PORT(vPorts[port_id]);
721             src->pRoll          = vPorts[port_id++];
722             TRACE_PORT(vPorts[port_id]);
723             src->pSize          = vPorts[port_id++];
724             TRACE_PORT(vPorts[port_id]);
725             src->pHeight        = vPorts[port_id++];
726             TRACE_PORT(vPorts[port_id]);
727             src->pAngle         = vPorts[port_id++];
728             TRACE_PORT(vPorts[port_id]);
729             src->pCurvature     = vPorts[port_id++];
730 
731             TRACE_PORT(vPorts[port_id]);
732             port_id++;          // Skip hue value
733         }
734 
735         // Bind captures
736         TRACE_PORT(vPorts[port_id]);            // Skip capture selector
737         port_id++;
738 
739         for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
740         {
741             capture_t *cap  = &vCaptures[i];
742 
743             TRACE_PORT(vPorts[port_id]);
744             cap->pEnabled       = vPorts[port_id++];
745             TRACE_PORT(vPorts[port_id]);
746             cap->pRMin          = vPorts[port_id++];
747             TRACE_PORT(vPorts[port_id]);
748             cap->pRMax          = vPorts[port_id++];
749             TRACE_PORT(vPorts[port_id]);
750             cap->pPosX          = vPorts[port_id++];
751             TRACE_PORT(vPorts[port_id]);
752             cap->pPosY          = vPorts[port_id++];
753             TRACE_PORT(vPorts[port_id]);
754             cap->pPosZ          = vPorts[port_id++];
755             TRACE_PORT(vPorts[port_id]);
756             cap->pYaw           = vPorts[port_id++];
757             TRACE_PORT(vPorts[port_id]);
758             cap->pPitch         = vPorts[port_id++];
759             TRACE_PORT(vPorts[port_id]);
760             cap->pRoll          = vPorts[port_id++];
761             TRACE_PORT(vPorts[port_id]);
762             cap->pCapsule       = vPorts[port_id++];
763             TRACE_PORT(vPorts[port_id]);
764             cap->pConfig        = vPorts[port_id++];
765             TRACE_PORT(vPorts[port_id]);
766             cap->pAngle         = vPorts[port_id++];
767             TRACE_PORT(vPorts[port_id]);
768             cap->pDistance      = vPorts[port_id++];
769             TRACE_PORT(vPorts[port_id]);
770             cap->pDirection     = vPorts[port_id++];
771             TRACE_PORT(vPorts[port_id]);
772             cap->pSide          = vPorts[port_id++];
773 
774             TRACE_PORT(vPorts[port_id]);
775             cap->pHeadCut       = vPorts[port_id++];
776             TRACE_PORT(vPorts[port_id]);
777             cap->pTailCut       = vPorts[port_id++];
778             TRACE_PORT(vPorts[port_id]);
779             cap->pFadeIn        = vPorts[port_id++];
780             TRACE_PORT(vPorts[port_id]);
781             cap->pFadeOut       = vPorts[port_id++];
782             TRACE_PORT(vPorts[port_id]);
783             cap->pListen        = vPorts[port_id++];
784             TRACE_PORT(vPorts[port_id]);
785             cap->pReverse       = vPorts[port_id++];
786             TRACE_PORT(vPorts[port_id]);
787             cap->pMakeup        = vPorts[port_id++];
788             TRACE_PORT(vPorts[port_id]);
789             cap->pStatus        = vPorts[port_id++];
790             TRACE_PORT(vPorts[port_id]);
791             cap->pLength        = vPorts[port_id++];
792             TRACE_PORT(vPorts[port_id]);
793             cap->pCurrLen       = vPorts[port_id++];
794             TRACE_PORT(vPorts[port_id]);
795             cap->pMaxLen        = vPorts[port_id++];
796             TRACE_PORT(vPorts[port_id]);
797             cap->pThumbs        = vPorts[port_id++];
798 
799             TRACE_PORT(vPorts[port_id]);
800             cap->pOutFile       = vPorts[port_id++];
801             TRACE_PORT(vPorts[port_id]);
802             cap->pSaveCmd       = vPorts[port_id++];
803             TRACE_PORT(vPorts[port_id]);
804             cap->pSaveStatus    = vPorts[port_id++];
805             TRACE_PORT(vPorts[port_id]);
806             cap->pSaveProgress  = vPorts[port_id++];
807 
808             TRACE_PORT(vPorts[port_id]);
809             port_id++;          // Skip hue value
810         }
811 
812         // Bind convolver ports
813         for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
814         {
815             lsp_trace("Binding convolution #%d ports", int(i));
816             convolver_t *c  = &vConvolvers[i];
817 
818             if (nInputs > 1)    // Input panning
819             {
820                 TRACE_PORT(vPorts[port_id]);
821                 c->pPanIn       = vPorts[port_id++];
822             }
823 
824             TRACE_PORT(vPorts[port_id]);
825             c->pSample      = vPorts[port_id++];
826             TRACE_PORT(vPorts[port_id]);
827             c->pTrack       = vPorts[port_id++];
828             TRACE_PORT(vPorts[port_id]);
829             c->pMakeup      = vPorts[port_id++];
830             TRACE_PORT(vPorts[port_id]);
831             c->pMute        = vPorts[port_id++];
832             TRACE_PORT(vPorts[port_id]);
833             c->pActivity    = vPorts[port_id++];
834             TRACE_PORT(vPorts[port_id]);
835             c->pPredelay    = vPorts[port_id++];
836             TRACE_PORT(vPorts[port_id]);
837             c->pPanOut      = vPorts[port_id++];
838         }
839 
840         // Bind wet processing ports
841         lsp_trace("Binding wet processing ports");
842         size_t port         = port_id;
843         for (size_t i=0; i<2; ++i)
844         {
845             channel_t *c        = &vChannels[i];
846 
847             TRACE_PORT(vPorts[port_id]);
848             c->pWetEq           = vPorts[port_id++];
849             TRACE_PORT(vPorts[port_id]);
850             c->pLowCut          = vPorts[port_id++];
851             TRACE_PORT(vPorts[port_id]);
852             c->pLowFreq         = vPorts[port_id++];
853 
854             for (size_t j=0; j<room_builder_base_metadata::EQ_BANDS; ++j)
855             {
856                 TRACE_PORT(vPorts[port_id]);
857                 c->pFreqGain[j]     = vPorts[port_id++];
858             }
859 
860             TRACE_PORT(vPorts[port_id]);
861             c->pHighCut         = vPorts[port_id++];
862             TRACE_PORT(vPorts[port_id]);
863             c->pHighFreq        = vPorts[port_id++];
864 
865             port_id         = port;
866         }
867     }
868 
destroy()869     void room_builder_base::destroy()
870     {
871         // Stop active rendering task
872         if (pRenderer != NULL)
873         {
874             pRenderer->terminate();
875             pRenderer->join();
876             delete pRenderer;
877             pRenderer = NULL;
878         }
879 
880         sScene.destroy();
881         s3DLoader.destroy();
882 
883         if (pData != NULL)
884         {
885             free_aligned(pData);
886             pData       = NULL;
887         }
888 
889         // Destroy captures
890         for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
891         {
892             capture_t *c    = &vCaptures[i];
893             if (c->pCurr != NULL)
894             {
895                 c->pCurr->destroy();
896                 delete c->pCurr;
897                 c->pCurr        = NULL;
898             }
899             if (c->pSwap != NULL)
900             {
901                 c->pSwap->destroy();
902                 delete c->pSwap;
903                 c->pSwap        = NULL;
904             }
905         }
906 
907         for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
908         {
909             convolver_t *c  = &vConvolvers[i];
910             if (c->pCurr != NULL)
911             {
912                 c->pCurr->destroy();
913                 delete c->pCurr;
914                 c->pCurr    = NULL;
915             }
916             if (c->pSwap != NULL)
917             {
918                 c->pSwap->destroy();
919                 delete c->pSwap;
920                 c->pSwap    = NULL;
921             }
922 
923             c->sDelay.destroy();
924         }
925 
926         // Destroy channels
927         for (size_t i=0; i<2; ++i)
928         {
929             channel_t *c = &vChannels[i];
930             c->sEqualizer.destroy();
931             c->sPlayer.destroy(false);
932             c->vOut     = NULL;
933             c->vBuffer  = NULL;
934         }
935     }
936 
get_fft_rank(size_t rank)937     size_t room_builder_base::get_fft_rank(size_t rank)
938     {
939         return room_builder_base_metadata::FFT_RANK_MIN + rank;
940     }
941 
update_settings()942     void room_builder_base::update_settings()
943     {
944         float out_gain      = pOutGain->getValue();
945         float dry_gain      = pDry->getValue() * out_gain;
946         float wet_gain      = pWet->getValue() * out_gain;
947         bool bypass         = pBypass->getValue() >= 0.5f;
948         float predelay      = pPredelay->getValue();
949         size_t rank         = get_fft_rank(pRank->getValue());
950 
951         // Adjust FFT rank
952         if (rank != nFftRank)
953         {
954             nFftRank            = rank;
955             sConfigurator.queue_launch();
956         }
957 
958         // Adjust size of scene and number of threads to render
959         sScale.dx           = pScaleX->getValue() * 0.01f;
960         sScale.dy           = pScaleY->getValue() * 0.01f;
961         sScale.dz           = pScaleZ->getValue() * 0.01f;
962         nRenderThreads      = pRenderThreads->getValue();
963         bRenderNormalize    = pRenderNormalize->getValue() >= 0.5f;
964         fRenderQuality      = pRenderQuality->getValue() * 0.01f;
965 
966         // Check that render request has been triggered
967         float old_cmd       = fRenderCmd;
968         fRenderCmd          = pRenderCmd->getValue();
969         if ((old_cmd >= 0.5f) && (fRenderCmd < 0.5f))
970         {
971             lsp_trace("Triggered render request");
972             nSync              |= SYNC_TOGGLE_RENDER;
973         }
974 
975         // Adjust volume of dry channel
976         if (nInputs == 1)
977         {
978             float pan               = vInputs[0].pPan->getValue();
979             vChannels[0].fDryPan[0] = (100.0f - pan) * 0.005f * dry_gain;
980             vChannels[0].fDryPan[1] = 0.0f;
981             vChannels[1].fDryPan[0] = (100.0f + pan) * 0.005f * dry_gain;
982             vChannels[1].fDryPan[1] = 0.0f;
983         }
984         else
985         {
986             float pan_l             = vInputs[0].pPan->getValue();
987             float pan_r             = vInputs[1].pPan->getValue();
988 
989             vChannels[0].fDryPan[0] = (100.0f - pan_l) * 0.005f * dry_gain;
990             vChannels[0].fDryPan[1] = (100.0f - pan_r) * 0.005f * dry_gain;
991             vChannels[1].fDryPan[0] = (100.0f + pan_l) * 0.005f * dry_gain;
992             vChannels[1].fDryPan[1] = (100.0f + pan_r) * 0.005f * dry_gain;
993         }
994 
995         // Update source settings
996         for (size_t i=0; i<room_builder_base_metadata::SOURCES; ++i)
997         {
998             source_t *src       = &vSources[i];
999             src->bEnabled       = src->pEnabled->getValue() >= 0.5f;
1000             src->enType         = decode_source_type(src->pType->getValue());
1001             src->sPos.x         = src->pPosX->getValue();
1002             src->sPos.y         = src->pPosY->getValue();
1003             src->sPos.z         = src->pPosZ->getValue();
1004             src->sPos.w         = 1.0f;
1005             src->fYaw           = src->pYaw->getValue();
1006             src->fPitch         = src->pPitch->getValue();
1007             src->fRoll          = src->pRoll->getValue();
1008             src->fSize          = src->pSize->getValue() * 0.01f;   // cm -> m
1009             src->fHeight        = src->pHeight->getValue() * 0.01f; // cm -> m
1010             src->fAngle         = src->pAngle->getValue();
1011             src->fCurvature     = src->pCurvature->getValue();
1012             src->fAmplitude     = (src->pPhase->getValue() >= 0.5f) ? -1.0f : 1.0f;
1013         }
1014 
1015         // Update capture settings
1016         for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
1017         {
1018             capture_t *cap      = &vCaptures[i];
1019 
1020             cap->bEnabled       = cap->pEnabled->getValue() >= 0.5f;
1021             cap->nRMin          = ssize_t(cap->pRMin->getValue()) - 1;
1022             cap->nRMax          = ssize_t(cap->pRMax->getValue()) - 1;
1023             cap->sPos.x         = cap->pPosX->getValue();
1024             cap->sPos.y         = cap->pPosY->getValue();
1025             cap->sPos.z         = cap->pPosZ->getValue();
1026             cap->sPos.w         = 1.0f;
1027             cap->fYaw           = cap->pYaw->getValue();
1028             cap->fPitch         = cap->pPitch->getValue();
1029             cap->fRoll          = cap->pRoll->getValue();
1030             cap->fCapsule       = cap->pCapsule->getValue() * 0.5f;
1031             cap->sConfig        = decode_config(cap->pConfig->getValue());
1032             cap->fAngle         = cap->pAngle->getValue();
1033             cap->fDistance      = cap->pDistance->getValue();
1034             cap->enDirection    = decode_direction(cap->pDirection->getValue());
1035             cap->enSide         = decode_side_direction(cap->pSide->getValue());
1036             cap->fMakeup        = cap->pMakeup->getValue();
1037 
1038             // Accept changes
1039             path_t *path        = cap->pOutFile->getBuffer<path_t>();
1040             if ((path != NULL) && (path->pending()))
1041             {
1042                 path->accept();
1043                 path->commit();
1044             }
1045             if (cap->pSaveCmd->getValue() >= 0.5f) // Toggle the export flag
1046                 cap->bExport        = true;
1047 
1048             // Check that we need to synchronize capture settings with convolver
1049             float hcut      = cap->pHeadCut->getValue();
1050             float tcut      = cap->pTailCut->getValue();
1051             float fadein    = cap->pFadeIn->getValue();
1052             float fadeout   = cap->pFadeOut->getValue();
1053             bool  reverse   = cap->pReverse->getValue() >= 0.5f;
1054 
1055             if ((cap->fHeadCut != hcut) ||
1056                 (cap->fTailCut != tcut) ||
1057                 (cap->fFadeIn != fadein) ||
1058                 (cap->fFadeOut != fadeout) ||
1059                 (cap->bReverse != reverse))
1060             {
1061                 cap->fHeadCut       = hcut;
1062                 cap->fTailCut       = tcut;
1063                 cap->fFadeIn        = fadein;
1064                 cap->fFadeOut       = fadeout;
1065                 cap->bReverse       = reverse;
1066 
1067                 atomic_add(&cap->nChangeReq, 1);
1068                 sConfigurator.queue_launch();
1069             }
1070 
1071             // Listen button pressed?
1072             if (cap->pListen->getValue() >= 0.5f)
1073             {
1074                 size_t n_c = (cap->pCurr != NULL) ? cap->pCurr->channels() : 0;
1075                 if (n_c > 0)
1076                 {
1077                     for (size_t j=0; j<2; ++j)
1078                         vChannels[j].sPlayer.play(i, j % n_c, cap->fMakeup, 0);
1079                 }
1080             }
1081         }
1082 
1083         // Adjust channel setup
1084         for (size_t i=0; i<2; ++i)
1085         {
1086             channel_t *c        = &vChannels[i];
1087             c->sBypass.set_bypass(bypass);
1088             c->sPlayer.set_gain(out_gain);
1089 
1090             // Update equalization parameters
1091             Equalizer *eq               = &c->sEqualizer;
1092             equalizer_mode_t eq_mode    = (c->pWetEq->getValue() >= 0.5f) ? EQM_IIR : EQM_BYPASS;
1093             eq->set_mode(eq_mode);
1094 
1095             if (eq_mode != EQM_BYPASS)
1096             {
1097                 filter_params_t fp;
1098                 size_t band     = 0;
1099 
1100                 // Set-up parametric equalizer
1101                 while (band < room_builder_base_metadata::EQ_BANDS)
1102                 {
1103                     if (band == 0)
1104                     {
1105                         fp.fFreq        = band_freqs[band];
1106                         fp.fFreq2       = fp.fFreq;
1107                         fp.nType        = FLT_MT_LRX_LOSHELF;
1108                     }
1109                     else if (band == (room_builder_base_metadata::EQ_BANDS - 1))
1110                     {
1111                         fp.fFreq        = band_freqs[band-1];
1112                         fp.fFreq2       = fp.fFreq;
1113                         fp.nType        = FLT_MT_LRX_HISHELF;
1114                     }
1115                     else
1116                     {
1117                         fp.fFreq        = band_freqs[band-1];
1118                         fp.fFreq2       = band_freqs[band];
1119                         fp.nType        = FLT_MT_LRX_LADDERPASS;
1120                     }
1121 
1122                     fp.fGain        = c->pFreqGain[band]->getValue();
1123                     fp.nSlope       = 2;
1124                     fp.fQuality     = 0.0f;
1125 
1126                     // Update filter parameters
1127                     eq->set_params(band++, &fp);
1128                 }
1129 
1130                 // Setup hi-pass filter
1131                 size_t hp_slope = c->pLowCut->getValue() * 2;
1132                 fp.nType        = (hp_slope > 0) ? FLT_BT_BWC_HIPASS : FLT_NONE;
1133                 fp.fFreq        = c->pLowFreq->getValue();
1134                 fp.fFreq2       = fp.fFreq;
1135                 fp.fGain        = 1.0f;
1136                 fp.nSlope       = hp_slope;
1137                 fp.fQuality     = 0.0f;
1138                 eq->set_params(band++, &fp);
1139 
1140                 // Setup low-pass filter
1141                 size_t lp_slope = c->pHighCut->getValue() * 2;
1142                 fp.nType        = (lp_slope > 0) ? FLT_BT_BWC_LOPASS : FLT_NONE;
1143                 fp.fFreq        = c->pHighFreq->getValue();
1144                 fp.fFreq2       = fp.fFreq;
1145                 fp.fGain        = 1.0f;
1146                 fp.nSlope       = lp_slope;
1147                 fp.fQuality     = 0.0f;
1148                 eq->set_params(band++, &fp);
1149             }
1150         }
1151 
1152         // Update settings of convolvers
1153         for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
1154         {
1155             convolver_t *cv         = &vConvolvers[i];
1156 
1157             // Allow to reconfigure convolver only when configuration task is in idle state
1158             size_t sampleid         = cv->pSample->getValue();
1159             size_t trackid          = cv->pTrack->getValue();
1160 
1161             if ((cv->nSampleID != sampleid) ||
1162                 (cv->nTrackID != trackid))
1163             {
1164                 cv->nSampleID           = sampleid;
1165                 cv->nTrackID            = trackid;
1166                 sConfigurator.queue_launch();
1167             }
1168 
1169             // Apply panning to each convolver
1170             float smakeup           = (sampleid > 0) ? vCaptures[sampleid-1].fMakeup : 1.0f; // Sample makeup
1171             float makeup            = (cv->pMute->getValue() < 0.5f) ? cv->pMakeup->getValue() * wet_gain * smakeup : 0.0f;
1172             if (nInputs == 1)
1173             {
1174                 cv->fPanIn[0]       = 1.0f;
1175                 cv->fPanIn[1]       = 0.0f;
1176             }
1177             else
1178             {
1179                 float pan           = cv->pPanIn->getValue();
1180                 cv->fPanIn[0]       = (100.0f - pan) * 0.005f;
1181                 cv->fPanIn[1]       = (100.0f + pan) * 0.005f;
1182             }
1183 
1184             float pan           = cv->pPanOut->getValue();
1185             cv->fPanOut[0]      = (100.0f - pan) * 0.005f * makeup;
1186             cv->fPanOut[1]      = (100.0f + pan) * 0.005f * makeup;
1187 
1188             // Set pre-delay
1189             cv->sDelay.set_delay(millis_to_samples(fSampleRate, predelay + cv->pPredelay->getValue()));
1190         }
1191     }
1192 
decode_source_type(float value)1193     rt_audio_source_t room_builder_base::decode_source_type(float value)
1194     {
1195         switch (ssize_t(value))
1196         {
1197             case 1:     return RT_AS_TETRA;
1198             case 2:     return RT_AS_OCTA;
1199             case 3:     return RT_AS_BOX;
1200             case 4:     return RT_AS_ICO;
1201             case 5:     return RT_AS_CYLINDER;
1202             case 6:     return RT_AS_CONE;
1203             case 7:     return RT_AS_OCTASPHERE;
1204             case 8:     return RT_AS_ICOSPHERE;
1205             case 9:     return RT_AS_FSPOT;
1206             case 10:    return RT_AS_CSPOT;
1207             case 11:    return RT_AS_SSPOT;
1208             default:    break;
1209         }
1210         return RT_AS_TRIANGLE;
1211     }
1212 
decode_config(float value)1213     rt_capture_config_t room_builder_base::decode_config(float value)
1214     {
1215         switch (ssize_t(value))
1216         {
1217             case 1:     return RT_CC_XY;
1218             case 2:     return RT_CC_AB;
1219             case 3:     return RT_CC_ORTF;
1220             case 4:     return RT_CC_MS;
1221             default:    break;
1222         }
1223         return RT_CC_MONO;
1224     }
1225 
decode_direction(float value)1226     rt_audio_capture_t room_builder_base::decode_direction(float value)
1227     {
1228         switch (ssize_t(value))
1229         {
1230             case 1:     return RT_AC_SCARDIO; break;
1231             case 2:     return RT_AC_HCARDIO; break;
1232             case 3:     return RT_AC_BIDIR; break;
1233             case 4:     return RT_AC_EIGHT; break;
1234             case 5:     return RT_AC_OMNI; break;
1235             default:    break;
1236         }
1237         return RT_AC_CARDIO;
1238     }
1239 
decode_side_direction(float value)1240     rt_audio_capture_t room_builder_base::decode_side_direction(float value)
1241     {
1242         switch (ssize_t(value))
1243         {
1244             case 1: return RT_AC_EIGHT;
1245             default: break;
1246         }
1247         return RT_AC_BIDIR;
1248     }
1249 
update_sample_rate(long sr)1250     void room_builder_base::update_sample_rate(long sr)
1251     {
1252         for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
1253             vConvolvers[i].sDelay.init(millis_to_samples(sr, room_builder_base_metadata::PREDELAY_MAX * 4.0f));
1254 
1255         for (size_t i=0; i<2; ++i)
1256         {
1257             vChannels[i].sBypass.init(sr);
1258             vChannels[i].sEqualizer.set_sample_rate(sr);
1259         }
1260     }
1261 
process(size_t samples)1262     void room_builder_base::process(size_t samples)
1263     {
1264         // Stage 1: Process reconfiguration requests and file events
1265         sync_offline_tasks();
1266 
1267         // Stage 2: Main processing
1268         for (size_t i=0; i<nInputs; ++i)
1269             vInputs[i].vIn      = vInputs[i].pIn->getBuffer<float>();
1270 
1271         for (size_t i=0; i<2; ++i)
1272             vChannels[i].vOut   = vChannels[i].pOut->getBuffer<float>();
1273 
1274         // Process samples
1275         while (samples > 0)
1276         {
1277             // Determine number of samples to process
1278             size_t to_do        = TMP_BUF_SIZE;
1279             if (to_do > samples)
1280                 to_do               = samples;
1281 
1282             // Clear temporary channel buffer
1283             dsp::fill_zero(vChannels[0].vBuffer, to_do);
1284             dsp::fill_zero(vChannels[1].vBuffer, to_do);
1285 
1286             // Call convolvers
1287             for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
1288             {
1289                 convolver_t *c      = &vConvolvers[i];
1290 
1291                 // Prepare input buffer: apply panning if present
1292                 if (nInputs == 1)
1293                     dsp::copy(c->vBuffer, vInputs[0].vIn, to_do);
1294                 else
1295                     dsp::mix_copy2(c->vBuffer, vInputs[0].vIn, vInputs[1].vIn, c->fPanIn[0], c->fPanIn[1], to_do);
1296 
1297                 // Do processing
1298                 if (c->pCurr != NULL)
1299                     c->pCurr->process(c->vBuffer, c->vBuffer, to_do);
1300                 else
1301                     dsp::fill_zero(c->vBuffer, to_do);
1302                 c->sDelay.process(c->vBuffer, c->vBuffer, to_do);
1303 
1304                 // Apply processed signal to output channels
1305                 dsp::fmadd_k3(vChannels[0].vBuffer, c->vBuffer, c->fPanOut[0], to_do);
1306                 dsp::fmadd_k3(vChannels[1].vBuffer, c->vBuffer, c->fPanOut[1], to_do);
1307             }
1308 
1309             // Now apply equalization, bypass control and players
1310             for (size_t i=0; i<2; ++i)
1311             {
1312                 channel_t *c        = &vChannels[i];
1313 
1314                 // Apply equalization
1315                 c->sEqualizer.process(c->vBuffer, c->vBuffer, to_do);
1316 
1317                 // Pass dry sound to output channels
1318                 if (nInputs == 1)
1319                     dsp::fmadd_k3(c->vBuffer, vInputs[0].vIn, c->fDryPan[0], to_do);
1320                 else
1321                     dsp::mix_add2(c->vBuffer, vInputs[0].vIn, vInputs[1].vIn, c->fDryPan[0], c->fDryPan[1], to_do);
1322 
1323                 // Apply player and bypass
1324                 c->sPlayer.process(c->vBuffer, c->vBuffer, to_do);
1325                 c->sBypass.process(c->vOut, vInputs[i%nInputs].vIn, c->vBuffer, to_do);
1326 
1327                 // Update pointers
1328                 c->vOut            += to_do;
1329             }
1330 
1331             for (size_t i=0; i<nInputs; ++i)
1332                 vInputs[i].vIn     += to_do;
1333 
1334             samples            -= to_do;
1335         }
1336 
1337         // Stage 3: output additional metering parameters
1338         if (p3DStatus != NULL)
1339             p3DStatus->setValue(nSceneStatus);
1340         if (p3DProgress != NULL)
1341             p3DProgress->setValue(fSceneProgress);
1342         if (pRenderStatus != NULL)
1343             pRenderStatus->setValue(enRenderStatus);
1344         if (pRenderProgress != NULL)
1345             pRenderProgress->setValue(fRenderProgress);
1346 
1347         for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
1348         {
1349             // Output information about the convolver
1350             convolver_t *c          = &vConvolvers[i];
1351             c->pActivity->setValue(c->pCurr != NULL);
1352         }
1353 
1354         for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
1355         {
1356             capture_t *c            = &vCaptures[i];
1357 
1358             // Output information about the file
1359             size_t length           = c->nLength;
1360             c->pLength->setValue(samples_to_millis(fSampleRate, length));
1361             c->pCurrLen->setValue(c->fCurrLen);
1362             c->pMaxLen->setValue(c->fMaxLen);
1363             c->pStatus->setValue(c->nStatus);
1364 
1365             // Store file dump to mesh
1366             mesh_t *mesh        = c->pThumbs->getBuffer<mesh_t>();
1367             if ((mesh == NULL) || (!mesh->isEmpty()) || (!c->bSync))
1368                 continue;
1369 
1370             size_t channels     = (c->pCurr != NULL) ? c->pCurr->channels() : 0;
1371             if (channels > 0)
1372             {
1373                 // Copy thumbnails
1374                 for (size_t j=0; j<channels; ++j)
1375                     dsp::copy(mesh->pvData[j], c->vThumbs[j], room_builder_base_metadata::MESH_SIZE);
1376                 mesh->data(channels, room_builder_base_metadata::MESH_SIZE);
1377             }
1378             else
1379                 mesh->data(0, 0);
1380             c->bSync            = false;
1381         }
1382     }
1383 
sync_offline_tasks()1384     void room_builder_base::sync_offline_tasks()
1385     {
1386         // The render signal is pending?
1387         if ((nSync & SYNC_TOGGLE_RENDER) && (s3DLauncher.idle()) && (s3DLoader.idle()))
1388         {
1389             if (pExecutor->submit(&s3DLauncher))
1390             {
1391                 lsp_trace("Successfully submitted Render launcher task");
1392                 nSync &= ~SYNC_TOGGLE_RENDER;       // Reset render request flag
1393             }
1394         }
1395         else if (s3DLauncher.completed())
1396         {
1397             status_t res = s3DLauncher.code();
1398             if (res != STATUS_OK)
1399             {
1400                 fRenderProgress = 0.0f;
1401                 enRenderStatus  = s3DLauncher.code();
1402             }
1403             s3DLauncher.reset();
1404         }
1405 
1406         // Check the state of input file
1407         path_t *path        = p3DFile->getBuffer<path_t>();
1408         if (path != NULL)
1409         {
1410             if ((path->pending()) && (s3DLoader.idle()) && (s3DLauncher.idle())) // There is pending request for 3D file reload
1411             {
1412                 // Copy path
1413                 ::strncpy(s3DLoader.sPath, path->get_path(), PATH_MAX);
1414                 s3DLoader.nFlags            = path->get_flags();
1415                 s3DLoader.sPath[PATH_MAX]   = '\0';
1416                 lsp_trace("Submitted scene file %s", s3DLoader.sPath);
1417 
1418                 // Try to submit task
1419                 if (pExecutor->submit(&s3DLoader))
1420                 {
1421                     lsp_trace("Successfully submitted load task");
1422                     nSceneStatus    = STATUS_LOADING;
1423                     fSceneProgress  = 0.0f;
1424                     path->accept();
1425                 }
1426             }
1427             else if ((path->accepted()) && (s3DLoader.completed())) // The reload request has been processed
1428             {
1429                 // Update file status and set re-rendering flag
1430                 nSceneStatus    = s3DLoader.code();
1431                 fSceneProgress  = 100.0f;
1432 
1433                 sScene.swap(&s3DLoader.sScene);
1434                 nReconfigReq    ++;
1435 
1436                 // Now we surely can commit changes and reset task state
1437                 lsp_trace("File loading task has completed with status %d", int(nSceneStatus));
1438                 path->commit();
1439                 s3DLoader.reset();
1440             }
1441         }
1442 
1443         if (sSaver.idle())
1444         {
1445             // Submit save requests
1446             for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
1447             {
1448                 capture_t *cap      = &vCaptures[i];
1449                 if (!cap->bExport)
1450                     continue;
1451 
1452                 sSaver.bind(i, cap);
1453                 if (pExecutor->submit(&sSaver))
1454                 {
1455                     cap->bExport        = false;
1456                     cap->pSaveStatus->setValue(STATUS_LOADING);
1457                     cap->pSaveProgress->setValue(0.0f);
1458                     break;
1459                 }
1460             }
1461         }
1462         else if (sSaver.completed())
1463         {
1464             capture_t *cap = &vCaptures[sSaver.nSampleID];
1465             cap->pSaveStatus->setValue(sSaver.code());
1466             cap->pSaveProgress->setValue(100.0f);
1467 
1468             sSaver.reset();
1469         }
1470 
1471         // Do we need to launch configurator task?
1472         if ((sConfigurator.idle()) && (sConfigurator.need_launch()))
1473         {
1474             reconfig_t *cfg = &sConfigurator.sConfig;
1475 
1476             // Deploy actual configuration to the configurator
1477             for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
1478             {
1479                 capture_t *cap      = &vCaptures[i];
1480                 size_t req          = cap->nChangeReq;
1481 
1482                 cfg->bReconfigure[i]    = (cap->nChangeResp != req);
1483                 cfg->nChangeResp[i]     = req;
1484             }
1485 
1486             for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
1487             {
1488                 convolver_t *cv     = &vConvolvers[i];
1489 
1490                 cfg->nSampleID[i]   = cv->nSampleID;
1491                 cfg->nTrack[i]      = cv->nTrackID;
1492                 cfg->nRank[i]       = nFftRank;
1493             }
1494 
1495             // Try to launch configurator
1496             if (pExecutor->submit(&sConfigurator))
1497             {
1498                 sConfigurator.launched();
1499                 lsp_trace("Successfully submitted reconfigurator task");
1500             }
1501         }
1502         else if ((sConfigurator.completed()) && (sSaver.idle()))
1503         {
1504             lsp_trace("Reconfiguration task has completed with status %d", int(sConfigurator.code()));
1505 
1506             // Commit state of convolvers
1507             for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
1508             {
1509                 convolver_t *c  = &vConvolvers[i];
1510                 Convolver *cv   = c->pCurr;
1511                 c->pCurr        = c->pSwap;
1512                 c->pSwap        = cv;
1513             }
1514 
1515             for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
1516             {
1517                 capture_t  *c   = &vCaptures[i];
1518                 if (!c->bCommit)
1519                     continue;
1520 
1521                 c->bCommit      = false;
1522                 c->bSync        = true;
1523 
1524                 Sample *s       = c->pCurr;
1525                 c->pCurr        = c->pSwap;
1526                 c->pSwap        = s;
1527 
1528                 // Bind sample player
1529                 for (size_t j=0; j<2; ++j)
1530                 {
1531                     channel_t *sc = &vChannels[j];
1532                     sc->sPlayer.bind(i, c->pCurr, false);
1533                 }
1534             }
1535 
1536             // Accept the configurator task
1537             sConfigurator.reset();
1538         }
1539     }
1540 
bind_sources(RayTrace3D * rt)1541     status_t room_builder_base::bind_sources(RayTrace3D *rt)
1542     {
1543         size_t sources = 0;
1544 
1545         for (size_t i=0; i<room_builder_base_metadata::SOURCES; ++i)
1546         {
1547             source_t *src = &vSources[i];
1548             if (!src->bEnabled)
1549                 continue;
1550 
1551             // Configure source
1552             rt_source_settings_t ss;
1553             status_t res = rt_configure_source(&ss, src);
1554             if (res != STATUS_OK)
1555                 return res;
1556 
1557             // Add source to capture
1558             res = rt->add_source(&ss);
1559             if (res != STATUS_OK)
1560                 return res;
1561 
1562             ++sources;
1563         }
1564 
1565         return (sources <= 0) ? STATUS_NO_SOURCES : STATUS_OK;
1566     }
1567 
bind_captures(cvector<sample_t> & samples,RayTrace3D * rt)1568     status_t room_builder_base::bind_captures(cvector<sample_t> &samples, RayTrace3D *rt)
1569     {
1570         size_t captures = 0;
1571 
1572         for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
1573         {
1574             capture_t *cap = &vCaptures[i];
1575             if (!cap->bEnabled)
1576                 continue;
1577             else if ((cap->nRMax >= 0) && (cap->nRMax < cap->nRMin)) // Validate nRMin and nRMax
1578                 continue;
1579 
1580             // Configure capture
1581             size_t n = 0;
1582             rt_capture_settings_t cs[2];
1583             status_t res = rt_configure_capture(&n, cs, cap);
1584             if (res != STATUS_OK)
1585                 return res;
1586 
1587             // Create sample, add to list and initialize
1588             sample_t *s = new sample_t();
1589             if (s == NULL)
1590                 return STATUS_NO_MEM;
1591             else if (!samples.add(s))
1592             {
1593                 delete s;
1594                 return STATUS_NO_MEM;
1595             }
1596             s->nID          = i;
1597             s->enConfig     = cap->sConfig;
1598             if (!s->sSample.init(n, 512))
1599                 return STATUS_NO_MEM;
1600 
1601             // Bind captures to samples
1602             for (size_t i=0; i<n; ++i)
1603             {
1604                 ssize_t cap_id = rt->add_capture(&cs[i]);
1605                 if (cap_id < 0)
1606                     return status_t(-cap_id);
1607 
1608                 res = rt->bind_capture(cap_id, &s->sSample, i, cap->nRMin, cap->nRMax);
1609                 if (res != STATUS_OK)
1610                     return res;
1611 
1612                 ++captures;
1613             }
1614         }
1615 
1616         return (captures <= 0) ? STATUS_NO_CAPTURES : STATUS_OK;
1617     }
1618 
destroy_samples(cvector<sample_t> & samples)1619     void room_builder_base::destroy_samples(cvector<sample_t> &samples)
1620     {
1621         for (size_t i=0, n=samples.size(); i<n; ++i)
1622         {
1623             sample_t *s = samples.at(i);
1624             if (s != NULL)
1625             {
1626                 s->sSample.destroy();
1627                 delete s;
1628             }
1629         }
1630         samples.flush();
1631     }
1632 
bind_scene(KVTStorage * kvt,RayTrace3D * rt)1633     status_t room_builder_base::bind_scene(KVTStorage *kvt, RayTrace3D *rt)
1634     {
1635         // Clone the scene
1636         Scene3D *dst = new Scene3D();
1637         if (dst == NULL)
1638             return STATUS_NO_MEM;
1639 
1640         status_t res = dst->clone_from(&sScene);
1641         if (res != STATUS_OK)
1642         {
1643             delete dst;
1644             return res;
1645         }
1646 
1647         // Set-up scene
1648         res = rt->set_scene(dst, true);
1649         if (res != STATUS_OK)
1650         {
1651             dst->destroy();
1652             delete dst;
1653             return res;
1654         }
1655 
1656         // Update object properties
1657         obj_props_t props;
1658         char base[0x40];
1659         rt_material_t mat;
1660         matrix3d_t world;
1661         dsp::init_matrix3d_scale(&world, sScale.dx, sScale.dy, sScale.dz);
1662 
1663         for (size_t i=0, n=dst->num_objects(); i<n; ++i)
1664         {
1665             Object3D *obj = dst->object(i);
1666             if (obj == NULL)
1667                 continue;
1668 
1669             // Read object properties
1670             sprintf(base, "/scene/object/%d", int(i));
1671             read_object_properties(&props, base, kvt);
1672 
1673             // Update object matrix and visibility
1674             build_object_matrix(obj->matrix(), &props, &world);
1675             obj->set_visible(props.bEnabled);
1676 
1677             // Initialize material
1678             mat.absorption[0]   = props.fAbsorption[0] * 0.01f; // % -> units
1679             mat.absorption[1]   = props.fAbsorption[1] * 0.01f; // % -> units
1680             mat.diffusion[0]    = props.fDiffusion[0];
1681             mat.diffusion[1]    = props.fDiffusion[1];
1682             mat.dispersion[0]   = props.fDispersion[0];
1683             mat.dispersion[1]   = props.fDispersion[1];
1684             mat.transparency[0] = props.fTransparency[0] * 0.01f; // % -> units
1685             mat.transparency[1] = props.fTransparency[1] * 0.01f; // % -> units
1686             mat.permeability    = props.fSndSpeed / SOUND_SPEED_M_S;
1687 
1688             // Commit material properties
1689             res = rt->set_material(i, &mat);
1690             if (res != STATUS_OK)
1691                 return res;
1692         }
1693 
1694         return STATUS_OK;
1695     }
1696 
progress_callback(float progress,void * ptr)1697     status_t room_builder_base::progress_callback(float progress, void *ptr)
1698     {
1699         room_builder_base *_this    = reinterpret_cast<room_builder_base *>(ptr);
1700         _this->enRenderStatus       = STATUS_IN_PROCESS;
1701         _this->fRenderProgress      = progress * 100.0f;    // Update the progress value
1702         return STATUS_OK;
1703     }
1704 
start_rendering()1705     status_t room_builder_base::start_rendering()
1706     {
1707         // Terminate previous thread (if active)
1708         if (pRenderer != NULL)
1709         {
1710             bool finished = pRenderer->finished();
1711 
1712             pRenderer->terminate();
1713             pRenderer->join();
1714             delete pRenderer;
1715             pRenderer = NULL;
1716 
1717             // Current task has been cancelled?
1718             if (!finished)
1719             {
1720                 fRenderProgress = 0;
1721                 enRenderStatus  = STATUS_CANCELLED;
1722                 return STATUS_OK;
1723             }
1724         }
1725 
1726         // Create raytracing object and initialize with basic values
1727         RayTrace3D *rt = new RayTrace3D();
1728         if (rt == NULL)
1729             return STATUS_NO_MEM;
1730 
1731         status_t res = rt->init();
1732         if (res != STATUS_OK)
1733         {
1734             rt->destroy(false);
1735             delete rt;
1736             return res;
1737         }
1738 
1739         rt->set_sample_rate(fSampleRate);
1740 
1741         float energy    = 1e-3f * expf(-4.0f * M_LN10 * fRenderQuality);    // 1e-3 .. 1e-7
1742         float tolerance = 1e-4f * expf(-2.0f * M_LN10 * fRenderQuality);    // 1e-4 .. 1e-6
1743         float details   = 1e-8f * expf(-2.0f * M_LN10 * fRenderQuality);    // 1e-8 .. 1e-10
1744 
1745         rt->set_energy_threshold(energy);
1746         rt->set_tolerance(tolerance);
1747         rt->set_detalization(details);
1748         rt->set_normalize(bRenderNormalize);
1749         rt->set_progress_callback(progress_callback, this);
1750 
1751         // Bind scene to the raytracing
1752         KVTStorage *kvt = kvt_lock();
1753         if (kvt != NULL)
1754         {
1755             res = bind_scene(kvt, rt);
1756             kvt_release();
1757         }
1758         else
1759             res = STATUS_NO_DATA;
1760 
1761         // Bind sources
1762         res = bind_sources(rt);
1763         if (res != STATUS_OK)
1764         {
1765             rt->destroy(true);
1766             delete rt;
1767             return res;
1768         }
1769 
1770         // Bind captures
1771         cvector<sample_t> samples;
1772         res = bind_captures(samples, rt);
1773         if (res != STATUS_OK)
1774         {
1775             destroy_samples(samples);
1776             rt->destroy(true);
1777             delete rt;
1778             return res;
1779         }
1780 
1781         // Create renderer and start execution
1782         if (res == STATUS_OK)
1783         {
1784             pRenderer = new Renderer(this, rt, nRenderThreads, samples);
1785             if (pRenderer == NULL)
1786                 res = STATUS_NO_MEM;
1787             else if ((res = pRenderer->start()) != STATUS_OK)
1788             {
1789                 delete pRenderer;
1790                 pRenderer = NULL;
1791             }
1792         }
1793 
1794         if (res != STATUS_OK)
1795         {
1796             destroy_samples(samples);
1797             rt->destroy(true);
1798             delete rt;
1799             return res;
1800         }
1801 
1802         // All seems to be OK
1803         return STATUS_OK;
1804     }
1805 
commit_samples(cvector<sample_t> & samples)1806     status_t room_builder_base::commit_samples(cvector<sample_t> &samples)
1807     {
1808         // Put each sample to KVT and toggle the reload flag
1809         kvt_param_t p;
1810         char path[0x40];
1811 
1812         for (size_t i=0, n=samples.size(); i<n; ++i)
1813         {
1814             sample_t *s     = samples.at(i);
1815             if (s == NULL)
1816                 continue;
1817 
1818             // Create sample data
1819             size_t slen         = s->sSample.length();
1820             size_t payload      = sizeof(sample_header_t) + slen * s->sSample.channels() * sizeof(float);
1821             sample_header_t *hdr = reinterpret_cast<sample_header_t *>(::malloc(payload));
1822             if (hdr == NULL)
1823                 return STATUS_NO_MEM;
1824             hdr->version        = __IF_LEBE(0, 1);
1825             hdr->channels       = s->sSample.channels();
1826             hdr->sample_rate    = fSampleRate;
1827             hdr->samples        = s->sSample.length();
1828 
1829             hdr->version        = CPU_TO_BE(hdr->version);
1830             hdr->channels       = CPU_TO_BE(hdr->channels);
1831             hdr->sample_rate    = CPU_TO_BE(hdr->sample_rate);
1832             hdr->samples        = CPU_TO_BE(hdr->samples);
1833 
1834             float *fdst         = reinterpret_cast<float *>(&hdr[1]);
1835             for (size_t i=0; i<s->sSample.channels(); ++i, fdst += slen)
1836                 ::memcpy(fdst, s->sSample.getBuffer(i), slen * sizeof(float));
1837 
1838             // Post-process Mid/Side audio data
1839             if (s->enConfig == RT_CC_MS)
1840             {
1841                 float *l            = reinterpret_cast<float *>(&hdr[1]);
1842                 float *r            = &l[slen];
1843                 dsp::ms_to_lr(l, r, l, r, slen);
1844             }
1845 
1846             // Create KVT parameter
1847             p.type          = KVT_BLOB;
1848             p.blob.ctype    = ::strdup(AUDIO_SAMPLE_CONTENT_TYPE);
1849             if (p.blob.ctype == NULL)
1850             {
1851                 ::free(hdr);
1852                 return STATUS_NO_MEM;
1853             }
1854             p.blob.size     = payload;
1855             p.blob.data     = hdr;
1856 
1857             // Deploy KVT parameter
1858             sprintf(path, "/samples/%d", int(s->nID));
1859             KVTStorage *kvt = kvt_lock();
1860             if (kvt != NULL)
1861             {
1862                 kvt->put(path, &p, KVT_PRIVATE | KVT_DELEGATE); // Delegate memory management to KVT Storage
1863                 kvt->gc();
1864                 kvt_release();
1865             }
1866             else
1867                 return STATUS_BAD_STATE;
1868 
1869             // Update the number of changes for the sample and toggle configurator launch
1870             atomic_add(&vCaptures[s->nID].nChangeReq, 1);
1871             sConfigurator.queue_launch();
1872         }
1873 
1874         return STATUS_OK;
1875     }
1876 
reconfigure(const reconfig_t * cfg)1877     status_t room_builder_base::reconfigure(const reconfig_t *cfg)
1878     {
1879         status_t res;
1880 
1881         // Collect the garbage
1882         for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
1883         {
1884             convolver_t *c  = &vConvolvers[i];
1885             if (c->pSwap != NULL)
1886             {
1887                 c->pSwap->destroy();
1888                 delete c->pSwap;
1889                 c->pSwap    = NULL;
1890             }
1891         }
1892 
1893         for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
1894         {
1895             capture_t *c  = &vCaptures[i];
1896             if (c->pSwap != NULL)
1897             {
1898                 c->pSwap->destroy();
1899                 delete c->pSwap;
1900                 c->pSwap    = NULL;
1901             }
1902         }
1903 
1904         // Re-render samples
1905         for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
1906         {
1907             capture_t *c    = &vCaptures[i];
1908 
1909             // Do we need to change the sample?
1910             if (!cfg->bReconfigure[i])
1911                 continue;
1912 
1913             // Update status and commit request
1914             c->nStatus      = STATUS_OK;
1915             c->nChangeResp  = cfg->nChangeResp[i];
1916             c->bCommit      = true;
1917 
1918             // Lock KVT and fetch sample data
1919             KVTStorage *kvt = kvt_lock();
1920             if (kvt == NULL)
1921             {
1922                 c->nStatus      = STATUS_BAD_STATE;
1923                 continue;
1924             }
1925 
1926             // Fetch KVT sample
1927             sample_header_t hdr;
1928             const float *samples;
1929             res = fetch_kvt_sample(kvt, i, &hdr, &samples);
1930             if (res != STATUS_OK)
1931             {
1932                 c->nStatus      = res;
1933                 kvt_release();
1934                 continue;
1935             }
1936 
1937             // Allocate new sample
1938             Sample *s           = new Sample();
1939             if (s == NULL)
1940             {
1941                 kvt_release();
1942                 c->nStatus      = STATUS_NO_MEM;
1943                 continue;
1944             }
1945             c->nLength          = hdr.samples;
1946             c->fMaxLen          = samples_to_millis(hdr.sample_rate, hdr.samples);
1947             c->pSwap            = s;
1948             lsp_trace("Allocated sample=%p, original length=%d samples", s, int(c->nLength));
1949 
1950             // Initialize sample
1951             if (!s->init(hdr.channels, hdr.samples, hdr.samples))
1952             {
1953                 kvt_release();
1954                 c->nStatus      = STATUS_NO_MEM;
1955                 continue;
1956             }
1957 
1958             // Sample is present, check boundaries
1959             size_t head_cut     = millis_to_samples(fSampleRate, c->fHeadCut);
1960             size_t tail_cut     = millis_to_samples(fSampleRate, c->fTailCut);
1961             ssize_t fsamples    = hdr.samples - head_cut - tail_cut;
1962             if (fsamples <= 0)
1963             {
1964                 s->setLength(0);
1965                 c->fCurrLen         = 0.0f;
1966                 kvt_release();
1967 
1968                 for (size_t j=0; j<hdr.channels; ++j)
1969                     dsp::fill_zero(c->vThumbs[j], room_builder_base_metadata::MESH_SIZE);
1970                 continue;
1971             }
1972             c->fCurrLen         = samples_to_millis(hdr.sample_rate, fsamples);
1973 
1974             // Render the sample
1975             float norm          = 0.0f;
1976             for (size_t j=0; j<hdr.channels; ++j)
1977             {
1978                 const float *src    = &samples[j * hdr.samples];
1979                 float *dst          = s->getBuffer(j);
1980 
1981                 // Get normalizing factor
1982                 float xnorm         = dsp::abs_max(src, hdr.samples);
1983                 if (xnorm > norm)
1984                     norm            = xnorm;
1985 
1986                 // Copy sample data and apply fading
1987                 if (c->bReverse)
1988                     dsp::reverse2(dst, &src[tail_cut], fsamples);
1989                 else
1990                     dsp::copy(dst, &src[head_cut], fsamples);
1991                 if ((hdr.version & 1) != __IF_LEBE(0, 1)) // Endianess does not match?
1992                     byte_swap(dst, fsamples);
1993 
1994                 fade_in(dst, dst, millis_to_samples(fSampleRate, c->fFadeIn), fsamples);
1995                 fade_out(dst, dst, millis_to_samples(fSampleRate, c->fFadeOut), fsamples);
1996 
1997                 // Now render thumbnail
1998                 src                 = dst;
1999                 dst                 = c->vThumbs[j];
2000                 for (size_t k=0; k<room_builder_base_metadata::MESH_SIZE; ++k)
2001                 {
2002                     size_t first    = (k * fsamples) / room_builder_base_metadata::MESH_SIZE;
2003                     size_t last     = ((k + 1) * fsamples) / room_builder_base_metadata::MESH_SIZE;
2004                     if (first < last)
2005                         dst[k]          = dsp::abs_max(&src[first], last - first);
2006                     else
2007                         dst[k]          = fabs(src[first]);
2008                 }
2009             }
2010 
2011             // Normalize graph if possible
2012             if (norm != 0.0f)
2013             {
2014                 norm    = 1.0f / norm;
2015                 for (size_t j=0; j<hdr.channels; ++j)
2016                     dsp::mul_k2(c->vThumbs[j], norm, room_builder_base_metadata::MESH_SIZE);
2017             }
2018 
2019             // Release KVT storage
2020             kvt_release();
2021         }
2022 
2023         // Randomize phase of the convolver
2024         uint32_t phase  = seed_addr(this);
2025         phase           = ((phase << 16) | (phase >> 16)) & 0x7fffffff;
2026         uint32_t step   = 0x80000000 / (room_builder_base_metadata::CONVOLVERS + 1);
2027 
2028         // Reconfigure convolvers
2029         for (size_t i=0; i<room_builder_base_metadata::CONVOLVERS; ++i)
2030         {
2031             convolver_t *c  = &vConvolvers[i];
2032 
2033             // Check that routing has changed
2034             size_t capture  = cfg->nSampleID[i];
2035             size_t track    = cfg->nTrack[i];
2036             if ((capture <= 0) || (capture > room_builder_base_metadata::CAPTURES))
2037                 continue;
2038             else
2039                 --capture;
2040 
2041             // Analyze sample
2042             Sample *s       = (vCaptures[capture].bCommit) ? vCaptures[capture].pSwap: vCaptures[capture].pCurr;
2043             if ((s == NULL) || (!s->valid()) || (s->channels() <= track))
2044                 continue;
2045 
2046             // Now we can create convolver
2047             Convolver *cv   = new Convolver();
2048             if (!cv->init(s->getBuffer(track), s->length(), cfg->nRank[i], float((phase + i*step)& 0x7fffffff)/float(0x80000000)))
2049             {
2050                 cv->destroy();
2051                 delete cv;
2052                 return STATUS_NO_MEM;
2053             }
2054 
2055             lsp_trace("Allocated convolver pSwap=%p for channel %d (pCurr=%p)", cv, int(i), c->pCurr);
2056             c->pSwap        = cv;
2057         }
2058 
2059         return STATUS_OK;
2060     }
2061 
fetch_kvt_sample(KVTStorage * kvt,size_t sample_id,sample_header_t * hdr,const float ** samples)2062     status_t room_builder_base::fetch_kvt_sample(KVTStorage *kvt, size_t sample_id, sample_header_t *hdr, const float **samples)
2063     {
2064         status_t res;
2065         const kvt_param_t *p;
2066         const sample_header_t *phdr;
2067         char path[0x40];
2068 
2069         sprintf(path, "/samples/%d", int(sample_id));
2070 
2071         // Fetch parameter
2072         res = kvt->get(path, &p, KVT_BLOB);
2073         if ((res != STATUS_OK) ||
2074             (p == NULL))
2075             return STATUS_NO_DATA;
2076 
2077         // Validate blob settings
2078         if ((p->blob.ctype == NULL) ||
2079             (p->blob.data == NULL) ||
2080             (p->blob.size < sizeof(sample_header_t)) ||
2081             (::strcmp(p->blob.ctype, AUDIO_SAMPLE_CONTENT_TYPE) != 0))
2082             return STATUS_CORRUPTED;
2083 
2084         // Decode sample data
2085         phdr                = reinterpret_cast<const sample_header_t *>(p->blob.data);
2086         hdr->version        = BE_TO_CPU(phdr->version);
2087         hdr->channels       = BE_TO_CPU(phdr->channels);
2088         hdr->sample_rate    = BE_TO_CPU(phdr->sample_rate);
2089         hdr->samples        = BE_TO_CPU(phdr->samples);
2090 
2091         if (((hdr->version >> 1) != 0) ||
2092             ((hdr->samples * hdr->channels * sizeof(float) + sizeof(sample_header_t)) != p->blob.size))
2093             return STATUS_CORRUPTED;
2094 
2095         *samples            = reinterpret_cast<const float *>(&phdr[1]);
2096         return STATUS_OK;
2097     }
2098 
save_sample(const char * path,size_t sample_id)2099     status_t room_builder_base::save_sample(const char *path, size_t sample_id)
2100     {
2101         if (::strlen(path) <= 0)
2102             return STATUS_BAD_PATH;
2103 
2104         LSPString sp, lspc;
2105         if ((!sp.set_utf8(path)) || (!lspc.set_ascii(".lspc")))
2106             return STATUS_NO_MEM;
2107 
2108         // Lock KVT storage
2109         KVTStorage *kvt = kvt_lock();
2110         if (kvt == NULL)
2111             return STATUS_BAD_STATE;
2112 
2113         sample_header_t hdr;
2114         const float *samples;
2115         status_t res    = fetch_kvt_sample(kvt, sample_id, &hdr, &samples);
2116 
2117         // Check the extension of file
2118         if (sp.ends_with_nocase(&lspc))
2119         {
2120             lspc_audio_parameters_t params;
2121             params.channels         = hdr.channels;
2122             params.sample_format    = (hdr.version & 1) ? LSPC_SAMPLE_FMT_F32BE : LSPC_SAMPLE_FMT_F32LE;
2123             params.sample_rate      = hdr.sample_rate;
2124             params.codec            = LSPC_CODEC_PCM;
2125             params.frames           = hdr.samples;
2126 
2127             // Initialize sample array
2128             const float **vs        = reinterpret_cast<const float **>(::malloc(params.channels * sizeof(float *)));
2129             if (vs == NULL)
2130             {
2131                 kvt_release();
2132                 return STATUS_NO_MEM;
2133             }
2134             for (size_t i=0; i<params.channels; ++i)
2135                 vs[i]               = &samples[i * params.frames];
2136 
2137             // Create LSPC writer
2138             LSPCAudioWriter wr;
2139             res = wr.create(&sp, &params);
2140             if (res != STATUS_OK)
2141             {
2142                 ::free(vs);
2143                 kvt_release();
2144                 return res;
2145             }
2146 
2147             // Write all samples to the file
2148             res = wr.write_samples(vs, params.frames);
2149             status_t res2 = wr.close();
2150             if (res == STATUS_OK)
2151                 res     = res2;
2152             ::free(vs);
2153         }
2154         else
2155         {
2156             AudioFile af;
2157             res     = af.create_samples(hdr.channels, hdr.sample_rate, hdr.samples);
2158             if (res != STATUS_OK)
2159             {
2160                 kvt_release();
2161                 return res;
2162             }
2163 
2164             // Prepare file contents
2165             for (size_t i=0; i<hdr.channels; ++i)
2166             {
2167                 float *dst = af.channel(i);
2168                 dsp::copy(dst, &samples[i * hdr.samples], hdr.samples);
2169                 if ((hdr.version & 1) != __IF_LEBE(0, 1))
2170                     byte_swap(dst, hdr.samples);
2171             }
2172 
2173             // Store file contents
2174             res     = af.store(&sp);
2175             af.destroy();
2176         }
2177 
2178         // Release KVT storage and return result
2179         kvt_release();
2180         return res;
2181     }
2182 
state_loaded()2183     void room_builder_base::state_loaded()
2184     {
2185         // We need to sync all loaded samples in KVT with internal state
2186         for (size_t i=0; i<room_builder_base_metadata::CAPTURES; ++i)
2187         {
2188             capture_t *cap      = &vCaptures[i];
2189             atomic_add(&cap->nChangeReq, 1);
2190             sConfigurator.queue_launch();
2191         }
2192     }
2193 
ui_activated()2194     void room_builder_base::ui_activated()
2195     {
2196         // Synchronize thumbnails with UI
2197         for (size_t i=0; i<impulse_reverb_base_metadata::FILES; ++i)
2198             vCaptures[i].bSync  = true;
2199     }
2200 
2201     //-------------------------------------------------------------------------
room_builder_mono()2202     room_builder_mono::room_builder_mono(): room_builder_base(metadata, 1)
2203     {
2204     }
2205 
~room_builder_mono()2206     room_builder_mono::~room_builder_mono()
2207     {
2208     }
2209 
room_builder_stereo()2210     room_builder_stereo::room_builder_stereo(): room_builder_base(metadata, 2)
2211     {
2212     }
2213 
~room_builder_stereo()2214     room_builder_stereo::~room_builder_stereo()
2215     {
2216     }
2217 }
2218 
2219