[kln2-devel] accidentals
Brought to you by:
wiecko
|
From: Marek W. <mar...@o2...> - 2004-02-13 21:36:12
|
Hi e-body,
This is an e-mail about implementing notes with accidentals:
flats (bemols 'b') and sharps (crosses '#').
Although it is quite lengthy, IT IS ABSOLUTELY OBLIGATORY FOR
EACH OF YOU TO READ THIS AND SEND A COMMENT TO kln2-devel
WITHIN NEXT FEW DAYS. It can be as simple as "I've read it and
it seems OK.", but more elaborate comments are welcome too.
* Is the idea clear?
* Did I miss something? (please don't report missing
constructors, destructors and protected/private properties
unless you expect some kind of problem in implementing; on=20
the other hand, please report all missing public methods,=20
even if they seem obvious)
* Can something be implemented in more easy/straightforward way?
* Is there something that doesn't fit to future ideas, that
would have to be completely rebuild in future because of the
future goals?
* Any other problems?
Also, does the naming of classes/members seem alright?
This message is structured in the following way:
* first there are some general comments and motivation for Note
copy constructor;
* then there are some general comments about sharps and flats;
* then there is a suggested skeleton .h file for new classes
and new members of Note;
* then there are suggestions for step-by-step implementation
(which would enable tests of each new part of the code).
This is quite lengthy message. Please, in your replies cite only
few lines you refer to/comment about, NOT the whole message!
Have fun reading!
Best
~Marek
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D
=3D=3D Note copy constructor
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D
Right now in kln2 there is one instance of each note. They make
a pool of available notes. Each of them may be shown or hidden. =20
That's all.
There are many situations in which multiple notes may be needed
to be visible in the same line (C, C#, Cb occupy the same place,
in sequence/chords exercise the same note may be visible in few
positions). In the following I will take motivation from a
sequence exercise (but it will turn out quite useful in
sharps/flats implementation too).
Example:
-----O-----O----
--------O-------
----------------
----------------
----------------
could not be shown right now, because right now there is only one=20
note F.
The idea is:
* we still have a pool of available notes
* but when a note is shown it is copy-constructed from one of
the pool notes and it is destructed/deleted when it is=20
hidden (this means destructors will have to be double-checked
for not deleted things).
There would be a KeySignature class. When a note is created it
should either have it's own sign or decide about it based on key
signature.
enum AccidentalType {natural, sharp, flat, key, exact};
Now, imagine we have a G major key signature
-#-
---
---
---
---
and we have a 'Note* fFlat' which hold an F flat note and=20
'Note* newNote'.=20
Now, copy constructor
newNote =3D new Note (fFlat, exact); // should construct a new F flat=20
note
newNote =3D new Note (fFlat); // should construct a new F flat=20
note
newNote =3D new Note (fFlat, flat); // should construct a new F flat=20
note
newNote =3D new Note (fFlat, natural); // should construct a new F note
newNote =3D new Note (fFlat, sharp); // should construct a new F sharp=20
note
newNote =3D new Note (fFlat, key); // should construct a new F sharp=20
note
Note, that Note(fFlat,sharp) should create just an F sharp (not
double-sharp, as suggested by the Gmajor key), because it is the
way it is sometimes written in the score just to remind of the
key signature in some tricky part of the score.
// In future we may implement double sharps and double flats,
// but let us keep things simple for now.
Also, I think F flat and E should be represented as distinct
objects (for example, they would have different name when
highlighted). The same goes for e.g. F# and Gb, and so on.
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
=3D=3D sharps and flats as part of a Note
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
OK, so we have a pool of all (right now 23) notes plus some
notes displayed in the staff. Each of them should have a sharp
AND a flat AND a natural (only max one of them would be
displayed). This is very little waste of memory and makes the
code clearer: you want to show note's flat/sharp - you just
show() it. And the pictures are static members of
Flat/Sharp/Natural (so there is only one copy of e.g. red flat
picture for all the flats!).
Therefore, Note's protected properties:
Sharp* sharp;
Flat* flat;
Natural* natural;
Each note has a 'AccidentalType accidental' property; if it is:
'flat', 'sharp', 'natural' - as it says.
'key' and 'exact' - should be used only in the copy constructor
and not here.
There should be a 'setAccidental(AccidentalType type)' public
method (which both sets the property and if the note is shown
shows or hides the accidental and changes note's name).
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
=3D=3D new classes definitions; skeleton .h=20
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
class Flat: public QCanvasRectangle
{
Q_OBJECT
// note, that inherited QCanvasRectangle provides
// methods e.g. for setting picture's position in canvas
public slots:
//! change picture's color for fixed time
changeColorTmp(char color, int time);
//! change picture's color permanently
changeColor(char color);
protected slots:
//! overridden QCanvasRectangle::drawShape
/*! chooses right color for the picture - like in the class Note */
void drawShape( QPainter & );
protected:
char color; // '0' (zero,black),'b' (blue), 'r'(red) or 'g'(green) -=20
like for Note
static QPixmap* blackFlat;
static QPixmap* blueFlat;
static QPixmap* redFlat;
static QPixmap* greenFlat;
} ////////////////////// END OF class Flat DEFINITION///////////
// The same for class Sharp and class Natural.
// In future ImageItem should enable rescaling the objects based
// on SC or other scale variable; so maybe one day we should
// inherit ImageItem rather than QCanvasRectangle; but for now
// it would not enable any extra properties. And ImageItem hold
// only one picture which is not static. :(
class KeySignature : public RandomObject, public QCanvasRectangle
{
Q_OBJECT
// it inherits QCanvasRectangle, because from canvas point of
// view KeySignature does have width, height and so on (e.g.
// notes cannot be shown too much to the left, because after the
// key there is a signature). This would require overriding
// drawShape() (which // shouldn't do anything).
public:
int numberOfAccidentals(); // >0 - sharps, =3D=3D0 - C Major, <0 - flats
QString fullNameMajor(); // e.g. "C sharp Major"
char baseNameMajor(); // e.g. 'C'
AccidentalType accidentalOfNameMajor(); // e.g. sharp, flat or=20
natural=20
// ('key' and 'exact' not used!)
QString fullNameMinor();
char baseNameMinor();
AccidentalType accidentalOfNameMinor();
void setName(char baseName, AccidentalType accidentalOfName, bool=20
Major);
// e.g. setName('C',neutral,true) sets C Major
// setName('G',flat,false) sets Gb minor etc.
void setNumberOfSharps(int numOfSharps);=20
void setNumberOfFlats(int numOfFlats); // sets numOfAccidentals to=20
'minus numOfFlats'
AccidentalType accidental(char noteBaseName);=20
// Dear key, I am 'C'; do I get an accidental from you?
// returns 'flat' 'sharp' or 'natural'
bool isPartOf(Note* note); // a better name suggestion welcome :( ?
bool isPartOf(char baseNoteName, AccidentalType noteAccidental);
// Dear key, I am 'Cb'; can I not show my flat?
// returns 'true' if note's accidental is set by the key,=20
// 'false' if the note has to show it's accidental explicitly;
//
// Note, that not-accidented notes should ask the key too!
// For example the GMajor key would say to a note F :=20
// 'false' which would mean 'you don't fit in here, show your=20
natural'.
void show(); // shows all key signature's flats or sharps
void hide();
void highlight(char color); // sets all flats/sharps color to color
// and displays key major and minor names under the staff (like=20
// Note-s do now by note's pix).
protected:
void drawShape( QPainter & ) {};
Flat* allFlats[7]; // it CANNOT be static! because for some types of
// exercises few staves may be needed with separate key signatures
Sharp* allSharps[7];
int numberOfAccidentals;=20
// > 0 =3D> num of sharps; e.g. 1 means G Major
// =3D=3D0 =3D> no sharps or flats (C Major)
// < 0 =3D> num of flats ; e.g. -1 means F Major
static char orderOfFlats[7]; // e.g. orderOfFlats[0]=3D'B/H' because
// this is where the first flat goes;
static char orderOfSharps[7]; // e.g. orderOfSharps[0]=3D'F'
} //////////////////////////// END OF class KeySignature DEFINITION
// new properties of class Note :
{
QString fullName();
char baseCharName(); // e.g. 'C'
AccidentalType accidentalOfName(); // e.g. sharp, flat or natural
setName(char newBaseName, AccidentalType newAccidental=3Dnatural);
setAccidental(AccidentalType newAccidental);
bool isAccidentalShown();
void setExplicitAccidental(bool);
//! the copy constructors:
Note (Note* , AccidentalType=3Dexact);
protected:
Flat* flat;
Sharp* sharp;
Natural* natural;
char baseCharName; // renamed cName
AccidentalType accidental;
bool isAccidentalShown; // true =3D yes, the acc. is displayed
bool explicitAccidental; // true =3D show it even if=20
// the key signature takes care of it
// what to do with integer representation of the name?=20
// we have to check how it is used; should we number
// * all the notes (like 0=3DC, 1=3DC#, 2=3DDb, 3=3DD )
// * distinct note's positions (like 0=3DC, 1=3DC#/Db, 2=3DD)
// * only base notes (like it is now - 0=3DC, 1=3DD )
// * or abandon the integer representation as an unclear
// one?
KeySignature* keySignature; // or maybe there should be a parent
// Staff class with a keySignature property?
// This would inherit a QCanvas, take care of all the staff lines,=20
// key type (bass/treble), key signature and so on? In such a case
// each note would hold a pointer to it's parent Staff rather than
// to the keySignature directly; but for now 'KS* kS' would do.
} //////////////////////////////// END OF NEW NOTE MEMBERS
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=20
Implementation:=20
1. add the declarations; define empty functions; check that
things compile and work as previously (note: not that=20
obvious, e.g. checking note's name will have to be changed=20
in few places)
2. note2name (current exercise) without changing KeySignature
(that is in C Major key only)
this is quite stupid from user's point of view but makes an
important step in step-by-step implementation;
this is quite easy to implement because this does not change
program's structure; the only major decisions are to be made
about the widgets layout:
- extra buttons (ordered in piano keyboard style? but maybe two
sets of 'black' keys: one above with sharps, one below with
flats?:
C# D# E# F#
C D E F G=20
Db Eb Fb Gb
and so on ? )=20
- extra name checkboxes (grrr this makes the interface quite
unclear :( )
- user's input : letter with Shift =3D sharp note, letter with
Ctrl =3D flat note (note, that Alts are on some kbds used for=20
'accidented' local letters like =F3)
- bigger pool of all notes
- when choosing notes for a test, not only range and name=20
checkboxes but also three new checkboxes:
* natural notes (which activates middle row of nameButtons)
* sharp notes (which activates top row of nameButtons)
* flat notes (which activates button row of nameButtons)
They should probably be placed to the right of nameButtons,
bellow rangeSliders.
3. the same as (2.) but with manually changed KeySignature:=20
now this becomes important from user's point of view:
user chooses a key signature from a combo in the toolbar (to=20
focus: G major); the key should be displayed; and now if "CMajor=20
F" note is shown the user should recognize it is F sharp.
I guess, the questions should be asked only about notes without=20
extra accidentals.
Lessons:=20
* Gmajor, notes in middle region (one sharp F#)
* Gmajor, notes in upper region
* Gmajor, notes in lower region
* Gmajor, all notes
* Fmajor, notes in m.r. (one flat, H/B b)
* Fmajor, u.r.
* Fmajor, l.r.
* Fmajor, all notes
* Dmajor, m.r. (two sharps: F# and C#)
and so-on.
At this point we know both accidented notes and key signatures=20
work. We can move few-fold:
4a. the same with randomly chosen KeySignature for each question
Lessons:
Gmajor and Cmajor, (no sharps or one sharp at F)
Dmajor, Gmajor and Cmajor (no sharps one or two sharps)
Amajor, Dmajor, Gmajor and Cmajor (0,1 or 3 sharps)
and so on
Fmajor and Cmajor, (no flats, or one flat at H/B)
Hb/Bb major, Fmajor and Cmajor (no flat, one or two flats)
4b. exercises about key signatures
4c. fretboard (AT LAST! :)
4d. sequence reading exercise=20
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--=20
\/ /|\ Marek Wieckowski
##### | | |
=3D . . =3D \|/ Institute of Theoretical Physics
U | Warsaw University
/ ~ \___ |
<| | |
> . < | http://www.fuw.edu.pl/~wiecko
<<___>> | http://klearnnotes2.sourceforge.net
|