structuring-sounds-v2.ppt: uploaded 1 April 2016 at 4:01 pm

Download Report

Transcript structuring-sounds-v2.ppt: uploaded 1 April 2016 at 4:01 pm

Structuring Sounds
CS1316: Representing
Structure and Behavior
Story



Structuring sounds into songs (? Collections?)
•
Version 1: Representing linearity through elements order.
• Repeating and weaving with sounds
• Finding and replacing sounds in a list
How do trace and debug what we’re doing with
sounds and lists?
Structuring sounds into songs
•
Version 2: Creating trees of sounds.
• Traversing in a tree
•
Pre-order and post-order
SoundElement:
Creating a linked list of sounds
/**
* Sounds for a linked list
**/
public class SoundElement {
/**
* The sound this element is associated with
**/
Sound mySound;
/**
* The next element to process
**/
public SoundElement next;
Constructing an element
/**
* Constructor sets next to null
* and references the input sound.
**/
public SoundElement(Sound aSound){
next = null;
mySound = aSound;
}
Linked List blah-blah-blah
/**
* Methods to set and get next elements
* @param nextOne next element in list
**/
public void setNext(SoundElement nextOne){
this.next = nextOne;
}
public SoundElement getNext(){
return this.next;
}
Methods for Playing
/**
* Play the list of sound elements
* after me
**/
public void playFromMeOn(){
this.collect().play();
}
/**
* Collect all the sounds from me on,
* recursively.
**/
public Sound collect(){
if (this.getNext() == null)
{return mySound;}
else
{return mySound.append(this.getNext().collect());}
}
Starting to explore
> Sound s = new Sound("D:/cs1316/mediasources/shh-a-h.wav");
> Sound t = new Sound("D:/cs1316/mediasources/croak-h.wav");
> Sound u = new Sound("D:/cs1316/mediasources/clap-q.wav");
> SoundElement e1 = new SoundElement(s);
> SoundElement e2 = new SoundElement(t);
> SoundElement e3 = new SoundElement(u);
> e1.playFromMeOn();
> e1
SoundElement with sound: Sound file: D:/cs1316/mediasources/shh-ah.wav number of samples: 11004 (and next: null).
How did that happen?



How were we able to get that string from
typing in e1?
By defining the method toString() on a
class, we can define how it should
appear.
VERY useful in debugging!
toString() for SoundElement
/**
* Provide a printable representation of
me
**/
public String toString(){
return "SoundElement with sound:
"+mySound+" (and next: "+next+").";
}
Growing the list
> e1.setNext(e2)
> e1
SoundElement with sound: Sound file: D:/cs1316/mediasources/shh-ah.wav number of samples: 11004 (and next: SoundElement with
sound: Sound file: D:/cs1316/mediasources/croak-h.wav number of
samples: 8808 (and next: null).).
> e2.setNext(e3)
> e1
SoundElement with sound: Sound file: D:/cs1316/mediasources/shh-ah.wav number of samples: 11004 (and next: SoundElement with
sound: Sound file: D:/cs1316/mediasources/croak-h.wav number of
samples: 8808 (and next: SoundElement with sound: Sound file:
D:/cs1316/mediasources/clap-q.wav number of samples: 4584 (and
next: null).).).
Where’s all that coming
from?!?
When we add in next to
our toString(), it calls
next’s toString()
e1.playFromMeOn()
e1
e2
e3
mySound:
shh-ah.wav
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:
next: null
public void
playFromMeOn(){
this.collect().play();
}
public Sound collect(){
if (this.getNext() == null)
{return mySound;}
e1
e2
e3
mySound:
shh-ah.wav
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:
next:null
this
else
{return
mySound.append(this.getNext().collect());}
e1
e2
e3
mySound:
shh-ah.wav
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:
next:null
this
We “freeze” this
call to e1.collect()
public Sound collect(){
if (this.getNext() == null)
{return mySound;}
e2
e3
Where’s e1?
What e1?
mySound:
croakh.wav
mySound:
clapq.wav
From e2’s view,
there is no e1
next:
next:null
this
else
{return
mySound.append(this.getNext().collect());}
e2
e3
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:null
this
We “freeze” this
call to e2.collect()
public Sound collect(){
if (this.getNext() == null)
{return mySound;}
e3
mySound:
clapq.wav
next:null
this
We return clap-q.wav
else
{return
mySound.append(this.getNext().collect());}
e2
e3
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:null
this
Back in
e2.collect(),
We return croakh.wav appended
with clap-q.wav
else
{return
mySound.append(this.getNext().collect());}
e1
e2
e3
mySound:
shh-ah.wav
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:
next:null
this
Back in
e2.collect(),
We return shh-ah.wav appended
with croak-h.wav
appended with
clap-q.wav
e1.playFromMeOn()
e1
e2
public void
playFromMeOn(){
this.collect().play();
}
e3
mySound:
shh-ah.wav
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:
next: null
Now we play shha-h.wav
appended with
croak-h.wav
appended with
clap-q.wav
Testing
Sound
Lists
public class SoundListTest {
SoundElement root;
public SoundElement root(){ return root;}
public void setUp(){
FileChooser.setMediaPath("D:/cs1316/MediaSources/");
Sound s = new Sound(FileChooser.getMediaPath( "scratchh.wav"));
root = new SoundElement(s);
s = new Sound(FileChooser.getMediaPath( "gonga-2.wav"));
SoundElement one = new SoundElement(s);
root.repeatNext(one,10);
s = new Sound(FileChooser.getMediaPath( "scritch-q.wav"));
SoundElement two = new SoundElement(s);
root.weave(two,3,3);
s = new Sound(FileChooser.getMediaPath( "clap-q.wav"));
SoundElement three = new SoundElement(s);
root.weave(three,5,2);
root.playFromMeOn();
}
Repeating
Shockingly
similar to
what we saw
before!
/**
* Repeat the input phrase for the number of times
specified.
* It always appends to the current node, NOT
insert.
* @param nextOne node to be copied in to list
* @param count number of times to copy it in.
*/
public void repeatNext(SoundElement nextOne,int
count) {
SoundElement current = this; // Start from here
SoundElement copy; // Where we keep the current
copy
for (int i=1; i <= count; i++)
{
copy = nextOne.copyNode(); // Make a copy
current.setNext(copy); // Set as next
current = copy; // Now append to copy
}
}
Copying by filename
/**
* copyNode returns a copy of this element
* @return another element with the same sound
*/
public SoundElement copyNode(){
Sound copySound;
if (this.mySound.getFileName() == null)
{copySound = this.mySound.scale(1.0);} // Does nothing -- copies
else
{copySound = new Sound(this.mySound.getFileName());} // Copy from
file
SoundElement returnMe = new SoundElement(copySound);
return returnMe;
}
Weaving
/**
* Weave the input sound count times every skipAmount elements
* @param nextOne SoundElement to be copied into the list
* @param count how many times to copy
* @param skipAmount how many nodes to skip per weave
*/
public void weave(SoundElement nextOne, int count, int skipAmount)
{
SoundElement current = this; // Start from here
SoundElement copy; // Where we keep the one to be weaved in
SoundElement oldNext; // Need this to insert properly
int skipped; // Number skipped currently
for (int i=1; i <= count; i++)
{
copy = nextOne.copyNode(); // Make a copy
Again,
shockingly
similar to
what we saw
before!
//Skip skipAmount nodes
skipped = 1;
while ((current.getNext() != null) && (skipped < skipAmount))
{
current = current.getNext();
Should we
skipped++;
};
break before
oldNext = current.getNext(); // Save its next
current.insertAfter(copy); // Insert the copy after this one
current = oldNext; // Continue on with the rest
if (current == null) // Did we actually get to the end early?
break; // Leave the loop
}
}
the last
insert (when
we get to the
end) or
after?
What? You don’t like gongs?


So, what happens when you have
umpteen copies of gong, but you don’t
like gongs?
Yes, we can remake the list, but what if
you couldn’t?
• Imagine a huge data structure that was too
•
expensive (time-consuming) to recreate.
How could you fix it in place?
Degong-ing the SoundListTest
public void degong(){
Sound gong = new
Sound(FileChooser.getMediaPath( "gonga2.wav"));
Sound snap = new
Sound(FileChooser.getMediaPath( "snaptenth.wav"));
root.replace(gong,snap);
}
Replacing one sound by another
in the list (recursively)
/**
* Replace the one sound with the other sound
* in all the elements from me on.
* Decide two sounds are equal if come from same filename
* @param oldSound sound to be replaced
* @param newSOund sound to put in its place
**/
public void replace(Sound oldSound, Sound newSound){
if (mySound.getFileName() != null)
{if (mySound.getFileName().equals(oldSound.getFileName()))
{mySound = newSound;}}
if (next != null)
{next.replace(oldSound,newSound);}
}
Imagine: e1.replace(croak,clap)
e1
e2
e3
mySound:
shh-ah.wav
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:
next: null
public void replace(Sound oldSound, Sound newSound){
if (mySound.getFileName() != null)
{if (mySound.getFileName().equals(oldSound.getFileName()))
{mySound = newSound;}}
e1
e2
e3
mySound:
shh-ah.wav
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:
next: null
this
if (next != null)
{next.replace(oldSound,newSound);}
e1
e2
e3
mySound:
shh-ah.wav
mySound:
croakh.wav
mySound:
clapq.wav
next:
next:
next: null
this
public void replace(Sound oldSound, Sound newSound){
if (mySound.getFileName() != null)
{if (mySound.getFileName().equals(oldSound.getFileName()))
{mySound = newSound;}}
e2
This is a
match!
e3
mySound:
croakh.wav
mySound:
clapq.wav
clapq.wav
next: null
next:
this
if (next != null)
{next.replace(oldSound,newSound);}
e2
e3
mySound:
croakh.wav
mySound:
clapq.wav
clapq.wav
next: null
next:
this
public void replace(Sound oldSound, Sound newSound){
if (mySound.getFileName() != null)
{if (mySound.getFileName().equals(oldSound.getFileName()))
{mySound = newSound;}}
e3
mySound:
clapq.wav
next: null
this
if (next != null)
{next.replace(oldSound,newSound);}
e3
mySound:
clapq.wav
next: null
this
And we’re done!
We do return all the
way back up again,
but there’s nothing
else to be done.
Tracing what’s happening



Use “Debug Mode” from Debugger menu
Type in variable names to trace.
Set Breakpoints (Control-B) on the lines
you want to stop in.
• Step over—skips to the next line
• Step in—skips in to that method call
• Resume—go to the next breakpoint
Version 2: When lists aren’t
good enough

Do we think about music as a linear list of
sounds?
•


That is how we experience it.
Or do we tend to cluster the sounds?
•
•
Verse, chorus, verse?
This motif, that motif, this motif?
And what about this cool idea of embedding
operations into the data structure?
Creating a tree of sounds
SoundBranch
SoundBranch
Clap,
rest,
snap
Aah, snap,
rest
children
next
ScaleBranch
Clink,clave,
gong
Is, chirp,
clap
SoundBranch
Clap,snap,snap
Bassoon, snap,
clap
Creating and playing our tree of
sounds
Welcome to DrJava.
> SoundTreeExample tree = new
SoundTreeExample(); tree.setUp();
> tree.play()
> tree.playScaled(2.0);
> tree.play();
SoundTreeExample
public class SoundTreeExample {
ScaleBranch scaledBranch; // Needed
between methods
SoundBranch root;
public SoundBranch root() {return root;}
Setting up the basic sounds
public void setUp() {
FileChooser.setMediaPath("D:/cs1316/MediaSources/");
Sound clap = new Sound(FileChooser.getMediaPath("clap-q.wav"));
Sound chirp = new Sound(FileChooser.getMediaPath("chirp-2.wav"));
Sound rest = new Sound(FileChooser.getMediaPath("rest-1.wav"));
Sound snap = new Sound(FileChooser.getMediaPath("snap-tenth.wav"));
Sound clink = new Sound(FileChooser.getMediaPath("clink-tenth.wav"));
Sound clave = new Sound(FileChooser.getMediaPath("clave-twentieth.wav"));
Sound gong = new Sound(FileChooser.getMediaPath("gongb-2.wav"));
Sound bassoon = new Sound(FileChooser.getMediaPath("bassoon-c4.wav"));
Sound is = new Sound(FileChooser.getMediaPath("is.wav"));
Sound aah = new Sound(FileChooser.getMediaPath("aah.wav"));
Build the root and first branch
root = new SoundBranch();
SoundNode sn;
SoundBranch branch1 = new SoundBranch();
sn = new
SoundNode(clap.append(rest).append(snap));
branch1.addChild(sn);
sn = new
SoundNode(aah.append(snap).append(rest));
branch1.addChild(sn);
A ScaleBranch and last Branch
scaledBranch = new ScaleBranch(1.0);
sn = new SoundNode(clink.append(clave).append(gong));
scaledBranch.addChild(sn);
sn = new SoundNode(is.append(chirp).append(clap));
scaledBranch.addChild(sn);
SoundBranch branch2 = new SoundBranch();
sn = new SoundNode(clap.append(snap).append(snap));
branch2.addChild(sn);
sn = new
SoundNode(bassoon.append(snap).append(clap));
branch2.addChild(sn);
Building the whole tree
root.addChild(branch1);
root.addChild(scaledBranch);
root.addChild(branch2);
}
Playing the tree,
and changing the scale
public void play(){
root.playFromMeOn();
}
public void playScaled(double factor){
scaledBranch.setFactor(factor);
root.playFromMeOn();
}
What a tree “looks like” (printed,
e.g., toString())
> tree
Obviously, this doesn’t
SoundTreeExample@92b1a1 help us much, but the
root() does
> tree.root()
SoundBranch (with child: SoundBranch (with child: SoundNode (with
sound: Sound number of samples: 28568 and next: SoundNode (with
sound: Sound number of samples: 46034 and next: null and next:
ScaleBranch (1.0) SoundBranch (with child: SoundNode (with sound:
Sound number of samples: 47392 and next: SoundNode (with sound:
Sound number of samples: 32126 and next: null and next:
SoundBranch (with child: SoundNode (with sound: Sound number of
samples: 8452 and next: SoundNode (with sound: Sound number of
samples: 28568 and next: null and next: No next))) and next: No next)
Going deeper:
How’d we do that?

How we build a tree of sounds is very much
like a tree of pictures.
•
•
•
•
Set up a general node abstract class that all other
nodes inherit from.
Create a leaf node that will store our data of interest
(sounds).
Create a branch node that will store collections of
leaves and references to other branches.
Create (as we wish) branch nodes with operations that
do things to the children of this branch.
Our SoundTree Class Structure
abstract CollectableNode
Knows next
Knows How to do basic
list operations, and
defines abstract sound
operations (can collect()
its sound(s))
The subclasses
extend
CollectableNode
SoundBranch
SoundNode
Knows children
Knows mySound
Knows How add children,
and collect all the sounds
from its children and next
Knows How collect itself
and its next
Our SoundTree Class Structure
(a little further)
abstract CollectableNode
Knows next
Knows How to do basic list
operations, and collect()
SoundBranch
SoundNode
Knows children
Knows How add
children, and collect()
Knows mySound
Knows How collect()
ScaleBranch
Knows a scaling factor
Knows How to access scaling factor,
and to collect from children then scale
the resultant sound
CollectableNode
/**
* Stuff that all nodes and branches in the
* sound tree know.
**/
abstract public class CollectableNode {
/**
* The next branch/node/whatever to process
**/
public CollectableNode next;
/**
* Constructor for CollectableNode just sets
* next to null
**/
public CollectableNode(){
next = null;
}
CollectableNode’s know about
linked lists
/**
* Methods to set and get next elements
* @param nextOne next element in list
**/
public void setNext(CollectableNode nextOne){
this.next = nextOne;
The rest of it is
}
public CollectableNode getNext(){
return this.next;
}
there, too: add(),
last(),
insertAfter(),
remove()…
But you’ve seen
that all before.
CollectableNodes know only a
little about sound
/**
* Play the list of sound elements
* after me
**/
public void playFromMeOn(){
this.collect().play();
}
/**
* Collect all the sounds from me on
**/
abstract public Sound collect();
/**
* Collect all the sounds from me on,
* but if there's processing, do it after.
**/
abstract public Sound collectAfter();
SoundNodes know sounds
/*
* SoundNode is a class representing a drawn picture
* node in a scene tree.
**/
public class SoundNode extends CollectableNode {
/**
* The sound I'm associated with
**/
Sound mySound;
/*
* Make me with this sound
* @param sound the Sound I'm associated with
**/
public SoundNode(Sound sound){
super(); // Call superclass constructor
mySound = sound;
}
What happens if you print a
SoundNode
/**
* Method to return a string with informaiton
* about this node
*/
public String toString()
{
return "SoundNode (with sound: "+mySound+"
and next: "+next;
}
How SoundNodes collect their
sounds
/**
* Collect all the sounds from me on,
“Hang on, how
does that
* recursively.
work?!?”
**/
Wait until we see
public Sound collect(){
the rest, then we’ll
if (this.getNext() == null)
trace it.
{return mySound;}
else
{return mySound.append(this.getNext().collect());}
}
SoundBranches know about
children
public class SoundBranch extends CollectableNode {
/*
* A list of children to draw
Notice: Children
*/
could be
public CollectableNode children;
SoundNodes or
other branches—
/*
any kind of
* Construct a branch with children and
CollectableNode
* next as null
**/
public SoundBranch(){
super(); // Call superclass constructor
children = null;
}
SoundBranches know about
children (contd.)
/**
* Method to add nodes to children
**/
public void addChild(CollectableNode child){
if (children != null)
{children.add(child);}
else
{children = child;}
}
How SoundBranches print
/**
* Method to return a string with informaiton
* about this branch
*/
public String toString()
{
String childString = "No children", nextString="No next";
if (children != null)
{childString = children.toString();}
if (next != null)
{nextString = next.toString();}
return "SoundBranch (with child: "+childString+" and next: "+
nextString+")";
}
Collecting sounds from
SoundBranches
/**
* Collect all the sound from our children,
* then collect from next.
* @param pen Turtle to draw with
**/
public Sound collect(){
Sound childSound;
if (children != null)
{childSound = children.collect();}
else
{childSound = new Sound(1);}
// Collect from my next
if (this.getNext() != null)
{childSound=childSound.append(this.getNext().collect());}
return childSound;
}
This collects
from the
children (or
creates a
default sound),
then from the
next’s, and
returns them
all together
ScaleBranches know about
scaling (and a factor)
public class ScaleBranch extends SoundBranch {
/** Amount to scale **/
double factor;
/**
* Construct a branch with this factor
**/
public ScaleBranch(double nufactor){
super(); // Call superclass constructor
this.factor = nufactor;
}
/** Accessors **/
public double getFactor() {return this.factor;}
public void setFactor(double nufactor) {this.factor = nufactor;}
Scaling the children in a
ScaleBranch
/**
* Collect all the sound from our children,
* then collect from next. Scale the children
* @param pen Turtle to draw with
**/
public Sound collect(){
Sound childSound;
if (children != null)
{childSound = children.collect().scale(factor);}
else
{childSound = new Sound(1);}
// Collect from my next
if (this.getNext() != null)
{Sound nextSound=this.getNext().collect();
childSound = childSound.append(nextSound);}
return childSound;
}
Printing a ScaleBranch
/**
* Method to return a string with informaiton
* about this branch
*/
public String toString()
{
return "ScaleBranch ("+factor+")
"+super.toString();
}
Walking the data structure:
At the root
> tree.root()
SoundBranch (with child: SoundBranch (with child:
SoundNode (with sound: Sound number of samples:
28568 and next: SoundNode (with sound: Sound number
of samples: 46034 and next: null and next: ScaleBranch
(1.0) SoundBranch (with child: SoundNode (with sound:
Sound number of samples: 47392 and next: SoundNode
(with sound: Sound number of samples: 32126 and next:
null and next: SoundBranch (with child: SoundNode (with
sound: Sound number of samples: 8452 and next:
SoundNode (with sound: Sound number of samples:
28568 and next: null and next: No next))) and next: No
next)
Down the children’s next path
> tree.root().children
SoundBranch (with child: SoundNode (with sound: Sound number of samples: 28568
and next: SoundNode (with sound: Sound number of samples: 46034 and next: null
and next: ScaleBranch (1.0) SoundBranch (with child: SoundNode (with sound:
Sound number of samples: 47392 and next: SoundNode (with sound: Sound number
of samples: 32126 and next: null and next: SoundBranch (with child: SoundNode
(with sound: Sound number of samples: 8452 and next: SoundNode (with sound:
Sound number of samples: 28568 and next: null and next: No next)))
> tree.root().children.getNext()
ScaleBranch (1.0) SoundBranch (with child: SoundNode (with sound: Sound number of
samples: 47392 and next: SoundNode (with sound: Sound number of samples:
32126 and next: null and next: SoundBranch (with child: SoundNode (with sound:
Sound number of samples: 8452 and next: SoundNode (with sound: Sound number
of samples: 28568 and next: null and next: No next))
> tree.root().children.getNext().getNext()
SoundBranch (with child: SoundNode (with sound: Sound number of samples: 8452 and
next: SoundNode (with sound: Sound number of samples: 28568 and next: null and
next: No next)
> tree.root().children.getNext().getNext().getNext()
null
Tracing a tree traversal
Welcome to DrJava.
> SoundTreeExample tree =
new
SoundTreeExample();
tree.setUp();
> tree.root().collect().play()
This code:
(1) Creates a tree object.
(2) Fills it up.
(3) Then gets the root, collects
the sound from the whole
tree (traverses the tree),
then plays the collected
sound.
Tracing the traversal:
tree.root().collect().play
SoundBranch
tree.root()
SoundBranch
Clap,
rest,
snap
Aah, snap,
rest
children
next
ScaleBranch
Clink,clave,
gong
Is, chirp,
clap
SoundBranch
Clap,snap,snap
Bassoon, snap,
clap
public Sound collect(){
Sound childSound;
if (children != null)
{childSound = children.collect();}
else
{childSound = new Sound(1);}
SoundBranch
this
SoundBranch
Clap,
rest,
snap
Aah, snap,
rest
ScaleBranch
SoundBranch
Clink,clave,
gong
Is, chirp,
clap
Clap,snap,snap
Bassoon, snap,
clap
Yes, the root has children, so
we ask the first of our children
to collect()
public Sound collect(){
Sound childSound;
if (children != null)
{childSound = children.collect();}
else
{childSound = new Sound(1);}
SoundBranch
SoundBranch
this
Clap,
rest,
snap
Aah, snap,
rest
The call to root().collect() is
now paused…
ScaleBranch
SoundBranch
Clink,clave,
gong
Is, chirp,
clap
Clap,snap,snap
Bassoon, snap,
clap
Yes, we’re in the same
method, but now with a new
this. It’s really a new call.
public Sound collect(){
if (this.getNext() == null)
{return mySound;}
else
{return
mySound.append(this.getNext().collect());}
}
Now, we’re in SoundNode’s
collect()
SoundBranch
SoundBranch
ScaleBranch
Clap,
rest,
snap
Clink,clave,
gong
this
Aah, snap,
rest
The call to root().children.collect()
is now paused…
Is, chirp,
clap
SoundBranch
Clap,snap,snap
Bassoon, snap,
clap
We have a next, so we collect
from there, too.
public Sound collect(){
if (this.getNext() == null)
{return mySound;}
else
{return
mySound.append(this.getNext().collect());}
}
We’re in SoundNode’s
collect(), with a new
SoundNode
SoundBranch
SoundBranch
ScaleBranch
Clap,
rest,
snap
this
Aah, snap,
rest
The call to
root().children.children.collect() is
now paused…
Clink,clave,
gong
Is, chirp,
clap
SoundBranch
Clap,snap,snap
Bassoon, snap,
clap
We have no next here, so we
return “Aah, snap, rest”
We’re back in SoundNode’s
collect()
SoundBranch
SoundBranch
Clap,
rest,
snap
public Sound collect(){
if (this.getNext() == null)
{return mySound;}
else
{return
mySound.append(this.getNext().collect());}
}
ScaleBranch
SoundBranch
Clink,clave,
gong
this
Aah, snap,
rest
Is, chirp,
clap
Clap,snap,snap
Bassoon, snap,
clap
We now return “Clap, rest,
snap” appended with “aah,
snap, rest”
We’re now in the second half
of SoundBranch’s collect()
// Collect from my next
if (this.getNext() != null)
{Sound nextSound=this.getNext().collect();
childSound = childSound.append(nextSound);}
}
return childSound;
SoundBranch
SoundBranch
this
Clap,
rest,
snap
Aah, snap,
rest
ScaleBranch
SoundBranch
Clink,clave,
gong
Is, chirp,
clap
Clap,snap,snap
Bassoon, snap,
clap
We have a next, so we move
on to call collect() on that.
Now we’re in ScaleBranch’s
collect()
public Sound collect(){
Sound childSound;
if (children != null)
{childSound =
children.collect().scale(factor);}
else
{childSound = new Sound(1);}
SoundBranch
this
SoundBranch
Clap,
rest,
snap
Aah, snap,
rest
ScaleBranch
SoundBranch
Clink,clave,
gong
Is, chirp,
clap
Clap,snap,snap
Bassoon, snap,
clap
Let’s skip the path down the
children. childSound will become
“Clink,clave,gong,is,chirp,clap”
scaled by the factor
Second half of ScaleBranch—
we get the sounds from next
// Collect from my next
if (this.getNext() != null)
{Sound
nextSound=this.getNext().collect();
childSound =
childSound.append(nextSound);}
return childSound;
}
SoundBranch
this
SoundBranch
Clap,
rest,
snap
Aah, snap,
rest
ScaleBranch
Clink,clave,
gong
Is, chirp,
clap
SoundBranch
Clap,snap,snap
Bassoon, snap,
clap
We’re in the first half of
SoundBranch
public Sound collect(){
Sound childSound;
if (children != null)
{childSound = children.collect();}
else
{childSound = new Sound(1);}
SoundBranch
this
SoundBranch
Clap,
rest,
snap
Aah, snap,
rest
ScaleBranch
Clink,clave,
gong
Is, chirp,
clap
SoundBranch
Clap,snap,snap
Bassoon, snap,
clap
childSound will
become “Clap, snap,
snap, bassoon, snap,
clap”
We’re in the second half of
SoundBranch
// Collect from my next
if (this.getNext() != null)
{Sound
nextSound=this.getNext().collect();
childSound =
childSound.append(nextSound);}
return childSound;
}
SoundBranch
this
SoundBranch
Clap,
rest,
snap
Aah, snap,
rest
ScaleBranch
Clink,clave,
gong
Is, chirp,
clap
SoundBranch
Clap,snap,snap
Bassoon, snap,
clap
There is no next so we
return “Clap, snap,
snap, bassoon, snap,
clap”
// Collect from my next
if (this.getNext() != null)
{Sound
nextSound=this.getNext().collect();
childSound =
childSound.append(nextSound);}
return childSound;
}
Back to second half of
ScaleBranch
SoundBranch
this
SoundBranch
ScaleBranch
Clap,
rest,
snap
aah, snap,
rest
getNext().collect() returned “Clap,
snap, snap, bassoon, snap, clap”
Clink,clave,
gong
is, chirp,
clap
SoundBranch
Clap,snap,snap
bassoon, snap,
clap
This returns
“Clink,clave,gong,is,chirp,clap” scaled
by the factor, plus “Clap, snap, snap,
bassoon, snap, clap”
Back in the second half of
SoundBranch’s collect()
// Collect from my next
if (this.getNext() != null)
{Sound nextSound=this.getNext().collect();
childSound = childSound.append(nextSound);}
}
return childSound;
SoundBranch
SoundBranch
this
Clap,
rest,
snap
ScaleBranch
Clink,clave,
gong
SoundBranch
Clap,snap,snap
Bassoon, snap,
clap
Aah, snap,
rest
Is, chirp,
clap
This returns
“Clap,rest,snap,aah,snap,rest”, plus
“Clink,clave,gong,is,chirp,clap”
scaled by the factor, plus “Clap,
snap, snap, bassoon, snap, clap”
// Collect from my next
if (this.getNext() != null)
{Sound nextSound=this.getNext().collect();
childSound = childSound.append(nextSound);}
Now we’re back to
the second half of
this. There is no
next
SoundBranch
}
return childSound;
this
SoundBranch
Clap,
rest,
snap
Aah, snap,
rest
ScaleBranch
Clink,clave,
gong
Is, chirp,
clap
SoundBranch
Clap,snap,snap
Bassoon, snap,
clap
FINAL RETURN:
“Clap,rest,snap,aah,snap,rest”, plus
“Clink,clave,gong,is,chirp,clap” scaled
by the factor, plus “Clap, snap, snap,
bassoon, snap, clap”
That was one kind of tree
traversal

There are other ways of thinking about
traversing a tree!
•
•


Go down the next before going to the children?
Maybe do the scaling to the next instead of to the
children?
What we’ve been doing (children-branchnext) is called in-order traversal.
There are other kinds, like pre-order traversal
and post-order traversal.
SoundBranch’s collectAfter()
/**
* Collect all the sound from our children,
* then collect from next.
* If there's processing, do to Next, not to Children
* @param pen Turtle to draw with
**/
public Sound collectAfter(){
Sound childSound;
if (children != null)
{childSound = children.collectAfter();}
else
{childSound = new Sound(1);}
// Collect from my next
if (this.getNext() != null)
{childSound=childSound.append(this.getNext().collectAfter());}
return childSound;
}
ScaleBranch’s collectAfter()
/**
* Collect all the sound from our children,
* then collect from next.
* Scale the next list, not the children
* @param pen Turtle to draw with
**/
public Sound collectAfter(){
Sound childSound;
if (children != null)
{childSound = children.collect();}
else
{childSound = new Sound(1);}
Notice that we’re
scaling what
getNext().collect()
returns, not what
children.collect()
returns
// Collect from my next
if (this.getNext() != null)
{Sound nextSound=this.getNext().collect();
childSound = childSound.append(nextSound.scale(factor));}
return childSound;
}
Trying this in SoundTreeList
public void playAfter(){
root.collectAfter().play();
}
> tree.playAfter()
Where else could we go with
this?



How about a kind of SoundBranch that knows
how to repeat its children a certain number of
time?
How about a kind of SoundBranch that can mix
its children with the next, with a certain
percentage of each?
Back with pictures:
•
•
How about a kind of branch that can rotate its children?
Or scale them, so you could zoom in or out, or
grow/shrink the monster, across frames?
Arrays, and Lists, and Trees,
oh my!

Where we’re going next:
•
•
•

We’ve explored lists and trees in a good deal of depth
now!
You obviously saw lots of similarities between these
implementations.
“Couldn’t we do this once, instead of duplicating all
this code?!?” YES! Let me show you…
“Man, what I really want to do is user interfaces
with Java—real applications! What does this all
have to do with that?!?”
•
A user interface is a tree. Let me show you…