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