1 // Copyright (c) Charles J. Cliffe
2 // SPDX-License-Identifier: GPL-2.0+
3 
4 #include "SpectrumVisualProcessor.h"
5 #include "CubicSDR.h"
6 
7 //50 ms
8 #define HEARTBEAT_CHECK_PERIOD_MICROS (50 * 1000)
9 
SpectrumVisualProcessor()10 SpectrumVisualProcessor::SpectrumVisualProcessor() : outputBuffers("SpectrumVisualProcessorBuffers") {
11     lastInputBandwidth = 0;
12     lastBandwidth = 0;
13     lastDataSize = 0;
14     resampler = nullptr;
15     resamplerRatio = 0;
16 
17     fftInput = nullptr;
18     fftOutput = nullptr;
19     fftInData = nullptr;
20     fftLastData = nullptr;
21     fftPlan = nullptr;
22 
23     is_view = false;
24     fftSize = 0;
25     centerFreq = 0;
26     bandwidth = 0;
27     hideDC = false;
28 
29     freqShifter = nco_crcf_create(LIQUID_NCO);
30     shiftFrequency = 0;
31 
32     fft_ceil_ma = fft_ceil_maa = 100.0;
33     fft_floor_ma = fft_floor_maa = 0.0;
34     fft_floor_peak = 0.0;
35     desiredInputSize = 0;
36     fft_average_rate = 0.65f;
37     scaleFactor = 1.0;
38     fftSizeChanged = false;
39     newFFTSize = 0;
40     lastView = false;
41     peakHold = false;
42     peakReset = false;
43 
44 }
45 
~SpectrumVisualProcessor()46 SpectrumVisualProcessor::~SpectrumVisualProcessor() {
47     nco_crcf_destroy(freqShifter);
48 }
49 
isView()50 bool SpectrumVisualProcessor::isView() {
51 
52 	std::lock_guard < std::mutex > busy_lock(busy_run);
53 
54 	return is_view;
55 }
56 
setView(bool bView)57 void SpectrumVisualProcessor::setView(bool bView) {
58 
59 	std::lock_guard < std::mutex > busy_lock(busy_run);
60 
61     is_view = bView;
62 }
63 
setView(bool bView,long long centerFreq_in,long bandwidth_in)64 void SpectrumVisualProcessor::setView(bool bView, long long centerFreq_in, long bandwidth_in) {
65 
66     std::lock_guard < std::mutex > busy_lock(busy_run);
67     is_view = bView;
68     bandwidth = bandwidth_in;
69     centerFreq = centerFreq_in;
70 }
71 
72 
setFFTAverageRate(float fftAverageRate)73 void SpectrumVisualProcessor::setFFTAverageRate(float fftAverageRate) {
74 
75     std::lock_guard < std::mutex > busy_lock(busy_run);
76 
77     this->fft_average_rate = fftAverageRate;
78 }
79 
getFFTAverageRate()80 float SpectrumVisualProcessor::getFFTAverageRate() {
81 
82 	std::lock_guard < std::mutex > busy_lock(busy_run);
83 
84     return this->fft_average_rate;
85 }
86 
setCenterFrequency(long long centerFreq_in)87 void SpectrumVisualProcessor::setCenterFrequency(long long centerFreq_in) {
88 
89     std::lock_guard < std::mutex > busy_lock(busy_run);
90 
91     centerFreq = centerFreq_in;
92 }
93 
getCenterFrequency()94 long long SpectrumVisualProcessor::getCenterFrequency() {
95 
96 	std::lock_guard < std::mutex > busy_lock(busy_run);
97 
98     return centerFreq;
99 }
100 
setBandwidth(long bandwidth_in)101 void SpectrumVisualProcessor::setBandwidth(long bandwidth_in) {
102 
103     std::lock_guard < std::mutex > busy_lock(busy_run);
104 
105 	bandwidth = bandwidth_in;
106 }
107 
getBandwidth()108 long SpectrumVisualProcessor::getBandwidth() {
109 
110 	std::lock_guard < std::mutex > busy_lock(busy_run);
111 
112     return bandwidth;
113 }
114 
setPeakHold(bool peakHold_in)115 void SpectrumVisualProcessor::setPeakHold(bool peakHold_in) {
116 
117 	std::lock_guard < std::mutex > busy_lock(busy_run);
118 
119     if (peakHold && peakHold_in) {
120         peakReset = PEAK_RESET_COUNT;
121     } else {
122         peakHold = peakHold_in;
123         peakReset = 1;
124     }
125 }
126 
getPeakHold()127 bool SpectrumVisualProcessor::getPeakHold() {
128 
129 	std::lock_guard < std::mutex > busy_lock(busy_run);
130 
131     return peakHold;
132 }
133 
getDesiredInputSize()134 int SpectrumVisualProcessor::getDesiredInputSize() {
135 	std::lock_guard < std::mutex > busy_lock(busy_run);
136 
137     return desiredInputSize;
138 }
139 
setup(unsigned int fftSize_in)140 void SpectrumVisualProcessor::setup(unsigned int fftSize_in) {
141 
142     std::lock_guard < std::mutex > busy_lock(busy_run);
143 
144     fftSize = fftSize_in;
145     fftSizeInternal = fftSize_in * SPECTRUM_VZM;
146     lastDataSize = 0;
147 
148     int memSize = sizeof(liquid_float_complex) * fftSizeInternal;
149 
150     if (fftInput) {
151         free(fftInput);
152     }
153     fftInput = (liquid_float_complex*)malloc(memSize);
154     memset(fftInput,0,memSize);
155 
156     if (fftInData) {
157         free(fftInData);
158     }
159     fftInData = (liquid_float_complex*)malloc(memSize);
160     memset(fftInput,0,memSize);
161 
162     if (fftLastData) {
163         free(fftLastData);
164     }
165     fftLastData = (liquid_float_complex*)malloc(memSize);
166     memset(fftInput,0,memSize);
167 
168     if (fftOutput) {
169         free(fftOutput);
170     }
171     fftOutput = (liquid_float_complex*)malloc(memSize);
172     memset(fftInput,0,memSize);
173 
174     if (fftPlan) {
175         fft_destroy_plan(fftPlan);
176     }
177     fftPlan = fft_create_plan(fftSizeInternal, fftInput, fftOutput, LIQUID_FFT_FORWARD, 0);
178 }
179 
setFFTSize(unsigned int fftSize_in)180 void SpectrumVisualProcessor::setFFTSize(unsigned int fftSize_in) {
181 
182 	//then get the busy_lock
183 	std::lock_guard < std::mutex > busy_lock(busy_run);
184 
185     if (fftSize_in == fftSize) {
186         return;
187     }
188     newFFTSize = fftSize_in;
189     fftSizeChanged = true;
190 }
191 
getFFTSize()192 unsigned int SpectrumVisualProcessor::getFFTSize() {
193 
194 	//then get the busy_lock
195 	std::lock_guard < std::mutex > busy_lock(busy_run);
196 
197     if (fftSizeChanged) {
198         return newFFTSize;
199     }
200     return fftSize;
201 }
202 
203 
setHideDC(bool hideDC_in)204 void SpectrumVisualProcessor::setHideDC(bool hideDC_in) {
205 
206 	std::lock_guard < std::mutex > busy_lock(busy_run);
207 
208     hideDC = hideDC_in;
209 }
210 
211 
process()212 void SpectrumVisualProcessor::process() {
213     if (!isOutputEmpty()) {
214         return;
215     }
216     if (!input || input->empty()) {
217         return;
218     }
219 
220 	bool executeSetup = false;
221 
222 	{ // scoped lock here
223 		std::lock_guard < std::mutex > busy_lock(busy_run);
224 		if (fftSizeChanged) {
225 			executeSetup = true;
226 			fftSizeChanged = false;
227 		}
228 	}
229 
230 	if (executeSetup) {
231 		setup(newFFTSize);
232 	}
233 
234     DemodulatorThreadIQDataPtr iqData;
235 
236     if (!input->pop(iqData, HEARTBEAT_CHECK_PERIOD_MICROS)) {
237         return;
238     }
239 
240     if (!iqData) {
241         return;
242     }
243 
244     //then get the busy_lock for the rest of the processing.
245     std::lock_guard < std::mutex > busy_lock(busy_run);
246 
247     bool doPeak = peakHold && (peakReset == 0);
248 
249     if (fft_result.size() != fftSizeInternal) {
250 
251         if (fft_result.capacity() < fftSizeInternal) {
252             fft_result.reserve(fftSizeInternal);
253             fft_result_ma.reserve(fftSizeInternal);
254             fft_result_maa.reserve(fftSizeInternal);
255             fft_result_peak.reserve(fftSizeInternal);
256         }
257         fft_result.resize(fftSizeInternal);
258         fft_result_ma.resize(fftSizeInternal);
259         fft_result_maa.resize(fftSizeInternal);
260         fft_result_temp.resize(fftSizeInternal);
261         fft_result_peak.resize(fftSizeInternal);
262     }
263 
264     if (peakReset != 0) {
265         peakReset--;
266         if (peakReset == 0) {
267             for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) {
268                 fft_result_peak[i] = fft_floor_maa;
269             }
270             fft_ceil_peak = fft_floor_maa;
271             fft_floor_peak = fft_ceil_maa;
272         }
273     }
274 
275     std::vector<liquid_float_complex> *data = &iqData->data;
276 
277     if (data && !data->empty()) {
278         unsigned int num_written;
279         long resampleBw = iqData->sampleRate;
280         bool newResampler = false;
281         int bwDiff = 0;
282 
283         if (is_view) {
284             if (!iqData->sampleRate) {
285 
286                 return;
287             }
288 
289             while (resampleBw / SPECTRUM_VZM >= (long) bandwidth) {
290                 resampleBw /= SPECTRUM_VZM;
291             }
292 
293             resamplerRatio = (double) (resampleBw) / (double) iqData->sampleRate;
294 
295             size_t desired_input_size = fftSizeInternal / resamplerRatio;
296 
297             this->desiredInputSize = desired_input_size;
298 
299             if (iqData->data.size() < desired_input_size) {
300                 //                std::cout << "fft underflow, desired: " << desired_input_size << " actual:" << input->data.size() << std::endl;
301                 desired_input_size = iqData->data.size();
302             }
303 
304             if (centerFreq != iqData->frequency) {
305                 if ((centerFreq - iqData->frequency) != shiftFrequency || lastInputBandwidth != iqData->sampleRate) {
306                     if (abs(iqData->frequency - centerFreq) < (wxGetApp().getSampleRate() / 2)) {
307                         long lastShiftFrequency = shiftFrequency;
308                         shiftFrequency = centerFreq - iqData->frequency;
309                         nco_crcf_set_frequency(freqShifter, (2.0 * M_PI) * (((double) abs(shiftFrequency)) / ((double) iqData->sampleRate)));
310 
311                         if (is_view) {
312                             long freqDiff = shiftFrequency - lastShiftFrequency;
313 
314                             if (lastBandwidth!=0) {
315                                 double binPerHz = double(lastBandwidth) / double(fftSizeInternal);
316 
317                                 unsigned int numShift = floor(double(abs(freqDiff)) / binPerHz);
318 
319                                 if (numShift < fftSizeInternal/2 && numShift) {
320                                     if (freqDiff > 0) {
321                                         memmove(&fft_result_ma[0], &fft_result_ma[numShift], (fftSizeInternal-numShift) * sizeof(double));
322                                         memmove(&fft_result_maa[0], &fft_result_maa[numShift], (fftSizeInternal-numShift) * sizeof(double));
323 //                                        memmove(&fft_result_peak[0], &fft_result_peak[numShift], (fftSizeInternal-numShift) * sizeof(double));
324 //                                        memset(&fft_result_peak[fftSizeInternal-numShift], 0, numShift * sizeof(double));
325                                     } else {
326                                         memmove(&fft_result_ma[numShift], &fft_result_ma[0], (fftSizeInternal-numShift) * sizeof(double));
327                                         memmove(&fft_result_maa[numShift], &fft_result_maa[0], (fftSizeInternal-numShift) * sizeof(double));
328 //                                        memmove(&fft_result_peak[numShift], &fft_result_peak[0], (fftSizeInternal-numShift) * sizeof(double));
329 //                                        memset(&fft_result_peak[0], 0, numShift * sizeof(double));
330                                     }
331                                 }
332                             }
333                         }
334                     }
335                     peakReset = PEAK_RESET_COUNT;
336                 }
337 
338                 if (shiftBuffer.size() != desired_input_size) {
339                     if (shiftBuffer.capacity() < desired_input_size) {
340                         shiftBuffer.reserve(desired_input_size);
341                     }
342                     shiftBuffer.resize(desired_input_size);
343                 }
344 
345                 if (shiftFrequency < 0) {
346                     nco_crcf_mix_block_up(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size);
347                 } else {
348                     nco_crcf_mix_block_down(freqShifter, &iqData->data[0], &shiftBuffer[0], desired_input_size);
349                 }
350             } else {
351                 shiftBuffer.assign(iqData->data.begin(), iqData->data.begin()+desired_input_size);
352             }
353 
354             if (!resampler || resampleBw != lastBandwidth || lastInputBandwidth != iqData->sampleRate) {
355                 float As = 480.0;
356 
357                 if (resampler) {
358                     msresamp_crcf_destroy(resampler);
359                 }
360 
361                 resampler = msresamp_crcf_create(resamplerRatio, As);
362 
363                 bwDiff = resampleBw-lastBandwidth;
364                 lastBandwidth = resampleBw;
365                 lastInputBandwidth = iqData->sampleRate;
366                 newResampler = true;
367                 peakReset = PEAK_RESET_COUNT;
368             }
369 
370             unsigned int out_size = ceil((double) (desired_input_size) * resamplerRatio) + 512;
371 
372             if (resampleBuffer.size() != out_size) {
373                 if (resampleBuffer.capacity() < out_size) {
374                     resampleBuffer.reserve(out_size);
375                 }
376                 resampleBuffer.resize(out_size);
377             }
378 
379             msresamp_crcf_execute(resampler, &shiftBuffer[0], desired_input_size, &resampleBuffer[0], &num_written);
380 
381             if (num_written < fftSizeInternal) {
382                 memcpy(fftInData, resampleBuffer.data(), num_written * sizeof(liquid_float_complex));
383                 memset(&(fftInData[num_written]), 0, (fftSizeInternal-num_written) * sizeof(liquid_float_complex));
384             } else {
385                 memcpy(fftInData, resampleBuffer.data(), fftSizeInternal * sizeof(liquid_float_complex));
386             }
387         } else {
388             this->desiredInputSize = fftSizeInternal;
389 
390             num_written = data->size();
391             if (data->size() < fftSizeInternal) {
392                 memcpy(fftInData, data->data(), data->size() * sizeof(liquid_float_complex));
393                 memset(&fftInData[data->size()], 0, (fftSizeInternal - data->size()) * sizeof(liquid_float_complex));
394             } else {
395                 memcpy(fftInData, data->data(), fftSizeInternal * sizeof(liquid_float_complex));
396             }
397         }
398 
399         bool execute = false;
400 
401         if (num_written >= fftSizeInternal) {
402             execute = true;
403             memcpy(fftInput, fftInData, fftSizeInternal * sizeof(liquid_float_complex));
404             memcpy(fftLastData, fftInput, fftSizeInternal * sizeof(liquid_float_complex));
405 
406         } else {
407             if (lastDataSize + num_written < fftSizeInternal) { // priming
408                 unsigned int num_copy = fftSizeInternal - lastDataSize;
409                 if (num_written > num_copy) {
410                     num_copy = num_written;
411                 }
412                 memcpy(fftLastData, fftInData, num_copy * sizeof(liquid_float_complex));
413                 lastDataSize += num_copy;
414             } else {
415                 unsigned int num_last = (fftSizeInternal - num_written);
416                 memcpy(fftInput, fftLastData + (lastDataSize - num_last), num_last * sizeof(liquid_float_complex));
417                 memcpy(fftInput + num_last, fftInData, num_written * sizeof(liquid_float_complex));
418                 memcpy(fftLastData, fftInput, fftSizeInternal * sizeof(liquid_float_complex));
419                 execute = true;
420             }
421         }
422 
423         if (execute) {
424             SpectrumVisualDataPtr output = outputBuffers.getBuffer();
425 
426             if (output->spectrum_points.size() != fftSize * 2) {
427                 output->spectrum_points.resize(fftSize * 2);
428             }
429             if (doPeak) {
430                 if (output->spectrum_hold_points.size() != fftSize * 2) {
431                     output->spectrum_hold_points.resize(fftSize * 2);
432                 }
433             } else {
434                 output->spectrum_hold_points.resize(0);
435             }
436 
437             float fft_ceil = 0, fft_floor = 1;
438 
439             fft_execute(fftPlan);
440 
441             for (int i = 0, iMax = fftSizeInternal / 2; i < iMax; i++) {
442                 float a = fftOutput[i].real;
443                 float b = fftOutput[i].imag;
444                 float c = sqrt(a * a + b * b);
445 
446                 float x = fftOutput[fftSizeInternal / 2 + i].real;
447                 float y = fftOutput[fftSizeInternal / 2 + i].imag;
448                 float z = sqrt(x * x + y * y);
449 
450                 fft_result[i] = (z);
451                 fft_result[fftSizeInternal / 2 + i] = (c);
452             }
453 
454             if (newResampler && lastView) {
455                 if (bwDiff < 0) {
456                     for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) {
457                         fft_result_temp[i] = fft_result_ma[(fftSizeInternal/4) + (i/2)];
458                     }
459                     for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) {
460                         fft_result_ma[i] = fft_result_temp[i];
461 
462                         fft_result_temp[i] = fft_result_maa[(fftSizeInternal/4) + (i/2)];
463                     }
464                     for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) {
465                         fft_result_maa[i] = fft_result_temp[i];
466                     }
467                 } else {
468                     for (size_t i = 0, iMax = fftSizeInternal; i < iMax; i++) {
469                         if (i < fftSizeInternal/4) {
470                             fft_result_temp[i] = 0; // fft_result_ma[fftSizeInternal/4];
471                         } else if (i >= fftSizeInternal - fftSizeInternal/4) {
472                             fft_result_temp[i] = 0; // fft_result_ma[fftSizeInternal - fftSizeInternal/4-1];
473                         } else {
474                             fft_result_temp[i] = fft_result_ma[(i-fftSizeInternal/4)*2];
475                         }
476                     }
477                     for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) {
478                         fft_result_ma[i] = fft_result_temp[i];
479 
480                         if (i < fftSizeInternal/4) {
481                             fft_result_temp[i] = 0; //fft_result_maa[fftSizeInternal/4];
482                         } else if (i >= fftSizeInternal - fftSizeInternal/4) {
483                             fft_result_temp[i] = 0; // fft_result_maa[fftSizeInternal - fftSizeInternal/4-1];
484                         } else {
485                             fft_result_temp[i] = fft_result_maa[(i-fftSizeInternal/4)*2];
486                         }
487                     }
488                     for (unsigned int i = 0, iMax = fftSizeInternal; i < iMax; i++) {
489                         fft_result_maa[i] = fft_result_temp[i];
490                     }
491                 }
492             }
493 
494             for (int i = 0, iMax = fftSizeInternal; i < iMax; i++) {
495                 if (fft_result_maa[i] != fft_result_maa[i]) fft_result_maa[i] = fft_result[i];
496                 fft_result_maa[i] += (fft_result_ma[i] - fft_result_maa[i]) * fft_average_rate;
497                 if (fft_result_ma[i] != fft_result_ma[i]) fft_result_ma[i] = fft_result[i];
498                 fft_result_ma[i] += (fft_result[i] - fft_result_ma[i]) * fft_average_rate;
499 
500                 if (fft_result_maa[i] > fft_ceil || fft_ceil != fft_ceil) {
501                     fft_ceil = fft_result_maa[i];
502                 }
503                 if (fft_result_maa[i] < fft_floor || fft_floor != fft_floor) {
504                     fft_floor = fft_result_maa[i];
505                 }
506                 if (doPeak) {
507                     if (fft_result_maa[i] > fft_result_peak[i]) {
508                         fft_result_peak[i] = fft_result_maa[i];
509                     }
510                 }
511             }
512 
513             if (fft_ceil_ma != fft_ceil_ma) fft_ceil_ma = fft_ceil;
514             fft_ceil_ma = fft_ceil_ma + (fft_ceil - fft_ceil_ma) * 0.05;
515             if (fft_ceil_maa != fft_ceil_maa) fft_ceil_maa = fft_ceil;
516             fft_ceil_maa = fft_ceil_maa + (fft_ceil_ma - fft_ceil_maa) * 0.05;
517 
518             if (fft_floor_ma != fft_floor_ma) fft_floor_ma = fft_floor;
519             fft_floor_ma = fft_floor_ma + (fft_floor - fft_floor_ma) * 0.05;
520             if (fft_floor_maa != fft_floor_maa) fft_floor_maa = fft_floor;
521             fft_floor_maa = fft_floor_maa + (fft_floor_ma - fft_floor_maa) * 0.05;
522 
523             if (doPeak) {
524                 if (fft_ceil_maa > fft_ceil_peak) {
525                     fft_ceil_peak = fft_ceil_maa;
526                 }
527                 if (fft_floor_maa < fft_floor_peak) {
528                     fft_floor_peak = fft_floor_maa;
529                 }
530             }
531 
532             float sf = scaleFactor;
533 
534             double visualRatio = (double(bandwidth) / double(resampleBw));
535             double visualStart = (double(fftSizeInternal) / 2.0) - (double(fftSizeInternal) * (visualRatio / 2.0));
536             double visualAccum = 0;
537             double peak_acc = 0, acc = 0, accCount = 0, i = 0;
538 
539             double point_ceil = doPeak?fft_ceil_peak:fft_ceil_maa;
540             double point_floor = doPeak?fft_floor_peak:fft_floor_maa;
541 
542             for (int x = 0, xMax = output->spectrum_points.size() / 2; x < xMax; x++) {
543                 visualAccum += visualRatio * double(SPECTRUM_VZM);
544 
545                 while (visualAccum >= 1.0) {
546                     unsigned int idx = round(visualStart+i);
547                     if (idx > 0 && idx < fftSizeInternal) {
548                         acc += fft_result_maa[idx];
549                         if (doPeak) {
550                             peak_acc += fft_result_peak[idx];
551                         }
552                     } else {
553                         acc += fft_floor_maa;
554                         if (doPeak) {
555                             peak_acc += fft_floor_maa;
556                         }
557                     }
558                     accCount += 1.0;
559                     visualAccum -= 1.0;
560                     i++;
561                 }
562 
563                 output->spectrum_points[x * 2] = ((float) x / (float) xMax);
564                 if (doPeak) {
565                     output->spectrum_hold_points[x * 2] = ((float) x / (float) xMax);
566                 }
567                 if (accCount) {
568                     output->spectrum_points[x * 2 + 1] = ((log10((acc/accCount)+0.25 - (point_floor-0.75)) / log10((point_ceil+0.25) - (point_floor-0.75))))*sf;
569                     acc = 0.0;
570                     if (doPeak) {
571                         output->spectrum_hold_points[x * 2 + 1] = ((log10((peak_acc/accCount)+0.25 - (point_floor-0.75)) / log10((point_ceil+0.25) - (point_floor-0.75))))*sf;
572                         peak_acc = 0.0;
573                     }
574                     accCount = 0.0;
575                 }
576             }
577 
578             if (hideDC) { // DC-spike removal
579                 long long freqMin = centerFreq-(bandwidth/2);
580                 long long freqMax = centerFreq+(bandwidth/2);
581                 long long zeroPt = (iqData->frequency-freqMin);
582 
583                 if (freqMin < iqData->frequency && freqMax > iqData->frequency) {
584                     int freqRange = int(freqMax-freqMin);
585                     int freqStep = freqRange/fftSize;
586                     int fftStart = (zeroPt/freqStep)-(2000/freqStep);
587                     int fftEnd = (zeroPt/freqStep)+(2000/freqStep);
588 
589 //                    std::cout << "range:" << freqRange << ", step: " << freqStep << ", start: " << fftStart << ", end: " << fftEnd << std::endl;
590 
591                     if (fftEnd-fftStart < 2) {
592                         fftEnd++;
593                         fftStart--;
594                     }
595 
596                     int numSteps = (fftEnd-fftStart);
597                     int halfWay = fftStart+(numSteps/2);
598 
599                     if ((fftEnd+numSteps/2+1 < (long long) fftSize) && (fftStart-numSteps/2-1 >= 0) && (fftEnd > fftStart)) {
600                         int n = 1;
601                         for (int i = fftStart; i < halfWay; i++) {
602                             output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftStart - n) * 2 + 1];
603                             n++;
604                         }
605                         n = 1;
606                         for (int i = halfWay; i < fftEnd; i++) {
607                             output->spectrum_points[i * 2 + 1] = output->spectrum_points[(fftEnd + n) * 2 + 1];
608                             n++;
609                         }
610                         if (doPeak) {
611                             int n = 1;
612                             for (int i = fftStart; i < halfWay; i++) {
613                                 output->spectrum_hold_points[i * 2 + 1] = output->spectrum_hold_points[(fftStart - n) * 2 + 1];
614                                 n++;
615                             }
616                             n = 1;
617                             for (int i = halfWay; i < fftEnd; i++) {
618                                 output->spectrum_hold_points[i * 2 + 1] = output->spectrum_hold_points[(fftEnd + n) * 2 + 1];
619                                 n++;
620                             }
621                         }
622                     }
623                 }
624             }
625 
626             output->fft_ceiling = point_ceil/sf;
627             output->fft_floor = point_floor;
628 
629             output->centerFreq = centerFreq;
630             output->bandwidth = bandwidth;
631 
632             distribute(output);
633         }
634     }
635 
636     lastView = is_view;
637 }
638 
639 
setScaleFactor(float sf)640 void SpectrumVisualProcessor::setScaleFactor(float sf) {
641 	std::lock_guard < std::mutex > busy_lock(busy_run);
642 
643     scaleFactor = sf;
644 }
645 
646 
getScaleFactor()647 float SpectrumVisualProcessor::getScaleFactor() {
648 	std::lock_guard < std::mutex > busy_lock(busy_run);
649     return scaleFactor;
650 }
651 
652