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