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 }