From: <wha...@us...> - 2012-07-31 21:59:48
|
Revision: 8410 http://planeshift.svn.sourceforge.net/planeshift/?rev=8410&view=rev Author: whacko88 Date: 2012-07-31 21:59:42 +0000 (Tue, 31 Jul 2012) Log Message: ----------- retrieved more statistics of musical scores Modified Paths: -------------- trunk/src/common/util/music.cpp trunk/src/common/util/music.h Modified: trunk/src/common/util/music.cpp =================================================================== --- trunk/src/common/util/music.cpp 2012-07-31 18:01:07 UTC (rev 8409) +++ trunk/src/common/util/music.cpp 2012-07-31 21:59:42 UTC (rev 8410) @@ -90,6 +90,77 @@ } } +void psMusic::EnharmonicPitch(char &pitch, int &accidental) +{ + switch(accidental) + { + case 1: + switch(pitch) + { + case 'A': + pitch = 'B'; + accidental = -1; + break; + case 'B': + pitch = 'C'; + accidental = 0; + break; + case 'C': + pitch = 'D'; + accidental = -1; + break; + case 'D': + pitch = 'E'; + accidental = -1; + break; + case 'E': + pitch = 'F'; + accidental = 0; + break; + case 'F': + pitch = 'G'; + accidental = -1; + break; + case 'G': + pitch = 'A'; + accidental = -1; + break; + } + case -1: + switch(pitch) + { + case 'A': + pitch = 'G'; + accidental = 1; + break; + case 'B': + pitch = 'A'; + accidental = 1; + break; + case 'C': + pitch = 'B'; + accidental = 0; + break; + case 'D': + pitch = 'C'; + accidental = 1; + break; + case 'E': + pitch = 'D'; + accidental = 1; + break; + case 'F': + pitch = 'E'; + accidental = 0; + break; + case 'G': + pitch = 'F'; + accidental = 1; + break; + } + } +} + bool psMusic::GetMeasures(csRef<iDocument> musicalScore, csRefArray<iDocumentNode> &measures) { csRef<iDocumentNode> partNode; @@ -122,19 +193,9 @@ float timePerMeasure; float timePerDivision; - uint nNotes; // number of notes in the score - uint nChords; // number of chords in the score - float totalLengthNoRests; // total duration of the score in ms excluding rests. + ScoreStatistics endingStats; // keeps statistics of the previous ending + ScoreStatistics lastRepeatStats; // keeps statistics from the last start repeat - uint endingNNotes; // number of notes in the previous endings - uint endingNChords; // number of chords in the previous endings - float endingTotalLengthNoRests; // total length of the previous endings excluding rests - - uint lastRepeatNNotes; // number of notes from the last start repeat - uint lastRepeatNChords; // number of chords from the last start repeat - float lastRepeatTotalLength; // total length from the last start repeat - float lastRepeatTotalLengthNoRests; // total length from the last start repeat excluding rests - csRefArray<iDocumentNode> measures; // this retrieves the general attributes of the score and checks some syntax @@ -147,37 +208,27 @@ timePerDivision = 60.0f / tempo / quarterDivisions * 1000; // (ms) timePerMeasure = 60.0f / tempo * beats / stats.beatType * 4 * 1000; // (ms) - // initializing variables - nNotes = 0; - nChords = 0; - totalLengthNoRests = 0.0; - - endingNNotes = 0; - endingNChords = 0; - endingTotalLengthNoRests = 0.0; - - lastRepeatNNotes = 0; - lastRepeatNChords = 0; - lastRepeatTotalLength = 0.0; - lastRepeatTotalLengthNoRests = 0.0; - - stats.totalLength = 0.0; - stats.maximumPolyphony = 0; + // we make sure than stats are just initialized + stats.Reset(); stats.minimumDuration = 500000; // (ms) this is more than a 8/4 note at bpm = 1 // parsing the score for(size_t i = 0; i < measures.GetSize(); i++) { int noteDuration; - uint measureNNotes = 0; - uint measureNChords = 0; - float measureDurationNoRests = 0.0; + ScoreStatistics measureStats; int currentChordPolyphony = 0; + csRef<iDocumentNode> noteNode; + csRef<iDocumentNode> pitchNode; + csRef<iDocumentNode> alterNode; csRef<iDocumentNode> barlineNode; csRef<iDocumentNode> durationNode; csRef<iDocumentNodeIterator> notesIter; + // we already know the measure length + measureStats.totalLength = timePerMeasure; + // handling repeats barlineNode = measures.Get(i)->GetNode("barline"); if(barlineNode.IsValid()) @@ -195,10 +246,7 @@ // resetting lastRepeat variables data if(csStrCaseCmp(direction, "forward") == 0) { - lastRepeatNNotes = 0; - lastRepeatNChords = 0; - lastRepeatTotalLength = 0.0; - lastRepeatTotalLengthNoRests = 0.0; + lastRepeatStats.Reset(); } } } @@ -208,6 +256,7 @@ while(notesIter->HasNext()) { noteNode = notesIter->Next(); + pitchNode = noteNode->GetNode("pitch"); durationNode = noteNode->GetNode("duration"); // if there's no durationNode syntax is incorrect @@ -225,10 +274,53 @@ // when the last notes of the score is a rests currentChordPolyphony = 0; } - else // this is a note + else if(pitchNode.IsValid()) // this is a note { - measureNNotes++; + int alter; + char step; + measureStats.nNotes++; + + // handling alteration counting + step = *(pitchNode->GetNode("step")->GetContentsValue()); + alterNode = pitchNode->GetNode("alter"); + if(alterNode == 0) + { + alter = 0; + } + else + { + alter = alterNode->GetContentsValueAsInt(); + } + + if(alter > 0) + { + // we need to convert it into the flat enharmonic equivalent + psMusic::EnharmonicPitch(step, alter); + } + + if(alter < 0) + { + switch(step) + { + case 'A': + measureStats.nAb++; + break; + case 'B': + measureStats.nBb++; + break; + case 'D': + measureStats.nDb++; + break; + case 'E': + measureStats.nEb++; + break; + case 'G': + measureStats.nGb++; + break; + } + } + // if the note is part of a chord neither the chords count nor the total // length have to be updated. The first notes in a chord doesn't have the // tag <chord> so the condition is true for them. @@ -250,10 +342,14 @@ } currentChordPolyphony = 1; - measureNChords++; - measureDurationNoRests += noteDuration; + measureStats.nChords++; + measureStats.totalLengthNoRests += noteDuration; } } + else // a note without pitch not is not valid + { + return false; + } } // updating maximum polyphony and minimum duration if the last note was part of a chord @@ -269,16 +365,10 @@ } } - nNotes += measureNNotes; - nChords += measureNChords; - stats.totalLength += timePerMeasure; - totalLengthNoRests += measureDurationNoRests; + // adding measure's statistics + stats += measureStats; + lastRepeatStats += measureStats; - lastRepeatNNotes += measureNNotes; - lastRepeatNChords += measureNChords; - lastRepeatTotalLength += timePerMeasure; - lastRepeatTotalLengthNoRests += measureDurationNoRests; - // checking if this measure is the end of a repeat if(barlineNode.IsValid()) { @@ -295,12 +385,9 @@ if(csStrCaseCmp(direction, "backward") == 0) { - int repeatCounter = repeatNode->GetAttributeValueAsInt("times"); + int nRepeat = repeatNode->GetAttributeValueAsInt("times"); - nNotes += lastRepeatNNotes * repeatCounter; - nChords += lastRepeatNChords * repeatCounter; - stats.totalLength += lastRepeatTotalLength * repeatCounter; - totalLengthNoRests += lastRepeatTotalLengthNoRests * repeatCounter; + stats += lastRepeatStats * nRepeat; } } @@ -311,38 +398,28 @@ if(endingNumber == 1) // reset ending data { - endingNNotes = 0; - endingNChords = 0; - endingTotalLengthNoRests = 0.0; + endingStats.Reset(); } else { // previous endings are not played so they aren't taken into account - nNotes -= endingNNotes; - nChords -= endingNChords; - stats.totalLength -= (endingNumber - 1) * timePerMeasure; - totalLengthNoRests -= endingTotalLengthNoRests; - - endingNNotes += measureNNotes; - endingNChords += measureNChords; - endingTotalLengthNoRests += measureDurationNoRests; + stats -= endingStats; + endingStats += measureStats; } } } } // setting average values - if(nChords == 0) + if(stats.nChords == 0) { // here stats.minimumDuration is still 500000 stats.minimumDuration = 0; - stats.averageDuration = 0.0; - stats.averagePolyphony = 0.0; } else { - stats.averageDuration = totalLengthNoRests / nChords; - stats.averagePolyphony = (float)nNotes / nChords; + stats.averageDuration = stats.totalLengthNoRests / stats.nChords; + stats.averagePolyphony = (float)stats.nNotes / stats.nChords; } return true; Modified: trunk/src/common/util/music.h =================================================================== --- trunk/src/common/util/music.h 2012-07-31 18:01:07 UTC (rev 8409) +++ trunk/src/common/util/music.h 2012-07-31 21:59:42 UTC (rev 8410) @@ -30,13 +30,142 @@ /** - * This struct keeps general information about a score. + * This struct keeps general information about a score. All average statistics are + * computed by using the total length without rests. If the score is empty (or has + * only rests), the average polyphony and duration are set to 0. */ struct ScoreStatistics { + /** + * Constructor. Initialize all statistics to 0. + */ ScoreStatistics() { + Reset(); + } + + /** + * Copy constructor. + */ + ScoreStatistics(const ScoreStatistics ©) + { + nNotes = copy.nNotes; + nChords = copy.nChords; + nBb = copy.nBb; + nGb = copy.nGb; + nEb = copy.nEb; + nDb = copy.nDb; + nAb = copy.nAb; + + totalLength = copy.totalLength; + totalLengthNoRests = copy.totalLengthNoRests; + + minimumDuration = copy.minimumDuration; + maximumPolyphony = copy.maximumPolyphony; + averageDuration =copy.averageDuration; + averagePolyphony = copy.averagePolyphony; + + beatType = copy.beatType; + fifths = copy.fifths; + } + + /** + * Adds all statistics concerning number of notes/chords and length. It does not + * change minimum,maximum and average statistics nor beat type and fifths. + * @param addend the statistics that must be added to these. + * @return these updated statistics. + */ + ScoreStatistics &operator+=(const ScoreStatistics &addend) + { + nNotes += addend.nNotes; + nChords += addend.nChords; + nBb += addend.nBb; + nGb += addend.nGb; + nEb += addend.nEb; + nDb += addend.nDb; + nAb += addend.nAb; + + totalLength += addend.totalLength; + totalLengthNoRests += addend.totalLengthNoRests; + + return *this; + } + + /** + * Subtracts all statistics concerning number of notes/chords and length. It does + * not change minimum,maximum and average statistics nor beat type and fifths. + * @param subtrahend the statistics that must be subtracted to these. + * @return these updated statistics. + */ + ScoreStatistics &operator-=(const ScoreStatistics &subtrahend) + { + nNotes -= subtrahend.nNotes; + nChords -= subtrahend.nChords; + nBb -= subtrahend.nBb; + nGb -= subtrahend.nGb; + nEb -= subtrahend.nEb; + nDb -= subtrahend.nDb; + nAb -= subtrahend.nAb; + + totalLength -= subtrahend.totalLength; + totalLengthNoRests -= subtrahend.totalLengthNoRests; + + return *this; + } + + /** + * Multiplies all statistics concerning number of notes/chords and length for + * a integer scalar. It does not change minimum,maximum and average statistics + * nor beat type and fifths. + * @param scalar the integer that multiplies these statistics. + * @return these updated statistics. + */ + ScoreStatistics &operator*=(const int scalar) + { + nNotes *= scalar; + nChords *= scalar; + nBb *= scalar; + nGb *= scalar; + nEb *= scalar; + nDb *= scalar; + nAb *= scalar; + + totalLength *= scalar; + totalLengthNoRests *= scalar; + + return *this; + } + + /** + * Multiplies all statistics concerning number of notes/chords and length for + * a integer scalar. It does not change minimum,maximum and average statistics + * nor beat type and fifths. + * @param scalar the integer that multiplies these statistics. + * @return the updated statistics. + */ + const ScoreStatistics operator*(const int scalar) + { + ScoreStatistics result(*this); + result *= scalar; + return result; + } + + /** + * Set all statistics to 0. + */ + void Reset() + { + nNotes = 0; + nChords = 0; + nBb = 0; + nGb = 0; + nEb = 0; + nDb = 0; + nAb = 0; + totalLength = 0.0; + totalLengthNoRests = 0.0; + minimumDuration = 0; maximumPolyphony = 0; averageDuration = 0.0; @@ -46,7 +175,17 @@ fifths = 0; } + int nNotes; ///< total number of played notes in the score. + int nChords; ///< total number of played chords in the score. + int nBb; ///< total number of played Bb. + int nGb; ///< total number of played Gb. + int nEb; ///< total number of played Eb. + int nDb; ///< total number of played Db. + int nAb; ///< total number of played Ab. + float totalLength; ///< total duration of the score in milliseconds. + float totalLengthNoRests; ///< total duration of the score in ms excluding rests. + int minimumDuration; ///< duration of the shortest note in the score in ms. int maximumPolyphony; ///< maximum number of notes played at the same time. float averageDuration; ///< average duration of the score's notes in ms. @@ -79,6 +218,13 @@ static void PreviousPitch(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); + + /** * Gets the XML nodes representing the measures contained in the musical score. * * @param musicalScore the musical score. @@ -88,10 +234,7 @@ static bool GetMeasures(csRef<iDocument> score, csRefArray<iDocumentNode> &measures); /** - * Returns the length of the score in milliseconds in the parameter length. Rests - * are counted only for determining the song's total length but they are excluded - * from other statistics. If the score is empty (or has only rests), the average - * polyphony and duration are set to 0. + * Returns the statistics of the score. * * @param musicalScore the musical score. * @param stats the retrieved statistics of the given score. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |