qt: playlist: use item title if available
[vlc.git] / modules / audio_filter / channel_mixer / spatialaudio.cpp
1 /*****************************************************************************
2  * spatialaudio.cpp : Ambisonics audio renderer and binauralizer filter
3  *****************************************************************************
4  * Copyright © 2017 VLC authors and VideoLAN
5  *
6  * Authors: Adrien Maglo <magsoft@videolan.org>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program 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 this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <assert.h>
32
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_aout.h>
36 #include <vlc_filter.h>
37 #include <vlc_viewpoint.h>
38
39 #include <new>
40 #include <vector>
41 #include <sstream>
42
43 #include <spatialaudio/Ambisonics.h>
44 #include <spatialaudio/SpeakersBinauralizer.h>
45
46 #define CFG_PREFIX "spatialaudio-"
47
48 #define DEFAULT_HRTF_PATH "hrtfs" DIR_SEP "dodeca_and_7channel_3DSL_HRTF.sofa"
49
50 #define HRTF_FILE_TEXT N_("HRTF file for the binauralization")
51 #define HRTF_FILE_LONGTEXT N_("Custom HRTF (Head-related transfer function) file " \
52                               "in the SOFA format.")
53
54 #define HEADPHONES_TEXT N_("Headphones mode (binaural)")
55 #define HEADPHONES_LONGTEXT N_("If the output is stereo, render ambisonics " \
56                                "with the binaural decoder.")
57
58 static int OpenBinauralizer(vlc_object_t *p_this);
59 static int Open( vlc_object_t * );
60 static void Close( filter_t * );
61 static void Flush( filter_t * );
62
63 vlc_module_begin()
64     set_shortname("Spatialaudio")
65     set_description(N_("Ambisonics renderer and binauralizer"))
66     set_capability("audio renderer", 1)
67     set_category(CAT_AUDIO)
68     set_subcategory(SUBCAT_AUDIO_AFILTER)
69     set_callback(Open)
70     add_bool(CFG_PREFIX "headphones", false,
71              HEADPHONES_TEXT, HEADPHONES_LONGTEXT, true)
72     add_loadfile("hrtf-file", NULL, HRTF_FILE_TEXT, HRTF_FILE_LONGTEXT)
73     add_shortcut("ambisonics")
74
75     add_submodule()
76     set_shortname(N_("Binauralizer"))
77     set_capability("audio filter", 0)
78     set_callback(OpenBinauralizer)
79     add_shortcut("binauralizer")
80 vlc_module_end()
81
82 #define AMB_BLOCK_TIME_LEN 1024
83
84 #define AMB_MAX_ORDER 3
85
86 struct filter_spatialaudio
87 {
88     filter_spatialaudio()
89         : speakers(NULL)
90         , i_inputPTS(0)
91         , inBuf(NULL)
92         , outBuf(NULL)
93     {}
94     ~filter_spatialaudio()
95     {
96         delete[] speakers;
97         if (inBuf != NULL)
98             for (unsigned i = 0; i < i_inputNb; ++i)
99                 free(inBuf[i]);
100         free(inBuf);
101
102         if (outBuf != NULL)
103             for (unsigned i = 0; i < i_outputNb; ++i)
104                 free(outBuf[i]);
105         free(outBuf);
106     }
107
108     enum
109     {
110         AMBISONICS_DECODER, // Ambisonics decoding module
111         AMBISONICS_BINAURAL_DECODER, // Ambisonics decoding module using binaural
112         BINAURALIZER // Binauralizer module
113     } mode;
114
115     CAmbisonicBinauralizer binauralDecoder;
116     SpeakersBinauralizer binauralizer;
117     CAmbisonicDecoder speakerDecoder;
118     CAmbisonicProcessor processor;
119     CAmbisonicZoomer zoomer;
120
121     CAmbisonicSpeaker *speakers;
122
123     std::vector<float> inputSamples;
124     vlc_tick_t i_inputPTS;
125     unsigned i_order;
126     unsigned i_nondiegetic;
127     unsigned i_lr_channels; // number of physical left/right channel pairs
128
129     float** inBuf;
130     float** outBuf;
131     unsigned i_inputNb;
132     unsigned i_outputNb;
133
134     /* View point. */
135     float f_teta;
136     float f_phi;
137     float f_roll;
138     float f_zoom;
139 };
140
141 static std::string getHRTFPath(filter_t *p_filter)
142 {
143     std::string HRTFPath;
144
145     char *userHRTFPath = var_InheritString(p_filter, "hrtf-file");
146
147     if (userHRTFPath != NULL)
148     {
149         HRTFPath = std::string(userHRTFPath);
150         free(userHRTFPath);
151     }
152     else
153     {
154         char *dataDir = config_GetSysPath(VLC_PKG_DATA_DIR, DEFAULT_HRTF_PATH);
155         if (dataDir != NULL)
156         {
157             HRTFPath = std::string(dataDir);
158             free(dataDir);
159         }
160     }
161
162     return HRTFPath;
163 }
164
165 static block_t *Mix( filter_t *p_filter, block_t *p_buf )
166 {
167     filter_spatialaudio *p_sys = reinterpret_cast<filter_spatialaudio *>(p_filter->p_sys);
168
169     const size_t i_prevSize = p_sys->inputSamples.size();
170     p_sys->inputSamples.resize(i_prevSize + p_buf->i_nb_samples * p_sys->i_inputNb);
171     memcpy((char*)(p_sys->inputSamples.data() + i_prevSize), (char*)p_buf->p_buffer, p_buf->i_buffer);
172
173     const size_t i_inputBlockSize = sizeof(float) * p_sys->i_inputNb * AMB_BLOCK_TIME_LEN;
174     const size_t i_outputBlockSize = sizeof(float) * p_sys->i_outputNb * AMB_BLOCK_TIME_LEN;
175     const size_t i_nbBlocks = p_sys->inputSamples.size() * sizeof(float) / i_inputBlockSize;
176
177     block_t *p_out_buf = block_Alloc(i_outputBlockSize * i_nbBlocks);
178     if (unlikely(p_out_buf == NULL))
179     {
180         block_Release(p_buf);
181         return NULL;
182     }
183
184     p_out_buf->i_nb_samples = i_nbBlocks * AMB_BLOCK_TIME_LEN;
185     if (p_sys->i_inputPTS == 0)
186         p_out_buf->i_pts = p_buf->i_pts;
187     else
188         p_out_buf->i_pts = p_sys->i_inputPTS;
189     p_out_buf->i_dts = p_out_buf->i_pts;
190     p_out_buf->i_length = vlc_tick_from_samples(p_out_buf->i_nb_samples, p_filter->fmt_in.audio.i_rate);
191
192     float *p_dest = (float *)p_out_buf->p_buffer;
193     const float *p_src = (float *)p_sys->inputSamples.data();
194
195     for (unsigned b = 0; b < i_nbBlocks; ++b)
196     {
197         for (unsigned i = 0; i < p_sys->i_inputNb; ++i)
198         {
199             for (unsigned j = 0; j < AMB_BLOCK_TIME_LEN; ++j)
200             {
201                 float val = p_src[(b * AMB_BLOCK_TIME_LEN + j) * p_sys->i_inputNb + i];
202                 p_sys->inBuf[i][j] = val;
203             }
204         }
205
206         // Compute
207         switch (p_sys->mode)
208         {
209             case filter_spatialaudio::BINAURALIZER:
210                 p_sys->binauralizer.Process(p_sys->inBuf, p_sys->outBuf);
211                 break;
212             case filter_spatialaudio::AMBISONICS_DECODER:
213             case filter_spatialaudio::AMBISONICS_BINAURAL_DECODER:
214             {
215                 CBFormat inData;
216                 inData.Configure(p_sys->i_order, true, AMB_BLOCK_TIME_LEN);
217
218                 for (unsigned i = 0; i < p_sys->i_inputNb - p_sys->i_nondiegetic; ++i)
219                     inData.InsertStream(p_sys->inBuf[i], i, AMB_BLOCK_TIME_LEN);
220
221                 Orientation ori(p_sys->f_teta, p_sys->f_phi, p_sys->f_roll);
222                 p_sys->processor.SetOrientation(ori);
223                 p_sys->processor.Refresh();
224                 p_sys->processor.Process(&inData, inData.GetSampleCount());
225
226                 p_sys->zoomer.SetZoom(p_sys->f_zoom);
227                 p_sys->zoomer.Refresh();
228                 p_sys->zoomer.Process(&inData, inData.GetSampleCount());
229
230                 if (p_sys->mode == filter_spatialaudio::AMBISONICS_DECODER)
231                     p_sys->speakerDecoder.Process(&inData, inData.GetSampleCount(), p_sys->outBuf);
232                 else
233                     p_sys->binauralDecoder.Process(&inData, p_sys->outBuf);
234                 break;
235             }
236             default:
237                 vlc_assert_unreachable();
238         }
239
240         // Interleave the results.
241         for (unsigned i = 0; i < p_sys->i_outputNb; ++i)
242             for (unsigned j = 0; j < AMB_BLOCK_TIME_LEN; ++j)
243                 p_dest[(b * AMB_BLOCK_TIME_LEN + j) * p_sys->i_outputNb + i] = p_sys->outBuf[i][j];
244
245         if (p_sys->i_nondiegetic == 2)
246         {
247             for (unsigned i = 0; i < p_sys->i_lr_channels * 2; i += 2)
248                 for (unsigned j = 0; j < AMB_BLOCK_TIME_LEN; ++j)
249                 {
250                     p_dest[(b * AMB_BLOCK_TIME_LEN + j) * p_sys->i_outputNb + i] =
251                             p_dest[(b * AMB_BLOCK_TIME_LEN + j) * p_sys->i_outputNb + i]  / 2.f
252                             + p_sys->inBuf[p_sys->i_inputNb - 2][j] / 2.f; //left
253                     p_dest[(b * AMB_BLOCK_TIME_LEN + j) * p_sys->i_outputNb + i + 1] =
254                             p_dest[(b * AMB_BLOCK_TIME_LEN + j) * p_sys->i_outputNb + i + 1]  / 2.f
255                             + p_sys->inBuf[p_sys->i_inputNb - 1][j] / 2.f; //right
256                 }
257         }
258     }
259
260     p_sys->inputSamples.erase(p_sys->inputSamples.begin(),
261                               p_sys->inputSamples.begin() + i_inputBlockSize * i_nbBlocks / sizeof(float));
262
263     assert(p_sys->inputSamples.size() < i_inputBlockSize);
264
265     p_sys->i_inputPTS = p_out_buf->i_pts + p_out_buf->i_length;
266
267     block_Release(p_buf);
268     return p_out_buf;
269 }
270
271 static void Flush( filter_t *p_filter )
272 {
273     filter_spatialaudio *p_sys = reinterpret_cast<filter_spatialaudio *>(p_filter->p_sys);
274     p_sys->inputSamples.clear();
275     p_sys->i_inputPTS = 0;
276 }
277
278 static void ChangeViewpoint( filter_t *p_filter, const vlc_viewpoint_t *p_vp)
279 {
280     filter_spatialaudio *p_sys = reinterpret_cast<filter_spatialaudio *>(p_filter->p_sys);
281
282 #define RAD(d) ((float) ((d) * M_PI / 180.f))
283     p_sys->f_teta = -RAD(p_vp->yaw);
284     p_sys->f_phi = RAD(p_vp->pitch);
285     p_sys->f_roll = RAD(p_vp->roll);
286
287     if (p_vp->fov >= FIELD_OF_VIEW_DEGREES_DEFAULT)
288         p_sys->f_zoom = 0.f; // no unzoom as it does not really make sense.
289     else
290         p_sys->f_zoom = (FIELD_OF_VIEW_DEGREES_DEFAULT - p_vp->fov) / (FIELD_OF_VIEW_DEGREES_DEFAULT - FIELD_OF_VIEW_DEGREES_MIN);
291 #undef RAD
292 }
293
294 static int allocateBuffers(filter_spatialaudio *p_sys)
295 {
296     p_sys->inBuf = (float**)calloc(p_sys->i_inputNb, sizeof(float*));
297     if (p_sys->inBuf == NULL)
298         return VLC_ENOMEM;
299
300     for (unsigned i = 0; i < p_sys->i_inputNb; ++i)
301     {
302         p_sys->inBuf[i] = (float *)vlc_alloc(AMB_BLOCK_TIME_LEN, sizeof(float));
303         if (p_sys->inBuf[i] == NULL)
304             return VLC_ENOMEM;
305     }
306
307     p_sys->outBuf = (float**)calloc(p_sys->i_outputNb, sizeof(float*));
308     if (p_sys->outBuf == NULL)
309         return VLC_ENOMEM;
310
311     for (unsigned i = 0; i < p_sys->i_outputNb; ++i)
312     {
313         p_sys->outBuf[i] = (float *)vlc_alloc(AMB_BLOCK_TIME_LEN, sizeof(float));
314         if (p_sys->outBuf[i] == NULL)
315             return VLC_ENOMEM;
316     }
317
318     return VLC_SUCCESS;
319 }
320
321 static const struct FilterOperationInitializer {
322     struct vlc_filter_operations ops {};
323     FilterOperationInitializer()
324     {
325         ops.filter_audio = Mix;
326         ops.flush = Flush;
327         ops.change_viewpoint = ChangeViewpoint;
328         ops.close = Close;
329     };
330 } filter_ops;
331
332 static int OpenBinauralizer(vlc_object_t *p_this)
333 {
334     filter_t *p_filter = (filter_t *)p_this;
335     audio_format_t *infmt = &p_filter->fmt_in.audio;
336     audio_format_t *outfmt = &p_filter->fmt_out.audio;
337
338     filter_spatialaudio *p_sys = new(std::nothrow)filter_spatialaudio();
339     if (p_sys == NULL)
340         return VLC_ENOMEM;
341
342     p_sys->mode = filter_spatialaudio::BINAURALIZER;
343     p_sys->i_inputNb = p_filter->fmt_in.audio.i_channels;
344     p_sys->i_outputNb = 2;
345     p_sys->i_lr_channels = 1;
346
347     if (allocateBuffers(p_sys) != VLC_SUCCESS)
348     {
349         delete p_sys;
350         return VLC_ENOMEM;
351     }
352
353     unsigned s = 0;
354     p_sys->speakers = new(std::nothrow)CAmbisonicSpeaker[infmt->i_channels]();
355     if (!p_sys->speakers)
356     {
357         delete p_sys;
358         return VLC_ENOMEM;
359     }
360
361     p_sys->speakers[s++].SetPosition({DegreesToRadians(30), 0.f, 1.f});
362     p_sys->speakers[s++].SetPosition({DegreesToRadians(-30), 0.f, 1.f});
363
364     if ((infmt->i_physical_channels & AOUT_CHANS_MIDDLE) == AOUT_CHANS_MIDDLE)
365     {
366         /* Middle */
367         p_sys->speakers[s++].SetPosition({DegreesToRadians(110), 0.f, 1.f});
368         p_sys->speakers[s++].SetPosition({DegreesToRadians(-110), 0.f, 1.f});
369     }
370
371     if ((infmt->i_physical_channels & AOUT_CHANS_REAR) == AOUT_CHANS_REAR)
372     {
373         /* Rear */
374         p_sys->speakers[s++].SetPosition({DegreesToRadians(145), 0.f, 1.f});
375         p_sys->speakers[s++].SetPosition({DegreesToRadians(-145), 0.f, 1.f});
376     }
377
378     if ((infmt->i_physical_channels & AOUT_CHAN_CENTER) == AOUT_CHAN_CENTER)
379         p_sys->speakers[s++].SetPosition({DegreesToRadians(0), 0.f, 1.f});
380
381     if ((infmt->i_physical_channels & AOUT_CHAN_LFE) == AOUT_CHAN_LFE)
382         p_sys->speakers[s++].SetPosition({DegreesToRadians(0), 0.f, 0.5f});
383
384     std::string HRTFPath = getHRTFPath(p_filter);
385     msg_Dbg(p_filter, "Using the HRTF file: %s", HRTFPath.c_str());
386
387     unsigned i_tailLength = 0;
388     if (!p_sys->binauralizer.Configure(p_filter->fmt_in.audio.i_rate, AMB_BLOCK_TIME_LEN,
389                                        p_sys->speakers, infmt->i_channels, i_tailLength,
390                                        HRTFPath))
391     {
392         msg_Err(p_filter, "Error creating the binauralizer.");
393         delete p_sys;
394         return VLC_EGENERIC;
395     }
396     p_sys->binauralizer.Reset();
397
398     outfmt->i_format = infmt->i_format = VLC_CODEC_FL32;
399     outfmt->i_rate = infmt->i_rate;
400     outfmt->i_physical_channels = AOUT_CHANS_STEREO;
401     aout_FormatPrepare(infmt);
402     aout_FormatPrepare(outfmt);
403
404     p_filter->p_sys = p_sys;
405     p_filter->ops = &filter_ops.ops;
406
407     return VLC_SUCCESS;
408 }
409
410 static int Open(vlc_object_t *p_this)
411 {
412     filter_t *p_filter = (filter_t *)p_this;
413     audio_format_t *infmt = &p_filter->fmt_in.audio;
414     audio_format_t *outfmt = &p_filter->fmt_out.audio;
415
416     assert(infmt->channel_type != outfmt->channel_type);
417
418     if (infmt->channel_type != AUDIO_CHANNEL_TYPE_AMBISONICS)
419         return VLC_EGENERIC;
420
421     if (infmt->i_format != VLC_CODEC_FL32 || outfmt->i_format != VLC_CODEC_FL32)
422         return VLC_EGENERIC;
423
424     //support order 1 to 3
425     if ( infmt->i_channels < 4 || infmt->i_channels > ( (AMB_MAX_ORDER + 1) * (AMB_MAX_ORDER + 1) + 2 ) )
426     {
427         msg_Err(p_filter, "Unsupported number of Ambisonics channels");
428         return VLC_EGENERIC;
429     }
430
431     filter_spatialaudio *p_sys = new(std::nothrow)filter_spatialaudio();
432     if (p_sys == NULL)
433         return VLC_ENOMEM;
434
435     p_sys->f_teta = 0.f;
436     p_sys->f_phi = 0.f;
437     p_sys->f_roll = 0.f;
438     p_sys->f_zoom = 0.f;
439     p_sys->i_inputNb = p_filter->fmt_in.audio.i_channels;
440     p_sys->i_outputNb = p_filter->fmt_out.audio.i_channels;
441
442     if (allocateBuffers(p_sys) != VLC_SUCCESS)
443     {
444         delete p_sys;
445         return VLC_ENOMEM;
446     }
447
448     int i_sqrt_channels = 1;
449     while( ( i_sqrt_channels < ( AMB_MAX_ORDER + 2 ) )
450            && ( i_sqrt_channels * i_sqrt_channels <= infmt->i_channels ) )
451         i_sqrt_channels++;
452     i_sqrt_channels--;
453
454     p_sys->i_order = i_sqrt_channels - 1;
455     p_sys->i_nondiegetic = infmt->i_channels - i_sqrt_channels * i_sqrt_channels;
456
457     if ( p_sys->i_nondiegetic != 0 && p_sys->i_nondiegetic != 2 )
458     {
459         msg_Err(p_filter, "Invalid number of non-diegetic Ambisonics channels %i", p_sys->i_nondiegetic);
460         delete p_sys;
461         return VLC_EGENERIC;
462     }
463
464     msg_Dbg(p_filter, "Order: %d %d %d", p_sys->i_order, p_sys->i_nondiegetic, infmt->i_channels);
465
466     static const char *const options[] = { "headphones", NULL };
467     config_ChainParse(p_filter, CFG_PREFIX, options, p_filter->p_cfg);
468
469     unsigned i_tailLength = 0;
470     if (p_filter->fmt_out.audio.i_channels == 2
471      && var_InheritBool(p_filter, CFG_PREFIX "headphones"))
472     {
473         p_sys->mode = filter_spatialaudio::AMBISONICS_BINAURAL_DECODER;
474
475         std::string HRTFPath = getHRTFPath(p_filter);
476         msg_Dbg(p_filter, "Using the HRTF file: %s", HRTFPath.c_str());
477
478         if (!p_sys->binauralDecoder.Configure(p_sys->i_order, true,
479                 p_filter->fmt_in.audio.i_rate, AMB_BLOCK_TIME_LEN, i_tailLength,
480                 HRTFPath))
481         {
482             msg_Err(p_filter, "Error creating the binaural decoder.");
483             delete p_sys;
484             return VLC_EGENERIC;
485         }
486         p_sys->binauralDecoder.Reset();
487     }
488     else
489     {
490         p_sys->mode = filter_spatialaudio::AMBISONICS_DECODER;
491
492         unsigned i_nbChannels = aout_FormatNbChannels(&p_filter->fmt_out.audio);
493         if (i_nbChannels == 1
494          || !p_sys->speakerDecoder.Configure(p_sys->i_order, true,
495                                              kAmblib_CustomSpeakerSetUp,
496                                              i_nbChannels))
497         {
498             msg_Err(p_filter, "Error creating the Ambisonics decoder.");
499             delete p_sys;
500             return VLC_EGENERIC;
501         }
502
503         /* Speaker setup, inspired from:
504          * https://www.dolby.com/us/en/guide/surround-sound-speaker-setup/7-1-setup.html
505          * The position must follow the order of pi_vlc_chan_order_wg4 */
506         unsigned s = 0;
507
508         p_sys->speakerDecoder.SetPosition(s++, {DegreesToRadians(30), 0.f, 1.f});
509         p_sys->speakerDecoder.SetPosition(s++, {DegreesToRadians(-30), 0.f, 1.f});
510         p_sys->i_lr_channels = 1;
511
512         if ((outfmt->i_physical_channels & AOUT_CHANS_MIDDLE) == AOUT_CHANS_MIDDLE)
513         {
514             p_sys->speakerDecoder.SetPosition(s++, {DegreesToRadians(110), 0.f, 1.f});
515             p_sys->speakerDecoder.SetPosition(s++, {DegreesToRadians(-110), 0.f, 1.f});
516             p_sys->i_lr_channels++;
517         }
518
519         if ((outfmt->i_physical_channels & AOUT_CHANS_REAR) == AOUT_CHANS_REAR)
520         {
521             p_sys->speakerDecoder.SetPosition(s++, {DegreesToRadians(145), 0.f, 1.f});
522             p_sys->speakerDecoder.SetPosition(s++, {DegreesToRadians(-145), 0.f, 1.f});
523             p_sys->i_lr_channels++;
524         }
525
526         if ((outfmt->i_physical_channels & AOUT_CHAN_CENTER) == AOUT_CHAN_CENTER)
527             p_sys->speakerDecoder.SetPosition(s++, {DegreesToRadians(0), 0.f, 1.f});
528
529         if ((outfmt->i_physical_channels & AOUT_CHAN_LFE) == AOUT_CHAN_LFE)
530             p_sys->speakerDecoder.SetPosition(s++, {DegreesToRadians(0), 0.f, 0.5f});
531
532         /* Check we have setup the right number of speaker. */
533         assert(s == i_nbChannels);
534
535         p_sys->speakerDecoder.Refresh();
536     }
537
538     if (!p_sys->processor.Configure(p_sys->i_order, true, AMB_BLOCK_TIME_LEN, 0))
539     {
540         msg_Err(p_filter, "Error creating the ambisonic processor.");
541         delete p_sys;
542         return VLC_EGENERIC;
543     }
544
545     if (!p_sys->zoomer.Configure(p_sys->i_order, true, 0))
546     {
547         msg_Err(p_filter, "Error creating the ambisonic zoomer.");
548         delete p_sys;
549         return VLC_EGENERIC;
550     }
551
552     p_filter->p_sys = p_sys;
553     p_filter->ops = &filter_ops.ops;
554
555     return VLC_SUCCESS;
556 }
557
558 static void Close(filter_t *p_filter)
559 {
560     filter_spatialaudio *p_sys = reinterpret_cast<filter_spatialaudio *>(p_filter->p_sys);
561     delete p_sys;
562 }