1 / 87

Problem Solving with Data Structures using Java: A Multimedia Approach

Problem Solving with Data Structures using Java: A Multimedia Approach. Chapter 9: Lists and Trees for Structuring Sounds. Chapter Objectives. Story. Structuring sounds into songs (? Collections?) Version 1: Representing linearity through elements order. Repeating and weaving with sounds

abra
Download Presentation

Problem Solving with Data Structures using Java: A Multimedia Approach

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Problem Solving with Data Structures using Java: A Multimedia Approach Chapter 9: Lists and Trees for Structuring Sounds

  2. Chapter Objectives

  3. 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

  4. 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;

  5. Constructing an element /** * Constructor sets next to null * and references the input sound. **/ public SoundElement(Sound aSound){ next = null; mySound = aSound; }

  6. 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; }

  7. Printing a SoundElement list /** * Provide a printable representation of me * @return the information string */ public String toString(){ return "SoundElement with sound: " + mySound + " (and next: " + next + ")."; } Think about it:What is “next”? What method gets called to convert next into a string? What method is this?

  8. Methods for playing /** * The method to get the sound * @return the sound associated with this node */ public Sound getSound(){return mySound;} /** * Play JUST me, blocked. */ public void playSound(){mySound.blockingPlay();} /** * Play JUST me, blocked. */ public void blockingPlay(){mySound.blockingPlay();}

  9. 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());} }

  10. 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-a-h.wav number of samples: 11004 (and next: null).

  11. 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!

  12. Growing the list > e1.setNext(e2) > e1 SoundElement with sound: Sound file: D:/cs1316/mediasources/shh-a-h.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-a-h.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()

  13. e1.playFromMeOn() public void playFromMeOn(){ this.collect().play(); } e1 e2 e3 mySound: shh-a-h.wav next: mySound: croak-h.wav next: mySound: clap-q.wav next: null

  14. public Sound collect(){ if (this.getNext() == null) {return mySound;} e1 e2 e3 mySound: shh-a-h.wav next: mySound: croak-h.wav next: mySound: clap-q.wav next:null this

  15. else {return mySound.append(this.getNext().collect());} e1 e2 e3 mySound: shh-a-h.wav next: mySound: croak-h.wav next: mySound: clap-q.wav next:null We “freeze” this call to e1.collect() this

  16. public Sound collect(){ if (this.getNext() == null) {return mySound;} e2 e3 mySound: croak-h.wav next: mySound: clap-q.wav next:null Where’s e1? What e1? From e2’s view, there is no e1 this

  17. else {return mySound.append(this.getNext().collect());} e2 e3 mySound: croak-h.wav next: mySound: clap-q.wav next:null We “freeze” this call to e2.collect() this

  18. public Sound collect(){ if (this.getNext() == null) {return mySound;} e3 mySound: clap-q.wav next:null We return clap-q.wav this

  19. else {return mySound.append(this.getNext().collect());} e2 e3 Back in e2.collect(), We return croak-h.wav appended with clap-q.wav mySound: croak-h.wav next: mySound: clap-q.wav next:null this

  20. else {return mySound.append(this.getNext().collect());} e1 e2 e3 Back in e2.collect(), We return shh-a-h.wav appended with croak-h.wav appended with clap-q.wav mySound: shh-a-h.wav next: mySound: croak-h.wav next: mySound: clap-q.wav next:null this

  21. e1.playFromMeOn() public void playFromMeOn(){ this.collect().play(); } e1 e2 e3 Now we play shh-a-h.wav appended with croak-h.wav appended with clap-q.wav mySound: shh-a-h.wav next: mySound: croak-h.wav next: mySound: clap-q.wav next: null

  22. 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( "scratch-h.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(); }

  23. Repeating /** * 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 } } Shockingly similar to what we saw before!

  24. Copying a node (for repeat and weave) /** * copyNode returns a copy of this element * @return another element with the same sound */ public SoundElement copyNode() { Sound copySound = new Sound(mySound); SoundElement returnMe = new SoundElement(copySound); return returnMe; }

  25. 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 //Skip skipAmount nodes skipped = 1; while ((current.getNext() != null) && (skipped < skipAmount)) { current = current.getNext(); skipped++; }; 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 } } Again, shockingly similar to what we saw before! Should we break before the last insert (when we get to the end) or after?

  26. 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?

  27. Degong-ing the SoundListTest public void degong(){ Sound gong = new Sound(FileChooser.getMediaPath( "gonga-2.wav")); Sound snap = new Sound(FileChooser.getMediaPath( "snap-tenth.wav")); root.replace(gong,snap); }

  28. 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);} }

  29. Imagine: e1.replace(croak,clap) e1 e2 e3 mySound: shh-a-h.wav next: mySound: croak-h.wav next: mySound: clap-q.wav next: null

  30. public void replace(Sound oldSound, Sound newSound){ if (mySound.getFileName() != null) {if (mySound.getFileName().equals(oldSound.getFileName())) {mySound = newSound;}} e1 e2 e3 mySound: shh-a-h.wav next: mySound: croak-h.wav next: mySound: clap-q.wav next: null this

  31. if (next != null) {next.replace(oldSound,newSound);} e1 e2 e3 mySound: shh-a-h.wav next: mySound: croak-h.wav next: mySound: clap-q.wav next: null this

  32. public void replace(Sound oldSound, Sound newSound){ if (mySound.getFileName() != null) {if (mySound.getFileName().equals(oldSound.getFileName())) {mySound = newSound;}} e2 e3 This is a match! mySound: croak-h.wav clap-q.wav next: mySound: clap-q.wav next: null this

  33. if (next != null) {next.replace(oldSound,newSound);} e2 e3 mySound: croak-h.wav clap-q.wav next: mySound: clap-q.wav next: null this

  34. public void replace(Sound oldSound, Sound newSound){ if (mySound.getFileName() != null) {if (mySound.getFileName().equals(oldSound.getFileName())) {mySound = newSound;}} e3 mySound: clap-q.wav next: null this

  35. if (next != null) {next.replace(oldSound,newSound);} e3 And we’re done! We do return all the way back up again, but there’s nothing else to be done. mySound: clap-q.wav next: null this

  36. 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

  37. 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?

  38. Creating a tree of sounds SoundBranch SoundBranch ScaleBranch SoundBranch Clap, rest, snap Clap,snap,snap Clink,clave, gong Rest, snap, clap Rest, chirp, clap Rest, snap, rest children next

  39. Creating and playing our tree of sounds Welcome to DrJava. > SoundTreeExample tree = new SoundTreeExample(); > tree.play() > tree.playScaled(2.0); > tree.play();

  40. SoundTreeExample public class SoundTreeExample { ScaleBranch scaledBranch; // Needed between methods SoundBranch root; public SoundBranch getRoot() {return root;} /** * Constructor to set up the example */ public SoundTreeExample() { setUp(); }

  41. Setting up the basic sounds public void setUp() { 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"));

  42. 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(rest.append(snap).append(rest)); branch1.addChild(sn);

  43. A ScaleBranch and last Branch scaledBranch = new ScaleBranch(1.0); sn = new SoundNode(clink.append(clave).append(gong)); scaledBranch.addChild(sn); sn = new SoundNode(rest.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(rest.append(snap).append(clap)); branch2.addChild(sn);

  44. Building the whole tree root.addChild(branch1); root.addChild(scaledBranch); root.addChild(branch2); }

  45. Playing the tree, and changing the scale public void play(){ root.playFromMeOn(); } public void playScaled(double factor){ scaledBranch.setFactor(factor); root.playFromMeOn(); }

  46. Method to test the whole tree /** * Method to test the tree */ public static void main(String[] args) { SoundTreeExample tree = new SoundTreeExample(); tree.getRoot().collect().play(); }

  47. What a tree “looks like” (printed, e.g., toString()) > tree SoundTreeExample@92b1a1 > 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) Obviously, this doesn’t help us much, but the root() does

  48. 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.

  49. 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 extendCollectableNode SoundBranch Knows children Knows How add children, and collect all the sounds from its children and next SoundNode Knows mySound Knows How collect itself and its next

More Related