├── INSTALL.txt ├── LICENSE ├── Makefile.am ├── README.md ├── configure.ac ├── fraction-131-140_16bit_s.raw ├── include └── lm.h ├── src ├── lm.c └── lmtool.c └── test ├── lmtest1.c ├── lmtest10.c ├── lmtest11.c ├── lmtest2.c ├── lmtest3.c ├── lmtest4.c ├── lmtest5.c ├── lmtest6.c ├── lmtest7.c ├── lmtest8.c └── lmtest9.c /INSTALL.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | libmusic 39 | 40 | Frequency detection using MUSIC algorithm. 41 | 42 | 43 | Sources 44 | 45 | git clone https://github.com/dataandsignal/libmusic.git 46 | 47 | 48 | Compilation 49 | 50 | cd libmusic 51 | sudo apt-get update 52 | sudo apt-get -y install automake autoconf 53 | sudo apt-get -y install libblas-dev liblapack-dev 54 | autoreconf --install 55 | autoconf 56 | automake --add-missing 57 | ./configure 58 | make (for debug: make CFLAGS="-ggdb -O0") 59 | make test 60 | make test check (for autotesting) 61 | sudo make install (will install libmusic to /usr/local/lib) 62 | sudo ldconfig 63 | 64 | 65 | Contact 66 | 67 | Data And Signal - IT Solutions, JUNE 2018 68 | libmusic@dataandsignal.com 69 | piotr@dataandsignal.com 70 | 71 | bugs: https://github.com/dataandsignal/libmusic/issues 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 dataandsignal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | #SUBDIRS = test 2 | dist_doc_DATA = README.md 3 | 4 | AM_CFLAGS = -I./src -Iinclude -I$(srcdir)/include 5 | lib_LTLIBRARIES = libmusic.la 6 | libmusic_la_SOURCES = src/lm.c 7 | include_HEADERS = include/lm.h 8 | libmusic_la_LDFLAGS = -version-info 1:0:0 9 | 10 | bin_PROGRAMS = lmtool 11 | lmtool_SOURCES = src/lmtool.c 12 | lmtool_LDADD = libmusic.la 13 | 14 | check_PROGRAMS = lmtest1 lmtest2 lmtest3 lmtest4 lmtest5 lmtest6 lmtest7 lmtest8 lmtest9 lmtest10 lmtest11 15 | TESTS = $(check_PROGRAMS) 16 | 17 | # MUSIC algorithm tests 18 | LIBS += -llapack -lm 19 | 20 | # Test internals 21 | 22 | lmtest1_SOURCES = test/lmtest1.c src/lm.c lm.h 23 | lmtest1_CFLAGS = -Iinclude 24 | 25 | # Test SVD 26 | 27 | lmtest2_SOURCES = test/lmtest2.c src/lm.c lm.h 28 | lmtest2_CFLAGS = -Iinclude 29 | 30 | lmtest3_SOURCES = test/lmtest3.c src/lm.c lm.h 31 | lmtest3_CFLAGS = -Iinclude 32 | 33 | lmtest4_SOURCES = test/lmtest4.c src/lm.c lm.h 34 | lmtest4_CFLAGS = -Iinclude 35 | 36 | # Test correlation 37 | 38 | lmtest5_SOURCES = test/lmtest5.c src/lm.c lm.h 39 | lmtest5_CFLAGS = -Iinclude 40 | 41 | # Test MUSIC algorithm, lm_detect 42 | 43 | lmtest6_SOURCES = test/lmtest6.c src/lm.c lm.h 44 | lmtest6_CFLAGS = -Iinclude 45 | 46 | lmtest7_SOURCES = test/lmtest7.c src/lm.c lm.h 47 | lmtest7_CFLAGS = -Iinclude 48 | 49 | lmtest8_SOURCES = test/lmtest8.c src/lm.c lm.h 50 | lmtest8_CFLAGS = -Iinclude 51 | 52 | # Test DTMF detect with MUSIC algorithm, lm_standard_init and lm_dtmf_init 53 | 54 | lmtest9_SOURCES = test/lmtest9.c src/lm.c lm.h 55 | lmtest9_CFLAGS = -Iinclude 56 | 57 | # Test DTMF detect with MUSIC algorithm, lm_dtmf_detect, on reference data 58 | 59 | lmtest10_SOURCES = test/lmtest10.c src/lm.c lm.h 60 | lmtest10_CFLAGS = -Iinclude 61 | 62 | # Test DTMF detect with MUSIC algorithm, lm_dtmf_detect, external file 63 | 64 | lmtest11_SOURCES = test/lmtest11.c src/lm.c lm.h 65 | lmtest11_CFLAGS = -Iinclude 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Welcome to libmusic 2 | ### a frequency detection library 3 | 4 | ![2tone_psd](https://user-images.githubusercontent.com/40000574/190140071-8672a878-4146-49a2-8a79-20bb2e777f86.jpg) 5 | 6 | 7 | 8 | ## Overview 9 | 10 | libmusic is a frequency detection library, that uses [MUSIC](https://en.wikipedia.org/wiki/MUSIC_(algorithm)) algorithm. It belongs to parametric methods of spectral estimation which working principle is to decompose signal space into pure signal and noise subspaces, using SVD of autocorrelation matrix. 11 | 12 | This allows for superresolution detection, where [Heisenberg uncertainty principle](https://en.wikipedia.org/wiki/Uncertainty_principle) no longer holds and one is able to compute pseudospectrum at every point up to half sampling rate just from a (very) limited amount of samples, and with better accuracy than parametric methods of spectrum estimation do (e.g. periodogram). 13 | 14 | For applications with real time requirements (playout buffers, jitter, etc.) this provides significantly reduced latency over other detection methods and opens up possibilities to detect from minimum amount of data. 15 | 16 | In telephony for example, by using this method instead of popular Goertzel algorithm, 17 | one can detect DTMF tones with samples requirement reduced from 110 to 8 and frequency resolution (accuracy) increased from 72.73 Hz to 10^-2 Hz in the same time. 18 | 19 | 20 | ## Performance in noise 21 | 22 | In a noisy environment, MUSIC performs well as long as SNR is above 66.53 dB. 23 | 24 | 25 | ## Applications 26 | 27 | In telephony, DTMF symbols must be removed from stream, due too sensitive data protection. Often though, fractions of those DTMFs are left in a stream and must be removed. This cannot be done with Goertzel algorithm as it needs more than 110 samples to achieve right frequency resolution, to be able to detect DTMF frequencies. An example of such DTMF fraction is shown on the picture. This one is 14 samples in length (1.75 ms at a sampling rate of 8000 Hz). 28 | 29 | ![dtmf_test_vector_valid](https://user-images.githubusercontent.com/40000574/190151206-2e7b78a0-0d79-459f-bf8f-cf422fd9da72.jpg) 30 | 31 | With MUSIC, samples requirement is reduced from 110 to 8 and frequency resolution (accuracy) increased from 72.73 Hz to 10^-2 Hz in the same time. 32 | This picture presents correctness as a percentage of detected fractions of dual tone signals (DTMFs), by input vector length **N** (8,9,10,11,12,14), autocorrelation order **M** (4-8) and fraction length **L** (8-28 samples). 33 | 34 | ![dtmf_test_valid_freq_2](https://user-images.githubusercontent.com/40000574/190215259-e8a2c69e-921d-4c7b-99a7-69d4fb2ece7a.jpg) 35 | 36 | 37 | 38 | For example, using a block of N=12 samples, all fractions of length L=10 and above can be detected (with autocorrelation order M={6,7}). N=8 detects all fractions longer than 8 samples (1 ms) with M=4. 39 | 40 | 41 | ## Discussion 42 | 43 | If you have any questions, or would like to share your thoughts, 44 | request new features, etc. - please post them to [Discussions](https://github.com/dataandsignal/libmusic/discussions). 45 | 46 | 47 | ## Issues 48 | 49 | If you would like to report an issue, please do it on [issues](https://github.com/dataandsignal/libmusic/issues) page. 50 | 51 | 52 | ## MATLAB 53 | 54 | A MATLAB sandbox for libmusic: [libmusic_m](https://github.com/dataandsignal/libmusic_m) 55 | 56 | 57 | ## Repository 58 | 59 | URL: https://github.com/dataandsignal/libmusic 60 | 61 | 62 | ## Further reading, references 63 | 64 | A good references about spectral analysis and space decomposition methods are: 65 | 66 | - Hayes M. H., Statistical Digital Signal Processing And Modeling, Georgia Institute of Technology, Wiley, 2008 67 | - Lawrence Marple S. Jr., Digital Spectral Analysis, Dover Publications, 2019 68 | - Schmidt R. O., Multiple Emitter Location and Signal Parameter Estimation, IEEE Transactions on Antennas and Propagation, Vol. AP-34, No. 3, 1986 69 | 70 | These references are missing though (or skipping intentionally) a crucial result about autocorrelation and sinusoids embedded in a vector space whose elements are shifted samples of that same sinusoid (with all the phases). This is a fundamental finding space decomposition methods are built on. 71 | 72 | This is explained in more detail in: 73 | 74 | - Penny W. D., Signal Processing Course, University College London, 2000 75 | 76 | - and also in my [engineering thesis](https://drive.google.com/file/d/1dfen9z3E5YuNjXSm3PTG00R4N38hmIMR/view?usp=sharing) 77 | 78 | 79 | 80 | 81 | ## Copyright 82 | 83 | LIBMUSIC 84 | 85 | Copyright (C) 2018-2023, Piotr Gregor piotr@dataandsignal.com 86 | 87 | August 2022 88 | 89 |
90 |

91 | Project developed with a help of 92 |

93 |

94 | JetBrains Black Box Logo logo. 95 |

