1 // Pulse Class
2 
3 #include <queue>
4 #include <stack>
5 #include <string>
6 
7 #include "play.pa.h"
8 #include "misc.h"
9 #include "debug.h"
10 #include "configuration.h"
11 #include "fl_digi.h"
12 #include "qrunner.h"
13 #include "confdialog.h"
14 
15 #define DR_MP3_IMPLEMENTATION
16 #include "dr_mp3.h"
17 
18 #define CHANNELS                  2
19 #define SCRATE                 8000
20 #define FRAMES_PER_BUFFER      1024 // lower values causes audio distortion on pi3
21 #define RBUFF_SIZE            16384 // 4096
22 
23 static pthread_t       alert_pthread;
24 static pthread_cond_t  alert_cond;
25 static pthread_mutex_t alert_mutex = PTHREAD_MUTEX_INITIALIZER;
26 
27 static pthread_mutex_t	filter_mutex = PTHREAD_MUTEX_INITIALIZER;
28 static pthread_mutex_t	rx_stream_mutex = PTHREAD_MUTEX_INITIALIZER;
29 
30 static pthread_t		filelist_pthread;
31 static pthread_mutex_t	filelist_mutex = PTHREAD_MUTEX_INITIALIZER;
32 
33 static void start_alert_thread(void);
34 static void start_filelist_thread(void);
35 static void stop_alert_thread(void);
36 static void stop_filelist_thread(void);
37 
38 static bool alert_thread_running   = false;
39 static bool alert_terminate_flag   = false;
40 //static bool stream_ready           = false;
41 
42 enum { NONE, START, OPEN, CLOSE, TERMINATE };
43 static int alert_process_flag     = NONE;
44 
45 struct PLAYLIST {
46 	c_portaudio *cpa;
47 	float *fbuff;
48 	unsigned long int bufflen;
49 	unsigned long int data_ptr;
50 	unsigned long int frames;
51 	int src;
52 };
53 
54 std::queue<PLAYLIST *> playlist;
55 
56 static PLAYLIST *plist = 0;
57 
58 struct FILELIST {
59 	c_portaudio *cpa;
60 	std::string fn;
FILELISTFILELIST61 	FILELIST() { cpa = 0; fn = ""; }
FILELISTFILELIST62 	FILELIST( c_portaudio *_cpa, std::string _fn ) {
63 		cpa = _cpa; fn = _fn;
64 	}
~FILELISTFILELIST65 	~FILELIST() {};
66 };
67 
68 std::stack<FILELIST> filelist;
69 
70 /**********************************************************************************
71  * AUDIO_ALERT process event.
72  **********************************************************************************/
73 int csinc = 2;// 0 - best, 1 - medium, 2 - fastest, 3 - zoh, 4 - linear
74 static sf_count_t
rate_convert(float * inbuff,int len,float * outbuff,int outlen,double src_ratio,int channels)75 rate_convert (float *inbuff, int len, float *outbuff, int outlen, double src_ratio, int channels)
76 {
77 	SRC_DATA	src_data ;
78 
79 	src_data.data_in		= inbuff;				// pointer to the input data samples.
80 	src_data.input_frames	= len / channels;		// number of frames of data pointed to by data_in.
81 	src_data.data_out		= outbuff;				// pointer to the output data samples.
82 	src_data.output_frames	= outlen / channels;	// Maximum number of frames pointed to by data_out.
83 	src_data.src_ratio		= src_ratio;			// output_sample_rate / input_sample_rate.
84 
85 	int error = src_simple (&src_data, csinc, channels) ;
86 
87 	return error;
88 }
89 
stream_process(const void * in,void * out,unsigned long nframes,const PaStreamCallbackTimeInfo * time_info,PaStreamCallbackFlags flags,void * data)90 int stream_process(
91 			const void* in, void* out, unsigned long nframes,
92 			const PaStreamCallbackTimeInfo *time_info,
93 			PaStreamCallbackFlags flags, void* data)
94 {
95 	float* outf = reinterpret_cast<float*>(out);
96 	memset(outf, 0, nframes * 2 * sizeof(float));
97 
98 	if (!plist) {
99 		if (playlist.empty()) {
100 			guard_lock rx_lock(&rx_stream_mutex);
101 			c_portaudio *cpa = (c_portaudio *)data;
102 			unsigned long len = nframes * cpa->paStreamParameters.channelCount;
103 			unsigned long available = cpa->monitor_rb->read_space();
104 			if (progdefaults.mon_xcvr_audio && available >= len) {
105 				cpa->monitor_rb->read((float *)out, len);
106 			}
107 			return paContinue;
108 		}
109 		guard_lock que_lock(&alert_mutex);
110 		plist = playlist.front();
111 		playlist.pop();
112 	}
113 
114 	c_portaudio* cpa = plist->cpa;
115 	int chcnt = cpa->paStreamParameters.channelCount;
116 	unsigned long int nbr_frames = plist->bufflen / chcnt;
117 
118 	unsigned int ncopy = nbr_frames - plist->data_ptr;
119 	if (ncopy > nframes) ncopy = nframes;
120 
121 	memcpy(	outf,
122 			plist->fbuff + plist->data_ptr * chcnt,
123 			ncopy * chcnt * sizeof(float));
124 	plist->data_ptr += ncopy;
125 
126 	float outvol = 0.01 * progdefaults.alert_volume;
127 	for (unsigned int n = 0; n < ncopy * chcnt; n++) outf[n] *= outvol;
128 
129 	if (nbr_frames && plist->src == c_portaudio::ALERT) {
130 		static char progress[20];
131 		snprintf(progress, sizeof(progress), "%d %%", int(100.0 * plist->data_ptr / nbr_frames));
132 		put_status(progress, 2.0, STATUS_CLEAR);
133 	}
134 
135 	if (plist->data_ptr >= nbr_frames) {
136 		plist = NULL;
137 	}
138 
139 	return paContinue;
140 }
141 
142 static int paStatus;
StreamFinished(void * userData)143 static void StreamFinished( void* userData )
144 {
145 	paStatus = paComplete;
146 }
147 
process_alert()148 void process_alert()
149 {
150 	return;
151 }
152 
153 /**********************************************************************************
154  * AUDIO_ALERT processing loop.
155  * syncs to requests for audio alert output
156  **********************************************************************************/
157 static c_portaudio *requester = 0;
158 
alert_loop(void * args)159 static void * alert_loop(void *args)
160 {
161 	SET_THREAD_ID(AUDIO_ALERT_TID);
162 
163 	alert_thread_running   = true;
164 	alert_terminate_flag   = false;
165 
166 	while(1) {
167 		pthread_mutex_lock(&alert_mutex);
168 		pthread_cond_wait(&alert_cond, &alert_mutex);
169 		pthread_mutex_unlock(&alert_mutex);
170 
171 		if (alert_process_flag == OPEN) {
172 			if (requester)
173 				requester->open();
174 			alert_process_flag = NONE;
175 			requester = 0;
176 		}
177 		if (alert_process_flag == CLOSE) {
178 			if (requester)
179 				requester->close();
180 			alert_process_flag = NONE;
181 			requester = 0;
182 		}
183 
184 		if (alert_process_flag == TERMINATE)
185 			break;
186 
187 	}
188 	return (void *)0;
189 }
190 
open_alert_port(c_portaudio * cpa)191 void open_alert_port(c_portaudio *cpa)
192 {
193 	alert_process_flag = OPEN;
194 	requester = cpa;
195 	pthread_cond_signal(&alert_cond);
196 }
197 
close_alert_port(c_portaudio * cpa)198 void close_alert_port(c_portaudio *cpa)
199 {
200 	alert_process_flag = CLOSE;
201 	requester = cpa;
202 	pthread_cond_signal(&alert_cond);
203 }
204 
205 /**********************************************************************************
206  * Start AUDIO_ALERT Thread
207  **********************************************************************************/
start_alert_thread(void)208 static void start_alert_thread(void)
209 {
210 	if(alert_thread_running) return;
211 
212 	memset((void *) &alert_pthread, 0, sizeof(alert_pthread));
213 	memset((void *) &alert_mutex,   0, sizeof(alert_mutex));
214 	memset((void *) &alert_cond,    0, sizeof(alert_cond));
215 
216 	if(pthread_cond_init(&alert_cond, NULL)) {
217 		LOG_ERROR("Alert thread create fail (pthread_cond_init)");
218 		return;
219 	}
220 
221 	if(pthread_mutex_init(&alert_mutex, NULL)) {
222 		LOG_ERROR("AUDIO_ALERT thread create fail (pthread_mutex_init)");
223 		return;
224 	}
225 
226 	if (pthread_create(&alert_pthread, NULL, alert_loop, NULL) < 0) {
227 		pthread_mutex_destroy(&alert_mutex);
228 		LOG_ERROR("AUDIO_ALERT thread create fail (pthread_create)");
229 	}
230 
231 	LOG_VERBOSE("started audio alert thread");
232 
233 	MilliSleep(10); // Give the CPU time to set 'alert_thread_running'
234 }
235 
236 /**********************************************************************************
237  * Stop AUDIO_ALERT Thread
238  **********************************************************************************/
stop_alert_thread(void)239 static void stop_alert_thread(void)
240 {
241 	if(!alert_thread_running) return;
242 
243 	struct PLAYLIST *plist = 0;
244 	while (!playlist.empty()) {
245 		plist = playlist.front();
246 		delete [] plist->fbuff;
247 		playlist.pop();
248 	}
249 
250 	alert_process_flag = TERMINATE;
251 	pthread_cond_signal(&alert_cond);
252 
253 	MilliSleep(10);
254 
255 	pthread_join(alert_pthread, NULL);
256 
257 	LOG_VERBOSE("%s", "audio alert thread - stopped");
258 
259 	pthread_mutex_destroy(&alert_mutex);
260 	pthread_cond_destroy(&alert_cond);
261 
262 	memset((void *) &alert_pthread, 0, sizeof(alert_pthread));
263 	memset((void *) &alert_mutex,   0, sizeof(alert_mutex));
264 
265 	alert_thread_running   = false;
266 	alert_terminate_flag   = false;
267 }
268 
add_alert(c_portaudio * _cpa,float * buffer,int len,int src)269 static void add_alert(c_portaudio * _cpa, float *buffer, int len, int src)
270 {
271 	if(alert_thread_running) {
272 		if (_cpa->paStreamParameters.device == -1) return;
273 		struct PLAYLIST *plist = new PLAYLIST;
274 		plist->fbuff = buffer;
275 		plist->bufflen = len;
276 		plist->cpa = _cpa;
277 		plist->data_ptr = 0;
278 		plist->frames = len / _cpa->paStreamParameters.channelCount;
279 
280 		guard_lock que_lock(&alert_mutex);
281 		playlist.push(plist);
282 		LOG_VERBOSE("play list contains %d items", (int)(playlist.size()));
283 	}
284 }
285 
286 // Initialize the c_portaudio class
c_portaudio()287 c_portaudio::c_portaudio()
288 {
289 	PaError paError = Pa_Initialize();
290 	if (paError != paNoError) {
291 		LOG_ERROR("pa Error # %d, %s", paError, Pa_GetErrorText(paError));
292 		throw cPA_exception(paError);
293 	}
294 
295 	stream = 0;
296 	fbuffer = new float[1024];
297 	nubuffer = new float[1024 * 6];
298 	data_frames = new float[ FRAMES_PER_BUFFER * CHANNELS ];
299 
300 	paStreamParameters.device = -1;
301 	sr = 44100;
302 	paStreamParameters.channelCount = 2;
303 	paStreamParameters.sampleFormat = paFloat32;
304 	paStreamParameters.hostApiSpecificStreamInfo = NULL;
305 
306 	sr = SCRATE;
307 	b_sr = SCRATE;
308 	b_len = 0;
309 	rc = src_new (progdefaults.sample_converter, 1, &rc_error) ;
310 	monitor_rb = new ringbuffer<float>(RBUFF_SIZE);
311 
312 	bpfilt = new C_FIR_filter();
313 
314 	start_alert_thread();
315 	start_filelist_thread();
316 }
317 
~c_portaudio()318 c_portaudio::~c_portaudio()
319 {
320 	close();
321 	stop_filelist_thread();
322 	stop_alert_thread();
323 	Pa_Terminate();
324 
325 	delete monitor_rb;
326 	delete [] fbuffer;
327 	delete [] nubuffer;
328 	delete [] data_frames;
329 	src_delete(rc);
330 	delete bpfilt;
331 }
332 
init_filter()333 void c_portaudio::init_filter()
334 {
335 	guard_lock filter_lock(&filter_mutex);
336 
337 	if (!bpfilt) bpfilt = new C_FIR_filter();
338 
339 	flo = 1.0 * progdefaults.RxFilt_low / sr;
340 	fhi = 1.0 * progdefaults.RxFilt_high / sr;
341 	double fmid = progdefaults.RxFilt_mid / sr;
342 
343 	bpfilt->init_bandpass (511, 1, flo, fhi);
344 
345 	{
346 		C_FIR_filter *testfilt = new C_FIR_filter();
347 		testfilt->init_bandpass(511, 1, flo, fhi);
348 		double amp = 0;
349 		double sum_in = 0, sum_out = 0;
350 		double inp = 0;
351 		for (int i = 0; i <  100 / fmid; i++) {
352 			inp = cos (TWOPI * i * fmid);
353 			if (testfilt->Irun( inp, amp ) ) {
354 				sum_in += fabs(inp);
355 				sum_out += fabs(amp);
356 			}
357 		}
358 		gain = 0.98 * sum_in / sum_out;
359 		delete testfilt;
360 	}
361 
362 //	std::cout << "############################################" << std::endl;
363 //	std::cout << "Sampling rate: " << sr << std::endl;
364 //	std::cout << "BW : " << progdefaults.RxFilt_bw << " : [ " << progdefaults.RxFilt_low <<
365 //				 " | " << progdefaults.RxFilt_mid <<
366 //				 " | " << progdefaults.RxFilt_high << " ]" <<
367 //				 std::endl;
368 //	std::cout << "bpfilt :       " << flo << " | " << fhi << std::endl;
369 //	std::cout << "gain :         " << gain << std::endl;
370 //	std::cout << "############################################" << std::endl;
371 
372 }
373 
open()374 int c_portaudio::open()//void *data)
375 {
376 	paStreamParameters.device = progdefaults.AlertIndex;
377 	sr = Pa_GetDeviceInfo(paStreamParameters.device)->defaultSampleRate;
378 	paStreamParameters.channelCount = 2;
379 	paStreamParameters.sampleFormat = paFloat32;
380 	paStreamParameters.suggestedLatency = Pa_GetDeviceInfo(paStreamParameters.device)->defaultLowOutputLatency;
381 	paStreamParameters.hostApiSpecificStreamInfo = NULL;
382 
383 	LOG_INFO("\n\
384 open pa stream:\n\
385   samplerate         : %.0f\n\
386   device name        : %s\n\
387   device number      : %d\n\
388   # channels         : %d\n\
389   latency            : %f\n\
390   sample Format      : paFloat32",
391 	sr,
392 	progdefaults.AlertDevice.c_str(),
393 	paStreamParameters.device,
394 	paStreamParameters.channelCount,
395 	paStreamParameters.suggestedLatency);
396 
397 	state = paContinue;
398 
399 	paError = Pa_OpenStream(
400 		&stream,
401 		NULL, &paStreamParameters,
402 		sr,
403 		FRAMES_PER_BUFFER, paClipOff,
404 		stream_process, this);
405 
406 	if (paError != paNoError) {
407 		LOG_ERROR("pa Error # %d, %s", paError, Pa_GetErrorText(paError));
408 		stream = 0;
409 		return 0;
410 	}
411 
412 	paError = Pa_SetStreamFinishedCallback( stream, StreamFinished );
413 	if (paError != paNoError) {
414 		LOG_ERROR("pa Error # %d, %s", paError, Pa_GetErrorText(paError));
415 		paError = Pa_StopStream(stream );
416 		paError = Pa_CloseStream(stream);
417 		stream = 0;
418 		return 0;
419 	}
420 
421 	paError = Pa_StartStream(stream );
422 	if (paError != paNoError) {
423 		LOG_ERROR("pa Error # %d, %s", paError, Pa_GetErrorText(paError));
424 		paError = Pa_StopStream(stream );
425 		paError = Pa_CloseStream(stream);
426 		stream = 0;
427 		return 0;
428 	}
429 
430 LOG_INFO("OPENED pa stream %p @ %f samples/sec", stream, sr);
431 
432 	init_filter();
433 
434 	return 1;
435 }
436 
close()437 void c_portaudio::close()
438 {
439 	if (stream) {
440 		paError = Pa_StopStream(stream );
441 		paError = Pa_CloseStream(stream);
442 		if (paError != paNoError) {
443 			LOG_ERROR("pa Error # %d, %s", paError, Pa_GetErrorText(paError));
444 		}
445 		else
446 			LOG_VERBOSE("closed stream %p", stream);
447 	}
448 }
449 
450 // Play 2 channel buffer
451 
play_buffer(float * buffer,int len,int _sr,int src)452 void c_portaudio::play_buffer(float *buffer, int len, int _sr, int src)
453 {
454 // do not delete [] nubuffer
455 // deleted after use
456 	float *nubuffer = new float[len];
457 	int nusize = len;
458 
459 	if (sr == _sr) { // do not resample if sample rate is default
460 		for (int i = 0; i < len; i++)
461 			nubuffer[i] = buffer[i];
462 	} else {
463 		double		src_ratio = 1.0 * sr / _sr;
464 // resize nubuffer
465 		nusize  = len * src_ratio;
466 		delete [] nubuffer;
467 		nubuffer = new float[nusize];
468 
469 		int num = rate_convert(
470 					buffer, len,
471 					nubuffer, nusize,
472 					src_ratio,
473 					paStreamParameters.channelCount);
474 		if (num != 0) {
475 			LOG_ERROR("rate converter failed");
476 			return;
477 		}
478 	}
479 
480 	data_ptr = 0;
481 	add_alert(this, nubuffer, nusize, src);
482 
483 	return;
484 }
485 
486 // play mono buffer
play_sound(int * buffer,int len,int _sr)487 void c_portaudio::play_sound(int *buffer, int len, int _sr)
488 {
489 	float *fbuff = new float[2];
490 	try {
491 		delete [] fbuff;
492 		fbuff = new float[2*len];
493 		for (int i = 0; i < len; i++)
494 			fbuff[2*i] = fbuff[2*i+1] = buffer[i] / 33000.0; //32768.0;
495 		play_buffer(fbuff, 2*len, _sr, ALERT);
496 	} catch (...) {
497 		delete [] fbuff;
498 		throw;
499 	}
500 	delete [] fbuff;
501 	return;
502 }
503 
504 // play mono buffer
play_sound(float * buffer,int len,int _sr)505 void c_portaudio::play_sound(float *buffer, int len, int _sr)
506 {
507 	float *fbuff = new float[2];
508 	try {
509 		delete [] fbuff;
510 		fbuff = new float[2*len];
511 		for (int i = 0; i < len; i++) {
512 			fbuff[2*i] = fbuff[2*i+1] = buffer[i];
513 		}
514 		play_buffer(fbuff, 2 * len, _sr, ALERT);
515 	} catch (...) {
516 		delete [] fbuff;
517 		throw;
518 	}
519 	delete [] fbuff;
520 	return;
521 }
522 
silence(float secs,int _sr)523 void c_portaudio::silence(float secs, int _sr)
524 {
525 	int len = secs * _sr;
526 	int nosound[len];
527 	memset(nosound, 0, sizeof(*nosound) * len);
528 	play_sound(nosound, len, _sr);
529 }
530 
play_mp3(std::string fname)531 void c_portaudio::play_mp3(std::string fname)
532 {
533 	if (fname.empty()) return;
534 
535     FILE* pFile;
536     pFile = fopen(fname.c_str(), "rb");
537     if (pFile == NULL) {
538         return;
539     }
540     fclose(pFile);
541 
542 	drmp3_config config;
543 	drmp3_uint64 frame_count;
544 
545 	float* mp3_buffer =  drmp3_open_file_and_read_f32(
546 						fname.c_str(), &config, &frame_count );
547 
548 	if (!mp3_buffer) {
549 		LOG_ERROR("File must be mp3 float 32 format");
550 		return;
551 	}
552 
553 	LOG_INFO("\n\
554 MP3 channels: %d\n\
555  sample rate: %d\n\
556  frame count: %ld\n",
557        config.outputChannels,
558        config.outputSampleRate,
559        long(frame_count));
560 
561 	if (config.outputChannels == 2)
562 		play_buffer(mp3_buffer, config.outputChannels * frame_count, config.outputSampleRate);
563 	else
564 		play_sound(mp3_buffer, frame_count, config.outputSampleRate);
565 
566 	drmp3_free(mp3_buffer);
567 }
568 
play_wav(std::string fname)569 void c_portaudio::play_wav(std::string fname)
570 {
571 	playinfo.frames = 0;
572 	playinfo.samplerate = 0;
573 	playinfo.channels = 0;
574 	playinfo.format = 0;
575 	playinfo.sections = 0;
576 	playinfo.seekable = 0;
577 
578 	if ((playback = sf_open(fname.c_str(), SFM_READ, &playinfo)) == NULL)
579 		return;
580 
581 	int ch = playinfo.channels;
582 	int fsize = playinfo.frames * ch;
583 
584 	float *buffer = new float[fsize];
585 	memset(buffer, 0, fsize * sizeof(*buffer));
586 
587 	if (sf_readf_float( playback, buffer, playinfo.frames )) {
588 		if (ch == 2) {
589 			play_buffer(buffer, fsize, playinfo.samplerate);
590 		} else {
591 			play_sound(buffer, fsize, playinfo.samplerate);
592 		}
593 	}
594 
595 	sf_close(playback);
596 	delete [] buffer;
597 
598 }
599 
do_play_file(std::string fname)600 void c_portaudio::do_play_file(std::string fname)
601 {
602 	if ((fname.find(".mp3") != std::string::npos) ||
603 		(fname.find(".MP3") != std::string::npos)) {
604 //std::cout << "do_play_file(" << fname << ")" << std::endl;
605 		return play_mp3(fname);
606 	}
607 	if ((fname.find(".wav") != std::string::npos) ||
608 		(fname.find(".WAV") != std::string::npos)) {
609 		return play_wav(fname);
610 	}
611 	LOG_ERROR("%s : Audio file format must be either wav or mp3", fname.c_str());
612 }
613 
play_file(std::string fname)614 void c_portaudio::play_file(std::string fname)
615 {
616 	guard_lock filelock(&filelist_mutex);
617 //std::cout << "filelist.push(this, " << fname << ")" << std::endl;
618 	filelist.push( FILELIST(this, fname));
619 }
620 
621 // write len elements from monophonic audio stream to ring buffer
622 // ring buffer is stereo; LRLRLR...
623 
mon_write(double * buffer,int len,int mon_sr)624 void c_portaudio::mon_write(double *buffer, int len, int mon_sr)
625 {
626 	float vol = 0.01 * progdefaults.RxFilt_vol;
627 	float *rsbuffer = 0;
628 
629 	if (!bpfilt) init_filter();
630 
631 	try {
632 
633 // do not resample if alert samplerate == modem samplerate
634 		if (sr == mon_sr) {
635 			if (progdefaults.mon_dsp_audio) {
636 				guard_lock filter_lock(&filter_mutex);
637 				double out;
638 				for (int n = 0; n < len; n++) {
639 					if (bpfilt->Irun(buffer[n], out)) {
640 						nubuffer[2*n] = nubuffer[2*n + 1] = vol * gain * out;
641 					}
642 				}
643 			} else
644 				for (int i = 0; i < len; i++)
645 					nubuffer[2*i] = nubuffer[2*i+1] = vol * buffer[i];
646 			monitor_rb->write(nubuffer, 2 * len);
647 			return;
648 		}
649 
650 
651 // sample rates not equal; resample monophonic
652 		else {
653 			for (int i = 0; i < len; i++) fbuffer[i] = vol * buffer[i];
654 
655 			double src_ratio = 1.0 * sr / mon_sr;
656 
657 			rcdata.data_in		 = fbuffer;    // pointer to the input data samples.
658 			rcdata.input_frames	 = len;        // number of frames of data pointed to by data_in.
659 			rcdata.data_out		 = nubuffer;   // pointer to the output data samples.
660 			rcdata.output_frames = 512 * 6;    //nusize;     // Maximum number of frames pointed to by data_out.
661 			rcdata.src_ratio	 = src_ratio;  // output_sample_rate / input_sample_rate.
662 			rcdata.end_of_input	 = 0;
663 
664 			// resample before filtering
665 			int erc;
666 			if ((erc = src_process (rc, &rcdata)) != 0) {
667 				LOG_ERROR("rate converter failed: %s", src_strerror (erc));
668 				throw;
669 			}
670 			int flen = rcdata.output_frames_gen;
671 
672 			float *rsbuffer = new float[2*flen];
673 			if (progdefaults.mon_dsp_audio) {
674 				guard_lock filter_lock(&filter_mutex);
675 				double out;
676 				for (int n = 0; n < flen; n++) {
677 					if (bpfilt->Irun(nubuffer[n], out)) {
678 						rsbuffer[2*n] = rsbuffer[2*n+1] = gain * out;
679 					}
680 				}
681 				monitor_rb->write(rsbuffer, 2*flen);
682 				delete [] rsbuffer;
683 				return;
684 			}
685 			else {
686 				for (int n = 0; n < flen; n++) {
687 					rsbuffer[2*n] = rsbuffer[2*n+1] = nubuffer[n];
688 				}
689 				monitor_rb->write(rsbuffer, 2*flen);
690 				delete [] rsbuffer;
691 				return;
692 			}
693 		}
694 
695 	} catch (...) {
696 		if (rsbuffer) { delete [] rsbuffer; rsbuffer = 0;}
697 		throw;
698 	}
699 
700 }
701 
702 // read len elements of 2 channel audio from ring buffer
703 //size_t c_portaudio::mon_read(float *buffer, int len)
704 //{
705 //	return monitor_rb->read(buffer, len);
706 //}
707 
708 /**********************************************************************************
709  * AUDIO FILELIST processing loop.
710  * syncs to requests for file / clip playback
711  **********************************************************************************/
712 
713 bool filelist_thread_running = false;
714 bool filelist_terminate_flag = false;
715 
filelist_loop(void * args)716 static void * filelist_loop(void *args)
717 {
718 //	SET_THREAD_ID(AUDIO_ALERT_TID);
719 
720 	alert_thread_running   = true;
721 	alert_terminate_flag   = false;
722 	FILELIST fl;
723 	while(1) {
724 		MilliSleep(50);
725 
726 		if (filelist_terminate_flag) break;
727 		{
728 			guard_lock filelock(&filelist_mutex);
729 			if (!filelist.empty()) {
730 				fl = filelist.top();
731 				filelist.pop();
732 				fl.cpa->do_play_file(fl.fn);
733 			}
734 		}
735 	}
736 	return (void *)0;
737 }
738 
739 /**********************************************************************************
740  * Start FILELIST Thread
741  **********************************************************************************/
start_filelist_thread(void)742 static void start_filelist_thread(void)
743 {
744 	if(filelist_thread_running) return;
745 
746 	memset((void *) &filelist_pthread, 0, sizeof(filelist_pthread));
747 	memset((void *) &filelist_mutex,   0, sizeof(filelist_mutex));
748 
749 	if(pthread_mutex_init(&filelist_mutex, NULL)) {
750 		LOG_ERROR("AUDIO_ALERT thread create fail (pthread_mutex_init)");
751 		return;
752 	}
753 
754 	memset((void *) &filelist_pthread, 0, sizeof(filelist_pthread));
755 
756 	if (pthread_create(&filelist_pthread, NULL, filelist_loop, NULL) < 0) {
757 		pthread_mutex_destroy(&filelist_mutex);
758 		LOG_ERROR("AUDIO_ALERT thread create fail (pthread_create)");
759 	}
760 
761 	LOG_VERBOSE("started audio alert thread");
762 
763 	MilliSleep(10);
764 }
765 
766 /**********************************************************************************
767  * Stop AUDIO_ALERT Thread
768  **********************************************************************************/
stop_filelist_thread(void)769 static void stop_filelist_thread(void)
770 {
771 	if(!filelist_thread_running) return;
772 
773 	filelist_terminate_flag = true;
774 
775 	MilliSleep(10);
776 
777 	pthread_join(filelist_pthread, NULL);
778 
779 	LOG_VERBOSE("%s", "pa filelist thread - stopped");
780 
781 	pthread_mutex_destroy(&filelist_mutex);
782 
783 	memset((void *) &filelist_pthread, 0, sizeof(filelist_pthread));
784 	memset((void *) &filelist_mutex,   0, sizeof(filelist_mutex));
785 
786 	filelist_thread_running   = false;
787 	filelist_terminate_flag   = false;
788 }
789 
790