diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
commit | 145364a8af6a1fec06556221e66d4b724a62fc9a (patch) | |
tree | 53bd71a544008c518034f208d64c932dc2883f50 /src/base/NotationTypes.h | |
download | rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip |
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/base/NotationTypes.h')
-rw-r--r-- | src/base/NotationTypes.h | 1342 |
1 files changed, 1342 insertions, 0 deletions
diff --git a/src/base/NotationTypes.h b/src/base/NotationTypes.h new file mode 100644 index 0000000..9133983 --- /dev/null +++ b/src/base/NotationTypes.h @@ -0,0 +1,1342 @@ +// -*- c-basic-offset: 4 -*- + + +/* + Rosegarden + A sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + The moral right of the authors to claim authorship of this work + has been asserted. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. +*/ + +#ifndef _NOTATION_TYPES_H_ +#define _NOTATION_TYPES_H_ + +#include <list> +#include <map> + +#include "Event.h" +#include "Instrument.h" + +/* + * NotationTypes.h + * + * This file contains definitions of several classes to assist in + * creating and manipulating certain event types. The classes are: + * + * Accidental + * Clef + * Key + * Indication + * Pitch + * Note + * TimeSignature + * AccidentalTable + * + * The classes in this file are _not_ actually used for storing + * events. Events are always stored in Event objects (see Event.h). + * + * These classes are usually constructed on-the-fly when a particular + * operation specific to a single sort of event is required, and + * usually destroyed as soon as they go out of scope. The most common + * usages are for creating events (create an instance of one of these + * classes with the data you require, then call getAsEvent on it), for + * doing notation-related calculations from existing events (such as + * the bar duration of a time signature), and for doing calculations + * that are independent of any particular instance of an event (such + * as the Note methods that calculate duration-related values without + * reference to any specific pitch or other note-event properties; or + * everything in Pitch). + * + * This file also defines the event types and standard property names + * for the basic events. + */ + +namespace Rosegarden +{ + +extern const int MIN_SUBORDERING; + +typedef std::list<int> DurationList; + + +/** + * Accidentals are stored in the event as string properties, purely + * for clarity. (They aren't manipulated _all_ that often, so this + * probably isn't a great inefficiency.) Originally we used an enum + * for the Accidental type with conversion functions to and from + * strings, but making Accidental a string seems simpler. + */ + +typedef std::string Accidental; + +namespace Accidentals +{ + extern const Accidental NoAccidental; + extern const Accidental Sharp; + extern const Accidental Flat; + extern const Accidental Natural; + extern const Accidental DoubleSharp; + extern const Accidental DoubleFlat; + + typedef std::vector<Accidental> AccidentalList; + + /** + * When no accidental is specified for a pitch, there are several + * strategies to determine what accidental to display for an + * out-of-key pitch + */ + enum NoAccidentalStrategy { + /** always use sharps */ + UseSharps, + /** always use flats */ + UseFlats, + /** always use sharps or always use flats depending on of what + * type of accidentals the current key is made up */ + UseKeySharpness, + /** use the most likely accidental for this key */ + UseKey + }; + + /** + * Get the predefined accidentals (i.e. the ones listed above) + * in their defined order. + */ + extern AccidentalList getStandardAccidentals(); + + /** + * Get the change in pitch resulting from an accidental: -1 for + * flat, 2 for double-sharp, 0 for natural or NoAccidental etc. + * This is not as useful as it may seem, as in reality the + * effect of an accidental depends on the key as well -- see + * the Key and Pitch classes. + */ + extern int getPitchOffset(const Accidental &accidental); + + + /** + * Get the Accidental corresponding to a change in pitch: flat + * for -1, double-sharp for 2, natural for 0 etc. + * + * Useful for tying to code that represents accidentals by + * their pitch change. + */ + extern Accidental getAccidental(int pitchChange); +} + + +/** + * Marks, like Accidentals, are stored in the event as string properties. + */ + +typedef std::string Mark; + +namespace Marks //!!! This would be better as a class, these days +{ + extern const Mark NoMark; // " " + + extern const Mark Accent; // ">" + extern const Mark Tenuto; // "-" ("legato" in RG2.1) + extern const Mark Staccato; // "." + extern const Mark Staccatissimo; // "'" + extern const Mark Marcato; // "^" + extern const Mark Sforzando; // "sf" + extern const Mark Rinforzando; // "rf" + + extern const Mark Trill; // "tr" + extern const Mark LongTrill; // with wiggly line + extern const Mark TrillLine; // line on its own + extern const Mark Turn; // "~" + + extern const Mark Pause; // aka "fermata" + + extern const Mark UpBow; // "v" + extern const Mark DownBow; // a square with the bottom side missing + + extern const Mark Mordent; + extern const Mark MordentInverted; + extern const Mark MordentLong; + extern const Mark MordentLongInverted; + + /** + * Given a string, return a mark that will be recognised as a + * text mark containing that string. For example, the Sforzando + * mark is actually defined as getTextMark("sf"). + */ + extern Mark getTextMark(std::string text); + + /** + * Return true if the given mark is a text mark. + */ + extern bool isTextMark(Mark mark); + + /** + * Extract the string from a text mark. + */ + extern std::string getTextFromMark(Mark mark); + + /** + * Given a string, return a mark that will be recognised as a + * fingering mark containing that string. (We use a string + * instead of a number to permit "fingering" marks containing + * labels like "+".) + */ + extern Mark getFingeringMark(std::string fingering); + + /** + * Return true if the given mark is a fingering mark. + */ + extern bool isFingeringMark(Mark mark); + + /** + * Extract the string from a fingering mark. + */ + extern std::string getFingeringFromMark(Mark mark); + + /** + * Extract the number of marks from an event. + */ + extern int getMarkCount(const Event &e); + + /** + * Extract the marks from an event. + */ + extern std::vector<Mark> getMarks(const Event &e); + + /** + * Return the first fingering mark on an event (or NoMark, if none). + */ + extern Mark getFingeringMark(const Event &e); + + /** + * Add a mark to an event. If unique is true, add the mark only + * if the event does not already have it (otherwise permit + * multiple identical marks). + */ + extern void addMark(Event &e, const Mark &mark, bool unique); + + /** + * Remove a mark from an event. Returns true if the mark was + * there to remove. If the mark was not unique, removes only + * the first instance of it. + */ + extern bool removeMark(Event &e, const Mark &mark); + + /** + * Returns true if the event has the given mark. + */ + extern bool hasMark(const Event &e, const Mark &mark); + + /** + * Get the predefined marks (i.e. the ones listed above) in their + * defined order. + */ + extern std::vector<Mark> getStandardMarks(); +} + + +/** + * Clefs are represented as one of a set of standard strings, stored + * within a clef Event. The Clef class defines those standards and + * provides a few bits of information about the clefs. + */ + +class Clef +{ +public: + static const std::string EventType; + static const int EventSubOrdering; + static const PropertyName ClefPropertyName; + static const PropertyName OctaveOffsetPropertyName; + static const Clef DefaultClef; + typedef Exception BadClefName; + + static const std::string Treble; + static const std::string French; + static const std::string Soprano; + static const std::string Mezzosoprano; + static const std::string Alto; + static const std::string Tenor; + static const std::string Baritone; + static const std::string Varbaritone; + static const std::string Bass; + static const std::string Subbass; + + /** + * Construct the default clef (treble). + */ + Clef() : m_clef(DefaultClef.m_clef), m_octaveOffset(0) { } + + /** + * Construct a Clef from the clef data in the given event. If the + * event is not of clef type or contains insufficient data, this + * returns the default clef (with a warning). You should normally + * test Clef::isValid() to catch that before construction. + */ + Clef(const Event &e); + + /** + * Construct a Clef from the given data. Throws a BadClefName + * exception if the given string does not match one of the above + * clef name constants. + */ + Clef(const std::string &s, int octaveOffset = 0); + + Clef(const Clef &c) : m_clef(c.m_clef), m_octaveOffset(c.m_octaveOffset) { + } + + Clef &operator=(const Clef &c); + + bool operator==(const Clef &c) const { + return c.m_clef == m_clef && c.m_octaveOffset == m_octaveOffset; + } + + bool operator!=(const Clef &c) const { + return !(c == *this); + } + + ~Clef() { } + + /** + * Test whether the given event is a valid Clef event. + */ + static bool isValid(const Event &e); + + /** + * Return the basic clef type (Treble, French, Soprano, Mezzosoprano, Alto, Tenor, Baritone, Varbaritone, Bass, Subbass) + */ + std::string getClefType() const { return m_clef; } + + /** + * Return any additional octave offset, that is, return 1 for + * a clef shifted an 8ve up, etc + */ + int getOctaveOffset() const { return m_octaveOffset; } + + /** + * Return the number of semitones a pitch in the treble clef would + * have to be lowered by in order to be drawn with the same height + * and accidental in this clef + */ + int getTranspose() const; + + /** + * Return the octave component of getTranspose(), i.e. the number + * of octaves difference in pitch between this clef and the treble + */ + int getOctave() const; + + /** + * Return the intra-octave component of getTranspose(), i.e. the + * number of semitones this clef is distinct in pitch from the treble + * besides the difference in octaves + */ + int getPitchOffset() const; + + /** + * Return the height-on-staff (in Pitch terminology) + * of the clef's axis -- the line around which the clef is drawn. + */ + int getAxisHeight() const; + + typedef std::vector<Clef> ClefList; + + /** + * Return all the clefs, in ascending order of pitch + */ + static ClefList getClefs(); + + /// Returned event is on heap; caller takes responsibility for ownership + Event *getAsEvent(timeT absoluteTime) const; + +private: + std::string m_clef; + int m_octaveOffset; +}; + +/** + * All we store in a key Event is the name of the key. A Key object + * can be constructed from such an Event or just from its name, and + * will return all the properties of the key. The Key class also + * provides some useful mechanisms for getting information about and + * transposing between keys. + */ + +class Key +{ +public: + static const std::string EventType; + static const int EventSubOrdering; + static const PropertyName KeyPropertyName; + static const Key DefaultKey; + typedef Exception BadKeyName; + typedef Exception BadKeySpec; + + /** + * Construct the default key (C major). + */ + Key(); + + /** + * Construct a Key from the key data in the given event. If the + * event is not of key type or contains insufficient data, this + * returns the default key (with a warning). You should normally + * test Key::isValid() to catch that before construction. + */ + Key(const Event &e); + + /** + * Construct the named key. Throws a BadKeyName exception if the + * given string does not match one of the known key names. + */ + Key(const std::string &name); + + /** + * Construct a key from signature and mode. May throw a + * BadKeySpec exception. + */ + Key(int accidentalCount, bool isSharp, bool isMinor); + + /** + * Construct the key with the given tonic and mode. (Ambiguous.) + * May throw a BadKeySpec exception. + */ + Key(int tonicPitch, bool isMinor); + + Key(const Key &kc); + + ~Key() { + delete m_accidentalHeights; + } + + Key &operator=(const Key &kc); + + bool operator==(const Key &k) const { + return k.m_name == m_name; + } + + bool operator!=(const Key &k) const { + return !(k == *this); + } + + /** + * Test whether the given event is a valid Key event. + */ + static bool isValid(const Event &e); + + /** + * Return true if this is a minor key. Unlike in RG2.1, + * we distinguish between major and minor keys with the + * same signature. + */ + bool isMinor() const { + return m_keyDetailMap[m_name].m_minor; + } + + /** + * Return true if this key's signature is made up of + * sharps, false if flats. + */ + bool isSharp() const { + return m_keyDetailMap[m_name].m_sharps; + } + + /** + * Return the pitch of the tonic note in this key, as a + * MIDI (or RG4) pitch modulo 12 (i.e. in the range 0-11). + * This is the pitch of the note named in the key's name, + * e.g. 0 for the C in C major. + */ + int getTonicPitch() const { + return m_keyDetailMap[m_name].m_tonicPitch; + } + + /** + * Return the number of sharps or flats in the key's signature. + */ + int getAccidentalCount() const { + return m_keyDetailMap[m_name].m_sharpCount; + } + + /** + * Return the key with the same signature but different + * major/minor mode. For example if called on C major, + * returns A minor. + */ + Key getEquivalent() const { + return Key(m_keyDetailMap[m_name].m_equivalence); + } + + /** + * Return the name of the key, in a human-readable form + * also suitable for passing to the Key constructor. + */ + std::string getName() const { + return m_name; + } + + /** + * Return the name of the key, in the form used by RG2.1. + */ + std::string getRosegarden2Name() const { + return m_keyDetailMap[m_name].m_rg2name; + } + + /** + * Return the accidental at the given height-on-staff + * (in Pitch terminology) in the given clef. + */ + Accidental getAccidentalAtHeight(int height, const Clef &clef) const; + + /** + * Return the accidental for the the given number of steps + * from the tonic. For example: for F major, step '3' is the + * Bb, so getAccidentalForStep(3) will yield a Flat. + */ + Accidental getAccidentalForStep(int steps) const; + + /** + * Return the heights-on-staff (in Pitch + * terminology) of all accidentals in the key's signature, + * in the given clef. + */ + std::vector<int> getAccidentalHeights(const Clef &clef) const; + + /** + * Return the result of applying this key to the given + * pitch, that is, modifying the pitch so that it has the + * same status in terms of accidentals as it had when + * found in the given previous key. + */ + int convertFrom(int pitch, const Key &previousKey, + const Accidental &explicitAccidental = + Accidentals::NoAccidental) const; + + /** + * Return the result of transposing the given pitch into + * this key, that is, modifying the pitch by the difference + * between the tonic pitches of this and the given previous + * key. + */ + int transposeFrom(int pitch, const Key &previousKey) const; + + /** + * Reduce a height-on-staff to a single octave, so that it + * can be compared against the accidental heights returned + * by the preceding method. + */ + static inline unsigned int canonicalHeight(int height) { + return (height > 0) ? (height % 7) : ((7 - (-height % 7)) % 7); + } + + typedef std::vector<Key> KeyList; + + /** + * Return all the keys in the given major/minor mode, in + * no particular order. + */ + static KeyList getKeys(bool minor = false); + + + /// Returned event is on heap; caller takes responsibility for ownership + Event *getAsEvent(timeT absoluteTime) const; + + /** + * Transpose this key by the specified interval given in pitch and steps + * + * For example: transposing F major by a major triad (4,2) yields + * A major. + */ + Key transpose(int pitchDelta, int heightDelta); + +private: + std::string m_name; + mutable std::vector<int> *m_accidentalHeights; + + struct KeyDetails { + bool m_sharps; + bool m_minor; + int m_sharpCount; + std::string m_equivalence; + std::string m_rg2name; + int m_tonicPitch; + + KeyDetails(); // ctor needed in order to live in a map + + KeyDetails(bool sharps, bool minor, int sharpCount, + std::string equivalence, std::string rg2name, + int m_tonicPitch); + + KeyDetails(const KeyDetails &d); + + KeyDetails &operator=(const KeyDetails &d); + }; + + + typedef std::map<std::string, KeyDetails> KeyDetailMap; + static KeyDetailMap m_keyDetailMap; + static void checkMap(); + void checkAccidentalHeights() const; + +}; + + +/** + * Indication is a collective name for graphical marks that span a + * series of events, such as slurs, dynamic marks etc. These are + * stored in indication Events with a type and duration. The + * Indication class gives a basic set of indication types. + */ + +class Indication +{ +public: + static const std::string EventType; + static const int EventSubOrdering; + static const PropertyName IndicationTypePropertyName; + typedef Exception BadIndicationName; + + static const std::string Slur; + static const std::string PhrasingSlur; + static const std::string Crescendo; + static const std::string Decrescendo; + static const std::string Glissando; + + static const std::string QuindicesimaUp; + static const std::string OttavaUp; + static const std::string OttavaDown; + static const std::string QuindicesimaDown; + + Indication(const Event &e) + /* throw (Event::NoData, Event::BadType) */; + Indication(const std::string &s, timeT indicationDuration) + /* throw (BadIndicationName) */; + + Indication(const Indication &m) : m_indicationType(m.m_indicationType), + m_duration(m.m_duration) { } + + Indication &operator=(const Indication &m); + + ~Indication() { } + + std::string getIndicationType() const { return m_indicationType; } + timeT getIndicationDuration() const { return m_duration; } + + bool isOttavaType() const { + return + m_indicationType == QuindicesimaUp || + m_indicationType == OttavaUp || + m_indicationType == OttavaDown || + m_indicationType == QuindicesimaDown; + } + + int getOttavaShift() const { + return (m_indicationType == QuindicesimaUp ? 2 : + m_indicationType == OttavaUp ? 1 : + m_indicationType == OttavaDown ? -1 : + m_indicationType == QuindicesimaDown ? -2 : 0); + } + + /// Returned event is on heap; caller takes responsibility for ownership + Event *getAsEvent(timeT absoluteTime) const; + +private: + bool isValid(const std::string &s) const; + + std::string m_indicationType; + timeT m_duration; +}; + + + +/** + * Definitions for use in the text Event type. + */ + +class Text +{ +public: + static const std::string EventType; + static const int EventSubOrdering; + static const PropertyName TextPropertyName; + static const PropertyName TextTypePropertyName; + static const PropertyName LyricVersePropertyName; + + /** + * Text styles + */ + static const std::string UnspecifiedType; + static const std::string StaffName; + static const std::string ChordName; + static const std::string KeyName; + static const std::string Lyric; + static const std::string Chord; + static const std::string Dynamic; + static const std::string Direction; + static const std::string LocalDirection; + static const std::string Tempo; + static const std::string LocalTempo; + static const std::string Annotation; + static const std::string LilyPondDirective; + + /** + * Special LilyPond directives + */ + static const std::string Segno; // print segno here + static const std::string Coda; // print coda sign here + static const std::string Alternate1; // first alternative ending + static const std::string Alternate2; // second alternative ending + static const std::string BarDouble; // next barline is double + static const std::string BarEnd; // next barline is final double + static const std::string BarDot; // next barline is dotted + static const std::string Gliss; // \glissando on this note (to next note) + static const std::string Arpeggio; // \arpeggio on this chord +// static const std::string ArpeggioUp; // \ArpeggioUp on this chord +// static const std::string ArpeggioDn; // \ArpeggioDown on this chord + static const std::string Tiny; // begin \tiny font section + static const std::string Small; // begin \small font section + static const std::string NormalSize; // begin \normalsize font section + + Text(const Event &e) + /* throw (Event::NoData, Event::BadType) */; + Text(const std::string &text, + const std::string &textType = UnspecifiedType); + Text(const Text &); + Text &operator=(const Text &); + ~Text(); + + std::string getText() const { return m_text; } + std::string getTextType() const { return m_type; } + + int getVerse() const { return m_verse; } // only relevant for lyrics + void setVerse(int verse) { m_verse = verse; } + + static bool isTextOfType(Event *, std::string type); + + /** + * Return those text types that the user should be allowed to + * specify directly and visually + */ + static std::vector<std::string> getUserStyles(); + + /** + * Return a list of available special LilyPond directives + */ + static std::vector<std::string> getLilyPondDirectives(); + + /// Returned event is on heap; caller takes responsibility for ownership + Event *getAsEvent(timeT absoluteTime) const; + +private: + std::string m_text; + std::string m_type; + long m_verse; +}; + + + +/** + * Pitch stores a note's pitch and provides information about it in + * various different ways, notably in terms of the position of the + * note on the staff and its associated accidental. + * + * (See docs/discussion/units.txt for explanation of pitch units.) + * + * This completely replaces the older NotationDisplayPitch class. + */ + +class Pitch +{ +public: + /** + * Construct a Pitch object based on the given Event, which must + * have a BaseProperties::PITCH property. If the property is + * absent, NoData is thrown. The BaseProperties::ACCIDENTAL + * property will also be used if present. + */ + Pitch(const Event &e) + /* throw Event::NoData */; + + /** + * Construct a Pitch object based on the given performance (MIDI) pitch. + */ + Pitch(int performancePitch, + const Accidental &explicitAccidental = Accidentals::NoAccidental); + + /** + * Construct a Pitch based on octave and pitch in octave. The + * lowest permissible octave number is octaveBase, and middle C is + * in octave octaveBase + 5. pitchInOctave must be in the range + * 0-11 where 0 is C, 1 is C sharp, etc. + */ + Pitch(int pitchInOctave, int octave, + const Accidental &explicitAccidental = Accidentals::NoAccidental, + int octaveBase = -2); + + /** + * Construct a Pitch based on octave and note in scale. The + * lowest permissible octave number is octaveBase, and middle C is + * in octave octaveBase + 5. The octave supplied should be that + * of the root note in the given key, which may be in a different + * MIDI octave from the resulting pitch (as MIDI octaves always + * begin at C). noteInScale must be in the range 0-6 where 0 is + * the root of the key and so on. The accidental is relative to + * noteInScale: if there is an accidental in the key for this note + * already, explicitAccidental will be "added" to it. + * + * For minor keys, the harmonic scale is used. + */ + Pitch(int noteInScale, int octave, const Key &key, + const Accidental &explicitAccidental = Accidentals::NoAccidental, + int octaveBase = -2); + + /** + * Construct a Pitch based on (MIDI) octave, note in the C major scale and + * performance pitch. The accidental is calculated based on these + * properties. + */ + Pitch(int noteInCMajor, int octave, int pitch, + int octaveBase = -2); + + /** + * Construct a Pitch based on octave and note name. The lowest + * permissible octave number is octaveBase, and middle C is in + * octave octaveBase + 5. noteName must be a character in the + * range [CDEFGAB] or lower-case equivalents. The key is supplied + * so that we know how to interpret the NoAccidental case. + */ + Pitch(char noteName, int octave, const Key &key, + const Accidental &explicitAccidental = Accidentals::NoAccidental, + int octaveBase = -2); + + /** + * Construct a Pitch corresponding a staff line or space on a + * classical 5-line staff. The bottom staff line has height 0, + * the top has height 8, and both positive and negative values are + * permissible. + */ + Pitch(int heightOnStaff, const Clef &clef, const Key &key, + const Accidental &explicitAccidental = Accidentals::NoAccidental); + + Pitch(const Pitch &); + Pitch &operator=(const Pitch &); + + /** + * Return the MIDI pitch for this Pitch object. + */ + int getPerformancePitch() const; + + /** + * Return the accidental for this pitch using a bool to prefer sharps over + * flats if there is any doubt. This is the accidental + * that would be used to display this pitch outside of the context + * of any key; that is, it may duplicate an accidental actually in + * the current key. This should not be used if you need to get an + * explicit accidental returned for E#, Fb, B# or Cb. + * + * This version of the function exists to avoid breaking old code. + */ + Accidental getAccidental(bool useSharps) const; + + /** + * Return the accidental for this pitch, using a key. This should be used + * if you need an explicit accidental returned for E#, Fb, B# or Cb, which + * can't be resolved correctly without knowing that their key requires + * them to take an accidental. The provided key will also be used to + * determine whether to prefer sharps over flats. + */ + Accidental getAccidental(const Key &key) const; + + /** + * Return the accidental that should be used to display this pitch + * in a given key. For example, if the pitch is F-sharp in a key + * in which F has a sharp, NoAccidental will be returned. (This + * is in contrast to getAccidental, which would return Sharp.) + * This obviously can't take into account things like which + * accidentals have already been displayed in the bar, etc. + */ + Accidental getDisplayAccidental(const Key &key) const; + + /** + * Return the accidental that should be used to display this pitch + * in a given key, using the given strategy to resolve pitches where + * an accidental is needed but not specified. + */ + Accidental getDisplayAccidental(const Key &key, Accidentals::NoAccidentalStrategy) const; + + /** + * Return the position in the scale for this pitch, as a number in + * the range 0 to 6 where 0 is the root of the key. + */ + int getNoteInScale(const Key &key) const; + + /** + * Return the note name for this pitch, as a single character in + * the range A to G. (This is a reference value that should not + * normally be shown directly to the user, for i18n reasons.) + */ + char getNoteName(const Key &key) const; + + /** + * Return the height at which this pitch should display on a + * conventional 5-line staff. 0 is the bottom line, 1 the first + * space, etc., so for example middle-C in the treble clef would + * return -2. + * + * Chooses the most likely accidental for this pitch in this key. + */ + int getHeightOnStaff(const Clef &clef, const Key &key) const; + + /** + * Return the height at which this pitch should display on a + * conventional 5-line staff. 0 is the bottom line, 1 the first + * space, etc., so for example middle-C in the treble clef would + * return -2. + * + * Chooses the accidental specified by the 'useSharps' parameter + */ + int getHeightOnStaff(const Clef &clef, bool useSharps) const; + + /** + * Return the octave containing this pitch. The octaveBase argument + * specifies the octave containing MIDI pitch 0; middle-C is in octave + * octaveBase + 5. + */ + int getOctave(int octaveBase = -2) const; + + /** + * Return the pitch within the octave, in the range 0 to 11. + */ + int getPitchInOctave() const; + + /** + * Return whether this pitch is diatonic in the given key. + */ + bool isDiatonicInKey(const Key &key) const; + + /** + * Return a reference name for this pitch. (C4, Bb2, etc...) + * according to http://www.harmony-central.com/MIDI/Doc/table2.html + * + * Note that this does not take into account the stored accidental + * -- this string is purely an encoding of the MIDI pitch, with + * the accidental in the string selected according to the + * useSharps flag (which may be expected to have come from a call + * to Key::isSharp). + * + * If inclOctave is false, this will return C, Bb, etc. + */ + std::string getAsString(bool useSharps, + bool inclOctave = true, + int octaveBase = -2) const; + + /** + * Return a number 0-6 corresponding to the given note name, which + * must be in the range [CDEFGAB] or lower-case equivalents. The + * return value is in the range 0-6 with 0 for C, 1 for D etc. + */ + static int getIndexForNote(char noteName); + + /** + * Return a note name corresponding to the given note index, which + * must be in the range 0-6 with 0 for C, 1 for D etc. + */ + static char getNoteForIndex(int index); + + /** + * Calculate and return the performance (MIDI) pitch corresponding + * to the stored height and accidental, interpreting them as + * Rosegarden-2.1-style values (for backward compatibility use), + * in the given clef and key + */ + static int getPerformancePitchFromRG21Pitch(int heightOnStaff, + const Accidental &accidental, + const Clef &clef, + const Key &key); + + /** + * return the result of transposing the given pitch by the + * specified interval in the given key. The key is left unchanged, + * only the pitch is transposed. + */ + Pitch transpose(const Key &key, int pitchDelta, int heightDelta); + + /** + * checks whether the accidental specified for this pitch (if any) + * is valid - for example, a Sharp for pitch 11 is invalid, as + * it's between A# and B#. + */ + bool validAccidental() const; + + /** + * Returned event is on heap; caller takes responsibility for ownership + */ + Event *getAsNoteEvent(timeT absoluteTime, timeT duration) const; + +private: + int m_pitch; + Accidental m_accidental; + + static void rawPitchToDisplayPitch + (int, const Clef &, const Key &, int &, Accidental &, + Accidentals::NoAccidentalStrategy); + + static void displayPitchToRawPitch + (int, Accidental, const Clef &, const Key &, + int &, bool ignoreOffset = false); +}; + + + +class TimeSignature; + + +/** + * The Note class represents note durations only, not pitch or + * accidental; it's therefore just as relevant to rest events as to + * note events. You can construct one of these from either. + */ + +class Note +{ +public: + static const std::string EventType; + static const std::string EventRestType; + static const int EventRestSubOrdering; + + typedef int Type; // not an enum, too much arithmetic at stake + + // define both sorts of names; some people prefer the American + // names, but I just can't remember which of them is which + + static const Type + + SixtyFourthNote = 0, + ThirtySecondNote = 1, + SixteenthNote = 2, + EighthNote = 3, + QuarterNote = 4, + HalfNote = 5, + WholeNote = 6, + DoubleWholeNote = 7, + + Hemidemisemiquaver = 0, + Demisemiquaver = 1, + Semiquaver = 2, + Quaver = 3, + Crotchet = 4, + Minim = 5, + Semibreve = 6, + Breve = 7, + + Shortest = 0, + Longest = 7; + + + /** + * Create a Note object of the given type, representing a + * particular sort of duration. Note objects are strictly + * durational; they don't represent pitch, and may be as + * relevant to rests as actual notes. + */ + Note(Type type, int dots = 0) : + m_type(type < Shortest ? Shortest : + type > Longest ? Longest : + type), + m_dots(dots) { } + + Note(const Note &n) : m_type(n.m_type), m_dots(n.m_dots) { } + ~Note() { } + + Note &operator=(const Note &n); + + Type getNoteType() const { return m_type; } + int getDots() const { return m_dots; } + + /** + * Return the duration of this note type. + */ + timeT getDuration() const { + return m_dots ? getDurationAux() : (m_shortestTime * (1 << m_type)); + } + + /** + * Return the Note whose duration is closest to (but shorter than or + * equal to) the given duration, permitting at most maxDots dots. + */ + static Note getNearestNote(timeT duration, int maxDots = 2); + + /// Returned event is on heap; caller takes responsibility for ownership + Event *getAsNoteEvent(timeT absoluteTime, int pitch) const; + + /// Returned event is on heap; caller takes responsibility for ownership + Event *getAsRestEvent(timeT absoluteTime) const; + + +private: + Type m_type; + int m_dots; + + timeT getDurationAux() const; + + // a time & effort saving device; if changing this, change + // TimeSignature::m_crotchetTime etc too + static const timeT m_shortestTime; +}; + + + +/** + * TimeSignature contains arithmetic methods relevant to time + * signatures and bar durations, including code for splitting long + * rest intervals into bite-sized chunks. Although there is a time + * signature Event type, these Events don't appear in regular Segments + * but only in the Composition's reference segment. + */ + +class TimeSignature +{ +public: + static const TimeSignature DefaultTimeSignature; + typedef Exception BadTimeSignature; + + TimeSignature() : + m_numerator(DefaultTimeSignature.m_numerator), + m_denominator(DefaultTimeSignature.m_denominator), + m_common(false), m_hidden(false), m_hiddenBars(false) { } + + /** + * Construct a TimeSignature object describing a time signature + * with the given numerator and denominator. If preferCommon is + * true and the time signature is a common or cut-common time, the + * constructed object will return true for isCommon; if hidden is + * true, the time signature is intended not to be displayed and + * isHidden will return true; if hiddenBars is true, the bar lines + * between this time signature and the next will not be shown. + */ + TimeSignature(int numerator, int denominator, + bool preferCommon = false, + bool hidden = false, + bool hiddenBars = false) + /* throw (BadTimeSignature) */; + + TimeSignature(const TimeSignature &ts) : + m_numerator(ts.m_numerator), + m_denominator(ts.m_denominator), + m_common(ts.m_common), + m_hidden(ts.m_hidden), + m_hiddenBars(ts.m_hiddenBars) { } + + ~TimeSignature() { } + + TimeSignature &operator=(const TimeSignature &ts); + + bool operator==(const TimeSignature &ts) const { + return ts.m_numerator == m_numerator && ts.m_denominator == m_denominator; + } + bool operator!=(const TimeSignature &ts) const { + return !operator==(ts); + } + + int getNumerator() const { return m_numerator; } + int getDenominator() const { return m_denominator; } + + bool isCommon() const { return m_common; } + bool isHidden() const { return m_hidden; } + bool hasHiddenBars() const { return m_hiddenBars; } + + timeT getBarDuration() const; + + /** + * Return the unit of the time signature. This is the note + * implied by the denominator. For example, the unit of 4/4 time + * is the crotchet, and that of 6/8 is the quaver. (The numerator + * of the time signature gives the number of units per bar.) + */ + Note::Type getUnit() const; + + /** + * Return the duration of the unit of the time signature. + * See also getUnit(). In most cases getBeatDuration() gives + * a more meaningful value. + */ + timeT getUnitDuration() const; + + /** + * Return true if this time signature indicates dotted time. + */ + bool isDotted() const; + + /** + * Return the duration of the beat of the time signature. For + * example, the beat of 4/4 time is the crotchet, the same as its + * unit, but that of 6/8 is the dotted crotchet (there are only + * two beats in a 6/8 bar). The beat therefore depends on whether + * the signature indicates dotted or undotted time. + */ + timeT getBeatDuration() const; + + /** + * Return the number of beats in a complete bar. + */ + int getBeatsPerBar() const { + return getBarDuration() / getBeatDuration(); + } + + /** + * Get the "optimal" list of rest durations to make up a bar in + * this time signature. + */ + void getDurationListForBar(DurationList &dlist) const; + + /** + * Get the "optimal" list of rest durations to make up a time + * interval of the given total duration, starting at the given + * offset after the start of a bar, assuming that the interval + * is entirely in this time signature. + */ + void getDurationListForInterval(DurationList &dlist, + timeT intervalDuration, + timeT startOffset = 0) const; + + /** + * Get the level of emphasis for a position in a bar. 4 is lots + * of emphasis, 0 is none. + */ + int getEmphasisForTime(timeT offset); + + /** + * Return a list of divisions, subdivisions, subsubdivisions + * etc of a bar in this time, up to the given depth. For example, + * if the time signature is 6/8 and the depth is 3, return a list + * containing 2, 3, and 2 (there are 2 beats to the bar, each of + * which is best subdivided into 3 subdivisions, each of which + * divides most neatly into 2). + */ + void getDivisions(int depth, std::vector<int> &divisions) const; + +private: + friend class Composition; + friend class TimeTempoSelection; + + TimeSignature(const Event &e) + /* throw (Event::NoData, Event::BadType, BadTimeSignature) */; + + static const std::string EventType; + static const int EventSubOrdering; + static const PropertyName NumeratorPropertyName; + static const PropertyName DenominatorPropertyName; + static const PropertyName ShowAsCommonTimePropertyName; + static const PropertyName IsHiddenPropertyName; + static const PropertyName HasHiddenBarsPropertyName; + + /// Returned event is on heap; caller takes responsibility for ownership + Event *getAsEvent(timeT absoluteTime) const; + +private: + int m_numerator; + int m_denominator; + + bool m_common; + bool m_hidden; + bool m_hiddenBars; + + mutable int m_barDuration; + mutable int m_beatDuration; + mutable int m_beatDivisionDuration; + mutable bool m_dotted; + void setInternalDurations() const; + + // a time & effort saving device + static const timeT m_crotchetTime; + static const timeT m_dottedCrotchetTime; +}; + + + +/** + * AccidentalTable represents a set of accidentals in force at a + * given time. + * + * Keep an AccidentalTable variable on-hand as you track through a + * staff; then when reading a chord, call processDisplayAccidental + * on the accidentals found in the chord to obtain the actual + * displayed accidentals and to tell the AccidentalTable to + * remember the accidentals that have been found in the chord. + * Then when the chord ends, call update() on the AccidentalTable + * so that that chord's accidentals are taken into account for the + * next one. + * + * Create a new AccidentalTable whenever a new key is encountered, + * and call newBar() or newClef() when a new bar happens or a new + * clef is encountered. + */ +class AccidentalTable +{ +public: + enum OctaveType { + OctavesIndependent, // if c' and c'' sharp, mark them both sharp + OctavesCautionary, // if c' and c'' sharp, put the second one in brackets + OctavesEquivalent // if c' and c'' sharp, only mark the first one + }; + + enum BarResetType { + BarResetNone, // c# | c -> omit natural + BarResetCautionary, // c# | c -> add natural to c in brackets + BarResetExplicit // c# | c -> add natural to c + }; + + AccidentalTable(const Key &, const Clef &, + OctaveType = OctavesCautionary, + BarResetType = BarResetCautionary); + + AccidentalTable(const AccidentalTable &); + AccidentalTable &operator=(const AccidentalTable &); + + Accidental processDisplayAccidental(const Accidental &displayAcc, + int heightOnStaff, + bool &cautionary); + + void update(); + + void newBar(); + void newClef(const Clef &); + +private: + Key m_key; + Clef m_clef; + OctaveType m_octaves; + BarResetType m_barReset; + + struct AccidentalRec { + AccidentalRec() : accidental(Accidentals::NoAccidental), previousBar(false) { } + AccidentalRec(Accidental a, bool p) : accidental(a), previousBar(p) { } + Accidental accidental; + bool previousBar; + }; + + typedef std::map<int, AccidentalRec> AccidentalMap; + + AccidentalMap m_accidentals; + AccidentalMap m_canonicalAccidentals; + + AccidentalMap m_newAccidentals; + AccidentalMap m_newCanonicalAccidentals; +}; + + +} + + +#endif |