Guitar Tutor Report
(New page: ==Introduction== ===The Domain=== This project is being developed to model the process of jazz improvisation. The end goal is a program that will augment the learning process. It is hop...) |
|||
Line 15: | Line 15: | ||
==Design Introduction== | ==Design Introduction== | ||
− | To model this domain I have split the program into four main parts. The metronome part drives the simulation supplying a beat to play along with. In the future it will also be used to drive a progress indicator in the GUI. The instrument part interfaces with the real instrument, or as in this test just reads from a file. The GUI component drives the domain model in other ways | + | To model this domain I have split the program into four main parts. The metronome part drives the simulation supplying a beat to play along with. In the future it will also be used to drive a progress indicator in the GUI. The instrument part interfaces with the real instrument, or as in this test just reads from a file. The GUI component drives the domain model in other ways directed by the user input. Underneath it all is the domain model that models the structure of the music. |
+ | |||
+ | ==Design Discussion== | ||
+ | |||
+ | [[Image:GuitarTutorDomainDesigh1.jpg|thumb|Fig. 3. Design take one.]][[Image:GuitarTutorDomainDesigh2.jpg|thumb|Fig. 4. Design take two.]][[Image:GuitarTutorDomainDesigh3.jpg|thumb|Fig. 5. Design take three.]][[Image:GuitarTutorDomainDesigh4.jpg|thumb|Fig. 6. Design take four.]][[Image:ChartComponent.jpg|thumb|Fig. 7 class ''ChartComponen'' hierarchy]] | ||
+ | As [[Riel's heuristics|Riel]] states [[A class should not depend on its users|a class should not depend on its users]].Throughout this project I have tried to minimise bidirectional dependencies between classes. One method used here to keep the coupling low between separate parts of the program is to use the observer pattern. Using this decouples the Metronome from the Domain model. It also decouples the Domain model from the GUI | ||
+ | |||
+ | There is a utility class that holds some useful tools for interpreting the file input. This brakes the huristic [[Keep related data and behavior in one place]] but the data really belongs in the domain model and the utilities can not really be spread to where ever they are needed without breaking breaking the maxim [[Don't repeat yourself]] | ||
+ | |||
+ | Both ''TimeSignature'' and ''Tempo'' need to keet current values updated across the whole program. There need to be multiple occurrences of each class within the program to store future updates but the values referenced by the program for calculation need to be the same across the whole program. This is a good place for a modification of the singleton pattern that allows for this sort of behaviour. | ||
+ | |||
+ | The relationship between the abstract class Instrument and the concrete class TestInstrument is a good example of why object level encapsulation can be a much better way of structuring programmes. The superclass provides a framework of sorts for any instrument input to be attached to the system. Any such instrument will have its own driving forces, however in this example the TestInstrument class is just a dummy class that reads a file to create the input. I could just have the class read the file and dump the data into the rest of the program but i want the notes to be spaced out over time. One of the easiest solutions here is to implement the Observer interface and add the instrument as an observer to the metronome object. There are several problems with this solution when using class encapsulation. The superclass does not implement the observer interface. so there is no way of having the addObserver and update methods in this class. There is also no need for the super class to implement this interface just for one temporary and unusual application. If the objects are class encapsulated then the subclass can not have access to the Metronome object to add itself as observer. We could solve this by a more public getter but this supplies access to a more general audience. By approximating object encapsulation by using the protected keyword slightly differently than it is intended we can gain legitimate access to the metronome object from the subclass and add the observer from this subclass. this is also true of other protected members in the superclass that are not intended for public interface but rather to help with implementation. Protected actually means visible to all classes within a package. This allows for far to much coupling between classes. | ||
+ | |||
+ | The class ''ChartComponent'' is a good example of the implementation of message layers, abstract state, and deferred state variables. also by using these techniques I manage avoid repeating myself again. | ||
+ | |||
+ | Initaly in my design fig. 3. I had one ''Note'' class containing a ''Pitch'' class. The note was working out how long it was by using the difference of two system times. This was far from idea. The length of each note had to be updated it the tempo changed. there was no protection from invalid pitch settings. This was a good example of [[Program to the interface not the implementation]] In stage three fig. 5. I separated Note into subclasses for each type of note and created an enum for pitch. This lead to a proliferation of classes. By modelling the ''Note'' class as a note of a duration like this with a pitch i also keded up working against the idea of [[Separation of concerns]]. The class note was trying to encapsulate to many relationships. Finally the length of the note was extracted as an enum and note was agian one class. | ||
+ | |||
+ | The ''ChartComponent'' hierarchy is an interesting fig. 7. part of the class structure it ilustrates the idea behind Riels heuristic 4.6 [[Put semantic constraints in class definition]]. The hierarchy nicely encapsulates the definition above with respect to repeats and chart structure. | ||
+ | |||
+ | One of the key concepts i like to adhere to for all but the most basic classes is [[Tell, don't ask]] now is asking a chart for all the notes in the chart telling or asking. I would say telling. This is one of the things i had to do to interface with the library that i had for drawing notes on the screen. | ||
+ | |||
+ | The class ''NoteFactory'' is in the process of becoming a [[Flyweight]] for the Pitch and Note classes. As the note class is now independent of time there is only a limited number of notes available this is the same for pitches. I could probably apply the same technique to Tempo and Timesignature also but the numbers of these objects present is not high enough to warrant it. |
Latest revision as of 03:59, 1 October 2008
Contents |
Introduction
The Domain
This project is being developed to model the process of jazz improvisation. The end goal is a program that will augment the learning process. It is hoped to achieve this by providing feedback on the notes selected while playing. The structure of music can be very complex. I have started by only dealing with a subset of this structure.
Jazz improvisation
Jazz musition normally improvise with the help of a chart fig. 1. the chart generally has a title an indication of how fast it is meant to be played. Sometime the vocal of main melody is recorded on the chart. When a band plays the chart the will play it once the way it is written to give respect to the composer after that the will play through a number of times to take turns improvising over the piece and then they will play the piece again as written to finish. There are general rules to follow that dictate what notes can be played at any particular time in the piece. These rules can get fairly complex and this has influenced the project. The main problem with applying the rules is that the key that the music is in for a particular section of any chart must be identified. the player will analyse the piece and find points where the key changes. This task can be difficult to do for a person and it turns out that it can in some cases there can be more than one answer. because of this difficulty implementing the process of note validation is a topic for future research.
The structure of music
The structure of music that I have modelled in the domain model form the basic backbone that all notated music is hung on. Each chart has a time signature, this specifies how many notes each bar can hold, a tempo that indicates how fast to play, and a continuous line bars that form the basis of the piece. The line of bars can contain repeats. a repeat is a section of music that is played two or more times. like the section from A to B in fig. 2. The symbol as A is to indicate the start of the repeated section this can occur anywhere in the piece. It is followed by a corresponding end of repeat symbol as can be seen in the middle of the third line fig. 2. The horizontal lines above line three fig. 2. marked 1. and 2. are to indicate alternate endings. When the piece is played the second part is substituted on the second time through. There can be any number of alternate endings and each ending can be played for any number of times. They are labelled by number 1 and 2 as in the example or 1,2 play this part first and second 1..5 means play this on repeat one through five. The endings must be played in the order written and can not be in an arbitrary order. Repeats can not be nested. one repeat can run straight into the beginning of another.
Design Introduction
To model this domain I have split the program into four main parts. The metronome part drives the simulation supplying a beat to play along with. In the future it will also be used to drive a progress indicator in the GUI. The instrument part interfaces with the real instrument, or as in this test just reads from a file. The GUI component drives the domain model in other ways directed by the user input. Underneath it all is the domain model that models the structure of the music.
Design Discussion
As Riel states a class should not depend on its users.Throughout this project I have tried to minimise bidirectional dependencies between classes. One method used here to keep the coupling low between separate parts of the program is to use the observer pattern. Using this decouples the Metronome from the Domain model. It also decouples the Domain model from the GUI
There is a utility class that holds some useful tools for interpreting the file input. This brakes the huristic Keep related data and behavior in one place but the data really belongs in the domain model and the utilities can not really be spread to where ever they are needed without breaking breaking the maxim Don't repeat yourself
Both TimeSignature and Tempo need to keet current values updated across the whole program. There need to be multiple occurrences of each class within the program to store future updates but the values referenced by the program for calculation need to be the same across the whole program. This is a good place for a modification of the singleton pattern that allows for this sort of behaviour.
The relationship between the abstract class Instrument and the concrete class TestInstrument is a good example of why object level encapsulation can be a much better way of structuring programmes. The superclass provides a framework of sorts for any instrument input to be attached to the system. Any such instrument will have its own driving forces, however in this example the TestInstrument class is just a dummy class that reads a file to create the input. I could just have the class read the file and dump the data into the rest of the program but i want the notes to be spaced out over time. One of the easiest solutions here is to implement the Observer interface and add the instrument as an observer to the metronome object. There are several problems with this solution when using class encapsulation. The superclass does not implement the observer interface. so there is no way of having the addObserver and update methods in this class. There is also no need for the super class to implement this interface just for one temporary and unusual application. If the objects are class encapsulated then the subclass can not have access to the Metronome object to add itself as observer. We could solve this by a more public getter but this supplies access to a more general audience. By approximating object encapsulation by using the protected keyword slightly differently than it is intended we can gain legitimate access to the metronome object from the subclass and add the observer from this subclass. this is also true of other protected members in the superclass that are not intended for public interface but rather to help with implementation. Protected actually means visible to all classes within a package. This allows for far to much coupling between classes.
The class ChartComponent is a good example of the implementation of message layers, abstract state, and deferred state variables. also by using these techniques I manage avoid repeating myself again.
Initaly in my design fig. 3. I had one Note class containing a Pitch class. The note was working out how long it was by using the difference of two system times. This was far from idea. The length of each note had to be updated it the tempo changed. there was no protection from invalid pitch settings. This was a good example of Program to the interface not the implementation In stage three fig. 5. I separated Note into subclasses for each type of note and created an enum for pitch. This lead to a proliferation of classes. By modelling the Note class as a note of a duration like this with a pitch i also keded up working against the idea of Separation of concerns. The class note was trying to encapsulate to many relationships. Finally the length of the note was extracted as an enum and note was agian one class.
The ChartComponent hierarchy is an interesting fig. 7. part of the class structure it ilustrates the idea behind Riels heuristic 4.6 Put semantic constraints in class definition. The hierarchy nicely encapsulates the definition above with respect to repeats and chart structure.
One of the key concepts i like to adhere to for all but the most basic classes is Tell, don't ask now is asking a chart for all the notes in the chart telling or asking. I would say telling. This is one of the things i had to do to interface with the library that i had for drawing notes on the screen.
The class NoteFactory is in the process of becoming a Flyweight for the Pitch and Note classes. As the note class is now independent of time there is only a limited number of notes available this is the same for pitches. I could probably apply the same technique to Tempo and Timesignature also but the numbers of these objects present is not high enough to warrant it.