|
A blog where I will write mostly about programming in Cocoa and CoreMIDI, and experiences from my ports of Emacs and XEmacs to the Mac OS.
Friday February 6, 2004
C++ strings and STL containers and iterators provide programmers with the functionality of NSString and Foundation Collections, with higher efficiency and better type safety. Another reason to write in C++, instead of Objective C, is portability to other platforms.
A sample program only half a page long will parse and read chord charts such as this one:
| Fmaj7 | Em7b5 A7 | Dm7 G7 | Cm7 F7 |
| Bbmaj7 | Bbm7 Eb7 | Am7 D7 | Abm7 Db7 |
| Gm7 | C7 | Fmaj7 Dm7 | Gm7 C7 |
Compared to NSString and Foundation collections, C++ strings and STL containers are much nicer to use because theyre more integrated into the language (like playing well with exceptions, automatic destruction when objects go out of scope, etc.). Theyre a little harder to learn. This is probably one of the reasons many Mac programmers prefer Objective C over C++. Then again, I know many people who say they prefer this language and that language over C++, although I dont think they know C++ very well :-).
Thursday February 5, 2004
The representation of chords in MusES, although flexible, isnt particularly user-friendly. A chord such as C7b5 is printed as [C dim5 7], instead of a more customary chord symbol. This is probably no better than printing a chord as its root and list of intervals.
In my C++ implementation, a chord object contains its root note, (string) type, and list of intervals. A singleton class member contains a STL map from chord types to the corresponding interval lists, for all chords known to the library. A chord is constructed by specifying its root and type. In tonality analysis, it will be necessary to determine the type of a chord. The member functions isMajor() and isMinor() can be used to check whether a chord is a major or minor chord. The member function hasInterval() can be used to check whether a chord contains any given interval. Since interval lists are represented by STL vectors, these member functions are all implemented by a single statement.
Herere a sample program for chords and its output.
#include <iostream>
#include "JazzTheoryClasses.h"
int main (int argc, char * const argv[])
{
Chord Abmaj7(Note(A, Flat), "maj7");
std::cout << Abmaj7 << " [" << Abmaj7.notes() << ']' << std::endl;
std::cout << Abmaj7.isMajor() << std::endl;
std::cout << Abmaj7.isMinor() << std::endl;
std::cout << Abmaj7.hasInterval(majorSeventh) << std::endl;
std::cout << Abmaj7.hasInterval(minorSeventh) << std::endl;
return 0;
}
Abmaj7 [Ab C Eb G ]
1
0
1
0
|
Notes and Scales as Classes, not Class Hierarchies
|
Wednesday February 4, 2004
In MusES, notes are instances of the class NaturalNote, SharpNote, DoubleSharpNote, FlatNote, or DoubleFlatNote. The entire class hierarchy for notes consists of these and two abstract classes. Such complexity is unnecessary. Notes of different classes dont behave that differently and can be represented adequately by just a single class, if the arithmetic is performed carefully. This is what I have done in my C++ implementation.
Different scales are also represented by different classes in MusES. The only thing that distinguishes the classes is the scales interval lists. This is again unnecessary. Its interesting to note that intervals and chords are represented by single classes in MusES. This is in fact the suitable choice.
A few better questions to ask in the design of scales and chords might be as follows.
- Should the set of chords and scales that are recognized by the system be expandable? If so, will the code need to be recompiled to add new ones?
- How should a chord or scale type be specified in the program code? Should it be specified by a variable name/enumerated constant (e.g., as
Chord(Note(A), major)) or should an external string name be used (e.g., as Chord(Note(A), "major"))?
- How will scales and chords be used in an application such as tonality analysis?
I have studied these questions today and worked out a design. Ill try to describe it tomorrow.
|
Note/Interval Arithmetic and Scales
|
Tuesday February 3, 2004
Ive implemented some more of my C++ classes for representing notes and intervals. As in MusES the note resulting from adding or subtracting an interval to or from a note has the correct enharmonic spelling. I then added a class for scales. Herere another sample program that demonstrates the new functions and its output. The last scale is not valid according to Pachets definition but it should be easy to add an additional check. Ill work on chords tomorrow but it seems that I need to do some design work. The implementation of chords in MusES is highly flexible but I believe a better defined set of chords will make analysis more manageable.
#include <iostream>
#include "JazzTheoryClasses.h"
int main (int argc, char * const argv[])
{
std::cout << Note(F, Sharp) - diminishedFifth << std::endl;
std::cout << Note(G, Flat) - diminishedFifth << std::endl;
std::cout << Note(F, Sharp) - Note(C) << std::endl;
std::cout << Note(G) - Note(C, Sharp) << std::endl;
std::cout << Scale(Note(A, Flat), Major) << std::endl;
std::cout << Scale(Note(C), HarmonicMinor) << std::endl;
std::cout << Scale(Note(G, Sharp), Major) << std::endl;
}
B#
C
augmentedFourth
diminishedFifth
Ab Major [Ab Bb C Db Eb F G]
C HarmonicMinor [C D Eb F G Ab B]
G# Major [G# A# B# C# D# E# F##]
|
C++ Classes for Jazz Theory
|
Monday February 2, 2004
I read Scott Meyers book Effective STL last weekend. Its a nice book for someone who already knows some STL and wants to gain confidence in using it correctly. For an introduction to STL, try Musser and Sainis STL Tutorial and Reference Guide: C++ Programming with the Standard Template Library.
Ive implemented C++ classes to represent notes (pitch classes) and intervals that behave like their Smalltalk counterparts in MusES as described by Pachet. Herere a sample program that contains some of his examples on notes and intervals and its output.
#include <iostream>
#include "JazzTheoryClasses.h"
int main (int argc, char * const argv[])
{
std::cout << Note(C) << std::endl;
std::cout << Note(C, Sharp) << std::endl;
std::cout << Note(C).sharp().sharp().flat() << std::endl;
try {
std::cout << Note(C).flat().flat().flat() << std::endl;
}
catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
std::cout << Note(C, Sharp).pitchEquals(Note(D, Flat)) << std::endl;
std::cout << Note(C) + diminishedFifth << std::endl;
std::cout << Note(C) + augmentedFourth << std::endl;
std::cout << Note(C) + majorThird + majorThird << std::endl;
std::cout << Note(C, Flat) + minorSeventh << std::endl;
try {
std::cout << Note(C, Flat) + diminishedSeventh << std::endl;
}
catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
}
C
C#
C#
Cannot flat a double flat
1
Gb
F#
G#
Bbb
Adding interval resulted in illegal note
|