1 /*
2 
3     eboard - chess client
4     http://www.bergo.eng.br/eboard
5     https://github.com/fbergo/eboard
6     Copyright (C) 2000-2016 Felipe Bergo
7     fbergo/at/gmail/dot/com
8 
9     This program is free software; you can redistribute it and/or modify
10     it under the terms of the GNU General Public License as published by
11     the Free Software Foundation; either version 2 of the License, or
12     (at your option) any later version.
13 
14     This program is distributed in the hope that it will be useful,
15     but WITHOUT ANY WARRANTY; without even the implied warranty of
16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17     GNU General Public License for more details.
18 
19     You should have received a copy of the GNU General Public License
20     along with this program; if not, write to the Free Software
21     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22 
23 */
24 
25 #include <iostream>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <stdint.h>
29 #include <string.h>
30 #include <ctype.h>
31 #include <unistd.h>
32 #include <sys/ioctl.h>
33 #include <sys/time.h>
34 #include <sys/types.h>
35 #include <sys/wait.h>
36 #include <signal.h>
37 #include <fcntl.h>
38 #include <math.h>
39 #include <limits.h>
40 #include <gst/gst.h>
41 #include <sys/endian.h>
42 
43 #include "sound.h"
44 #include "global.h"
45 #include "tstring.h"
46 #include "util.h"
47 #include "stl.h"
48 
49 #include "config.h"
50 #include "eboard.h"
51 
SoundEvent()52 SoundEvent::SoundEvent() {
53   type=INT_WAVE;
54   Pitch=800;
55   Duration=250;
56   Count=1;
57   memset(ExtraData, 0, 256);
58   enabled = true;
59 }
60 
operator =(SoundEvent & se)61 SoundEvent SoundEvent::operator=(SoundEvent &se) {
62   type=se.type;
63   Pitch=se.Pitch;
64   Duration=se.Duration;
65   Count=se.Count;
66   enabled = se.enabled;
67   strcpy(ExtraData,se.ExtraData);
68   return(*this);
69 }
70 
operator ==(SoundEvent & se)71 int SoundEvent::operator==(SoundEvent &se) {
72   if (enabled!=se.enabled) return 0;
73   if (type!=se.type) return 0;
74   if (Pitch!=se.Pitch) return 0;
75   if (Duration!=se.Duration) return 0;
76   if (Count!=se.Count) return 0;
77   if (strcmp(ExtraData,se.ExtraData)) return 0;
78   return 1;
79 }
80 
operator !=(SoundEvent & se)81 int SoundEvent::operator!=(SoundEvent &se) {
82   return(! (se==(*this)) );
83 }
84 
read(tstring & rcline)85 void SoundEvent::read(tstring &rcline) {
86   int t;
87   static const char *sep=",\r\n";
88   string *p;
89 
90   memset(ExtraData,0,256);
91 
92   t=rcline.tokenvalue(sep);
93   switch(t) {
94   case 0: // INT_WAVE
95     type=INT_WAVE;
96     Count=1;
97     Pitch=rcline.tokenvalue(sep);
98     Duration=rcline.tokenvalue(sep);
99     p=rcline.token(sep); // Device value, deprecated
100     Count=rcline.tokenvalue(sep);
101     if (!Count) Count=1;
102     enabled = rcline.tokenbool(sep,true);
103     break;
104   case 1: // EXT_WAVE
105     type=EXT_WAVE;
106     Pitch=Duration=0;
107     p=rcline.token(sep); // Device value, deprecated
108     p=rcline.token(sep); p->copy(ExtraData,255);
109     enabled = rcline.tokenbool(sep,true);
110     break;
111   case 2: // EXT_PROGRAM
112     type=EXT_PROGRAM;
113     Pitch=Duration=0;
114     p=rcline.token(sep); p->copy(ExtraData,255);
115     enabled = rcline.tokenbool(sep,true);
116     break;
117   case 3: // PLAIN_BEEP
118     type=PLAIN_BEEP;
119     Pitch=Duration=0;
120     rcline.token(sep); /* beep string */
121     enabled = rcline.tokenbool(sep,true);
122     break;
123   default:
124     cerr << _("[eboard] bad RC line\n");
125   }
126 }
127 
operator <<(ostream & s,SoundEvent e)128 ostream & operator<<(ostream &s,  SoundEvent e) {
129   switch(e.type) {
130   case INT_WAVE:
131     s << "0," << e.Pitch << ',' << e.Duration << ',';
132     s << "default" << ',' << e.Count << ',' << (e.enabled?1:0);
133     break;
134   case EXT_WAVE:
135     s << "1," << "default" << ',' << e.ExtraData;
136     s << ',' << (e.enabled?1:0);
137     break;
138   case EXT_PROGRAM:
139     s << "2," << e.ExtraData << ',' << (e.enabled?1:0);
140     break;
141   case PLAIN_BEEP:
142     s << "3,beep" << ',' << (e.enabled?1:0);
143     break;
144   }
145   return(s);
146 }
147 
play()148 void SoundEvent::play() {
149 
150   if (type==PLAIN_BEEP) {
151     printf("%c",7);
152     fflush(stdout);
153     return;
154   }
155 
156   if (!fork()) {
157     if (type==INT_WAVE) {
158       gstBeep();
159       _exit(0);
160     }
161 
162     if (type==EXT_WAVE) {
163       gstPlay(string(ExtraData));
164       _exit(0);
165     }
166 
167     if (type==EXT_PROGRAM) {
168       close(1);
169       close(2);
170       execlp("/bin/sh","/bin/sh","-c",ExtraData,0);
171       _exit(0);
172     }
173 
174     _exit(0);
175   }
176 }
177 
gstPlay(const string & _input)178 void SoundEvent::gstPlay(const string &_input) {
179   char gst_string[512];
180   GstElement *pipeline;
181   GstBus     *bus;
182   GstMessage *msg;
183   string input(_input);
184 
185   if (input.empty()) return;
186   char tmp[512], *ptr;
187   ptr = realpath(input.c_str(), tmp);
188   if (ptr==NULL) return;
189   input = tmp;
190 
191   //printf("gstPlay=[%s]\n",input.c_str());
192 
193   memset(gst_string,0,512);
194   snprintf(gst_string,511,"playbin uri=file://%s",input.c_str());
195 
196   pipeline = gst_parse_launch(gst_string, NULL);
197   gst_element_set_state(pipeline, GST_STATE_PLAYING);
198 
199   bus = gst_element_get_bus(pipeline);
200   msg = gst_bus_timed_pop_filtered(bus,GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
201 
202   if (msg != NULL)
203     gst_message_unref(msg);
204   gst_object_unref(bus);
205   gst_element_set_state(pipeline, GST_STATE_NULL);
206   gst_object_unref(pipeline);
207 
208   //printf("gstPlay done\n");
209 }
210 
211 class PlaybackData {
212 public:
PlaybackData()213   PlaybackData() {
214     pipeline = src = NULL;
215     beep = NULL;
216     pos   = 0;
217   }
218 
219   GstElement *pipeline, *src;
220   MultiBeep  *beep;
221   int         pos;
222 };
223 
gstbeep_push(GstElement * src,PlaybackData * pd)224 static void gstbeep_push(GstElement *src, PlaybackData *pd) {
225   GstBuffer *buffer;
226   GstFlowReturn ret;
227   GstMapInfo info;
228 
229   int chunk = 2 * pd->beep->samples;
230 
231   //printf("gstbeep_push chunk=%d B samples=%d\n",chunk, chunk/2);
232   buffer = gst_buffer_new_and_alloc(chunk);
233 
234   if (buffer != NULL) {
235     GST_BUFFER_TIMESTAMP(buffer) = gst_util_uint64_scale(0, GST_SECOND, pd->beep->SampleRate);
236     GST_BUFFER_DURATION(buffer)  = gst_util_uint64_scale(chunk/2, GST_SECOND, pd->beep->SampleRate);
237 
238     gst_buffer_map(buffer, &info, GST_MAP_WRITE);
239 
240     memcpy( info.data, pd->beep->data, chunk );
241     gst_buffer_unmap(buffer, &info);
242 
243     g_signal_emit_by_name (src, "push-buffer", buffer, &ret);
244     gst_buffer_unref(buffer);
245 
246     //if (ret != GST_FLOW_OK) printf("oops ret = %d\n", (int)ret);
247   }
248   g_signal_emit_by_name(src, "end-of-stream", &ret);
249 
250 }
251 
gstbeep_setup(GstElement * pipeline,GstElement * source,PlaybackData * pd)252 static void gstbeep_setup(GstElement *pipeline, GstElement *source, PlaybackData *pd) {
253   gchar *audio_caps_text;
254   GstCaps *audio_caps;
255   GstFlowReturn ret;
256 
257   //printf("beep::setup\n");
258 
259   pd->src = source;
260 
261   audio_caps_text = g_strdup_printf("audio/x-raw,format=S16LE,channels=1,rate=%d,layout=interleaved",
262 				    pd->beep->SampleRate);
263 
264   audio_caps = gst_caps_from_string (audio_caps_text);
265   g_object_set (source, "caps", audio_caps, "format", GST_FORMAT_TIME, NULL);
266   gst_caps_unref (audio_caps);
267   gstbeep_push(source, pd);
268   g_free (audio_caps_text);
269 }
270 
gstBeep()271 void SoundEvent::gstBeep() {
272   MultiBeep *mb;
273   PlaybackData *pd;
274   GstBus *bus;
275   GstMessage *msg;
276 
277   //printf("beep::go\n");
278 
279   mb = new MultiBeep(44100,Duration,Pitch,Count);
280   pd = new PlaybackData();
281   pd->beep = mb;
282 
283   pd->pipeline = gst_parse_launch("playbin uri=appsrc://", NULL);
284 
285   g_signal_connect(pd->pipeline, "source-setup", G_CALLBACK(gstbeep_setup), pd);
286   bus = gst_element_get_bus(pd->pipeline);
287   gst_element_set_state(pd->pipeline, GST_STATE_PLAYING);
288 
289   //printf("beep::waiting\n");
290 
291   msg = gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE, (GstMessageType)(GST_MESSAGE_EOS | GST_MESSAGE_ERROR));
292   if (msg != NULL) {
293 
294     /*
295     if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) {
296       GError *err;
297       gchar *debug_info;
298       gst_message_parse_error (msg, &err, &debug_info);
299       g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
300       g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
301       g_clear_error (&err);
302       g_free (debug_info);
303     }
304     */
305 
306     gst_message_unref(msg);
307   }
308 
309   gst_object_unref(bus);
310   gst_element_set_state(pd->pipeline, GST_STATE_NULL);
311   gst_object_unref(pd->pipeline);
312   delete pd->beep;
313   delete pd;
314 }
315 
getDescription()316 char *SoundEvent::getDescription() {
317   switch(type) {
318   case INT_WAVE:
319     snprintf(pvt,128,_("%d %s, %d Hz for %d msec"),
320 	     Count,(Count>1)?_("beeps"):
321 	                     _("beep"),Pitch,Duration);
322     break;
323   case EXT_WAVE:
324     snprintf(pvt,128,_("play file %s"),ExtraData);
325     break;
326   case EXT_PROGRAM:
327     snprintf(pvt,128,_("run %s"),ExtraData);
328     break;
329   case PLAIN_BEEP:
330     snprintf(pvt,128,_("plain console beep"));
331     break;
332   default:
333     g_strlcpy(pvt,_("nothing"),128);
334   }
335   return pvt;
336 }
337 
edit(SoundEventChangeListener * listener)338 void SoundEvent::edit(SoundEventChangeListener *listener) {
339   (new SoundEventDialog(this,listener))->show();
340 }
341 
342 // dialog
343 
SoundEventDialog(SoundEvent * src,SoundEventChangeListener * listener)344 SoundEventDialog::SoundEventDialog(SoundEvent *src, SoundEventChangeListener *listener) : ModalDialog(N_("Sound Event")) {
345   GtkWidget *v,*tf,*rh,*mh[4],*ml[4],*hs,*bb,*ok,*cancel,*test,*brw;
346   GSList *rg;
347   int i,j;
348   GtkObject *pitch,*dur,*cou;
349   GtkWidget *tl=0,*om=0,*cbh=0,*omm,*ommi;
350 
351   gtk_window_set_default_size(GTK_WINDOW(widget),480,340);
352 
353   obj=src;
354   hearer=listener;
355 
356   v=gtk_vbox_new(FALSE,4);
357   gtk_container_add(GTK_CONTAINER(widget),v);
358 
359   tf=gtk_frame_new(_("Event Type"));
360   gtk_frame_set_shadow_type(GTK_FRAME(tf),GTK_SHADOW_ETCHED_IN);
361   gtk_box_pack_start(GTK_BOX(v),tf,FALSE,FALSE,4);
362 
363   rh=gtk_vbox_new(FALSE,4);
364   gtk_container_add(GTK_CONTAINER(tf),rh);
365 
366   rd[0]=gtk_radio_button_new_with_label( 0, _("Beep (need Pitch, Duration and Count)") );
367   rg=gtk_radio_button_group(GTK_RADIO_BUTTON(rd[0]));
368   rd[1]=gtk_radio_button_new_with_label(rg, _("Play Media File (need Filename)") );
369   rg=gtk_radio_button_group(GTK_RADIO_BUTTON(rd[1]));
370   rd[2]=gtk_radio_button_new_with_label(rg, _("Run Program (need Filename)") );
371   rg=gtk_radio_button_group(GTK_RADIO_BUTTON(rd[2]));
372   rd[3]=gtk_radio_button_new_with_label(rg, _("Console Beep") );
373 
374   for(i=0;i<4;i++) {
375     gtk_box_pack_start(GTK_BOX(rh),rd[i],FALSE,FALSE,4);
376     gshow(rd[i]);
377   }
378 
379   mh[0]=gtk_hbox_new(FALSE,4);
380   mh[1]=gtk_hbox_new(FALSE,4);
381   mh[2]=gtk_hbox_new(FALSE,4);
382   mh[3]=gtk_hbox_new(FALSE,4);
383 
384   ml[0]=gtk_label_new(_("Pitch (Hz):"));
385   ml[1]=gtk_label_new(_("Duration (msec):"));
386   ml[2]=gtk_label_new(_("File to play / Program to run:"));
387   ml[3]=gtk_label_new(_("Count:"));
388 
389   brw=gtk_button_new_with_label(_(" Browse... "));
390 
391   pitch=gtk_adjustment_new((gfloat)(src->Pitch),50.0,2000.0,1.0,10.0,0.0);
392   en[0]=gtk_spin_button_new(GTK_ADJUSTMENT(pitch),0.5,0);
393 
394   dur=gtk_adjustment_new((gfloat)(src->Duration),30.0,4000.0,10.0,100.0,0.0);
395   en[1]=gtk_spin_button_new(GTK_ADJUSTMENT(dur),0.5,0);
396 
397   en[2]=gtk_entry_new(); // file/program
398 
399   cou=gtk_adjustment_new((gfloat)(src->Count),1.0,5.0,1.0,1.0,0);
400   en[3]=gtk_spin_button_new(GTK_ADJUSTMENT(cou),0.5,0);
401 
402   j=global.SoundFiles.size();
403 
404   if (j) {
405     cbh=gtk_hbox_new(FALSE,4);
406     om=gtk_option_menu_new();
407     omm=gtk_menu_new();
408 
409     for(i=0;i<j;i++) {
410       ommi=gtk_menu_item_new_with_label(global.SoundFiles[i].c_str());
411       gtk_signal_connect(GTK_OBJECT(ommi),"activate",
412 			 GTK_SIGNAL_FUNC(snddlg_picktheme),(gpointer)this);
413       gtk_menu_shell_append(GTK_MENU_SHELL(omm),ommi);
414       gshow(ommi);
415       sthemes.push_back(ommi);
416     }
417     gtk_option_menu_set_menu(GTK_OPTION_MENU(om),omm);
418     tl=gtk_label_new(_("Configured Sound Files:"));
419   }
420 
421   gtk_box_pack_start(GTK_BOX(v),mh[0],TRUE,TRUE,4);
422 
423   // row: pitch - duration - count
424   gtk_box_pack_start(GTK_BOX(mh[0]),ml[0],FALSE,FALSE,4);
425   gtk_box_pack_start(GTK_BOX(mh[0]),en[0],TRUE,TRUE,4);
426   gtk_box_pack_start(GTK_BOX(mh[0]),ml[1],FALSE,FALSE,4);
427   gtk_box_pack_start(GTK_BOX(mh[0]),en[1],TRUE,TRUE,4);
428   gtk_box_pack_start(GTK_BOX(mh[0]),ml[3],FALSE,FALSE,4);
429   gtk_box_pack_start(GTK_BOX(mh[0]),en[3],TRUE,TRUE,4);
430 
431   // row: file to play label
432   gtk_box_pack_start(GTK_BOX(v),mh[2],FALSE,FALSE,4);
433   gtk_box_pack_start(GTK_BOX(mh[2]),ml[2],FALSE,FALSE,4);
434 
435   // row: file entry + browse
436   gtk_box_pack_start(GTK_BOX(v),mh[3],FALSE,FALSE,4);
437   gtk_box_pack_start(GTK_BOX(mh[3]),en[2],TRUE,TRUE,4);
438   gtk_box_pack_start(GTK_BOX(mh[3]),brw,FALSE,FALSE,4);
439 
440   if (j) {
441     gtk_box_pack_end(GTK_BOX(cbh),om,FALSE,FALSE,4);
442     gtk_box_pack_end(GTK_BOX(cbh),tl,FALSE,FALSE,4);
443     gtk_box_pack_start(GTK_BOX(v),cbh,FALSE,FALSE,4);
444   }
445 
446   for(i=0;i<4;i++)
447     Gtk::show(ml[i],en[i],mh[i],NULL);
448 
449   if (j)
450     Gtk::show(cbh,tl,om,NULL);
451 
452   gshow(brw);
453 
454   hs=gtk_hseparator_new();
455   gtk_box_pack_start(GTK_BOX(v),hs,FALSE,FALSE,4);
456 
457   bb=gtk_hbutton_box_new();
458   gtk_button_box_set_layout(GTK_BUTTON_BOX(bb), GTK_BUTTONBOX_END);
459   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bb), 5);
460   gtk_box_pack_start(GTK_BOX(v),bb,FALSE,FALSE,2);
461 
462   ok=gtk_button_new_with_label(_("Ok"));
463   GTK_WIDGET_SET_FLAGS(ok,GTK_CAN_DEFAULT);
464   test=gtk_button_new_with_label(_("Test"));
465   GTK_WIDGET_SET_FLAGS(test,GTK_CAN_DEFAULT);
466   cancel=gtk_button_new_with_label(_("Cancel"));
467   GTK_WIDGET_SET_FLAGS(cancel,GTK_CAN_DEFAULT);
468   gtk_box_pack_start(GTK_BOX(bb),ok,TRUE,TRUE,0);
469   gtk_box_pack_start(GTK_BOX(bb),test,TRUE,TRUE,0);
470   gtk_box_pack_start(GTK_BOX(bb),cancel,TRUE,TRUE,0);
471   gtk_widget_grab_default(ok);
472 
473   Gtk::show(bb,ok,test,cancel,hs,rh,tf,v,NULL);
474   setDismiss(GTK_OBJECT(cancel),"clicked");
475 
476   switch(src->type) {
477   case INT_WAVE:
478     gtset(GTK_TOGGLE_BUTTON(rd[0]), 1);
479     break;
480   case EXT_WAVE:
481     gtset(GTK_TOGGLE_BUTTON(rd[1]), 1);
482     break;
483   case EXT_PROGRAM:
484     gtset(GTK_TOGGLE_BUTTON(rd[2]), 1);
485     break;
486   case PLAIN_BEEP:
487     gtset(GTK_TOGGLE_BUTTON(rd[3]), 1);
488     break;
489   }
490 
491   gtk_entry_set_text(GTK_ENTRY(en[2]),src->ExtraData);
492 
493   gtk_signal_connect(GTK_OBJECT(ok),"clicked",
494 		     GTK_SIGNAL_FUNC(snddlg_ok),(gpointer)(this));
495   gtk_signal_connect(GTK_OBJECT(test),"clicked",
496 		     GTK_SIGNAL_FUNC(snddlg_test),(gpointer)(this));
497   gtk_signal_connect(GTK_OBJECT(brw),"clicked",
498 		     GTK_SIGNAL_FUNC(snddlg_browse),(gpointer)(this));
499 
500 }
501 
snddlg_picktheme(GtkMenuItem * w,gpointer data)502 void snddlg_picktheme(GtkMenuItem *w,gpointer data) {
503   SoundEventDialog *me;
504   EboardFileFinder eff;
505   char z[512],zz[512];
506   int i,j;
507 
508   me=(SoundEventDialog *)data;
509 
510   j=me->sthemes.size();
511 
512   for(i=0;i<j;i++)
513     if (w == GTK_MENU_ITEM(me->sthemes[i])) {
514       g_strlcpy(z,global.SoundFiles[i].c_str(),512);
515       if (strlen(z)) {
516 	if (eff.find(z,zz))
517 	  strcpy(z,zz);
518 	gtk_entry_set_text(GTK_ENTRY(me->en[2]),z);
519 	gtset(GTK_TOGGLE_BUTTON(me->rd[1]),TRUE);
520       }
521       return;
522     }
523 }
524 
snddlg_browse(GtkWidget * w,gpointer data)525 void snddlg_browse(GtkWidget *w,gpointer data) {
526   SoundEventDialog *me;
527   FileDialog *fd;
528 
529   me=(SoundEventDialog *)data;
530   fd=new FileDialog(_("Browse"));
531 
532   if (fd->run()) {
533     gtk_entry_set_text(GTK_ENTRY(me->en[2]), fd->FileName);
534   }
535   delete fd;
536 }
537 
snddlg_ok(GtkWidget * w,gpointer data)538 void snddlg_ok(GtkWidget *w,gpointer data) {
539   SoundEventDialog *me;
540   me=(SoundEventDialog *)data;
541   me->apply(me->obj);
542   if (me->hearer)
543     me->hearer->SoundEventChanged();
544   me->release();
545 }
546 
snddlg_test(GtkWidget * w,gpointer data)547 void snddlg_test(GtkWidget *w,gpointer data) {
548   SoundEventDialog *me;
549   SoundEvent foo;
550   me=(SoundEventDialog *)data;
551   me->apply(&foo);
552   foo.play();
553 }
554 
apply(SoundEvent * dest)555 void SoundEventDialog::apply(SoundEvent *dest) {
556   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rd[0])))
557     dest->type=INT_WAVE;
558   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rd[1])))
559     dest->type=EXT_WAVE;
560   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rd[2])))
561     dest->type=EXT_PROGRAM;
562   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rd[3])))
563     dest->type=PLAIN_BEEP;
564   dest->Pitch=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(en[0]));
565   dest->Duration=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(en[1]));
566   dest->Count=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(en[3]));
567   g_strlcpy(dest->ExtraData,gtk_entry_get_text(GTK_ENTRY(en[2])),256);
568 }
569 
MultiBeep(int _samplerate,int _duration,int _pitch,int _count)570 MultiBeep::MultiBeep(int _samplerate, int _duration, int _pitch, int _count) {
571   SampleRate = _samplerate;
572   Duration   = _duration;
573   Pitch      = _pitch;
574   Count      = _count;
575 
576   int interval;
577   short int silence[128];
578   int bl,i,ts,ec,sc;
579   double r,s;
580 
581   interval=(120*SampleRate)/1000; // 120 msec
582   bl = (SampleRate*Duration)/1000;
583 
584   ts = bl*Count + interval*(Count-1); // total samples
585   ts += 128- ts%128;
586   samples = ts;
587 
588   data = (short int *) malloc(2 * ts);
589   if (data==NULL) return;
590 
591   memset(data,0,2*ts);
592   memset(silence,0,2*128);
593   sc = ((120*SampleRate)/1000) / 128; // silence frames
594 
595   for(i=0;i<bl;i++) {
596     r= i * ((double)Pitch / (double)SampleRate);
597     s= ((double)i / (double)bl);
598     s= sin(M_PI*s)*0.80;
599     data[i]=(short int)(32000.0*s*sin(M_PI*2.0*r));
600   }
601 
602   for(i=1;i<Count;i++)
603     memcpy(&data[i*(bl+interval)],data,bl*2);
604 }
605 
~MultiBeep()606 MultiBeep::~MultiBeep() {
607   if (data!=NULL) free(data);
608 }
609