1 /*-
2  * Copyright (c) 2018-2020 Hans Petter Selasky. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  */
25 
26 #include "qaudiosonar.h"
27 
28 static pthread_cond_t qas_wave_cond;
29 static pthread_mutex_t qas_wave_mutex;
30 
31 static TAILQ_HEAD(,qas_wave_job) qas_wave_head = TAILQ_HEAD_INITIALIZER(qas_wave_head);
32 
33 double qas_tuning = 1.0;
34 double *qas_freq_table;
35 QString *qas_descr_table;
36 size_t qas_num_bands;
37 double qas_low_octave;
38 
39 struct qas_wave_job *
qas_wave_job_alloc()40 qas_wave_job_alloc()
41 {
42 	struct qas_wave_job *pjob;
43 
44 	pjob = (struct qas_wave_job *)malloc(sizeof(*pjob));
45 	return (pjob);
46 }
47 
48 void
qas_wave_job_insert(struct qas_wave_job * pjob)49 qas_wave_job_insert(struct qas_wave_job *pjob)
50 {
51 	qas_wave_lock();
52 	TAILQ_INSERT_TAIL(&qas_wave_head, pjob, entry);
53 	qas_wave_signal();
54 	qas_wave_unlock();
55 }
56 
57 struct qas_wave_job *
qas_wave_job_dequeue()58 qas_wave_job_dequeue()
59 {
60 	struct qas_wave_job *pjob;
61 
62   	qas_wave_lock();
63 	while ((pjob = TAILQ_FIRST(&qas_wave_head)) == 0)
64 		qas_wave_wait();
65 	TAILQ_REMOVE(&qas_wave_head, pjob, entry);
66 	qas_wave_unlock();
67 
68 	return (pjob);
69 }
70 
71 void
qas_wave_job_free(qas_wave_job * pjob)72 qas_wave_job_free(qas_wave_job *pjob)
73 {
74 	free(pjob);
75 }
76 
77 void
qas_wave_signal()78 qas_wave_signal()
79 {
80 	pthread_cond_signal(&qas_wave_cond);
81 }
82 
83 void
qas_wave_wait()84 qas_wave_wait()
85 {
86 	pthread_cond_wait(&qas_wave_cond, &qas_wave_mutex);
87 }
88 
89 void
qas_wave_lock()90 qas_wave_lock()
91 {
92 	pthread_mutex_lock(&qas_wave_mutex);
93 }
94 
95 void
qas_wave_unlock()96 qas_wave_unlock()
97 {
98 	pthread_mutex_unlock(&qas_wave_mutex);
99 }
100 
101 static void
qas_wave_analyze(const double * indata,double delta_phase,double * out)102 qas_wave_analyze(const double *indata, double delta_phase, double *out)
103 {
104 	double phase = 0.0;
105 	double cos_in = 0.0;
106 	double sin_in = 0.0;
107 
108 	for (size_t x = 0; x != qas_window_size; x++) {
109 		cos_in += qas_ftt_cos(phase) * indata[x];
110 		sin_in += qas_ftt_sin(phase) * indata[x];
111 		phase += delta_phase;
112 	}
113 
114 	out[0] = (fabs(cos_in) + fabs(sin_in)) / ((double)qas_window_size * 0.5);
115 	if (out[0] < 1.0)
116 		out[0] = 1.0;
117 }
118 
119 static size_t
qas_wave_analyze_binary_search(const double * indata,double * out,size_t band,size_t rem)120 qas_wave_analyze_binary_search(const double *indata, double *out, size_t band, size_t rem)
121 {
122 	double dp;
123 	double temp[1];
124 	size_t pos = 0;
125 
126 	/* find maximum amplitude */
127 	while (rem != 0) {
128 		pos |= rem;
129 
130 		dp = qas_tuning * qas_freq_table[band + pos] / (double)qas_sample_rate;
131 		qas_wave_analyze(indata, dp, temp);
132 		if (out[0] > temp[0])
133 			pos &= ~rem;
134 		else
135 			memcpy(out, temp, sizeof(temp));
136 		rem /= 2;
137 	}
138 	return (pos);
139 }
140 
141 static void *
qas_wave_worker(void * arg)142 qas_wave_worker(void *arg)
143 {
144 	while (1) {
145 		struct qas_wave_job *pjob;
146 
147 		pjob = qas_wave_job_dequeue();
148 
149 		switch (pjob->data->state) {
150 		case QAS_STATE_1ST_SCAN:
151 			qas_wave_analyze(pjob->data->monitor_data,
152 			    qas_tuning * qas_freq_table[pjob->band_start] / (double)qas_sample_rate,
153 			    pjob->data->band_data + (pjob->band_start / QAS_WAVE_STEP));
154 			break;
155 		case QAS_STATE_2ND_SCAN:
156 			pjob->band_start +=
157 			    qas_wave_analyze_binary_search(pjob->data->monitor_data,
158 			        pjob->data->band_data + (pjob->band_start / QAS_WAVE_STEP),
159 			        pjob->band_start, QAS_WAVE_STEP / 2);
160 			break;
161 		}
162 		qas_display_job_insert(pjob);
163 	}
164 	return (0);
165 }
166 
167 const double qas_base_freq = 440.0;	/* A-key in Hz */
168 
169 void
qas_wave_init()170 qas_wave_init()
171 {
172 	const double min_hz = 8.0;
173 	const double max_hz = qas_sample_rate / 2.0;
174 	double num_low_octave = 0;
175 	double num_high_octave = 0;
176 
177 	pthread_mutex_init(&qas_wave_mutex, 0);
178 	pthread_cond_init(&qas_wave_cond, 0);
179 
180 	while ((qas_base_freq * pow(2.0, -num_low_octave)) > min_hz)
181 		num_low_octave++;
182 
183 	while ((qas_base_freq * pow(2.0, num_high_octave)) < max_hz)
184 		num_high_octave++;
185 
186 	num_high_octave--;
187 	num_low_octave--;
188 
189 	if (num_high_octave < 1 || num_low_octave < 1)
190 		errx(EX_USAGE, "Invalid number of octaves\n");
191 
192 	qas_low_octave = num_low_octave;
193 	qas_num_bands = (size_t)(num_high_octave + num_low_octave) * 12 * QAS_WAVE_STEP;
194 
195 	qas_freq_table = (double *)malloc(sizeof(double) * qas_num_bands);
196 	qas_descr_table = new QString [qas_num_bands];
197 
198 	for (size_t x = 0; x != qas_num_bands; x++) {
199 		const char *map[12] = {
200 			"A%1", "B%1B", "B%1", "C%1",
201 			"D%1B", "D%1", "E%1B", "E%1",
202 			"F%1", "G%1B", "G%1", "A%1B"
203 		};
204 		qas_freq_table[x] = qas_base_freq *
205 		    pow(2.0, (double)x / (double)(12 * QAS_WAVE_STEP) - num_low_octave);
206 		qas_descr_table[x] = QString(map[(x / QAS_WAVE_STEP) % 12])
207 		    .arg((x + 9 * QAS_WAVE_STEP) / (12 * QAS_WAVE_STEP));
208 
209 		if (x % QAS_WAVE_STEP) {
210 			size_t y = 0;
211 			if (x & 1)
212 				y |= 128;
213 			if (x & 2)
214 				y |= 64;
215 			if (x & 4)
216 				y |= 32;
217 			if (x & 8)
218 				y |= 16;
219 			if (x & 16)
220 				y |= 8;
221 			if (x & 32)
222 				y |= 4;
223 			if (x & 64)
224 				y |= 2;
225 			if (x & 128)
226 				y |= 1;
227 			qas_descr_table[x] += QString(".%1").arg(y);
228 		}
229 	}
230 
231 	for (int i = 0; i != qas_num_workers; i++) {
232 		pthread_t qas_wave_thread;
233 		pthread_create(&qas_wave_thread, 0, &qas_wave_worker, 0);
234 	}
235 }
236