/Users/lyon/j4p/src/sound/player/AudioSynth01.java

1    package sound.player; 
2     
3    /** 
4     * Created by IntelliJ IDEA. 
5     * User: Douglas Lyon 
6     * Date: Dec 13, 2004 
7     * Time: 8:09:16 PM 
8     * Copyright DocJava, Inc. 
9     */ 
10    
11   import javax.swing.*; 
12   import java.awt.*; 
13   import java.awt.event.*; 
14   import javax.sound.sampled.*; 
15   import java.io.*; 
16   import java.nio.channels.*; 
17   import java.nio.*; 
18   import java.util.*; 
19    
20   public class AudioSynth01 extends JFrame { 
21    
22       //The following are general instance variables 
23       // used to create a SourceDataLine object. 
24       AudioFormat audioFormat; 
25       AudioInputStream audioInputStream; 
26       SourceDataLine sourceDataLine; 
27    
28       //The following are audio format parameters. 
29       // They may be modified by the signal generator 
30       // at runtime.  Values allowed by Java 
31       // SDK 1.4.1 are shown in comments. 
32       float sampleRate = 16000.0F; 
33       //Allowable 8000,11025,16000,22050,44100 
34       int sampleSizeInBits = 16; 
35       //Allowable 8,16 
36       int channels = 1; 
37       //Allowable 1,2 
38       boolean signed = true; 
39       //Allowable true,false 
40       boolean bigEndian = true; 
41       //Allowable true,false 
42    
43       //A buffer to hold two seconds monaural and one 
44       // second stereo data at 16000 samp/sec for 
45       // 16-bit samples 
46       byte audioData[] = new byte[16000 * 4]; 
47    
48       //Following components appear in the North 
49       // position of the GUI. 
50       final JButton generateBtn = 
51               new JButton("Generate"); 
52       final JButton playOrFileBtn = 
53               new JButton("Play/File"); 
54       final JLabel elapsedTimeMeter = 
55               new JLabel("0000"); 
56    
57       //Following radio buttons select a synthetic 
58       // data type.  Add more buttons if you add 
59       // more synthetic data types.  They appear in 
60       // the center position of the GUI. 
61       final JRadioButton tones = 
62               new JRadioButton("Tones", true); 
63       final JRadioButton stereoPanning = 
64               new JRadioButton("Stereo Panning"); 
65       final JRadioButton stereoPingpong = 
66               new JRadioButton("Stereo Pingpong"); 
67       final JRadioButton fmSweep = 
68               new JRadioButton("FM Sweep"); 
69       final JRadioButton decayPulse = 
70               new JRadioButton("Decay Pulse"); 
71       final JRadioButton echoPulse = 
72               new JRadioButton("Echo Pulse"); 
73       final JRadioButton waWaPulse = 
74               new JRadioButton("WaWa Pulse"); 
75    
76       //Following components appear in the South 
77       // position of the GUI. 
78       final JRadioButton listen = 
79               new JRadioButton("Listen", true); 
80       final JRadioButton file = 
81               new JRadioButton("File"); 
82       final JTextField fileName = 
83               new JTextField("junk", 10); 
84    
85       //-------------------------------------------// 
86       public static void main(String args[]) { 
87           new AudioSynth01(); 
88       }//end main 
89       //-------------------------------------------// 
90    
91       public AudioSynth01() {//constructor 
92           //A panel for the North position.  Note the 
93           // etched border. 
94           final JPanel controlButtonPanel = 
95                   new JPanel(); 
96           controlButtonPanel.setBorder(BorderFactory.createEtchedBorder()); 
97    
98           //A panel and button group for the radio 
99           // buttons in the Center position. 
100          final JPanel synButtonPanel = new JPanel(); 
101          final ButtonGroup synButtonGroup = 
102                  new ButtonGroup(); 
103          //This panel is used for cosmetic purposes 
104          // only, to cause the radio buttons to be 
105          // centered horizontally in the Center 
106          // position. 
107          final JPanel centerPanel = new JPanel(); 
108   
109          //A panel for the South position.  Note the 
110          // etched border. 
111          final JPanel outputButtonPanel = 
112                  new JPanel(); 
113          outputButtonPanel.setBorder(BorderFactory.createEtchedBorder()); 
114          final ButtonGroup outputButtonGroup = 
115                  new ButtonGroup(); 
116   
117          //Disable the Play button initially to force 
118          // the user to generate some data before 
119          // trying to listen to it or write it to a 
120          // file. 
121          playOrFileBtn.setEnabled(false); 
122   
123          //Register anonymous listeners on the 
124          // Generate button and the Play/File button. 
125          generateBtn.addActionListener(new ActionListener() { 
126              public void actionPerformed(ActionEvent e) { 
127                  //Don't allow Play during generation 
128                  playOrFileBtn.setEnabled(false); 
129                  //Generate synthetic data 
130                  new SynGen().getSyntheticData(audioData); 
131                  //Now it is OK for the user to listen 
132                  // to or file the synthetic audio data. 
133                  playOrFileBtn.setEnabled(true); 
134              }//end actionPerformed 
135          }//end ActionListener 
136          );//end addActionListener() 
137   
138          playOrFileBtn.addActionListener(new ActionListener() { 
139              public void actionPerformed(ActionEvent e) { 
140                  //Play or file the data synthetic data 
141                  playOrFileData(); 
142              }//end actionPerformed 
143          }//end ActionListener 
144          );//end addActionListener() 
145   
146          //Add two buttons and a text field to a 
147          // physical group in the North of the GUI. 
148          controlButtonPanel.add(generateBtn); 
149          controlButtonPanel.add(playOrFileBtn); 
150          controlButtonPanel.add(elapsedTimeMeter); 
151   
152          //Add radio buttons to a mutually exclusive 
153          // group in the Center of the GUI.  Make 
154          // additions here if you add new synthetic 
155          // generator methods. 
156          synButtonGroup.add(tones); 
157          synButtonGroup.add(stereoPanning); 
158          synButtonGroup.add(stereoPingpong); 
159          synButtonGroup.add(fmSweep); 
160          synButtonGroup.add(decayPulse); 
161          synButtonGroup.add(echoPulse); 
162          synButtonGroup.add(waWaPulse); 
163   
164          //Add radio buttons to a physical group and 
165          // center it in the Center of the GUI. Make 
166          // additions here if you add new synthetic 
167          // generator methods. 
168          synButtonPanel.setLayout(new GridLayout(0, 1)); 
169          synButtonPanel.add(tones); 
170          synButtonPanel.add(stereoPanning); 
171          synButtonPanel.add(stereoPingpong); 
172          synButtonPanel.add(fmSweep); 
173          synButtonPanel.add(decayPulse); 
174          synButtonPanel.add(echoPulse); 
175          synButtonPanel.add(waWaPulse); 
176   
177          //Note that the centerPanel has center 
178          // alignment by default. 
179          centerPanel.add(synButtonPanel); 
180   
181          //Add radio buttons to a mutually exclusive 
182          // group in the South of the GUI. 
183          outputButtonGroup.add(listen); 
184          outputButtonGroup.add(file); 
185   
186          //Add radio buttons to a physical group in 
187          // the South of the GUI. 
188          outputButtonPanel.add(listen); 
189          outputButtonPanel.add(file); 
190          outputButtonPanel.add(fileName); 
191   
192          //Add the panels containing components to the 
193          // content pane of the GUI in the appropriate 
194          // positions. 
195          getContentPane().add(controlButtonPanel, BorderLayout.NORTH); 
196          getContentPane().add(centerPanel, 
197                  BorderLayout.CENTER); 
198          getContentPane().add(outputButtonPanel, 
199                  BorderLayout.SOUTH); 
200   
201          //Finish the GUI.  If you add more radio 
202          // buttons in the center, you may need to 
203          // modify the call to setSize to increase 
204          // the vertical component of the GUI size. 
205          setTitle("Copyright 2003, R.G.Baldwin"); 
206          setDefaultCloseOperation(EXIT_ON_CLOSE); 
207          setSize(250, 275); 
208          setVisible(true); 
209      }//end constructor 
210      //-------------------------------------------// 
211   
212      //This method plays or files the synthetic 
213      // audio data that has been generated and saved 
214      // in an array in memory. 
215      private void playOrFileData() { 
216          try { 
217              //Get an input stream on the byte array 
218              // containing the data 
219              InputStream byteArrayInputStream = 
220                      new ByteArrayInputStream(audioData); 
221   
222              //Get the required audio format 
223              audioFormat = new AudioFormat(sampleRate, 
224                      sampleSizeInBits, 
225                      channels, 
226                      signed, 
227                      bigEndian); 
228   
229              //Get an audio input stream from the 
230              // ByteArrayInputStream 
231              audioInputStream = new AudioInputStream(byteArrayInputStream, 
232                      audioFormat, 
233                      audioData.length / audioFormat. 
234                      getFrameSize()); 
235   
236              //Get info on the required data line 
237              DataLine.Info dataLineInfo = 
238                      new DataLine.Info(SourceDataLine.class, 
239                              audioFormat); 
240   
241              //Get a SourceDataLine object 
242              sourceDataLine = (SourceDataLine) 
243                      AudioSystem.getLine(dataLineInfo); 
244              //Decide whether to play the synthetic 
245              // data immediately, or to write it into 
246              // an audio file, based on the user 
247              // selection of the radio buttons in the 
248              // South of the GUI.. 
249              if (listen.isSelected()) { 
250                  //Create a thread to play back the data and 
251                  // start it running.  It will run until all 
252                  // the data has been played back 
253                  new ListenThread().start(); 
254              } else { 
255                  //Disable buttons until existing data 
256                  // is written to the file. 
257                  generateBtn.setEnabled(false); 
258                  playOrFileBtn.setEnabled(false); 
259   
260                  //Write the data to an output file with 
261                  // the name provided by the text field 
262                  // in the South of the GUI. 
263                  try { 
264                      AudioSystem.write(audioInputStream, 
265                              AudioFileFormat.Type.AU, 
266                              new File(fileName.getText() + 
267                              ".au")); 
268                  } catch (Exception e) { 
269                      e.printStackTrace(); 
270                      System.exit(0); 
271                  }//end catch 
272                  //Enable buttons for another operation 
273                  generateBtn.setEnabled(true); 
274                  playOrFileBtn.setEnabled(true); 
275              }//end else 
276          } catch (Exception e) { 
277              e.printStackTrace(); 
278              System.exit(0); 
279          }//end catch 
280      }//end playOrFileData 
281  //=============================================// 
282   
283  //Inner class to play back the data that was 
284  // saved. 
285      class ListenThread extends Thread { 
286          //This is a working buffer used to transfer 
287          // the data between the AudioInputStream and 
288          // the SourceDataLine.  The size is rather 
289          // arbitrary. 
290          byte playBuffer[] = new byte[16384]; 
291   
292          public void run() { 
293              try { 
294                  //Disable buttons while data is being 
295                  // played. 
296                  generateBtn.setEnabled(false); 
297                  playOrFileBtn.setEnabled(false); 
298   
299                  //Open and start the SourceDataLine 
300                  sourceDataLine.open(audioFormat); 
301                  sourceDataLine.start(); 
302   
303                  int cnt; 
304                  //Get beginning of elapsed time for 
305                  // playback 
306                  long startTime = new Date().getTime(); 
307   
308                  //Transfer the audio data to the speakers 
309                  while ((cnt = audioInputStream.read(playBuffer, 0, 
310                          playBuffer.length)) 
311                          != -1) { 
312                      //Keep looping until the input read 
313                      // method returns -1 for empty stream. 
314                      if (cnt > 0) { 
315                          //Write data to the internal buffer of 
316                          // the data line where it will be 
317                          // delivered to the speakers in real 
318                          // time 
319                          sourceDataLine.write(playBuffer, 0, cnt); 
320                      }//end if 
321                  }//end while 
322   
323                  //Block and wait for internal buffer of the 
324                  // SourceDataLine to become empty. 
325                  sourceDataLine.drain(); 
326   
327   
328                  //Get and display the elapsed time for 
329                  // the previous playback. 
330                  int elapsedTime = 
331                          (int) (new Date().getTime() - startTime); 
332                  elapsedTimeMeter.setText("" + elapsedTime); 
333   
334                  //Finish with the SourceDataLine 
335                  sourceDataLine.stop(); 
336                  sourceDataLine.close(); 
337   
338                  //Re-enable buttons for another operation 
339                  generateBtn.setEnabled(true); 
340                  playOrFileBtn.setEnabled(true); 
341              } catch (Exception e) { 
342                  e.printStackTrace(); 
343                  System.exit(0); 
344              }//end catch 
345   
346          }//end run 
347      }//end inner class ListenThread 
348  //=============================================// 
349   
350  //Inner signal generator class. 
351   
352  //An object of this class can be used to 
353  // generate a variety of different synthetic 
354  // audio signals.  Each time the getSyntheticData 
355  // method is called on an object of this class, 
356  // the method will fill the incoming array with 
357  // the samples for a synthetic signal. 
358      class SynGen { 
359          //Note:  Because this class uses a ByteBuffer 
360          // asShortBuffer to handle the data, it can 
361          // only be used to generate signed 16-bit 
362          // data. 
363          ByteBuffer byteBuffer; 
364          ShortBuffer shortBuffer; 
365          int byteLength; 
366   
367          void getSyntheticData(byte[] synDataBuffer) { 
368              //Prepare the ByteBuffer and the shortBuffer 
369              // for use 
370              byteBuffer = ByteBuffer.wrap(synDataBuffer); 
371              shortBuffer = byteBuffer.asShortBuffer(); 
372   
373              byteLength = synDataBuffer.length; 
374   
375              //Decide which synthetic data generator 
376              // method to invoke based on which radio 
377              // button the user selected in the Center of 
378              // the GUI.  If you add more methods for 
379              // other synthetic data types, you need to 
380              // add corresponding radio buttons to the 
381              // GUI and add statements here to test the 
382              // new radio buttons.  Make additions here 
383              // if you add new synthetic generator 
384              // methods. 
385   
386              if (tones.isSelected()) tones(); 
387              if (stereoPanning.isSelected()) 
388                  stereoPanning(); 
389              if (stereoPingpong.isSelected()) 
390                  stereoPingpong(); 
391              if (fmSweep.isSelected()) fmSweep(); 
392              if (decayPulse.isSelected()) decayPulse(); 
393              if (echoPulse.isSelected()) echoPulse(); 
394              if (waWaPulse.isSelected()) waWaPulse(); 
395   
396          }//end getSyntheticData method 
397          //-------------------------------------------// 
398   
399          //This method generates a monaural tone 
400          // consisting of the sum of three sinusoids. 
401          void tones() { 
402              channels = 1;//Java allows 1 or 2 
403              //Each channel requires two 8-bit bytes per 
404              // 16-bit sample. 
405              int bytesPerSamp = 2; 
406              sampleRate = 16000.0F; 
407              // Allowable 8000,11025,16000,22050,44100 
408              int sampLength = byteLength / bytesPerSamp; 
409              for (int cnt = 0; cnt < sampLength; cnt++) { 
410                  double time = cnt / sampleRate; 
411                  double freq = 950.0;//arbitrary frequency 
412                  double sinValue = 
413                          (Math.sin(2 * Math.PI * freq * time) + 
414                          Math.sin(2 * Math.PI * (freq / 1.8) * time) + 
415                          Math.sin(2 * Math.PI * (freq / 1.5) * time)) / 3.0; 
416                  shortBuffer.put((short) (16000 * sinValue)); 
417              }//end for loop 
418          }//end method tones 
419          //-------------------------------------------// 
420   
421          //This method generates a stereo speaker sweep, 
422          // starting with a relatively high frequency 
423          // tone on the left speaker and moving across 
424          // to a lower frequency tone on the right 
425          // speaker. 
426          void stereoPanning() { 
427              channels = 2;//Java allows 1 or 2 
428              int bytesPerSamp = 4;//Based on channels 
429              sampleRate = 16000.0F; 
430              // Allowable 8000,11025,16000,22050,44100 
431              int sampLength = byteLength / bytesPerSamp; 
432              for (int cnt = 0; cnt < sampLength; cnt++) { 
433                  //Calculate time-varying gain for each 
434                  // speaker 
435                  double rightGain = 16000.0 * cnt / sampLength; 
436                  double leftGain = 16000.0 - rightGain; 
437   
438                  double time = cnt / sampleRate; 
439                  double freq = 600;//An arbitrary frequency 
440                  //Generate data for left speaker 
441                  double sinValue = 
442                          Math.sin(2 * Math.PI * (freq) * time); 
443                  shortBuffer.put((short) (leftGain * sinValue)); 
444                  //Generate data for right speaker 
445                  sinValue = 
446                          Math.sin(2 * Math.PI * (freq * 0.8) * time); 
447                  shortBuffer.put((short) (rightGain * sinValue)); 
448              }//end for loop 
449          }//end method stereoPanning 
450          //-------------------------------------------// 
451   
452          //This method uses stereo to switch a sound 
453          // back and forth between the left and right 
454          // speakers at a rate of about eight switches 
455          // per second.  On my system, this is a much 
456          // better demonstration of the sound separation 
457          // between the two speakers than is the 
458          // demonstration produced by the stereoPanning 
459          // method.  Note also that because the sounds 
460          // are at different frequencies, the sound 
461          // produced is similar to that of U.S. 
462          // emergency vehicles. 
463   
464          void stereoPingpong() { 
465              channels = 2;//Java allows 1 or 2 
466              int bytesPerSamp = 4;//Based on channels 
467              sampleRate = 16000.0F; 
468              // Allowable 8000,11025,16000,22050,44100 
469              int sampLength = byteLength / bytesPerSamp; 
470              double leftGain = 0.0; 
471              double rightGain = 16000.0; 
472              for (int cnt = 0; cnt < sampLength; cnt++) { 
473                  //Calculate time-varying gain for each 
474                  // speaker 
475                  if (cnt % (sampLength / 8) == 0) { 
476                      //swap gain values 
477                      double temp = leftGain; 
478                      leftGain = rightGain; 
479                      rightGain = temp; 
480                  }//end if 
481   
482                  double time = cnt / sampleRate; 
483                  double freq = 600;//An arbitrary frequency 
484                  //Generate data for left speaker 
485                  double sinValue = 
486                          Math.sin(2 * Math.PI * (freq) * time); 
487                  shortBuffer.put((short) (leftGain * sinValue)); 
488                  //Generate data for right speaker 
489                  sinValue = 
490                          Math.sin(2 * Math.PI * (freq * 0.8) * time); 
491                  shortBuffer.put((short) (rightGain * sinValue)); 
492              }//end for loop 
493          }//end stereoPingpong method 
494          //-------------------------------------------// 
495   
496          //This method generates a monaural linear 
497          // frequency sweep from 100 Hz to 1000Hz. 
498          void fmSweep() { 
499              channels = 1;//Java allows 1 or 2 
500              int bytesPerSamp = 2;//Based on channels 
501              sampleRate = 16000.0F; 
502              // Allowable 8000,11025,16000,22050,44100 
503              int sampLength = byteLength / bytesPerSamp; 
504              double lowFreq = 100.0; 
505              double highFreq = 1000.0; 
506   
507              for (int cnt = 0; cnt < sampLength; cnt++) { 
508                  double time = cnt / sampleRate; 
509   
510                  double freq = lowFreq + 
511                          cnt * (highFreq - lowFreq) / sampLength; 
512                  double sinValue = 
513                          Math.sin(2 * Math.PI * freq * time); 
514                  shortBuffer.put((short) (16000 * sinValue)); 
515              }//end for loop 
516          }//end method fmSweep 
517          //-------------------------------------------// 
518   
519          //This method generates a monaural triple- 
520          // frequency pulse that decays in a linear 
521          // fashion with time. 
522          void decayPulse() { 
523              channels = 1;//Java allows 1 or 2 
524              int bytesPerSamp = 2;//Based on channels 
525              sampleRate = 16000.0F; 
526              // Allowable 8000,11025,16000,22050,44100 
527              int sampLength = byteLength / bytesPerSamp; 
528              for (int cnt = 0; cnt < sampLength; cnt++) { 
529                  //The value of scale controls the rate of 
530                  // decay - large scale, fast decay. 
531                  double scale = 2 * cnt; 
532                  if (scale > sampLength) scale = sampLength; 
533                  double gain = 
534                          16000 * (sampLength - scale) / sampLength; 
535                  double time = cnt / sampleRate; 
536                  double freq = 499.0;//an arbitrary freq 
537                  double sinValue = 
538                          (Math.sin(2 * Math.PI * freq * time) + 
539                          Math.sin(2 * Math.PI * (freq / 1.8) * time) + 
540                          Math.sin(2 * Math.PI * (freq / 1.5) * time)) / 3.0; 
541                  shortBuffer.put((short) (gain * sinValue)); 
542              }//end for loop 
543          }//end method decayPulse 
544          //-------------------------------------------// 
545   
546          //This method generates a monaural triple- 
547          // frequency pulse that decays in a linear 
548          // fashion with time.  However, three echoes 
549          // can be heard over time with the amplitude 
550          // of the echoes also decreasing with time. 
551          void echoPulse() { 
552              channels = 1;//Java allows 1 or 2 
553              int bytesPerSamp = 2;//Based on channels 
554              sampleRate = 16000.0F; 
555              // Allowable 8000,11025,16000,22050,44100 
556              int sampLength = byteLength / bytesPerSamp; 
557              int cnt2 = -8000; 
558              int cnt3 = -16000; 
559              int cnt4 = -24000; 
560              for (int cnt1 = 0; cnt1 < sampLength; 
561                   cnt1++, cnt2++, cnt3++, cnt4++) { 
562                  double val = echoPulseHelper(cnt1, sampLength); 
563                  if (cnt2 > 0) { 
564                      val += 0.7 * echoPulseHelper(cnt2, sampLength); 
565                  }//end if 
566                  if (cnt3 > 0) { 
567                      val += 0.49 * echoPulseHelper(cnt3, sampLength); 
568                  }//end if 
569                  if (cnt4 > 0) { 
570                      val += 0.34 * echoPulseHelper(cnt4, sampLength); 
571                  }//end if 
572   
573                  shortBuffer.put((short) val); 
574              }//end for loop 
575          }//end method echoPulse 
576          //-------------------------------------------// 
577   
578          double echoPulseHelper(int cnt, int sampLength) { 
579              //The value of scale controls the rate of 
580              // decay - large scale, fast decay. 
581              double scale = 2 * cnt; 
582              if (scale > sampLength) scale = sampLength; 
583              double gain = 
584                      16000 * (sampLength - scale) / sampLength; 
585              double time = cnt / sampleRate; 
586              double freq = 499.0;//an arbitrary freq 
587              double sinValue = 
588                      (Math.sin(2 * Math.PI * freq * time) + 
589                      Math.sin(2 * Math.PI * (freq / 1.8) * time) + 
590                      Math.sin(2 * Math.PI * (freq / 1.5) * time)) / 3.0; 
591              return (short) (gain * sinValue); 
592          }//end echoPulseHelper 
593   
594          //-------------------------------------------// 
595   
596          //This method generates a monaural triple- 
597          // frequency pulse that decays in a linear 
598          // fashion with time.  However, three echoes 
599          // can be heard over time with the amplitude 
600          // of the echoes also decreasing with time. 
601          //Note that this method is identical to the 
602          // method named echoPulse, except that the 
603          // algebraic sign was switched on the amplitude 
604          // of two of the echoes before adding them to 
605          // the composite synthetic signal.  This 
606          // resulted in a difference in the 
607          // sound. 
608          void waWaPulse() { 
609              channels = 1;//Java allows 1 or 2 
610              int bytesPerSamp = 2;//Based on channels 
611              sampleRate = 16000.0F; 
612              // Allowable 8000,11025,16000,22050,44100 
613              int sampLength = byteLength / bytesPerSamp; 
614              int cnt2 = -8000; 
615              int cnt3 = -16000; 
616              int cnt4 = -24000; 
617              for (int cnt1 = 0; cnt1 < sampLength; 
618                   cnt1++, cnt2++, cnt3++, cnt4++) { 
619                  double val = waWaPulseHelper(cnt1, sampLength); 
620                  if (cnt2 > 0) { 
621                      val += -0.7 * waWaPulseHelper(cnt2, sampLength); 
622                  }//end if 
623                  if (cnt3 > 0) { 
624                      val += 0.49 * waWaPulseHelper(cnt3, sampLength); 
625                  }//end if 
626                  if (cnt4 > 0) { 
627                      val += -0.34 * waWaPulseHelper(cnt4, sampLength); 
628                  }//end if 
629   
630                  shortBuffer.put((short) val); 
631              }//end for loop 
632          }//end method waWaPulse 
633          //-------------------------------------------// 
634   
635          double waWaPulseHelper(int cnt, int sampLength) { 
636              //The value of scale controls the rate of 
637              // decay - large scale, fast decay. 
638              double scale = 2 * cnt; 
639              if (scale > sampLength) scale = sampLength; 
640              double gain = 
641                      16000 * (sampLength - scale) / sampLength; 
642              double time = cnt / sampleRate; 
643              double freq = 499.0;//an arbitrary freq 
644              double sinValue = 
645                      (Math.sin(2 * Math.PI * freq * time) + 
646                      Math.sin(2 * Math.PI * (freq / 1.8) * time) + 
647                      Math.sin(2 * Math.PI * (freq / 1.5) * time)) / 3.0; 
648              return (short) (gain * sinValue); 
649          }//end waWaPulseHelper 
650   
651          //-------------------------------------------// 
652      }//end SynGen class 
653  //=============================================// 
654   
655  }//end outer class AudioSynth01.java 
656