-------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([libmusic], [1.0.0], [libmusic@dataandsignal.com, bugs: https://github.com/spinlockirqsave/libmusic/issues], [libmusic], [dataandsignal.com]) 2 | AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign]) 3 | AC_PROG_CC 4 | 5 | AM_PROG_AR 6 | LT_INIT 7 | 8 | AC_CANONICAL_HOST 9 | 10 | AC_CONFIG_HEADERS([config.h]) 11 | AC_CONFIG_FILES([Makefile]) 12 | 13 | AC_OUTPUT 14 | -------------------------------------------------------------------------------- /fraction-131-140_16bit_s.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dataandsignal/libmusic/da7338a7837c5d47ec9180f34caa6ea9f1ca9d59/fraction-131-140_16bit_s.raw -------------------------------------------------------------------------------- /include/lm.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifndef LM_LM_H 39 | #define LM_LM_H 40 | 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | #include 54 | #include 55 | 56 | #define LM_VER_STRING PACKAGE_STRING 57 | #define LM_VERSION PACKAGE_VERSION 58 | 59 | #define LM_DTMF_SIGNALS_EXPECTED_N 2 60 | #define LM_DTMF_SIGNALS_N (2 * (LM_DTMF_SIGNALS_EXPECTED_N)) /* Need to account for complex signals. */ 61 | #define LM_CORR_ORD ((2 * (LM_DTMF_SIGNALS_N)) - 1) 62 | 63 | #define LM_DTMF_PEAK_THRESHOLD 1000 64 | #define LM_DTMF_PEAK_THRESHOLD1 500 65 | #define LM_DTMF_PEAK_THRESHOLD2 20 66 | #define LM_DTMF_PEAK_THRESHOLD3 90 67 | 68 | #define LM_DTMF_CROSSTALK_THRESHOLD 4 69 | 70 | /* 10dB */ 71 | #define LM_DTMF_FREQ_POWER_DIFF_THRESHOLD 3.1623 72 | 73 | #define LM_DTMF_ENERGY_RATIO 0.95 74 | 75 | typedef enum lm_status { 76 | lm_status_false = 0, 77 | lm_status_true = 1 78 | } lm_status_t; 79 | 80 | typedef struct lm_detection_s { 81 | uint16_t freq; 82 | double peak; 83 | } lm_detection_t; 84 | 85 | static const uint16_t lm_dtmf_freqs[] = { 86 | 697, 770, 852, 941, 1209, 1336, 1477, 1633 87 | }; 88 | 89 | static const uint16_t lm_dtmf_row_freqs[] = { 90 | 697, 770, 852, 941 91 | }; 92 | 93 | static const uint16_t lm_dtmf_col_freqs[] = { 94 | 1209, 1336, 1477, 1633 95 | }; 96 | 97 | /** 98 | * DTMF association to indices, used consistenly throughout lmusic. 99 | * 100 | * Tones: 101 | * 1209 Hz | 1336 Hz | 1477 Hz | 1633 Hz 102 | * 697 Hz '1' '2' '3' 'A' 103 | * 770 Hz '4' '5' '6' 'B' 104 | * 852 Hz '7' '8' '9' 'C' 105 | * 941 Hz '*' '0' '#' 'D' 106 | * 107 | * Indices 108 | * 1|2|3|4 109 | * 5|6|7|8 110 | * 9|10|11|12 111 | * 13|14|15|16 112 | */ 113 | static const char lm_dtmf_idx_2_char[17] = { 114 | 'X', '1', '2', '3', 'A', '4', '5', '6', 'B', '7', '8', '9', 'C', '*', '0', '#', 'D' 115 | }; 116 | 117 | typedef struct lm_detection_info_s { 118 | double peak_min; 119 | double peak_max; 120 | uint16_t peak_min_freq; 121 | uint16_t peak_max_freq; 122 | double peak_697; 123 | double peak_770; 124 | double peak_852; 125 | double peak_941; 126 | double peak_1209; 127 | double peak_1336; 128 | double peak_1477; 129 | double peak_1633; 130 | } lm_detection_info_t; 131 | 132 | typedef struct lm_detector_s { 133 | uint16_t fs; /* sampling frequency in Hz */ 134 | uint16_t x_n; /* configured length of the detection block in samples */ 135 | double dt; /* time between two consecutive samples in seconds = 1/@fs */ 136 | 137 | struct lm_svd { 138 | uint8_t prealloced; /* 1 if memory and parameters needed by LAPACK are set. 139 | lm_detect doesn't need to query LAPACK for the optimal values of parameters. 140 | Otherise LAPACK is run twice: once to query for the params, and second to compute SVD. 141 | If set to 0 all values in @svd array are ignored. */ 142 | char JOBU; /* JOBU is CHARACTER*1 143 | Specifies options for computing all or part of the matrix U: 144 | = 'A': all M columns of U are returned in array U: 145 | = 'S': the first min(m,n) columns of U (the left singular 146 | vectors) are returned in the array U; 147 | = 'O': the first min(m,n) columns of U (the left singular 148 | vectors) are overwritten on the array A; 149 | = 'N': no columns of U (no left singular vectors) are 150 | computed. */ 151 | char JOBVT; /* JOBVT is CHARACTER*1 152 | Specifies options for computing all or part of the matrix 153 | V**T: 154 | = 'A': all N rows of V**T are returned in the array VT; 155 | = 'S': the first min(m,n) rows of V**T (the right singular 156 | vectors) are returned in the array VT; 157 | = 'O': the first min(m,n) rows of V**T (the right singular 158 | vectors) are overwritten on the array A; 159 | = 'N': no rows of V**T (no right singular vectors) are 160 | computed. JOBVT and JOBU cannot both be 'O'. */ 161 | int M; /* M is INTEGER. The number of rows of the input matrix A. M >= 0. Correlation result matrix row dimension. */ 162 | int N; /* N is INTEGER. The number of columns of the input matrix A. N >= 0. Correlation result matrix column dimension */ 163 | double *A, *A2; /* A is DOUBLE PRECISION array, dimension (LDA,N) 164 | On entry, the M-by-N matrix A. On exit, if JOBZ = 'O', 165 | A is overwritten with the first N columns of U (the left singular vectors, stored columnwise) if M >= N; 166 | A is overwritten with the first M rows of V**T (the right singular vectors, stored rowwise) otherwise. 167 | if JOBZ .ne. 'O', the contents of A are destroyed. */ 168 | int LDA; /* LDA is INTEGER. The leading dimension of the array A. LDA >= max(1,M). */ 169 | double *S; /* S is DOUBLE PRECISION array, dimension (min(M,N)). The singular values of A, sorted so that S(i) >= S(i+1). */ 170 | double *U; /* U is DOUBLE PRECISION array, dimension (LDU,UCOL) 171 | UCOL = M if JOBZ = 'A' or JOBZ = 'O' and M < N; 172 | UCOL = min(M,N) if JOBZ = 'S'. 173 | If JOBZ = 'A' or JOBZ = 'O' and M < N, U contains the M-by-M 174 | orthogonal matrix U; 175 | if JOBZ = 'S', U contains the first min(M,N) columns of U 176 | (the left singular vectors, stored columnwise); 177 | if JOBZ = 'O' and M >= N, or JOBZ = 'N', U is not referenced. */ 178 | int LDU; /* LDU is INTEGER 179 | The leading dimension of the array U. LDU >= 1; if 180 | JOBZ = 'S' or 'A' or JOBZ = 'O' and M < N, LDU >= M. */ 181 | double *VT; /* VT is DOUBLE PRECISION array, dimension (LDVT,N) 182 | If JOBZ = 'A' or JOBZ = 'O' and M >= N, VT contains the 183 | N-by-N orthogonal matrix V**T; 184 | if JOBZ = 'S', VT contains the first min(M,N) rows of 185 | V**T (the right singular vectors, stored rowwise); 186 | if JOBZ = 'O' and M < N, or JOBZ = 'N', VT is not referenced. */ 187 | int LDVT; /* LDVT is INTEGER 188 | The leading dimension of the array VT. LDVT >= 1; 189 | if JOBZ = 'A' or JOBZ = 'O' and M >= N, LDVT >= N; 190 | if JOBZ = 'S', LDVT >= min(M,N). */ 191 | double *WORK; /* WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) 192 | On exit, if INFO = 0, WORK(1) returns the optimal LWORK; */ 193 | int LWORK; /* LWORK is INTEGER 194 | The dimension of the array WORK. LWORK >= 1. 195 | If LWORK = -1, a workspace query is assumed. The optimal 196 | size for the WORK array is calculated and stored in WORK(1), 197 | and no other work except argument checking is performed. 198 | 199 | Let mx = max(M,N) and mn = min(M,N). 200 | If JOBZ = 'N', LWORK >= 3*mn + max( mx, 7*mn ). 201 | If JOBZ = 'O', LWORK >= 3*mn + max( mx, 5*mn*mn + 4*mn ). 202 | If JOBZ = 'S', LWORK >= 4*mn*mn + 7*mn. 203 | If JOBZ = 'A', LWORK >= 4*mn*mn + 6*mn + mx. 204 | These are not tight minimums in all cases; see comments inside code. 205 | For good performance, LWORK should generally be larger; 206 | a query is recommended. */ 207 | } svd; 208 | 209 | lm_detection_t *detections; /* Points to array of detections to be done. */ 210 | uint8_t signals_n; /* Number of expected signals in the sample vector. */ 211 | uint16_t detections_n; /* Number of detections of interest in @detections array. */ 212 | 213 | double *a_real; 214 | double *a_imag; 215 | 216 | lm_detection_info_t info; /* If on entry @want_info is set to 1, then on exit @info contains details about result detection. */ 217 | 218 | double peak_max_threshold1; /* Upper threshold on MIN, min peak must be below it. */ 219 | double peak_max_threshold2; /* Lower threshold on MAX, both row and col max freqs must be above it. */ 220 | double peak_max_threshold3; /* Upper threshold on MAX, only max peak must be above it. */ 221 | } lm_detector_t; 222 | 223 | /** 224 | * Returns a covariance matrix unbiased estimator. 225 | * 226 | * Partition a length-N signal vector @s into nonoverlapping data segments (frames) of length 227 | * @n - @corr_ord. 228 | * Each data frame occupies one column of output matrix. 229 | * No windowing of the data is done. This improves the accuracy. 230 | * The estimate is unbiased. Result matrix has dimension @n - @corr_ord by @corr_ord + 1. 231 | * 232 | * @s - input signal samples vector 233 | * @corr_ord - correlation order 234 | * @n - number of samples in @s 235 | * @inline_where - if not NULL then result array is created at this memory, otherwise 236 | * memory for result array is dynamically allocated 237 | * 238 | * TODO: Aalm option for computing autocorrelation matrix instead of covariance. 239 | * Convenient when a positive definite estimate is required. However, it is 240 | * biased estimator and it windows the data. 241 | */ 242 | double* lm_corr(double *s, uint16_t corr_ord, uint16_t n, double *inline_where); 243 | 244 | /** 245 | * Make a query to the LAPACK for optimal matrices dimensions. 246 | */ 247 | int lm_lapack_query(lm_detector_t *d, uint16_t x_n); 248 | 249 | /** 250 | * Detect number of frequencies using MUSIC algorithm. 251 | * 252 | * Use this function for detection of any number of signals. 253 | * 254 | * Return 0 on success, negative values on error: 255 | * -1 - bad input 256 | * -2 - couldn't get memory 257 | * -3 - generation of correlation matrix failed 258 | * -4 - call to LAPACKS dgesvd_ quering optimal values failed: the INFO-th argument had illegal value. 259 | * -5 - call to LAPACKS dgesvd_ quering optimal values failed: DBDSDC didn't converge, updating process failed. 260 | * -6 - @want_info is 1, but @detections contains less than Fs/2 entries 261 | * 262 | * @x - (in) array of the samples 263 | * @x_n - (in) number of samples in the array @x 264 | * @d - (in/out) on entry describes what is to be detected, on exit contains result 265 | */ 266 | int lm_detect(lm_detector_t *d, double *x, uint16_t x_n); 267 | 268 | /** 269 | * Make a decision if DTMF is detected. 270 | * 271 | * Return the index of the detected DTMFs. 0 if nothing has been detected. 272 | * 273 | * DTMF association to indices 274 | * 275 | * Tones: 276 | * 1209 Hz | 1336 Hz | 1477 Hz | 1633 Hz 277 | * 697 Hz '1' '2' '3' 'A' 278 | * 770 Hz '4' '5' '6' 'B' 279 | * 852 Hz '7' '8' '9' 'C' 280 | * 941 Hz '*' '0' '#' 'D' 281 | * 282 | * Indices 283 | * 1|2|3|4 284 | * 5|6|7|8 285 | * 9|10|11|12 286 | * 13|14|15|16 287 | */ 288 | uint8_t lm_dtmf_decision(lm_detector_t *d); 289 | 290 | /** 291 | * Detect DTMF frequencies using MUSIC algorithm. 292 | * 293 | * Use this function for detection of two frequencies. 294 | * Return the index of the detected DTMFs (1-16). 295 | * 0 if nothing has been detected. Negative value on error. 296 | * 297 | * DTMF association to indices 298 | * 299 | * Tones: 300 | * 1209 Hz | 1336 Hz | 1477 Hz | 1633 Hz 301 | * 697 Hz '1' '2' '3' 'A' 302 | * 770 Hz '4' '5' '6' 'B' 303 | * 852 Hz '7' '8' '9' 'C' 304 | * 941 Hz '*' '0' '#' 'D' 305 | * 306 | * Indices 307 | * 1|2|3|4 308 | * 5|6|7|8 309 | * 9|10|11|12 310 | * 13|14|15|16 311 | * 312 | * 313 | * @x - array of the samples 314 | * @x_n - number of samples in the array @x 315 | */ 316 | int lm_dtmf_detect(lm_detector_t *d, double *x, uint16_t x_n); 317 | 318 | void lm_deinit(lm_detector_t *d); 319 | 320 | /** 321 | * Detect frequencies using MUSIC algorithm. 322 | * 323 | * Use this function for detection of any number of signals. 324 | */ 325 | int lm_standard_init(lm_detector_t *d, uint16_t fs, uint16_t x_n, uint8_t signals_n, lm_detection_t *detections, uint16_t detections_n); 326 | 327 | void lm_standard_deinit(lm_detector_t *d); 328 | 329 | /** 330 | * Init DTMF descriptor for detection with MUSIC algorithm. 331 | * Allocate memory for SVD computation, configure info structure, 332 | * setup signals number and dimensions of U, S, V matrices. 333 | * 334 | * Return 0 on success, negative values on error. 335 | * -1 - bad input 336 | * -2 - couldn't get memory 337 | * -3 - call to LAPACKS dgesvd_ quering optimal values failed: the INFO-th argument had illegal value. 338 | * -4 - call to LAPACKS dgesvd_ quering optimal values failed: DBDSDC didn't converge, updating process failed. 339 | * 340 | * @d - detector to be initialised 341 | * @fs - sampling frequency 342 | * @x_n - detection block in samples (number of samples in a sample vector passed to lm_dtmf_detect) 343 | */ 344 | int lm_dtmf_init(lm_detector_t *d, uint16_t fs, uint16_t x_n); 345 | 346 | /** 347 | * Revert what lm_dtmf_init did: free memory. 348 | */ 349 | void lm_dtmf_deinit(lm_detector_t *d); 350 | 351 | /* LAPACK */ 352 | 353 | /* dgesvd_ prototype */ 354 | extern void dgesvd_(char* jobu, char* jobvt, int* m, int* n, double* a, int* lda, double* s, double* u, int* ldu, double* vt, int* ldvt, double* work, int* lwork, int* info); 355 | 356 | const char *lm_get_version_string(void); 357 | unsigned int lm_get_version(void); 358 | void lm_die(const char *s, const char *file, int line); 359 | 360 | #define lm_circ_inc(p) p += 1; \ 361 | p %= LM_LVL_GROUP_SIZE_MAX; 362 | 363 | #define lm_test(x, m) if (!(x)) lm_die(m, __FILE__, __LINE__); 364 | 365 | #define lm_min(x, y) ((x) < (y) ? (x) : (y)) 366 | #define lm_max(x, y) ((x) < (y) ? (y) : (x)) 367 | 368 | #define LM_FL_RCV 1u 369 | #define LM_FL_SND 2u 370 | 371 | #define lm_set_flag(p, f) (p)->flags |= (f) 372 | #define lm_clear_flag(p, f) (p)->flags &= ~(f) 373 | #define lm_test_flag(p, f) (p)->flags & (f) 374 | 375 | /* Transpose square matrix @a in place. */ 376 | void lm_transpose(double *a, int n); 377 | 378 | /* For testing. */ 379 | 380 | void lm_walk_32bit_arr_rowwise(uint32_t *arr, int m, int n, void (*cb)(uint32_t *)); 381 | void lm_walk_32bit_arr_colwise(uint32_t *arr, int m, int n, void (*cb)(uint32_t *)); 382 | void lm_walk_dbl_arr_rowwise(double *arr, int m, int n, void (*cb)(double *), void (*cb_row_end)(void)); 383 | void lm_walk_dbl_arr_colwise(double *arr, int m, int n, void (*cb)(double *), void (*cb_row_end)(void)); 384 | 385 | void lm_walk_dbl_arr2_rowwise(double **arr, int m, int n, void (*cb)(double *), void (*cb_row_end)(void)); 386 | 387 | /* Compute double EPSILON value on this machine. */ 388 | double lm_epsilon(void); 389 | 390 | /* Cmp both arrays rowwise. */ 391 | int lm_cmp_dbl_arr_rowwise(double *arr1, double *arr2, int m, int n); 392 | 393 | /* Cmp first array rowwise, second colwise. */ 394 | int lm_cmp_dbl_arr_row_col(double *arr1, double *arr2, int m, int n); 395 | 396 | /* Cmp first array on diagonal, second rowwise. */ 397 | int lm_cmp_dbl_arr_diag_row(double *arr1, double *arr2, int m, int n); 398 | 399 | #ifdef __cplusplus 400 | } 401 | #endif 402 | 403 | #endif /* LM_LM_H */ 404 | -------------------------------------------------------------------------------- /src/lm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #include "config.h" 39 | #include "lm.h" 40 | 41 | 42 | double* lm_corr(double *s, uint16_t corr_ord, uint16_t n, double *inline_where) 43 | { 44 | double *y = inline_where; 45 | uint16_t i = 0, j = 0; 46 | uint16_t ym = 0, yn = 0; 47 | double sq = 0; 48 | 49 | 50 | if (n < corr_ord) { 51 | return NULL; 52 | } 53 | 54 | ym = n - corr_ord; /* for 16 sample vector: corr_ord=7, ym=9, yn=8 */ 55 | yn = corr_ord + 1; 56 | sq = sqrt(n - corr_ord); 57 | 58 | if (y == NULL) { 59 | y = calloc(ym * yn, sizeof(double)); 60 | if (!y) { 61 | return NULL; 62 | } 63 | } 64 | 65 | /* Produce delayed frames, and swap columns, rightmost is delay == 0, leftmost is delay == corr_ord. 66 | * Scale them in place. */ 67 | 68 | i = 0; 69 | j = 0; 70 | while (i < ym) { 71 | j = 0; 72 | while (j < yn) { 73 | 74 | y[i * yn + yn - 1 - j] = s[(i + j) % n] / sq; 75 | ++j; 76 | } 77 | ++i; 78 | } 79 | 80 | return y; 81 | } 82 | 83 | int lm_lapack_query(lm_detector_t *d, uint16_t x_n) 84 | { 85 | /* Arguments to LAPACK's dgeslm_. 86 | * 87 | * DGESDD computes the singular value decomposition (SVD) of a real 88 | * M-by-N matrix A, optionally computing the left and right singular 89 | * vectors. If singular vectors are desired, it uses a 90 | * divide-and-conquer algorithm. 91 | * 92 | * The SVD is written 93 | * 94 | * A = U * SIGMA * transpose(V) 95 | * 96 | * where SIGMA is an M-by-N matrix which is zero except for its 97 | * min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and 98 | * V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA 99 | * are the singular values of A; they are real and non-negative, and 100 | * are returned in descending order. The first min(m,n) columns of 101 | * U and V are the left and right singular vectors of A. 102 | * 103 | * Note that the routine returns VT = V**T, not V. 104 | * 105 | * The divide and conquer algorithm makes very mild assumptions about 106 | * floating point arithmetic. It will work on machines with a guard 107 | * digit in alm/subtract, or on those binary machines without guard 108 | * digits which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or 109 | * Cray-2. It could conceivably fail on hexadecimal or decimal machines 110 | * without guard digits. 111 | */ 112 | char JOBU; /* JOBU is CHARACTER*1 113 | Specifies options for computing all or part of the matrix U: 114 | = 'A': all M columns of U are returned in array U: 115 | = 'S': the first min(m,n) columns of U (the left singular 116 | vectors) are returned in the array U; 117 | = 'O': the first min(m,n) columns of U (the left singular 118 | vectors) are overwritten on the array A; 119 | = 'N': no columns of U (no left singular vectors) are 120 | computed. */ 121 | char JOBVT; /* JOBVT is CHARACTER*1 122 | Specifies options for computing all or part of the matrix 123 | V**T: 124 | = 'A': all N rows of V**T are returned in the array VT; 125 | = 'S': the first min(m,n) rows of V**T (the right singular 126 | vectors) are returned in the array VT; 127 | = 'O': the first min(m,n) rows of V**T (the right singular 128 | vectors) are overwritten on the array A; 129 | = 'N': no rows of V**T (no right singular vectors) are 130 | computed. JOBVT and JOBU cannot both be 'O'. */ 131 | int M; /* M is INTEGER. The number of rows of the input matrix A. M >= 0. */ 132 | int N; /* N is INTEGER. The number of columns of the input matrix A. N >= 0. */ 133 | double *A, *A2; /* A is DOUBLE PRECISION array, dimension (LDA,N) 134 | On entry, the M-by-N matrix A. On exit, if JOBZ = 'O', 135 | A is overwritten with the first N columns of U (the left singular vectors, stored columnwise) if M >= N; 136 | A is overwritten with the first M rows of V**T (the right singular vectors, stored rowwise) otherwise. 137 | if JOBZ .ne. 'O', the contents of A are destroyed. */ 138 | int LDA; /* LDA is INTEGER. The leading dimension of the array A. LDA >= max(1,M). */ 139 | double *S; /* S is DOUBLE PRECISION array, dimension (min(M,N)). The singular values of A, sorted so that S(i) >= S(i+1). */ 140 | double *U; /* U is DOUBLE PRECISION array, dimension (LDU,UCOL) 141 | UCOL = M if JOBZ = 'A' or JOBZ = 'O' and M < N; 142 | UCOL = min(M,N) if JOBZ = 'S'. 143 | If JOBZ = 'A' or JOBZ = 'O' and M < N, U contains the M-by-M 144 | orthogonal matrix U; 145 | if JOBZ = 'S', U contains the first min(M,N) columns of U 146 | (the left singular vectors, stored columnwise); 147 | if JOBZ = 'O' and M >= N, or JOBZ = 'N', U is not referenced. */ 148 | int LDU; /* LDU is INTEGER 149 | The leading dimension of the array U. LDU >= 1; if 150 | JOBZ = 'S' or 'A' or JOBZ = 'O' and M < N, LDU >= M. */ 151 | double *VT; /* VT is DOUBLE PRECISION array, dimension (LDVT,N) 152 | If JOBZ = 'A' or JOBZ = 'O' and M >= N, VT contains the 153 | N-by-N orthogonal matrix V**T; 154 | if JOBZ = 'S', VT contains the first min(M,N) rows of 155 | V**T (the right singular vectors, stored rowwise); 156 | if JOBZ = 'O' and M < N, or JOBZ = 'N', VT is not referenced. */ 157 | int LDVT; /* LDVT is INTEGER 158 | The leading dimension of the array VT. LDVT >= 1; 159 | if JOBZ = 'A' or JOBZ = 'O' and M >= N, LDVT >= N; 160 | if JOBZ = 'S', LDVT >= min(M,N). */ 161 | double *WORK; /* WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) 162 | On exit, if INFO = 0, WORK(1) returns the optimal LWORK; */ 163 | int LWORK; /* LWORK is INTEGER 164 | The dimension of the array WORK. LWORK >= 1. 165 | If LWORK = -1, a workspace query is assumed. The optimal 166 | size for the WORK array is calculated and stored in WORK(1), 167 | and no other work except argument checking is performed. 168 | 169 | Let mx = max(M,N) and mn = min(M,N). 170 | If JOBZ = 'N', LWORK >= 3*mn + max( mx, 7*mn ). 171 | If JOBZ = 'O', LWORK >= 3*mn + max( mx, 5*mn*mn + 4*mn ). 172 | If JOBZ = 'S', LWORK >= 4*mn*mn + 7*mn. 173 | If JOBZ = 'A', LWORK >= 4*mn*mn + 6*mn + mx. 174 | These are not tight minimums in all cases; see comments inside code. 175 | For good performance, LWORK should generally be larger; 176 | a query is recommended. */ 177 | int INFO; /* INFO is INTEGER 178 | = 0: successful exit. 179 | < 0: if INFO = -i, the i-th argument had an illegal value. 180 | > 0: DBDSDC did not converge, updating process failed. */ 181 | double WORK_QUERY = 0; 182 | 183 | uint8_t corr_ord = 0; /* correlation order */ 184 | int ym = 0; /* correlation result matrix row dimension */ 185 | int yn = 0; /* correlation result matrix column dimension */ 186 | 187 | int i = 0, j = 0; 188 | 189 | 190 | if (d == NULL) { 191 | return -1; 192 | } 193 | 194 | corr_ord = 4 * d->signals_n - 1; 195 | ym = x_n - corr_ord; /* correlation result matrix row dimension */ 196 | yn = corr_ord + 1; /* correlation result matrix column dimension */ 197 | d->svd.M = ym; 198 | d->svd.N = yn; 199 | 200 | /* Alloc all memory needed in SVD computations. */ 201 | 202 | if (d->svd.A == NULL) { 203 | A = calloc(ym * yn, sizeof(double)); 204 | if (!A) { 205 | goto lm_m_l_q_fail_sys; 206 | } 207 | d->svd.A = A; 208 | } 209 | 210 | if (d->svd.A2 == NULL) { 211 | A2 = calloc(ym * yn, sizeof(double)); 212 | if (!A2) { 213 | goto lm_m_l_q_fail_sys; 214 | } 215 | d->svd.A2 = A2; 216 | } 217 | 218 | if (d->svd.S == NULL) { 219 | S = calloc(lm_min(ym, yn), sizeof(double)); 220 | if (!S) { 221 | goto lm_m_l_q_fail_sys; 222 | } 223 | d->svd.S = S; 224 | } 225 | 226 | if (d->svd.U == NULL) { 227 | U = calloc(ym * ym, sizeof(double)); 228 | if (!S) { 229 | goto lm_m_l_q_fail_sys; 230 | } 231 | d->svd.U = U; 232 | } 233 | 234 | if (d->svd.VT == NULL) { 235 | VT = calloc(yn * yn, sizeof(double)); 236 | if (!VT) { 237 | goto lm_m_l_q_fail_sys; 238 | } 239 | d->svd.VT = VT; 240 | } 241 | 242 | /* Call dgesvd_ with lwork = -1 to query optimal workspace size. */ 243 | 244 | WORK_QUERY = 0; 245 | d->svd.LDA = ym; 246 | d->svd.LDU = ym; 247 | d->svd.LDVT = yn; 248 | d->svd.LWORK = 4 * ym * yn * ym * yn + 6 * ym * yn + lm_max(ym, yn); 249 | 250 | LWORK = -1; 251 | dgesvd_(&d->svd.JOBU, &d->svd.JOBVT, &d->svd.M, &d->svd.N, d->svd.A, &d->svd.LDA, d->svd.S, d->svd.U, &d->svd.LDU, d->svd.VT, &d->svd.LDVT, &WORK_QUERY, &LWORK, &INFO); 252 | if (INFO != 0) { 253 | if (INFO < 0) { 254 | /* Error on LAPACK's dgesvd_ query: the INFO-th argument had illegal value. */ 255 | return -4; 256 | } else { 257 | /* Error on LAPACK's dgesvd_ query: "DBDSDC didn't converge, updating process failed. */ 258 | return -5; 259 | } 260 | } 261 | 262 | LWORK = (int) WORK_QUERY; 263 | 264 | if (d->svd.WORK == NULL) { 265 | WORK = calloc(LWORK, sizeof(double)); 266 | if (!WORK) { 267 | goto lm_m_l_q_fail_sys; 268 | } 269 | d->svd.WORK = WORK; 270 | } 271 | 272 | d->svd.LWORK = LWORK; 273 | 274 | if (d->detections == NULL) { 275 | 276 | /* By default span all frequencies up to half sampling rate. */ 277 | 278 | d->detections = calloc(d->fs / 2, sizeof(lm_detection_t)); 279 | if (!d->detections) { 280 | goto lm_m_l_q_fail_sys; 281 | } 282 | 283 | i = 0; 284 | while (i < d->fs / 2) { 285 | d->detections[i].freq = i; 286 | ++i; 287 | } 288 | 289 | d->detections_n = d->fs / 2; 290 | } 291 | 292 | if (d->a_real == NULL) { 293 | d->a_real = calloc(d->svd.N, sizeof(double)); 294 | if (!d->a_real) { 295 | goto lm_m_l_q_fail_sys; 296 | } 297 | } 298 | 299 | if (d->a_imag == NULL) { 300 | d->a_imag = calloc(d->svd.N, sizeof(double)); 301 | if (!d->a_imag) { 302 | goto lm_m_l_q_fail_sys; 303 | } 304 | } 305 | 306 | d->svd.prealloced = 1; 307 | 308 | return 0; 309 | 310 | lm_m_l_q_fail_sys: 311 | 312 | if (A != NULL) 313 | free(A); 314 | if (A2 != NULL) 315 | free(A2); 316 | if (S != NULL) 317 | free(S); 318 | if (U != NULL) 319 | free(U); 320 | if (VT != NULL) 321 | free(VT); 322 | if (WORK != NULL) 323 | free(WORK); 324 | 325 | return -2; 326 | } 327 | 328 | int lm_detect(lm_detector_t *d, double *x, uint16_t x_n) 329 | { 330 | int i = 0, j = 0; 331 | int INFO = 0; 332 | int err = 0; 333 | uint16_t corr_ord = 0; 334 | 335 | /* If want info. Peak detection */ 336 | double t = 0; 337 | double omega = 0; 338 | double av_real = 0; /* real result of a * V multiplication */ 339 | double av_imag = 0; /* imaginary result of a * V multiplication */ 340 | uint16_t f = 0; 341 | double peak = 0; 342 | double peak_min = DBL_MAX, peak_max = DBL_MIN; 343 | double peak_min_freq = DBL_MAX, peak_max_freq = DBL_MIN; 344 | uint16_t ym = 0; 345 | uint16_t yn = 0; 346 | 347 | 348 | if (d == NULL) { 349 | return -1; 350 | } 351 | 352 | corr_ord = 4 * d->signals_n - 1; 353 | 354 | /* Need to query LAPACK if it hasn't been done yet. */ 355 | 356 | if (d->svd.prealloced == 0) { 357 | 358 | d->svd.M = x_n - corr_ord; 359 | d->svd.N = corr_ord + 1; 360 | err = lm_lapack_query(d, x_n); 361 | if (err != 0) { 362 | return err; 363 | } 364 | } 365 | 366 | /* Compute correlation matrix. */ 367 | d->svd.A = lm_corr(x, corr_ord, x_n, d->svd.A); 368 | if (d->svd.A == NULL) { 369 | return -2; 370 | } 371 | 372 | ym = d->svd.M; 373 | yn = d->svd.N; 374 | 375 | /* LAPACKaize correlation matrix, LAPACK wants it colwise. 376 | * In A2 matric store correlation values collwise. */ 377 | for (i = 0; i < ym; ++i) { 378 | for (j = 0; j < d->svd.N; ++j) { 379 | d->svd.A2[j * ym + i] = d->svd.A[i * yn + j]; 380 | } 381 | } 382 | 383 | if (!d->detections) { 384 | 385 | /* By default span all frequencies up to half sampling rate. */ 386 | 387 | d->detections = calloc(d->fs / 2, sizeof(lm_detection_t)); 388 | if (!d->detections) { 389 | return -3; 390 | } 391 | 392 | i = 0; 393 | while (f < d->fs / 2) { 394 | d->detections[f].freq = i; 395 | ++i; 396 | } 397 | 398 | d->detections_n = d->fs / 2; 399 | } 400 | 401 | /* Compute SVD. */ 402 | dgesvd_(&d->svd.JOBU, &d->svd.JOBVT, &d->svd.M, &d->svd.N, d->svd.A2, &d->svd.LDA, d->svd.S, d->svd.U, &d->svd.LDU, d->svd.VT, &d->svd.LDVT, d->svd.WORK, &d->svd.LWORK, &INFO); 403 | if (INFO != 0) { 404 | if (INFO < 0) { 405 | /* Error on LAPACK's dgesvd_ query: the INFO-th argument had illegal value. */ 406 | return -4; 407 | } else { 408 | /* Error on LAPACK's dgesvd_ query: "DBDSDC didn't converge, updating process failed. */ 409 | return -5; 410 | } 411 | } 412 | 413 | /* Run peak detection through all detection frequencies. */ 414 | 415 | j = 0; 416 | peak_min = DBL_MAX; 417 | peak_max = DBL_MIN; 418 | 419 | while (j < d->detections_n) { 420 | 421 | f = d->detections[j].freq; 422 | 423 | i = 0; 424 | t = 0; 425 | while (i < yn) { 426 | omega = 2 * M_PI * f; 427 | d->a_real[i] = cos(omega * t); 428 | d->a_imag[i] = sin(omega * t); 429 | t += d->dt; 430 | ++i; 431 | } 432 | 433 | i = 0; 434 | av_real = av_imag = 0; 435 | while (i < yn) { 436 | av_real += d->a_real[i] * (d->svd.VT[4 + i * yn] + d->svd.VT[5 + i * yn] + d->svd.VT[6 + i * yn] + d->svd.VT[7 + i * yn]); 437 | av_imag += d->a_imag[i] * (d->svd.VT[4 + i * yn] + d->svd.VT[5 + i * yn] + d->svd.VT[6 + i * yn] + d->svd.VT[7 + i * yn]); 438 | ++i; 439 | } 440 | 441 | peak = 1 / (av_real * av_real + av_imag * av_imag); 442 | d->detections[j].peak = peak; 443 | 444 | if (peak > peak_max) { 445 | peak_max = peak; 446 | peak_max_freq = f; 447 | } 448 | if (peak < peak_min) { 449 | peak_min = peak; 450 | peak_min_freq = f; 451 | } 452 | 453 | if (f == 697) { 454 | d->info.peak_697 = peak; 455 | } else if (f == 770) { 456 | d->info.peak_770 = peak; 457 | } else if (f == 852) { 458 | d->info.peak_852 = peak; 459 | } else if (f == 941) { 460 | d->info.peak_941 = peak; 461 | } 462 | 463 | if (f == 1209) { 464 | d->info.peak_1209 = peak; 465 | } else if (f == 1336) { 466 | d->info.peak_1336 = peak; 467 | } else if (f == 1477) { 468 | d->info.peak_1477 = peak; 469 | } else if (f == 1633) { 470 | d->info.peak_1633 = peak; 471 | } 472 | 473 | j++; 474 | } 475 | 476 | d->info.peak_max = peak_max; 477 | d->info.peak_max_freq = peak_max_freq; 478 | d->info.peak_min = peak_min; 479 | d->info.peak_min_freq = peak_min_freq; 480 | 481 | 482 | return 0; 483 | 484 | lm_d_m_fail_sys: 485 | 486 | return -2; 487 | } 488 | 489 | uint8_t lm_dtmf_decision(lm_detector_t *d) 490 | { 491 | uint8_t dtmf_idx = 0; 492 | double peak_diff = 0; 493 | double peak = DBL_MIN; 494 | 495 | uint8_t i = 0; 496 | uint8_t peak_row_idx = 0; 497 | uint8_t peak_col_idx = 4; 498 | 499 | uint8_t peak_row_idx2 = 0; 500 | uint8_t peak_col_idx2 = 4; 501 | 502 | double row_val = 0; 503 | double col_val = 0; 504 | 505 | double row_val2 = 0; 506 | double col_val2 = 0; 507 | 508 | double row_2_peak = 0; 509 | double col_2_peak = 0; 510 | 511 | double dtmf_peak_energy = 0; /* Energy of two maximum DTMF frequencies in the segment. */ 512 | double total_dtmf_energy = 0; /* Energy of all DTMF frequencies. */ 513 | double energy_ratio = 0; /* Ratio of above: Amount of two max DTMF freq in total energy of all DTMF frequencies. */ 514 | 515 | 516 | if (!d || !d->detections || d->detections_n < 1) { 517 | return 0; 518 | } 519 | 520 | 521 | /** 522 | * Check if there are 2 DTMF frequencies with peak above peak threshold: 523 | * one frequency must belong to the row frequencies, other to the column 524 | * frequencies. 525 | */ 526 | 527 | /* Search for maximum peak in row frequencies. */ 528 | i = 0; 529 | peak = d->info.peak_min; 530 | while (i < 4) { 531 | 532 | /* Add energy to total energy calculation. */ 533 | total_dtmf_energy += d->detections[i].peak; 534 | 535 | if (d->detections[i].peak > peak) { 536 | peak = d->detections[i].peak; 537 | peak_row_idx = i; 538 | } else { 539 | if (d->detections[i].peak > row_2_peak) { 540 | row_2_peak = d->detections[i].peak; 541 | peak_row_idx2 = i; 542 | } 543 | } 544 | ++i; 545 | } 546 | 547 | /* Search for maximum peak in column frequencies. */ 548 | peak = d->info.peak_min; 549 | while (i < 8) { 550 | 551 | /* Add energy to total energy calculation. */ 552 | total_dtmf_energy += d->detections[i].peak; 553 | 554 | if (d->detections[i].peak > peak) { 555 | peak = d->detections[i].peak; 556 | peak_col_idx = i - 4; 557 | } else { 558 | if (d->detections[i].peak > col_2_peak) { 559 | col_2_peak = d->detections[i].peak; 560 | peak_col_idx2 = i - 4; 561 | } 562 | } 563 | ++i; 564 | } 565 | 566 | row_val = d->detections[peak_row_idx].peak / d->info.peak_min; 567 | col_val = d->detections[4 + peak_col_idx].peak / d->info.peak_min; 568 | row_val2 = d->detections[peak_row_idx2].peak / d->info.peak_min; 569 | col_val2 = d->detections[4 + peak_col_idx2].peak / d->info.peak_min; 570 | 571 | /* Calculate energy of maximum DTMF frequencies. */ 572 | dtmf_peak_energy = d->detections[peak_row_idx].peak; 573 | dtmf_peak_energy += d->detections[4 + peak_col_idx].peak; 574 | 575 | if (total_dtmf_energy < 0.001) { 576 | total_dtmf_energy = 0.001; 577 | } 578 | 579 | energy_ratio = dtmf_peak_energy / total_dtmf_energy; 580 | 581 | #if DEBUG_LM 582 | printf("lmusic: peak max=%f min=%f, max row/col: %f/%f max2 row/col: %f/%f, val row/col: %f/%f val2 row/col: %f/%f, power diff=%f, energy[t:%f dtmf:%f d/t:%f] digit candidate = '%c'\n", 583 | d->info.peak_max, d->info.peak_min, 584 | d->detections[peak_row_idx].peak, d->detections[4 + peak_col_idx].peak, 585 | d->detections[peak_row_idx2].peak, d->detections[4 + peak_col_idx2].peak, 586 | row_val, col_val, row_val2, col_val2, fabs(row_val / col_val), 587 | total_dtmf_energy, dtmf_peak_energy, energy_ratio, 588 | lm_dtmf_idx_2_char[1 + peak_row_idx * 4 + peak_col_idx]); 589 | #endif 590 | 591 | /* Both peaks must be above threshold. */ 592 | if ((row_val < d->peak_max_threshold1) 593 | || (col_val < d->peak_max_threshold1)) { 594 | 595 | #if DEBUG_LM 596 | printf("libmusic: rejected due to energy threshold1 check\n"); 597 | #endif 598 | return 0; 599 | } 600 | 601 | /* Both peaks must be more than CROSSTALK times second peaks. */ 602 | if ((row_val / row_val2 < LM_DTMF_CROSSTALK_THRESHOLD) 603 | || (col_val / col_val2 < LM_DTMF_CROSSTALK_THRESHOLD)) { 604 | 605 | #if DEBUG_LM 606 | printf("libmusic: rejected due to CROSSTALK check\n"); 607 | #endif 608 | return 0; 609 | } 610 | 611 | /* ITU Q-24 Recommendation for a power difference between frequencies 612 | * imposes 10dB maximum tollerance. */ 613 | if (fabs(row_val / col_val) > LM_DTMF_FREQ_POWER_DIFF_THRESHOLD) { 614 | 615 | #if DEBUG_LM 616 | printf("libmusic: rejected due to ITU Q-24 10dB check\n"); 617 | #endif 618 | return 0; 619 | } 620 | 621 | /* Energy check. */ 622 | if (energy_ratio < LM_DTMF_ENERGY_RATIO) { 623 | 624 | #if DEBUG_LM 625 | printf("libmusic: rejected due to energy check\n"); 626 | #endif 627 | return 0; 628 | } 629 | 630 | /** 631 | * DTMF association to indices 632 | * 633 | * Tones: 634 | * 1209 Hz | 1336 Hz | 1477 Hz | 1633 Hz 635 | * 697 Hz '1' '2' '3' 'A' 636 | * 770 Hz '4' '5' '6' 'B' 637 | * 852 Hz '7' '8' '9' 'C' 638 | * 941 Hz '*' '0' '#' 'D' 639 | * 640 | * Indices 641 | * 1|2|3|4 642 | * 5|6|7|8 643 | * 9|10|11|12 644 | * 13|14|15|16 645 | */ 646 | dtmf_idx = 1 + peak_row_idx * 4 + peak_col_idx; 647 | 648 | return dtmf_idx; 649 | } 650 | 651 | int lm_dtmf_detect(lm_detector_t *d, double *x, uint16_t x_n) 652 | { 653 | uint8_t idx = 0; 654 | 655 | if (!d) { 656 | return -1; 657 | } 658 | 659 | if (lm_detect(d, x, x_n) != 0) { 660 | return -2; 661 | } 662 | 663 | return (int) lm_dtmf_decision(d); 664 | } 665 | 666 | /* 667 | int lm_init(lm_detector_t *d, uint16_t fs, uint16_t x_n) 668 | { 669 | int corr_ord = 4 * d->signals_n - 1; / Detector must have number of expected signals set. / 670 | int ym = x_n - corr_ord; / correlation result matrix row dimension, for 16 sample vector: 9 / 671 | int yn = corr_ord + 1; / correlation result matrix column dimension, for 16 sample vector: 8 / 672 | 673 | double *VT = NULL; 674 | double *WORK = NULL; 675 | double WORK_QUERY = 0; 676 | double *A = NULL; / Input to SVD: correlation matrix columnwise for LAPACK. / 677 | double *S = NULL; 678 | double *U = NULL; 679 | int INFO = 0; 680 | 681 | int i = 0, j = 0; 682 | 683 | 684 | if (!d) { 685 | return -1; 686 | } 687 | 688 | d->fs = fs; 689 | d->x_n = x_n; 690 | d->dt = dt; 691 | 692 | d->prealloced = 1; 693 | d->svd.JOBU = 'N'; 694 | d->svd.JOBVT = 'A'; 695 | d->svd.LWORK = 4 * M * N * M *N + 6 * M * N + lm_max(M, N); 696 | d->svd.M = ym; 697 | d->svd.N = yn; 698 | d->svd.LDVT = yn; 699 | d->svd.LDA = ym; 700 | d->svd.LDU = ym; 701 | 702 | / Call dgesvd_ with lwork = -1 to query optimal workspace size. / 703 | 704 | / prealloc space for the correlation matrix / 705 | A = calloc(ym * yn, sizeof(double)); 706 | if (!A) { 707 | goto lm_d_m_d_i_fail_sys; 708 | } 709 | 710 | S = calloc(lm_min(ym, yn), sizeof(double)); 711 | if (!S) { 712 | goto lm_d_m_d_i_fail_sys; 713 | } 714 | 715 | VT = calloc(yn * yn, sizeof(double)); 716 | if (!VT) { 717 | goto lm_d_m_d_i_fail_sys; 718 | } 719 | 720 | LWORK = -1; 721 | dgesvd_(&d->svd.JOBU, &d->svd.JOBVT, &d->svd.M, &d->svd.N, A, &d->svd.LDA, S, U, &d->svd.LDU, VT, &d->svd.LDVT, &WORK_QUERY, &d->svd.LWORK, &INFO); 722 | if (INFO != 0) { 723 | if (INFO < 0) { 724 | /Error on LAPACK's dgesvd_ query: the INFO-th argument had illegal value. / 725 | return -3; 726 | } else { 727 | /Error on LAPACK's dgesvd_ query: "DBDSDC didn't converge, updating process failed. / 728 | return -4; 729 | } 730 | } 731 | 732 | d->svd.LWORK = (int) WORK_QUERY; 733 | WORK = calloc(d->svd.LWORK, sizeof(double)); 734 | if (!WORK) { 735 | goto lm_d_m_d_i_fail_sys; 736 | } 737 | 738 | d->svd.WORK = WORK; 739 | d->svd.A = A; 740 | d->svd.U = NULL; 741 | d->svd.S = S; 742 | d->svd.VT = VT; 743 | 744 | if (d.want_info) { 745 | 746 | / if want to know max and min peak / 747 | detections = calloc(fs/2, sizeof(lm_detection_t)); 748 | if (!detections) { 749 | return -2; 750 | } 751 | 752 | i = 0; 753 | while (i < fs/2) { 754 | detections[i].freq = i; 755 | ++i; 756 | } 757 | } 758 | 759 | d->detections = &detections[0]; 760 | 761 | return 0; 762 | 763 | lm_d_m_d_i_fail_sys: 764 | 765 | if (A != NULL) 766 | free(A); 767 | if (Y != NULL) 768 | free(Y); 769 | if (S != NULL) 770 | free(S); 771 | if (U != NULL) 772 | free(U); 773 | if (VT != NULL && !(d->want_evec)) 774 | free(VT); 775 | return -2; 776 | } 777 | */ 778 | 779 | void lm_deinit(lm_detector_t *d) 780 | { 781 | if (!d) { 782 | return; 783 | } 784 | 785 | if (d->svd.A != NULL) { 786 | free(d->svd.A); 787 | d->svd.A = NULL; 788 | } 789 | if (d->svd.A2 != NULL) { 790 | free(d->svd.A2); 791 | d->svd.A2 = NULL; 792 | } 793 | if (d->svd.S != NULL) { 794 | free(d->svd.S); 795 | d->svd.S = NULL; 796 | } 797 | if (d->svd.U != NULL) { 798 | free(d->svd.U); 799 | d->svd.U = NULL; 800 | } 801 | if (d->svd.VT != NULL) { 802 | free(d->svd.VT); 803 | d->svd.VT = NULL; 804 | } 805 | if (d->svd.WORK != NULL) { 806 | free(d->svd.WORK); 807 | d->svd.WORK = NULL; 808 | } 809 | if (d->detections != NULL) { 810 | free(d->detections); 811 | d->detections = NULL; 812 | } 813 | if (d->a_real != NULL) { 814 | free(d->a_real); 815 | d->a_real = NULL; 816 | } 817 | if (d->a_imag != NULL) { 818 | free(d->a_imag); 819 | d->a_imag = NULL; 820 | } 821 | } 822 | 823 | int lm_standard_init(lm_detector_t *d, uint16_t fs, uint16_t x_n, uint8_t signals_n, lm_detection_t *detections, uint16_t detections_n) 824 | { 825 | int f = 0; 826 | 827 | 828 | if (!d) { 829 | return -1; 830 | } 831 | 832 | memset(d, 0, sizeof(lm_detector_t)); 833 | 834 | d->svd.prealloced = 0; 835 | d->svd.JOBU = 'N'; 836 | d->svd.JOBVT = 'A'; 837 | 838 | d->fs = fs; 839 | d->dt = 1.0 / (double) fs; 840 | d->x_n = x_n; 841 | 842 | d->signals_n = signals_n; /* we are searching for @signals_n number of frequencies */ 843 | 844 | if (!detections) { 845 | 846 | /* By default span all frequencies up to half sampling rate. */ 847 | 848 | d->detections = calloc(fs / 2, sizeof(lm_detection_t)); 849 | if (!d->detections) { 850 | return -2; 851 | } 852 | 853 | f = 0; 854 | while (f < fs / 2) { 855 | d->detections[f].freq = f; 856 | ++f; 857 | } 858 | 859 | d->detections_n = fs / 2; 860 | } else { 861 | d->detections = detections; 862 | d->detections_n = detections_n; 863 | } 864 | 865 | /* Complete the setup. */ 866 | return lm_lapack_query(d, x_n); 867 | } 868 | 869 | void lm_standard_deinit(lm_detector_t *d) 870 | { 871 | lm_deinit(d); 872 | } 873 | 874 | int lm_dtmf_init(lm_detector_t *d, uint16_t fs, uint16_t x_n) 875 | { 876 | int f = 0; 877 | 878 | 879 | if (!d) { 880 | return -1; 881 | } 882 | 883 | memset(d, 0, sizeof(lm_detector_t)); 884 | 885 | d->svd.prealloced = 0; 886 | d->svd.JOBU = 'N'; 887 | d->svd.JOBVT = 'A'; 888 | 889 | d->fs = fs; 890 | d->dt = 1.0 / (double) fs; 891 | d->x_n = x_n; 892 | 893 | d->signals_n = 2; /* we are searching for 2 frequencies */ 894 | 895 | d->peak_max_threshold1 = LM_DTMF_PEAK_THRESHOLD1; 896 | d->peak_max_threshold2 = LM_DTMF_PEAK_THRESHOLD2; 897 | d->peak_max_threshold3 = LM_DTMF_PEAK_THRESHOLD3; 898 | 899 | /* Configure detection at DTMF frequencies. */ 900 | 901 | d->detections = calloc(8, sizeof(lm_detection_t)); 902 | if (!d->detections) { 903 | return -2; 904 | } 905 | 906 | f = 0; 907 | while (f < 8) { 908 | d->detections[f].freq = lm_dtmf_freqs[f]; 909 | ++f; 910 | } 911 | 912 | d->detections_n = 8; 913 | 914 | /* DTMF detection searches for any combination of the two DTMF frequencies. */ 915 | return lm_lapack_query(d, x_n); 916 | } 917 | 918 | void lm_dtmf_deinit(lm_detector_t *d) 919 | { 920 | lm_deinit(d); 921 | } 922 | 923 | const char *lm_get_version_string(void) 924 | { 925 | /* 926 | * Simply return the autotools generated string. 927 | */ 928 | return LM_VER_STRING; 929 | } 930 | 931 | unsigned int lm_get_version(void) 932 | { 933 | uint32_t major = 0, minor = 0, micro = 0; 934 | uint32_t rv = 0; 935 | int parse_rv; 936 | 937 | /* 938 | * Parse the autotools generated version. 939 | */ 940 | parse_rv = sscanf(LM_VERSION, "%u.%u.%u", &major, &minor, µ); 941 | if (parse_rv != 3) { 942 | /* 943 | * We're expected to parse all 3 version levels. 944 | * If not, then this must not be an official release. 945 | * Return all zeros on the version 946 | */ 947 | return 0; 948 | } 949 | 950 | /* 951 | * We allow 8 bits for the major and minor, while 952 | * allowing 16 bits for the micro. 16 bits for the micro 953 | * may be beneficial for a continuous delivery model 954 | * in the future. 955 | */ 956 | rv |= (major & 0xFF) << 24; 957 | rv |= (minor & 0xFF) << 16; 958 | rv |= micro & 0xFFFF; 959 | return rv; 960 | } 961 | 962 | void lm_transpose(double *a, int n) 963 | { 964 | int i = 0, j = 0; 965 | double tmp = 0; 966 | 967 | while (i < n) { 968 | j = i; 969 | while (j < n) { 970 | tmp = a[i * n + j]; 971 | a[i * n + j] = a[j * n + i]; 972 | a[j * n + i] = tmp; 973 | ++j; 974 | } 975 | ++i; 976 | } 977 | } 978 | 979 | /* Testing. */ 980 | 981 | void lm_die(const char *reason, const char *file, int line) 982 | { 983 | fprintf(stderr, "Test failed: %s. %s:%d\n", reason, file, line); 984 | exit(EXIT_FAILURE); 985 | } 986 | 987 | void lm_walk_32bit_arr_rowwise(uint32_t *arr, int m, int n, void (*cb)(uint32_t *)) 988 | { 989 | int i = 0, j = 0; 990 | 991 | while (i < m) { 992 | j = 0; 993 | while (j < n) { 994 | cb(&arr[i * n + j]); 995 | ++j; 996 | } 997 | ++i; 998 | } 999 | } 1000 | 1001 | void lm_walk_32bit_arr_colwise(uint32_t *arr, int m, int n, void (*cb)(uint32_t *)) 1002 | { 1003 | int i = 0, j = 0; 1004 | 1005 | while (j < n) { 1006 | i = 0; 1007 | while (i < m) { 1008 | cb(&arr[i * n + j]); 1009 | ++i; 1010 | } 1011 | ++j; 1012 | } 1013 | } 1014 | 1015 | void lm_walk_dbl_arr_rowwise(double *arr, int m, int n, void (*cb)(double *), void (*cb_row_end)(void)) 1016 | { 1017 | int i = 0, j = 0; 1018 | 1019 | while (i < m) { 1020 | j = 0; 1021 | while (j < n) { 1022 | cb(&arr[i * n + j]); 1023 | ++j; 1024 | } 1025 | cb_row_end(); 1026 | ++i; 1027 | } 1028 | } 1029 | 1030 | void lm_walk_dbl_arr_colwise(double *arr, int m, int n, void (*cb)(double *), void (*cb_col_end)(void)) 1031 | { 1032 | int i = 0, j = 0; 1033 | 1034 | while (j < n) { 1035 | i = 0; 1036 | while (i < m) { 1037 | cb(&arr[i * n + j]); 1038 | ++i; 1039 | } 1040 | cb_col_end(); 1041 | ++j; 1042 | } 1043 | } 1044 | 1045 | void lm_walk_dbl_arr2_rowwise(double **arr, int m, int n, void (*cb)(double *), void (*cb_row_end)(void)) 1046 | { 1047 | int i = 0, j = 0; 1048 | 1049 | while (i < m) { 1050 | j = 0; 1051 | while (j < n) { 1052 | cb(&arr[i][j]); 1053 | ++j; 1054 | } 1055 | cb_row_end(); 1056 | ++i; 1057 | } 1058 | } 1059 | 1060 | double lm_epsilon(void) { 1061 | typedef union { 1062 | long long i64; 1063 | double d64; 1064 | } dbl_64; 1065 | 1066 | dbl_64 s; 1067 | 1068 | s.d64 = 1.; 1069 | s.i64++; 1070 | return (s.d64 - 1.); 1071 | } 1072 | 1073 | int lm_cmp_dbl_arr_rowwise(double *arr1, double *arr2, int m, int n) 1074 | { 1075 | int i = 0, j = 0; 1076 | double epsilon = lm_epsilon(); 1077 | 1078 | while (i < m) { 1079 | j = 0; 1080 | while (j < n) { 1081 | if (fabs(arr1[i * n + j] - arr2[i * n + j]) > epsilon) { 1082 | return -1; 1083 | } 1084 | ++j; 1085 | } 1086 | ++i; 1087 | } 1088 | 1089 | return 0; 1090 | } 1091 | 1092 | int lm_cmp_dbl_arr_row_col(double *arr1, double *arr2, int m, int n) 1093 | { 1094 | int i = 0, j = 0; 1095 | double epsilon = 0.0000005; 1096 | double a, b; 1097 | 1098 | fprintf(stderr, "epsilon: %.20f\n", epsilon); 1099 | while (i < m) { 1100 | j = 0; 1101 | while (j < n) { 1102 | a = arr1[i * n + j]; 1103 | b = arr2[i + j * n]; 1104 | fprintf(stderr, "[%d][%f]=?[%d][%f]\t[%.20f][%.20f]\n", i*n+j, arr1[i*n+j], i + j * n, arr2[i + j * n], a - b, fabs(a-b)); 1105 | if (fabs(arr1[i * n + j] - arr2[i + j * n]) > epsilon) { 1106 | return -1; 1107 | } 1108 | ++j; 1109 | } 1110 | ++i; 1111 | } 1112 | 1113 | return 0; 1114 | } 1115 | 1116 | int lm_cmp_dbl_arr_diag_row(double *arr1, double *arr2, int m, int n) 1117 | { 1118 | int i = 0; 1119 | double epsilon = 0.0000001; 1120 | double a, b; 1121 | 1122 | fprintf(stderr, "epsilon: %.20f\n", epsilon); 1123 | while (i < m) { 1124 | a = arr1[i * n + i]; 1125 | b = arr2[i]; 1126 | fprintf(stderr, "[%d][%f]=?[%d][%f]\t[%.20f][%.20f]\n", i*n+i, arr1[i*n+i], i, arr2[i], a - b, fabs(a-b)); 1127 | if (fabs(a - b) > epsilon) { 1128 | return -1; 1129 | } 1130 | ++i; 1131 | } 1132 | 1133 | return 0; 1134 | } 1135 | -------------------------------------------------------------------------------- /src/lmtool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Detect DTMF tones using lmusic. 49 | * 50 | * Input is external file. 51 | */ 52 | 53 | #define Fs 8000 54 | #define XN 16 55 | 56 | 57 | void usage(void) 58 | { 59 | fprintf(stderr, "\nUsage:\n"); 60 | fprintf(stderr, "lmtool \n\n"); 61 | fprintf(stderr, "Example:\n\tlmtool fraction-131-140_16bit_s.raw\n\n"); 62 | } 63 | 64 | typedef struct lmtool_detection_state_s { 65 | int dtmf_idx; 66 | size_t dtmf_start; 67 | } lmtool_detection_state_t; 68 | 69 | static void lmtool_update_detection_state(lmtool_detection_state_t *state, int dtmf_idx, size_t sample_n) 70 | { 71 | if (dtmf_idx != state->dtmf_idx) { 72 | 73 | if (dtmf_idx == 0 || (state->dtmf_idx > 0)) { 74 | 75 | /** 76 | * Record DTMF end. 77 | */ 78 | fprintf(stderr, "DTMF end. Digit=%c, sample=%zu, duration=%zums\n", lm_dtmf_idx_2_char[state->dtmf_idx], sample_n, (sample_n - state->dtmf_start) * 1000/Fs); 79 | state->dtmf_start = 0; 80 | } 81 | 82 | if (dtmf_idx > 0) { 83 | 84 | /** 85 | * Record DTMF start. 86 | */ 87 | fprintf(stderr, "DTMF start. Digit=%c, sample=%zu\n", lm_dtmf_idx_2_char[dtmf_idx], sample_n); 88 | 89 | state->dtmf_start = sample_n; 90 | } 91 | 92 | state->dtmf_idx = dtmf_idx; 93 | } 94 | } 95 | 96 | // convert 16 bit ints to doubles 97 | void int2float(int16_t *input, double *output, int length) 98 | { 99 | int i; 100 | 101 | for (i = 0; i < length; i++) { 102 | output[i] = (double)input[i] / ((double) 1.0); 103 | } 104 | } 105 | 106 | int main (int argc, char **argv) 107 | { 108 | int16_t data[XN]; 109 | int16_t buffer[2*XN]; 110 | double sample_vector[2*XN]; 111 | size_t word_n = 0; 112 | size_t sample_n = 0; 113 | const char *input_file_name = NULL; 114 | FILE *fin = NULL; 115 | size_t frame_n = 0; 116 | 117 | lm_detector_t d = { 0 }; 118 | int dtmf_idx = 0; 119 | lmtool_detection_state_t detection_state = { 0 }; 120 | 121 | 122 | if (argc != 2) { 123 | goto lmtoolhelp; 124 | } 125 | 126 | input_file_name = argv[1]; 127 | 128 | fprintf(stderr, "\nInput= %s\n", input_file_name); 129 | 130 | fin = fopen(input_file_name,"rb"); 131 | if (!fin) { 132 | fprintf(stderr, "Cannot open input file %s\n", input_file_name); 133 | goto lmtoolerr; 134 | } 135 | 136 | /** 137 | * Init standard MUSIC DTMF detector. 138 | * Detects only 8 DTMF frequencies. 139 | */ 140 | 141 | if (lm_dtmf_init(&d, Fs, XN) != 0) { 142 | fprintf(stderr, "Cannot initialise lmusic descriptor.\n"); 143 | goto lmtoolerr; 144 | } 145 | 146 | sample_n = 0; 147 | 148 | word_n = fread(data, sizeof(int16_t), XN, fin); 149 | 150 | // Shift previous 151 | memcpy(&buffer[0], &buffer[XN], XN*sizeof(int16_t)); 152 | // Append new 153 | memcpy(&buffer[XN], data, XN*sizeof(int16_t)); 154 | 155 | while(word_n == XN) 156 | { 157 | int k = 0; 158 | frame_n++; 159 | 160 | if (frame_n == 1) { 161 | goto read_next; 162 | } 163 | 164 | int2float(buffer, sample_vector, 2*word_n); 165 | 166 | while (k < XN) { 167 | dtmf_idx = lm_dtmf_detect(&d, &sample_vector[k], XN); 168 | if (dtmf_idx < 0) { 169 | fprintf(stderr, "Error. lm_dtmf_detect failed with error %d\n", dtmf_idx); 170 | goto lmtoolerr; 171 | } 172 | k++; 173 | ++sample_n; 174 | 175 | /* Update detection state. */ 176 | lmtool_update_detection_state(&detection_state, dtmf_idx, sample_n); 177 | } 178 | 179 | read_next: 180 | 181 | /* Read next frame. */ 182 | word_n = fread(data, sizeof(int16_t), XN, fin); 183 | 184 | // Shift previous 185 | memcpy(&buffer[0], &buffer[XN], XN*sizeof(int16_t)); 186 | // Append new 187 | memcpy(&buffer[XN], data, XN*sizeof(int16_t)); 188 | } 189 | 190 | /* Run 0 through process to finish DTMF detection reports. */ 191 | lmtool_update_detection_state(&detection_state, 0, sample_n); 192 | 193 | fprintf(stderr, "\nFinished. sampleCount = %zu\n", sample_n); 194 | fclose(fin); 195 | 196 | lm_dtmf_deinit(&d); 197 | 198 | return 0; 199 | 200 | lmtoolhelp: 201 | usage(); 202 | return EXIT_FAILURE; 203 | 204 | lmtoolerr: 205 | return EXIT_FAILURE; 206 | } 207 | -------------------------------------------------------------------------------- /test/lmtest1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test library version. 49 | */ 50 | int main (void) 51 | { 52 | fprintf(stdout, "libmusic version string: %s\n", lm_get_version_string()); 53 | 54 | uint32_t v = lm_get_version(); 55 | fprintf(stdout, "libmusic version: %u (%u.%u.%u)\n", v, (v >> 24) & 0xFF, (v >> 16) & 0xFF, (v & 0xFFFF)); 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /test/lmtest10.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test of DTMF decision making with MUSIC agorithm, lm_dtmf_detect. 49 | * 50 | * Input is DTMF "1" = { 697 Hz, 1209 Hz }. 8000 Hz, 16 samples: [10, 25]. 51 | */ 52 | 53 | #define Fs 8000 54 | #define XN 16 55 | 56 | double ref_array_X[XN] = { -0.2071, -0.7942, -1.1108, -0.6395, 0.5197, 1.6468, 1.9312, 1.1106, -0.3025, -1.3984, -1.5514, -0.8580, 0.0096, 0.3922, 0.1753, -0.1748 }; 57 | 58 | 59 | int main (int argc, char **argv) 60 | { 61 | int dtmf = 0; 62 | 63 | 64 | lm_detector_t d; 65 | 66 | /** 67 | * Init standard MUSIC DTMF detector. 68 | * Detects only 8 DTMF frequencies. 69 | */ 70 | lm_test(lm_dtmf_init(&d, Fs, XN) == 0, "lm_dtmf_init failed"); 71 | 72 | dtmf = lm_dtmf_detect(&d, ref_array_X, XN); 73 | if (dtmf != 1) { 74 | printf("lm_dtmf_detect failed got=%d but expected=1\n", dtmf); 75 | goto lmt10_fail_sys; 76 | } 77 | lm_test(dtmf == 1, "lm_dtmf_detect failed"); 78 | 79 | /* Peak detection */ 80 | 81 | printf(".info.peak_min = %f\n", d.info.peak_min); 82 | printf(".info.peak_max = %f\n", d.info.peak_max); 83 | printf(".info.peak_min_freq = %u\n", d.info.peak_min_freq); 84 | printf(".info.peak_max_freq = %u\n", d.info.peak_max_freq); 85 | printf(".info.peak_697 = %f\n", d.info.peak_697); 86 | printf(".info.peak_770 = %f\n", d.info.peak_770); 87 | printf(".info.peak_852 = %f\n", d.info.peak_852); 88 | printf(".info.peak_941 = %f\n", d.info.peak_941); 89 | printf(".info.peak_1209 = %f\n", d.info.peak_1209); 90 | printf(".info.peak_1336 = %f\n", d.info.peak_1336); 91 | printf(".info.peak_1477 = %f\n", d.info.peak_1477); 92 | printf(".info.peak_1633 = %f\n", d.info.peak_1633); 93 | 94 | lm_deinit(&d); 95 | 96 | return 0; 97 | 98 | lmt10_fail_sys: 99 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 100 | return -1; 101 | } 102 | -------------------------------------------------------------------------------- /test/lmtest11.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test of MUSIC agorithm. 49 | * 50 | * Input is external file. 51 | */ 52 | 53 | #define Fs 8000 54 | #define XN 16 55 | 56 | 57 | // convert 16 bit ints to doubles 58 | void intToFloat(int16_t *input, double *output, int length) 59 | { 60 | int i; 61 | 62 | for (i = 0; i < length; i++) { 63 | output[i] = (double)input[i] / ((double) 1.0); 64 | } 65 | } 66 | 67 | int main (int argc, char **argv) 68 | { 69 | uint16_t x[XN]; /* samples */ 70 | int i = 0, j = 0; 71 | 72 | int res; 73 | 74 | /* Testing data */ 75 | int16_t intData[XN]; 76 | double inputData[XN]; 77 | int numWords = 0; 78 | int sampleCount = 0; 79 | const char *inFileName = "fraction-131-140_16bit_s.raw"; 80 | FILE *inFile = NULL; 81 | uint32_t frame_n = 0; 82 | 83 | lm_detector_t d; 84 | int dtmf = 0; 85 | uint16_t dtmf_row = 0; 86 | uint16_t dtmf_col = 0; 87 | 88 | 89 | /** 90 | * Init standard MUSIC DTMF detector. 91 | * Detects only 8 DTMF frequencies. 92 | */ 93 | lm_test(lm_dtmf_init(&d, Fs, XN) == 0, "lm_dtmf_init failed"); 94 | 95 | if (argc == 2) { 96 | inFileName = argv[1]; 97 | } 98 | 99 | printf("input file name = %s\n",inFileName); 100 | 101 | inFile = fopen(inFileName,"rb"); 102 | if (!inFile) { 103 | printf("Cannot open input file %s\n",inFileName); 104 | goto lmt11_fail_sys; 105 | } 106 | 107 | sampleCount = 0; 108 | 109 | numWords = fread(intData, sizeof(int16_t), XN, inFile); 110 | 111 | // until end of file 112 | while(numWords == XN) 113 | { 114 | frame_n++; 115 | printf("\nframe [%u]:\n", frame_n); 116 | 117 | intToFloat(intData, inputData, numWords); 118 | 119 | sampleCount += XN; 120 | printf("samples_n = [%d, %d)\n", sampleCount - XN, sampleCount); 121 | 122 | dtmf = lm_dtmf_detect(&d, inputData, XN); 123 | if (dtmf < 0) { 124 | printf("lm_dtmf_detect failed with error %d\n", dtmf); 125 | goto lmt11_fail_sys; 126 | } 127 | lm_test(dtmf >= 0, "lm_dtmf_detect failed"); 128 | 129 | /* Peak detection */ 130 | 131 | printf(".info.peak_min = %f\n", d.info.peak_min); 132 | printf(".info.peak_max = %f\n", d.info.peak_max); 133 | printf(".info.peak_min_freq = %u\n", d.info.peak_min_freq); 134 | printf(".info.peak_max_freq = %u\n", d.info.peak_max_freq); 135 | printf(".info.peak_697 = %f\n", d.info.peak_697); 136 | printf(".info.peak_770 = %f\n", d.info.peak_770); 137 | printf(".info.peak_852 = %f\n", d.info.peak_852); 138 | printf(".info.peak_941 = %f\n", d.info.peak_941); 139 | printf(".info.peak_1209 = %f\n", d.info.peak_1209); 140 | printf(".info.peak_1336 = %f\n", d.info.peak_1336); 141 | printf(".info.peak_1477 = %f\n", d.info.peak_1477); 142 | printf(".info.peak_1633 = %f\n", d.info.peak_1633); 143 | 144 | if (dtmf > 0) { 145 | printf("DTMF decision=%d, DTMF digit=%c\n", dtmf, lm_dtmf_idx_2_char[dtmf]); 146 | dtmf_row = (uint16_t) (dtmf - 1) / 4; 147 | dtmf_col = (uint16_t) (dtmf - 1) % 4; 148 | printf("row=%u, col=%u, freq1=%u, freq2=%u\n", dtmf_row, dtmf_col, lm_dtmf_row_freqs[dtmf_row], lm_dtmf_col_freqs[dtmf_col]); 149 | } 150 | 151 | numWords = fread(intData, sizeof(int16_t), XN, inFile ); 152 | } 153 | 154 | printf("\nFinished. sampleCount = %d\n",sampleCount); 155 | fclose(inFile); 156 | 157 | lm_dtmf_deinit(&d); 158 | 159 | return 0; 160 | 161 | lmt11_fail_sys: 162 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 163 | return -1; 164 | } 165 | -------------------------------------------------------------------------------- /test/lmtest2.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test LAPACK's dgesvd_. 49 | * 50 | * DGESDD computes the singular value decomposition (SVD) of a real 51 | * M-by-N matrix A, optionally computing the left and right singular 52 | * vectors. If singular vectors are desired, it uses a 53 | * divide-and-conquer algorithm. 54 | * 55 | * The SVD is written 56 | * 57 | * A = U * SIGMA * transpose(V) 58 | * 59 | * where SIGMA is an M-by-N matrix which is zero except for its 60 | * min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and 61 | * V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA 62 | * are the singular values of A; they are real and non-negative, and 63 | * are returned in descending order. The first min(m,n) columns of 64 | * U and V are the left and right singular vectors of A. 65 | * 66 | * Note that the routine returns VT = V**T, not V. 67 | * 68 | * The divide and conquer algorithm makes very mild assumptions about 69 | * floating point arithmetic. It will work on machines with a guard 70 | * digit in alm/subtract, or on those binary machines without guard 71 | * digits which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or 72 | * Cray-2. It could conceivably fail on hexadecimal or decimal machines 73 | * without guard digits. 74 | */ 75 | 76 | /* Reference data. */ 77 | double ref_array_A[4][5] = { 78 | { 1, 0, 0, 0, 2 }, 79 | { 0, 0, 3, 0, 0 }, 80 | { 0, 0, 0, 0, 0 }, 81 | { 0, 2, 0, 0, 0 } 82 | }; 83 | 84 | double ref_array_U[4][4] = { 85 | { 0, 1, 0, 0 }, 86 | { 1, 0, 0, 0 }, 87 | { 0, 0, 0, -1 }, 88 | { 0, 0, 1, 0 } 89 | }; 90 | 91 | double ref_array_S[4][5] = { 92 | { 3, 0, 0, 0, 0 }, 93 | { 0, 2.236068, 0, 0, 0 }, 94 | { 0, 0, 2, 0, 0 }, 95 | { 0, 0, 0, 0, 0 } 96 | }; 97 | 98 | double ref_array_V[5][5] = { 99 | { 0, 0.447214, 0, -0.894427 }, 100 | { 0, 0, 1, 0, 0 }, 101 | { 1, 0, 0, 0, 0 }, 102 | { 0, 0, 0, 1, 0 }, 103 | { 0, 0.894427, 0, 0, 0.447214 } 104 | }; 105 | 106 | /* MATLAB result 107 | * 108 | * >> A = [ 1 0 0 0 2; 0 0 3 0 0 ; 0 0 0 0 0 ;0 2 0 0 0 ]; 109 | * >> [U, S, V] = svd(A) 110 | * 111 | * U = 112 | * 0 1 0 0 113 | * 1 0 0 0 114 | * 0 0 0 -1 115 | * 0 0 1 0 116 | * 117 | * S = 118 | * 3.0000 0 0 0 0 119 | * 0 2.2361 0 0 0 120 | * 0 0 2.0000 0 0 121 | * 0 0 0 0 0 122 | * 123 | * V = 124 | * 0 0.4472 0 0 -0.8944 125 | * 0 0 1.0000 0 0 126 | * 1.0000 0 0 0 0 127 | * 0 0 0 1.0000 0 128 | * 0 0.8944 0 0 0.4472 129 | */ 130 | 131 | void cb_dbl(double *x) 132 | { 133 | fprintf(stderr, "\t%f", *x); 134 | } 135 | 136 | void cb_dbl_row_end(void) 137 | { 138 | fprintf(stderr, "\n"); 139 | } 140 | 141 | int main (void) 142 | { 143 | /* Arguments to LAPACK's dgesvd_. 144 | * 145 | * DGESDD computes the singular value decomposition (SVD) of a real 146 | * M-by-N matrix A, optionally computing the left and right singular 147 | * vectors. If singular vectors are desired, it uses a 148 | * divide-and-conquer algorithm. 149 | * 150 | * The SVD is written 151 | * 152 | * A = U * SIGMA * transpose(V) 153 | * 154 | * where SIGMA is an M-by-N matrix which is zero except for its 155 | * min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and 156 | * V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA 157 | * are the singular values of A; they are real and non-negative, and 158 | * are returned in descending order. The first min(m,n) columns of 159 | * U and V are the left and right singular vectors of A. 160 | * 161 | * Note that the routine returns VT = V**T, not V. 162 | * 163 | * The divide and conquer algorithm makes very mild assumptions about 164 | * floating point arithmetic. It will work on machines with a guard 165 | * digit in alm/subtract, or on those binary machines without guard 166 | * digits which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or 167 | * Cray-2. It could conceivably fail on hexadecimal or decimal machines 168 | * without guard digits. 169 | */ 170 | char JOBU; /* JOBU is CHARACTER*1 171 | Specifies options for computing all or part of the matrix U: 172 | = 'A': all M columns of U are returned in array U: 173 | = 'S': the first min(m,n) columns of U (the left singular 174 | vectors) are returned in the array U; 175 | = 'O': the first min(m,n) columns of U (the left singular 176 | vectors) are overwritten on the array A; 177 | = 'N': no columns of U (no left singular vectors) are 178 | computed. */ 179 | char JOBVT; /* JOBVT is CHARACTER*1 180 | Specifies options for computing all or part of the matrix 181 | V**T: 182 | = 'A': all N rows of V**T are returned in the array VT; 183 | = 'S': the first min(m,n) rows of V**T (the right singular 184 | vectors) are returned in the array VT; 185 | = 'O': the first min(m,n) rows of V**T (the right singular 186 | vectors) are overwritten on the array A; 187 | = 'N': no rows of V**T (no right singular vectors) are 188 | computed. JOBVT and JOBU cannot both be 'O'. */ 189 | int M; /* M is INTEGER. The number of rows of the input matrix A. M >= 0. */ 190 | int N; /* N is INTEGER. The number of columns of the input matrix A. N >= 0. */ 191 | double *A; /* A is DOUBLE PRECISION array, dimension (LDA,N) 192 | On entry, the M-by-N matrix A. On exit, if JOBZ = 'O', 193 | A is overwritten with the first N columns of U (the left singular vectors, stored columnwise) if M >= N; 194 | A is overwritten with the first M rows of V**T (the right singular vectors, stored rowwise) otherwise. 195 | if JOBZ .ne. 'O', the contents of A are destroyed. */ 196 | int LDA; /* LDA is INTEGER. The leading dimension of the array A. LDA >= max(1,M). */ 197 | double *S; /* S is DOUBLE PRECISION array, dimension (min(M,N)). The singular values of A, sorted so that S(i) >= S(i+1). */ 198 | double *U; /* U is DOUBLE PRECISION array, dimension (LDU,UCOL) 199 | UCOL = M if JOBZ = 'A' or JOBZ = 'O' and M < N; 200 | UCOL = min(M,N) if JOBZ = 'S'. 201 | If JOBZ = 'A' or JOBZ = 'O' and M < N, U contains the M-by-M 202 | orthogonal matrix U; 203 | if JOBZ = 'S', U contains the first min(M,N) columns of U 204 | (the left singular vectors, stored columnwise); 205 | if JOBZ = 'O' and M >= N, or JOBZ = 'N', U is not referenced. */ 206 | int LDU; /* LDU is INTEGER 207 | The leading dimension of the array U. LDU >= 1; if 208 | JOBZ = 'S' or 'A' or JOBZ = 'O' and M < N, LDU >= M. */ 209 | double *VT; /* VT is DOUBLE PRECISION array, dimension (LDVT,N) 210 | If JOBZ = 'A' or JOBZ = 'O' and M >= N, VT contains the 211 | N-by-N orthogonal matrix V**T; 212 | if JOBZ = 'S', VT contains the first min(M,N) rows of 213 | V**T (the right singular vectors, stored rowwise); 214 | if JOBZ = 'O' and M < N, or JOBZ = 'N', VT is not referenced. */ 215 | int LDVT; /* LDVT is INTEGER 216 | The leading dimension of the array VT. LDVT >= 1; 217 | if JOBZ = 'A' or JOBZ = 'O' and M >= N, LDVT >= N; 218 | if JOBZ = 'S', LDVT >= min(M,N). */ 219 | double *WORK; /* WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) 220 | On exit, if INFO = 0, WORK(1) returns the optimal LWORK; */ 221 | int LWORK; /* LWORK is INTEGER 222 | The dimension of the array WORK. LWORK >= 1. 223 | If LWORK = -1, a workspace query is assumed. The optimal 224 | size for the WORK array is calculated and stored in WORK(1), 225 | and no other work except argument checking is performed. 226 | 227 | Let mx = max(M,N) and mn = min(M,N). 228 | If JOBZ = 'N', LWORK >= 3*mn + max( mx, 7*mn ). 229 | If JOBZ = 'O', LWORK >= 3*mn + max( mx, 5*mn*mn + 4*mn ). 230 | If JOBZ = 'S', LWORK >= 4*mn*mn + 7*mn. 231 | If JOBZ = 'A', LWORK >= 4*mn*mn + 6*mn + mx. 232 | These are not tight minimums in all cases; see comments inside code. 233 | For good performance, LWORK should generally be larger; 234 | a query is recommended. */ 235 | int INFO; /* INFO is INTEGER 236 | = 0: successful exit. 237 | < 0: if INFO = -i, the i-th argument had an illegal value. 238 | > 0: DBDSDC did not converge, updating process failed. */ 239 | 240 | /* helper variables */ 241 | int i = 0, j = 0; 242 | double WORK_QUERY = 0; 243 | 244 | 245 | /* Call dgesvd_ with lwork = -1 to query optimal workspace size. */ 246 | 247 | JOBU = 'A'; 248 | JOBVT = 'A'; 249 | M = 4; 250 | N = 5; 251 | LDA = 4; /* (out) */ 252 | LDU = 4; /* (out) */ 253 | S = NULL; /* (don't care) */ 254 | U = NULL; /* (don't care) */ 255 | VT = NULL; /* (don't care) */ 256 | LDVT = 5; /* (out) */ 257 | WORK = NULL; /* (out) , because LWORK is 0 do not care */ 258 | LWORK = 4 * M * N * M *N + 6 * M * N + lm_max(M, N); 259 | 260 | A = calloc(M * N, sizeof(double)); 261 | if (!A) { 262 | goto lmt2_fail_sys; 263 | } 264 | for (i = 0; i < M; ++i) { 265 | for (j = 0; j < N; ++j) { 266 | A[j * M + i] = ref_array_A[i][j]; 267 | } 268 | } 269 | 270 | S = calloc(lm_min(M, N), sizeof(double)); 271 | if (!S) { 272 | goto lmt2_fail_sys; 273 | } 274 | 275 | U = calloc(LDU * M, sizeof(double)); 276 | if (!U) { 277 | goto lmt2_fail_sys; 278 | } 279 | 280 | VT = calloc(LDVT * N, sizeof(double)); 281 | if (!VT) { 282 | goto lmt2_fail_sys; 283 | } 284 | 285 | fprintf(stderr, "Reference array A (rowwise):\n"); 286 | lm_walk_dbl_arr_rowwise(&ref_array_A[0][0], M, N, cb_dbl, cb_dbl_row_end); 287 | 288 | fprintf(stderr, "Reference array U (rowwise):\n"); 289 | lm_walk_dbl_arr_rowwise(&ref_array_U[0][0], M, M, cb_dbl, cb_dbl_row_end); 290 | fprintf(stderr, "Reference array U (colwise):\n"); 291 | lm_walk_dbl_arr_colwise(&ref_array_U[0][0], M, M, cb_dbl, cb_dbl_row_end); 292 | 293 | fprintf(stderr, "Reference array S (rowwise):\n"); 294 | lm_walk_dbl_arr_rowwise(&ref_array_S[0][0], M, N, cb_dbl, cb_dbl_row_end); 295 | 296 | fprintf(stderr, "Reference array V (rowwise):\n"); 297 | lm_walk_dbl_arr_rowwise(&ref_array_V[0][0], N, N, cb_dbl, cb_dbl_row_end); 298 | 299 | LWORK = -1; 300 | dgesvd_("A", "A", &M, &N, A, &LDA, S, U, &LDU, VT, &LDVT, &WORK_QUERY, &LWORK, &INFO); 301 | if (INFO != 0) { 302 | if (INFO < 0) { 303 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"the %d-th argument had illegal value\"\n", INFO); 304 | } else { 305 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"DBDSDC didn't converge, updating process failed\"\n"); 306 | } 307 | return -1; 308 | } 309 | 310 | LWORK = (int) WORK_QUERY; 311 | WORK = calloc(LWORK, sizeof(double)); 312 | if (!WORK) { 313 | goto lmt2_fail_sys; 314 | } 315 | 316 | fprintf(stderr, "LAPACK's dgesvd_ query optimal results: LDA %d, LDU %d, LDVT %d, LWORK %d, WORK_QUERY %f\n", LDA, LDU, LDVT, LWORK, WORK_QUERY); 317 | fprintf(stderr, "Rest of params: M %d, N %d\n", M, N); 318 | 319 | fprintf(stderr, "Matrix A submitted to LAPACK:\n"); 320 | lm_walk_dbl_arr_rowwise(A, M, N, cb_dbl, cb_dbl_row_end); 321 | 322 | /* Compute SVD. */ 323 | dgesvd_(&JOBU, &JOBVT, &M, &N, A, &LDA, S, U, &LDU, VT, &LDVT, WORK, &LWORK, &INFO); 324 | if (INFO != 0) { 325 | if (INFO < 0) { 326 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"the %d-th argument had illegal value\"\n", INFO); 327 | } else { 328 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"DBDSDC didn't converge, updating process failed\"\n"); 329 | } 330 | return -1; 331 | } 332 | 333 | fprintf(stderr, "LAPACK's dgesvd_ SVD completed\n"); 334 | 335 | fprintf(stderr, "Result A:\n"); 336 | lm_walk_dbl_arr_rowwise(A, M, N, cb_dbl, cb_dbl_row_end); 337 | 338 | fprintf(stderr, "Result U (rowwise):\n"); 339 | lm_walk_dbl_arr_rowwise(U, LDU, M, cb_dbl, cb_dbl_row_end); 340 | fprintf(stderr, "Result U (colwise):\n"); 341 | lm_walk_dbl_arr_colwise(U, LDU, M, cb_dbl, cb_dbl_row_end); 342 | 343 | lm_test(lm_cmp_dbl_arr_row_col(&ref_array_U[0][0], U, LDU, M) == 0, "Err: array U"); 344 | 345 | fprintf(stderr, "Result S (rowwise):\n"); 346 | lm_walk_dbl_arr_rowwise(S, M, 1, cb_dbl, cb_dbl_row_end); 347 | 348 | lm_test(lm_cmp_dbl_arr_diag_row(&ref_array_S[0][0], S, M, N) == 0, "Err: array S"); 349 | 350 | fprintf(stderr, "Result VT (rowwise):\n"); 351 | lm_walk_dbl_arr_rowwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 352 | fprintf(stderr, "Result VT (colwise):\n"); 353 | lm_walk_dbl_arr_colwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 354 | 355 | lm_transpose(VT, N); 356 | 357 | fprintf(stderr, "Result VT^T (rowwise):\n"); 358 | lm_walk_dbl_arr_rowwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 359 | fprintf(stderr, "Result VT^T (colwise):\n"); 360 | lm_walk_dbl_arr_colwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 361 | 362 | //lm_test(lm_cmp_dbl_arr_row_col(&ref_array_V[0][0], VT, LDVT, N) == 0, "Err: array VT"); 363 | 364 | free(WORK); 365 | free(A); 366 | free(S); 367 | free(U); 368 | free(VT); 369 | 370 | return 0; 371 | 372 | lmt2_fail_sys: 373 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 374 | return -1; 375 | } 376 | -------------------------------------------------------------------------------- /test/lmtest3.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test LAPACK's dgesvd_. 49 | * 50 | * Example 1. 51 | * 52 | * DGESDD computes the singular value decomposition (SVD) of a real 53 | * M-by-N matrix A, optionally computing the left and right singular 54 | * vectors. If singular vectors are desired, it uses a 55 | * divide-and-conquer algorithm. 56 | * 57 | * The SVD is written 58 | * 59 | * A = U * SIGMA * transpose(V) 60 | * 61 | * where SIGMA is an M-by-N matrix which is zero except for its 62 | * min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and 63 | * V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA 64 | * are the singular values of A; they are real and non-negative, and 65 | * are returned in descending order. The first min(m,n) columns of 66 | * U and V are the left and right singular vectors of A. 67 | * 68 | * Note that the routine returns VT = V**T, not V. 69 | * 70 | * The divide and conquer algorithm makes very mild assumptions about 71 | * floating point arithmetic. It will work on machines with a guard 72 | * digit in alm/subtract, or on those binary machines without guard 73 | * digits which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or 74 | * Cray-2. It could conceivably fail on hexadecimal or decimal machines 75 | * without guard digits. 76 | */ 77 | 78 | /* Reference data. */ 79 | double ref_array_A[4][4] = { 80 | { 1, 1, 0, 0 }, 81 | { 0, 2, 1, 0 }, 82 | { 0, 0, 3, 1 }, 83 | { 0, 0, 0, 4 } 84 | }; 85 | 86 | double ref_array_Sigma[4][1] = { 87 | { 4.260007 }, 88 | { 3.107349 }, 89 | { 2.111785 }, 90 | { 0.858542 } 91 | }; 92 | 93 | /* MATLAB result 94 | * 95 | * 96 | * U = 97 | * 0.0135 -0.1354 0.5426 0.8289 98 | * 0.1093 -0.5184 0.6673 -0.5234 99 | * 0.4702 -0.7142 -0.4820 0.1911 100 | * 0.8757 0.4503 0.1671 -0.0501 101 | * 102 | * 103 | * S = 104 | * 4.2600 0 0 0 105 | * 0 3.1073 0 0 106 | * 0 0 2.1118 0 107 | * 0 0 0 0.8585 108 | * 109 | * V = 110 | * 0.0032 -0.0436 0.2570 0.9654 111 | * 0.0545 -0.3773 0.8890 -0.2538 112 | * 0.3568 -0.8564 -0.3687 0.0583 113 | * 0.9326 0.3498 0.0882 -0.0108 114 | */ 115 | 116 | void cb_dbl(double *x) 117 | { 118 | fprintf(stderr, "\t%f", *x); 119 | } 120 | 121 | void cb_dbl_row_end(void) 122 | { 123 | fprintf(stderr, "\n"); 124 | } 125 | 126 | 127 | int main (void) 128 | { 129 | /* Arguments to LAPACK's dgesvd_. 130 | * 131 | * DGESDD computes the singular value decomposition (SVD) of a real 132 | * M-by-N matrix A, optionally computing the left and right singular 133 | * vectors. If singular vectors are desired, it uses a 134 | * divide-and-conquer algorithm. 135 | * 136 | * The SVD is written 137 | * 138 | * A = U * SIGMA * transpose(V) 139 | * 140 | * where SIGMA is an M-by-N matrix which is zero except for its 141 | * min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and 142 | * V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA 143 | * are the singular values of A; they are real and non-negative, and 144 | * are returned in descending order. The first min(m,n) columns of 145 | * U and V are the left and right singular vectors of A. 146 | * 147 | * Note that the routine returns VT = V**T, not V. 148 | * 149 | * The divide and conquer algorithm makes very mild assumptions about 150 | * floating point arithmetic. It will work on machines with a guard 151 | * digit in alm/subtract, or on those binary machines without guard 152 | * digits which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or 153 | * Cray-2. It could conceivably fail on hexadecimal or decimal machines 154 | * without guard digits. 155 | */ 156 | char JOBU; /* JOBU is CHARACTER*1 157 | Specifies options for computing all or part of the matrix U: 158 | = 'A': all M columns of U are returned in array U: 159 | = 'S': the first min(m,n) columns of U (the left singular 160 | vectors) are returned in the array U; 161 | = 'O': the first min(m,n) columns of U (the left singular 162 | vectors) are overwritten on the array A; 163 | = 'N': no columns of U (no left singular vectors) are 164 | computed. */ 165 | char JOBVT; /* JOBVT is CHARACTER*1 166 | Specifies options for computing all or part of the matrix 167 | V**T: 168 | = 'A': all N rows of V**T are returned in the array VT; 169 | = 'S': the first min(m,n) rows of V**T (the right singular 170 | vectors) are returned in the array VT; 171 | = 'O': the first min(m,n) rows of V**T (the right singular 172 | vectors) are overwritten on the array A; 173 | = 'N': no rows of V**T (no right singular vectors) are 174 | computed. JOBVT and JOBU cannot both be 'O'. */ 175 | int M; /* M is INTEGER. The number of rows of the input matrix A. M >= 0. */ 176 | int N; /* N is INTEGER. The number of columns of the input matrix A. N >= 0. */ 177 | double *A; /* A is DOUBLE PRECISION array, dimension (LDA,N) 178 | On entry, the M-by-N matrix A. On exit, if JOBZ = 'O', 179 | A is overwritten with the first N columns of U (the left singular vectors, stored columnwise) if M >= N; 180 | A is overwritten with the first M rows of V**T (the right singular vectors, stored rowwise) otherwise. 181 | if JOBZ .ne. 'O', the contents of A are destroyed. */ 182 | int LDA; /* LDA is INTEGER. The leading dimension of the array A. LDA >= max(1,M). */ 183 | double *S; /* S is DOUBLE PRECISION array, dimension (min(M,N)). The singular values of A, sorted so that S(i) >= S(i+1). */ 184 | double *U; /* U is DOUBLE PRECISION array, dimension (LDU,UCOL) 185 | UCOL = M if JOBZ = 'A' or JOBZ = 'O' and M < N; 186 | UCOL = min(M,N) if JOBZ = 'S'. 187 | If JOBZ = 'A' or JOBZ = 'O' and M < N, U contains the M-by-M 188 | orthogonal matrix U; 189 | if JOBZ = 'S', U contains the first min(M,N) columns of U 190 | (the left singular vectors, stored columnwise); 191 | if JOBZ = 'O' and M >= N, or JOBZ = 'N', U is not referenced. */ 192 | int LDU; /* LDU is INTEGER 193 | The leading dimension of the array U. LDU >= 1; if 194 | JOBZ = 'S' or 'A' or JOBZ = 'O' and M < N, LDU >= M. */ 195 | double *VT; /* VT is DOUBLE PRECISION array, dimension (LDVT,N) 196 | If JOBZ = 'A' or JOBZ = 'O' and M >= N, VT contains the 197 | N-by-N orthogonal matrix V**T; 198 | if JOBZ = 'S', VT contains the first min(M,N) rows of 199 | V**T (the right singular vectors, stored rowwise); 200 | if JOBZ = 'O' and M < N, or JOBZ = 'N', VT is not referenced. */ 201 | int LDVT; /* LDVT is INTEGER 202 | The leading dimension of the array VT. LDVT >= 1; 203 | if JOBZ = 'A' or JOBZ = 'O' and M >= N, LDVT >= N; 204 | if JOBZ = 'S', LDVT >= min(M,N). */ 205 | double *WORK; /* WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) 206 | On exit, if INFO = 0, WORK(1) returns the optimal LWORK; */ 207 | int LWORK; /* LWORK is INTEGER 208 | The dimension of the array WORK. LWORK >= 1. 209 | If LWORK = -1, a workspace query is assumed. The optimal 210 | size for the WORK array is calculated and stored in WORK(1), 211 | and no other work except argument checking is performed. 212 | 213 | Let mx = max(M,N) and mn = min(M,N). 214 | If JOBZ = 'N', LWORK >= 3*mn + max( mx, 7*mn ). 215 | If JOBZ = 'O', LWORK >= 3*mn + max( mx, 5*mn*mn + 4*mn ). 216 | If JOBZ = 'S', LWORK >= 4*mn*mn + 7*mn. 217 | If JOBZ = 'A', LWORK >= 4*mn*mn + 6*mn + mx. 218 | These are not tight minimums in all cases; see comments inside code. 219 | For good performance, LWORK should generally be larger; 220 | a query is recommended. */ 221 | int INFO; /* INFO is INTEGER 222 | = 0: successful exit. 223 | < 0: if INFO = -i, the i-th argument had an illegal value. 224 | > 0: DBDSDC did not converge, updating process failed. */ 225 | 226 | /* helper variables */ 227 | int i = 0, j = 0; 228 | double WORK_QUERY = 0; 229 | 230 | 231 | /* Call dgesvd_ with lwork = -1 to query optimal workspace size. */ 232 | 233 | JOBU = 'N'; 234 | JOBVT = 'N'; 235 | M = 4; 236 | N = 4; 237 | LDA = 4; /* (out) */ 238 | LDU = 4; /* (out) */ 239 | S = NULL; /* (don't care) */ 240 | U = NULL; /* (don't care) */ 241 | VT = NULL; /* (don't care) */ 242 | LDVT = 4; /* (out) */ 243 | WORK = NULL; /* (out) , because LWORK is 0 do not care */ 244 | LWORK = 3 *M * N + lm_max( lm_max(M, N), 7 * M * N ); /* (in), dynamically alloc WORK */ 245 | 246 | A = calloc(M * N, sizeof(double)); 247 | if (!A) { 248 | goto lmt3_fail_sys; 249 | } 250 | for (i = 0; i < M; ++i) { 251 | for (j = 0; j < N; ++j) { 252 | A[j * M + i] = ref_array_A[i][j]; 253 | } 254 | } 255 | 256 | S = calloc(lm_min(M, N), sizeof(double)); 257 | if (!S) { 258 | goto lmt3_fail_sys; 259 | } 260 | 261 | fprintf(stderr, "Reference array A (rowwise):\n"); 262 | lm_walk_dbl_arr_rowwise(&ref_array_A[0][0], M, N, cb_dbl, cb_dbl_row_end); 263 | 264 | fprintf(stderr, "Reference array Sigma (rowwise):\n"); 265 | lm_walk_dbl_arr_rowwise(&ref_array_Sigma[0][0], lm_min(M, N), 1, cb_dbl, cb_dbl_row_end); 266 | 267 | LWORK = -1; 268 | dgesvd_("A", "A", &M, &N, A, &LDA, S, U, &LDU, VT, &LDVT, &WORK_QUERY, &LWORK, &INFO); 269 | if (INFO != 0) { 270 | if (INFO < 0) { 271 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"the %d-th argument had illegal value\"\n", INFO); 272 | } else { 273 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"DBDSDC didn't converge, updating process failed\"\n"); 274 | } 275 | return -1; 276 | } 277 | 278 | LWORK = (int) WORK_QUERY; 279 | WORK = calloc(LWORK, sizeof(double)); 280 | if (!WORK) { 281 | goto lmt3_fail_sys; 282 | } 283 | 284 | fprintf(stderr, "LAPACK's dgesvd_ query optimal results: LDA %d, LDU %d, LDVT %d, LWORK %d, WORK_QUERY %f\n", LDA, LDU, LDVT, LWORK, WORK_QUERY); 285 | fprintf(stderr, "Rest of params: M %d, N %d\n", M, N); 286 | 287 | fprintf(stderr, "Matrix A submitted to LAPACK:\n"); 288 | lm_walk_dbl_arr_rowwise(A, M, N, cb_dbl, cb_dbl_row_end); 289 | 290 | /* Compute SVD. */ 291 | dgesvd_(&JOBU, &JOBVT, &M, &N, A, &LDA, S, U, &LDU, VT, &LDVT, WORK, &LWORK, &INFO); 292 | if (INFO != 0) { 293 | if (INFO < 0) { 294 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"the %d-th argument had illegal value\"\n", INFO); 295 | } else { 296 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"DBDSDC didn't converge, updating process failed\"\n"); 297 | } 298 | return -1; 299 | } 300 | 301 | fprintf(stderr, "LAPACK's dgesvd_ SVD completed\n"); 302 | 303 | fprintf(stderr, "Result A:\n"); 304 | lm_walk_dbl_arr_rowwise(A, M, N, cb_dbl, cb_dbl_row_end); 305 | 306 | fprintf(stderr, "Result S (rowwise):\n"); 307 | lm_walk_dbl_arr_rowwise(S, lm_min(M, N), 1, cb_dbl, cb_dbl_row_end); 308 | 309 | free(WORK); 310 | free(A); 311 | free(S); 312 | free(U); 313 | free(VT); 314 | 315 | return 0; 316 | 317 | lmt3_fail_sys: 318 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 319 | return -1; 320 | } 321 | -------------------------------------------------------------------------------- /test/lmtest4.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test LAPACK's dgesvd_. 49 | * 50 | * Example 2. 51 | * 52 | * DGESDD computes the singular value decomposition (SVD) of a real 53 | * M-by-N matrix A, optionally computing the left and right singular 54 | * vectors. If singular vectors are desired, it uses a 55 | * divide-and-conquer algorithm. 56 | * 57 | * The SVD is written 58 | * 59 | * A = U * SIGMA * transpose(V) 60 | * 61 | * where SIGMA is an M-by-N matrix which is zero except for its 62 | * min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and 63 | * V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA 64 | * are the singular values of A; they are real and non-negative, and 65 | * are returned in descending order. The first min(m,n) columns of 66 | * U and V are the left and right singular vectors of A. 67 | * 68 | * Note that the routine returns VT = V**T, not V. 69 | * 70 | * The divide and conquer algorithm makes very mild assumptions about 71 | * floating point arithmetic. It will work on machines with a guard 72 | * digit in alm/subtract, or on those binary machines without guard 73 | * digits which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or 74 | * Cray-2. It could conceivably fail on hexadecimal or decimal machines 75 | * without guard digits. 76 | */ 77 | 78 | /* Reference data. */ 79 | double ref_array_A[3][3] = { 80 | { 1, 2, 3}, 81 | { 2, 4, 5 }, 82 | { 3, 5, 6 } 83 | }; 84 | 85 | double ref_array_U[3][3] = { 86 | { -0.327985, -0.736976, -0.591009 }, 87 | { -0.591009, -0.327985, 0.736976 }, 88 | { -0.736976, 0.591009, -0.327985 } 89 | }; 90 | 91 | double ref_array_Sigma[3][1] = { 92 | { 11.344814 }, 93 | { 0.515729 }, 94 | { 0.170915 } 95 | }; 96 | 97 | double ref_array_VT[3][3] = { 98 | { -0.327985, -0.591009, -0.736976 }, 99 | { 0.736976, 0.327985, -0.591009 }, 100 | { -0.591009, 0.736976, -0.327985 } 101 | }; 102 | 103 | /* MATLAB result 104 | * 105 | * >> A = [ 1, 2, 3; 2, 4, 5; 3, 5, 6] 106 | * 107 | * A = 108 | * 1 2 3 109 | * 2 4 5 110 | * 3 5 6 111 | * 112 | * >> [U, S, V] = svd(A) 113 | * 114 | * U = 115 | * -0.3280 -0.7370 -0.5910 116 | * -0.5910 -0.3280 0.7370 117 | * -0.7370 0.5910 -0.3280 118 | * 119 | * S = 120 | * 11.3448 0 0 121 | * 0 0.5157 0 122 | * 0 0 0.1709 123 | * 124 | * V = 125 | * -0.3280 0.7370 -0.5910 126 | * -0.5910 0.3280 0.7370 127 | * -0.7370 -0.5910 -0.3280 128 | */ 129 | 130 | void cb_dbl(double *x) 131 | { 132 | fprintf(stderr, "\t%f", *x); 133 | } 134 | 135 | void cb_dbl_row_end(void) 136 | { 137 | fprintf(stderr, "\n"); 138 | } 139 | 140 | 141 | int main (void) 142 | { 143 | /* Arguments to LAPACK's dgesvd_. 144 | * 145 | * DGESDD computes the singular value decomposition (SVD) of a real 146 | * M-by-N matrix A, optionally computing the left and right singular 147 | * vectors. If singular vectors are desired, it uses a 148 | * divide-and-conquer algorithm. 149 | * 150 | * The SVD is written 151 | * 152 | * A = U * SIGMA * transpose(V) 153 | * 154 | * where SIGMA is an M-by-N matrix which is zero except for its 155 | * min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and 156 | * V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA 157 | * are the singular values of A; they are real and non-negative, and 158 | * are returned in descending order. The first min(m,n) columns of 159 | * U and V are the left and right singular vectors of A. 160 | * 161 | * Note that the routine returns VT = V**T, not V. 162 | * 163 | * The divide and conquer algorithm makes very mild assumptions about 164 | * floating point arithmetic. It will work on machines with a guard 165 | * digit in alm/subtract, or on those binary machines without guard 166 | * digits which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or 167 | * Cray-2. It could conceivably fail on hexadecimal or decimal machines 168 | * without guard digits. 169 | */ 170 | char JOBU; /* JOBU is CHARACTER*1 171 | Specifies options for computing all or part of the matrix U: 172 | = 'A': all M columns of U are returned in array U: 173 | = 'S': the first min(m,n) columns of U (the left singular 174 | vectors) are returned in the array U; 175 | = 'O': the first min(m,n) columns of U (the left singular 176 | vectors) are overwritten on the array A; 177 | = 'N': no columns of U (no left singular vectors) are 178 | computed. */ 179 | char JOBVT; /* JOBVT is CHARACTER*1 180 | Specifies options for computing all or part of the matrix 181 | V**T: 182 | = 'A': all N rows of V**T are returned in the array VT; 183 | = 'S': the first min(m,n) rows of V**T (the right singular 184 | vectors) are returned in the array VT; 185 | = 'O': the first min(m,n) rows of V**T (the right singular 186 | vectors) are overwritten on the array A; 187 | = 'N': no rows of V**T (no right singular vectors) are 188 | computed. JOBVT and JOBU cannot both be 'O'. */ 189 | int M; /* M is INTEGER. The number of rows of the input matrix A. M >= 0. */ 190 | int N; /* N is INTEGER. The number of columns of the input matrix A. N >= 0. */ 191 | double *A; /* A is DOUBLE PRECISION array, dimension (LDA,N) 192 | On entry, the M-by-N matrix A. On exit, if JOBZ = 'O', 193 | A is overwritten with the first N columns of U (the left singular vectors, stored columnwise) if M >= N; 194 | A is overwritten with the first M rows of V**T (the right singular vectors, stored rowwise) otherwise. 195 | if JOBZ .ne. 'O', the contents of A are destroyed. */ 196 | int LDA; /* LDA is INTEGER. The leading dimension of the array A. LDA >= max(1,M). */ 197 | double *S; /* S is DOUBLE PRECISION array, dimension (min(M,N)). The singular values of A, sorted so that S(i) >= S(i+1). */ 198 | double *U; /* U is DOUBLE PRECISION array, dimension (LDU,UCOL) 199 | UCOL = M if JOBZ = 'A' or JOBZ = 'O' and M < N; 200 | UCOL = min(M,N) if JOBZ = 'S'. 201 | If JOBZ = 'A' or JOBZ = 'O' and M < N, U contains the M-by-M 202 | orthogonal matrix U; 203 | if JOBZ = 'S', U contains the first min(M,N) columns of U 204 | (the left singular vectors, stored columnwise); 205 | if JOBZ = 'O' and M >= N, or JOBZ = 'N', U is not referenced. */ 206 | int LDU; /* LDU is INTEGER 207 | The leading dimension of the array U. LDU >= 1; if 208 | JOBZ = 'S' or 'A' or JOBZ = 'O' and M < N, LDU >= M. */ 209 | double *VT; /* VT is DOUBLE PRECISION array, dimension (LDVT,N) 210 | If JOBZ = 'A' or JOBZ = 'O' and M >= N, VT contains the 211 | N-by-N orthogonal matrix V**T; 212 | if JOBZ = 'S', VT contains the first min(M,N) rows of 213 | V**T (the right singular vectors, stored rowwise); 214 | if JOBZ = 'O' and M < N, or JOBZ = 'N', VT is not referenced. */ 215 | int LDVT; /* LDVT is INTEGER 216 | The leading dimension of the array VT. LDVT >= 1; 217 | if JOBZ = 'A' or JOBZ = 'O' and M >= N, LDVT >= N; 218 | if JOBZ = 'S', LDVT >= min(M,N). */ 219 | double *WORK; /* WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) 220 | On exit, if INFO = 0, WORK(1) returns the optimal LWORK; */ 221 | int LWORK; /* LWORK is INTEGER 222 | The dimension of the array WORK. LWORK >= 1. 223 | If LWORK = -1, a workspace query is assumed. The optimal 224 | size for the WORK array is calculated and stored in WORK(1), 225 | and no other work except argument checking is performed. 226 | 227 | Let mx = max(M,N) and mn = min(M,N). 228 | If JOBZ = 'N', LWORK >= 3*mn + max( mx, 7*mn ). 229 | If JOBZ = 'O', LWORK >= 3*mn + max( mx, 5*mn*mn + 4*mn ). 230 | If JOBZ = 'S', LWORK >= 4*mn*mn + 7*mn. 231 | If JOBZ = 'A', LWORK >= 4*mn*mn + 6*mn + mx. 232 | These are not tight minimums in all cases; see comments inside code. 233 | For good performance, LWORK should generally be larger; 234 | a query is recommended. */ 235 | int INFO; /* INFO is INTEGER 236 | = 0: successful exit. 237 | < 0: if INFO = -i, the i-th argument had an illegal value. 238 | > 0: DBDSDC did not converge, updating process failed. */ 239 | 240 | /* helper variables */ 241 | int i = 0, j = 0; 242 | double WORK_QUERY = 0; 243 | 244 | 245 | /* Call dgesvd_ with lwork = -1 to query optimal workspace size. */ 246 | 247 | JOBU = 'A'; 248 | JOBVT = 'A'; 249 | M = 3; 250 | N = 3; 251 | LDA = 3; /* (out) */ 252 | LDU = 3; /* (out) */ 253 | S = NULL; /* (don't care) */ 254 | U = NULL; /* (don't care) */ 255 | VT = NULL; /* (don't care) */ 256 | LDVT = 3; /* (out) */ 257 | WORK = NULL; /* (out) , because LWORK is 0 do not care */ 258 | LWORK = 4 * M * N * M *N + 6 * M * N + lm_max(M, N); 259 | 260 | A = calloc(M * N, sizeof(double)); 261 | if (!A) { 262 | goto lmt4_fail_sys; 263 | } 264 | for (i = 0; i < M; ++i) { 265 | for (j = 0; j < N; ++j) { 266 | A[j * M + i] = ref_array_A[i][j]; 267 | } 268 | } 269 | 270 | S = calloc(lm_min(M, N), sizeof(double)); 271 | if (!S) { 272 | goto lmt4_fail_sys; 273 | } 274 | 275 | U = calloc(LDU * M, sizeof(double)); 276 | if (!U) { 277 | goto lmt4_fail_sys; 278 | } 279 | 280 | VT = calloc(LDVT * N, sizeof(double)); 281 | if (!VT) { 282 | goto lmt4_fail_sys; 283 | } 284 | 285 | fprintf(stderr, "Reference array A:\n"); 286 | lm_walk_dbl_arr_rowwise(&ref_array_A[0][0], M, N, cb_dbl, cb_dbl_row_end); 287 | 288 | fprintf(stderr, "Reference array U (rowwise):\n"); 289 | lm_walk_dbl_arr_rowwise(&ref_array_U[0][0], M, M, cb_dbl, cb_dbl_row_end); 290 | 291 | fprintf(stderr, "Reference array Sigma (rowwise):\n"); 292 | lm_walk_dbl_arr_rowwise(&ref_array_Sigma[0][0], lm_min(M, N), 1, cb_dbl, cb_dbl_row_end); 293 | 294 | fprintf(stderr, "Reference array V (rowwise):\n"); 295 | lm_walk_dbl_arr_rowwise(&ref_array_VT[0][0], N, N, cb_dbl, cb_dbl_row_end); 296 | 297 | LWORK = -1; 298 | dgesvd_("A", "A", &M, &N, A, &LDA, S, U, &LDU, VT, &LDVT, &WORK_QUERY, &LWORK, &INFO); 299 | if (INFO != 0) { 300 | if (INFO < 0) { 301 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"the %d-th argument had illegal value\"\n", INFO); 302 | } else { 303 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"DBDSDC didn't converge, updating process failed\"\n"); 304 | } 305 | return -1; 306 | } 307 | 308 | LWORK = (int) WORK_QUERY; 309 | WORK = calloc(LWORK, sizeof(double)); 310 | if (!WORK) { 311 | goto lmt4_fail_sys; 312 | } 313 | 314 | fprintf(stderr, "LAPACK's dgesvd_ query optimal results: LDA %d, LDU %d, LDVT %d, LWORK %d, WORK_QUERY %f\n", LDA, LDU, LDVT, LWORK, WORK_QUERY); 315 | fprintf(stderr, "Rest of params: M %d, N %d\n", M, N); 316 | 317 | fprintf(stderr, "Matrix A submitted to LAPACK:\n"); 318 | lm_walk_dbl_arr_rowwise(A, M, N, cb_dbl, cb_dbl_row_end); 319 | 320 | /* Compute SVD. */ 321 | dgesvd_(&JOBU, &JOBVT, &M, &N, A, &LDA, S, U, &LDU, VT, &LDVT, WORK, &LWORK, &INFO); 322 | if (INFO != 0) { 323 | if (INFO < 0) { 324 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"the %d-th argument had illegal value\"\n", INFO); 325 | } else { 326 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"DBDSDC didn't converge, updating process failed\"\n"); 327 | } 328 | return -1; 329 | } 330 | 331 | fprintf(stderr, "LAPACK's dgesvd_ SVD completed\n"); 332 | 333 | fprintf(stderr, "Result A:\n"); 334 | lm_walk_dbl_arr_rowwise(A, M, N, cb_dbl, cb_dbl_row_end); 335 | 336 | fprintf(stderr, "Result U (rowwise):\n"); 337 | lm_walk_dbl_arr_rowwise(U, LDU, M, cb_dbl, cb_dbl_row_end); 338 | fprintf(stderr, "Result U (colwise):\n"); 339 | lm_walk_dbl_arr_colwise(U, LDU, M, cb_dbl, cb_dbl_row_end); 340 | 341 | 342 | fprintf(stderr, "Result S (rowwise):\n"); 343 | lm_walk_dbl_arr_rowwise(S, lm_min(M, N), 1, cb_dbl, cb_dbl_row_end); 344 | 345 | fprintf(stderr, "Result VT (rowwise):\n"); 346 | lm_walk_dbl_arr_rowwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 347 | fprintf(stderr, "Result VT (colwise):\n"); 348 | lm_walk_dbl_arr_colwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 349 | 350 | free(WORK); 351 | free(A); 352 | free(S); 353 | free(U); 354 | free(VT); 355 | 356 | return 0; 357 | 358 | lmt4_fail_sys: 359 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 360 | return -1; 361 | } 362 | -------------------------------------------------------------------------------- /test/lmtest5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test Correlation. 49 | * 50 | * Input is DTMF "1" = { 697 Hz, 1209 Hz }. 8000 Hz, 16 samples: [10, 25]. 51 | */ 52 | 53 | /* Reference data. */ 54 | #define SIGNALS_EXPECTED_N 2 55 | #define SIGNALS_N (2 * (SIGNALS_EXPECTED_N)) /* Need to account for complex signals. */ 56 | #define CORR_ORD ((2 * (SIGNALS_N)) - 1) 57 | #define N 16 58 | double ref_array_X[N] = { -0.2071, -0.7942, -1.1108, -0.6395, 0.5197, 1.6468, 1.9312, 1.1106, -0.3025, -1.3984, -1.5514, -0.8580, 0.0096, 0.3922, 0.1753, -0.1748 }; 59 | double ref_array_Corr[N - CORR_ORD][CORR_ORD + 1] = { 60 | { 0.3702, 0.6437, 0.5489, 0.1732, -0.2132, -0.3703, -0.2647, -0.0690 }, 61 | { -0.1008, 0.3702, 0.6437, 0.5489, 0.1732, -0.2132, -0.3703, -0.2647 }, 62 | { -0.4661, -0.1008, 0.3702, 0.6437, 0.5489, 0.1732, -0.2132, -0.3703 }, 63 | { -0.5171, -0.4661, -0.1008, 0.3702, 0.6437, 0.5489, 0.1732, -0.2132 }, 64 | { -0.2860, -0.5171, -0.4661, -0.1008, 0.3702, 0.6437, 0.5489, 0.1732 }, 65 | { 0.0032, -0.2860, -0.5171, -0.4661, -0.1008, 0.3702, 0.6437, 0.5489 }, 66 | { 0.1307, 0.0032, -0.2860, -0.5171, -0.4661, -0.1008, 0.3702, 0.6437 }, 67 | { 0.0584, 0.1307, 0.0032, -0.2860, -0.5171, -0.4661, -0.1008, 0.3702 }, 68 | { -0.0583, 0.0584, 0.1307, 0.0032, -0.2860, -0.5171, -0.4661, -0.1008 } 69 | }; 70 | 71 | /* MATLAB result 72 | * 73 | * >> y2(10:25) 74 | * ans = 75 | * Columns 1 through 11 76 | * -0.2071 -0.7942 -1.1108 -0.6395 0.5197 1.6468 1.9312 1.1106 -0.3025 -1.3984 -1.5514 77 | * Columns 12 through 16 78 | * -0.8580 0.0096 0.3922 0.1753 -0.1748 79 | * 80 | * r_x = 81 | * 0.3702 0.6437 0.5489 0.1732 -0.2132 -0.3703 -0.2647 -0.0690 82 | * -0.1008 0.3702 0.6437 0.5489 0.1732 -0.2132 -0.3703 -0.2647 83 | * -0.4661 -0.1008 0.3702 0.6437 0.5489 0.1732 -0.2132 -0.3703 84 | * -0.5171 -0.4661 -0.1008 0.3702 0.6437 0.5489 0.1732 -0.2132 85 | * -0.2860 -0.5171 -0.4661 -0.1008 0.3702 0.6437 0.5489 0.1732 86 | * 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 0.3702 0.6437 0.5489 87 | * 0.1307 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 0.3702 0.6437 88 | * 0.0584 0.1307 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 0.3702 89 | * -0.0583 0.0584 0.1307 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 90 | */ 91 | 92 | void cb_dbl(double *x) 93 | { 94 | fprintf(stderr, "\t%f", *x); 95 | } 96 | 97 | void cb_dbl_row_end(void) 98 | { 99 | fprintf(stderr, "\n"); 100 | } 101 | 102 | 103 | int main (void) 104 | { 105 | double *X; /* samples */ 106 | double *Y; /* result correlation matrix of X */ 107 | int ym = N - CORR_ORD; /* result matrix row dimension */ 108 | int yn = CORR_ORD + 1; /* result matrix column dimension */ 109 | 110 | /* helper variables */ 111 | int i = 0, j = 0; 112 | 113 | fprintf(stderr, "Reference array Corr:\n"); 114 | lm_walk_dbl_arr_rowwise(&ref_array_Corr[0][0], ym, yn, cb_dbl, cb_dbl_row_end); 115 | 116 | X = calloc(1 * N, sizeof(double)); 117 | if (!X) { 118 | goto lmt5_fail_sys; 119 | } 120 | for (i = 0; i < N; ++i) { 121 | X[i] = ref_array_X[i]; 122 | } 123 | 124 | Y = lm_corr(X, CORR_ORD, N, NULL); 125 | lm_test(Y != NULL, "lm_corr failed"); 126 | 127 | fprintf(stderr, "Result Y (rowwise):\n"); 128 | lm_walk_dbl_arr_rowwise(Y, ym, yn, cb_dbl, cb_dbl_row_end); 129 | fprintf(stderr, "Result Y (colwise):\n"); 130 | lm_walk_dbl_arr_colwise(Y, ym, yn, cb_dbl, cb_dbl_row_end); 131 | 132 | free(X); 133 | free(Y); 134 | 135 | return 0; 136 | 137 | lmt5_fail_sys: 138 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 139 | return -1; 140 | } 141 | -------------------------------------------------------------------------------- /test/lmtest6.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Introduction test to MUSIC agorithm. 49 | * 50 | * Input is DTMF "1" = { 697 Hz, 1209 Hz }. 8000 Hz, 16 samples: [10, 25]. 51 | */ 52 | 53 | /* Reference data. */ 54 | #define SIGNALS_EXPECTED_N 2 55 | #define SIGNALS_N (2 * (SIGNALS_EXPECTED_N)) /* Need to account for complex signals. */ 56 | #define CORR_ORD ((2 * (SIGNALS_N)) - 1) 57 | #define XN 16 58 | 59 | double ref_array_X[XN] = { -0.2071, -0.7942, -1.1108, -0.6395, 0.5197, 1.6468, 1.9312, 1.1106, -0.3025, -1.3984, -1.5514, -0.8580, 0.0096, 0.3922, 0.1753, -0.1748 }; 60 | 61 | double ref_array_Corr[XN - CORR_ORD][CORR_ORD + 1] = { 62 | { 0.3702, 0.6437, 0.5489, 0.1732, -0.2132, -0.3703, -0.2647, -0.0690 }, 63 | { -0.1008, 0.3702, 0.6437, 0.5489, 0.1732, -0.2132, -0.3703, -0.2647 }, 64 | { -0.4661, -0.1008, 0.3702, 0.6437, 0.5489, 0.1732, -0.2132, -0.3703 }, 65 | { -0.5171, -0.4661, -0.1008, 0.3702, 0.6437, 0.5489, 0.1732, -0.2132 }, 66 | { -0.2860, -0.5171, -0.4661, -0.1008, 0.3702, 0.6437, 0.5489, 0.1732 }, 67 | { 0.0032, -0.2860, -0.5171, -0.4661, -0.1008, 0.3702, 0.6437, 0.5489 }, 68 | { 0.1307, 0.0032, -0.2860, -0.5171, -0.4661, -0.1008, 0.3702, 0.6437 }, 69 | { 0.0584, 0.1307, 0.0032, -0.2860, -0.5171, -0.4661, -0.1008, 0.3702 }, 70 | { -0.0583, 0.0584, 0.1307, 0.0032, -0.2860, -0.5171, -0.4661, -0.1008 } 71 | }; 72 | 73 | double ref_array_V[CORR_ORD + 1][CORR_ORD + 1] = { 74 | { -0.0846, 0.3488, -0.4748, 0.5541, 0.1778, 0.1959, 0.5104, 0.0903 }, 75 | { -0.3811, 0.2414, -0.5224, -0.0641, -0.4035, -0.3970, -0.2920, -0.3374 }, 76 | { -0.5024, -0.0882, -0.2875, -0.3743, 0.5031, 0.0787, -0.1739, 0.4770 }, 77 | { -0.3285, -0.4445, -0.1048, -0.2422, -0.4236, 0.5067, 0.3984, -0.1731 }, 78 | { 0.0505, -0.5803, -0.1801, 0.0589, 0.4140, -0.4871, 0.2815, -0.3700 }, 79 | { 0.3934, -0.3885, -0.4023, 0.1241, -0.3768, -0.1516, -0.0926, 0.5861 }, 80 | { 0.4873, 0.0101, -0.4507, -0.1995, 0.2398, 0.4709, -0.3288, -0.3635 }, 81 | { 0.3041, 0.3564, -0.1024, -0.6568, -0.0480, -0.2430, 0.5190, 0.0891 } 82 | }; 83 | 84 | /* MATLAB result 85 | * 86 | * >> y2(10:25) 87 | * ans = 88 | * Columns 1 through 11 89 | * -0.2071 -0.7942 -1.1108 -0.6395 0.5197 1.6468 1.9312 1.1106 -0.3025 -1.3984 -1.5514 90 | * Columns 12 through 16 91 | * -0.8580 0.0096 0.3922 0.1753 -0.1748 92 | * 93 | * r_x = 94 | * 0.3702 0.6437 0.5489 0.1732 -0.2132 -0.3703 -0.2647 -0.0690 95 | * -0.1008 0.3702 0.6437 0.5489 0.1732 -0.2132 -0.3703 -0.2647 96 | * -0.4661 -0.1008 0.3702 0.6437 0.5489 0.1732 -0.2132 -0.3703 97 | * -0.5171 -0.4661 -0.1008 0.3702 0.6437 0.5489 0.1732 -0.2132 98 | * -0.2860 -0.5171 -0.4661 -0.1008 0.3702 0.6437 0.5489 0.1732 99 | * 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 0.3702 0.6437 0.5489 100 | * 0.1307 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 0.3702 0.6437 101 | * 0.0584 0.1307 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 0.3702 102 | * -0.0583 0.0584 0.1307 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 103 | * 104 | * eigenvects (U) (not needed in MUSIC) = 105 | * 106 | * -0.0846 0.3488 -0.4748 0.5541 0.1778 0.1959 0.5104 0.0903 107 | * -0.3811 0.2414 -0.5224 -0.0641 -0.4035 -0.3970 -0.2920 -0.3374 108 | * -0.5024 -0.0882 -0.2875 -0.3743 0.5031 0.0787 -0.1739 0.4770 109 | * -0.3285 -0.4445 -0.1048 -0.2422 -0.4236 0.5067 0.3984 -0.1731 110 | * 0.0505 -0.5803 -0.1801 0.0589 0.4140 -0.4871 0.2815 -0.3700 111 | * 0.3934 -0.3885 -0.4023 0.1241 -0.3768 -0.1516 -0.0926 0.5861 112 | * 0.4873 0.0101 -0.4507 -0.1995 0.2398 0.4709 -0.3288 -0.3635 113 | * 0.3041 0.3564 -0.1024 -0.6568 -0.0480 -0.2430 0.5190 0.0891 114 | * 115 | * eigenvals = (squared) 116 | * 117 | * 5.3295 118 | * 4.3849 119 | * 0.4334 120 | * 0.2227 121 | * 0.0000 122 | * 0.0000 123 | * 0.0000 124 | * 0.0000 125 | * 126 | * eigenvects V = 127 | * 128 | * -0.0846 0.3488 -0.4748 0.5541 0.1778 0.1959 0.5104 0.0903 129 | * -0.3811 0.2414 -0.5224 -0.0641 -0.4035 -0.3970 -0.2920 -0.3374 130 | * -0.5024 -0.0882 -0.2875 -0.3743 0.5031 0.0787 -0.1739 0.4770 131 | * -0.3285 -0.4445 -0.1048 -0.2422 -0.4236 0.5067 0.3984 -0.1731 132 | * 0.0505 -0.5803 -0.1801 0.0589 0.4140 -0.4871 0.2815 -0.3700 133 | * 0.3934 -0.3885 -0.4023 0.1241 -0.3768 -0.1516 -0.0926 0.5861 134 | * 0.4873 0.0101 -0.4507 -0.1995 0.2398 0.4709 -0.3288 -0.3635 135 | * 0.3041 0.3564 -0.1024 -0.6568 -0.0480 -0.2430 0.5190 0.0891 136 | */ 137 | 138 | void cb_dbl(double *x) 139 | { 140 | fprintf(stderr, "\t%f", *x); 141 | } 142 | 143 | void cb_dbl_row_end(void) 144 | { 145 | fprintf(stderr, "\n"); 146 | } 147 | 148 | 149 | int main (void) 150 | { 151 | /* Correlation */ 152 | double *X; /* samples */ 153 | double *Y; /* correlation result correlation matrix of X */ 154 | int ym = XN - CORR_ORD; /* correlation result matrix row dimension */ 155 | int yn = CORR_ORD + 1; /* correlation result matrix column dimension */ 156 | 157 | /* SVD */ 158 | double WORK_QUERY = 0; 159 | char JOBU = 'A'; 160 | char JOBVT = 'A'; 161 | int M = ym; 162 | int N = yn; 163 | double *A = NULL; /* Input to SVD: correlation matrix columnwise for LAPACK. */ 164 | int LDA = ym; 165 | double *S = NULL; 166 | double *U = NULL; 167 | int LDU = ym; 168 | double *VT = NULL; 169 | int LDVT = yn ; 170 | double *WORK; 171 | int LWORK = 4 * M * N * M *N + 6 * M * N + lm_max(M, N); 172 | int INFO; 173 | 174 | int i = 0, j = 0; 175 | 176 | /* Peak detection */ 177 | double Fs = 8000; 178 | double dt = 1/Fs; 179 | double t = 0; 180 | double omega = 0; 181 | double a_real[yn]; /* delays, real(exp(j*omega*t)) */ 182 | double a_imag[yn]; /* delays, imag(exp(j*omega*t)) */ 183 | double av_real = 0; /* real result of a * V multiplication */ 184 | double av_imag = 0; /* imaginary result of a * V multiplication */ 185 | uint16_t f = 0; 186 | double peak = 0; 187 | double peak_min, peak_max, peak_697, peak_1209; /* test */ 188 | 189 | 190 | fprintf(stderr, "Reference array Corr:\n"); 191 | lm_walk_dbl_arr_rowwise(&ref_array_Corr[0][0], ym, yn, cb_dbl, cb_dbl_row_end); 192 | 193 | fprintf(stderr, "Reference array V:\n"); 194 | lm_walk_dbl_arr_rowwise(&ref_array_V[0][0], N, N, cb_dbl, cb_dbl_row_end); 195 | 196 | X = calloc(1 * XN, sizeof(double)); 197 | if (!X) { 198 | goto lmt6_fail_sys; 199 | } 200 | for (i = 0; i < XN; ++i) { 201 | X[i] = ref_array_X[i]; 202 | } 203 | 204 | Y = lm_corr(X, CORR_ORD, XN, NULL); 205 | lm_test(Y != NULL, "lm_corr failed"); 206 | 207 | fprintf(stderr, "Result Corr (rowwise):\n"); 208 | lm_walk_dbl_arr_rowwise(Y, ym, yn, cb_dbl, cb_dbl_row_end); 209 | fprintf(stderr, "Result Corr (colwise):\n"); 210 | lm_walk_dbl_arr_colwise(Y, ym, yn, cb_dbl, cb_dbl_row_end); 211 | 212 | 213 | /* Call dgesvd_ with lwork = -1 to query optimal workspace size. */ 214 | 215 | A = calloc(M * N, sizeof(double)); 216 | if (!A) { 217 | goto lmt6_fail_sys; 218 | } 219 | for (i = 0; i < M; ++i) { 220 | for (j = 0; j < N; ++j) { 221 | A[j * M + i] = Y[i * yn + j]; 222 | } 223 | } 224 | 225 | S = calloc(lm_min(M, N), sizeof(double)); 226 | if (!S) { 227 | goto lmt6_fail_sys; 228 | } 229 | 230 | U = calloc(LDU * M, sizeof(double)); 231 | if (!U) { 232 | goto lmt6_fail_sys; 233 | } 234 | 235 | VT = calloc(LDVT * N, sizeof(double)); 236 | if (!VT) { 237 | goto lmt6_fail_sys; 238 | } 239 | 240 | LWORK = -1; 241 | dgesvd_(&JOBU, &JOBVT, &M, &N, A, &LDA, S, U, &LDU, VT, &LDVT, &WORK_QUERY, &LWORK, &INFO); 242 | if (INFO != 0) { 243 | if (INFO < 0) { 244 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"the %d-th argument had illegal value\"\n", INFO); 245 | } else { 246 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"DBDSDC didn't converge, updating process failed\"\n"); 247 | } 248 | return -1; 249 | } 250 | 251 | LWORK = (int) WORK_QUERY; 252 | WORK = calloc(LWORK, sizeof(double)); 253 | if (!WORK) { 254 | goto lmt6_fail_sys; 255 | } 256 | 257 | fprintf(stderr, "LAPACK's dgesvd_ query optimal results: LDA %d, LDU %d, LDVT %d, LWORK %d, WORK_QUERY %f\n", LDA, LDU, LDVT, LWORK, WORK_QUERY); 258 | fprintf(stderr, "Rest of params: M %d, N %d\n", M, N); 259 | 260 | fprintf(stderr, "Matrix A submitted to LAPACK:\n"); 261 | lm_walk_dbl_arr_rowwise(A, M, N, cb_dbl, cb_dbl_row_end); 262 | 263 | /* Compute SVD. */ 264 | dgesvd_(&JOBU, &JOBVT, &M, &N, A, &LDA, S, U, &LDU, VT, &LDVT, WORK, &LWORK, &INFO); 265 | if (INFO != 0) { 266 | if (INFO < 0) { 267 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"the %d-th argument had illegal value\"\n", INFO); 268 | } else { 269 | fprintf(stderr, "Error on LAPACK's dgesvd_ query: \"DBDSDC didn't converge, updating process failed\"\n"); 270 | } 271 | return -1; 272 | } 273 | 274 | fprintf(stderr, "LAPACK's dgesvd_ SVD completed\n"); 275 | 276 | fprintf(stderr, "Result A:\n"); 277 | lm_walk_dbl_arr_rowwise(A, M, N, cb_dbl, cb_dbl_row_end); 278 | 279 | fprintf(stderr, "Result U (rowwise):\n"); 280 | lm_walk_dbl_arr_rowwise(U, LDU, M, cb_dbl, cb_dbl_row_end); 281 | fprintf(stderr, "Result U (colwise):\n"); 282 | lm_walk_dbl_arr_colwise(U, LDU, M, cb_dbl, cb_dbl_row_end); 283 | 284 | fprintf(stderr, "Result S (rowwise):\n"); 285 | lm_walk_dbl_arr_rowwise(S, M, 1, cb_dbl, cb_dbl_row_end); 286 | 287 | fprintf(stderr, "Result VT (rowwise):\n"); 288 | lm_walk_dbl_arr_rowwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 289 | fprintf(stderr, "Result VT (colwise):\n"); 290 | lm_walk_dbl_arr_colwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 291 | 292 | /* Peak detection */ 293 | 294 | f = 0; 295 | peak_min = DBL_MAX; 296 | peak_max = DBL_MIN; 297 | peak_697 = peak_1209 = DBL_MIN; 298 | 299 | while (f < 4000) { 300 | 301 | i = 0; 302 | t = 0; 303 | while (i < N) { 304 | omega = 2 * M_PI * f; 305 | a_real[i] = cos(omega * t); 306 | a_imag[i] = sin(omega * t); 307 | t += dt; 308 | ++i; 309 | } 310 | 311 | i = 0; 312 | av_real = av_imag = 0; 313 | while (i < N) { 314 | av_real += a_real[i] * (VT[4 + i * yn] + VT[5 + i * yn] + VT[6 + i * yn] + VT[7 + i * yn]); 315 | av_imag += a_imag[i] * (VT[4 + i * yn] + VT[5 + i * yn] + VT[6 + i * yn] + VT[7 + i * yn]); 316 | ++i; 317 | } 318 | 319 | peak = 1 / (av_real * av_real + av_imag * av_imag); 320 | 321 | printf("peak [%04u] = %f\n", f, peak); 322 | 323 | if (peak > peak_max) { 324 | peak_max = peak; 325 | } 326 | if (peak < peak_min) { 327 | peak_min = peak; 328 | } 329 | if (f == 697) { 330 | peak_697 = peak; 331 | } 332 | if (f == 1209) { 333 | peak_1209 = peak; 334 | } 335 | 336 | f++; 337 | } 338 | 339 | printf("\npeak_min = %f\n", peak_min); 340 | printf("peak_max = %f\n", peak_max); 341 | printf("peak_697 = %f\n", peak_697); 342 | printf("peak_1209 = %f\n", peak_1209); 343 | printf("fabs((peak_697 - peak_1209) / (peak_max - peak_min)) = %f, %f, %f\n", fabs(peak_697 - peak_1209), fabs(peak_max - peak_min), fabs(peak_697 - peak_1209) / (peak_max - peak_min)); 344 | printf("fabs(peak_697 - peak_min) / (peak_max - peak_min) = %f, %f, %f\n", fabs(peak_697 - peak_min), peak_max - peak_min, fabs(peak_697 - peak_min) / (peak_max - peak_min)); 345 | printf("fabs(peak_1209 - peak_min) / (peak_max - peak_min) = %f, %f, %f\n", fabs(peak_1209 - peak_min), peak_max - peak_min, fabs(peak_1209 - peak_min) / (peak_max - peak_min)); 346 | lm_test(peak_697 > peak_min || peak_1209 == peak_min, "Minimum peak not less than peak 697 Hz and not less than peak 1209 Hz"); 347 | lm_test(peak_697 == peak_max || peak_1209 == peak_max, "Maximum peak not at 697 Hz and not at 1209 Hz"); 348 | lm_test(fabs(peak_697 - peak_1209) < 0.25 * fabs(peak_max - peak_min), "Peaks at 697 Hz and 1209 Hz differ more than 0.25 * fabs(max - min)"); 349 | lm_test((fabs(peak_697 - peak_min) > 0.75 * fabs(peak_max - peak_min)), "Peak at 697 Hz not more than 0.75 * fabs(max - min)"); 350 | lm_test((fabs(peak_1209 - peak_min) > 0.75 * fabs(peak_max - peak_min)), "Peak at 1209 Hz not more than 0.75 * fabs(max - min)"); 351 | 352 | free(X); 353 | free(Y); 354 | 355 | free(WORK); 356 | free(A); 357 | free(S); 358 | free(U); 359 | free(VT); 360 | 361 | return 0; 362 | 363 | lmt6_fail_sys: 364 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 365 | return -1; 366 | } 367 | -------------------------------------------------------------------------------- /test/lmtest7.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test of MUSIC agorithm. 49 | * 50 | * Input is DTMF "1" = { 697 Hz, 1209 Hz }. 8000 Hz, 16 samples: [10, 25]. 51 | */ 52 | 53 | /* Reference data. */ 54 | #define SIGNALS_EXPECTED_N 2 55 | #define SIGNALS_N (2 * (SIGNALS_EXPECTED_N)) /* Need to account for complex signals. */ 56 | #define CORR_ORD ((2 * (SIGNALS_N)) - 1) 57 | #define XN 16 58 | 59 | double ref_array_X[XN] = { -0.2071, -0.7942, -1.1108, -0.6395, 0.5197, 1.6468, 1.9312, 1.1106, -0.3025, -1.3984, -1.5514, -0.8580, 0.0096, 0.3922, 0.1753, -0.1748 }; 60 | 61 | /* MATLAB result 62 | * 63 | * >> y2(10:25) 64 | * ans = 65 | * Columns 1 through 11 66 | * -0.2071 -0.7942 -1.1108 -0.6395 0.5197 1.6468 1.9312 1.1106 -0.3025 -1.3984 -1.5514 67 | * Columns 12 through 16 68 | * -0.8580 0.0096 0.3922 0.1753 -0.1748 69 | * 70 | * r_x = 71 | * 0.3702 0.6437 0.5489 0.1732 -0.2132 -0.3703 -0.2647 -0.0690 72 | * -0.1008 0.3702 0.6437 0.5489 0.1732 -0.2132 -0.3703 -0.2647 73 | * -0.4661 -0.1008 0.3702 0.6437 0.5489 0.1732 -0.2132 -0.3703 74 | * -0.5171 -0.4661 -0.1008 0.3702 0.6437 0.5489 0.1732 -0.2132 75 | * -0.2860 -0.5171 -0.4661 -0.1008 0.3702 0.6437 0.5489 0.1732 76 | * 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 0.3702 0.6437 0.5489 77 | * 0.1307 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 0.3702 0.6437 78 | * 0.0584 0.1307 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 0.3702 79 | * -0.0583 0.0584 0.1307 0.0032 -0.2860 -0.5171 -0.4661 -0.1008 80 | * 81 | * eigenvects (U) (not needed in MUSIC) = 82 | * 83 | * -0.0846 0.3488 -0.4748 0.5541 0.1778 0.1959 0.5104 0.0903 84 | * -0.3811 0.2414 -0.5224 -0.0641 -0.4035 -0.3970 -0.2920 -0.3374 85 | * -0.5024 -0.0882 -0.2875 -0.3743 0.5031 0.0787 -0.1739 0.4770 86 | * -0.3285 -0.4445 -0.1048 -0.2422 -0.4236 0.5067 0.3984 -0.1731 87 | * 0.0505 -0.5803 -0.1801 0.0589 0.4140 -0.4871 0.2815 -0.3700 88 | * 0.3934 -0.3885 -0.4023 0.1241 -0.3768 -0.1516 -0.0926 0.5861 89 | * 0.4873 0.0101 -0.4507 -0.1995 0.2398 0.4709 -0.3288 -0.3635 90 | * 0.3041 0.3564 -0.1024 -0.6568 -0.0480 -0.2430 0.5190 0.0891 91 | * 92 | * eigenvals = (squared) 93 | * 94 | * 5.3295 95 | * 4.3849 96 | * 0.4334 97 | * 0.2227 98 | * 0.0000 99 | * 0.0000 100 | * 0.0000 101 | * 0.0000 102 | * 103 | * eigenvects V = 104 | * 105 | * -0.0846 0.3488 -0.4748 0.5541 0.1778 0.1959 0.5104 0.0903 106 | * -0.3811 0.2414 -0.5224 -0.0641 -0.4035 -0.3970 -0.2920 -0.3374 107 | * -0.5024 -0.0882 -0.2875 -0.3743 0.5031 0.0787 -0.1739 0.4770 108 | * -0.3285 -0.4445 -0.1048 -0.2422 -0.4236 0.5067 0.3984 -0.1731 109 | * 0.0505 -0.5803 -0.1801 0.0589 0.4140 -0.4871 0.2815 -0.3700 110 | * 0.3934 -0.3885 -0.4023 0.1241 -0.3768 -0.1516 -0.0926 0.5861 111 | * 0.4873 0.0101 -0.4507 -0.1995 0.2398 0.4709 -0.3288 -0.3635 112 | * 0.3041 0.3564 -0.1024 -0.6568 -0.0480 -0.2430 0.5190 0.0891 113 | */ 114 | 115 | void cb_dbl(double *x) 116 | { 117 | fprintf(stderr, "\t%f", *x); 118 | } 119 | 120 | void cb_dbl_row_end(void) 121 | { 122 | fprintf(stderr, "\n"); 123 | } 124 | 125 | 126 | int main (void) 127 | { 128 | int ym = XN - CORR_ORD; /* correlation result matrix row dimension */ 129 | int yn = CORR_ORD + 1; /* correlation result matrix column dimension */ 130 | 131 | uint16_t x[XN]; /* samples */ 132 | 133 | int M = ym; 134 | int N = yn; 135 | double *VT = NULL; 136 | int LDVT = yn ; 137 | double *WORK = NULL; 138 | int LWORK = 4 * M * N * M *N + 6 * M * N + lm_max(M, N); 139 | 140 | int i = 0, j = 0; 141 | 142 | /* Peak detection */ 143 | double Fs = 8000; 144 | double dt = 1/Fs; 145 | double t = 0; 146 | double omega = 0; 147 | double a_real[yn]; /* delays, real(exp(j*omega*t)) */ 148 | double a_imag[yn]; /* delays, imag(exp(j*omega*t)) */ 149 | double av_real = 0; /* real result of a * V multiplication */ 150 | double av_imag = 0; /* imaginary result of a * V multiplication */ 151 | uint16_t f = 0; 152 | double peak = 0; 153 | double peak_min, peak_max, peak_697, peak_1209; /* test */ 154 | 155 | int res; 156 | 157 | lm_detector_t d; 158 | 159 | 160 | memset(&d, 0, sizeof(d)); 161 | 162 | d.fs = 8000; 163 | d.dt = 1.0 / (double) d.fs; 164 | d.x_n = XN; 165 | 166 | d.signals_n = 2; /* we are searching for 2 frequencies */ 167 | d.svd.JOBU = 'N'; 168 | d.svd.JOBVT = 'A'; /* we want eigenvalues */ 169 | 170 | res = lm_detect(&d, &ref_array_X[0], XN); 171 | if (res != 0) { 172 | printf("lm_detect failed with error %d\n", res); 173 | goto lmt7_fail_sys; 174 | } 175 | lm_test(res == 0, "lm_detect failed"); 176 | 177 | lm_test(d.svd.M == M, "lm_detect failed: M"); 178 | lm_test(d.svd.N == N, "lm_detect failed: N"); 179 | VT = d.svd.VT; 180 | 181 | fprintf(stderr, "Result VT (rowwise):\n"); 182 | lm_walk_dbl_arr_rowwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 183 | fprintf(stderr, "Result VT (colwise):\n"); 184 | lm_walk_dbl_arr_colwise(VT, LDVT, N, cb_dbl, cb_dbl_row_end); 185 | 186 | /* Peak detection */ 187 | 188 | f = 0; 189 | peak_min = DBL_MAX; 190 | peak_max = DBL_MIN; 191 | peak_697 = peak_1209 = DBL_MIN; 192 | 193 | while (f < 4000) { 194 | 195 | i = 0; 196 | t = 0; 197 | while (i < N) { 198 | omega = 2 * M_PI * f; 199 | a_real[i] = cos(omega * t); 200 | a_imag[i] = sin(omega * t); 201 | t += dt; 202 | ++i; 203 | } 204 | 205 | i = 0; 206 | av_real = av_imag = 0; 207 | while (i < N) { 208 | av_real += a_real[i] * (VT[4 + i * yn] + VT[5 + i * yn] + VT[6 + i * yn] + VT[7 + i * yn]); 209 | av_imag += a_imag[i] * (VT[4 + i * yn] + VT[5 + i * yn] + VT[6 + i * yn] + VT[7 + i * yn]); 210 | ++i; 211 | } 212 | 213 | peak = 1 / (av_real * av_real + av_imag * av_imag); 214 | 215 | printf("peak [%04u] = %f\n", f, peak); 216 | printf("peak got from lm_detect [%04u] = %f\n", f, d.detections[f].peak); 217 | 218 | if (peak > peak_max) { 219 | peak_max = peak; 220 | } 221 | if (peak < peak_min) { 222 | peak_min = peak; 223 | } 224 | if (f == 697) { 225 | peak_697 = peak; 226 | } 227 | if (f == 1209) { 228 | peak_1209 = peak; 229 | } 230 | 231 | f++; 232 | } 233 | 234 | printf("\npeak_min = %f\n", peak_min); 235 | printf("peak_max = %f\n", peak_max); 236 | printf("peak_697 = %f\n", peak_697); 237 | printf("peak_1209 = %f\n", peak_1209); 238 | printf("fabs((peak_697 - peak_1209) / (peak_max - peak_min)) = %f, %f, %f\n", fabs(peak_697 - peak_1209), fabs(peak_max - peak_min), fabs(peak_697 - peak_1209) / (peak_max - peak_min)); 239 | printf("fabs(peak_697 - peak_min) / (peak_max - peak_min) = %f, %f, %f\n", fabs(peak_697 - peak_min), peak_max - peak_min, fabs(peak_697 - peak_min) / (peak_max - peak_min)); 240 | printf("fabs(peak_1209 - peak_min) / (peak_max - peak_min) = %f, %f, %f\n", fabs(peak_1209 - peak_min), peak_max - peak_min, fabs(peak_1209 - peak_min) / (peak_max - peak_min)); 241 | 242 | printf(".info.peak_min = %f\n", d.info.peak_min); 243 | printf(".info.peak_max = %f\n", d.info.peak_max); 244 | printf(".info.peak_min_freq = %u\n", d.info.peak_min_freq); 245 | printf(".info.peak_max_freq = %u\n", d.info.peak_max_freq); 246 | printf(".info.peak_697 = %f\n", d.info.peak_697); 247 | printf(".info.peak_1209 = %f\n", d.info.peak_1209); 248 | 249 | lm_test(peak_697 > peak_min || peak_1209 == peak_min, "Minimum peak not less than peak 697 Hz and not less than peak 1209 Hz"); 250 | lm_test(peak_697 == peak_max || peak_1209 == peak_max, "Maximum peak not at 697 Hz and not at 1209 Hz"); 251 | lm_test(fabs(peak_697 - peak_1209) < 0.25 * fabs(peak_max - peak_min), "Peaks at 697 Hz and 1209 Hz differ more than 0.25 * fabs(max - min)"); 252 | lm_test((fabs(peak_697 - peak_min) > 0.75 * fabs(peak_max - peak_min)), "Peak at 697 Hz not more than 0.75 * fabs(max - min)"); 253 | lm_test((fabs(peak_1209 - peak_min) > 0.75 * fabs(peak_max - peak_min)), "Peak at 1209 Hz not more than 0.75 * fabs(max - min)"); 254 | 255 | lm_deinit(&d); 256 | 257 | return 0; 258 | 259 | lmt7_fail_sys: 260 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 261 | return -1; 262 | } 263 | -------------------------------------------------------------------------------- /test/lmtest8.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test of MUSIC agorithm. 49 | * 50 | * Input is external file. 51 | */ 52 | 53 | #define XN 16 54 | 55 | 56 | // convert 16 bit ints to doubles 57 | void intToFloat(int16_t *input, double *output, int length) 58 | { 59 | int i; 60 | 61 | for (i = 0; i < length; i++) { 62 | output[i] = (double)input[i] / ((double) 1.0); 63 | } 64 | } 65 | 66 | int main (int argc, char **argv) 67 | { 68 | uint16_t x[XN]; /* samples */ 69 | int i = 0, j = 0; 70 | 71 | int res; 72 | 73 | /* Testing data */ 74 | int16_t intData[XN]; 75 | double inputData[XN]; 76 | int numWords = 0; 77 | int sampleCount = 0; 78 | const char *inFileName = "fraction-131-140_16bit_s.raw"; 79 | FILE *inFile = NULL; 80 | uint32_t frame_n = 0; 81 | 82 | lm_detector_t d; 83 | 84 | 85 | if (argc == 2) { 86 | inFileName = argv[1]; 87 | } 88 | 89 | printf("input file name = %s\n",inFileName); 90 | 91 | inFile = fopen(inFileName,"rb"); 92 | if (!inFile) { 93 | printf("Cannot open input file %s\n",inFileName); 94 | goto lmt8_fail_sys; 95 | } 96 | 97 | memset(&d, 0, sizeof(d)); 98 | 99 | d.fs = 8000; 100 | d.dt = 1.0 / (double) d.fs; 101 | d.x_n = XN; 102 | d.signals_n = 2; /* we are searching for 2 frequencies */ 103 | d.svd.JOBU = 'N'; 104 | d.svd.JOBVT = 'A'; /* we want eigenvalues */ 105 | 106 | sampleCount = 0; 107 | 108 | numWords = fread(intData, sizeof(int16_t), XN, inFile); 109 | 110 | // until end of file 111 | while(numWords == XN) 112 | { 113 | frame_n++; 114 | printf("\nframe [%u]:\n", frame_n); 115 | 116 | intToFloat(intData, inputData, numWords); 117 | 118 | sampleCount += XN; 119 | printf("samples_n = [%d, %d)\n", sampleCount - XN, sampleCount); 120 | 121 | res = lm_detect(&d, inputData, XN); 122 | if (res != 0) { 123 | printf("lm_detect failed with error %d\n", res); 124 | goto lmt8_fail_sys; 125 | } 126 | lm_test(res == 0, "lm_detect failed"); 127 | 128 | /* Peak detection */ 129 | 130 | printf(".info.peak_min = %f\n", d.info.peak_min); 131 | printf(".info.peak_max = %f\n", d.info.peak_max); 132 | printf(".info.peak_min_freq = %u\n", d.info.peak_min_freq); 133 | printf(".info.peak_max_freq = %u\n", d.info.peak_max_freq); 134 | printf(".info.peak_697 = %f\n", d.info.peak_697); 135 | printf(".info.peak_770 = %f\n", d.info.peak_770); 136 | printf(".info.peak_852 = %f\n", d.info.peak_852); 137 | printf(".info.peak_941 = %f\n", d.info.peak_941); 138 | printf(".info.peak_1209 = %f\n", d.info.peak_1209); 139 | printf(".info.peak_1336 = %f\n", d.info.peak_1336); 140 | printf(".info.peak_1477 = %f\n", d.info.peak_1477); 141 | printf(".info.peak_1633 = %f\n", d.info.peak_1633); 142 | 143 | numWords = fread(intData, sizeof(int16_t), XN, inFile ); 144 | } 145 | 146 | printf("\nFinished. sampleCount = %d\n",sampleCount); 147 | fclose(inFile); 148 | 149 | lm_deinit(&d); 150 | 151 | return 0; 152 | 153 | lmt8_fail_sys: 154 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 155 | return -1; 156 | } 157 | -------------------------------------------------------------------------------- /test/lmtest9.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of libmusic - frequency detection library. 3 | * 4 | * Copyright (c) 2018 Data And Signal - IT Solutions 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * Redistributions in binary form must reproduce the above 15 | * copyright notice, this list of conditions and the following 16 | * disclaimer in the documentation and/or other materials provided 17 | * with the distribution. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 | * OF THE POSSIBILITY OF SUCH DAMAGE. 31 | * 32 | * Piotr Gregor 33 | * Data And Signal - IT Solutions 34 | * 35 | */ 36 | 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #include "lm.h" 45 | 46 | 47 | /** 48 | * Test of DTMF detection with MUSIC agorithm. 49 | * Test all results for DTMF frequencies are same: 50 | * 51 | * 1. using lm_standard_init for standard detector but with frequencies limited to DTMF frequencies. 52 | * 2. using lm_standard_init for standard detector with default settings. 53 | * 3. using lm_dtmf_init for default DTMF detection 54 | * 55 | * Input is external file. 56 | */ 57 | 58 | #define Fs 8000 59 | #define XN 16 60 | #define SIGNALS_N 2 61 | 62 | 63 | // convert 16 bit ints to doubles 64 | void intToFloat(int16_t *input, double *output, int length) 65 | { 66 | int i; 67 | 68 | for (i = 0; i < length; i++) { 69 | output[i] = (double)input[i] / ((double) 1.0); 70 | } 71 | } 72 | 73 | int main (int argc, char **argv) 74 | { 75 | uint16_t x[XN]; /* samples */ 76 | 77 | int i = 0, j = 0; 78 | 79 | int res; 80 | 81 | /* Testing data */ 82 | int16_t intData[XN]; 83 | double inputData[XN]; 84 | int numWords = 0; 85 | int sampleCount = 0; 86 | const char *inFileName = "fraction-131-140_16bit_s.raw"; 87 | FILE *inFile = NULL; 88 | uint32_t frame_n = 0; 89 | 90 | lm_detector_t d; 91 | lm_detection_t *detections = NULL; 92 | 93 | lm_detection_info_t info1; 94 | 95 | detections = calloc(8, sizeof(lm_detection_t)); 96 | if (!detections) { 97 | printf("Can't get memory for testing\n"); 98 | goto lmt9_fail_sys; 99 | } 100 | 101 | i = 0; 102 | while (i < 8) { 103 | detections[i].freq = lm_dtmf_freqs[i]; 104 | ++i; 105 | } 106 | 107 | if (argc == 2) { 108 | inFileName = argv[1]; 109 | } 110 | 111 | printf("input file name = %s\n",inFileName); 112 | 113 | inFile = fopen(inFileName,"rb"); 114 | if (!inFile) { 115 | printf("Cannot open input file %s\n",inFileName); 116 | goto lmt9_fail_sys; 117 | } 118 | 119 | /** 120 | * Init standard MUSIC detector but with frequency range limited to 8 frequencies 121 | * (DTMF frequencies) specified in @detections array. 122 | * Detects only those 8 frequencies. 123 | */ 124 | lm_test(lm_standard_init(&d, Fs, XN, SIGNALS_N, detections, 8) == 0, "lm_standard_init failed"); 125 | 126 | frame_n = 0; 127 | sampleCount = 0; 128 | 129 | numWords = fread(intData, sizeof(int16_t), XN, inFile); 130 | 131 | // until end of file 132 | while(numWords == XN) 133 | { 134 | frame_n++; 135 | printf("\nframe [%u]:\n", frame_n); 136 | 137 | intToFloat(intData, inputData, numWords); 138 | 139 | sampleCount += XN; 140 | printf("samples_n = [%d, %d)\n", sampleCount - XN, sampleCount); 141 | 142 | res = lm_detect(&d, inputData, XN); 143 | if (res != 0) { 144 | printf("lm_detect failed with error %d\n", res); 145 | goto lmt9_fail_sys; 146 | } 147 | lm_test(res == 0, "lm_detect failed"); 148 | 149 | /* Peak detection */ 150 | 151 | printf(".info.peak_min = %f\n", d.info.peak_min); 152 | printf(".info.peak_max = %f\n", d.info.peak_max); 153 | printf(".info.peak_min_freq = %u\n", d.info.peak_min_freq); 154 | printf(".info.peak_max_freq = %u\n", d.info.peak_max_freq); 155 | printf(".info.peak_697 = %f\n", d.info.peak_697); 156 | printf(".info.peak_770 = %f\n", d.info.peak_770); 157 | printf(".info.peak_852 = %f\n", d.info.peak_852); 158 | printf(".info.peak_941 = %f\n", d.info.peak_941); 159 | printf(".info.peak_1209 = %f\n", d.info.peak_1209); 160 | printf(".info.peak_1336 = %f\n", d.info.peak_1336); 161 | printf(".info.peak_1477 = %f\n", d.info.peak_1477); 162 | 163 | numWords = fread(intData, sizeof(int16_t), XN, inFile ); 164 | } 165 | 166 | printf("\nFinished. sampleCount = %d\n",sampleCount); 167 | fclose( inFile ); 168 | 169 | /* Copy detection info for next test. */ 170 | memcpy(&info1, &d.info, sizeof(lm_detection_info_t)); 171 | 172 | lm_deinit(&d); 173 | 174 | /* Run test again, now with detections == NULL, i.e. want all frequencies in range [0, Fs / 2). */ 175 | 176 | printf("input file name = %s\n",inFileName); 177 | 178 | inFile = fopen(inFileName,"rb"); 179 | if (!inFile) { 180 | printf("Cannot open input file %s\n",inFileName); 181 | goto lmt9_fail_sys; 182 | } 183 | 184 | memset(&d, 0, sizeof(d)); 185 | 186 | /** 187 | * Init standard MUSIC detector. 188 | * Detects all frequencies in range [0, Fs / 2). 189 | */ 190 | lm_test(lm_standard_init(&d, Fs, XN, SIGNALS_N, NULL, 0) == 0, "lm_standard_init failed"); 191 | 192 | frame_n = 0; 193 | sampleCount = 0; 194 | 195 | numWords = fread(intData, sizeof(int16_t), XN, inFile); 196 | 197 | // until end of file 198 | while(numWords == XN) 199 | { 200 | frame_n++; 201 | printf("\nframe [%u]:\n", frame_n); 202 | 203 | intToFloat(intData, inputData, numWords); 204 | 205 | sampleCount += XN; 206 | printf("samples_n = [%d, %d)\n", sampleCount - XN, sampleCount); 207 | 208 | res = lm_detect(&d, inputData, XN); 209 | if (res != 0) { 210 | printf("lm_detect failed with error %d\n", res); 211 | goto lmt9_fail_sys; 212 | } 213 | lm_test(res == 0, "lm_detect failed"); 214 | 215 | /* Peak detection */ 216 | 217 | printf(".info.peak_min = %f\n", d.info.peak_min); 218 | printf(".info.peak_max = %f\n", d.info.peak_max); 219 | printf(".info.peak_min_freq = %u\n", d.info.peak_min_freq); 220 | printf(".info.peak_max_freq = %u\n", d.info.peak_max_freq); 221 | printf(".info.peak_697 = %f\n", d.info.peak_697); 222 | printf(".info.peak_770 = %f\n", d.info.peak_770); 223 | printf(".info.peak_852 = %f\n", d.info.peak_852); 224 | printf(".info.peak_941 = %f\n", d.info.peak_941); 225 | printf(".info.peak_1209 = %f\n", d.info.peak_1209); 226 | printf(".info.peak_1336 = %f\n", d.info.peak_1336); 227 | printf(".info.peak_1477 = %f\n", d.info.peak_1477); 228 | printf(".info.peak_1633 = %f\n", d.info.peak_1633); 229 | 230 | numWords = fread(intData, sizeof(int16_t), XN, inFile ); 231 | } 232 | 233 | printf("\nFinished. sampleCount = %d\n",sampleCount); 234 | fclose(inFile); 235 | 236 | /* Copy detection info for next test. */ 237 | memcpy(&info1, &d.info, sizeof(lm_detection_info_t)); 238 | 239 | /* Test compare. */ 240 | lm_test(info1.peak_697 == d.info.peak_697, "peak error: 697 Hz (standard detector: default)"); 241 | lm_test(info1.peak_770 == d.info.peak_770, "peak error: 770 Hz (standard detector: default)"); 242 | lm_test(info1.peak_852 == d.info.peak_852, "peak error: 852 Hz (standard detector: default)"); 243 | lm_test(info1.peak_941 == d.info.peak_941, "peak error: 941 Hz (standard detector: default)"); 244 | lm_test(info1.peak_1209 == d.info.peak_1209, "peak error: 1209 Hz (standard detector: default)"); 245 | lm_test(info1.peak_1336 == d.info.peak_1336, "peak error: 1336 Hz (standard detector: default)"); 246 | lm_test(info1.peak_1477 == d.info.peak_1477, "peak error: 1477 Hz (standard detector: default)"); 247 | lm_test(info1.peak_1633 == d.info.peak_1633, "peak error: 1633 Hz (standard detector: default)"); 248 | 249 | lm_deinit(&d); 250 | 251 | /* Run test again, now using standard DTMF detector. */ 252 | 253 | printf("input file name = %s\n",inFileName); 254 | 255 | inFile = fopen(inFileName,"rb"); 256 | if (!inFile) { 257 | printf("Cannot open input file %s\n",inFileName); 258 | goto lmt9_fail_sys; 259 | } 260 | 261 | /** 262 | * Init standard MUSIC DTMF detector. 263 | * Detects only 8 DTMF frequencies. 264 | */ 265 | lm_test(lm_dtmf_init(&d, Fs, XN) == 0, "lm_dtmf_init failed"); 266 | 267 | frame_n = 0; 268 | sampleCount = 0; 269 | 270 | numWords = fread(intData, sizeof(int16_t), XN, inFile); 271 | 272 | // until end of file 273 | while(numWords == XN) 274 | { 275 | frame_n++; 276 | printf("\nframe [%u]:\n", frame_n); 277 | 278 | intToFloat(intData, inputData, numWords); 279 | 280 | sampleCount += XN; 281 | printf("samples_n = [%d, %d)\n", sampleCount - XN, sampleCount); 282 | 283 | res = lm_detect(&d, inputData, XN); 284 | if (res != 0) { 285 | printf("lm_detect failed with error %d\n", res); 286 | goto lmt9_fail_sys; 287 | } 288 | lm_test(res == 0, "lm_detect failed"); 289 | 290 | /* Peak detection */ 291 | 292 | printf(".info.peak_min = %f\n", d.info.peak_min); 293 | printf(".info.peak_max = %f\n", d.info.peak_max); 294 | printf(".info.peak_min_freq = %u\n", d.info.peak_min_freq); 295 | printf(".info.peak_max_freq = %u\n", d.info.peak_max_freq); 296 | printf(".info.peak_697 = %f\n", d.info.peak_697); 297 | printf(".info.peak_770 = %f\n", d.info.peak_770); 298 | printf(".info.peak_852 = %f\n", d.info.peak_852); 299 | printf(".info.peak_941 = %f\n", d.info.peak_941); 300 | printf(".info.peak_1209 = %f\n", d.info.peak_1209); 301 | printf(".info.peak_1336 = %f\n", d.info.peak_1336); 302 | printf(".info.peak_1477 = %f\n", d.info.peak_1477); 303 | printf(".info.peak_1633 = %f\n", d.info.peak_1633); 304 | 305 | numWords = fread(intData, sizeof(int16_t), XN, inFile ); 306 | } 307 | 308 | printf("\nFinished. sampleCount = %d\n",sampleCount); 309 | fclose( inFile ); 310 | 311 | /* Test compare. */ 312 | lm_test(info1.peak_697 == d.info.peak_697, "peak error: 697 Hz (standard DTMF detector)"); 313 | lm_test(info1.peak_770 == d.info.peak_770, "peak error: 770 Hz (standard DTMF detector)"); 314 | lm_test(info1.peak_852 == d.info.peak_852, "peak error: 852 Hz (standard DTMF detector)"); 315 | lm_test(info1.peak_941 == d.info.peak_941, "peak error: 941 Hz (standard DTMF detector)"); 316 | lm_test(info1.peak_1209 == d.info.peak_1209, "peak error: 1209 Hz (standard DTMF detector)"); 317 | lm_test(info1.peak_1336 == d.info.peak_1336, "peak error: 1336 Hz (standard DTMF detector)"); 318 | lm_test(info1.peak_1477 == d.info.peak_1477, "peak error: 1477 Hz (standard DTMF detector)"); 319 | lm_test(info1.peak_1633 == d.info.peak_1633, "peak error: 1633 Hz (standard DTMF detector)"); 320 | 321 | lm_deinit(&d); 322 | 323 | return 0; 324 | 325 | lmt9_fail_sys: 326 | fprintf(stderr, "Couldn't complete the test due to system error\n"); 327 | return -1; 328 | } 329 | --------------------------------------------------------------------------------