Section 9.3. Splicing Sounds


[Page 297 (continued)]

9.3. Splicing Sounds

Splicing sounds is a term that dates back to when sounds were recorded on tape, so juggling the order of things on the tape involved literally cutting the tape into segments and then gluing it back together in the right order. That's "splicing". When everything is digital, it's much easier.

To splice sounds, we simply have to copy elements around in the array. It's easiest to do this with two (or more) arrays, rather than copying within the same array. Splicing lets you create all kinds of sounds, speeches, nonsense, and art.

The easiest kind of splice to do is when the sounds are in separate files. All that you need to do is to copy each sound, in order, into a target sound. You need to keep track of the next index in the target sound. Here's a method that creates the start of a sentence "Guzdial is ..." (readers are welcome to complete the sentence).


[Page 298]

Program 73. Splice Words into a Single Sentence
(This item is displayed on pages 298 - 299 in the print version)

/**  * Method to splice two sounds together with some silence  * between them into the current sound  */ public void splice() {   Sound sound1 =     new Sound(FileChooser.getMediaPath("guzdial.wav"));   Sound sound2 =     new Sound(FileChooser.getMediaPath("is.wav"));   int targetIndex = 0;  // the starting place on the target   int value = 0;   // copy all of sound 1 into the current sound (target)   for (int i = 0;        i < sound1.getLength();        i++, targetIndex++)   {      value = sound1.getSampleValueAt(i);      this.setSampleValueAt(targetIndex,value);   }   //create silence between words by setting values to 0   for (int i = 0;        i < (int) (this.getSamplingRate() * 0.1);        i++, targetIndex++)   {      this.setSampleValueAt(targetIndex,0);   } 
[Page 299]
//copy all of sound 2 into the current sound (target) for (int i = 0; i < sound2.getLength(); i++, targetIndex++) { value = sound2.getSampleValueAt(i); this.setSampleValueAt(targetIndex,value); } }


To test this we need a "blank" sound that the two sounds will be copied to. You can use the file "sec3silence.wav" for this. It holds 3 seconds of silence, which should be more than enough.

> String silence = FileChooser.getMediaPath("sec3silence.wav"); > Sound target = new Sound(silence); > target.play(); > target.splice(); > target.play();


There are three loops in this method splice, each of which copies one segment into the current (target) sounda segment being either a word or a silence between words.

  • The method starts by creating sound objects for the word "Guzdial" (sound1) and the word "is" (sound2).

  • Notice that we set targetIndex (the index for the target sound) equal to 0 before the first loop. We then increment it in every loop, but we never again set it to a specific value. That's because targetIndex is always the index for the next empty sample in the target sound. Because each loop follows the previous one, we just keep tacking samples onto the end of the current sound (the target).

  • In the first loop, we copy each and every sample from sound1 into the current sound this. We have the index i go from 0 to one before the length of sound1. We get the sample value at index i from sound1, then set the sample value at targetIndex in the current sound to that value. We then increment both i and targetIndex.

  • In the second loop, we create 0.1 seconds of silence. Since getSamplingRate() gives us the number of samples in one second of the current sound, 0.1 times that tells us the number of samples in 0.1 seconds. We don't get any source value herewe simply set the targetIndex-th sample to 0 (for silence), then increment the targetIndex.

  • Finally, we copy in all the samples from sound2, just like the first loop where we copied in sound1.

The more common kind of splicing is when the words are in the middle of an existing sound, and you need to pull them out from there. The first thing to do in splicing like that is to figure out the index numbers that delimit the pieces you're interested in. Using the explorer, that's pretty easy to do.


[Page 300]
  • Open an explorer using sound.explore().

  • Click the mouse button to choose a current position and then play the sound before or after that current position.

  • Or, select part of the sound by clicking the mouse when the cursor points to a place to start and dragging the mouse to the end of an area of interest. This selection will highlight and you can play the selection.

Using exactly this process, Mark found the ending points of the first few words in preamble10.wav. (He assumed that the first word starts at the index 0, though that might not always be true for every sound.)

Word

Ending index

We

15,730

the

17,407

People

26,726

of

32,131

the

33,413

United

40,052

States

55,510


Writing a loop that copies things from one array to another requires a little bit of juggling. You need to think about keeping track of two indices: where you are in the array that you're copying from, and where you are in the array that you're copying to. These are two different variables, tracking two different indexes. But they both increment in the same way.

Below is the method that changes the preamble from "We the people of the United States" to "We the UNITED people of the United States."

Program 74. Splice the Preamble to have United People
(This item is displayed on pages 300 - 301 in the print version)

Be sure to set the media path before trying this on your computer.

/**  * Method to splice "We the " then "United" then  * "people of the United States" into the current  * sound  */ public void splicePreamble() {   String file = FileChooser.getMediaPath("preamble10.wav");   Sound source = new Sound(file);   int targetIndex = 0; //start copying to first sample value   int value = 0; 
[Page 301]
// loop copying the "We the " into the current sound for (int sourceIndex = 0; sourceIndex < 17407; sourceIndex++, targetIndex++) { value = source.getSampleValueAt(sourceIndex); this.setSampleValueAt(targetIndex,value); } // loop copying the "united" into the current sound for (int sourceIndex = 33414; sourceIndex < 40052; sourceIndex++,targetIndex++) { value = source.getSampleValueAt(sourceIndex); this.setSampleValueAt(targetIndex,value); } // copy the "people of the United States" for (int sourceIndex = 17408; sourceIndex < 55510; sourceIndex++, targetIndex++) { value = source.getSampleValueAt(sourceIndex); this.setSampleValueAt(targetIndex,value); } }


> String silence = FileChooser.getMediaPath("sec3silence.wav"); > Sound target = new Sound(silence); > target.play(); > target.splicePreamble(); > target.play();


The first loop copies the words "We the" into the current sound. The second loop copies the word "united" into the current sound. The last loop copies the words "people of the United States" into the current sound. Notice that the value of targetIndex is set to 0 at the beginning, so we start copying at the beginning of the current sound. In each loop we increment targetIndex but we never reset its value, so it always points to the next place in the current sound to copy to.

Figure 9.3 shows the original preamble10.wav file in the left sound explorer, and the new spliced one (saved with write(String fileName)) on the right.

Figure 9.3. Comparing the original sound (left) to the spliced sound (right).
(This item is displayed on page 302 in the print version)


Let's see if we can figure out what's going on mathematically. Recall the table back on page 300. First we copy the range from 0 to 17,406 to the target sound. This means we copied 17,407 pixels (17,406 0 + 1 = 17,407). After the first loop, the value of targetIndex will be 17,407. Next we copy the range from 33,414 to 40,051 which means we copy (40,051 33,414 + 1 = 6,638) 6,638 pixels. After the second loop, the value of targetIndex will be 24,045 (17,407 + 6,638 = 24,045). Next we copy the range from 17,408 to 55,509 which is (55,509 17,408 + 1 = 38,102) pixels. The total number of copied pixels is (17,407 + 6,638 + 38,102 = 62,147). The value of targetIndex will be 62,147 after the last loop. You can add System.out.println("Target index is " + targetIndex); after each loop to check that this is correct.


[Page 302]
Program 75. Splice Preamble and Show Target Index
(This item is displayed on pages 302 - 303 in the print version)

/**  * Method to splice "We the " then "United" then  * "people of the United States" into the current  * sound  */ public void splicePreamble() {   String file = FileChooser.getMediaPath("preamble10.wav");   Sound source = new Sound(file);   int targetIndex = 0; //start copying to first sample value   int value = 0;   //loop copying the "We the " into the current sound   for(int sourceIndex = 0;       sourceIndex < 17407;       sourceIndex++, targetIndex++)   {     value = source.getSampleValueAt(sourceIndex);     this.setSampleValueAt(targetIndex,value);   }   //print the value of the target index   System.out.println("Target index is " + targetIndex);   //loop copying the "united" into the current sound   for (int sourceIndex = 33414;        sourceIndex < 40052; 
[Page 303]
sourceIndex++,targetIndex++) { value = source.getSampleValueAt(sourceIndex); this.setSampleValueAt(targetIndex,value); } //print the value of the target index System.out.println("Target index is " + targetIndex); //copy the "people of the United States" for (int sourceIndex = 17408; sourceIndex < 55510; sourceIndex++, targetIndex++) { value = source.getSampleValueAt(sourceIndex); this.setSampleValueAt(targetIndex,value); } //print the value of the target index System.out.println("Target index is " + targetIndex); }


We can also use the explorer to check that the last copied pixel is at 62,146 by checking the value at 62,147. It should still be 0, as should all the values from that index to the end of the sound.

Each of the loops that copies part of the preamble sound into the current sound is very similar. To make a general splice method we will pass in the Sound object to copy from, the starting index to use in that passed sound, the index to stop before in the passed sound, and the place to start the copy to in current sound.

Program 76. General Splice Method
(This item is displayed on pages 303 - 304 in the print version)

/**  * Method to copy part of the passed sound into this sound at  * the given start index  * @param source the source sound to copy from  * @param sourceStart the starting index to copy from in the  * source (the copy will include this)  * @param sourceStop the ending index (the copy won't include  * this)  * @param targetStart the index to start copying into  */ public void splice(Sound source,                    int sourceStart,                    int sourceStop,                    int targetStart) {   // loop copying from source to target   for (int sourceIndex = sourceStart,           targetIndex = targetStart; 
[Page 304]
sourceIndex < sourceStop && targetIndex < this.getLength(); sourceIndex++, targetIndex++) this.setSampleValueAt(targetIndex, source.getSampleValueAt(sourceIndex)); }


This new object method can be used to splice "united" in the phrase "We the people of the United States" as shown below:

Program 77. Using the General Splice Method

/**  * Method to splice the preamble into the current sound so that  * it says We the United people of the United States  */ public void splicePreamble2() {   Sound preamble =     new Sound(FileChooser.getMediaPath("preamble10.wav"));   // first splice the "we the" into the current sound   this.splice(preamble,0,17407,0);   // now splice the "united" into the current sound   this.splice(preamble,33414,40052,17407);   /* now splice the "people of the United States" into    * the current sound    */   this.splice(preamble,17408,55510,24045); }


You can execute this new method using the following:

> String fileName = FileChooser.getMediaPath("sec3silence.wav"); > Sound target = new Sound(fileName); > target.splicePreamble2(); > target.explore();


Compare the sound created using the splicePreamble method versus the sound created using the splicePreamble2 method. They should result in exactly the same sound. Why should we try to write general methods? Take a look at splicePreamble2. It is much easier to read than splicePreamble. We want general methods because they are easier to reuse and make our programs smaller and easier to understand.



Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
Introduction to Computing & Programming Algebra in Java(c) A Multimedia Approach
ISBN: N/A
EAN: N/A
Year: 2007
Pages: 191

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net