1 /*
2 * Copyright (C) 2016 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.numerical.signal.processing;
17
18 /**
19 * Convolves a 1D signal with a 1D kernel.
20 */
21 public class Convolver1D {
22
23 /**
24 * Default constant value to use during convolution if CONSTANT_EDGE method
25 * is being used for signal's edge extension.
26 */
27 public static final double DEFAULT_CONSTANT_VALUE = 0.0;
28
29 /**
30 * Default position of kernel center.
31 */
32 public static final int DEFAULT_KERNEL_CENTER = 0;
33
34 /**
35 * Default method to use for signal's edge extension.
36 */
37 public static final ConvolverEdgeMethod DEFAULT_EDGE_METHOD = ConvolverEdgeMethod.ZERO_EDGE;
38
39 /**
40 * Signal to be convolved.
41 */
42 private double[] signal;
43
44 /**
45 * Kernel to convolve the signal with.
46 */
47 private double[] kernel;
48
49 /**
50 * Position of kernel center. Typical values are the start of kernel
51 * (position 0) or the kernel's center. For the former, the edge extension
52 * is applied only at the end of the resulting signal; for the latter, edge
53 * extension is applied symmetrically at the beginning and end of the
54 * resulting signal.
55 */
56 private int kernelCenter = DEFAULT_KERNEL_CENTER;
57
58 /**
59 * Edge extension method to use during convolution when parts of the kernel
60 * are required to lie outside the signal's boundaries.
61 */
62 private ConvolverEdgeMethod edgeMethod = DEFAULT_EDGE_METHOD;
63
64 /**
65 * Constant value to use during edge extension when CONSTANT_EDGE method is
66 * being used.
67 */
68 private double constantValue = DEFAULT_CONSTANT_VALUE;
69
70 /**
71 * Listener in charge of attending events generated by this instance.
72 */
73 private Convolver1DListener listener;
74
75 /**
76 * Constructor.
77 */
78 public Convolver1D() {
79 }
80
81 /**
82 * Constructor.
83 *
84 * @param signal signal to be convolved.
85 * @param kernel kernel to convolve signal with.
86 */
87 public Convolver1D(final double[] signal, final double[] kernel) {
88 setSignal(signal);
89 setKernel(kernel);
90 }
91
92 /**
93 * Gets signal to be convolved.
94 *
95 * @return signal to be convolved.
96 */
97 public double[] getSignal() {
98 return signal;
99 }
100
101 /**
102 * Sets signal to be convolved.
103 *
104 * @param signal signal to be convolved.
105 */
106 public final void setSignal(final double[] signal) {
107 this.signal = signal;
108 }
109
110 /**
111 * Gets kernel to convolve the signal with.
112 *
113 * @return kernel to convolve the signal with.
114 */
115 public double[] getKernel() {
116 return kernel;
117 }
118
119 /**
120 * Sets kernel to convolve the signal with.
121 *
122 * @param kernel kernel to convolve the signal with.
123 */
124 public final void setKernel(final double[] kernel) {
125 this.kernel = kernel;
126 }
127
128 /**
129 * Gets position of kernel center. Typical values are the start of kernel
130 * (position 0) or the kernel's center. For the former, the edge extension
131 * is applied only at the end of the resulting signal; for the latter, edge
132 * extension is applied symmetrically at the beginning and end of the
133 * resulting signal.
134 *
135 * @return position of kernel center.
136 */
137 public int getKernelCenter() {
138 return kernelCenter;
139 }
140
141 /**
142 * Sets position of kernel center. Typical values are the start of kernel
143 * (position 0) or the kernel's center. For the former, the edge extension
144 * is applied only at the end of the resulting signal; for the latter, edge
145 * extension is applied symmetrically at the beginning and end of the
146 * resulting signal.
147 *
148 * @param kernelCenter position of kernel center.
149 * @throws IllegalArgumentException if provided position is negative.
150 */
151 public void setKernelCenter(final int kernelCenter) {
152 if (kernelCenter < 0) {
153 throw new IllegalArgumentException("kernel center must be zero or greater and within kernel's boundaries");
154 }
155
156 this.kernelCenter = kernelCenter;
157 }
158
159 /**
160 * Gets edge extension method to use during convolution when parts of the
161 * kernel are required to lie outside the signal's boundaries.
162 *
163 * @return edge extension method.
164 */
165 public ConvolverEdgeMethod getEdgeMethod() {
166 return edgeMethod;
167 }
168
169 /**
170 * Sets edge extension method to use during convolution when parts of the
171 * kernel are required to lie outside the signal's boundaries.
172 *
173 * @param edgeMethod edge extension method.
174 */
175 public void setEdgeMethod(final ConvolverEdgeMethod edgeMethod) {
176 this.edgeMethod = edgeMethod;
177 }
178
179 /**
180 * Gets constant value to use during edge extension when CONSTANT_EDGE
181 * method is being used.
182 *
183 * @return constant value to use during edge extension.
184 */
185 public double getConstantValue() {
186 return constantValue;
187 }
188
189 /**
190 * Sets constant value to use during edge extension when CONSTANT_EDGE
191 * method is being used.
192 *
193 * @param constantValue constant value to use during edge extension.
194 */
195 public void setConstantValue(final double constantValue) {
196 this.constantValue = constantValue;
197 }
198
199 /**
200 * Gets listener in charge of attending events generated by this instance.
201 *
202 * @return listener in charge of attending events generated by this
203 * instance.
204 */
205 public Convolver1DListener getListener() {
206 return listener;
207 }
208
209 /**
210 * Sets listener in charge of attending events generated by this instance.
211 *
212 * @param listener listener in charge of attending events generated by this
213 * instance.
214 */
215 public void setListener(final Convolver1DListener listener) {
216 this.listener = listener;
217 }
218
219 /**
220 * Indicates whether this instance is ready to start a convolution.
221 *
222 * @return true if ready, false otherwise.
223 */
224 public boolean isReady() {
225 return signal != null && kernel != null && kernelCenter >= 0 && kernelCenter < kernel.length;
226 }
227
228 /**
229 * Convolves provided signal with provided kernel using provided
230 * configuration for edge extension.
231 *
232 * @return a new array containing result of convolution.
233 */
234 public double[] convolve() {
235 return convolve(signal, kernel, kernelCenter, edgeMethod, constantValue, listener);
236 }
237
238 /**
239 * Convolves provided signal with provided kernel using provided
240 * configuration for edge extension.
241 *
242 * @param result array where result of convolution will be stored. Must
243 * have the sum of the lengths of signal and kernel minus one.
244 * @throws IllegalArgumentException if provided result array does not have
245 * proper length.
246 */
247 public void convolve(final double[] result) {
248 convolve(signal, kernel, kernelCenter, edgeMethod, constantValue, result, listener);
249 }
250
251 /**
252 * Convolves provided signal with provided kernel assuming a zero value
253 * extension method and kernel center located at its origin.
254 *
255 * @param signal signal to be convolved.
256 * @param kernel kernel used for convolution.
257 * @return a new array containing result of convolution.
258 */
259 public static double[] convolve(final double[] signal, final double[] kernel) {
260 return convolve(signal, kernel, DEFAULT_KERNEL_CENTER, (Convolver1DListener) null);
261 }
262
263 /**
264 * Convolves provided signal with provided kernel assuming a zero value
265 * extension method and kernel center located at its origin.
266 *
267 * @param signal signal to be convolved.
268 * @param kernel kernel used for convolution.
269 * @param listener listener attending events generated during convolution.
270 * @return a new array containing result of convolution.
271 */
272 public static double[] convolve(final double[] signal, final double[] kernel, final Convolver1DListener listener) {
273 return convolve(signal, kernel, DEFAULT_KERNEL_CENTER, listener);
274 }
275
276 /**
277 * Convolves provided signal with provided kernel assuming a zero value
278 * extension method and kernel center located at its origin.
279 *
280 * @param signal signal to be convolved.
281 * @param kernel kernel used for convolution.
282 * @param result array where result of convolution will be stored. Must
283 * have the sum of the lengths of signal and kernel minus one.
284 * @throws IllegalArgumentException if provided result array does not
285 * have proper length.
286 */
287 public static void convolve(final double[] signal, final double[] kernel, final double[] result) {
288 convolve(signal, kernel, DEFAULT_KERNEL_CENTER, result, null);
289 }
290
291 /**
292 * Convolves provided signal with provided kernel assuming a zero value
293 * extension method and kernel center located at its origin.
294 *
295 * @param signal signal to be convolved.
296 * @param kernel kernel used for convolution.
297 * @param result array where result of convolution will be stored. Must
298 * have the sum of the lengths of signal and kernel minus one.
299 * @param listener listener attending events generated during convolution.
300 * @throws IllegalArgumentException if provided result array does not
301 * have proper length.
302 */
303 public static void convolve(final double[] signal, final double[] kernel, final double[] result,
304 final Convolver1DListener listener) {
305 convolve(signal, kernel, DEFAULT_KERNEL_CENTER, result, listener);
306 }
307
308 /**
309 * Convolves provided signal with provided kernel assuming a zero value
310 * extension method.
311 *
312 * @param signal signal to be convolved.
313 * @param kernel kernel used for convolution.
314 * @param kernelCenter position of kernel center. This determines how
315 * signal's edge extension is applied. Must be a value within kernel's
316 * boundaries.
317 * @return a new array containing result of convolution.
318 * @throws IllegalArgumentException if kernel center is not within kernel's
319 * boundaries.
320 */
321 public static double[] convolve(final double[] signal, final double[] kernel, final int kernelCenter) {
322 return convolve(signal, kernel, kernelCenter, (Convolver1DListener) null);
323 }
324
325 /**
326 * Convolves provided signal with provided kernel assuming a zero value
327 * extension method.
328 *
329 * @param signal signal to be convolved.
330 * @param kernel kernel used for convolution.
331 * @param kernelCenter position of kernel center. This determines how
332 * signal's edge extension is applied. Must be a value within kernel's
333 * boundaries.
334 * @param listener listener attending events generated during convolution.
335 * @return a new array containing result of convolution.
336 * @throws IllegalArgumentException if kernel center is not within kernel's
337 * boundaries.
338 */
339 public static double[] convolve(final double[] signal, final double[] kernel, final int kernelCenter,
340 final Convolver1DListener listener) {
341 return convolve(signal, kernel, kernelCenter, DEFAULT_EDGE_METHOD, listener);
342 }
343
344 /**
345 * Convolves provided signal with provided kernel assuming a zero value
346 * extension method.
347 *
348 * @param signal signal to be convolved.
349 * @param kernel kernel used for convolution.
350 * @param kernelCenter position of kernel center. This determines how
351 * signal's edge extension is applied. Must be a value within kernel's
352 * boundaries.
353 * @param result array where result of convolution will be stored. Must
354 * have the sum of the lengths of signal and kernel minus one.
355 * @throws IllegalArgumentException if provided result array does not
356 * have proper length or if kernel center is not within kernel's boundaries.
357 */
358 public static void convolve(final double[] signal, final double[] kernel, final int kernelCenter,
359 final double[] result) {
360 convolve(signal, kernel, kernelCenter, result, null);
361 }
362
363 /**
364 * Convolves provided signal with provided kernel assuming a zero value
365 * extension method.
366 *
367 * @param signal signal to be convolved.
368 * @param kernel kernel used for convolution.
369 * @param kernelCenter position of kernel center. This determines how
370 * signal's edge extension is applied. Must be a value within kernel's
371 * boundaries.
372 * @param result array where result of convolution will be stored. Must
373 * have the sum of the lengths of signal and kernel minus one.
374 * @param listener listener attending events generated during convolution.
375 * @throws IllegalArgumentException if provided result array does not
376 * have proper length or if kernel center is not within kernel's boundaries.
377 */
378 public static void convolve(final double[] signal, final double[] kernel, final int kernelCenter,
379 final double[] result, final Convolver1DListener listener) {
380 convolve(signal, kernel, kernelCenter, DEFAULT_EDGE_METHOD, result, listener);
381 }
382
383 /**
384 * Convolves provided signal with provided kernel assuming a zero value when
385 * constant edge extension is used (which makes it equivalent to zero
386 * extension method).
387 *
388 * @param signal signal to be convolved.
389 * @param kernel kernel used for convolution.
390 * @param kernelCenter position of kernel center. This determines how
391 * signal's edge extension is applied. Must be a value within kernel's
392 * boundaries.
393 * @param edgeMethod edge extension method.
394 * @return a new array containing result of convolution.
395 * @throws IllegalArgumentException if kernel center is not within kernel's
396 * boundaries.
397 */
398 public static double[] convolve(final double[] signal, final double[] kernel, final int kernelCenter,
399 final ConvolverEdgeMethod edgeMethod) {
400 return convolve(signal, kernel, kernelCenter, edgeMethod, (Convolver1DListener) null);
401 }
402
403 /**
404 * Convolves provided signal with provided kernel assuming a zero value when
405 * constant edge extension is used (which makes it equivalent to zero
406 * extension method).
407 *
408 * @param signal signal to be convolved.
409 * @param kernel kernel used for convolution.
410 * @param kernelCenter position of kernel center. This determines how
411 * signal's edge extension is applied. Must be a value within kernel's
412 * boundaries.
413 * @param edgeMethod edge extension method.
414 * @param listener listener attending events generated during convolution.
415 * @return a new array containing result of convolution.
416 * @throws IllegalArgumentException if kernel center is not within kernel's
417 * boundaries.
418 */
419 public static double[] convolve(final double[] signal, final double[] kernel, final int kernelCenter,
420 final ConvolverEdgeMethod edgeMethod, final Convolver1DListener listener) {
421 return convolve(signal, kernel, kernelCenter, edgeMethod, DEFAULT_CONSTANT_VALUE, listener);
422 }
423
424 /**
425 * Convolves provided signal with provided kernel assuming a zero value when
426 * constant edge extension is used (which makes it equivalent to zero
427 * extension method).
428 *
429 * @param signal signal to be convolved.
430 * @param kernel kernel used for convolution.
431 * @param kernelCenter position of kernel center. This determines how
432 * signal's edge extension is applied. Must be a value within kernel's
433 * boundaries.
434 * @param edgeMethod edge extension method.
435 * @param result array where result of convolution will be stored. Must
436 * have the sum of the lengths of signal and kernel minus one.
437 * @throws IllegalArgumentException if provided result array does not
438 * have proper length or if kernel center is not within kernel's boundaries.
439 */
440 public static void convolve(final double[] signal, final double[] kernel, final int kernelCenter,
441 final ConvolverEdgeMethod edgeMethod, final double[] result) {
442 convolve(signal, kernel, kernelCenter, edgeMethod, result, null);
443 }
444
445 /**
446 * Convolves provided signal with provided kernel assuming a zero value when
447 * constant edge extension is used (which makes it equivalent to zero
448 * extension method).
449 *
450 * @param signal signal to be convolved.
451 * @param kernel kernel used for convolution.
452 * @param kernelCenter position of kernel center. This determines how
453 * signal's edge extension is applied. Must be a value within kernel's
454 * boundaries.
455 * @param edgeMethod edge extension method.
456 * @param listener listener attending events generated during convolution.
457 * @param result array where result of convolution will be stored. Must
458 * have the sum of the lengths of signal and kernel minus one.
459 * @throws IllegalArgumentException if provided result array does not
460 * have proper length or if kernel center is not within kernel's boundaries.
461 */
462 public static void convolve(final double[] signal, final double[] kernel, final int kernelCenter,
463 final ConvolverEdgeMethod edgeMethod, final double[] result,
464 final Convolver1DListener listener) {
465 convolve(signal, kernel, kernelCenter, edgeMethod, DEFAULT_CONSTANT_VALUE, result, listener);
466 }
467
468 /**
469 * Convolves provided signal with provided kernel.
470 *
471 * @param signal signal to be convolved.
472 * @param kernel kernel used for convolution.
473 * @param kernelCenter position of kernel center. This determines how
474 * signal's edge extension is applied. Must be a value within kernel's
475 * boundaries.
476 * @param edgeMethod edge extension method.
477 * @param constantValue constant value to be used for edge extension if
478 * constant extension method is used.
479 * @return a new array containing result of convolution.
480 * @throws IllegalArgumentException if kernel center is not within kernel's
481 * boundaries.
482 */
483 public static double[] convolve(final double[] signal, final double[] kernel, final int kernelCenter,
484 final ConvolverEdgeMethod edgeMethod, final double constantValue) {
485 return convolve(signal, kernel, kernelCenter, edgeMethod, constantValue, (Convolver1DListener) null);
486 }
487
488 /**
489 * Convolves provided signal with provided kernel.
490 *
491 * @param signal signal to be convolved.
492 * @param kernel kernel used for convolution.
493 * @param kernelCenter position of kernel center. This determines how
494 * signal's edge extension is applied. Must be a value within kernel's
495 * boundaries.
496 * @param edgeMethod edge extension method.
497 * @param constantValue constant value to be used for edge extension if
498 * constant extension method is used.
499 * @param listener listener attending events generated during convolution.
500 * @return a new array containing result of convolution.
501 * @throws IllegalArgumentException if kernel center is not within kernel's
502 * boundaries.
503 */
504 public static double[] convolve(final double[] signal, final double[] kernel, final int kernelCenter,
505 final ConvolverEdgeMethod edgeMethod, final double constantValue,
506 final Convolver1DListener listener) {
507
508 final var result = new double[signal.length + kernel.length - 1];
509 convolve(signal, kernel, kernelCenter, edgeMethod, constantValue, result, listener);
510 return result;
511 }
512
513 /**
514 * Convolves provided signal with provided kernel.
515 *
516 * @param signal signal to be convolved.
517 * @param kernel kernel used for convolution.
518 * @param kernelCenter position of kernel center. This determines how
519 * signal's edge extension is applied. Must be a value within kernel's
520 * boundaries.
521 * @param edgeMethod edge extension method.
522 * @param constantValue constant value to be used for edge extension if
523 * constant extension method is used.
524 * @param result array where result of convolution will be stored. Must
525 * have the sum of the lengths of signal and kernel minus one.
526 * @throws IllegalArgumentException if provided result array does not
527 * have proper length or if kernel center is not within kernel's boundaries.
528 */
529 public static void convolve(final double[] signal, final double[] kernel, final int kernelCenter,
530 final ConvolverEdgeMethod edgeMethod, final double constantValue,
531 final double[] result) {
532 convolve(signal, kernel, kernelCenter, edgeMethod, constantValue, result, null);
533 }
534
535 /**
536 * Convolves provided signal with provided kernel.
537 *
538 * @param signal signal to be convolved.
539 * @param kernel kernel used for convolution.
540 * @param kernelCenter position of kernel center. This determines how
541 * signal's edge extension is applied. Must be a value within kernel's
542 * boundaries.
543 * @param edgeMethod edge extension method.
544 * @param constantValue constant value to be used for edge extension if
545 * constant extension method is used.
546 * @param result array where result of convolution will be stored. Must
547 * have the sum of the lengths of signal and kernel minus one.
548 * @param listener listener attending events generated during convolution.
549 * @throws IllegalArgumentException if provided result array does not
550 * have proper length or if kernel center is not within kernel's boundaries.
551 */
552 public static void convolve(final double[] signal, final double[] kernel, final int kernelCenter,
553 final ConvolverEdgeMethod edgeMethod, final double constantValue, final double[] result,
554 final Convolver1DListener listener) {
555 if (kernelCenter < 0 || kernelCenter >= kernel.length) {
556 throw new IllegalArgumentException("kernel center must be within kernel boundaries");
557 }
558 if (result.length != signal.length + kernel.length - 1) {
559 throw new IllegalArgumentException(
560 " result array must have a length equal to the sum of signal and kernel lengths minus one");
561 }
562
563 switch (edgeMethod) {
564 case MIRROR_EDGE:
565 internalConvolveMirror(signal, kernel, kernelCenter, result, listener);
566 break;
567 case REPEAT_EDGE:
568 internalConvolveRepeat(signal, kernel, kernelCenter, result, listener);
569 break;
570 case CONSTANT_EDGE:
571 internalConvolveConstant(signal, kernel, kernelCenter, constantValue, result, listener);
572 break;
573 case ZERO_EDGE:
574 default:
575 internalConvolveZero(signal, kernel, kernelCenter, result, listener);
576 break;
577 }
578 }
579
580 /**
581 * Internal method to convolve signal using zero edge extension method.
582 *
583 * @param signal signal to be convolved.
584 * @param kernel kernel to convolve with.
585 * @param kernelCenter kernel center.
586 * @param result array where result is stored.
587 * @param listener listener attending events generated during convolution.
588 */
589 private static void internalConvolveZero(
590 final double[] signal, final double[] kernel, final int kernelCenter, final double[] result,
591 final Convolver1DListener listener) {
592 final var kernelLength = kernel.length;
593 final var resultLength = result.length;
594 double accum;
595 int signalPos;
596
597 if (listener != null) {
598 listener.onStartConvolution();
599 }
600
601 for (var i = 0; i < resultLength; i++) {
602
603 accum = 0.0;
604 signalPos = i - kernelCenter;
605 for (var j = 0; j < kernelLength; j++) {
606 final var signalValue = getSignalValueZero(signal, signalPos - j);
607 accum += signalValue * kernel[j];
608 }
609
610 result[i] = accum;
611
612 if (listener != null) {
613 final var progress = (float) i / (float) resultLength;
614 listener.onConvolveProgressChange(progress);
615 }
616 }
617
618 if (listener != null) {
619 listener.onFinishConvolution();
620 }
621 }
622
623 /**
624 * Internal method to determine signal value even when a position outside
625 * its boundaries is requested when zero edge extension is being used.
626 *
627 * @param signal signal to request a value for.
628 * @param pos requested position.
629 * @return signal value when position is within boundaries or zero
630 * otherwise.
631 */
632 protected static double getSignalValueZero(final double[] signal, final int pos) {
633 if (pos >= 0 && pos < signal.length) {
634 return signal[pos];
635 } else {
636 return 0.0;
637 }
638 }
639
640 /**
641 * Internal method to convolve signal using constant value edge extension
642 * method.
643 *
644 * @param signal signal to be convolved.
645 * @param kernel kernel to convolve with.
646 * @param kernelCenter kernel center.
647 * @param constantValue constant value to use for positions outside signal
648 * boundaries.
649 * @param result array where result is stored.
650 * @param listener listener attending events generated during convolution.
651 */
652 private static void internalConvolveConstant(
653 final double[] signal, final double[] kernel, final int kernelCenter, final double constantValue,
654 final double[] result, final Convolver1DListener listener) {
655 final var kernelLength = kernel.length;
656 final var resultLength = result.length;
657 double accum;
658 int signalPos;
659
660 if (listener != null) {
661 listener.onStartConvolution();
662 }
663
664 for (var i = 0; i < resultLength; i++) {
665
666 accum = 0.0;
667 signalPos = i - kernelCenter;
668 for (var j = 0; j < kernelLength; j++) {
669 final var signalValue = getSignalValueConstant(signal, signalPos - j, constantValue);
670 accum += signalValue * kernel[j];
671 }
672
673 result[i] = accum;
674
675 if (listener != null) {
676 final var progress = (float) i / (float) resultLength;
677 listener.onConvolveProgressChange(progress);
678 }
679 }
680
681 if (listener != null) {
682 listener.onFinishConvolution();
683 }
684 }
685
686 /**
687 * Internal method to determine signal value even when a position outside
688 * its boundaries is requested for constant value edge extension.
689 *
690 * @param signal signal to request a value for.
691 * @param pos requested position.
692 * @param constantValue constant value to use when requesting a position
693 * outside signal boundaries.
694 * @return signal value when position is within boundaries or constant
695 * value otherwise.
696 */
697 protected static double getSignalValueConstant(final double[] signal, final int pos, final double constantValue) {
698 if (pos >= 0 && pos < signal.length) {
699 return signal[pos];
700 } else {
701 return constantValue;
702 }
703 }
704
705 /**
706 * Internal method to convolve signal using a repeat extension method.
707 *
708 * @param signal signal to be convolved.
709 * @param kernel kernel to convolve with.
710 * @param kernelCenter kernel center.
711 * @param result array where result is stored.
712 * @param listener listener attending events generated during convolution.
713 */
714 private static void internalConvolveRepeat(
715 final double[] signal, final double[] kernel, final int kernelCenter, final double[] result,
716 final Convolver1DListener listener) {
717 final var kernelLength = kernel.length;
718 final var resultLength = result.length;
719 double accum;
720 int signalPos;
721
722 if (listener != null) {
723 listener.onStartConvolution();
724 }
725
726 for (var i = 0; i < resultLength; i++) {
727
728 accum = 0.0;
729 signalPos = i - kernelCenter;
730 for (var j = 0; j < kernelLength; j++) {
731 final var signalValue = getSignalValueRepeat(signal, signalPos - j);
732 accum += signalValue * kernel[j];
733 }
734
735 result[i] = accum;
736
737 if (listener != null) {
738 final var progress = (float) i / (float) resultLength;
739 listener.onConvolveProgressChange(progress);
740 }
741 }
742
743 if (listener != null) {
744 listener.onFinishConvolution();
745 }
746 }
747
748 /**
749 * Internal method to determine signal value even when a position outside
750 * its boundaries is requested for repeat edge extension.
751 *
752 * @param signal signal to request a value for.
753 * @param pos requested position.
754 * @return signal value assuming that signal is repeated indefinitely
755 * outside its boundaries.
756 */
757 protected static double getSignalValueRepeat(final double[] signal, int pos) {
758 final var signalLength = signal.length;
759 pos = pos % signalLength;
760 if (pos < 0) {
761 pos += signalLength;
762 }
763 return signal[pos];
764 }
765
766 /**
767 * Internal method to convolve signal using a mirror extension method.
768 *
769 * @param signal signal to be convolved.
770 * @param kernel kernel to convolve with.
771 * @param kernelCenter kernel center.
772 * @param result array where result is stored.
773 * @param listener listener attending events generated during convolution.
774 */
775 private static void internalConvolveMirror(
776 final double[] signal, final double[] kernel, final int kernelCenter, final double[] result,
777 final Convolver1DListener listener) {
778 final var kernelLength = kernel.length;
779 final var resultLength = result.length;
780 double accum;
781 int signalPos;
782
783 if (listener != null) {
784 listener.onStartConvolution();
785 }
786
787 for (var i = 0; i < resultLength; i++) {
788
789 accum = 0.0;
790 signalPos = i - kernelCenter;
791 for (var j = 0; j < kernelLength; j++) {
792 final var signalValue = getSignalValueMirror(signal, signalPos - j);
793 accum += signalValue * kernel[j];
794 }
795
796 result[i] = accum;
797
798 if (listener != null) {
799 final var progress = (float) i / (float) resultLength;
800 listener.onConvolveProgressChange(progress);
801 }
802 }
803
804 if (listener != null) {
805 listener.onFinishConvolution();
806 }
807 }
808
809 /**
810 * Internal method to determine signal value even when a position outside
811 * its boundaries is requested for mirror edge extension.
812 *
813 * @param signal signal to request a value for.
814 * @param pos requested position.
815 * @return signal value when assuming that signal is mirrored outside its
816 * boundaries.
817 */
818 protected static double getSignalValueMirror(double[] signal, int pos) {
819 final var signalLength = signal.length;
820 var times = pos / signalLength;
821
822 if (pos < 0) {
823 times = Math.abs(times);
824 if (times == 0) {
825 times = 1;
826 }
827 pos = 2 * signalLength * times + pos;
828 }
829
830 times = pos / signalLength;
831 final var reversed = (times % 2 != 0);
832
833 pos = pos % signalLength;
834 if (pos < 0) {
835 pos += signalLength;
836 } else {
837 if (reversed) {
838 // reversed
839 pos = signalLength - 1 - pos;
840 }
841 }
842
843 return signal[pos];
844 }
845
846 /**
847 * Interface defining events produced by this class.
848 */
849 public interface Convolver1DListener {
850 /**
851 * Called when convolution starts.
852 */
853 void onStartConvolution();
854
855 /**
856 * Called when convolution finishes.
857 */
858 void onFinishConvolution();
859
860 /**
861 * Called when convolution progress changes.
862 *
863 * @param progress convolution progress as a value between 0.0f and
864 * 1.0f.
865 */
866 void onConvolveProgressChange(final float progress);
867 }
868 }