📄 lilypondexporter.cpp
字号:
break; } // First tempo change may be before the first segment. // Do not apply it before the first segment appears. if (prevTempoChangeTime < compositionStartTime) { prevTempoChangeTime = compositionStartTime; } writeSkip(m_composition->getTimeSignatureAt(prevTempoChangeTime), prevTempoChangeTime, compositionEndTime - prevTempoChangeTime, false, str); str << std::endl; str << indent(--col) << "}" << std::endl; } // open \score section str << "\\score {" << std::endl; // bind staffs with or without staff group bracket str << indent(++col) // indent+ << (m_exportStaffGroup == true ? "\\new StaffGroup " : "") << "<<" << std::endl; // Make chords offset colliding notes by default str << indent(++col) << "% force offset of colliding notes in chords:" << std::endl; str << indent(col) << "\\override Score.NoteColumn #\'force-hshift = #1.0" << std::endl; int lastTrackIndex = -1; int voiceCounter = 0; // Write out all segments for each Track, in track order. // This involves a hell of a lot of loops through all tracks // and segments, but the time spent doing that should still // be relatively small in the greater scheme. Track *track = 0; for (int trackPos = 0; (track = m_composition->getTrackByPosition(trackPos)) != 0; ++trackPos) { for (Composition::iterator i = m_composition->begin(); i != m_composition->end(); ++i) { if ((*i)->getTrack() != track->getId()) continue; emit setProgress(int(double(trackPos) / double(m_composition->getNbTracks()) * 100.0)); rgapp->refreshGUI(50); bool currentSegmentSelected = false; if ((m_exportSelection == EXPORT_SELECTED_SEGMENTS) && (m_view != NULL) && (m_view->haveSelection())) { // // Check whether the current segment is in the list of selected segments. // SegmentSelection selection = m_view->getSelection(); for (SegmentSelection::iterator it = selection.begin(); it != selection.end(); it++) { if ((*it) == (*i)) currentSegmentSelected = true; } } else if ((m_exportSelection == EXPORT_SELECTED_SEGMENTS) && (m_notationView != NULL)) { currentSegmentSelected = m_notationView->hasSegment(*i); } // Check whether the track is a non-midi track. InstrumentId instrumentId = track->getInstrument(); // very off the cuff tentative fix for #1836149; this is so obvious // a fix that I wonder why this <SoftSynthInstrumentBase was ever // written in the first place, and it makes me suspicous that I'm // missing the big picture somewhere. Anyway, it seems to work: //bool isMidiTrack = (instrumentId >= MidiInstrumentBase && instrumentId) < SoftSynthInstrumentBase); bool isMidiTrack = instrumentId >= MidiInstrumentBase; if (isMidiTrack && ( // Skip non-midi tracks. (m_exportSelection == EXPORT_ALL_TRACKS) || ((m_exportSelection == EXPORT_NONMUTED_TRACKS) && (!track->isMuted())) || ((m_exportSelection == EXPORT_SELECTED_TRACK) && (m_view != NULL) && (track->getId() == m_composition->getSelectedTrack())) || ((m_exportSelection == EXPORT_SELECTED_TRACK) && (m_notationView != NULL) && (track->getId() == m_notationView->getCurrentSegment()->getTrack())) || ((m_exportSelection == EXPORT_SELECTED_SEGMENTS) && (currentSegmentSelected)))) { if ((int) (*i)->getTrack() != lastTrackIndex) { if (lastTrackIndex != -1) { // close the old track (Staff context) str << indent(--col) << ">> % Staff" << std::endl; // indent- } lastTrackIndex = (*i)->getTrack(); // avoid problem with <untitled> tracks yielding a // .ly file that jumbles all notes together on a // single staff... every Staff context has to // have a unique name, even if the // Staff.instrument property is the same for // multiple staffs... // Added an option to merge staffs with the same, non-empty // name. This option makes it possible to produce staffs // with polyphonic, and polyrhytmic, music. Polyrhytmic // music in a single staff is typical in piano, or // guitar music. (hjj) // In the case of colliding note heads, user may define // - DISPLACED_X -- for a note/chord // - INVISIBLE -- for a rest std::ostringstream staffName; staffName << protectIllegalChars(m_composition-> getTrackById(lastTrackIndex)->getLabel()); if (!m_exportStaffMerge || staffName.str() == "") { str << std::endl << indent(col) << "\\context Staff = \"track " << (trackPos + 1) << "\" "; } else { str << std::endl << indent(col) << "\\context Staff = \"" << staffName.str() << "\" "; } str << "<< " << std::endl; // The octavation is omitted in the instrument name. // HJJ: Should it be automatically added to the clef: G^8 ? // What if two segments have different transpose in a track? std::ostringstream staffNameWithTranspose; staffNameWithTranspose << "\\markup { \\column { \"" << staffName.str() << " \""; if (((*i)->getTranspose() % 12) != 0) { staffNameWithTranspose << " \\line { "; switch ((*i)->getTranspose() % 12) { case 1 : staffNameWithTranspose << "\"in D\" \\smaller \\flat"; break; case 2 : staffNameWithTranspose << "\"in D\""; break; case 3 : staffNameWithTranspose << "\"in E\" \\smaller \\flat"; break; case 4 : staffNameWithTranspose << "\"in E\""; break; case 5 : staffNameWithTranspose << "\"in F\""; break; case 6 : staffNameWithTranspose << "\"in G\" \\smaller \\flat"; break; case 7 : staffNameWithTranspose << "\"in G\""; break; case 8 : staffNameWithTranspose << "\"in A\" \\smaller \\flat"; break; case 9 : staffNameWithTranspose << "\"in A\""; break; case 10 : staffNameWithTranspose << "\"in B\" \\smaller \\flat"; break; case 11 : staffNameWithTranspose << "\"in B\""; break; } staffNameWithTranspose << " }"; } staffNameWithTranspose << " } }"; if (m_languageLevel < LILYPOND_VERSION_2_10) { str << indent(++col) << "\\set Staff.instrument = " << staffNameWithTranspose.str() << std::endl; } else { str << indent(++col) << "\\set Staff.instrumentName = " << staffNameWithTranspose.str() << std::endl; } if (m_exportMidi) { // Set midi instrument for the Staff std::ostringstream staffMidiName; Instrument *instr = m_studio->getInstrumentById(m_composition->getTrackById(lastTrackIndex)->getInstrument()); staffMidiName << instr->getProgramName(); str << indent(col) << "\\set Staff.midiInstrument = \"" << staffMidiName.str() << "\"" << std::endl; } // multi measure rests are used by default str << indent(col) << "\\set Score.skipBars = ##t" << std::endl; // turn off the stupid accidental cancelling business, // because we don't do that ourselves, and because my 11 // year old son pointed out to me that it "Looks really // stupid. Why is it cancelling out four flats and then // adding five flats back? That's brain damaged." str << indent(col) << "\\set Staff.printKeyCancellation = ##f" << std::endl; str << indent(col) << "\\new Voice \\global" << std::endl; if (tempoCount > 0) { str << indent(col) << "\\new Voice \\globalTempo" << std::endl; } } // Temporary storage for non-atomic events (!BOOM) // ex. Lilypond expects signals when a decrescendo starts // as well as when it ends eventendlist eventsInProgress; eventstartlist eventsToStart; // If the segment doesn't start at 0, add a "skip" to the start // No worries about overlapping segments, because Voices can overlap // voiceCounter is a hack because Lilypond does not by default make // them unique std::ostringstream voiceNumber; voiceNumber << "voice " << ++voiceCounter; str << std::endl << indent(col++) << "\\context Voice = \"" << voiceNumber.str() << "\" {"; // indent+ str << std::endl << indent(col) << "\\override Voice.TextScript #'padding = #2.0"; str << std::endl << indent(col) << "\\override MultiMeasureRest #'expand-limit = 1" << std::endl; SegmentNotationHelper helper(**i); helper.setNotationProperties(); int firstBar = m_composition->getBarNumber((*i)->getStartTime()); if (firstBar > 0) { // Add a skip for the duration until the start of the first // bar in the segment. If the segment doesn't start on a bar // line, an additional skip will be written (in the form of // a series of rests) at the start of writeBar, below. //!!! This doesn't cope correctly yet with time signature changes // during this skipped section. str << std::endl << indent(col); writeSkip(timeSignature, compositionStartTime, m_composition->getBarStart(firstBar) - compositionStartTime, false, str); } std::string lilyText = ""; // text events std::string prevStyle = ""; // track note styles Rosegarden::Key key; bool haveRepeating = false; bool haveAlternates = false; bool nextBarIsAlt1 = false; bool nextBarIsAlt2 = false; bool prevBarWasAlt2 = false; int MultiMeasureRestCount = 0; bool nextBarIsDouble = false; bool nextBarIsEnd = false; bool nextBarIsDot = false; for (int barNo = m_composition->getBarNumber((*i)->getStartTime()); barNo <= m_composition->getBarNumber((*i)->getEndMarkerTime()); ++barNo) { timeT barStart = m_composition->getBarStart(barNo); timeT barEnd = m_composition->getBarEnd(barNo); if (barStart < compositionStartTime) { barStart = compositionStartTime; } // open \repeat section if this is the first bar in the // repeat if ((*i)->isRepeating() && !haveRepeating) { haveRepeating = true; //!!! calculate the number of times this segment //repeats and make the following variable meaningful int numRepeats = 2; str << std::endl << indent(col++) << "\\repeat volta " << numRepeats << " {"; } // open the \alternative section if this bar is alternative ending 1 // ending (because there was an "Alt1" flag in the // previous bar to the left of where we are right now) // // Alt1 remains in effect until we run into Alt2, which // runs to the end of the segment if (nextBarIsAlt1 && haveRepeating) { str << std::endl << indent(--col) << "} \% repeat close (before alternatives) "; str << std::endl << indent(col++) << "\\alternative {"; str << std::endl << indent(col++) << "{ \% open alternative 1 "; nextBarIsAlt1 = false; haveAlternates = true; } else if (nextBarIsAlt2 && haveRepeating) { if (!prevBarWasAlt2) { col--; str << std::endl << indent(--col) << "} \% close alternative 1 " << std::endl << indent(col++) << "{ \% open alternative 2"; col++; } prevBarWasAlt2 = true; } // write out a bar's worth of events writeBar(*i, barNo, barStart, barEnd, col, key, lilyText, prevStyle, eventsInProgress, str, MultiMeasureRestCount, nextBarIsAlt1, nextBarIsAlt2, nextBarIsDouble, nextBarIsEnd, nextBarIsDot); } // close \repeat if (haveRepeating) { // close \alternative section if present if (haveAlternates) { str << std::endl << indent(--col) << " } \% close alternative 2 "; } // close \repeat section in either case str << std::endl << indent(--col) << " } \% close " << (haveAlternates ? "alternatives" : "repeat"); } // closing bar if (((*i)->getEndMarkerTime() == compositionEndTime) && !haveRepeating) { str << std::endl << indent(col) << "\\bar \"|.\""; } // close Voice context str << std::endl << indent(--col) << "} % Voice" << std::endl; // indent- // // Write accumulated lyric events to the Lyric context, if desired. // // Sync the code below with LyricEditDialog::unparse() !! // if (m_exportLyrics) { for (long currentVerse = 0, lastVerse = 0; currentVerse <= lastVerse; currentVerse++) { bool haveLyric = false; bool firstNote = true; QString text = ""; timeT lastTime = (*i)->getStartTime(); int lastBarNo = m_composition->getBarNumber(lastTime); for (Segment::iterator j = (*i)->begin(); (*i)->isBeforeEndMarker(j); ++j) { bool isNote = (*j)->isa(Note::EventType); bool isLyric = false; if (!isNote) { if ((*j)->isa(Text::EventType)) { std::string textType; if ((*j)->get <String>(Text::TextTypePropertyName, textType) && textType == Text::Lyric) { isLyric = true; } } } if (!isNote && !isLyric) continue; timeT myTime = (*j)->getNotationAbsoluteTime(); int myBarNo = m_composition->getBarNumber(myTime); if (isNote) { if ((myTime > lastTime) || firstNote) { if (!haveLyric) text += " _"; lastTime = myTime; haveLyric = false; firstNote = false; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -