[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 |