|
About Andrew Choi
MIDI Programs
MIDI File Player (External Device)
MIDI Destination Pop-Up Button
MIDI File Player (Internal Synth)
MusicSequence Sample Code
MIDI File Writer
MIDI Name Document Parser
NameConfigSetup
Fish Creek MIDI Framework
MidnamUtility
SysExSenderX
Other Programs
FCBlogEditor
FCBlog and Patch
Chinese Checkers Program
jyut6 ping3 Cantonese Input Method
Cocoa Sample Programs
Syntax Coloring Using Flex
NSTextField and Undo
NSToolbar
Implementing File Import
Launch Application and Open URL
Saving Uncommitted Text Field Edits
Algorithms
Jazz Chord Analysis as Optimization
Optimal Line Breaking for Music
Optimal Chord Spacing
|
|
|
|
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.
|
Text Format Chord Chart Input/Output
|
Friday May 7, 2004
Converted the C++ code I wrote earlier for reading and writing chord charts in text format into Objective C/Cocoa. At some point, Ill need to integrate the C++ jazz theory classes and accompaniment generation algorithms with the Objective C/Cocoa GUI code. But Id like file I/O to be done in Objective C/Cocoa. The BiaB file reader will be converted next.
Its great that I can now read (and edit!) all the files I created before. Heres an example.
Herere the first few lines of the corresponding text file.
Title: The Christmas Song
Meter: 4/4
Key: Eb
Tempo: 120
| Eb6 Bb7 | Eb6 _ Fm7 Bb7 | Eb6 _ Bbm7 Eb9 | Ab G7#5 |
| Cm7 Abm6 | Eb _ Am7 D7 | GMaj7 _ Abm7 Db9 | GbMaj7 _ Fm7 Bb7 |
| Eb6 Bb7 | Eb6 _ Fm7 Bb7 | Eb6 _ Bbm7 Eb9 | Ab G7#5 |
| Cm7 Abm6 | Eb _ Am7 D7 | Gm7 C7 Fm7 Bb7 | Eb6 |
| Bbm7 Eb9 | Bbm7 _ Eb9 Eb7 | Bbm7 Eb9 | AbMaj7 |
| Abm7 Db9 | GbMaj7 | Cm7 F7 | Fm7 Bb7b9 |
...
Thursday May 6, 2004
Added code to my NSFormatter subclass to handle slash chords. Heres the new code in action for the beginning of a tune in 3/4 time. The code has been written to handle different time signatures all along. But this is the first time Ive tried it.
As I was playing with my new program, I noticed a subtlety in the implementation of formatters. Since they validate the users input as he types, a common model of formatters is to have them enfore that at any given time, the partial string in the text field is a prefix of some valid chord. The problem with this model is when the user notices a mistake earlier on in the string, he cant correct it by going back and deleting a portion of the string. For example, if the user has entered EMaj7 when he has meant to enter FMaj7, he cant move the cursor back before Maj7 and hit delete, since the partial string Maj7 is not a valid prefix. He can however select the letter E using the mouse and type F to replace it. One method that will correctly validate such a string is to match the regular expression .*Maj7 with all possible chords, where .* is the location of the insertion point. If the user is allowed to go back to correct an earlier mistake without completing the chord (for example after he has typed EMa, for example), we can try to match the regular expression .*Ma.* with all possible chords instead. This can get a little complicated. I think Ill stick with the simpler method of testing prefixes for now.
While were demonstrating the use of the chord editor for tunes with odd time signatures, how can we not show one of Take Five? Herere its first few bars. Lets see BiaB or MiBAC Jazz do that :-)!
|
Automatically Creating Bars in the Chord Editor
|
Wednesday May 5, 2004
One thing I dont like about the chord editors in BiaB and MiBAC Jazz is that the length of a chorus is specified separately from the the entry of the chords. This is of course redundant because this length can be deduced from the number of bars of chords you enter. In addition to prettier layouts, this is another advantage of designing a chord editor to behave more like a word processor.
My chord editor is almost complete, with all the features I wrote about last week (input verification using a formatter and automatic completion) and now with cut, copy, and paste, and unlimited undos and redos. Whenever we try to move beyond the last bar using tab, right arrow, option right arrow, or command right arrow, a new bar is automatically appended. This is quite a nice design since one can just start and continue typing the chords without worrying about how long the chart will be!
Heres what a document window looks like when a new document is just created.
Suppose we type the chords F and Am and hit option right arrow (skip to the next half note). The chord editor will automatically create a new second bar and its first beat will be selected.
We continue to type in the first eight bars in this manner. Suppose the next eight bars are identical to the first eight. We select the first eight bars using the mouse and choose Edit->Copy from the menu.
Then we choose Edit->Insert From Pasteboard.
This inserts the contents of the pasteboard before the current selection.
At this point to continue to enter chords after bar 16, click on its last beat and hit right arrow.
Tuesday May 4, 2004
The Cocoa undo manager is such a pleasure to use! Implementing undos and redos in an application has never been easier. Basically in each routine that implements an operation that can be undone, you register with the undo manager a routine and the parameters to undo that operation. In this undo routine (when and if it is executed) you register with the undo manager a routine and the parameters to redo that undo. The beauty of the design is: the routine used for undo and redo can usually be the same one.
Let me illustrate with an actual routine in my code. The method replaceInRangeWithUndo:withBars: replaces a number of bars in the current chord chart with a number of bars passed as parameters.
- (void)replaceInRangeWithUndo:(NSRange)range withBars:(NSArray *)bars
{
// Get the contents of the bars that will be replaced.
NSArray *originalBars = [self barsInRange:range];
// Compute the range the new bars will occupy.
NSRange newRange = NSMakeRange(range.location, [bars count]);
NSUndoManager *undoManager = ...
[[undoManager prepareWithInvocationTarget:self]
replaceInRangeWithUndo:newRange withBars:originalBars];
// Do the actual replacing.
[self replaceInRange:range withBars:bars];
}
This is what the code says: to undo a replaceInRangeWithUndo:withBars: operation, replace the range occupied by the new bars with the original bars. Notice that this routine is used for redo as well. I.e., to redo a replaceInRangeWithUndo:withBars:, replace the original bars (again) with the new bars. Thats all you need to write, and thats also the beauty of this undo/redo mechanism.
|
Search this blog with
Lists
Less-Known Facts About Emacs
Emacs Rants
Chinese Restaurants in Calgary
Calgary/Banff Tourist Attractions
C++ Reading List
Science Fiction Series
Top-10 Reason I Stopped Working on Emacs
Top-10 Types of Questions I Get About Emacs
10 Defining Moments as Programmer
Misc
Carbon XEmacs
Emacs for
Mac OS X
|