// // Copyright 2002 by Craig Stuart Sapp, All Rights Reserved. // Programmer: Craig Stuart Sapp // Creation Date: Thu Feb 14 23:40:51 PST 2002 // Last Modified: Sun Mar 24 12:10:00 PST 2002 Small changes for visual C++ // Last Modified: Tue Mar 26 00:38:28 PST 2002 Added staff access functions // Last Modified: Fri Apr 12 18:09:11 PDT 2002 Added sorting by system // Last Modified: Sun Sep 2 05:07:31 PDT 2012 Renovated // Filename: ...sig/src/sigInfo/ScorePage.h // Web Address: http://sig.sapp.org/include/sigInfo/ScorePage.h // Syntax: C++ // // Description: Data structure for a page of SCORE data, with added // analysis functions. // #include "PerlRegularExpression.h" #include "ScorePage.h" #include "Convert.h" #include #ifndef OLDCPP using namespace std; #endif /* ////////////////////////////// // // ScorePage::ScorePage -- Constructor. Just initialize at the // ScorePageBase level since this class level does not contain // any data. // ScorePage::ScorePage(void) : ScorePageBase() { // do nothing } ////////////////////////////// // // ScorePage::~ScorePage -- Destructor. Destroying is handled by // ScorePageBase. // ScorePage::~ScorePage() { // do nothing } ////////////////////////////////////////////////////////////////////////// // // staff analysis functions // ////////////////////////////// // // ScorePage::getMaxStaff -- Return the number of the highest staff on // the page. // int ScorePage::getMaxStaff(void) { if (sortAnalysisQ == 0) { // maxStaffNumber is only valid if ScorePageBase::analyzeSort() // has been run. analyzeSort(); } return maxStaffNumber; } ////////////////////////////// // // ScorePage::getStaffCount -- return the number of staves on the page. // This may be smaller than the maxStaffNumber if there are missing // staves. // int ScorePage::getStaffCount(void) { if (sortAnalysisQ == 0) { // maxStaffNumber is only valid if ScorePageBase::analyzeSort() // has been run. analyzeSort(); } return pageStaffList.getSize(); } ////////////////////////////// // // ScorePage::getP2ToConsecutiveStaff -- Return the enumeration of the staff // on the page. Indexed from 0 and starting at the bottom of the page // working upwards. getStaffCount() returns the count "consecutive staves". // Returns -1 if the P2 value is invalid or has no data attached to it. // int ScorePage::getP2ToConsecutiveStaff(int p2number) { if (sortAnalysisQ == 0) { // maxStaffNumber is only valid if ScorePageBase::analyzeSort() // has been run. analyzeSort(); } if (p2number < 0) { return -1; } if (p2number >= lineStaffSequence.getSize()) { return -1; } if (lineStaffSequence[p2number].getSize() == 0) { return -1; } return pageStaffListReverse[p2number]; } ////////////////////////////// // // ScorePage::getConsecutiveStaffToP2 -- Given a consecutive enumeration // number for a staff on the page (indexed from 0 upwards on the page), // return the P2 value of that staff (indexed from 1 upwards on the page, // with possible gaps in staff number). If input is invalid, then // fuction will return -1; // int ScorePage::getConsecutiveStaffToP2(int consecutiveIndex) { if (sortAnalysisQ == 0) { // maxStaffNumber is only valid if ScorePageBase::analyzeSort() // has been run. analyzeSort(); } if ((consecutiveIndex < 0)|| (consecutiveIndex >= pageStaffList.getSize())) { return -1; } return pageStaffList[consecutiveIndex]; } // // staff analysis functions // ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // system analysis functions // ////////////////////////////// // // Defined above // // ScorePage::getSystemStaffCount -- This version of getStaffCount returns // the number of staves on a particular system on the page. When // calling getStaffCount(void), the function returns the number // of non-empty staves on the page rather than the count of staves // on the given system. The analyzeSystems() function must be called // first (enforced by the function). The first system (index 0) is // the top one on the page. // //int ScorePage::getSystemStaffCount(int sysidx) { // if (systemAnalysisQ == 0) { // analyzeSystems(); // } // return lineStaffSequence[sysidx].getSize(); //} ////////////////////////////// // // ScorePage::getSystemCount -- Return the number of system lines on the page. // The first system (0 indexed) is at the top of the page, unlike page // staffs (1 indexed) which start at the bottom of the page and go upward. // int ScorePage::getSystemCount(void) { if (systemAnalysisQ == 0) { analyzeSystems(); } return lineSystemSequence.getSize(); } ////////////////////////////// // // ScorePage::getSystemStaffIndex -- Given a ScoreRecord via an object // index in the primary data array, this function returns the index // of the staff's position on a page system. // int ScorePage::getSystemStaffIndex(int itemidx) { int staffnum = (int)(data[itemidx]->getValue(P2)); return P2ToSystemStaffIdx[staffnum]; } // // system analysis functions // ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// // // Analysis functions // ////////////////////////////// // // ScorePage::analyzeContent -- // void ScorePage::analyzeContent(void) { analyzeSystems(); //analyzePitch(); //analyzeRhythm(); //analyzeBarlines(); //analyzeTies(); //analyzeDirections(); //analyzeLyrics(); } ////////////////////////////// // // ScorePage::analyzeSystems -- identify the systems on the page // by grouping staves based on how they are connected to each // other with barlines. // void ScorePage::analyzeSystems(void) { if (sortAnalysisQ == 0) { analyzeSort(); } int i, j; // First identify the way in which staves are connected together // by barlines. Barlines in SCORE are typically attached to the // bottom staff (and that is the assumption here), and then extend // upwards according to the P4 value of the barline. The barlines // will be analyzed by "consecutive staves": in other words, if there // is a barline on staff 1 which goes up 3 staves, and there is no // staff 2 (or it is invisible), then it will appear that there are // two staves connected together rather than three. Array barheight; barheight.setSize(getStaffCount()); barheight.zero(); int consecutiveStaff = -1; for (i=0; iisBarlineItem()) { consecutiveStaff = getP2ToConsecutiveStaff(data[i]->getStaff()); if (barheight[consecutiveStaff] < data[i]->getBarHeight()) { barheight[consecutiveStaff] = data[i]->getBarHeight(); } } } int p2value; // Adjust barlines from P2 staff number to consecutive staff number. for (i=0; i grouping(getStaffCount()); grouping.setAll(-1); int system = -1; for (i=0; i= getStaffCount()) { // don't count barlines which hang off into nowhere above // the last staff (could be the bottom part of a compound // page which will not be handled by this functions). break; } grouping[i+j] = system; } } // The line systems are indexed from the highest position // on the page to the lowest position on the page (the opposite // direction of P2 staff numbers). Reverse the groupings here: for (i=0; i systemobjectcount(system+1); systemobjectcount.zero(); // Count the number of objects on each system so that space // can be preallocated for storage. for (i=0; igetStaff(); staffidx = P2ToSystemStaffIdx[staffnum]; sysidx = i; lineSystemStaffSequence[sysidx][staffidx].append( lineSystemSequence[i][j]); data[lineSystemSequence[i][j]]->setSystemStaffIndex(staffidx); rindex = lineSystemSequence[i][j]; itemSystemStaffIndex[rindex] = lineSystemStaffSequence[sysidx][staffidx].getSize()-1; } } // store pageSystemSequence. This is a concatenation of all // lineSystemSequence. This data storage is in a causal format // which sorts all notes/objects in temporal sequence on the page. pageSystemSequence.setSize(int(sysobjcount * 1.1)); pageSystemSequence.setSize(sysobjcount); int sum = 0; for (i=0; igetValue(P2)); return P2ToSystemIdx[staffnum]; } ////////////////////////////// // // ScorePage::analyzeDirections -- // -- tempo: // | = whole note // ] = half note // [ = quarter note // { = eighth note // } = sixteenth note // ?d = augmentation dot // void ScorePage::analyzeDirections(void) { int i; Array text; PerlRegularExpression pre; double tempo; double beat; // identify tempo markings in the score: for (i=0; i delta) { break; } if (!getStreamItem(i).isBarlineItem()) { continue; } getStreamItem(i).setValue("barnum", barnum); } for (i=itemidx; i>=0; i--) { tsysidx = getSystemIndex(i); if (sysidx != tsysidx) { break; } thpos = getStreamItem(i).getHpos(); if (fabs(thpos - hpos) > delta) { break; } if (!getStreamItem(i).isBarlineItem()) { continue; } getStreamItem(i).setValue("barnum", barnum); } } ////////////////////////////// // // ScorePage::analyzeLyrics -- Identify any lyrics above/below a // particular staff line in each system. // void ScorePage::analyzeLyrics(void) { int i, j; // rest the count of lyric verses on each SCORE staff and system // staff group (part) pageStaffLyrics.setAll(0); sysStaffLyrics.setAll(0); for (i=0; i leftside(32); Array rightside(32); for (i=0; i= 1.0) { // not a tie or a slur: it is a bracket or repeat ending. continue; } // staffidx = getSystemStaffIndex(i); // p3 = getItem(i).getValue(P3); // p6 = getItem(i).getValue(P3); getSlurEndNotes(i, leftside, rightside); // cout << "X " << i << "\tleft=" << leftside << "\trightside" << rightside << endl; // cout << "\tsystem = " << getSystemIndex(i) << endl; // cout << "\tstaff = " << getSystemStaffIndex(i) << endl; // if a simple tie, then marked the tied notes and the slur: if ((leftside.getSize() == 1) && (rightside.getSize() == 1)) { if (getItem(leftside[0]).getPitch() == getItem(rightside[0]).getPitch()) { getItem(leftside[0]).setTieStart(i); getItem(rightside[0]).setTieEnd(i); getItem(i).setTie(leftside[0], rightside[0]); } continue; } // check the case where the tied notes do not have the same accidentals. } } ////////////////////////////// // // ScorePage::getSystemDuration -- // double ScorePage::getSystemDuration(int sysIdx) { return systemDuration[sysIdx]; } ////////////////////////////// // // ScorePage::getSystemStaffCount -- returns the number of staves attached // to the specified system. System index is 0-offset. // int ScorePage::getSystemStaffCount(int sysidx) { if (systemAnalysisQ == 0) { analyzeSystems(); } return lineSystemStaffSequence[sysidx].getSize(); } ////////////////////////////// // // ScorePage::getSystemStaffP2 -- returns the line staff number (P2) for // the particular staff on the partcular system. System and staff indices // are both 0-offset. System is indexed with 0 at top of page. // Staff is indexed with 0 at bottom of system. Returned value is the // P2 value of the staff (1-indexed from the bottom of the page). // int ScorePage::getSystemStaffP2(int sysidx, int staffidx) { if (systemAnalysisQ == 0) { analyzeSystems(); } return systemStaffIdxToP2[sysidx][staffidx]; } ////////////////////////////// // // ScorePage::buildSystemIndexDatabase -- // void ScorePage::buildSystemIndexDatabase(void) { Array temprecords; temprecords.setSize(data.getSize()); int i; for (i=0; igetPValue(2)); temprecords[i].ptr = data[i]; } qsort(temprecords.getBase(), temprecords.getSize(), sizeof(SystemRecord), ScorePageBase::compareSystem); //systemind.setSize(data.getSize()); //for (i=0; iisBarlineItem() && (int)data[i]->getPValue(2) == staffno && (int)data[i]->getPValue(4) > output) { output = (int)data[i]->getPValue(4); } } return output; } int ScorePage::getMaxBarlineLength(int sysidx, int staffidx) { if (systemAnalysisQ == 0) { analyzeSystems(); } int staffno = ScorePage::getSystemStaffP2(sysidx, staffidx); return ScorePage::getMaxBarlineLength(staffno); } ////////////////////////////// // // ScorePage::getSystem -- return the system on the page which // contains the specified staff. The bottom system is label 1. // unknown system ownership is labeled 0. int ScorePage::getSystem(int staffno) { if (systemAnalysisQ == 0) { analyzeSystems(); } return P2ToSystemIdx[staffno]; } ////////////////////////////// // // ScorePage::getStaff -- returns the ScoreRecord for the given // staff number and // // ScoreRecord& ScorePage::getStaff(int staffno, int staffItem) { if (systemAnalysisQ == 0) { analyzeSystems(); } return *(data[staffStart[staffno]+staffItem]); } ////////////////////////////// // // ScorePage::getStaffSize -- returns the number of items on the // specified staff. // // int ScorePage::getStaffSize(int staffno) { if (systemAnalysisQ == 0) { analyzeSystems(); } return staffSize[staffno]; } ////////////////////////////// // // ScorePage::getTrack -- return the system track on the page which // contains the specified staff. The bottom staff on the system is label 1. // unknown track assignment is labeled 0. int ScorePage::getTrack(int staffno) { if (systemAnalysisQ == 0) { analyzeSystems(); } return track[staffno]; } ////////////////////////////////////////////////////////////////////////// // // pitch analysis functions // /////////////////////////////// // // ScorePage::analyzePitchX -- older function which may be removed. // This one works backwards from a particular note to determine // the accidental. // void ScorePage::analyzePitchX(void) { if (!sortQ) { sortByStaff(); } if (systemAnalysisQ == 0) { analyzeSystems(); } int currentposition = 0; int i; for (i=1; i<=getMaxStaff(); i++) { currentposition = assignPitchX(i, currentposition); } } ////////////////////////////// // // ScorePage::assignPitchX -- // int ScorePage::assignPitchX(int staff, int currentposition) { int i = currentposition; int clef = 0; // treble clef by default int clefoffset = 0; // base position by default int keysig = 0; // C major by default int p12clef = 0; int p12clefoffset = 0; int p12keysig = 0; ScoreRecord* clefptr = NULL; ScoreRecord* keyptr = NULL; Array accidentals(7); Array p12accidentals(7); accidentals.zero(); accidentals.allowGrowth(0); p12accidentals.zero(); p12accidentals.allowGrowth(0); while (data[i]->getPValue(2) == staff) { if (data[i]->isKeysigItem()) { keysig = (int)data[i]->getPValue(5); resetAccidentals(accidentals, keysig); } else if (data[i]->isBarlineItem()) { resetAccidentals(accidentals, keysig); } else if (data[i]->isNoteItem()) { switch ((int)data[i]->getPValue(12)) { case 1: // place on staff above // does not handle accidental alterations in affected staff clefptr = &getClefAtLocation(staff+1, data[currentposition]->getPValue(3)); keyptr = &getKeysigAtLocation(staff+1, data[currentposition]->getPValue(3)); p12clef = (int)clefptr->getPValue(5); p12clefoffset = (int)clefptr->getPValue(4); p12keysig = (int)keyptr->getPValue(5); resetAccidentals(p12accidentals, p12keysig); data[i]->setPitch(convertPitchToBase40( (int)(data[i]->getPValue(4) + 0.5) % 100, ((int)data[i]->getPValue(5)) % 10, p12clef, p12clefoffset, p12accidentals)); break; case 2: // place on staff below // does not handle accidental alterations in affected staff clefptr = &getClefAtLocation(staff-1, data[currentposition]->getPValue(3)); keyptr = &getKeysigAtLocation(staff-1, data[currentposition]->getPValue(3)); p12clef = (int)clefptr->getPValue(5); p12clefoffset = (int)clefptr->getPValue(4); p12keysig = (int)keyptr->getPValue(5); resetAccidentals(p12accidentals, p12keysig); data[i]->setPitch(convertPitchToBase40( (int)(data[i]->getPValue(4) + 0.5) % 100, ((int)data[i]->getPValue(5)) % 10, p12clef, p12clefoffset, p12accidentals)); break; case 0: // place on current staff default: data[i]->setPitch(convertPitchToBase40( (int)(data[i]->getPValue(4) + 0.5) % 100, ((int)data[i]->getPValue(5)) % 10, clef, clefoffset, accidentals)); break; } } else if (data[i]->isRestItem()) { data[i]->setPitch(-1000); } else if (data[i]->isClefItem()) { clef = (int)data[i]->getPValue(5); clefoffset = (int)data[i]->getPValue(4) % 100; if (data[i]->getPValue(5) == 0.8) { // Vocal tenor clef clef = 0; clefoffset += 8; } } i++; } return i; } ////////////////////////////// // // ScorePage::resetPitchSpellings -- Reset diatonic pitch states after // a barline has been encountered. // void ScorePage::resetPitchSpellings(int staffidx, int barheight, Array >& pitchstate, Array >& keysig) { if (barheight == 0) { barheight = 1; } if (pitchstate.getSize() <= staffidx+barheight) { // invisible staff, ignore pitch processing return; } int i, j; for (i=0; i middleCVpos(staffcount); // current clef info for each staff middleCVpos.setAll(1); // assume treble clef if no clef Array > keysig; // current key signature on staff Array > pitchstate; // current accidental on notes // resets to key signature after // barlines. keysig.setSize(staffcount); pitchstate.setSize(staffcount); for (i=0; iisClefItem()) { middleCVpos[staffidx] = ScoreRecord::getMiddleCVpos(*curr); continue; } if (curr->isKeysigItem()) { ScoreRecord::getDiatonicAccidentalState(keysig[staffidx], *curr); fillPitchStatesWithKeySig(pitchstate, staffidx, keysig); continue; } if (curr->isBarlineItem()) { int length = (int)(curr->getValue(P4)); resetPitchSpellings(staffidx, length, pitchstate, keysig); // deal with tied notes later... continue; } if (!curr->isNoteItem()) { continue; } vpos = (int)(curr->getVpos()+0.5); diatonic = vpos - middleCVpos[staffidx] + 7*4; base40 = Convert::base7ToBase40(diatonic); accidental = curr->getAccidental(); hasnatural = curr->hasNatural(); haseditorial = curr->hasEditorialAccidental(); if ((accidental == 0) && (hasnatural == 0)) { accidental = pitchstate[staffidx][diatonic]; } if (!haseditorial) { printedaccidental = curr->getPrintedAccidental(); // if the printed accidental matches the pitch state, // then mark the note object as possessing a cautionary accidental if ((printedaccidental == 1) && (pitchstate[staffidx][diatonic] == -1)) { curr->setValue("cautionary", 1); } else if ((printedaccidental == 2) && (pitchstate[staffidx][diatonic] == +1)) { curr->setValue("cautionary", 1); } else if ((printedaccidental == 3) && (pitchstate[staffidx][diatonic] == 0)) { curr->setValue("cautionary", 1); } else if ((printedaccidental == 4) && (pitchstate[staffidx][diatonic] == -2)) { curr->setValue("cautionary", 1); } else if ((printedaccidental == 5) && (pitchstate[staffidx][diatonic] == +2)) { curr->setValue("cautionary", 1); } pitchstate[staffidx][diatonic] = accidental; } base40 += accidental; curr->setPitch(base40); } } ////////////////////////////// // // ScorePage::fillPitchStatesWithKeySig -- ignoring a keysignature in the // middle of a measure for now... // void ScorePage::fillPitchStatesWithKeySig(Array >& pitchstate, int staffidx, Array >& keysig) { int i; int ii; for (i=0; i expectedStaffBeat; expectedStaffBeat.setSize(1000); expectedStaffBeat.setSize(0); expectedStaffBeat.setGrowth(1000); int i; double dur; double nextevent; double currentSysDur = 0.0; Array& system = lineSystemSequence[sysidx]; double activeHpos = 0.0; double hpos = 0.0; double threshold = 0.001; // first position every note/test on the system for (i=0; ihasDuration()) { continue; } hpos = data[system[i]]->getHpos(); if (hpos > activeHpos + threshold) { currentSysDur = getSmallestEvent(expectedStaffBeat); removeEvent(expectedStaffBeat, currentSysDur); activeHpos = hpos; } dur = data[system[i]]->getDuration(); data[system[i]]->setSystemBeat(currentSysDur); // dataSysBeat[system[i]] = currentSysDur; nextevent = dur+currentSysDur; storeNextEvent(expectedStaffBeat, nextevent); } systemDuration[sysidx] = getSmallestEvent(expectedStaffBeat); removeEvent(expectedStaffBeat, currentSysDur); //for (i=0; i=0; i--) { if (data[system[i]]->hasDuration()) { currentSysDur = data[system[i]]->getSystemBeat(); } else { data[system[i]]->setSystemBeat(currentSysDur); } } rhythmAnalysisQ = 1; } ////////////////////////////// // // ScorePage::getPitch -- return an objects duration. // double ScorePage::getPitch(int itemidx) { return data[itemidx]->getPitch(); } double ScorePage::getPitch(int sysidx, int staffidx, int objectidx) { return data[lineStaffSequence[sysidx][staffidx][objectidx]]->getPitch(); } ////////////////////////////// // // ScorePage::getDuration -- return an objects duration. // double ScorePage::getDuration(int itemidx) { return data[itemidx]->getDuration(); } double ScorePage::getDuration(int sysidx, int staffidx, int objectidx) { return data[lineStaffSequence[sysidx][staffidx][objectidx]]->getDuration(); } ////////////////////////////// // // ScorePage::hasDuration -- // int ScorePage::hasDuration(int itemidx) { return data[itemidx]->hasDuration(); } int ScorePage::hasDuration(int sysidx, int staffidx, int objectidx) { return data[lineStaffSequence[sysidx][staffidx][objectidx]]->hasDuration(); } ////////////////////////////// // // ScorePage::removeEvent -- // void ScorePage::removeEvent(Array& expectedStaffBeat, double currentSysDur) { int i; for (i=0; i& expectedStaffBeat) { double output = 123123123.0; int i; for (i=0; i& expectedStaffBeat, double nextevent) { int i; int firstpos = -1; for (i=0; i= 0) { expectedStaffBeat[firstpos] = nextevent; } else { expectedStaffBeat.append(nextevent); } } ////////////////////////////// // // ScorePage::getClefAtLocation -- returns the currently active clef // on the specified staff at the given position. If not clef was // found, then will return a treble clef. // ScoreRecord& ScorePage::getClefAtLocation(int staffno, float position) { if (!sortQ) { sortByStaff(); } static ScoreRecord defaultclef; static int init = 0; if (!init) { init = 1; defaultclef.setPValue(1, 3.0); defaultclef.setPValue(2, 1.0); defaultclef.setPValue(3, 0.0); defaultclef.setPValue(4, 0.0); defaultclef.setPValue(5, 0.0); } int i = findStaff(staffno); if (i < 0) { defaultclef.setPValue(2, (float)staffno); return defaultclef; } int clefindex = -1; while (i < data.getSize() && (int)data[i]->getPValue(2) == staffno && data[i]->getPValue(3) < position) { if (data[i]->isClefItem()) { clefindex = i; } i++; } if (clefindex >= 0) { return *(data[clefindex]); } defaultclef.setPValue(2, (float)staffno); return defaultclef; } ////////////////////////////// // // ScorePage::getKeysigAtLocation -- returns the currently active // key signature at the current location on the given staff. // If no key signature has been found, then use the key of C major. // ScoreRecord& ScorePage::getKeysigAtLocation(int staffno, float position) { if (!sortQ) { sortByStaff(); } static ScoreRecord defaultkeysig; static int init = 0; if (!init) { init = 1; defaultkeysig.setPValue(1, 17.0); defaultkeysig.setPValue(2, 1.0); defaultkeysig.setPValue(3, 0.0); defaultkeysig.setPValue(4, 0.0); defaultkeysig.setPValue(5, 0.0); defaultkeysig.setPValue(6, 0.0); } defaultkeysig.setPValue(2, (float)staffno); int i = findStaff(staffno); if (i < 0) { defaultkeysig.setPValue(2, (float)staffno); return defaultkeysig; } int keyindex = -1; while (i < data.getSize() && (int)data[i]->getPValue(2) == staffno && data[i]->getPValue(3) < position) { if (data[i]->isKeysigItem()) { keyindex = i; } i++; } if (keyindex >= 0) { return *(data[keyindex]); } defaultkeysig.setPValue(2, (float)staffno); return defaultkeysig; } ////////////////////////////// // // ScorePage::convertPitchToBase40 -- figure out what the @#$% pitch is. // int ScorePage::convertPitchToBase40(int line, int acc, int clef, int clefoffset, Array& accidentals) { int octave = 0; int diatonic = 0; int basepitch[7] = {0, 6, 12, 17, 23, 29, 35}; switch (clef) { case 1: // bass clef octave = 2; diatonic = line + 1 - clefoffset; break; case 2: // alto clef octave = 3; diatonic = line - clefoffset; break; case 3: // tenor clef octave = 3; diatonic = line - 2 - clefoffset; break; case 0: // treble clef default: // handle other clefs later, assign to treble clef for now octave = 4; diatonic = line - 1 - clefoffset; break; } if (diatonic < 0) { while (diatonic < 0) { diatonic += 7; octave -= 1; } } if (diatonic > 6) { octave += diatonic / 7; diatonic = diatonic % 7; } int output = basepitch[diatonic] + 40 * octave + 2; switch (acc) { case 1: // flat found on note output--; accidentals[diatonic] = -1; break; case 2: // sharp found on note output++; accidentals[diatonic] = +1; break; case 3: // natural found on note accidentals[diatonic] = 0; break; case 4: // double flat found on note output-=2; accidentals[diatonic] = -2; break; case 5: // double sharp found on note output+=2; accidentals[diatonic] = +2; break; case 0: // no accidental specified explicitly on the note default: output += accidentals[diatonic]; break; } if (output < 0) { cout << "ERROR: acc=" << accidentals[diatonic] << " octave=" << octave << " diatonic=" << diatonic << endl; } return output; } ////////////////////////////// // // resetAccidentals -- set the accidentals to the current key signature // void ScorePage::resetAccidentals(Array& accidentals, int keysig) { if (keysig == 0) { accidentals.zero(); return; } if (keysig > 0) { if (keysig > 0) accidentals[3] = +1; // F-sharp if (keysig > 1) accidentals[0] = +1; // C-sharp if (keysig > 2) accidentals[4] = +1; // G-sharp if (keysig > 3) accidentals[1] = +1; // D-sharp if (keysig > 4) accidentals[5] = +1; // A-sharp if (keysig > 5) accidentals[2] = +1; // E-sharp if (keysig > 6) accidentals[6] = +1; // B-sharp return; } if (keysig < 0) accidentals[6] = -1; // B-flat if (keysig < -1) accidentals[2] = -1; // E-flat if (keysig < -2) accidentals[5] = -1; // A-flat if (keysig < -3) accidentals[1] = -1; // D-flat if (keysig < -4) accidentals[4] = -1; // G-flat if (keysig < -5) accidentals[0] = -1; // C-flat if (keysig < -6) accidentals[3] = -1; // F-flat } ////////////////////////////// // // getStaffObjectIndexes -- // void ScorePage::getStaffObjectIndexes(int sysidx, int staffidx, Array& list) { if (systemAnalysisQ == 0) { analyzeSystems(); } list = lineStaffSequence[sysidx][staffidx]; } ////////////////////////////// // // ScorePage::getSystemObjectIndexes -- // void ScorePage::getSystemObjectIndexes(int sysidx, Array& list) { if (systemAnalysisQ == 0) { analyzeSystems(); } list = lineSystemSequence[sysidx]; } ////////////////////////////// // // ScorePage::getStaffOffset -- // int ScorePage::getStaffOffset(int itemidx) { return data[itemidx]->getStaffOffset(); } ////////////////////////////// // // ScorePage::getPageDuration -- // double ScorePage::getPageDuration(void) { if (rhythmAnalysisQ == 0) { analyzeRhythm(); } return pageDuration; } ////////////////////////////// // // ScorePage::PackBase40Pitch -- store the Base-40 (middle C = 162) in // the fractional part of P6 for notes (middle C = 163). // void ScorePage::packBase40Pitch(void) { if (systemAnalysisQ == 0) { analyzeSystems(); } int i; double oldval; for (i=0; iisNoteItem()) { continue; } oldval = (int)(data[i]->getValue(P6)); data[i]->setValue(P6, oldval + (data[i]->getPitch()+1)/1000.0); } } ////////////////////////////// // // ScorePage::getSystemStaffObjectCount -- // int ScorePage::getSystemStaffObjectCount(int sysidx, int staffidx) { return lineStaffSequence[sysidx][staffidx].getSize(); } ////////////////////////////// // // ScorePage::getSystemStaffObjectCount -- // int ScorePage::getSystemStaffItemCount(int sysidx, int staffidx) { return getSystemStaffObjectCount(sysidx, staffidx); } ////////////////////////////// // // ScorePage::getSystemStaffItemNumber -- // int ScorePage::getSystemStaffItemNumber(int sysidx, int staffidx, int itemidx) { return lineStaffSequence[sysidx][staffidx][itemidx]; } ScoreRecord& ScorePage::getSystemStaffItem(int sysidx, int staffidx, int itemidx) { return *(data[lineStaffSequence[sysidx][staffidx][itemidx]]); } int ScorePage::getSystemStaffItemIndex(int itemidx) { return itemSystemStaffIndex[itemidx]; // reverse lookup of // lineStaffSequence. } ////////////////////////////// // // ScorePage::getSlurEndNotes -- Return a list of notes found near each // end of a slur. This is used to decide if the slur is a slur or a tie. // void ScorePage::getSlurEndNotes(int itemidx, Array& leftside, Array& rightside) { leftside.setSize(0); rightside.setSize(0); double lefthpos = getItem(itemidx).getValue(P3); double righthpos = getItem(itemidx).getValue(P6); double staffidx = getSystemStaffIndex(itemidx); //search preferentially to the left of the left side of the slur double hposl = -1.0; double hposr = -1.0; int lsystemnum = -1; int systemnum = getItemSystemNumber(itemidx); int rsystemnum = -1; int lind = -1; int rind = -1; int i; for (i=itemidx; i=0; i--) { if (staffidx != getSystemStaffIndex(i)) { continue; } if (getItem(i).isNoteItem()) { lind = i; hposl = getItem(lind).getValue(P3); lsystemnum = getItemSystemNumber(lind); break; } } if (lsystemnum < 0) { return; } if (hposl == hposr) { getNotesAtHpos(leftside, systemnum, staffidx, hposl); } else if (lsystemnum < systemnum) { // the slur/tie must have travelled over a system break. So presume // that this is the best direction. getNotesAtHpos(leftside, lsystemnum, staffidx, hposl); } else if (abs(lefthpos - hposl) < abs(lefthpos - hposr)) { // notes on left side of left side of slur are closest getNotesAtHpos(leftside, lsystemnum, staffidx, hposl); } else { // notes on right side of left side of slur are closest getNotesAtHpos(leftside, rsystemnum, staffidx, hposr); } //search preferentially to the right of the right side of the slur // find the horizontal position, or the first object just before it. int rightindex = -1; int lasti = -1; int tsystemnum; double rhpos = getItem(itemidx).getValue(P3); double lastrhpos = rhpos; for (i=itemidx; i righthpos) { rightindex = lasti; break; } lastrhpos = rhpos; lasti = i; } if (rightindex < 0) { return; } for (i=rightindex; i=0; i--) { if (staffidx != getSystemStaffIndex(i)) { continue; } if (getItem(i).isNoteItem()) { lind = i; hposl = getItem(lind).getValue(P3); lsystemnum = getItemSystemNumber(lind); break; } } if (rsystemnum < 0) { // return if a tie hanging off the right side of a system // (tie will have to be linked to notes at the page level). return; } if (hposl == hposr) { getNotesAtHpos(rightside, systemnum, staffidx, hposl); } else if (rsystemnum > systemnum) { // the slur/tie must have travelled over a system break. So presume // that this is the best direction. getNotesAtHpos(rightside, rsystemnum, staffidx, hposl); } else if (abs(lefthpos - hposl) < abs(lefthpos - hposr)) { // notes on left side of left side of slur are closest getNotesAtHpos(rightside, rsystemnum, staffidx, hposl); } else { // notes on right side of left side of slur are closest getNotesAtHpos(rightside, rsystemnum, staffidx, hposr); } } ////////////////////////////// // // ScorePage::getNotesAtHpos -- Return a list of notes at the given // horizontal position on the given staff on the given system. // void ScorePage::getNotesAtHpos(Array& notes, int sysidx, int staffidx, double hpos) { // for now do a simple search, but could implement a sorted // search in the future... int i; int obj; double p3; notes.setSize(0); if (sysidx < 0) { return; } if (staffidx < 0) { return; } int itemcount = getSystemStaffItemCount(sysidx, staffidx); for (i=0; i hpos) { // already found all notes break; } else if (p3 == hpos) { obj = getSystemStaffItemNumber(sysidx, staffidx, i); notes.append(obj); } } } ////////////////////////////// // // ScorePage::getItemSystemNumber -- // int ScorePage::getItemSystemNumber(int itemidx) { int staffnum = getItem(itemidx).getValue(P2); return P2ToSystemIdx[staffnum]; } ////////////////////////////// // // ScorePage::identifyLyricsOnStaff -- // void ScorePage::identifyLyricsOnStaff(int sysidx, int staffidx) { int i; int count = getSystemStaffItemCount(sysidx, staffidx); Array itemidx(count); Array vposes(count); itemidx.setSize(0); vposes.setSize(0); double vpos; int index; Array histtextpos(201); Array histlinepos(201); histtextpos.setAll(0); histlinepos.setAll(0); int vvpos; for (i=0; i lyricverse; Array lyricvpos; lyricverse.setSize(100); lyricverse.setSize(0); lyricvpos.setSize(100); lyricvpos.setSize(0); Array score(histtextpos.getSize()); score.setAll(0); int verse = 0; int tval; int lval; int cutoff = 5; int value; for (i=1; i 0) { lval += histlinepos[i-1]; } if (i < histlinepos.getSize()-1) { lval += histlinepos[i+1]; } score[i] = (tval + lval*2); if (lval > tval+2) { score[i] = score[i] - lval*2; } } for (i=score.getSize()-2; i>=1; i--) { if (score[i] < cutoff) { continue; } if (score[i] < score[i-1]) { continue; } if (score[i] < score[i+1]) { continue; } // cout << "Verse" << verse << "\t" // << "SYS" << sysidx << "STAFF" << staffidx << "\t" // << histtextpos[i] // << "," << histlinepos[i] << "," << histtextpos[i]+histlinepos[i] // << "\t at vpos " << i-100 << "\tscore = " << score[i] << endl; lyricverse.append(verse); verse++; value = i-100; lyricvpos.append(value); } for (i=0; i pageStaffLyrics[getSystemStaffP2(sysidx, staffidx)]) { pageStaffLyrics[getSystemStaffP2(sysidx, staffidx)] = lyricverse.getSize(); } if (lyricverse.getSize() > sysStaffLyrics[staffidx]) { sysStaffLyrics[staffidx] = lyricverse.getSize(); } } ////////////////////////////// // // ScorePage::processVerse -- // // Example of a syllable continuation line: // P1 P2 P3 P4 P5 P6 // 4 15 143.3006900 16.000000 16.000000 164.756302 // // Example of a dash between syllables: // P1 P2 P3 P4 P5 P6 P7 P8 P9 // 4 15 119.624 16.960 16.960 136.460 1.00 1.543 13.445 // // Line Parameters: // // P1 = 4 // P2 = staff // P3 = hpos of left side // P4 = vpos of left side // P5 = vpos of right side (or 999 for hairpins) // P6 = hpos of right side // P7 = type of line (0=normal, 1=dashed, -1=wavy, -1,P5:999 = dimuendo, // 0,P5:999 = crescendo) // P8 = length of dashes on a dashed line // P9 = angle of line, or spaces between lines when dashed // // Identify whether syllables are connected to adjacent syllables with // hyphens. Also, identify line extensions. // // Set these numeric named parameters on the lyrics and the lines between // them (may change numbering system later to link the items together). // // hyphenbefore : 1 if part of a word with a hyphen preceeding it, 0 otherwise. // hyphenafter : 1 if part of a word with a hyphen following it, 0 otherwise. // extension : 1 if end of word with a multi-note extension line after it. // verse : verse number starting at 1 for the highest verse on the line // void ScorePage::processVerse(int verse, int vpos, int sysidx, int staffidx, Array& objlist, Array& vposes) { Array verseitems(objlist.getSize()); verseitems.setSize(0); int i; for (i=0; i textdata; ScoreRecord* srecord; ScoreRecord* slast; ScoreRecord* snext; for (i=0; isetValue("verse", verse+1); // offset by 1 for data so // that 0 can mean not a lyric if (i>0) { slast = data[verseitems[i-1]]; } else { slast = NULL; } if (iisTextItem()) { if (snext != NULL) { if (snext->isDashedLine()) { srecord->setValue("hyphenafter", 1); snext->setValue("hyphen", 1); } else if (snext->isPlainLine()) { srecord->setValue("extension", 1); } } if (slast != NULL) { if (slast->isDashedLine()) { srecord->setValue("hyphenbefore", 1); slast->setValue("hyphen", 1); } } } } // // // testing the analysis by printing the lyric line for the staff, // // without hyphens or extensions: // cout << "!! "; // for (i=0; iisLineItem()) { // // print a hyphen for a word break at the end of a line // if (srecord->isDashedLine()) { // cout << "-"; // } // } // // if (!srecord->isTextItem()) { // continue; // } // cout << srecord->getTextDataWithoutFonts(textdata); // if (!srecord->getNumKeyValue("hyphenafter")) { // // print a space if the syllable does not have a hyphen after it. // cout << " "; // } // } // cout << endl; } ////////////////////////////// // // ScorePage::getSystemStaffLyricCount -- return verse count for a particular part (system staff group). // int ScorePage::getSystemStaffLyricCount(int staffidx) { return sysStaffLyrics[staffidx]; } ////////////////////////////// // // ScorePage::getPageStaffLyricCount -- return verse count for lyrics for a particular staff (P2 value) // on the page. // int ScorePage::getPageStaffLyricCount(int staffidx) { return pageStaffLyrics[staffidx]; } ////////////////////////////// // // ScorePage::identifySystemBreakHyphens -- Search for hyphenations which // span system line breaks. // void ScorePage::identifySystemBreakHyphens(int sys1idx, int sys2idx, int staffidx) { int staff1num = getSystemStaffP2(sys1idx, staffidx); int staff2num = getSystemStaffP2(sys2idx, staffidx); int verse1count = getPageStaffLyricCount(staff1num); int verse2count = getPageStaffLyricCount(staff2num); // verse1count and verse2count should be the same value; otherwise, // the alignment of verses may be incorrect. int maxverse = verse1count < verse2count ? verse1count : verse2count; if (maxverse == 0) { return; } Array syllables(maxverse); syllables.setAll(-1); // find the first lyric syllable for each verse on the second line int total = 0; int verse; ScoreRecord* srecord; int i; for (i=0; iisTextItem()) { continue; } verse = srecord->getVerseIndex(); if (verse < 0) { continue; } // found a verse syllable, add it to the list if the verse has not // yet been processed if (verse > syllables.getSize()-1) { // one staff may have more verses than another, ignore any // verses which are greater than the least amount. continue; } if (syllables[verse] < 0) { syllables[verse] = i; total++; } // stop looking for syllables if all verses have been accounted for if (total >= maxverse) { break; } } // find the last lyric or hyphen for each verse on the first line Array proceeding(maxverse); proceeding.setAll(-1); total = 0; for (i=lineStaffSequence[sys1idx][staffidx].getSize()-1; i>=0; i--) { srecord = data[lineStaffSequence[sys1idx][staffidx][i]]; verse = srecord->getVerseIndex(); if (verse < 0) { continue; } if ((verse < proceeding.getSize()) && (proceeding[verse] < 0)) { proceeding[verse] = i; total++; } if (total >= maxverse) { break; } } // for each verse, add a "hyphenbefore" to syllables on the second staff // if the last item in the verse on the previous line as a hyphen. ScoreRecord* srecord1; ScoreRecord* srecord2; for (i=0; iisHyphen()) { srecord2->setValue("hyphenbefore", 1); } } } ////////////////////////////// // // getLowestStaffOnSystem -- // int ScorePage::getLowestStaffOnSystem(int sysidx) { // currently presume that the lowest staff in the system is the lowest staff // of the system on the page (this may change in the future). return getSystemStaffP2(sysidx, 0); } ////////////////////////////// // // ScorePage::getNextNotePositionOnStaff -- Get the next note/rest position // on the given objects staff. P12 of P1=1 is not considered. // A value of 200 will be returned if there are no notes/rests // after the given one. // double ScorePage::getNextNotePositionOnStaff(int itemidx) { int sysidx = getSystemIndex(itemidx); int staffidx = getSystemStaffIndex(itemidx); int ssitem = getSystemStaffItemIndex(itemidx); double hpos = getItem(itemidx).getHpos(); double thpos; double output = 200.0; int pagestaff = getItem(itemidx).getPageStaff(); int tpagestaff = 0; ScoreRecord* srecord; int i; for (i=ssitem+1; igetPageStaff(); thpos = srecord->getHpos(); if (pagestaff != tpagestaff) { break; } if (!srecord->hasDuration()) { // only considering notes and rests. continue; } if (fabs(thpos - hpos) < 0.1) { // the note/rest is in approximately the same horizontal postion // as the original. continue; } return thpos; } return output; } ////////////////////////////// // // ScorePage::getPreviousNotePositionOnStaff -- Get the previous note/rest // position on the given object's staff. P12 of P1=1 is not considered. // A value of 0 will be returned if there are no notes/rests before // the given one. Notes/rests at the same hpos will not be considered. // double ScorePage::getPreviousNotePositionOnStaff(int itemidx) { int sysidx = getSystemIndex(itemidx); int staffidx = getSystemStaffIndex(itemidx); int ssitem = getSystemStaffItemIndex(itemidx); double hpos = getItem(itemidx).getHpos(); double thpos; int pagestaff = getItem(itemidx).getPageStaff(); int tpagestaff; double output = 0.0; ScoreRecord* srecord; int i; for (i=ssitem-1; i>=0; i--) { srecord = &(getSysStaffItem(sysidx, staffidx, i)); tpagestaff = srecord->getPageStaff(); if (pagestaff != tpagestaff) { break; } if (!srecord->hasDuration()) { // only considering notes and rests. continue; } thpos = srecord->getHpos(); if (fabs(thpos - hpos) < 0.1) { // the note/rest is in approximately the same horizontal postion // as the original. continue; } return thpos; } return output; } */ // md5sum: 097b1076b87b8b25465be795f377816f ScorePage.cpp [20050403]