From: <wha...@us...> - 2013-08-29 05:03:27
|
Revision: 8773 http://sourceforge.net/p/planeshift/code/8773 Author: whacko88 Date: 2013-08-29 05:03:25 +0000 (Thu, 29 Aug 2013) Log Message: ----------- updated header of musicutil files and converted psMusic to a namespace Modified Paths: -------------- trunk/src/common/music/musicutil.cpp trunk/src/common/music/musicutil.h Modified: trunk/src/common/music/musicutil.cpp =================================================================== --- trunk/src/common/music/musicutil.cpp 2013-08-29 02:19:42 UTC (rev 8772) +++ trunk/src/common/music/musicutil.cpp 2013-08-29 05:03:25 UTC (rev 8773) @@ -1,7 +1,7 @@ /* - * music.cpp, Author: Andrea Rizzi <88w...@gm...> + * musicutil.cpp, Author: Andrea Rizzi <88w...@gm...> * - * Copyright (C) 2001-2011 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or Modified: trunk/src/common/music/musicutil.h =================================================================== --- trunk/src/common/music/musicutil.h 2013-08-29 02:19:42 UTC (rev 8772) +++ trunk/src/common/music/musicutil.h 2013-08-29 05:03:25 UTC (rev 8773) @@ -1,7 +1,7 @@ /* - * music.h, Author: Andrea Rizzi <88w...@gm...> + * musicutil.h, Author: Andrea Rizzi <88w...@gm...> * - * Copyright (C) 2001-2011 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or @@ -17,8 +17,8 @@ * */ -#ifndef PSMUSIC_H -#define PSMUSIC_H +#ifndef MUSIC_UTIL_H +#define MUSIC_UTIL_H //==================================================================================== @@ -29,7 +29,7 @@ #include <iutil/document.h> /** - * \addtogroup common_util + * \addtogroup common_music * @{ */ /** @@ -208,105 +208,105 @@ /** - * This class contains a set of functions that are usefull for the processing of music + * This namespace contains a set of functions that are usefull for the processing of music * and musical scores. */ -class psMusic +namespace psMusic { -public: - /** - * Turns the given pitch into the next one in the scale. - * - * @param pitch the pitch of the note. - * @param octave the octave of the note. - */ - static void NextPitch(char &pitch, uint &octave); - /** - * Turns the given pitch into the previous one in the scale. - * - * @param pitch the pitch of the note. - * @param octave the octave of the note. - */ - static void PreviousPitch(char &pitch, uint &octave); +/** + * Turns the given pitch into the next one in the scale. + * + * @param pitch the pitch of the note. + * @param octave the octave of the note. + */ +void NextPitch(char &pitch, uint &octave); - /** - * Turns the given pitch into the enharmonic equivalent. - * @param pitch the pitch of the note. - * @param accidental the alteration of the note. - */ - static void EnharmonicPitch(char &pitch, int &accidental); +/** + * Turns the given pitch into the previous one in the scale. + * + * @param pitch the pitch of the note. + * @param octave the octave of the note. + */ +void PreviousPitch(char &pitch, uint &octave); - /** - * Gets the XML nodes representing the measures contained in the musical score. - * - * @param score the musical score. - * @param measures a reference to a csRefArray that will contain the XML nodes. - * @return true if the document is a valid musical score, false otherwise. - */ - static bool GetMeasures(csRef<iDocument> score, csRefArray<iDocumentNode> &measures); +/** + * Turns the given pitch into the enharmonic equivalent. + * @param pitch the pitch of the note. + * @param accidental the alteration of the note. + */ +void EnharmonicPitch(char &pitch, int &accidental); - /** - * Returns the statistics of the score. - * - * @param musicalScore the musical score. - * @param stats the retrieved statistics of the given score. - * @return true if the document is a valid musical score, false otherwise. - */ - static bool GetStatistics(csRef<iDocument> musicalScore, ScoreStatistics &stats); +/** + * Gets the XML nodes representing the measures contained in the musical score. + * + * @param score the musical score. + * @param measures a reference to a csRefArray that will contain the XML nodes. + * @return true if the document is a valid musical score, false otherwise. + */ +bool GetMeasures(csRef<iDocument> score, csRefArray<iDocumentNode> &measures); - /** - * Gets the attributes in the first measure of the given score. - * - * The provided attributes are always valid. If the musical score contains non valid attributes - * (e.g. beatType <= 0), they are set to a valid default value. - * - * @param musicalScore the musical score. - * @param quarterDivisions the number of divisions in a quarter for the score. - * @param fifths the tonality of the score. - * @param beats beats of the song. - * @param beatType the beat type of the song. - * @param tempo the beat per minutes of the song. - * @return true if the document is a valid musical score and the attributes could be - * found in the first measure, false otherwise. False is returned also if there are - * not measures in the given score. - */ - static bool GetAttributes(csRef<iDocument> musicalScore, int &quarterDivisions, - int &fifths, int &beats, int &beatType, int &tempo); +/** + * Returns the statistics of the score. + * + * @param musicalScore the musical score. + * @param stats the retrieved statistics of the given score. + * @return true if the document is a valid musical score, false otherwise. + */ +bool GetStatistics(csRef<iDocument> musicalScore, ScoreStatistics &stats); - /** - * Compress a song with the zlib compression algorithm. - * - * @attention the output string is not a normal string but a sequence of bytes. It - * could contain null characters. Do not treat it like a null terminated string. - * - * @param inputScore the musical sheet in uncompressed XML format. - * @param outputScore at the end this string will contain the compressed score. - * @return true if the compression is done without errors, false otherwise. - */ - static bool ZCompressSong(const csString &inputScore, csString &outputScore); +/** + * Gets the attributes in the first measure of the given score. + * + * The provided attributes are always valid. If the musical score contains non valid attributes + * (e.g. beatType <= 0), they are set to a valid default value. + * + * @param musicalScore the musical score. + * @param quarterDivisions the number of divisions in a quarter for the score. + * @param fifths the tonality of the score. + * @param beats beats of the song. + * @param beatType the beat type of the song. + * @param tempo the beat per minutes of the song. + * @return true if the document is a valid musical score and the attributes could be + * found in the first measure, false otherwise. False is returned also if there are + * not measures in the given score. + */ +bool GetAttributes(csRef<iDocument> musicalScore, int &quarterDivisions, + int &fifths, int &beats, int &beatType, int &tempo); - /** - * Decompress a song with the zlib compression algorithm. - * - * @param inputScore the compressed musical sheet. - * @param outputScore at the end this string will contain the uncompressed score. - * @return true if the decompression is done without errors, false otherwise. - */ - static bool ZDecompressSong(const csString &inputScore, csString &outputScore); +/** + * Compress a song with the zlib compression algorithm. + * + * @attention the output string is not a normal string but a sequence of bytes. It + * could contain null characters. Do not treat it like a null terminated string. + * + * @param inputScore the musical sheet in uncompressed XML format. + * @param outputScore at the end this string will contain the compressed score. + * @return true if the compression is done without errors, false otherwise. + */ +bool ZCompressSong(const csString &inputScore, csString &outputScore); -private: - /** - * Checks if the given document is a valid musical score and provide the \<part\> node. - * - * @param musicalScore the musical score. - * @param partNode a reference that will contain the part XML node. - * @return true if the document is valid, false otherwise. - */ - static bool CheckValidity(csRef<iDocument> musicalScore, csRef<iDocumentNode> &partNode); -}; +/** + * Decompress a song with the zlib compression algorithm. + * + * @param inputScore the compressed musical sheet. + * @param outputScore at the end this string will contain the uncompressed score. + * @return true if the decompression is done without errors, false otherwise. + */ +bool ZDecompressSong(const csString &inputScore, csString &outputScore); +/** + * Checks if the given document is a valid musical score and provide the \<part\> node. + * + * @param musicalScore the musical score. + * @param partNode a reference that will contain the part XML node. + * @return true if the document is valid, false otherwise. + */ +bool CheckValidity(csRef<iDocument> musicalScore, csRef<iDocumentNode> &partNode); + +} + /** @} */ -#endif // PSMUSIC_H +#endif // MUSIC_UTIL_H This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2013-08-29 20:47:53
|
Revision: 8774 http://sourceforge.net/p/planeshift/code/8774 Author: whacko88 Date: 2013-08-29 20:47:50 +0000 (Thu, 29 Aug 2013) Log Message: ----------- added all files I have in mind for the new music library so that next msvc project generation will include them Added Paths: ----------- trunk/src/common/music/basemusicscore.cpp trunk/src/common/music/basemusicscore.h trunk/src/common/music/musicxmlscore.cpp trunk/src/common/music/musicxmlscore.h trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Added: trunk/src/common/music/basemusicscore.cpp =================================================================== --- trunk/src/common/music/basemusicscore.cpp (rev 0) +++ trunk/src/common/music/basemusicscore.cpp 2013-08-29 20:47:50 UTC (rev 8774) @@ -0,0 +1,35 @@ +/* + * basemusicscore.cpp, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ Property changes on: trunk/src/common/music/basemusicscore.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: trunk/src/common/music/basemusicscore.h =================================================================== --- trunk/src/common/music/basemusicscore.h (rev 0) +++ trunk/src/common/music/basemusicscore.h 2013-08-29 20:47:50 UTC (rev 8774) @@ -0,0 +1,41 @@ +/* + * basemusicscore.h, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef BASE_MUSIC_SCORE_H +#define BASE_MUSIC_SCORE_H + + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ + + +#endif // BASE_MUSIC_SCORE_H Property changes on: trunk/src/common/music/basemusicscore.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: trunk/src/common/music/musicxmlscore.cpp =================================================================== --- trunk/src/common/music/musicxmlscore.cpp (rev 0) +++ trunk/src/common/music/musicxmlscore.cpp 2013-08-29 20:47:50 UTC (rev 8774) @@ -0,0 +1,35 @@ +/* + * musicxmlscore.cpp, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ Property changes on: trunk/src/common/music/musicxmlscore.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: trunk/src/common/music/musicxmlscore.h =================================================================== --- trunk/src/common/music/musicxmlscore.h (rev 0) +++ trunk/src/common/music/musicxmlscore.h 2013-08-29 20:47:50 UTC (rev 8774) @@ -0,0 +1,41 @@ +/* + * musicxmlscore.h, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef MUSIC_XML_SCORE_H +#define MUSIC_XML_SCORE_H + + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ + + +#endif // MUSIC_XML_SCORE_H Property changes on: trunk/src/common/music/musicxmlscore.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp (rev 0) +++ trunk/src/common/music/scoreelements.cpp 2013-08-29 20:47:50 UTC (rev 8774) @@ -0,0 +1,35 @@ +/* + * scoreelements.cpp, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ Property changes on: trunk/src/common/music/scoreelements.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h (rev 0) +++ trunk/src/common/music/scoreelements.h 2013-08-29 20:47:50 UTC (rev 8774) @@ -0,0 +1,52 @@ +/* + * scoreelements.h, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#ifndef SCORE_ELEMENTS_H +#define SCORE_ELEMENTS_H + + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ + + +/** + * A single note in a musical score. + */ +class Note +{ +private: + char name; ///< Name of the note (C, D, E, F, G, A or B). + short int octave; ///< Octave number. C4 is the central C of the piano. + Accidental accidental; ///< The accidental with which the note is represented. +}; + +#endif // SCORE_ELEMENTS_H Property changes on: trunk/src/common/music/scoreelements.h ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2013-09-01 20:23:11
|
Revision: 8780 http://sourceforge.net/p/planeshift/code/8780 Author: whacko88 Date: 2013-09-01 20:23:07 +0000 (Sun, 01 Sep 2013) Log Message: ----------- implemented notes and musical contexts Modified Paths: -------------- trunk/src/common/music/musicutil.h trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Modified: trunk/src/common/music/musicutil.h =================================================================== --- trunk/src/common/music/musicutil.h 2013-09-01 19:39:55 UTC (rev 8779) +++ trunk/src/common/music/musicutil.h 2013-09-01 20:23:07 UTC (rev 8780) @@ -24,6 +24,7 @@ //==================================================================================== // Crystal Space Includes //==================================================================================== +#include <cssysdef.h> #include <csutil/refarr.h> #include <csutil/csstring.h> #include <iutil/document.h> @@ -214,6 +215,16 @@ namespace psMusic { +enum Accidental +{ + NO_ACCIDENTAL, + DOUBLE_FLAT, + FLAT, + NATURAL, + SHARP, + DOUBLE_SHARP +}; + /** * Turns the given pitch into the next one in the scale. * Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2013-09-01 19:39:55 UTC (rev 8779) +++ trunk/src/common/music/scoreelements.cpp 2013-09-01 20:23:07 UTC (rev 8780) @@ -17,6 +17,7 @@ * */ +#include "scoreelements.h" //==================================================================================== // Crystal Space Includes @@ -33,3 +34,176 @@ //------------------------------------------------------------------------------------ // Forward Declarations //------------------------------------------------------------------------------------ + +// 10 octaves are more than enough and each octave contains maximum +// 7 notes so there is really no need of a bigger hash table +#define PREV_ACCIDENTALS_NAME_SIZE 7 +#define PREV_ACCIDENTALS_OCTAVE_SIZE 10 + +//-------------------------------------------------- + +Note::NoteContext::NoteContext() +: prevAccidentals(PREV_ACCIDENTALS_OCTAVE_SIZE) +{ +} + +Accidental Note::NoteContext::GetPreviousAccidental(const Note ¬e) const +{ + Accidental prevAccidental = NO_ACCIDENTAL; + const csHash<Accidental, char>* nameHash = + prevAccidentals.GetElementPointer(note.octave); + + if(nameHash != 0) + { + prevAccidental = nameHash->Get(note.name, NO_ACCIDENTAL); + } + return prevAccidental; +} + +void Note::NoteContext::UpdateContext(const Note ¬e) +{ + if(note.accidental != NO_ACCIDENTAL) + { + csHash<Accidental, char>* nameHash = + prevAccidentals.GetElementPointer(note.octave); + + if(nameHash == 0) + { + nameHash = &prevAccidentals.Put(note.octave, + csHash<Accidental, char>::csHash(PREV_ACCIDENTALS_NAME_SIZE)); + nameHash->Put(note.name, note.accidental); + } + else + { + nameHash->PutUnique(note.name, note.accidental); + } + } +} + +void Note::NoteContext::ResetContext() +{ + // we delete only the internal hash elements so that we don't have to reallocate + // the memory later, octaves are more or less the same in the whole piece anyway + csHash<Accidental, char> *nameHash; + csHash<csHash<Accidental, char>, int>::GlobalIterator + nameIter(prevAccidentals.GetIterator()); + + while(nameIter.HasNext()) + { + nameHash = &nameIter.Next(); + nameHash->DeleteAll(); + } +} + +Note::Note(char name_, int octave_, Accidental accidental_) +: name(name_), octave(octave_), accidental(accidental_) +{ + CS_ASSERT(name >= 'A' && name <= 'G'); +} + +Accidental Note::GetPlayedAccidental(const ScoreContext &context) const +{ + Accidental prevAccidental; + int fifths = context.prevAttributes.fifths; // convenience variable + + CS_ASSERT(fifths >= -7 && fifths <= 7); + + // accidentals written for this note have priority + if(accidental != NO_ACCIDENTAL) + { + return accidental; + } + + // then accidentals written on previous note with same name and octave + prevAccidental = context.noteContext.GetPreviousAccidental(*this); + if(prevAccidental != NO_ACCIDENTAL) + { + return prevAccidental; + } + + // finally the tonality decide + switch(name) + { + case 'A': + if(fifths <= -3) + { + return FLAT; + } + else if(fifths >= 5) + { + return SHARP; + } + break; + case 'B': + if(fifths <= -1) + { + return FLAT; + } + else if(fifths >= 7) + { + return SHARP; + } + break; + case 'C': + if(fifths <= -6) + { + return FLAT; + } + else if(fifths >= 2) + { + return SHARP; + } + break; + case 'D': + if(fifths <= -4) + { + return FLAT; + } + else if(fifths >= 4) + { + return SHARP; + } + break; + case 'E': + if(fifths <= -2) + { + return FLAT; + } + else if(fifths >= 6) + { + return SHARP; + } + break; + case 'F': + if(fifths <= -7) + { + return FLAT; + } + else if(fifths >= 1) + { + return SHARP; + } + break; + case 'G': + if(fifths <= -5) + { + return FLAT; + } + else if(fifths >= 3) + { + return SHARP; + } + break; + } + + return NO_ACCIDENTAL; +} + +//-------------------------------------------------- + +Measure::MeasureAttributes::MeasureAttributes() +: tempo(UNDEFINED_MEASURE_ATTRIBUTE), beats(UNDEFINED_MEASURE_ATTRIBUTE), +beatType(UNDEFINED_MEASURE_ATTRIBUTE), quarterDivisions(UNDEFINED_MEASURE_ATTRIBUTE), +fifths(UNDEFINED_MEASURE_ATTRIBUTE) +{ +} Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2013-09-01 19:39:55 UTC (rev 8779) +++ trunk/src/common/music/scoreelements.h 2013-09-01 20:23:07 UTC (rev 8780) @@ -24,6 +24,8 @@ //==================================================================================== // Crystal Space Includes //==================================================================================== +#include <cssysdef.h> +#include <csutil/hash.h> //==================================================================================== // Project Includes @@ -32,21 +34,181 @@ //==================================================================================== // Local Includes //==================================================================================== +#include "musicutil.h" //------------------------------------------------------------------------------------ // Forward Declarations //------------------------------------------------------------------------------------ +#define UNDEFINED_MEASURE_ATTRIBUTE -100 +struct ScoreContext; +using namespace psMusic; + /** + * \addtogroup common_music + * @{ */ + +//-------------------------------------------------- + +/** * A single note in a musical score. */ class Note { +public: + /** + * Used to keep track of previous altered notes in the current measure. Hides the + * implementation of note context updating to other classes. + */ + class NoteContext + { + public: + /** + * Constructor. + */ + NoteContext(); + + /** + * Get an eventual accidental that previously appeared in the same measure. + * + * @param note The note that should be checked. + * @return The accidental in case there where previous altered notes or + * NO_ACCIDENTAL if none. + */ + Accidental GetPreviousAccidental(const Note ¬e) const; + + /** + * Update the list of previous accidental if the given note has any. + * + * @param note The current played note. + */ + void UpdateContext(const Note &context); + + /** + * Empty the list of previously altered note. + */ + void ResetContext(); + + private: + /** + * Previous notes with a written accidental. Accidentals are indexed by note octave + * and name. Must be resetted at every measure and updated at every note. + */ + csHash<csHash<Accidental, char>, int> prevAccidentals; + }; + + /** + * Constructor. + * + * @param name Name of the note (C, D, E, F, G, A or B). Must be uppercase. + * @param octave Octave number. Octave number 4 is the one that contains the central + * C on the piano. + * @param accidental The accidental with which the note is written on the score. This + * is not related to the tonality of the piece but only on its representation. In + * other words the variable accidental must be different than UNALTERED if and only if + * an actual accidental is written on the musical score. + */ + Note(char name, int octave, Accidental accidental); + + /** + * Get the note name. + * + * @return The uppercase name of the note (C, D, E, F, G, A or B). + */ + char GetName() const { return name; } + + /** + * Get the note octave number. + * + * @return The octave number of this note. Octave number 4 is the one that contains + * the central C on the piano. + */ + int GetOctave() const { return octave; } + + /** + * Get the accidental of the played note. This, depending on the tonality, may not be + * the same as the written accidental. + * + * @param context The context where tonality and previous accidentals are kept. + * @return The accidental with which the note must be played in the context of the + * tonality of the piece. An eventual accidental written on the score have the + * priority. + */ + Accidental GetPlayedAccidental(const ScoreContext &context) const; + private: - char name; ///< Name of the note (C, D, E, F, G, A or B). - short int octave; ///< Octave number. C4 is the central C of the piano. - Accidental accidental; ///< The accidental with which the note is represented. + char name; ///< Uppercase name of the note (C, D, E, F, G, A or B). + int octave; ///< Octave number. C4 is the central C on piano. + + /** + * The accidental with which the note is written on the score. It does not have + * anything to do with the tonality. With this representation, one does not have to + * modify all the notes in the score when changing the tonality of the piece. + */ + Accidental accidental; }; +//-------------------------------------------------- + +class Chord +{ +}; + +//-------------------------------------------------- + +class Measure +{ +public: + /** + * Keep general attributes that can change from a measure to another like key signature, + * beats and tempo. Some attributes can be undefined. This means that the specific + * attribute takes the same value as in the previous measure. Undefined attributes take + * the value UNDEFINED_MEASURE_ATTRIBUTE. + */ + struct MeasureAttributes + { + int tempo; ///< Quarter notes per minute (BPM). + int beats; ///< Numerator of the time signature. + int beatType; ///< Denominator of the time signature. + + /** + * Unit of measure for notes duration in terms of divisions per quarter. + */ + int quarterDivisions; + + /** + * Number of accidentals in the key signature. Positive for sharps, negative for + * flats. + */ + int fifths; + + MeasureAttributes(); + }; +}; + +//-------------------------------------------------- + +/** + * Keep track of previous accidentals in the same measure and attributes of previous + * measures when they are not specified in the current one. This struct make sense only + * when it refers to a specific point in the musical score. + */ +struct ScoreContext +{ + // TODO how to handle repeats for measure attributes? + + /** + * Used to keep track of previous accidentals in the same measure. + */ + Note::NoteContext noteContext; + + /** + * Attributes specified in the score up to now. Must be updated at every measure. + */ + Measure::MeasureAttributes prevAttributes; +}; + +/** @} */ + #endif // SCORE_ELEMENTS_H This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2013-09-04 23:34:38
|
Revision: 8781 http://sourceforge.net/p/planeshift/code/8781 Author: whacko88 Date: 2013-09-04 23:34:36 +0000 (Wed, 04 Sep 2013) Log Message: ----------- context as nested classes, implemented measure elements Modified Paths: -------------- trunk/src/common/music/musicutil.h trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Modified: trunk/src/common/music/musicutil.h =================================================================== --- trunk/src/common/music/musicutil.h 2013-09-01 20:23:07 UTC (rev 8780) +++ trunk/src/common/music/musicutil.h 2013-09-04 23:34:36 UTC (rev 8781) @@ -215,6 +215,29 @@ namespace psMusic { +/** + * Unit measure for duration in in terms of divisions per quarter. If you change this + * constants you must change the values in the enum Duration too. + */ +#define DURATION_QUARTER_DIVISIONS 16 // unit measure is a sixteenth + +/** + * The number associated to each duration is the number of quarter divisions as specified + * in DURATION_QUARTER_DIVISIONS. + */ +enum Duration +{ + SIXTEENTH_DURATION = 1, + EIGHTH_DURATION = 2, + DOTTED_EIGHTH_DURATION = 3, + QUARTER_DURATION = 4, + DOTTED_QUARTER_DURATION = 6, + HALF_DURATION = 8, + DOTTED_HALF_DURATION = 12, + WHOLE_DURATION = 16, + DOTTED_WHOLE_DURATION = 24 +}; + enum Accidental { NO_ACCIDENTAL, Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2013-09-01 20:23:07 UTC (rev 8780) +++ trunk/src/common/music/scoreelements.cpp 2013-09-04 23:34:36 UTC (rev 8781) @@ -40,8 +40,25 @@ #define PREV_ACCIDENTALS_NAME_SIZE 7 #define PREV_ACCIDENTALS_OCTAVE_SIZE 10 +// 2 notes is the minimum for a chord and it very rarely gets over 5 notes +#define NOTES_IN_CAPACITY 5 + //-------------------------------------------------- +MeasureElement::MeasureElement(Duration duration_) +: duration(duration_) +{ +} + +//-------------------------------------------------- + +Rest::Rest(Duration duration) +: MeasureElement(duration) +{ +} + +//-------------------------------------------------- + Note::NoteContext::NoteContext() : prevAccidentals(PREV_ACCIDENTALS_OCTAVE_SIZE) { @@ -60,9 +77,24 @@ return prevAccidental; } +void Note::NoteContext::ResetContext() +{ + // we delete only the internal hash elements so that we don't have to reallocate + // the memory later, octaves are more or less the same in the whole piece anyway + csHash<Accidental, char> *nameHash; + csHash<csHash<Accidental, char>, int>::GlobalIterator + nameIter(prevAccidentals.GetIterator()); + + while(nameIter.HasNext()) + { + nameHash = &nameIter.Next(); + nameHash->DeleteAll(); + } +} + void Note::NoteContext::UpdateContext(const Note ¬e) { - if(note.accidental != NO_ACCIDENTAL) + if(note.writtenAccidental != NO_ACCIDENTAL) { csHash<Accidental, char>* nameHash = prevAccidentals.GetElementPointer(note.octave); @@ -71,36 +103,21 @@ { nameHash = &prevAccidentals.Put(note.octave, csHash<Accidental, char>::csHash(PREV_ACCIDENTALS_NAME_SIZE)); - nameHash->Put(note.name, note.accidental); + nameHash->Put(note.name, note.writtenAccidental); } else { - nameHash->PutUnique(note.name, note.accidental); + nameHash->PutUnique(note.name, note.writtenAccidental); } } } -void Note::NoteContext::ResetContext() +Note::Note(char name_, int octave_, Accidental writtenAccidental_, Duration duration_) +: MeasureElement(duration_), octave(octave_), writtenAccidental(writtenAccidental_) { - // we delete only the internal hash elements so that we don't have to reallocate - // the memory later, octaves are more or less the same in the whole piece anyway - csHash<Accidental, char> *nameHash; - csHash<csHash<Accidental, char>, int>::GlobalIterator - nameIter(prevAccidentals.GetIterator()); - - while(nameIter.HasNext()) - { - nameHash = &nameIter.Next(); - nameHash->DeleteAll(); - } + SetName(name_); } -Note::Note(char name_, int octave_, Accidental accidental_) -: name(name_), octave(octave_), accidental(accidental_) -{ - CS_ASSERT(name >= 'A' && name <= 'G'); -} - Accidental Note::GetPlayedAccidental(const ScoreContext &context) const { Accidental prevAccidental; @@ -109,9 +126,9 @@ CS_ASSERT(fifths >= -7 && fifths <= 7); // accidentals written for this note have priority - if(accidental != NO_ACCIDENTAL) + if(writtenAccidental != NO_ACCIDENTAL) { - return accidental; + return writtenAccidental; } // then accidentals written on previous note with same name and octave @@ -199,11 +216,70 @@ return NO_ACCIDENTAL; } +bool Note::operator==(const Note ¬e) const +{ + return this->name == note.name && this->octave == note.octave; +} + +void Note::SetName(char newName) +{ + name = newName; + + CS_ASSERT(name >= 'A' && name <= 'G'); +} + +void Note::SetOctave(int newOctave) +{ + octave = newOctave; +} + +void Note::SetWrittenAccidental(Accidental accidental) +{ + writtenAccidental = accidental; +} + //-------------------------------------------------- +Chord::Chord(Duration duration) +: MeasureElement(duration), notes(NOTES_IN_CAPACITY) +{ +} + +bool Chord::AddNote(char name, int octave, Accidental writtenAccidental) +{ + Note note(name, octave, writtenAccidental, GetDuration()); + size_t noteIdx = notes.Find(note); + if(noteIdx == csArrayItemNotFound) + { + notes.Push(note); + return true; + } + else + { + notes[noteIdx].SetWrittenAccidental(note.GetWrittenAccidental()); + return false; + } +} + +bool Chord::RemoveNote(char name, int octave) +{ + Note note(name, octave, NO_ACCIDENTAL, GetDuration()); + return notes.Delete(note); +} + +void Chord::SetDuration(Duration duration_) +{ + MeasureElement::SetDuration(duration_); + for(size_t i = 0; i < notes.GetSize(); i++) + { + notes[i].SetDuration(duration_); + } +} + +//-------------------------------------------------- + Measure::MeasureAttributes::MeasureAttributes() : tempo(UNDEFINED_MEASURE_ATTRIBUTE), beats(UNDEFINED_MEASURE_ATTRIBUTE), -beatType(UNDEFINED_MEASURE_ATTRIBUTE), quarterDivisions(UNDEFINED_MEASURE_ATTRIBUTE), -fifths(UNDEFINED_MEASURE_ATTRIBUTE) +beatType(UNDEFINED_MEASURE_ATTRIBUTE), fifths(UNDEFINED_MEASURE_ATTRIBUTE) { } Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2013-09-01 20:23:07 UTC (rev 8780) +++ trunk/src/common/music/scoreelements.h 2013-09-04 23:34:36 UTC (rev 8781) @@ -49,17 +49,67 @@ * \addtogroup common_music * @{ */ +/** + * An element of a measure with a given duration. It can be a rest, a single note or a + * chord. + */ +class MeasureElement +{ +public: + /** + * Constructor. + * + * @param duration The duration of this element. + */ + MeasureElement(Duration duration); + + /** + * Get the duration of this element. + * + * @return The duration of this element. + */ + Duration GetDuration() { return duration; } + + /** + * Set the duration of this element. + * + * @param newDuration the new duration for this element. + */ + virtual void SetDuration(Duration newDuration) { duration = newDuration; } + +private: + Duration duration; +}; + //-------------------------------------------------- /** + * Represent a rest in the score. The class does not have particular members but its + * extension could. + */ +class Rest: public MeasureElement +{ +public: + /** + * Constructor. + * + * @param duration The duration of this rest. + */ + Rest(Duration duration); +}; + +//-------------------------------------------------- + +/** * A single note in a musical score. */ -class Note +class Note: public MeasureElement { public: /** * Used to keep track of previous altered notes in the current measure. Hides the - * implementation of note context updating to other classes. + * implementation of note context updating to other classes. It must be resetted at + * every measure and updated at every note. */ class NoteContext { @@ -73,27 +123,28 @@ * Get an eventual accidental that previously appeared in the same measure. * * @param note The note that should be checked. - * @return The accidental in case there where previous altered notes or - * NO_ACCIDENTAL if none. + * @return The accidental of the previous note within the same measure having the + * same name and octave as the given one. In case no previous note satisfying the + * requirements is found, NO_ACCIDENTAL is returned. */ Accidental GetPreviousAccidental(const Note ¬e) const; /** + * Empty the list of previously altered note. + */ + void ResetContext(); + + /** * Update the list of previous accidental if the given note has any. * * @param note The current played note. */ void UpdateContext(const Note &context); - /** - * Empty the list of previously altered note. - */ - void ResetContext(); - private: /** * Previous notes with a written accidental. Accidentals are indexed by note octave - * and name. Must be resetted at every measure and updated at every note. + * and name. */ csHash<csHash<Accidental, char>, int> prevAccidentals; }; @@ -108,8 +159,9 @@ * is not related to the tonality of the piece but only on its representation. In * other words the variable accidental must be different than UNALTERED if and only if * an actual accidental is written on the musical score. + * @param duration The duration of this note. */ - Note(char name, int octave, Accidental accidental); + Note(char name, int octave, Accidental writtenAccidental, Duration duration); /** * Get the note name. @@ -127,16 +179,53 @@ int GetOctave() const { return octave; } /** - * Get the accidental of the played note. This, depending on the tonality, may not be - * the same as the written accidental. + * Get the accidental of the played note. This takes into account the written + * accidental, the tonality and previous accidental in the same measure. * * @param context The context where tonality and previous accidentals are kept. * @return The accidental with which the note must be played in the context of the - * tonality of the piece. An eventual accidental written on the score have the - * priority. + * tonality of the piece. An eventual accidental written on the score previously + * have the priority. */ Accidental GetPlayedAccidental(const ScoreContext &context) const; + /** + * Get the written accidental. + * + * @return The accidental written on the score for this note. + */ + Accidental GetWrittenAccidental() const { return writtenAccidental; } + + /** + * Equal operator. + * + * @param note The note to be compared with. + * @return True if the given note has the same name and octave. + */ + bool operator==(const Note ¬e) const; + + /** + * Set the note name. + * + * @param name The uppercase name of the note (C, D, E, F, G, A or B). + */ + void SetName(char name); + + /** + * Set the note octave number. + * + * @param newOctave The octave number of this note. Octave number 4 is the one that + * contains the central C on the piano. + */ + void SetOctave(int octave); + + /** + * Set the written accidental. + * + * @param accidental The accidental written on the score for this note. + */ + void SetWrittenAccidental(Accidental accidental); + private: char name; ///< Uppercase name of the note (C, D, E, F, G, A or B). int octave; ///< Octave number. C4 is the central C on piano. @@ -146,13 +235,63 @@ * anything to do with the tonality. With this representation, one does not have to * modify all the notes in the score when changing the tonality of the piece. */ - Accidental accidental; + Accidental writtenAccidental; }; //-------------------------------------------------- -class Chord +/** + * A group of notes that must be played together. + */ + class Chord: public MeasureElement { +public: + /** + * Constructor. + */ + Chord(Duration duration); + + /** + * Copy a note into the chord. If a note with the same name and octave already exists + * this will overwrite it or, in other words, its accidental will be overwritten. + * + * @param name Name of the note (C, D, E, F, G, A or B). Must be uppercase. + * @param octave Octave number. Octave number 4 is the one that contains the central + * C on the piano. + * @param accidental The accidental with which the note is written on the score. + * @return True if no note was overwritten, false otherwise. + */ + bool AddNote(char name, int octave, Accidental writtenAccidental); + + /** + * Remove all the notes from the chord without releasing the allocated memory. + */ + void Empty() { notes.Empty(); } + + /** + * Get the number of notes in this chord. + * + * @return the number of notes in this chord. + */ + size_t GetSize() const { return notes.GetSize(); } + + /** + * Delete a note with the same name and octave. + * + * @param note The note that must be deleted. + * @return True if the note was found, false otherwise. + */ + bool RemoveNote(char name, int octave); + + /** + * Set the duration of this element. + * + * @param duration the new duration for this element. + */ + void SetDuration(Duration duration); + +private: + csArray<Note> notes; ///< The sequence of notes in the chord. }; //-------------------------------------------------- @@ -173,11 +312,6 @@ int beatType; ///< Denominator of the time signature. /** - * Unit of measure for notes duration in terms of divisions per quarter. - */ - int quarterDivisions; - - /** * Number of accidentals in the key signature. Positive for sharps, negative for * flats. */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2013-09-10 22:45:00
|
Revision: 8787 http://sourceforge.net/p/planeshift/code/8787 Author: whacko88 Date: 2013-09-10 22:44:57 +0000 (Tue, 10 Sep 2013) Log Message: ----------- implemented measures Modified Paths: -------------- trunk/src/common/music/musicutil.h trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Modified: trunk/src/common/music/musicutil.h =================================================================== --- trunk/src/common/music/musicutil.h 2013-09-10 19:34:49 UTC (rev 8786) +++ trunk/src/common/music/musicutil.h 2013-09-10 22:44:57 UTC (rev 8787) @@ -215,6 +215,16 @@ namespace psMusic { +enum Accidental +{ + NO_ACCIDENTAL, + DOUBLE_FLAT, + FLAT, + NATURAL, + SHARP, + DOUBLE_SHARP +}; + /** * Unit measure for duration in in terms of divisions per quarter. If you change this * constants you must change the values in the enum Duration too. @@ -238,16 +248,6 @@ DOTTED_WHOLE_DURATION = 24 }; -enum Accidental -{ - NO_ACCIDENTAL, - DOUBLE_FLAT, - FLAT, - NATURAL, - SHARP, - DOUBLE_SHARP -}; - /** * Turns the given pitch into the next one in the scale. * Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2013-09-10 19:34:49 UTC (rev 8786) +++ trunk/src/common/music/scoreelements.cpp 2013-09-10 22:44:57 UTC (rev 8787) @@ -38,7 +38,7 @@ // 10 octaves are more than enough and each octave contains maximum // 7 notes so there is really no need of a bigger hash table #define PREV_ACCIDENTALS_NAME_SIZE 7 -#define PREV_ACCIDENTALS_OCTAVE_SIZE 10 +#define PREV_ACCIDENTALS_OCTAVE_SIZE 5 // 2 notes is the minimum for a chord and it very rarely gets over 5 notes #define NOTES_IN_CAPACITY 5 @@ -121,7 +121,7 @@ Accidental Note::GetPlayedAccidental(const ScoreContext &context) const { Accidental prevAccidental; - int fifths = context.prevAttributes.fifths; // convenience variable + int fifths = context.measureAttributes.GetFifths(); // convenience variable CS_ASSERT(fifths >= -7 && fifths <= 7); @@ -223,9 +223,8 @@ void Note::SetName(char newName) { + CS_ASSERT(newName >= 'A' && newName <= 'G'); name = newName; - - CS_ASSERT(name >= 'A' && name <= 'G'); } void Note::SetOctave(int newOctave) @@ -283,3 +282,272 @@ beatType(UNDEFINED_MEASURE_ATTRIBUTE), fifths(UNDEFINED_MEASURE_ATTRIBUTE) { } + +bool Measure::MeasureAttributes::IsUndefined() const +{ + return tempo == UNDEFINED_MEASURE_ATTRIBUTE && + beats == UNDEFINED_MEASURE_ATTRIBUTE && + beatType == UNDEFINED_MEASURE_ATTRIBUTE && + fifths == UNDEFINED_MEASURE_ATTRIBUTE; +} + +void Measure::MeasureAttributes::UpdateAttributes( + const Measure::MeasureAttributes &attributes) +{ + if(attributes.beats != UNDEFINED_MEASURE_ATTRIBUTE) + { + beats = attributes.beats; + } + if(attributes.beatType != UNDEFINED_MEASURE_ATTRIBUTE) + { + beatType = attributes.beatType; + } + if(attributes.fifths != UNDEFINED_MEASURE_ATTRIBUTE) + { + fifths = attributes.fifths; + } + if(attributes.tempo != UNDEFINED_MEASURE_ATTRIBUTE) + { + tempo = attributes.tempo; + } +} + +Measure::Measure() +: attributes(0), isEnding(false), isStartRepeat(false), nEndRepeat(0) +{ +} + +Measure::~Measure() +{ + DeleteAttributes(); + + DeleteAllElements(); +} + +void Measure::DeleteElement(size_t n) +{ + if(n < elements.GetSize()) + { + delete elements.Get(n); + elements.DeleteIndex(n); + } +} + +void Measure::DeleteAllElements() +{ + for(size_t i = 0; i < elements.GetSize(); i++) + { + delete elements.Get(i); + } + elements.Empty(); +} + +void Measure::Fit(const MeasureAttributes* attributes_) +{ + int measDuration = 0; // the duration that the measure is supposed to be + int currDuration = 0; // the sum of the duration of the elements + size_t cutIdx = 0; // the index contains the index of the first element to be cut + + // Convenience variables + int beats = UNDEFINED_MEASURE_ATTRIBUTE; + int beatType = UNDEFINED_MEASURE_ATTRIBUTE; + + // Getting beat information + if(attributes != 0) + { + beats = attributes->GetBeats(); + beatType = attributes->GetBeatType(); + } + if(attributes_ != 0 && (beats == UNDEFINED_MEASURE_ATTRIBUTE || + beatType == UNDEFINED_MEASURE_ATTRIBUTE)) + { + beats = attributes_->GetBeats(); + beatType = attributes_->GetBeatType(); + } + + CS_ASSERT(beats != UNDEFINED_MEASURE_ATTRIBUTE && + beatType != UNDEFINED_MEASURE_ATTRIBUTE); + + // Determining the measure duration + measDuration = DURATION_QUARTER_DIVISIONS * beats / beatType; + + // Determining which notes exceed the measure + while(cutIdx < elements.GetSize() && currDuration <= measDuration) + { + currDuration += elements.Get(cutIdx)->GetDuration(); + cutIdx++; + } + + // Cutting or filling + if(currDuration > measDuration) // cut + { + int prevDuration = currDuration - elements.Get(cutIdx)->GetDuration(); + + // If there's a note that exceeds the measure duration we can cut it first + if(prevDuration < measDuration) + { + Duration tempDuration = GetBiggestDuration(measDuration - prevDuration); + elements.Get(cutIdx)->SetDuration(tempDuration); + cutIdx++; + } + for(size_t i = elements.GetSize() - 1; i >= cutIdx; i--) + { + DeleteElement(i); + } + } + else if(currDuration < measDuration) // fill + { + Duration tempDuration; + MeasureElement* rest = 0; + + while(currDuration < measDuration) + { + tempDuration = GetBiggestDuration(measDuration - currDuration); + MeasureElement* rest = new Rest(tempDuration); + PushElement(rest); + currDuration += tempDuration; + } + } +} + +Measure::MeasureAttributes Measure::GetAttributes() const +{ + if(attributes == 0) + { + return MeasureAttributes(); // all attributes are undefined + } + return *attributes; +} + +void Measure::InsertElement(size_t n, MeasureElement* element) +{ + CS_ASSERT(element != 0); + if(!elements.Insert(n, element)) + { + PushElement(element); + } +} + +void Measure::PushElement(MeasureElement* element) +{ + CS_ASSERT(element != 0); + elements.Push(element); +} + +void Measure::SetBeat(int beats, int beatType) +{ + if(beats != UNDEFINED_MEASURE_ATTRIBUTE && beatType != UNDEFINED_MEASURE_ATTRIBUTE) + { + // check if beatType_ is a power of 2 + CS_ASSERT((beatType > 0) && ((beatType & (~beatType + 1)) == beatType)); + CS_ASSERT(beats > 0); + CreateAttributes(); + } + + // doesn't make sense if one is defined and the other is not + if(beats == UNDEFINED_MEASURE_ATTRIBUTE || beatType == UNDEFINED_MEASURE_ATTRIBUTE) + { + beats = UNDEFINED_MEASURE_ATTRIBUTE; + beatType = UNDEFINED_MEASURE_ATTRIBUTE; + } + + if(attributes != 0) + { + attributes->SetBeats(beats); + attributes->SetBeatType(beatType); + } + UpdateAttributes(); +} + +void Measure::SetEnding(bool isEnding_) +{ + isEnding = isEnding_; +} + +void Measure::SetFifths(int fifths) +{ + if(fifths != UNDEFINED_MEASURE_ATTRIBUTE) + { + CS_ASSERT(fifths >= -7 && fifths <= 7); + CreateAttributes(); + } + if(attributes != 0) + { + attributes->SetFifths(fifths); + } + UpdateAttributes(); +} + +void Measure::SetNEndRepeat(int nEndRepeat_) +{ + CS_ASSERT(nEndRepeat_ >= 0); + nEndRepeat = nEndRepeat_; +} + +void Measure::SetStartRepeat(bool isStartRepeat_) +{ + isStartRepeat = isStartRepeat_; +} + +void Measure::SetTempo(int tempo) +{ + if(tempo != UNDEFINED_MEASURE_ATTRIBUTE) + { + CS_ASSERT(tempo > 0); + CreateAttributes(); + } + if(attributes != 0) + { + attributes->SetTempo(tempo); + } + UpdateAttributes(); +} + +void Measure::CreateAttributes() +{ + if(attributes == 0) + { + attributes = new MeasureAttributes(); + } +} + +void Measure::DeleteAttributes() +{ + if(attributes != 0) + { + delete attributes; + attributes = 0; + } +} + +Duration Measure::GetBiggestDuration(int duration) const +{ + CS_ASSERT(duration > 0); + + if(DOTTED_WHOLE_DURATION <= duration) + return DOTTED_WHOLE_DURATION; + else if(WHOLE_DURATION <= duration) + return WHOLE_DURATION; + else if(DOTTED_HALF_DURATION <= duration) + return DOTTED_HALF_DURATION; + else if(HALF_DURATION <= duration) + return HALF_DURATION; + else if(DOTTED_QUARTER_DURATION <= duration) + return DOTTED_QUARTER_DURATION; + else if(QUARTER_DURATION <= duration) + return QUARTER_DURATION; + else if(DOTTED_EIGHTH_DURATION <= duration) + return DOTTED_EIGHTH_DURATION; + else if(EIGHTH_DURATION <= duration) + return EIGHTH_DURATION; + + return SIXTEENTH_DURATION; +} + +void Measure::UpdateAttributes() +{ + if(attributes->IsUndefined()) + { + DeleteAttributes(); + } +} Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2013-09-10 19:34:49 UTC (rev 8786) +++ trunk/src/common/music/scoreelements.h 2013-09-10 22:44:57 UTC (rev 8787) @@ -264,7 +264,7 @@ bool AddNote(char name, int octave, Accidental writtenAccidental); /** - * Remove all the notes from the chord without releasing the allocated memory. + * Remove all the notes from the chord. */ void Empty() { notes.Empty(); } @@ -299,14 +299,95 @@ class Measure { public: + /** - * Keep general attributes that can change from a measure to another like key signature, - * beats and tempo. Some attributes can be undefined. This means that the specific - * attribute takes the same value as in the previous measure. Undefined attributes take - * the value UNDEFINED_MEASURE_ATTRIBUTE. + * Keep general attributes that can change from a measure to another like key + * signature, beats and tempo. Some attributes can be undefined. This means that the + * specific attribute takes the same value of the same attribute in the previous + * measure. Undefined attributes take the value UNDEFINED_MEASURE_ATTRIBUTE. */ - struct MeasureAttributes + class MeasureAttributes { + public: + /** + * Initialize all attributes to UNDEFINED_MEASURE_ATTRIBUTE. + */ + MeasureAttributes(); + + /** + * Get the numerator of the time signature. + * + * @return The numerator of the time signature. + */ + int GetBeats() const { return beats; } + + /** + * Get the denominator of the time signature. + * + * @return The denominator of the time signature. + */ + int GetBeatType() const { return beatType; } + + /** + * Get the tonality as the number of accidentals in the key signature. + * + * @return The number of accidentals in the key signature. Positive for sharps, + * negative for flats. + */ + int GetFifths() const { return fifths; } + + /** + * Get the tempo. + * + * @return The tempo in quarter notes per minute. + */ + int GetTempo() const { return tempo; } + + /** + * Check if all attributes are undefined. + * + * @return True if all attributes are undefined, false otherwise. + */ + bool IsUndefined() const; + + /** + * Set the numerator of the time signature. + * + * @param beats The new numerator of the time signature. + */ + void SetBeats(int beats) { this->beats = beats; } + + /** + * Set the denominator of the time signature. + * + * @param beatType The new denominator of the time signature. + */ + void SetBeatType(int beatType) { this->beatType = beatType; } + + /** + * Set the key signature as the number of accidentals. + * + * @param fifths The number of accidentals in the key signature. Positive for + * sharps, negative for flats. + */ + void SetFifths(int fifths) { this->fifths = fifths; } + + /** + * Set the tempo. + * + * @param tempo The new tempo. + */ + void SetTempo(int tempo) { this->tempo = tempo; } + + /** + * Update the measure context with the attributes of the given measure. + * Attributes that are undefined in the given object are not updated. + * + * @param measure The measure with the new attributes. + */ + void UpdateAttributes(const MeasureAttributes &attributes); + + private: int tempo; ///< Quarter notes per minute (BPM). int beats; ///< Numerator of the time signature. int beatType; ///< Denominator of the time signature. @@ -316,9 +397,181 @@ * flats. */ int fifths; + }; - MeasureAttributes(); - }; + /** + * Constructor. + */ + Measure(); + + /** + * Destructor. + */ + ~Measure(); + + /** + * Delete the element in position n. If n is not a valid index it does nothing. + * + * @param n The index of the measure element. + */ + void DeleteElement(size_t n); + + /** + * Delete all elements from the measure. + */ + void DeleteAllElements(); + + /** + * Remove the elements at the end of the measures that exceeds the total duration or + * push a rest if there are not enough elements. The total duration is determined + * from the beat and beat type contained in the attributes. Attributes specified in + * this measure have priority. If they are undefined, the given attributes are used + * instead. + * + * @param attributes Attributes of previous measures. This can be a null pointer if + * this measure specifies both beat and beat type. If this is not the case however, + * the parameter must specify at least information about beat and beat type. + */ + void Fit(const MeasureAttributes* attributes); + + /** + * Get a copy of the attributes of this measure. + * + * @return The attributes of this measure. + */ + MeasureAttributes GetAttributes() const; + + /** + * Get the element at position n. + * + * @param n The index of the element. + * @return The element or 0 if n is not a valid index. + */ + MeasureElement* GetElement(size_t n) { return elements.Get(n); } + + /** + * Get the number of elements in this measure. + * + * @return The number of elements in this measure. + */ + int GetNElements() { return elements.GetSize(); } + + /** + * Get the number of time the repeat at the end of this measure must be performed. + * + * @return The number of time the repeat must be performed or 0 if there is not a + * repeat at the end of the measure. + */ + int GetNEndRepeat() const { return nEndRepeat; } + + /** + * Insert the given element after element n. The element will be deallocated when the + * measure is deleted. If n is greater than the number of elements in this measure, + * the element is pushed at the end. + * + * @param n The number of the element after which the given element must be inserted. + * @param element The element to insert in the measure. + */ + void InsertElement(size_t n, MeasureElement* element); + + /** + * Return true if this is an ending measure. + * + * @return True if this is an ending measure, false otherwise. + */ + bool IsEnding() const { return isEnding; } + + /** + * Return true if this measure starts a repeat. + * + * @return True if this is the start of a repeat, false otherwise. + */ + bool IsStartRepeat() const { return isStartRepeat; } + + /** + * Push an element after all the elements currently in the measure. The element will + * be deallocated when the measure is deleted. + * + * @param element The element to push in the measure. + */ + void PushElement(MeasureElement* element); + + /** + * Set the beat information. Both beats and beatType must be defined. If only one of + * them is undefined, both of them will be saved as undefined. + * + * @param beats The numerator of the time signature. + * @param beatType The denominator of the time signature. + */ + void SetBeat(int beats, int beatType); + + /** + * Set this measure as an ending. + * + * @param isEnding True if this is an ending, false otherwise. + */ + void SetEnding(bool isEnding); + + /** + * @copydoc Measure::MeasureAttributes::SetFifths() + */ + void SetFifths(int fifths); + + /** + * Set the number of time the repeat at the end of this measure must be performed. + * + * @param nEndRepeat The number of time the repeat must be performed or 0 if this + * measure does not end a repeat. + */ + void SetNEndRepeat(int nEndRepeat); + + /** + * Set this measure to start a repeat. + * + * @param isEnding True if this is the start of a repeat, false otherwise. + */ + void SetStartRepeat(bool isStartRepeat); + + /** + * @copydoc Measure::MeasureAttributes::SetTempo() + */ + void SetTempo(int tempo); + +private: + bool isEnding; ///< True if this is an ending measure. + bool isStartRepeat; ///< True if this measure starts a repeat. + int nEndRepeat; ///< Number of times the repeat must be performed. + csArray<MeasureElement*> elements; ///< Elements of this measure. + + /** + * Attributes of this measure. We keep this allocated object only if the measure + * actually have some attributes that are defined. + */ + MeasureAttributes* attributes; + + /** + * Create a new MeasureAttributes object if it does not exists already. + */ + void CreateAttributes(); + + /** + * Delete the attributes (if it exists) and free the memory. + */ + void DeleteAttributes(); + + /** + * Returns the biggest duration that can be represented on the score which is less + * or equal to the given one. + * + * @param duration The maximum duration that must be returned. + * @return The duration value. + */ + Duration GetBiggestDuration(int duration) const; + + /** + * Delete the MeasureAttributes object if all its attributes are undefined. + */ + void UpdateAttributes(); }; //-------------------------------------------------- @@ -340,7 +593,7 @@ /** * Attributes specified in the score up to now. Must be updated at every measure. */ - Measure::MeasureAttributes prevAttributes; + Measure::MeasureAttributes measureAttributes; }; /** @} */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2013-09-29 01:19:37
|
Revision: 8822 http://sourceforge.net/p/planeshift/code/8822 Author: whacko88 Date: 2013-09-29 01:19:34 +0000 (Sun, 29 Sep 2013) Log Message: ----------- implemented BaseMusicalScore and unified measure elements Modified Paths: -------------- trunk/src/common/music/basemusicscore.cpp trunk/src/common/music/basemusicscore.h trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Modified: trunk/src/common/music/basemusicscore.cpp =================================================================== --- trunk/src/common/music/basemusicscore.cpp 2013-09-29 00:17:15 UTC (rev 8821) +++ trunk/src/common/music/basemusicscore.cpp 2013-09-29 01:19:34 UTC (rev 8822) @@ -17,6 +17,7 @@ * */ +#include "basemusicscore.h" //==================================================================================== // Crystal Space Includes @@ -33,3 +34,408 @@ //------------------------------------------------------------------------------------ // Forward Declarations //------------------------------------------------------------------------------------ +#define INVALID_CURSOR (size_t)-1 + + +BaseMusicalScore::BaseMusicalScore() +: mode(EDIT), cursor(new Cursor(this, EDIT)) +{ +} + +BaseMusicalScore::~BaseMusicalScore() +{ + delete cursor; +} + +bool BaseMusicalScore::AdvanceCursor(bool ignoreEndOfMeasure) +{ + return cursor->Advance(ignoreEndOfMeasure); +} + +BaseMusicalScore::Cursor* BaseMusicalScore::GetEditCursor() +{ + if(mode != EDIT) + { + return 0; + } + return cursor; +} + +const Measure* BaseMusicalScore::GetMeasure(size_t n) const +{ + CS_ASSERT(n < measures.GetSize()); + return &measures[n]; +} + +const BaseMusicalScore::Cursor* BaseMusicalScore::GetPlayCursor() const +{ + if(mode != PLAY) + { + return 0; + } + return cursor; +} + +BaseMusicalScore::Cursor* BaseMusicalScore::SetEditMode() +{ + if(mode == PLAY) + { + delete cursor; + cursor = new Cursor(this, EDIT); + } + return cursor; +} + +const BaseMusicalScore::Cursor* BaseMusicalScore::SetPlayMode() +{ + if(mode == EDIT) + { + delete cursor; + cursor = new Cursor(this, PLAY); + } + return cursor; +} + +//--------------------------------------------------------------------------------------- + +BaseMusicalScore::Cursor::~Cursor() +{ + if(context != 0) + { + delete context; + } +} + +bool BaseMusicalScore::Cursor::Advance(bool ignoreEndOfMeasure) +{ + bool isLastElem; + + // if this is the last element we move the cursor to end-of-score + if(!HasNext(ignoreEndOfMeasure)) + { + currElementIdx = INVALID_CURSOR; + currMeasureIdx = INVALID_CURSOR; + return false; + } + + // check if this is the last element of the current measure + isLastElem = (currElementIdx == score->measures[currMeasureIdx].GetNElements() - 1); + if(IsEndOfMeasure() || isLastElem && ignoreEndOfMeasure) + { + currElementIdx = 0; + + // in play mode we must take repeats into account and update the context + if(score->mode == PLAY) + { + if(CheckRepeat()) + { + currMeasureIdx = context->RestoreLastStartRepeat(); + } + else + { + // we must skip all the endings that end a repeat + // section that has been already performed + do + { + currMeasureIdx++; + }while(score->measures[currMeasureIdx].IsEnding() && + score->measures[currMeasureIdx].IsEndRepeat() && + !CheckRepeat()); + + // the context doesn't have to be updated with skipped endings + context->Update(currMeasureIdx, score->measures[currMeasureIdx]); + } + } + else // mode == EDIT + { + currMeasureIdx++; + } + } + else + { + // we update the context with the accidentals of the previous element + if(score->mode == PLAY) + { + context->Update(score->measures[currMeasureIdx].GetElement(currElementIdx)); + } + + if(isLastElem) // ignoreEndOfMeasure has been checked before + { + currElementIdx = INVALID_CURSOR; + } + else + { + currElementIdx++; + } + } + + return true; +} + +MeasureElement* BaseMusicalScore::Cursor::GetCurrentElement() +{ + return const_cast<MeasureElement*>( + static_cast<const BaseMusicalScore::Cursor &>(*this).GetCurrentElement()); +} + +const MeasureElement* BaseMusicalScore::Cursor::GetCurrentElement() const +{ + if(IsValid()) + { + return &score->measures[currMeasureIdx].GetElement(currElementIdx); + } + return 0; +} + +Measure* BaseMusicalScore::Cursor::GetCurrentMeasure() +{ + return const_cast<Measure*>( + static_cast<const BaseMusicalScore::Cursor &>(*this).GetCurrentMeasure()); +} + +const Measure* BaseMusicalScore::Cursor::GetCurrentMeasure() const +{ + if(IsEndOfScore()) + { + return 0; + } + return &score->measures[currMeasureIdx]; +} + +bool BaseMusicalScore::Cursor::HasNext(bool ignoreEndOfMeasure) const +{ + if(IsEndOfScore()) + { + return false; + } + + if(score->mode == PLAY && CheckRepeat()) + { + return true; + } + + // if in edit mode or if there are no repeats to perform + return HasNextWritten(ignoreEndOfMeasure); +} + +void BaseMusicalScore::Cursor::InsertElementAfter(const MeasureElement &element) +{ + Measure* currMeasure; // convenience variable + + if(IsEndOfScore()) + { + // we push a new measure + Measure measure; + currMeasureIdx = score->measures.Push(measure); + CS_ASSERT(IsEndOfMeasure()); + } + + currMeasure = &score->measures[currMeasureIdx]; + if(IsEndOfMeasure()) + { + currElementIdx = currMeasure->PushElement(element); + CS_ASSERT(IsValid()); + } + else if(currElementIdx == currMeasure->GetNElements() - 1) + { + currMeasure->PushElement(element); + } + else + { + currMeasure->InsertElement(currElementIdx + 1, element); + } +} + +void BaseMusicalScore::Cursor::InsertElementBefore(const MeasureElement &element) +{ + if(IsEndOfScore()) + { + // we push a new measure + Measure measure; + currMeasureIdx = score->measures.Push(measure); + CS_ASSERT(IsEndOfMeasure()); + } + + if(IsValid()) + { + // the index of the current element increase after the insertion + score->measures[currMeasureIdx].InsertElement(currElementIdx, element); + currElementIdx++; + } + else // IsEndOfMeasure() == true + { + score->measures[currMeasureIdx].PushElement(element); + } +} + +void BaseMusicalScore::Cursor::InsertMeasureAfter(const Measure &measure) +{ + if(IsEndOfScore()) + { + // we move the cursor to the first element of the newly inserted measure + currMeasureIdx = score->measures.Push(measure); + if(!measure.IsEmpty()) + { + currElementIdx = 0; + } // else the cursor is on end-of-measure + } + else if(currMeasureIdx == score->measures.GetSize() - 1) + { + // if this is the last measure we must use Push() + score->measures.Push(measure); + } + else + { + score->measures.Insert(currMeasureIdx + 1, measure); + } +} + +void BaseMusicalScore::Cursor::InsertMeasureBefore(const Measure &measure) +{ + if(IsEndOfScore()) + { + score->measures.Push(measure); + } + else + { + // the index of the current measure increases after the insertion + score->measures.Insert(currMeasureIdx, measure); + currMeasureIdx++; + } +} + +bool BaseMusicalScore::Cursor::IsEndOfMeasure() const +{ + return currElementIdx == INVALID_CURSOR && currMeasureIdx != INVALID_CURSOR; +} + +bool BaseMusicalScore::Cursor::IsEndOfScore() const +{ + return currElementIdx == INVALID_CURSOR && currMeasureIdx == INVALID_CURSOR; +} + +bool BaseMusicalScore::Cursor::IsValid() const +{ + return currElementIdx != INVALID_CURSOR; +} + +bool BaseMusicalScore::Cursor::RemoveCurrentElement() +{ + if(!IsValid()) + { + return false; + } + + score->measures[currMeasureIdx].DeleteElement(currElementIdx); + + if(currElementIdx > 0) + { + currElementIdx--; + } + else if(score->measures[currMeasureIdx].IsEmpty()) + { + currElementIdx = INVALID_CURSOR; // move to end-of-measure + } // else the cursor is on the element after the removed one + + return true; +} + +bool BaseMusicalScore::Cursor::RemoveCurrentMeasure() +{ + if(IsEndOfScore()) + { + return false; + } + + score->measures.DeleteIndex(currMeasureIdx); + + if(score->measures.IsEmpty()) + { + // move to end-of-score + currElementIdx = INVALID_CURSOR; + currMeasureIdx = INVALID_CURSOR; + } + else + { + if(currMeasureIdx > 0) + { + currMeasureIdx--; + } // else the score is on the measure next to the removed one + + if(score->measures[currMeasureIdx].IsEmpty()) + { + currElementIdx = INVALID_CURSOR; // move to end-of-measure + } + else + { + currElementIdx = 0; + } + } + + return true; +} + +void BaseMusicalScore::Cursor::Validate() +{ + if(IsValid() && currElementIdx >= score->measures[currMeasureIdx].GetNElements()) + { + currElementIdx = INVALID_CURSOR; + } +} + +BaseMusicalScore::Cursor::Cursor(BaseMusicalScore* score_, BaseMusicalScore::ScoreMode mode) +: context(0), score(score_) +{ + if(mode == PLAY) + { + context = new ScoreContext(); + } + Reset(); +} + +bool BaseMusicalScore::Cursor::CheckRepeat() const +{ + int nEndRepeat = score->measures[currMeasureIdx].GetNEndRepeat(); + return context->GetNPerformedRepeats(currMeasureIdx) < nEndRepeat; +} + +bool BaseMusicalScore::Cursor::HasNextWritten(bool ignoreEndOfMeasure) const +{ + bool isLastMeasure = (currMeasureIdx == score->measures.GetSize() - 1); + + CS_ASSERT(!IsEndOfScore()); + + if(IsEndOfMeasure()) + { + return isLastMeasure; + } + CS_ASSERT(IsValid()); + + if(ignoreEndOfMeasure && isLastMeasure && + currElementIdx == score->measures[currMeasureIdx].GetNElements() - 1) + { + return false; + } + return true; +} + +void BaseMusicalScore::Cursor::Reset() +{ + if(score->measures.GetSize() == 0) + { + // end-of-score + currElementIdx = INVALID_CURSOR; + currMeasureIdx = INVALID_CURSOR; + } + else if(score->measures[0].IsEmpty()) + { + // end-of-measure + currElementIdx = INVALID_CURSOR; + currMeasureIdx = 0; + } + else + { + currElementIdx = 0; + currMeasureIdx = 0; + } +} Modified: trunk/src/common/music/basemusicscore.h =================================================================== --- trunk/src/common/music/basemusicscore.h 2013-09-29 00:17:15 UTC (rev 8821) +++ trunk/src/common/music/basemusicscore.h 2013-09-29 01:19:34 UTC (rev 8822) @@ -32,10 +32,328 @@ //==================================================================================== // Local Includes //==================================================================================== +#include "scoreelements.h" //------------------------------------------------------------------------------------ // Forward Declarations //------------------------------------------------------------------------------------ +/** + * Implements a musical score. Central object of this class is the cursor which is part + * of BaseMusicalScore's interface to all intent and purposes. The score can be in edit + * or in play mode. When in play mode, it is read-only. + * + * Though the content of the score can be read with BaseMusicalScore::GetMeasure(size_t), + * using the cursor is the only way to modify it. After you set the score mode through + * BaseMusicalScore::Set*Mode(), a reference of the score can be retrieved by using the + * methods BaseMusicalScore::Get*Cursor(). + * + * The read-only property of the score in play mode is enforced by exploiting the Cursor + * class const-correctness. The drawback of this design is that you will not be able to + * move the cursor by calling its methods in play mode. To circumvent this, work-around + * methods are provided in BaseMusicalScore. You can use BaseMusicalScore::AdvanceCursor() + * instead of BaseMusicalScore::Cursor::Advance() for example. + */ +class BaseMusicalScore +{ +public: + class Cursor; + + /** + * Create an empty score in edit mode. + */ + BaseMusicalScore(); + + /** + * Destructor. + */ + ~BaseMusicalScore(); + + /** + * @copydoc BaseMusicalScore::Cursor::Advance() + */ + bool AdvanceCursor(bool ignoreEndOfMeasure); + + /** + * Return the cursor if the score is in edit mode. + * + * @retrn The cursor if the score is in edit mode, 0 otherwise. + */ + Cursor* GetEditCursor(); + + /** + * Return the n-th measure in the score. + * + * @param n The index of the measure to retrieve. Must be valid. + * @return The n-th measure in the score. + */ + const Measure* GetMeasure(size_t n) const; + + /** + * Return the number of measures in the score. + * + * @return The number of measures in the score. + */ + size_t GetNMeasures() const { return measures.GetSize(); } + + /** + * Return the cursor if the score is in play mode. + * + * @retrn The cursor if the score is in play mode, 0 otherwise. + */ + const Cursor* GetPlayCursor() const; + + /** + * Set the mode to edit. If the score is in play mode, the previous cursor is + * invalidated and the new one starts from the beginning of the score. + * + * @return The cursor of the score. + */ + Cursor* SetEditMode(); + + /** + * Set the mode to play. If the score is in edit mode, the previous cursor is + * invalidated and the new one starts from the beginning of the score. + * + * @return The cursor of the score. + */ + const Cursor* SetPlayMode(); + +private: + enum ScoreMode + { + EDIT, + PLAY + }; + + Cursor* cursor; ///< The cursor of this score. + ScoreMode mode; ///< The mode of this cursor. + + /** + * All the measures of this musical score. End-of-measure and end-of-score variables + * are not actually represented. They are just a nice abstraction for the user. + */ + csArray<Measure> measures; +}; + +//------------------------------------------------------------------------------------ + +/** + * This is part of the BaseMusicalScore API. Only a BaseMusicalScore object can create an + * instance of this object. + * + * In order to grasp how the cursor works, it is useful to understand that, similarly to + * the end-of-file character in files, musical scores use a couple of special measure + * elements. The end of a measure is marked by an end-of-measure element. Each measure in + * the score contains at least that one. Moreover, the last element of the score is a + * special end-of-score element. This is positioned after the last measure and it does not + * belong to any of them. An empty measure has only the end-of-measure element. Similarly, + * an empty score contains only the end-of-score element. + * + * Though the user is free to add and remove elements to an existing measure by using the + * methods provided by the Measure class, it must be noticed that by doing so the cursor + * is not updated in any way. This may cause undesired effects (e.g. the cursor may point + * to a non-existent measure). For this reason it is safer to use the methods of this + * class which clearly state their effect on the cursor. If you still want to use the + * methods in Measure, be sure to call Cursor::Validate() after. + */ +class BaseMusicalScore::Cursor +{ +public: + /** + * Destructor. + */ + ~Cursor(); + + /** + * Move the cursor to the next element. This takes into account repeat sections only + * if the score is in play mode. If the current element is the last one of the score, + * false is returned and the cursor is moved to the end-of-score element. Any + * subsequent call will not affect the cursor and return false. + * + * @param ignoreEndOfMeasure Skip end-of-measure elements if true. + * @return False if the score is in edit mode and there is no element written after + * the current one or if the score is in play mode and there is no element to play + * after the current one, true otherwise. + */ + bool Advance(bool ignoreEndOfMeasure); + + /** + * Get the element currently pointed by the cursor. + * + * @return The current measure element or a null pointer in case the cursor is on a + * end-of-measure or the end-of-score element. + */ + MeasureElement* GetCurrentElement(); + + /** + * @copydoc BaseMusicalScore::Cursor::GetCurrentElement() + */ + const MeasureElement* GetCurrentElement() const; + + /** + * Get the measure currently pointed by the cursor. + * + * @return The current measure or a null pointer if the cursor is on a end-of-measure + * or the end-of-score element. + */ + Measure* GetCurrentMeasure(); + + /** + * @copydoc BaseMusicalScore::Cursor::GetCurrentMeasure() + */ + const Measure* GetCurrentMeasure() const; + + /** + * Check if the element after the current one is the end-of-score element. This takes + * into account repeat sections only if the score is in play mode. + * + * @param ignoreEndOfMeasure If this is false, end-of-measure elements are considered + * valid next elements, otherwise they are ignored. + * @return True if the score is in edit mode and there is another note written on the + * score after the current one or if the score is in play mode and there is another + * note that must be played after the current one, false otherwise. + */ + bool HasNext(bool ignoreEndOfMeasure) const; + + /** + * Insert a copy of the given element before the current one. The cursor does not + * change position. If the cursor points to the end-of-score element, a new measure + * is pushed at the end of the score and the cursor is moved to the element just + * inserted. If the cursor points to an end-of-measure element, the element is + * inserted before it and the cursor is moved to the element just inserted. + * + * @param element The element to insert. + */ + void InsertElementAfter(const MeasureElement &element); + + /** + * Insert a copy of the given element before the current one. The cursor does not + * change position. If the cursor point to the end-of-score element, a new measure + * is pushed at the end of the score and the cursor is moved to the end-of-measure + * element of the new measure. + * + * @param element The element to insert. + */ + void InsertElementBefore(const MeasureElement &element); + + /** + * Insert a copy of the given measure after the current one. The cursor does not + * change position. If the cursor is on the end-of-score element, the measure is + * pushed at the end of the score and the cursor is moved to the first element of the + * new measure (which is end-of-measure if the given measure is empty). + * + * @param measure The measure to insert. + */ + void InsertMeasureAfter(const Measure &measure); + + /** + * Insert a copy of the given measure before the current one. The cursor does not + * change position. If the cursor is on the end-of-score element, the measure is + * pushed at the end of the score and the cursor is kept on end-of-score. + * + * @param measure The measure to insert. + */ + void InsertMeasureBefore(const Measure &measure); + + /** + * Check that the cursor is on an end-of-measure element. + * + * @return True if the cursor is on an end-of-measure element, false otherwise. + */ + bool IsEndOfMeasure() const; + + /** + * Check that the cursor is on the end-of-score element. Notice that this is different + * than checking if the current element is the last one of the score. You can use + * Cursor::HasNext()for that. + * + * @return True if the cursor is on an end-of-score element, false otherwise. + */ + bool IsEndOfScore() const; + + /** + * Check that the cursor is in a valid position which means that it is not on a + * special element. + * + * @return True if the cursor is valid, false otherwise. + */ + bool IsValid() const; + + /** + * Remove the current element. The cursor is moved to the previous element within the + * same measure. If the removed element is the first one of the measure, the cursor is + * moved to the next element. If the cursor is on a end-of-measure or the end-of-score + * element, nothing happens and false is returned. + * + * @return True if the element was removed, false if the cursor is on a end-of-measure + * or the end-of-score element. + */ + bool RemoveCurrentElement(); + + /** + * Remove the current measure. The cursor is moved to the first element of the previous + * measure. If the removed measure is the first one of the score, the cursor is moved + * to the first element of the next measure or to the end-of-score element in case the + * removed measure was the only one in the score. If the cursor is already on the + * end-of-score element, nothing happens and false is returned. + * + * @return True if the measure was removed, false if the cursor is on the end-of-score + * element. + */ + bool RemoveCurrentMeasure(); + + /** + * If the cursor is pointing to a non-existent element, this moves it to the + * end-of-measure character in the same measure. You have no reason to use this method + * unless you modify add/remove elements in a Measure by using its methods. + */ + void Validate(); + +private: + // In this way only BaseMusicalScore can access the private constructor + friend BaseMusicalScore::BaseMusicalScore(); + friend Cursor* BaseMusicalScore::SetEditMode(); + friend const Cursor* BaseMusicalScore::SetPlayMode(); + + size_t currElementIdx; ///< Index of the current element. + size_t currMeasureIdx; ///< Index of the current measure. + ScoreContext* context; ///< The context is valid only if it is in play mode. + BaseMusicalScore* score; ///< The musical score this cursor refers to. + + /** + * Create a cursor pointing to the first element of the score. Only the score can + * create a cursor. + * + * @param score The musical score this cursor refers to. + * @param mode If the mode is play, a context is created and mantained. + */ + Cursor(BaseMusicalScore* score, BaseMusicalScore::ScoreMode mode); + + /** + * Check if an eventual repeat section must be repeated after this measure. The + * context and the measure index must be valid. This make sense only in play mode. + * + * @return True if a repeat must be performed, false otherwise. + */ + bool CheckRepeat() const; + + /** + * Check if the cursor is placed on the last written element of the score. This does + * not take into account repeat sections. The cursor cannot be on the end-of-score + * element. + * + * @param ignoreEndOfMeasure If this is false, end-of-measure elements are considered + * valid next elements, otherwise they are ignored. + * @return False if the current element is the last one in the score, true otherwise. + */ + bool HasNextWritten(bool ignoreEndOfMeasure) const; + + /** + * Move the cursor to the first element of the score. + */ + void Reset(); +}; + #endif // BASE_MUSIC_SCORE_H Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2013-09-29 00:17:15 UTC (rev 8821) +++ trunk/src/common/music/scoreelements.cpp 2013-09-29 01:19:34 UTC (rev 8822) @@ -34,31 +34,15 @@ //------------------------------------------------------------------------------------ // Forward Declarations //------------------------------------------------------------------------------------ +using namespace psMusic; // 10 octaves are more than enough and each octave contains maximum // 7 notes so there is really no need of a bigger hash table #define PREV_ACCIDENTALS_NAME_SIZE 7 #define PREV_ACCIDENTALS_OCTAVE_SIZE 5 -// 2 notes is the minimum for a chord and it very rarely gets over 5 notes -#define NOTES_IN_CAPACITY 5 - //-------------------------------------------------- -MeasureElement::MeasureElement(Duration duration_) -: duration(duration_) -{ -} - -//-------------------------------------------------- - -Rest::Rest(Duration duration) -: MeasureElement(duration) -{ -} - -//-------------------------------------------------- - Note::NoteContext::NoteContext() : prevAccidentals(PREV_ACCIDENTALS_OCTAVE_SIZE) { @@ -112,9 +96,17 @@ } } -Note::Note(char name_, int octave_, Accidental writtenAccidental_, Duration duration_) -: MeasureElement(duration_), octave(octave_), writtenAccidental(writtenAccidental_) +void Note::NoteContext::UpdateContext(const MeasureElement &element) { + for(size_t i = 0; i < element.GetNNotes(); i++) + { + UpdateContext(element.GetNote(i)); + } +} + +Note::Note(char name_, int octave_, Accidental writtenAccidental_) +: octave(octave_), writtenAccidental(writtenAccidental_) +{ SetName(name_); } @@ -239,14 +231,14 @@ //-------------------------------------------------- -Chord::Chord(Duration duration) -: MeasureElement(duration), notes(NOTES_IN_CAPACITY) +MeasureElement::MeasureElement(Duration duration_) +: duration(duration_) { } -bool Chord::AddNote(char name, int octave, Accidental writtenAccidental) +bool MeasureElement::AddNote(char name, int octave, Accidental writtenAccidental) { - Note note(name, octave, writtenAccidental, GetDuration()); + Note note(name, octave, writtenAccidental); size_t noteIdx = notes.Find(note); if(noteIdx == csArrayItemNotFound) { @@ -260,21 +252,12 @@ } } -bool Chord::RemoveNote(char name, int octave) +bool MeasureElement::RemoveNote(char name, int octave) { - Note note(name, octave, NO_ACCIDENTAL, GetDuration()); + Note note(name, octave, NO_ACCIDENTAL); return notes.Delete(note); } -void Chord::SetDuration(Duration duration_) -{ - MeasureElement::SetDuration(duration_); - for(size_t i = 0; i < notes.GetSize(); i++) - { - notes[i].SetDuration(duration_); - } -} - //-------------------------------------------------- Measure::MeasureAttributes::MeasureAttributes() @@ -320,28 +303,8 @@ Measure::~Measure() { DeleteAttributes(); - - DeleteAllElements(); } -void Measure::DeleteElement(size_t n) -{ - if(n < elements.GetSize()) - { - delete elements.Get(n); - elements.DeleteIndex(n); - } -} - -void Measure::DeleteAllElements() -{ - for(size_t i = 0; i < elements.GetSize(); i++) - { - delete elements.Get(i); - } - elements.Empty(); -} - void Measure::Fit(const MeasureAttributes* attributes_) { int measDuration = 0; // the duration that the measure is supposed to be @@ -374,20 +337,20 @@ // Determining which notes exceed the measure while(cutIdx < elements.GetSize() && currDuration <= measDuration) { - currDuration += elements.Get(cutIdx)->GetDuration(); + currDuration += elements[cutIdx].GetDuration(); cutIdx++; } // Cutting or filling if(currDuration > measDuration) // cut { - int prevDuration = currDuration - elements.Get(cutIdx)->GetDuration(); + int prevDuration = currDuration - elements.Get(cutIdx).GetDuration(); // If there's a note that exceeds the measure duration we can cut it first if(prevDuration < measDuration) { Duration tempDuration = GetBiggestDuration(measDuration - prevDuration); - elements.Get(cutIdx)->SetDuration(tempDuration); + elements.Get(cutIdx).SetDuration(tempDuration); cutIdx++; } for(size_t i = elements.GetSize() - 1; i >= cutIdx; i--) @@ -402,8 +365,7 @@ while(currDuration < measDuration) { tempDuration = GetBiggestDuration(measDuration - currDuration); - MeasureElement* rest = new Rest(tempDuration); - PushElement(rest); + PushElement(MeasureElement(tempDuration)); currDuration += tempDuration; } } @@ -418,21 +380,14 @@ return *attributes; } -void Measure::InsertElement(size_t n, MeasureElement* element) +void Measure::InsertElement(size_t n, const MeasureElement &element) { - CS_ASSERT(element != 0); if(!elements.Insert(n, element)) { PushElement(element); } } -void Measure::PushElement(MeasureElement* element) -{ - CS_ASSERT(element != 0); - elements.Push(element); -} - void Measure::SetBeat(int beats, int beatType) { if(beats != UNDEFINED_MEASURE_ATTRIBUTE && beatType != UNDEFINED_MEASURE_ATTRIBUTE) @@ -454,8 +409,8 @@ { attributes->SetBeats(beats); attributes->SetBeatType(beatType); + UpdateAttributes(); } - UpdateAttributes(); } void Measure::SetEnding(bool isEnding_) @@ -473,8 +428,8 @@ if(attributes != 0) { attributes->SetFifths(fifths); + UpdateAttributes(); } - UpdateAttributes(); } void Measure::SetNEndRepeat(int nEndRepeat_) @@ -498,8 +453,8 @@ if(attributes != 0) { attributes->SetTempo(tempo); + UpdateAttributes(); } - UpdateAttributes(); } void Measure::CreateAttributes() @@ -521,8 +476,6 @@ Duration Measure::GetBiggestDuration(int duration) const { - CS_ASSERT(duration > 0); - if(DOTTED_WHOLE_DURATION <= duration) return DOTTED_WHOLE_DURATION; else if(WHOLE_DURATION <= duration) @@ -545,8 +498,54 @@ void Measure::UpdateAttributes() { - if(attributes->IsUndefined()) + if(attributes != 0) { - DeleteAttributes(); + if(attributes->IsUndefined()) + { + DeleteAttributes(); + } } } + +//-------------------------------------------------- + +ScoreContext::ScoreContext() +: lastStartRepeatID(0) +{ +} + +int ScoreContext::GetNPerformedRepeats(int measureID) const +{ + return repeatsDone.Get(measureID, 0); +} + +int ScoreContext::RestoreLastStartRepeat() +{ + measureAttributes = lastStartRepeatAttributes; + return lastStartRepeatID; +} + +void ScoreContext::Update(const MeasureElement &element) +{ + noteContext.UpdateContext(element); +} + +void ScoreContext::Update(int measureID, const Measure &measure) +{ + noteContext.ResetContext(); + measureAttributes.UpdateAttributes(measure.GetAttributes()); + + if(measure.IsStartRepeat()) + { + lastStartRepeatID = measureID; + lastStartRepeatAttributes = measureAttributes; + + // we don't need previous repeats anymore at this point + repeatsDone.DeleteAll(); + } + + if(measure.GetNEndRepeat() > 0 && !repeatsDone.Contains(measureID)) + { + repeatsDone.Put(measureID, 0); + } +} Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2013-09-29 00:17:15 UTC (rev 8821) +++ trunk/src/common/music/scoreelements.h 2013-09-29 01:19:34 UTC (rev 8822) @@ -39,73 +39,21 @@ //------------------------------------------------------------------------------------ // Forward Declarations //------------------------------------------------------------------------------------ +// A chord rarely have more than 6 notes +#define MEASURE_ELEMENT_NOTES_CAPACITY_GROWTH 4 #define UNDEFINED_MEASURE_ATTRIBUTE -100 -struct ScoreContext; +class ScoreContext; +class MeasureElement; -using namespace psMusic; - /** * \addtogroup common_music * @{ */ /** - * An element of a measure with a given duration. It can be a rest, a single note or a - * chord. - */ -class MeasureElement -{ -public: - /** - * Constructor. - * - * @param duration The duration of this element. - */ - MeasureElement(Duration duration); - - virtual ~MeasureElement() {} - - /** - * Get the duration of this element. - * - * @return The duration of this element. - */ - Duration GetDuration() { return duration; } - - /** - * Set the duration of this element. - * - * @param newDuration the new duration for this element. - */ - virtual void SetDuration(Duration newDuration) { duration = newDuration; } - -private: - Duration duration; -}; - -//-------------------------------------------------- - -/** - * Represent a rest in the score. The class does not have particular members but its - * extension could. - */ -class Rest: public MeasureElement -{ -public: - /** - * Constructor. - * - * @param duration The duration of this rest. - */ - Rest(Duration duration); -}; - -//-------------------------------------------------- - -/** * A single note in a musical score. */ -class Note: public MeasureElement +class Note { public: /** @@ -129,7 +77,7 @@ * same name and octave as the given one. In case no previous note satisfying the * requirements is found, NO_ACCIDENTAL is returned. */ - Accidental GetPreviousAccidental(const Note ¬e) const; + psMusic::Accidental GetPreviousAccidental(const Note ¬e) const; /** * Empty the list of previously altered note. @@ -137,18 +85,26 @@ void ResetContext(); /** + * Update the list of previous accidental with all the notes in the given measure + * element. + * + * @param element The current element. + */ + void UpdateContext(const MeasureElement &element); + + /** * Update the list of previous accidental if the given note has any. * * @param note The current played note. */ - void UpdateContext(const Note &context); + void UpdateContext(const Note ¬e); private: /** * Previous notes with a written accidental. Accidentals are indexed by note octave * and name. */ - csHash<csHash<Accidental, char>, int> prevAccidentals; + csHash<csHash<psMusic::Accidental, char>, int> prevAccidentals; }; /** @@ -161,9 +117,8 @@ * is not related to the tonality of the piece but only on its representation. In * other words the variable accidental must be different than UNALTERED if and only if * an actual accidental is written on the musical score. - * @param duration The duration of this note. */ - Note(char name, int octave, Accidental writtenAccidental, Duration duration); + Note(char name, int octave, psMusic::Accidental writtenAccidental); /** * Get the note name. @@ -189,14 +144,14 @@ * tonality of the piece. An eventual accidental written on the score previously * have the priority. */ - Accidental GetPlayedAccidental(const ScoreContext &context) const; + psMusic::Accidental GetPlayedAccidental(const ScoreContext &context) const; /** * Get the written accidental. * * @return The accidental written on the score for this note. */ - Accidental GetWrittenAccidental() const { return writtenAccidental; } + psMusic::Accidental GetWrittenAccidental() const { return writtenAccidental; } /** * Equal operator. @@ -226,7 +181,7 @@ * * @param accidental The accidental written on the score for this note. */ - void SetWrittenAccidental(Accidental accidental); + void SetWrittenAccidental(psMusic::Accidental accidental); private: char name; ///< Uppercase name of the note (C, D, E, F, G, A or B). @@ -237,21 +192,24 @@ * anything to do with the tonality. With this representation, one does not have to * modify all the notes in the score when changing the tonality of the piece. */ - Accidental writtenAccidental; + psMusic::Accidental writtenAccidental; }; //-------------------------------------------------- /** - * A group of notes that must be played together. + * An element of a measure with a given duration. It can be a rest, a single note or a + * group of notes with same duration that must be played together (a chord). */ - class Chord: public MeasureElement +class MeasureElement { public: /** - * Constructor. + * Constructor. The default element is a rest of the given duration. + * + * @param duration The duration of this element. */ - Chord(Duration duration); + MeasureElement(psMusic::Duration duration); /** * Copy a note into the chord. If a note with the same name and octave already exists @@ -263,21 +221,43 @@ * @param accidental The accidental with which the note is written on the score. * @return True if no note was overwritten, false otherwise. */ - bool AddNote(char name, int octave, Accidental writtenAccidental); + bool AddNote(char name, int octave, psMusic::Accidental writtenAccidental); /** - * Remove all the notes from the chord. + * Get the duration of this element. + * + * @return The duration of this element. */ - void Empty() { notes.Empty(); } + psMusic::Duration GetDuration() { return duration; } /** * Get the number of notes in this chord. * - * @return the number of notes in this chord. + * @return the number of notes in this chord, 0 if this is a rest. */ - size_t GetSize() const { return notes.GetSize(); } + size_t GetNNotes() const { return notes.GetSize(); } /** + * Return the n-th note in the element. + * + * @param n The number of the note in the element. + * @return The n-th note in the element. + */ + Note &GetNote(size_t n) { return notes[n]; } + + /** + * @copydoc Note::GetNote(size_t) + */ + const Note &GetNote(size_t n) const { return notes[n]; } + + /** + * Return whether this element is a rest or not. + * + * @return True if this is a rest. + */ + bool IsRest() const { return notes.GetSize() == 0; } + + /** * Delete a note with the same name and octave. * * @param note The note that must be deleted. @@ -290,14 +270,26 @@ * * @param duration the new duration for this element. */ - void SetDuration(Duration duration); + void SetDuration(psMusic::Duration duration) { this->duration = duration; } + /** + * Set this element to be a rest. All existing notes are deleted. + */ + void SetRest() { notes.Empty(); } + private: - csArray<Note> notes; ///< The sequence of notes in the chord. + psMusic::Duration duration; ///< The duration of this element. + + ///< The sequence of notes in the chord. + csArray<Note, csArrayElementHandler<Note>, CS::Container::ArrayAllocDefault, + csArrayCapacityFixedGrow<MEASURE_ELEMENT_NOTES_CAPACITY_GROWTH>> notes; }; //-------------------------------------------------- +/** + * A measure containing measure elements. + */ class Measure { public: @@ -416,12 +408,12 @@ * * @param n The index of the measure element. */ - void DeleteElement(size_t n); + void DeleteElement(size_t n) { elements.DeleteIndex(n); } /** * Delete all elements from the measure. */ - void DeleteAllElements(); + void DeleteAllElements() { elements.Empty(); } /** * Remove the elements at the end of the measures that exceeds the total duration or @@ -434,7 +426,7 @@ * this measure specifies both beat and beat type. If this is not the case however, * the parameter must specify at least information about beat and beat type. */ - void Fit(const MeasureAttributes* attributes); + void Fit(const MeasureAttributes* const attributes); /** * Get a copy of the attributes of this measure. @@ -447,16 +439,21 @@ * Get the element at position n. * * @param n The index of the element. - * @return The element or 0 if n is not a valid index. + * @return The element. */ - MeasureElement* GetElement(size_t n) { return elements.Get(n); } + MeasureElement &GetElement(size_t n) { return elements[n]; } /** + * @copydoc Measure::GetElement(size_t) + */ + const MeasureElement &GetElement(size_t n) const { return elements[n]; } + + /** * Get the number of elements in this measure. * * @return The number of elements in this measure. */ - int GetNElements() { return elements.GetSize(); } + size_t GetNElements() const { return elements.GetSize(); } /** * Get the number of time the repeat at the end of this measure must be performed. @@ -467,16 +464,22 @@ int GetNEndRepeat() const { return nEndRepeat; } /** - * Insert the given element after element n. The element will be deallocated when the - * measure is deleted. If n is greater than the number of elements in this measure, - * the element is pushed at the end. + * Insert a copy of the given element after element n. If n is greater than the + * number of elements in this measure, the element is pushed at the end. * * @param n The number of the element after which the given element must be inserted. * @param element The element to insert in the measure. */ - void InsertElement(size_t n, MeasureElement* element); + void InsertElement(size_t n, const MeasureElement &element); /** + * Check if the measure has at least one element. + * + * @return True if this measure does not contain elements, false otherwise. + */ + bool IsEmpty() const { return elements.GetSize() == 0; } + + /** * Return true if this is an ending measure. * * @return True if this is an ending measure, false otherwise. @@ -484,19 +487,26 @@ bool IsEnding() const { return isEnding; } /** - * Return true if this measure starts a repeat. + * Return true if this measure ends a repeat section. * - * @return True if this is the start of a repeat, false otherwise. + * @return True if this is the end of a repeat section, false otherwise. */ + bool IsEndRepeat() const { return nEndRepeat > 0; } + + /** + * Return true if this measure starts a repeat section. + * + * @return True if this is the start of a repeat section, false otherwise. + */ bool IsStartRepeat() const { return isStartRepeat; } /** - * Push an element after all the elements currently in the measure. The element will - * be deallocated when the measure is deleted. + * Push a copy of the given element after all the elements currently in the measure. * * @param element The element to push in the measure. + * @return The index of the pushed element. */ - void PushElement(MeasureElement* element); + size_t PushElement(const MeasureElement &element) { return elements.Push(element); } /** * Set the beat information. Both beats and beatType must be defined. If only one of @@ -528,9 +538,9 @@ void SetNEndRepeat(int nEndRepeat); /** - * Set this measure to start a repeat. + * Set this measure to start a repeat section. * - * @param isEnding True if this is the start of a repeat, false otherwise. + * @param isEnding True if this is the start of a repeat section, false otherwise. */ void SetStartRepeat(bool isStartRepeat); @@ -541,9 +551,9 @@ private: bool isEnding; ///< True if this is an ending measure. - bool isStartRepeat; ///< True if this measure starts a repeat. + bool isStartRepeat; ///< True if this measure starts a repeat section. int nEndRepeat; ///< Number of times the repeat must be performed. - csArray<MeasureElement*> elements; ///< Elements of this measure. + csArray<MeasureElement> elements; ///< Elements of this measure. /** * Attributes of this measure. We keep this allocated object only if the measure @@ -568,7 +578,7 @@ * @param duration The maximum duration that must be returned. * @return The duration value. */ - Duration GetBiggestDuration(int duration) const; + psMusic::Duration GetBiggestDuration(int duration) const; /** * Delete the MeasureAttributes object if all its attributes are undefined. @@ -579,14 +589,13 @@ //-------------------------------------------------- /** - * Keep track of previous accidentals in the same measure and attributes of previous - * measures when they are not specified in the current one. This struct make sense only - * when it refers to a specific point in the musical score. + * This is used to keep track of everything needed to play a score and provide some + * utility functions for this purpose. This class make sense only when it refers to a + * specific point in the musical score. */ -struct ScoreContext +class ScoreContext { - // TODO how to handle repeats for measure attributes? - +public: /** * Used to keep track of previous accidentals in the same measure. */ @@ -596,6 +605,60 @@ * Attributes specified in the score up to now. Must be updated at every measure. */ Measure::MeasureAttributes measureAttributes; + + /** + * Constructor. + */ + ScoreContext(); + + /** + * Return the number of times the eventual repeat in the given measure has been + * performed. + * + * @return The number of times the repeat in the given measure has been performed or + * 0 if the measure does not end a repeat section. + */ + int GetNPerformedRepeats(int measureID) const; + + /** + * Restore the context of the last measure containing a start repeat. If no explicit + * start repeats have been found, the function assumes it to be the first measure. + * + * @return The ID of the last measure encountered that starts a repeat. + */ + int RestoreLastStartRepeat(); + + /** + * Update the context with the new measure. This resets also previous accidentals. + * Note that you still have to call Update(MeasureElement &) to update the list of + * previous accidentals with the first note of this measure. + * + * @param measureID The ID of the updated measure. + * @param measure The new measure reached by the cursor. + */ + void Update(int measureID, const Measure &measure); + + /** + * Keep the list of previous accidentals updated. + * + * @param element The new element reached by the cursor. + */ + void Update(const MeasureElement &element); + +private: + int lastStartRepeatID; ///< The ID of the last measure containing a start repeat. + + /** + * Cache element used to store attributes in the last measure containins a start + * repeat. + */ + Measure::MeasureAttributes lastStartRepeatAttributes; + + /** + * Keeps track of the repeat sections already performed. It is indexed by measure index and + * contains the number of times the repeat has been already performed. + */ + csHash<int, int> repeatsDone; }; /** @} */ This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2013-09-29 02:30:19
|
Revision: 8824 http://sourceforge.net/p/planeshift/code/8824 Author: whacko88 Date: 2013-09-29 02:30:16 +0000 (Sun, 29 Sep 2013) Log Message: ----------- moved GetBiggestDuration from Measure to musicutil Modified Paths: -------------- trunk/src/common/music/musicutil.cpp trunk/src/common/music/musicutil.h trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Modified: trunk/src/common/music/musicutil.cpp =================================================================== --- trunk/src/common/music/musicutil.cpp 2013-09-29 01:23:50 UTC (rev 8823) +++ trunk/src/common/music/musicutil.cpp 2013-09-29 02:30:16 UTC (rev 8824) @@ -32,6 +32,28 @@ #define SCORE_COMPRESSION_FACTOR 3 +psMusic::Duration psMusic::GetBiggestDuration(int duration) +{ + if(DOTTED_WHOLE_DURATION <= duration) + return DOTTED_WHOLE_DURATION; + else if(WHOLE_DURATION <= duration) + return WHOLE_DURATION; + else if(DOTTED_HALF_DURATION <= duration) + return DOTTED_HALF_DURATION; + else if(HALF_DURATION <= duration) + return HALF_DURATION; + else if(DOTTED_QUARTER_DURATION <= duration) + return DOTTED_QUARTER_DURATION; + else if(QUARTER_DURATION <= duration) + return QUARTER_DURATION; + else if(DOTTED_EIGHTH_DURATION <= duration) + return DOTTED_EIGHTH_DURATION; + else if(EIGHTH_DURATION <= duration) + return EIGHTH_DURATION; + + return SIXTEENTH_DURATION; +} + void psMusic::NextPitch(char &pitch, uint &octave) { switch(pitch) Modified: trunk/src/common/music/musicutil.h =================================================================== --- trunk/src/common/music/musicutil.h 2013-09-29 01:23:50 UTC (rev 8823) +++ trunk/src/common/music/musicutil.h 2013-09-29 02:30:16 UTC (rev 8824) @@ -248,7 +248,17 @@ DOTTED_WHOLE_DURATION = 24 }; + /** + * Returns the biggest duration that can be represented on the score which is less + * or equal to the given one. + * + * @param duration The maximum duration as the number of DURATION_QUARTER_DIVISIONS. + * @return The duration value. + */ +Duration GetBiggestDuration(int duration); + +/** * Turns the given pitch into the next one in the scale. * * @param pitch the pitch of the note. Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2013-09-29 01:23:50 UTC (rev 8823) +++ trunk/src/common/music/scoreelements.cpp 2013-09-29 02:30:16 UTC (rev 8824) @@ -474,28 +474,6 @@ } } -Duration Measure::GetBiggestDuration(int duration) const -{ - if(DOTTED_WHOLE_DURATION <= duration) - return DOTTED_WHOLE_DURATION; - else if(WHOLE_DURATION <= duration) - return WHOLE_DURATION; - else if(DOTTED_HALF_DURATION <= duration) - return DOTTED_HALF_DURATION; - else if(HALF_DURATION <= duration) - return HALF_DURATION; - else if(DOTTED_QUARTER_DURATION <= duration) - return DOTTED_QUARTER_DURATION; - else if(QUARTER_DURATION <= duration) - return QUARTER_DURATION; - else if(DOTTED_EIGHTH_DURATION <= duration) - return DOTTED_EIGHTH_DURATION; - else if(EIGHTH_DURATION <= duration) - return EIGHTH_DURATION; - - return SIXTEENTH_DURATION; -} - void Measure::UpdateAttributes() { if(attributes != 0) Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2013-09-29 01:23:50 UTC (rev 8823) +++ trunk/src/common/music/scoreelements.h 2013-09-29 02:30:16 UTC (rev 8824) @@ -572,15 +572,6 @@ void DeleteAttributes(); /** - * Returns the biggest duration that can be represented on the score which is less - * or equal to the given one. - * - * @param duration The maximum duration that must be returned. - * @return The duration value. - */ - psMusic::Duration GetBiggestDuration(int duration) const; - - /** * Delete the MeasureAttributes object if all its attributes are undefined. */ void UpdateAttributes(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2014-05-18 22:09:29
|
Revision: 9508 http://sourceforge.net/p/planeshift/code/9508 Author: whacko88 Date: 2014-05-18 22:09:28 +0000 (Sun, 18 May 2014) Log Message: ----------- transformed BaseMusicalScore and Measure into templates Modified Paths: -------------- trunk/src/common/music/basemusicscore.h trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Added Paths: ----------- trunk/src/common/music/basemusicscore.hpp trunk/src/common/music/musicxmlscore.hpp Removed Paths: ------------- trunk/src/common/music/basemusicscore.cpp trunk/src/common/music/musicxmlscore.cpp Deleted: trunk/src/common/music/basemusicscore.cpp =================================================================== --- trunk/src/common/music/basemusicscore.cpp 2014-05-12 14:52:35 UTC (rev 9507) +++ trunk/src/common/music/basemusicscore.cpp 2014-05-18 22:09:28 UTC (rev 9508) @@ -1,441 +0,0 @@ -/* - * basemusicscore.cpp, Author: Andrea Rizzi <88w...@gm...> - * - * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) - * - * - * 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 (version 2 of the License) - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - -#include "basemusicscore.h" - -//==================================================================================== -// Crystal Space Includes -//==================================================================================== - -//==================================================================================== -// Project Includes -//==================================================================================== - -//==================================================================================== -// Local Includes -//==================================================================================== - -//------------------------------------------------------------------------------------ -// Forward Declarations -//------------------------------------------------------------------------------------ -#define INVALID_CURSOR (size_t)-1 - - -BaseMusicalScore::BaseMusicalScore() - : cursor(new Cursor(this, EDIT)), mode(EDIT) -{ -} - -BaseMusicalScore::~BaseMusicalScore() -{ - delete cursor; -} - -bool BaseMusicalScore::AdvanceCursor(bool ignoreEndOfMeasure) -{ - return cursor->Advance(ignoreEndOfMeasure); -} - -BaseMusicalScore::Cursor* BaseMusicalScore::GetEditCursor() -{ - if(mode != EDIT) - { - return 0; - } - return cursor; -} - -const Measure* BaseMusicalScore::GetMeasure(size_t n) const -{ - CS_ASSERT(n < measures.GetSize()); - return &measures[n]; -} - -const BaseMusicalScore::Cursor* BaseMusicalScore::GetPlayCursor() const -{ - if(mode != PLAY) - { - return 0; - } - return cursor; -} - -BaseMusicalScore::Cursor* BaseMusicalScore::SetEditMode() -{ - if(mode == PLAY) - { - delete cursor; - cursor = new Cursor(this, EDIT); - } - return cursor; -} - -const BaseMusicalScore::Cursor* BaseMusicalScore::SetPlayMode() -{ - if(mode == EDIT) - { - delete cursor; - cursor = new Cursor(this, PLAY); - } - return cursor; -} - -//--------------------------------------------------------------------------------------- - -BaseMusicalScore::Cursor::~Cursor() -{ - if(context != 0) - { - delete context; - } -} - -bool BaseMusicalScore::Cursor::Advance(bool ignoreEndOfMeasure) -{ - bool isLastElem; - - // if this is the last element we move the cursor to end-of-score - if(!HasNext(ignoreEndOfMeasure)) - { - currElementIdx = INVALID_CURSOR; - currMeasureIdx = INVALID_CURSOR; - return false; - } - - // check if this is the last element of the current measure - isLastElem = (currElementIdx == score->measures[currMeasureIdx].GetNElements() - 1); - if(IsEndOfMeasure() || (isLastElem && ignoreEndOfMeasure)) - { - currElementIdx = 0; - - // in play mode we must take repeats into account and update the context - if(score->mode == PLAY) - { - if(CheckRepeat()) - { - currMeasureIdx = context->RestoreLastStartRepeat(); - } - else - { - // we must skip all the endings that end a repeat - // section that has been already performed - do - { - currMeasureIdx++; - }while(score->measures[currMeasureIdx].IsEnding() && - score->measures[currMeasureIdx].IsEndRepeat() && - !CheckRepeat()); - - // the context doesn't have to be updated with skipped endings - context->Update(currMeasureIdx, score->measures[currMeasureIdx]); - } - } - else // mode == EDIT - { - currMeasureIdx++; - } - } - else - { - // we update the context with the accidentals of the previous element - if(score->mode == PLAY) - { - context->Update(score->measures[currMeasureIdx].GetElement(currElementIdx)); - } - - if(isLastElem) // ignoreEndOfMeasure has been checked before - { - currElementIdx = INVALID_CURSOR; - } - else - { - currElementIdx++; - } - } - - return true; -} - -MeasureElement* BaseMusicalScore::Cursor::GetCurrentElement() -{ - return const_cast<MeasureElement*>( - static_cast<const BaseMusicalScore::Cursor &>(*this).GetCurrentElement()); -} - -const MeasureElement* BaseMusicalScore::Cursor::GetCurrentElement() const -{ - if(IsValid()) - { - return &score->measures[currMeasureIdx].GetElement(currElementIdx); - } - return 0; -} - -Measure* BaseMusicalScore::Cursor::GetCurrentMeasure() -{ - return const_cast<Measure*>( - static_cast<const BaseMusicalScore::Cursor &>(*this).GetCurrentMeasure()); -} - -const Measure* BaseMusicalScore::Cursor::GetCurrentMeasure() const -{ - if(IsEndOfScore()) - { - return 0; - } - return &score->measures[currMeasureIdx]; -} - -bool BaseMusicalScore::Cursor::HasNext(bool ignoreEndOfMeasure) const -{ - if(IsEndOfScore()) - { - return false; - } - - if(score->mode == PLAY && CheckRepeat()) - { - return true; - } - - // if in edit mode or if there are no repeats to perform - return HasNextWritten(ignoreEndOfMeasure); -} - -void BaseMusicalScore::Cursor::InsertElementAfter(const MeasureElement &element) -{ - Measure* currMeasure; // convenience variable - - if(IsEndOfScore()) - { - // we push a new measure - Measure measure; - currMeasureIdx = score->measures.Push(measure); - CS_ASSERT(IsEndOfMeasure()); - } - - currMeasure = &score->measures[currMeasureIdx]; - if(IsEndOfMeasure()) - { - currElementIdx = currMeasure->PushElement(element); - CS_ASSERT(IsValid()); - } - else if(currElementIdx == currMeasure->GetNElements() - 1) - { - currMeasure->PushElement(element); - } - else - { - currMeasure->InsertElement(currElementIdx + 1, element); - } -} - -void BaseMusicalScore::Cursor::InsertElementBefore(const MeasureElement &element) -{ - if(IsEndOfScore()) - { - // we push a new measure - Measure measure; - currMeasureIdx = score->measures.Push(measure); - CS_ASSERT(IsEndOfMeasure()); - } - - if(IsValid()) - { - // the index of the current element increase after the insertion - score->measures[currMeasureIdx].InsertElement(currElementIdx, element); - currElementIdx++; - } - else // IsEndOfMeasure() == true - { - score->measures[currMeasureIdx].PushElement(element); - } -} - -void BaseMusicalScore::Cursor::InsertMeasureAfter(const Measure &measure) -{ - if(IsEndOfScore()) - { - // we move the cursor to the first element of the newly inserted measure - currMeasureIdx = score->measures.Push(measure); - if(!measure.IsEmpty()) - { - currElementIdx = 0; - } // else the cursor is on end-of-measure - } - else if(currMeasureIdx == score->measures.GetSize() - 1) - { - // if this is the last measure we must use Push() - score->measures.Push(measure); - } - else - { - score->measures.Insert(currMeasureIdx + 1, measure); - } -} - -void BaseMusicalScore::Cursor::InsertMeasureBefore(const Measure &measure) -{ - if(IsEndOfScore()) - { - score->measures.Push(measure); - } - else - { - // the index of the current measure increases after the insertion - score->measures.Insert(currMeasureIdx, measure); - currMeasureIdx++; - } -} - -bool BaseMusicalScore::Cursor::IsEndOfMeasure() const -{ - return currElementIdx == INVALID_CURSOR && currMeasureIdx != INVALID_CURSOR; -} - -bool BaseMusicalScore::Cursor::IsEndOfScore() const -{ - return currElementIdx == INVALID_CURSOR && currMeasureIdx == INVALID_CURSOR; -} - -bool BaseMusicalScore::Cursor::IsValid() const -{ - return currElementIdx != INVALID_CURSOR; -} - -bool BaseMusicalScore::Cursor::RemoveCurrentElement() -{ - if(!IsValid()) - { - return false; - } - - score->measures[currMeasureIdx].DeleteElement(currElementIdx); - - if(currElementIdx > 0) - { - currElementIdx--; - } - else if(score->measures[currMeasureIdx].IsEmpty()) - { - currElementIdx = INVALID_CURSOR; // move to end-of-measure - } // else the cursor is on the element after the removed one - - return true; -} - -bool BaseMusicalScore::Cursor::RemoveCurrentMeasure() -{ - if(IsEndOfScore()) - { - return false; - } - - score->measures.DeleteIndex(currMeasureIdx); - - if(score->measures.IsEmpty()) - { - // move to end-of-score - currElementIdx = INVALID_CURSOR; - currMeasureIdx = INVALID_CURSOR; - } - else - { - if(currMeasureIdx > 0) - { - currMeasureIdx--; - } // else the score is on the measure next to the removed one - - if(score->measures[currMeasureIdx].IsEmpty()) - { - currElementIdx = INVALID_CURSOR; // move to end-of-measure - } - else - { - currElementIdx = 0; - } - } - - return true; -} - -void BaseMusicalScore::Cursor::Validate() -{ - if(IsValid() && currElementIdx >= score->measures[currMeasureIdx].GetNElements()) - { - currElementIdx = INVALID_CURSOR; - } -} - -BaseMusicalScore::Cursor::Cursor(BaseMusicalScore* score_, BaseMusicalScore::ScoreMode mode) -: context(0), score(score_) -{ - if(mode == PLAY) - { - context = new ScoreContext(); - } - Reset(); -} - -bool BaseMusicalScore::Cursor::CheckRepeat() const -{ - int nEndRepeat = score->measures[currMeasureIdx].GetNEndRepeat(); - return context->GetNPerformedRepeats(currMeasureIdx) < nEndRepeat; -} - -bool BaseMusicalScore::Cursor::HasNextWritten(bool ignoreEndOfMeasure) const -{ - bool isLastMeasure = (currMeasureIdx == score->measures.GetSize() - 1); - - CS_ASSERT(!IsEndOfScore()); - - if(IsEndOfMeasure()) - { - return isLastMeasure; - } - CS_ASSERT(IsValid()); - - if(ignoreEndOfMeasure && isLastMeasure && - currElementIdx == score->measures[currMeasureIdx].GetNElements() - 1) - { - return false; - } - return true; -} - -void BaseMusicalScore::Cursor::Reset() -{ - if(score->measures.GetSize() == 0) - { - // end-of-score - currElementIdx = INVALID_CURSOR; - currMeasureIdx = INVALID_CURSOR; - } - else if(score->measures[0].IsEmpty()) - { - // end-of-measure - currElementIdx = INVALID_CURSOR; - currMeasureIdx = 0; - } - else - { - currElementIdx = 0; - currMeasureIdx = 0; - } -} Modified: trunk/src/common/music/basemusicscore.h =================================================================== --- trunk/src/common/music/basemusicscore.h 2014-05-12 14:52:35 UTC (rev 9507) +++ trunk/src/common/music/basemusicscore.h 2014-05-18 22:09:28 UTC (rev 9508) @@ -39,14 +39,15 @@ //------------------------------------------------------------------------------------ /** - * Implements a musical score. Central object of this class is the cursor which is part - * of BaseMusicalScore's interface to all intent and purposes. The score can be in edit - * or in play mode. When in play mode, it is read-only. + * Implements a musical score. Central object of this class is the cursor which is part of + * BaseMusicalScore's interface to all intent and purposes. The score can be in edit or in + * play mode. When in play mode, it is read-only. * - * Though the content of the score can be read with BaseMusicalScore::GetMeasure(size_t), - * using the cursor is the only way to modify it. After you set the score mode through - * BaseMusicalScore::Set*Mode(), a reference of the score can be retrieved by using the - * methods BaseMusicalScore::Get*Cursor(). + * Though the content of the score can be read with BaseMusicalScore::GetMeasure(), using + * the cursor is the only way to modify it. After you set the score mode through + * BaseMusicalScore::SetEditMode() and BaseMusicalScore::SetPlayMode(), a reference of the + * score can be retrieved by using the methods BaseMusicalScore::GetEditCursor() and + * BaseMusicalScore::GetPlayCursor(). * * The read-only property of the score in play mode is enforced by exploiting the Cursor * class const-correctness. The drawback of this design is that you will not be able to @@ -54,6 +55,7 @@ * methods are provided in BaseMusicalScore. You can use BaseMusicalScore::AdvanceCursor() * instead of BaseMusicalScore::Cursor::Advance() for example. */ +template<template<typename> class MeasureType = Measure, typename MeasureElementType = MeasureElement> class BaseMusicalScore { @@ -78,7 +80,7 @@ /** * Return the cursor if the score is in edit mode. * - * @retrn The cursor if the score is in edit mode, 0 otherwise. + * @return The cursor if the score is in edit mode, 0 otherwise. */ Cursor* GetEditCursor(); @@ -88,7 +90,7 @@ * @param n The index of the measure to retrieve. Must be valid. * @return The n-th measure in the score. */ - const Measure* GetMeasure(size_t n) const; + const MeasureType<MeasureElementType>* GetMeasure(size_t n) const; /** * Return the number of measures in the score. @@ -134,7 +136,7 @@ * All the measures of this musical score. End-of-measure and end-of-score variables * are not actually represented. They are just a nice abstraction for the user. */ - csArray<Measure> measures; + csArray<MeasureType<MeasureElementType>> measures; }; //------------------------------------------------------------------------------------ @@ -152,13 +154,14 @@ * an empty score contains only the end-of-score element. * * Though the user is free to add and remove elements to an existing measure by using the - * methods provided by the Measure class, it must be noticed that by doing so the cursor - * is not updated in any way. This may cause undesired effects (e.g. the cursor may point - * to a non-existent measure). For this reason it is safer to use the methods of this - * class which clearly state their effect on the cursor. If you still want to use the - * methods in Measure, be sure to call Cursor::Validate() after. + * methods provided by the MeasureType class, it must be noticed that by doing so the + * cursor is not updated in any way. This may cause undesired effects (e.g. the cursor may + * point to a non-existent measure). For this reason it is safer to use the methods of + * this class which clearly state their effect on the cursor. If you still want to use the + * methods in MeasureType, be sure to call Cursor::Validate() after. */ -class BaseMusicalScore::Cursor +template<template<typename> class MeasureType, typename MeasureElementType> +class BaseMusicalScore<MeasureType, MeasureElementType>::Cursor { public: /** @@ -185,12 +188,12 @@ * @return The current measure element or a null pointer in case the cursor is on a * end-of-measure or the end-of-score element. */ - MeasureElement* GetCurrentElement(); + MeasureElementType* GetCurrentElement(); /** * @copydoc BaseMusicalScore::Cursor::GetCurrentElement() */ - const MeasureElement* GetCurrentElement() const; + const MeasureElementType* GetCurrentElement() const; /** * Get the measure currently pointed by the cursor. @@ -198,12 +201,12 @@ * @return The current measure or a null pointer if the cursor is on a end-of-measure * or the end-of-score element. */ - Measure* GetCurrentMeasure(); + MeasureType<MeasureElementType>* GetCurrentMeasure(); /** * @copydoc BaseMusicalScore::Cursor::GetCurrentMeasure() */ - const Measure* GetCurrentMeasure() const; + const MeasureType<MeasureElementType>* GetCurrentMeasure() const; /** * Check if the element after the current one is the end-of-score element. This takes @@ -226,7 +229,7 @@ * * @param element The element to insert. */ - void InsertElementAfter(const MeasureElement &element); + void InsertElementAfter(const MeasureElementType &element); /** * Insert a copy of the given element before the current one. The cursor does not @@ -236,7 +239,7 @@ * * @param element The element to insert. */ - void InsertElementBefore(const MeasureElement &element); + void InsertElementBefore(const MeasureElementType &element); /** * Insert a copy of the given measure after the current one. The cursor does not @@ -246,7 +249,7 @@ * * @param measure The measure to insert. */ - void InsertMeasureAfter(const Measure &measure); + void InsertMeasureAfter(const MeasureType<MeasureElementType> &measure); /** * Insert a copy of the given measure before the current one. The cursor does not @@ -255,7 +258,7 @@ * * @param measure The measure to insert. */ - void InsertMeasureBefore(const Measure &measure); + void InsertMeasureBefore(const MeasureType<MeasureElementType> &measure); /** * Check that the cursor is on an end-of-measure element. @@ -307,20 +310,20 @@ /** * If the cursor is pointing to a non-existent element, this moves it to the * end-of-measure character in the same measure. You have no reason to use this method - * unless you modify add/remove elements in a Measure by using its methods. + * unless you modify add/remove elements in a MeasureType by using its methods. */ void Validate(); private: // In this way only BaseMusicalScore can access the private constructor - friend BaseMusicalScore::BaseMusicalScore(); - friend Cursor* BaseMusicalScore::SetEditMode(); - friend const Cursor* BaseMusicalScore::SetPlayMode(); + friend BaseMusicalScore<MeasureType, MeasureElementType>::BaseMusicalScore(); + friend Cursor* BaseMusicalScore<MeasureType, MeasureElementType>::SetEditMode(); + friend const Cursor* BaseMusicalScore<MeasureType, MeasureElementType>::SetPlayMode(); size_t currElementIdx; ///< Index of the current element. size_t currMeasureIdx; ///< Index of the current measure. ScoreContext* context; ///< The context is valid only if it is in play mode. - BaseMusicalScore* score; ///< The musical score this cursor refers to. + BaseMusicalScore<MeasureType, MeasureElementType>* score; ///< The musical score this cursor refers to. /** * Create a cursor pointing to the first element of the score. Only the score can @@ -329,7 +332,8 @@ * @param score The musical score this cursor refers to. * @param mode If the mode is play, a context is created and mantained. */ - Cursor(BaseMusicalScore* score, BaseMusicalScore::ScoreMode mode); + Cursor(BaseMusicalScore<MeasureType, MeasureElementType>* score, + typename BaseMusicalScore<MeasureType, MeasureElementType>::ScoreMode mode); /** * Check if an eventual repeat section must be repeated after this measure. The @@ -356,4 +360,6 @@ void Reset(); }; +#include "basemusicscore.hpp" + #endif // BASE_MUSIC_SCORE_H Copied: trunk/src/common/music/basemusicscore.hpp (from rev 9499, trunk/src/common/music/basemusicscore.cpp) =================================================================== --- trunk/src/common/music/basemusicscore.hpp (rev 0) +++ trunk/src/common/music/basemusicscore.hpp 2014-05-18 22:09:28 UTC (rev 9508) @@ -0,0 +1,488 @@ +/* + * basemusicscore.cpp, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ +#define INVALID_CURSOR (size_t)-1 + +template<template<typename> class MeasureType, typename MeasureElementType> +BaseMusicalScore<MeasureType, MeasureElementType>::BaseMusicalScore() + : cursor(new Cursor(this, EDIT)), mode(EDIT) +{ +} + +template<template<typename> class MeasureType, typename MeasureElementType> +BaseMusicalScore<MeasureType, MeasureElementType>::~BaseMusicalScore() +{ + delete cursor; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::AdvanceCursor( + bool ignoreEndOfMeasure) +{ + return cursor->Advance(ignoreEndOfMeasure); +} + +template<template<typename> class MeasureType, typename MeasureElementType> +typename BaseMusicalScore<MeasureType, MeasureElementType>::Cursor* + BaseMusicalScore<MeasureType, MeasureElementType>::GetEditCursor() +{ + if(mode != EDIT) + { + return 0; + } + return cursor; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +const MeasureType<MeasureElementType>* + BaseMusicalScore<MeasureType, MeasureElementType>::GetMeasure(size_t n) const +{ + CS_ASSERT(n < measures.GetSize()); + return &measures[n]; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +const typename BaseMusicalScore<MeasureType, MeasureElementType>::Cursor* + BaseMusicalScore<MeasureType, MeasureElementType>::GetPlayCursor() const +{ + if(mode != PLAY) + { + return 0; + } + return cursor; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +typename BaseMusicalScore<MeasureType, MeasureElementType>::Cursor* + BaseMusicalScore<MeasureType, MeasureElementType>::SetEditMode() +{ + if(mode == PLAY) + { + delete cursor; + cursor = new Cursor(this, EDIT); + } + return cursor; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +const typename BaseMusicalScore<MeasureType, MeasureElementType>::Cursor* + BaseMusicalScore<MeasureType, MeasureElementType>::SetPlayMode() +{ + if(mode == EDIT) + { + delete cursor; + cursor = new Cursor(this, PLAY); + } + return cursor; +} + +//--------------------------------------------------------------------------------------- + +template<template<typename> class MeasureType, typename MeasureElementType> +BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::~Cursor() +{ + if(context != 0) + { + delete context; + } +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::Advance( + bool ignoreEndOfMeasure) +{ + bool isLastElem; + + // if this is the last element we move the cursor to end-of-score + if(!HasNext(ignoreEndOfMeasure)) + { + currElementIdx = INVALID_CURSOR; + currMeasureIdx = INVALID_CURSOR; + return false; + } + + // check if this is the last element of the current measure + isLastElem = (currElementIdx == score->measures[currMeasureIdx].GetNElements() - 1); + if(IsEndOfMeasure() || (isLastElem && ignoreEndOfMeasure)) + { + currElementIdx = 0; + + // in play mode we must take repeats into account and update the context + if(score->mode == PLAY) + { + if(CheckRepeat()) + { + currMeasureIdx = context->RestoreLastStartRepeat(); + } + else + { + // we must skip all the endings that end a repeat + // section that has been already performed + do + { + currMeasureIdx++; + }while(score->measures[currMeasureIdx].IsEnding() && + score->measures[currMeasureIdx].IsEndRepeat() && + !CheckRepeat()); + + // the context doesn't have to be updated with skipped endings + context->Update(currMeasureIdx, score->measures[currMeasureIdx]); + } + } + else // mode == EDIT + { + currMeasureIdx++; + } + } + else + { + // we update the context with the accidentals of the previous element + if(score->mode == PLAY) + { + context->Update(score->measures[currMeasureIdx].GetElement(currElementIdx)); + } + + if(isLastElem) // ignoreEndOfMeasure has been checked before + { + currElementIdx = INVALID_CURSOR; + } + else + { + currElementIdx++; + } + } + + return true; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +MeasureElementType* + BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentElement() +{ + return const_cast<MeasureElementType*>( + static_cast<const BaseMusicalScore<MeasureType, MeasureElementType>::Cursor &> + (*this).GetCurrentElement()); +} + +template<template<typename> class MeasureType, typename MeasureElementType> +const MeasureElementType* + BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentElement() const +{ + if(IsValid()) + { + return &score->measures[currMeasureIdx].GetElement(currElementIdx); + } + return 0; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +MeasureType<MeasureElementType>* + BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentMeasure() +{ + return const_cast<MeasureType<MeasureElementType>*>( + static_cast<const BaseMusicalScore<MeasureType, MeasureElementType>::Cursor &> + (*this).GetCurrentMeasure()); +} + +template<template<typename> class MeasureType, typename MeasureElementType> +const MeasureType<MeasureElementType>* + BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentMeasure() const +{ + if(IsEndOfScore()) + { + return 0; + } + return &score->measures[currMeasureIdx]; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::HasNext( + bool ignoreEndOfMeasure) const +{ + if(IsEndOfScore()) + { + return false; + } + + if(score->mode == PLAY && CheckRepeat()) + { + return true; + } + + // if in edit mode or if there are no repeats to perform + return HasNextWritten(ignoreEndOfMeasure); +} + +template<template<typename> class MeasureType, typename MeasureElementType> +void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::InsertElementAfter( + const MeasureElementType &element) +{ + MeasureType<MeasureElementType>* currMeasure; // convenience variable + + if(IsEndOfScore()) + { + // we push a new measure + MeasureType<MeasureElementType> measure; + currMeasureIdx = score->measures.Push(measure); + CS_ASSERT(IsEndOfMeasure()); + } + + currMeasure = &score->measures[currMeasureIdx]; + if(IsEndOfMeasure()) + { + currElementIdx = currMeasure->PushElement(element); + CS_ASSERT(IsValid()); + } + else if(currElementIdx == currMeasure->GetNElements() - 1) + { + currMeasure->PushElement(element); + } + else + { + currMeasure->InsertElement(currElementIdx + 1, element); + } +} + +template<template<typename> class MeasureType, typename MeasureElementType> +void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::InsertElementBefore( + const MeasureElementType &element) +{ + if(IsEndOfScore()) + { + // we push a new measure + MeasureType<MeasureElementType> measure; + currMeasureIdx = score->measures.Push(measure); + CS_ASSERT(IsEndOfMeasure()); + } + + if(IsValid()) + { + // the index of the current element increase after the insertion + score->measures[currMeasureIdx].InsertElement(currElementIdx, element); + currElementIdx++; + } + else // IsEndOfMeasure() == true + { + score->measures[currMeasureIdx].PushElement(element); + } +} + +template<template<typename> class MeasureType, typename MeasureElementType> +void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::InsertMeasureAfter( + const MeasureType<MeasureElementType> &measure) +{ + if(IsEndOfScore()) + { + // we move the cursor to the first element of the newly inserted measure + currMeasureIdx = score->measures.Push(measure); + if(!measure.IsEmpty()) + { + currElementIdx = 0; + } // else the cursor is on end-of-measure + } + else if(currMeasureIdx == score->measures.GetSize() - 1) + { + // if this is the last measure we must use Push() + score->measures.Push(measure); + } + else + { + score->measures.Insert(currMeasureIdx + 1, measure); + } +} + +template<template<typename> class MeasureType, typename MeasureElementType> +void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::InsertMeasureBefore( + const MeasureType<MeasureElementType> &measure) +{ + if(IsEndOfScore()) + { + score->measures.Push(measure); + } + else + { + // the index of the current measure increases after the insertion + score->measures.Insert(currMeasureIdx, measure); + currMeasureIdx++; + } +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::IsEndOfMeasure() const +{ + return currElementIdx == INVALID_CURSOR && currMeasureIdx != INVALID_CURSOR; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::IsEndOfScore() const +{ + return currElementIdx == INVALID_CURSOR && currMeasureIdx == INVALID_CURSOR; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::IsValid() const +{ + return currElementIdx != INVALID_CURSOR; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::RemoveCurrentElement() +{ + if(!IsValid()) + { + return false; + } + + score->measures[currMeasureIdx].DeleteElement(currElementIdx); + + if(currElementIdx > 0) + { + currElementIdx--; + } + else if(score->measures[currMeasureIdx].IsEmpty()) + { + currElementIdx = INVALID_CURSOR; // move to end-of-measure + } // else the cursor is on the element after the removed one + + return true; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::RemoveCurrentMeasure() +{ + if(IsEndOfScore()) + { + return false; + } + + score->measures.DeleteIndex(currMeasureIdx); + + if(score->measures.IsEmpty()) + { + // move to end-of-score + currElementIdx = INVALID_CURSOR; + currMeasureIdx = INVALID_CURSOR; + } + else + { + if(currMeasureIdx > 0) + { + currMeasureIdx--; + } // else the score is on the measure next to the removed one + + if(score->measures[currMeasureIdx].IsEmpty()) + { + currElementIdx = INVALID_CURSOR; // move to end-of-measure + } + else + { + currElementIdx = 0; + } + } + + return true; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::Validate() +{ + if(IsValid() && currElementIdx >= score->measures[currMeasureIdx].GetNElements()) + { + currElementIdx = INVALID_CURSOR; + } +} + +template<template<typename> class MeasureType, typename MeasureElementType> +BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::Cursor( + BaseMusicalScore<MeasureType, MeasureElementType>* score_, + typename BaseMusicalScore<MeasureType, MeasureElementType>::ScoreMode mode) +: context(0), score(score_) +{ + if(mode == PLAY) + { + context = new ScoreContext(); + } + Reset(); +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::CheckRepeat() const +{ + int nEndRepeat = score->measures[currMeasureIdx].GetNEndRepeat(); + return context->GetNPerformedRepeats(currMeasureIdx) < nEndRepeat; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::HasNextWritten( + bool ignoreEndOfMeasure) const +{ + bool isLastMeasure = (currMeasureIdx == score->measures.GetSize() - 1); + + CS_ASSERT(!IsEndOfScore()); + + if(IsEndOfMeasure()) + { + return isLastMeasure; + } + CS_ASSERT(IsValid()); + + if(ignoreEndOfMeasure && isLastMeasure && + currElementIdx == score->measures[currMeasureIdx].GetNElements() - 1) + { + return false; + } + return true; +} + +template<template<typename> class MeasureType, typename MeasureElementType> +void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::Reset() +{ + if(score->measures.GetSize() == 0) + { + // end-of-score + currElementIdx = INVALID_CURSOR; + currMeasureIdx = INVALID_CURSOR; + } + else if(score->measures[0].IsEmpty()) + { + // end-of-measure + currElementIdx = INVALID_CURSOR; + currMeasureIdx = 0; + } + else + { + currElementIdx = 0; + currMeasureIdx = 0; + } +} Deleted: trunk/src/common/music/musicxmlscore.cpp =================================================================== --- trunk/src/common/music/musicxmlscore.cpp 2014-05-12 14:52:35 UTC (rev 9507) +++ trunk/src/common/music/musicxmlscore.cpp 2014-05-18 22:09:28 UTC (rev 9508) @@ -1,35 +0,0 @@ -/* - * musicxmlscore.cpp, Author: Andrea Rizzi <88w...@gm...> - * - * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) - * - * - * 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 (version 2 of the License) - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * - */ - - -//==================================================================================== -// Crystal Space Includes -//==================================================================================== - -//==================================================================================== -// Project Includes -//==================================================================================== - -//==================================================================================== -// Local Includes -//==================================================================================== - -//------------------------------------------------------------------------------------ -// Forward Declarations -//------------------------------------------------------------------------------------ Copied: trunk/src/common/music/musicxmlscore.hpp (from rev 9499, trunk/src/common/music/musicxmlscore.cpp) =================================================================== --- trunk/src/common/music/musicxmlscore.hpp (rev 0) +++ trunk/src/common/music/musicxmlscore.hpp 2014-05-18 22:09:28 UTC (rev 9508) @@ -0,0 +1,35 @@ +/* + * musicxmlscore.cpp, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2014-05-12 14:52:35 UTC (rev 9507) +++ trunk/src/common/music/scoreelements.cpp 2014-05-18 22:09:28 UTC (rev 9508) @@ -260,13 +260,15 @@ //-------------------------------------------------- -Measure::MeasureAttributes::MeasureAttributes() +template<typename MeasureElementType> +Measure<MeasureElementType>::MeasureAttributes::MeasureAttributes() : tempo(UNDEFINED_MEASURE_ATTRIBUTE), beats(UNDEFINED_MEASURE_ATTRIBUTE), beatType(UNDEFINED_MEASURE_ATTRIBUTE), fifths(UNDEFINED_MEASURE_ATTRIBUTE) { } -bool Measure::MeasureAttributes::IsUndefined() const +template<typename MeasureElementType> +bool Measure<MeasureElementType>::MeasureAttributes::IsUndefined() const { return tempo == UNDEFINED_MEASURE_ATTRIBUTE && beats == UNDEFINED_MEASURE_ATTRIBUTE && @@ -274,8 +276,9 @@ fifths == UNDEFINED_MEASURE_ATTRIBUTE; } -void Measure::MeasureAttributes::UpdateAttributes( - const Measure::MeasureAttributes &attributes) +template<typename MeasureElementType> +void Measure<MeasureElementType>::MeasureAttributes::UpdateAttributes( + const typename Measure<MeasureElementType>::MeasureAttributes &attributes) { if(attributes.beats != UNDEFINED_MEASURE_ATTRIBUTE) { @@ -295,17 +298,20 @@ } } -Measure::Measure() +template<typename MeasureElementType> +Measure<MeasureElementType>::Measure() : isEnding(false), isStartRepeat(false), nEndRepeat(0), attributes(0) { } -Measure::~Measure() +template<typename MeasureElementType> +Measure<MeasureElementType>::~Measure() { DeleteAttributes(); } -void Measure::Fit(const MeasureAttributes* attributes_) +template<typename MeasureElementType> +void Measure<MeasureElementType>::Fit(const MeasureAttributes* attributes_) { int measDuration = 0; // the duration that the measure is supposed to be int currDuration = 0; // the sum of the duration of the elements @@ -365,13 +371,15 @@ while(currDuration < measDuration) { tempDuration = GetBiggestDuration(measDuration - currDuration); - PushElement(MeasureElement(tempDuration)); + PushElement(MeasureElementType(tempDuration)); currDuration += tempDuration; } } } -Measure::MeasureAttributes Measure::GetAttributes() const +template<typename MeasureElementType> +typename Measure<MeasureElementType>::MeasureAttributes + Measure<MeasureElementType>::GetAttributes() const { if(attributes == 0) { @@ -380,7 +388,8 @@ return *attributes; } -void Measure::InsertElement(size_t n, const MeasureElement &element) +template<typename MeasureElementType> +void Measure<MeasureElementType>::InsertElement(size_t n, const MeasureElementType &element) { if(!elements.Insert(n, element)) { @@ -388,7 +397,8 @@ } } -void Measure::SetBeat(int beats, int beatType) +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetBeat(int beats, int beatType) { if(beats != UNDEFINED_MEASURE_ATTRIBUTE && beatType != UNDEFINED_MEASURE_ATTRIBUTE) { @@ -413,12 +423,14 @@ } } -void Measure::SetEnding(bool isEnding_) +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetEnding(bool isEnding_) { isEnding = isEnding_; } -void Measure::SetFifths(int fifths) +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetFifths(int fifths) { if(fifths != UNDEFINED_MEASURE_ATTRIBUTE) { @@ -432,18 +444,21 @@ } } -void Measure::SetNEndRepeat(int nEndRepeat_) +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetNEndRepeat(int nEndRepeat_) { CS_ASSERT(nEndRepeat_ >= 0); nEndRepeat = nEndRepeat_; } -void Measure::SetStartRepeat(bool isStartRepeat_) +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetStartRepeat(bool isStartRepeat_) { isStartRepeat = isStartRepeat_; } -void Measure::SetTempo(int tempo) +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetTempo(int tempo) { if(tempo != UNDEFINED_MEASURE_ATTRIBUTE) { @@ -457,7 +472,8 @@ } } -void Measure::CreateAttributes() +template<typename MeasureElementType> +void Measure<MeasureElementType>::CreateAttributes() { if(attributes == 0) { @@ -465,7 +481,8 @@ } } -void Measure::DeleteAttributes() +template<typename MeasureElementType> +void Measure<MeasureElementType>::DeleteAttributes() { if(attributes != 0) { @@ -474,7 +491,8 @@ } } -void Measure::UpdateAttributes() +template<typename MeasureElementType> +void Measure<MeasureElementType>::UpdateAttributes() { if(attributes != 0) { @@ -508,7 +526,7 @@ noteContext.UpdateContext(element); } -void ScoreContext::Update(int measureID, const Measure &measure) +void ScoreContext::Update(int measureID, const Measure<MeasureElement> &measure) { noteContext.ResetContext(); measureAttributes.UpdateAttributes(measure.GetAttributes()); Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2014-05-12 14:52:35 UTC (rev 9507) +++ trunk/src/common/music/scoreelements.h 2014-05-18 22:09:28 UTC (rev 9508) @@ -290,6 +290,7 @@ /** * A measure containing measure elements. */ +template<typename MeasureElementType> class Measure { public: @@ -441,12 +442,12 @@ * @param n The index of the element. * @return The element. */ - MeasureElement &GetElement(size_t n) { return elements[n]; } + MeasureElementType &GetElement(size_t n) { return elements[n]; } /** * @copydoc Measure::GetElement(size_t) */ - const MeasureElement &GetElement(size_t n) const { return elements[n]; } + const MeasureElementType &GetElement(size_t n) const { return elements[n]; } /** * Get the number of elements in this measure. @@ -470,7 +471,7 @@ * @param n The number of the element after which the given element must be inserted. * @param element The element to insert in the measure. */ - void InsertElement(size_t n, const MeasureElement &element); + void InsertElement(size_t n, const MeasureElementType &element); /** * Check if the measure has at least one element. @@ -506,7 +507,10 @@ * @param element The element to push in the measure. * @return The index of the pushed element. */ - size_t PushElement(const MeasureElement &element) { return elements.Push(element); } + size_t PushElement(const MeasureElementType &element) + { + return elements.Push(element); + } /** * Set the beat information. Both beats and beatType must be defined. If only one of @@ -553,7 +557,7 @@ bool isEnding; ///< True if this is an ending measure. bool isStartRepeat; ///< True if this measure starts a repeat section. int nEndRepeat; ///< Number of times the repeat must be performed. - csArray<MeasureElement> elements; ///< Elements of this measure. + csArray<MeasureElementType> elements; ///< Elements of this measure. /** * Attributes of this measure. We keep this allocated object only if the measure @@ -562,12 +566,12 @@ MeasureAttributes* attributes; /** - * Create a new MeasureAttributes object if it does not exists already. + * Create a new MeasureAttributes object if it does not exist already. */ void CreateAttributes(); /** - * Delete the attributes (if it exists) and free the memory. + * Delete the attributes (if the object exists) and free the memory. */ void DeleteAttributes(); @@ -595,7 +599,7 @@ /** * Attributes specified in the score up to now. Must be updated at every measure. */ - Measure::MeasureAttributes measureAttributes; + Measure<MeasureElement>::MeasureAttributes measureAttributes; /** * Constructor. @@ -627,7 +631,7 @@ * @param measureID The ID of the updated measure. * @param measure The new measure reached by the cursor. */ - void Update(int measureID, const Measure &measure); + void Update(int measureID, const Measure<MeasureElement> &measure); /** * Keep the list of previous accidentals updated. @@ -643,7 +647,7 @@ * Cache element used to store attributes in the last measure containins a start * repeat. */ - Measure::MeasureAttributes lastStartRepeatAttributes; + Measure<MeasureElement>::MeasureAttributes lastStartRepeatAttributes; /** * Keeps track of the repeat sections already performed. It is indexed by measure index and This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2014-05-22 20:59:50
|
Revision: 9509 http://sourceforge.net/p/planeshift/code/9509 Author: whacko88 Date: 2014-05-22 20:59:47 +0000 (Thu, 22 May 2014) Log Message: ----------- Separated template Measure implementation from non-template class definition Modified Paths: -------------- trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Added Paths: ----------- trunk/src/common/music/scoreelements.hpp Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2014-05-18 22:09:28 UTC (rev 9508) +++ trunk/src/common/music/scoreelements.cpp 2014-05-22 20:59:47 UTC (rev 9509) @@ -260,251 +260,6 @@ //-------------------------------------------------- -template<typename MeasureElementType> -Measure<MeasureElementType>::MeasureAttributes::MeasureAttributes() -: tempo(UNDEFINED_MEASURE_ATTRIBUTE), beats(UNDEFINED_MEASURE_ATTRIBUTE), -beatType(UNDEFINED_MEASURE_ATTRIBUTE), fifths(UNDEFINED_MEASURE_ATTRIBUTE) -{ -} - -template<typename MeasureElementType> -bool Measure<MeasureElementType>::MeasureAttributes::IsUndefined() const -{ - return tempo == UNDEFINED_MEASURE_ATTRIBUTE && - beats == UNDEFINED_MEASURE_ATTRIBUTE && - beatType == UNDEFINED_MEASURE_ATTRIBUTE && - fifths == UNDEFINED_MEASURE_ATTRIBUTE; -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::MeasureAttributes::UpdateAttributes( - const typename Measure<MeasureElementType>::MeasureAttributes &attributes) -{ - if(attributes.beats != UNDEFINED_MEASURE_ATTRIBUTE) - { - beats = attributes.beats; - } - if(attributes.beatType != UNDEFINED_MEASURE_ATTRIBUTE) - { - beatType = attributes.beatType; - } - if(attributes.fifths != UNDEFINED_MEASURE_ATTRIBUTE) - { - fifths = attributes.fifths; - } - if(attributes.tempo != UNDEFINED_MEASURE_ATTRIBUTE) - { - tempo = attributes.tempo; - } -} - -template<typename MeasureElementType> -Measure<MeasureElementType>::Measure() - : isEnding(false), isStartRepeat(false), nEndRepeat(0), attributes(0) -{ -} - -template<typename MeasureElementType> -Measure<MeasureElementType>::~Measure() -{ - DeleteAttributes(); -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::Fit(const MeasureAttributes* attributes_) -{ - int measDuration = 0; // the duration that the measure is supposed to be - int currDuration = 0; // the sum of the duration of the elements - size_t cutIdx = 0; // the index contains the index of the first element to be cut - - // Convenience variables - int beats = UNDEFINED_MEASURE_ATTRIBUTE; - int beatType = UNDEFINED_MEASURE_ATTRIBUTE; - - // Getting beat information - if(attributes != 0) - { - beats = attributes->GetBeats(); - beatType = attributes->GetBeatType(); - } - if(attributes_ != 0 && (beats == UNDEFINED_MEASURE_ATTRIBUTE || - beatType == UNDEFINED_MEASURE_ATTRIBUTE)) - { - beats = attributes_->GetBeats(); - beatType = attributes_->GetBeatType(); - } - - CS_ASSERT(beats != UNDEFINED_MEASURE_ATTRIBUTE && - beatType != UNDEFINED_MEASURE_ATTRIBUTE); - - // Determining the measure duration - measDuration = DURATION_QUARTER_DIVISIONS * beats / beatType; - - // Determining which notes exceed the measure - while(cutIdx < elements.GetSize() && currDuration <= measDuration) - { - currDuration += elements[cutIdx].GetDuration(); - cutIdx++; - } - - // Cutting or filling - if(currDuration > measDuration) // cut - { - int prevDuration = currDuration - elements.Get(cutIdx).GetDuration(); - - // If there's a note that exceeds the measure duration we can cut it first - if(prevDuration < measDuration) - { - Duration tempDuration = GetBiggestDuration(measDuration - prevDuration); - elements.Get(cutIdx).SetDuration(tempDuration); - cutIdx++; - } - for(size_t i = elements.GetSize() - 1; i >= cutIdx; i--) - { - DeleteElement(i); - } - } - else if(currDuration < measDuration) // fill - { - Duration tempDuration; - - while(currDuration < measDuration) - { - tempDuration = GetBiggestDuration(measDuration - currDuration); - PushElement(MeasureElementType(tempDuration)); - currDuration += tempDuration; - } - } -} - -template<typename MeasureElementType> -typename Measure<MeasureElementType>::MeasureAttributes - Measure<MeasureElementType>::GetAttributes() const -{ - if(attributes == 0) - { - return MeasureAttributes(); // all attributes are undefined - } - return *attributes; -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::InsertElement(size_t n, const MeasureElementType &element) -{ - if(!elements.Insert(n, element)) - { - PushElement(element); - } -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::SetBeat(int beats, int beatType) -{ - if(beats != UNDEFINED_MEASURE_ATTRIBUTE && beatType != UNDEFINED_MEASURE_ATTRIBUTE) - { - // check if beatType_ is a power of 2 - CS_ASSERT((beatType > 0) && ((beatType & (~beatType + 1)) == beatType)); - CS_ASSERT(beats > 0); - CreateAttributes(); - } - - // doesn't make sense if one is defined and the other is not - if(beats == UNDEFINED_MEASURE_ATTRIBUTE || beatType == UNDEFINED_MEASURE_ATTRIBUTE) - { - beats = UNDEFINED_MEASURE_ATTRIBUTE; - beatType = UNDEFINED_MEASURE_ATTRIBUTE; - } - - if(attributes != 0) - { - attributes->SetBeats(beats); - attributes->SetBeatType(beatType); - UpdateAttributes(); - } -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::SetEnding(bool isEnding_) -{ - isEnding = isEnding_; -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::SetFifths(int fifths) -{ - if(fifths != UNDEFINED_MEASURE_ATTRIBUTE) - { - CS_ASSERT(fifths >= -7 && fifths <= 7); - CreateAttributes(); - } - if(attributes != 0) - { - attributes->SetFifths(fifths); - UpdateAttributes(); - } -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::SetNEndRepeat(int nEndRepeat_) -{ - CS_ASSERT(nEndRepeat_ >= 0); - nEndRepeat = nEndRepeat_; -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::SetStartRepeat(bool isStartRepeat_) -{ - isStartRepeat = isStartRepeat_; -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::SetTempo(int tempo) -{ - if(tempo != UNDEFINED_MEASURE_ATTRIBUTE) - { - CS_ASSERT(tempo > 0); - CreateAttributes(); - } - if(attributes != 0) - { - attributes->SetTempo(tempo); - UpdateAttributes(); - } -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::CreateAttributes() -{ - if(attributes == 0) - { - attributes = new MeasureAttributes(); - } -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::DeleteAttributes() -{ - if(attributes != 0) - { - delete attributes; - attributes = 0; - } -} - -template<typename MeasureElementType> -void Measure<MeasureElementType>::UpdateAttributes() -{ - if(attributes != 0) - { - if(attributes->IsUndefined()) - { - DeleteAttributes(); - } - } -} - -//-------------------------------------------------- - ScoreContext::ScoreContext() : lastStartRepeatID(0) { Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2014-05-18 22:09:28 UTC (rev 9508) +++ trunk/src/common/music/scoreelements.h 2014-05-22 20:59:47 UTC (rev 9509) @@ -656,6 +656,8 @@ csHash<int, int> repeatsDone; }; +#include "scoreelements.hpp" + /** @} */ #endif // SCORE_ELEMENTS_H Added: trunk/src/common/music/scoreelements.hpp =================================================================== --- trunk/src/common/music/scoreelements.hpp (rev 0) +++ trunk/src/common/music/scoreelements.hpp 2014-05-22 20:59:47 UTC (rev 9509) @@ -0,0 +1,280 @@ +/* + * scoreelements.cpp, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2014 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ +using namespace psMusic; + + +template<typename MeasureElementType> +Measure<MeasureElementType>::MeasureAttributes::MeasureAttributes() +: tempo(UNDEFINED_MEASURE_ATTRIBUTE), beats(UNDEFINED_MEASURE_ATTRIBUTE), +beatType(UNDEFINED_MEASURE_ATTRIBUTE), fifths(UNDEFINED_MEASURE_ATTRIBUTE) +{ +} + +template<typename MeasureElementType> +bool Measure<MeasureElementType>::MeasureAttributes::IsUndefined() const +{ + return tempo == UNDEFINED_MEASURE_ATTRIBUTE && + beats == UNDEFINED_MEASURE_ATTRIBUTE && + beatType == UNDEFINED_MEASURE_ATTRIBUTE && + fifths == UNDEFINED_MEASURE_ATTRIBUTE; +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::MeasureAttributes::UpdateAttributes( + const typename Measure<MeasureElementType>::MeasureAttributes &attributes) +{ + if(attributes.beats != UNDEFINED_MEASURE_ATTRIBUTE) + { + beats = attributes.beats; + } + if(attributes.beatType != UNDEFINED_MEASURE_ATTRIBUTE) + { + beatType = attributes.beatType; + } + if(attributes.fifths != UNDEFINED_MEASURE_ATTRIBUTE) + { + fifths = attributes.fifths; + } + if(attributes.tempo != UNDEFINED_MEASURE_ATTRIBUTE) + { + tempo = attributes.tempo; + } +} + +template<typename MeasureElementType> +Measure<MeasureElementType>::Measure() + : isEnding(false), isStartRepeat(false), nEndRepeat(0), attributes(0) +{ +} + +template<typename MeasureElementType> +Measure<MeasureElementType>::~Measure() +{ + DeleteAttributes(); +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::Fit(const MeasureAttributes* attributes_) +{ + int measDuration = 0; // the duration that the measure is supposed to be + int currDuration = 0; // the sum of the duration of the elements + size_t cutIdx = 0; // the index contains the index of the first element to be cut + + // Convenience variables + int beats = UNDEFINED_MEASURE_ATTRIBUTE; + int beatType = UNDEFINED_MEASURE_ATTRIBUTE; + + // Getting beat information + if(attributes != 0) + { + beats = attributes->GetBeats(); + beatType = attributes->GetBeatType(); + } + if(attributes_ != 0 && (beats == UNDEFINED_MEASURE_ATTRIBUTE || + beatType == UNDEFINED_MEASURE_ATTRIBUTE)) + { + beats = attributes_->GetBeats(); + beatType = attributes_->GetBeatType(); + } + + CS_ASSERT(beats != UNDEFINED_MEASURE_ATTRIBUTE && + beatType != UNDEFINED_MEASURE_ATTRIBUTE); + + // Determining the measure duration + measDuration = DURATION_QUARTER_DIVISIONS * beats / beatType; + + // Determining which notes exceed the measure + while(cutIdx < elements.GetSize() && currDuration <= measDuration) + { + currDuration += elements[cutIdx].GetDuration(); + cutIdx++; + } + + // Cutting or filling + if(currDuration > measDuration) // cut + { + int prevDuration = currDuration - elements.Get(cutIdx).GetDuration(); + + // If there's a note that exceeds the measure duration we can cut it first + if(prevDuration < measDuration) + { + Duration tempDuration = GetBiggestDuration(measDuration - prevDuration); + elements.Get(cutIdx).SetDuration(tempDuration); + cutIdx++; + } + for(size_t i = elements.GetSize() - 1; i >= cutIdx; i--) + { + DeleteElement(i); + } + } + else if(currDuration < measDuration) // fill + { + Duration tempDuration; + + while(currDuration < measDuration) + { + tempDuration = GetBiggestDuration(measDuration - currDuration); + PushElement(MeasureElementType(tempDuration)); + currDuration += tempDuration; + } + } +} + +template<typename MeasureElementType> +typename Measure<MeasureElementType>::MeasureAttributes + Measure<MeasureElementType>::GetAttributes() const +{ + if(attributes == 0) + { + return MeasureAttributes(); // all attributes are undefined + } + return *attributes; +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::InsertElement(size_t n, const MeasureElementType &element) +{ + if(!elements.Insert(n, element)) + { + PushElement(element); + } +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetBeat(int beats, int beatType) +{ + if(beats != UNDEFINED_MEASURE_ATTRIBUTE && beatType != UNDEFINED_MEASURE_ATTRIBUTE) + { + // check if beatType_ is a power of 2 + CS_ASSERT((beatType > 0) && ((beatType & (~beatType + 1)) == beatType)); + CS_ASSERT(beats > 0); + CreateAttributes(); + } + + // doesn't make sense if one is defined and the other is not + if(beats == UNDEFINED_MEASURE_ATTRIBUTE || beatType == UNDEFINED_MEASURE_ATTRIBUTE) + { + beats = UNDEFINED_MEASURE_ATTRIBUTE; + beatType = UNDEFINED_MEASURE_ATTRIBUTE; + } + + if(attributes != 0) + { + attributes->SetBeats(beats); + attributes->SetBeatType(beatType); + UpdateAttributes(); + } +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetEnding(bool isEnding_) +{ + isEnding = isEnding_; +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetFifths(int fifths) +{ + if(fifths != UNDEFINED_MEASURE_ATTRIBUTE) + { + CS_ASSERT(fifths >= -7 && fifths <= 7); + CreateAttributes(); + } + if(attributes != 0) + { + attributes->SetFifths(fifths); + UpdateAttributes(); + } +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetNEndRepeat(int nEndRepeat_) +{ + CS_ASSERT(nEndRepeat_ >= 0); + nEndRepeat = nEndRepeat_; +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetStartRepeat(bool isStartRepeat_) +{ + isStartRepeat = isStartRepeat_; +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::SetTempo(int tempo) +{ + if(tempo != UNDEFINED_MEASURE_ATTRIBUTE) + { + CS_ASSERT(tempo > 0); + CreateAttributes(); + } + if(attributes != 0) + { + attributes->SetTempo(tempo); + UpdateAttributes(); + } +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::CreateAttributes() +{ + if(attributes == 0) + { + attributes = new MeasureAttributes(); + } +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::DeleteAttributes() +{ + if(attributes != 0) + { + delete attributes; + attributes = 0; + } +} + +template<typename MeasureElementType> +void Measure<MeasureElementType>::UpdateAttributes() +{ + if(attributes != 0) + { + if(attributes->IsUndefined()) + { + DeleteAttributes(); + } + } +} + Property changes on: trunk/src/common/music/scoreelements.hpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2014-05-22 22:02:46
|
Revision: 9510 http://sourceforge.net/p/planeshift/code/9510 Author: whacko88 Date: 2014-05-22 22:02:43 +0000 (Thu, 22 May 2014) Log Message: ----------- removed tabs Modified Paths: -------------- trunk/src/common/music/basemusicscore.h trunk/src/common/music/basemusicscore.hpp trunk/src/common/music/scoreelements.h trunk/src/common/music/scoreelements.hpp Modified: trunk/src/common/music/basemusicscore.h =================================================================== --- trunk/src/common/music/basemusicscore.h 2014-05-22 20:59:47 UTC (rev 9509) +++ trunk/src/common/music/basemusicscore.h 2014-05-22 22:02:43 UTC (rev 9510) @@ -333,7 +333,7 @@ * @param mode If the mode is play, a context is created and mantained. */ Cursor(BaseMusicalScore<MeasureType, MeasureElementType>* score, - typename BaseMusicalScore<MeasureType, MeasureElementType>::ScoreMode mode); + typename BaseMusicalScore<MeasureType, MeasureElementType>::ScoreMode mode); /** * Check if an eventual repeat section must be repeated after this measure. The Modified: trunk/src/common/music/basemusicscore.hpp =================================================================== --- trunk/src/common/music/basemusicscore.hpp 2014-05-22 20:59:47 UTC (rev 9509) +++ trunk/src/common/music/basemusicscore.hpp 2014-05-22 22:02:43 UTC (rev 9510) @@ -48,14 +48,14 @@ template<template<typename> class MeasureType, typename MeasureElementType> bool BaseMusicalScore<MeasureType, MeasureElementType>::AdvanceCursor( - bool ignoreEndOfMeasure) + bool ignoreEndOfMeasure) { return cursor->Advance(ignoreEndOfMeasure); } template<template<typename> class MeasureType, typename MeasureElementType> typename BaseMusicalScore<MeasureType, MeasureElementType>::Cursor* - BaseMusicalScore<MeasureType, MeasureElementType>::GetEditCursor() + BaseMusicalScore<MeasureType, MeasureElementType>::GetEditCursor() { if(mode != EDIT) { @@ -66,7 +66,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> const MeasureType<MeasureElementType>* - BaseMusicalScore<MeasureType, MeasureElementType>::GetMeasure(size_t n) const + BaseMusicalScore<MeasureType, MeasureElementType>::GetMeasure(size_t n) const { CS_ASSERT(n < measures.GetSize()); return &measures[n]; @@ -74,7 +74,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> const typename BaseMusicalScore<MeasureType, MeasureElementType>::Cursor* - BaseMusicalScore<MeasureType, MeasureElementType>::GetPlayCursor() const + BaseMusicalScore<MeasureType, MeasureElementType>::GetPlayCursor() const { if(mode != PLAY) { @@ -85,7 +85,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> typename BaseMusicalScore<MeasureType, MeasureElementType>::Cursor* - BaseMusicalScore<MeasureType, MeasureElementType>::SetEditMode() + BaseMusicalScore<MeasureType, MeasureElementType>::SetEditMode() { if(mode == PLAY) { @@ -97,7 +97,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> const typename BaseMusicalScore<MeasureType, MeasureElementType>::Cursor* - BaseMusicalScore<MeasureType, MeasureElementType>::SetPlayMode() + BaseMusicalScore<MeasureType, MeasureElementType>::SetPlayMode() { if(mode == EDIT) { @@ -120,7 +120,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::Advance( - bool ignoreEndOfMeasure) + bool ignoreEndOfMeasure) { bool isLastElem; @@ -188,16 +188,16 @@ template<template<typename> class MeasureType, typename MeasureElementType> MeasureElementType* - BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentElement() + BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentElement() { return const_cast<MeasureElementType*>( static_cast<const BaseMusicalScore<MeasureType, MeasureElementType>::Cursor &> - (*this).GetCurrentElement()); + (*this).GetCurrentElement()); } template<template<typename> class MeasureType, typename MeasureElementType> const MeasureElementType* - BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentElement() const + BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentElement() const { if(IsValid()) { @@ -208,16 +208,16 @@ template<template<typename> class MeasureType, typename MeasureElementType> MeasureType<MeasureElementType>* - BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentMeasure() + BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentMeasure() { return const_cast<MeasureType<MeasureElementType>*>( static_cast<const BaseMusicalScore<MeasureType, MeasureElementType>::Cursor &> - (*this).GetCurrentMeasure()); + (*this).GetCurrentMeasure()); } template<template<typename> class MeasureType, typename MeasureElementType> const MeasureType<MeasureElementType>* - BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentMeasure() const + BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::GetCurrentMeasure() const { if(IsEndOfScore()) { @@ -228,7 +228,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::HasNext( - bool ignoreEndOfMeasure) const + bool ignoreEndOfMeasure) const { if(IsEndOfScore()) { @@ -246,7 +246,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::InsertElementAfter( - const MeasureElementType &element) + const MeasureElementType &element) { MeasureType<MeasureElementType>* currMeasure; // convenience variable @@ -276,7 +276,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::InsertElementBefore( - const MeasureElementType &element) + const MeasureElementType &element) { if(IsEndOfScore()) { @@ -300,7 +300,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::InsertMeasureAfter( - const MeasureType<MeasureElementType> &measure) + const MeasureType<MeasureElementType> &measure) { if(IsEndOfScore()) { @@ -324,7 +324,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> void BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::InsertMeasureBefore( - const MeasureType<MeasureElementType> &measure) + const MeasureType<MeasureElementType> &measure) { if(IsEndOfScore()) { @@ -425,8 +425,8 @@ template<template<typename> class MeasureType, typename MeasureElementType> BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::Cursor( - BaseMusicalScore<MeasureType, MeasureElementType>* score_, - typename BaseMusicalScore<MeasureType, MeasureElementType>::ScoreMode mode) + BaseMusicalScore<MeasureType, MeasureElementType>* score_, + typename BaseMusicalScore<MeasureType, MeasureElementType>::ScoreMode mode) : context(0), score(score_) { if(mode == PLAY) @@ -445,7 +445,7 @@ template<template<typename> class MeasureType, typename MeasureElementType> bool BaseMusicalScore<MeasureType, MeasureElementType>::Cursor::HasNextWritten( - bool ignoreEndOfMeasure) const + bool ignoreEndOfMeasure) const { bool isLastMeasure = (currMeasureIdx == score->measures.GetSize() - 1); Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2014-05-22 20:59:47 UTC (rev 9509) +++ trunk/src/common/music/scoreelements.h 2014-05-22 22:02:43 UTC (rev 9510) @@ -108,6 +108,11 @@ }; /** + * Default constructor. Create a natural middle C. + */ + Note(); + + /** * Constructor. * * @param name Name of the note (C, D, E, F, G, A or B). Must be uppercase. @@ -508,9 +513,9 @@ * @return The index of the pushed element. */ size_t PushElement(const MeasureElementType &element) - { - return elements.Push(element); - } + { + return elements.Push(element); + } /** * Set the beat information. Both beats and beatType must be defined. If only one of Modified: trunk/src/common/music/scoreelements.hpp =================================================================== --- trunk/src/common/music/scoreelements.hpp 2014-05-22 20:59:47 UTC (rev 9509) +++ trunk/src/common/music/scoreelements.hpp 2014-05-22 22:02:43 UTC (rev 9510) @@ -154,7 +154,7 @@ template<typename MeasureElementType> typename Measure<MeasureElementType>::MeasureAttributes - Measure<MeasureElementType>::GetAttributes() const + Measure<MeasureElementType>::GetAttributes() const { if(attributes == 0) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2014-05-22 23:01:45
|
Revision: 9511 http://sourceforge.net/p/planeshift/code/9511 Author: whacko88 Date: 2014-05-22 23:01:43 +0000 (Thu, 22 May 2014) Log Message: ----------- implemented MusicXMLNote Modified Paths: -------------- trunk/src/common/music/musicxmlscore.h trunk/src/common/music/musicxmlscore.hpp trunk/src/common/music/scoreelements.cpp Added Paths: ----------- trunk/src/common/music/musicxmlscore.cpp Added: trunk/src/common/music/musicxmlscore.cpp =================================================================== --- trunk/src/common/music/musicxmlscore.cpp (rev 0) +++ trunk/src/common/music/musicxmlscore.cpp 2014-05-22 23:01:43 UTC (rev 9511) @@ -0,0 +1,112 @@ +/* + * musicxmlscore.cpp, Author: Andrea Rizzi <88w...@gm...> + * + * Copyright (C) 2001-2014 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * + * + * 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 (version 2 of the License) + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include "musicxmlscore.h" + +//==================================================================================== +// Crystal Space Includes +//==================================================================================== + +//==================================================================================== +// Project Includes +//==================================================================================== + +//==================================================================================== +// Local Includes +//==================================================================================== + +//------------------------------------------------------------------------------------ +// Forward Declarations +//------------------------------------------------------------------------------------ + +bool MusicXMLNote::LoadXML(csRef<iDocumentNode> &pitchNode) +{ + csRef<iDocumentNode> stepNode = pitchNode->GetNode("step"); + csRef<iDocumentNode> alterNode = pitchNode->GetNode("alter"); + csRef<iDocumentNode> octaveNode = pitchNode->GetNode("octave"); + + if(!stepNode.IsValid() || !octaveNode.IsValid()) + { + return false; + } + + char step = stepNode->GetContentsValue()[0]; + if(step < 'A' || step > 'G') + { + return false; + } + this->SetName(step); + + if(!alterNode.IsValid()) + { + this->SetWrittenAccidental(NO_ACCIDENTAL); + } + else // the alter node is optional + { + int alter = alterNode->GetContentsValueAsInt(); + switch(alter) + { + case 0: + this->SetWrittenAccidental(NO_ACCIDENTAL); + break; + case 1: + this->SetWrittenAccidental(SHARP); + break; + case -1: + this->SetWrittenAccidental(FLAT); + break; + default: + return false; + } + } + + int octave = octaveNode->GetContentsValueAsInt(); + if(octave < 0 || octave > 9) // octave limits according to xsd specification + { + return true; + } + this->SetOctave(octave); + + return true; +} + +csString MusicXMLNote::ToXML() +{ + int alterXML = 0; + Accidental alter = this->GetWrittenAccidental(); + switch(alter) + { + case SHARP: + alterXML = 1; + break; + case FLAT: + alterXML = -1; + break; + } + + csString pitch("<pitch>"); + pitch.AppendFmt("<step>%c</step>", this->GetName()); + if(alterXML != 0) + { + pitch.AppendFmt("<alter>%d</alter>", alterXML); + } + pitch.AppendFmt("<octave>%d</octave></pitch>", this->GetOctave()); + return pitch; +} + Property changes on: trunk/src/common/music/musicxmlscore.cpp ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Modified: trunk/src/common/music/musicxmlscore.h =================================================================== --- trunk/src/common/music/musicxmlscore.h 2014-05-22 22:02:43 UTC (rev 9510) +++ trunk/src/common/music/musicxmlscore.h 2014-05-22 23:01:43 UTC (rev 9511) @@ -1,7 +1,7 @@ /* * musicxmlscore.h, Author: Andrea Rizzi <88w...@gm...> * - * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * Copyright (C) 2001-2014 Atomic Blue (in...@pl..., http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or @@ -32,10 +32,36 @@ //==================================================================================== // Local Includes //==================================================================================== +#include "scoreelements.h" //------------------------------------------------------------------------------------ // Forward Declarations //------------------------------------------------------------------------------------ +/** +* \addtogroup common_music +* @{ */ +/** + * Extends the class Note by adding the ability to parse MusicXML. + */ +class MusicXMLNote: public Note +{ +public: + /** + * Load the note step, note and accidental from the given <pitch> node. + * + * @param pitchNode a valid pointer to the <pitch> node containing the definition of + * the note in MusicXML syntax. + * @return false if the given node syntax is wrong, true otherwise. + */ + bool LoadXML(csRef<iDocumentNode> &pitchNode); + + /** + * Convert the note to XML. + * @return the string containing the note definition in MusicXML syntax. + */ + csString ToXML(); +}; + #endif // MUSIC_XML_SCORE_H Modified: trunk/src/common/music/musicxmlscore.hpp =================================================================== --- trunk/src/common/music/musicxmlscore.hpp 2014-05-22 22:02:43 UTC (rev 9510) +++ trunk/src/common/music/musicxmlscore.hpp 2014-05-22 23:01:43 UTC (rev 9511) @@ -1,7 +1,7 @@ /* - * musicxmlscore.cpp, Author: Andrea Rizzi <88w...@gm...> + * musicxmlscore.hpp, Author: Andrea Rizzi <88w...@gm...> * - * Copyright (C) 2001-2013 Atomic Blue (in...@pl..., http://www.atomicblue.org) + * Copyright (C) 2001-2014 Atomic Blue (in...@pl..., http://www.atomicblue.org) * * * This program is free software; you can redistribute it and/or Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2014-05-22 22:02:43 UTC (rev 9510) +++ trunk/src/common/music/scoreelements.cpp 2014-05-22 23:01:43 UTC (rev 9511) @@ -104,6 +104,10 @@ } } +Note::Note(): name('C'), octave(4), writtenAccidental(NO_ACCIDENTAL) +{ +} + Note::Note(char name_, int octave_, Accidental writtenAccidental_) : octave(octave_), writtenAccidental(writtenAccidental_) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2014-05-26 17:37:27
|
Revision: 9512 http://sourceforge.net/p/planeshift/code/9512 Author: whacko88 Date: 2014-05-26 17:37:24 +0000 (Mon, 26 May 2014) Log Message: ----------- implemented MusicXMLElement in place of MusicXMLNote Modified Paths: -------------- trunk/src/common/music/musicutil.cpp trunk/src/common/music/musicutil.h trunk/src/common/music/musicxmlscore.cpp trunk/src/common/music/musicxmlscore.h trunk/src/common/music/scoreelements.cpp Modified: trunk/src/common/music/musicutil.cpp =================================================================== --- trunk/src/common/music/musicutil.cpp 2014-05-22 23:01:43 UTC (rev 9511) +++ trunk/src/common/music/musicutil.cpp 2014-05-26 17:37:24 UTC (rev 9512) @@ -32,6 +32,33 @@ #define SCORE_COMPRESSION_FACTOR 3 +bool psMusic::CheckDuration(int duration) +{ + switch(duration) + { + case SIXTEENTH_DURATION: + return true; + case EIGHTH_DURATION: + return true; + case DOTTED_EIGHTH_DURATION: + return true; + case QUARTER_DURATION: + return true; + case DOTTED_QUARTER_DURATION: + return true; + case HALF_DURATION: + return true; + case DOTTED_HALF_DURATION: + return true; + case WHOLE_DURATION: + return true; + case DOTTED_WHOLE_DURATION: + return true; + default: + return false; + } +} + psMusic::Duration psMusic::GetBiggestDuration(int duration) { if(DOTTED_WHOLE_DURATION <= duration) Modified: trunk/src/common/music/musicutil.h =================================================================== --- trunk/src/common/music/musicutil.h 2014-05-22 23:01:43 UTC (rev 9511) +++ trunk/src/common/music/musicutil.h 2014-05-26 17:37:24 UTC (rev 9512) @@ -225,11 +225,13 @@ DOUBLE_SHARP }; + +// Unit of measure is a sixteenth. If you change this constant you must change the values +// in the enum Duration and the method CheckDuration() too. /** - * Unit measure for duration in in terms of divisions per quarter. If you change this - * constants you must change the values in the enum Duration too. + * Unit measure for duration in in terms of divisions per quarter. */ -#define DURATION_QUARTER_DIVISIONS 16 // unit measure is a sixteenth +#define DURATION_QUARTER_DIVISIONS 16 /** * The number associated to each duration is the number of quarter divisions as specified @@ -250,6 +252,16 @@ /** + * Check wheather the given number of DURATION_QUARTER_DIVISIONS correspond to a specific + * duration taken into account by the enum Duration. + * + * @param duration the duration expressed as the number of DURATION_QUARTER_DIVISIONS. + * @return true if the given duration can be expressed by the type Duration, false + * otherwise. + */ +bool CheckDuration(int duration); + +/** * Returns the biggest duration that can be represented on the score which is less * or equal to the given one. * Modified: trunk/src/common/music/musicxmlscore.cpp =================================================================== --- trunk/src/common/music/musicxmlscore.cpp 2014-05-22 23:01:43 UTC (rev 9511) +++ trunk/src/common/music/musicxmlscore.cpp 2014-05-26 17:37:24 UTC (rev 9512) @@ -35,78 +35,161 @@ // Forward Declarations //------------------------------------------------------------------------------------ -bool MusicXMLNote::LoadXML(csRef<iDocumentNode> &pitchNode) + +bool MusicXMLElement::LoadXMLNote(const csRef<iDocumentNode> ¬eNode, int divisions) { - csRef<iDocumentNode> stepNode = pitchNode->GetNode("step"); - csRef<iDocumentNode> alterNode = pitchNode->GetNode("alter"); - csRef<iDocumentNode> octaveNode = pitchNode->GetNode("octave"); + char step; + int octave; + int duration; + Accidental accidental = NO_ACCIDENTAL; + csRef<iDocumentNode> durationNode; + csRef<iDocumentNode> pitchNode; + csRef<iDocumentNode> stepNode; + csRef<iDocumentNode> octaveNode; + csRef<iDocumentNode> accidentalNode; + if(!noteNode.IsValid()) + { + return false; + } + + // Check that this note belongs to this element + if(this->GetNNotes() > 0 && !noteNode->GetNode("chord").IsValid()) + { + return false; + } + + // Loading duration + durationNode = noteNode->GetNode("duration"); + if(!durationNode.IsValid()) + { + return false; + } + duration = durationNode->GetContentsValueAsInt(); + if(duration <= 0) + { + return false; + } + duration = duration * QUARTER_DURATION / divisions; // convert duration from quarters to sixteenths + if(!CheckDuration(duration)) + { + return false; + } + this->SetDuration(static_cast<Duration>(duration)); + + // If this is a rest, we don't need to do anything else + if(noteNode->GetNode("rest").IsValid()) + { + return true; + } + + // Loading name and octave + pitchNode = noteNode->GetNode("pitch"); + if(!pitchNode.IsValid()) + { + return false; + } + + stepNode = pitchNode->GetNode("step"); + octaveNode = pitchNode->GetNode("octave"); if(!stepNode.IsValid() || !octaveNode.IsValid()) { return false; } - char step = stepNode->GetContentsValue()[0]; + step = stepNode->GetContentsValue()[0]; if(step < 'A' || step > 'G') { return false; } - this->SetName(step); - if(!alterNode.IsValid()) + octave = octaveNode->GetContentsValueAsInt(); + if(octave < 0 || octave > 9) // octave limits according to xsd specification { - this->SetWrittenAccidental(NO_ACCIDENTAL); + return true; } - else // the alter node is optional + + // Loading accidental. Right now it considers both <alter> and <accidental> as the + // written accidental of the note (with priority to <accidental>). This is wrong but + // it's needed for backwards compatibility. In the future, only <accidental> should + // be parsed. + accidentalNode = noteNode->GetNode("accidental"); + if(accidentalNode.IsValid()) { - int alter = alterNode->GetContentsValueAsInt(); - switch(alter) + csString accidentalStr = accidentalNode->GetContentsValue(); + if(accidentalStr == "flat") { - case 0: - this->SetWrittenAccidental(NO_ACCIDENTAL); - break; - case 1: - this->SetWrittenAccidental(SHARP); - break; - case -1: - this->SetWrittenAccidental(FLAT); - break; - default: + accidental = FLAT; + } + else if(accidentalStr == "sharp") + { + accidental = SHARP; + } + else + { return false; } } - - int octave = octaveNode->GetContentsValueAsInt(); - if(octave < 0 || octave > 9) // octave limits according to xsd specification + else { - return true; + csRef<iDocumentNode> alterNode = pitchNode->GetNode("alter"); + if(alterNode.IsValid()) + { + int alter = alterNode->GetContentsValueAsInt(); + switch(alter) + { + case 0: + accidental = NO_ACCIDENTAL; + break; + case 1: + accidental = SHARP; + break; + case -1: + accidental = FLAT; + break; + default: + return false; + } + } } - this->SetOctave(octave); + this->AddNote(step, octave, accidental); + return true; } -csString MusicXMLNote::ToXML() +csString MusicXMLElement::ToXML() { - int alterXML = 0; - Accidental alter = this->GetWrittenAccidental(); - switch(alter) + csString element("<note>"); + + if(this->IsRest()) { - case SHARP: - alterXML = 1; - break; - case FLAT: - alterXML = -1; - break; + element.AppendFmt("<rest/><duration>%d</duration></note>", this->GetDuration()); + return element; } - csString pitch("<pitch>"); - pitch.AppendFmt("<step>%c</step>", this->GetName()); - if(alterXML != 0) + for(size_t i = 0; i < this->GetNNotes(); i++) { - pitch.AppendFmt("<alter>%d</alter>", alterXML); + Note note = this->GetNote(i); + + if(i > 0) + { + element += "<note><chord/>"; + } + + element.AppendFmt("<pitch><step>%c</step><octave>%d</octave></pitch><duration>%d</duration>", + note.GetName(), note.GetOctave(), this->GetDuration()); + switch(note.GetWrittenAccidental()) + { + case SHARP: + element += "<accidental>sharp</accidental>"; + break; + case FLAT: + element += "<accidental>flat</accidental>"; + break; + } + element += "</note>"; } - pitch.AppendFmt("<octave>%d</octave></pitch>", this->GetOctave()); - return pitch; + return element; } Modified: trunk/src/common/music/musicxmlscore.h =================================================================== --- trunk/src/common/music/musicxmlscore.h 2014-05-22 23:01:43 UTC (rev 9511) +++ trunk/src/common/music/musicxmlscore.h 2014-05-26 17:37:24 UTC (rev 9512) @@ -43,19 +43,22 @@ * @{ */ /** - * Extends the class Note by adding the ability to parse MusicXML. + * Extends the class MeasureElement by adding the ability to parse MusicXML. */ -class MusicXMLNote: public Note +class MusicXMLElement: public MeasureElement { public: /** - * Load the note step, note and accidental from the given <pitch> node. + * Load a note from the given <note> node. If this element contain already one or + * more note, only notes that present the <chord/> tag are accepted. * - * @param pitchNode a valid pointer to the <pitch> node containing the definition of - * the note in MusicXML syntax. - * @return false if the given node syntax is wrong, true otherwise. + * @param noteNode a reference to the <note> node containing the definition of the + * note in MusicXML syntax. + * @param divisions divisions per quarter used by the score to indicate duration. + * @return false if the given node syntax is wrong or if this note is not part of + * this chord, true otherwise. */ - bool LoadXML(csRef<iDocumentNode> &pitchNode); + bool LoadXMLNote(const csRef<iDocumentNode> ¬eNode, int divisions); /** * Convert the note to XML. @@ -64,4 +67,6 @@ csString ToXML(); }; +#include "musicxmlscore.hpp" + #endif // MUSIC_XML_SCORE_H Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2014-05-22 23:01:43 UTC (rev 9511) +++ trunk/src/common/music/scoreelements.cpp 2014-05-26 17:37:24 UTC (rev 9512) @@ -109,9 +109,10 @@ } Note::Note(char name_, int octave_, Accidental writtenAccidental_) -: octave(octave_), writtenAccidental(writtenAccidental_) +: writtenAccidental(writtenAccidental_) { SetName(name_); + SetOctave(octave_); } Accidental Note::GetPlayedAccidental(const ScoreContext &context) const @@ -225,6 +226,7 @@ void Note::SetOctave(int newOctave) { + CS_ASSERT(newOctave >= 0 && newOctave <= 9); octave = newOctave; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2015-07-29 03:26:55
|
Revision: 9752 http://sourceforge.net/p/planeshift/code/9752 Author: whacko88 Date: 2015-07-29 03:26:52 +0000 (Wed, 29 Jul 2015) Log Message: ----------- Fix bug to correctly handle repeats in play mode Modified Paths: -------------- trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2015-07-29 01:14:27 UTC (rev 9751) +++ trunk/src/common/music/scoreelements.cpp 2015-07-29 03:26:52 UTC (rev 9752) @@ -289,10 +289,12 @@ void ScoreContext::Update(int measureID, const Measure<MeasureElement> &measure) { + // Update context noteContext.ResetContext(); measureAttributes.UpdateAttributes(measure.GetAttributes()); - if(measure.IsStartRepeat()) + // If this is the new start of a repeat, save the context + if(measure.IsStartRepeat() && measureID != lastStartRepeatID) { lastStartRepeatID = measureID; lastStartRepeatAttributes = measureAttributes; @@ -301,8 +303,9 @@ repeatsDone.DeleteAll(); } - if(measure.GetNEndRepeat() > 0 && !repeatsDone.Contains(measureID)) + // If this is the end of a repeat, update the number of repeats done + if(measure.GetNEndRepeat() > 0) { - repeatsDone.Put(measureID, 0); + repeatsDone.GetOrCreate(measureID, 0)++; } } Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2015-07-29 01:14:27 UTC (rev 9751) +++ trunk/src/common/music/scoreelements.h 2015-07-29 03:26:52 UTC (rev 9752) @@ -623,6 +623,7 @@ /** * Restore the context of the last measure containing a start repeat. If no explicit * start repeats have been found, the function assumes it to be the first measure. + * Note that you still have to call Update(int, Measure<MeasureElement> &) afterwards. * * @return The ID of the last measure encountered that starts a repeat. */ @@ -631,7 +632,7 @@ /** * Update the context with the new measure. This resets also previous accidentals. * Note that you still have to call Update(MeasureElement &) to update the list of - * previous accidentals with the first note of this measure. + * previous accidentals with the first element of this measure. * * @param measureID The ID of the updated measure. * @param measure The new measure reached by the cursor. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <wha...@us...> - 2015-07-30 03:56:12
|
Revision: 9753 http://sourceforge.net/p/planeshift/code/9753 Author: whacko88 Date: 2015-07-30 03:56:09 +0000 (Thu, 30 Jul 2015) Log Message: ----------- Keep track of previous measure attributes rather than current ones, consistently with previous accidentals Modified Paths: -------------- trunk/src/common/music/basemusicscore.h trunk/src/common/music/basemusicscore.hpp trunk/src/common/music/scoreelements.cpp trunk/src/common/music/scoreelements.h Modified: trunk/src/common/music/basemusicscore.h =================================================================== --- trunk/src/common/music/basemusicscore.h 2015-07-29 03:26:52 UTC (rev 9752) +++ trunk/src/common/music/basemusicscore.h 2015-07-30 03:56:09 UTC (rev 9753) @@ -322,7 +322,12 @@ size_t currElementIdx; ///< Index of the current element. size_t currMeasureIdx; ///< Index of the current measure. - ScoreContext* context; ///< The context is valid only if it is in play mode. + + /** + * The context is valid only if the score is in play mode. This keeps track of attributes + * and accidentals of previous measures and notes, not the current ones. + */ + ScoreContext* context; BaseMusicalScore<MeasureType, MeasureElementType>* score; ///< The musical score this cursor refers to. /** Modified: trunk/src/common/music/basemusicscore.hpp =================================================================== --- trunk/src/common/music/basemusicscore.hpp 2015-07-29 03:26:52 UTC (rev 9752) +++ trunk/src/common/music/basemusicscore.hpp 2015-07-30 03:56:09 UTC (rev 9753) @@ -136,6 +136,7 @@ isLastElem = (currElementIdx == score->measures[currMeasureIdx].GetNElements() - 1); if(IsEndOfMeasure() || (isLastElem && ignoreEndOfMeasure)) { + // we need to move to the first element of the next measure currElementIdx = 0; // in play mode we must take repeats into account and update the context @@ -147,17 +148,18 @@ } else { - // we must skip all the endings that end a repeat - // section that has been already performed + // update the context with info on previous measures attributes + context->Update(currMeasureIdx, score->measures[currMeasureIdx]); + + // we must skip all the endings that end a repeat section that has been + // already performed, the context doesn't have to be updated with skipped + // endings do { currMeasureIdx++; }while(score->measures[currMeasureIdx].IsEnding() && score->measures[currMeasureIdx].IsEndRepeat() && !CheckRepeat()); - - // the context doesn't have to be updated with skipped endings - context->Update(currMeasureIdx, score->measures[currMeasureIdx]); } } else // mode == EDIT @@ -167,18 +169,17 @@ } else { - // we update the context with the accidentals of the previous element - if(score->mode == PLAY) - { - context->Update(score->measures[currMeasureIdx].GetElement(currElementIdx)); - } - if(isLastElem) // ignoreEndOfMeasure has been checked before { currElementIdx = INVALID_CURSOR; } else { + // we update the context with the accidentals of the previous element + if(score->mode == PLAY) + { + context->Update(score->measures[currMeasureIdx].GetElement(currElementIdx)); + } currElementIdx++; } } Modified: trunk/src/common/music/scoreelements.cpp =================================================================== --- trunk/src/common/music/scoreelements.cpp 2015-07-29 03:26:52 UTC (rev 9752) +++ trunk/src/common/music/scoreelements.cpp 2015-07-30 03:56:09 UTC (rev 9753) @@ -278,6 +278,7 @@ int ScoreContext::RestoreLastStartRepeat() { + noteContext.ResetContext(); measureAttributes = lastStartRepeatAttributes; return lastStartRepeatID; } Modified: trunk/src/common/music/scoreelements.h =================================================================== --- trunk/src/common/music/scoreelements.h 2015-07-29 03:26:52 UTC (rev 9752) +++ trunk/src/common/music/scoreelements.h 2015-07-30 03:56:09 UTC (rev 9753) @@ -623,7 +623,7 @@ /** * Restore the context of the last measure containing a start repeat. If no explicit * start repeats have been found, the function assumes it to be the first measure. - * Note that you still have to call Update(int, Measure<MeasureElement> &) afterwards. + * It is not necessary to call Update(int, Measure<MeasureElement> &) afterwards. * * @return The ID of the last measure encountered that starts a repeat. */ @@ -631,8 +631,8 @@ /** * Update the context with the new measure. This resets also previous accidentals. - * Note that you still have to call Update(MeasureElement &) to update the list of - * previous accidentals with the first element of this measure. + * Note that you still have to call Update(MeasureElement &) if you want to update + * the list of previous accidentals with the first element of this measure. * * @param measureID The ID of the updated measure. * @param measure The new measure reached by the cursor. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |