55 * MuseScore Studio
66 * Music Composition & Notation
77 *
8- * Copyright (C) 2021 MuseScore Limited
8+ * Copyright (C) 2025 MuseScore Limited
99 *
1010 * This program is free software: you can redistribute it and/or modify
1111 * it under the terms of the GNU General Public License version 3 as
1717 * GNU General Public License for more details.
1818 *
1919 * You should have received a copy of the GNU General Public License
20- * along with this program. If not, see <https://www.gnu.org/licenses/>.
20+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
2121 */
2222
23- #include " io/file .h"
23+ #include " lrcwriter .h"
2424
2525#include < QBuffer>
2626
27+ #include " io/file.h"
28+ #include " types/ret.h"
29+
30+ #include " engraving/dom/lyrics.h"
2731#include " engraving/dom/masterscore.h"
2832#include " engraving/dom/repeatlist.h"
29- #include " engraving/dom/lyrics.h"
3033
31- # include " iex_lyricsexport.h "
34+ using namespace Qt ::Literals ;
3235
33- using namespace mu ::engraving ;
36+ using namespace muse ;
3437using namespace muse ::io;
38+ using namespace mu ::engraving;
39+ using namespace mu ::project;
40+ using namespace mu ::iex::lrcexport;
3541
36- namespace mu ::iex::lrcexport {
37- // Interface implementation
38- std::vector<project::INotationWriter::UnitType> LRCWriter::supportedUnitTypes () const
42+ std::vector<INotationWriter::UnitType> LRCWriter::supportedUnitTypes () const
3943{
4044 return { UnitType::PER_PART };
4145}
4246
43- //
44- // LRCWriter::supportsUnitType
45- //
46-
4747bool LRCWriter::supportsUnitType (UnitType ut) const { return ut == UnitType::PER_PART; }
4848
49- //
50- // LRCWriter::write
51- //
52-
5349muse::Ret LRCWriter::write (notation::INotationPtr notation, muse::io::IODevice& device, const Options&)
5450{
5551 Score* score = notation->elements ()->msScore ();
5652 bool enhancedLrc = configuration ()->lrcUseEnhancedFormat ();
5753
58- return write (score, &device, enhancedLrc);
54+ return doWrite (score, &device, enhancedLrc);
5955}
6056
61- //
62- // LRCWriter::exportLrc
63- //
64-
65- bool LRCWriter::exportLrc (mu::engraving::Score* score, muse::io::IODevice* device, bool enhancedLrc)
66- {
67- write (score, device, enhancedLrc);
68- return true ;
69- }
70-
71- //
72- // LRCWriter::writeScore
73- //
74-
7557bool LRCWriter::writeScore (mu::engraving::Score* score, const muse::io::path_t & path, bool enhancedLrc)
7658{
7759 File f (path);
7860 if (!f.open (IODevice::WriteOnly)) {
7961 return false ;
8062 }
8163
82- bool res = exportLrc (score, &f, enhancedLrc) && !f.hasError ();
64+ bool res = doWrite (score, &f, enhancedLrc) && !f.hasError ();
8365 f.close ();
8466
8567 return res;
8668}
8769
88- //
89- // LRCWriter::writeList
90- //
91-
9270muse::Ret LRCWriter::writeList (const notation::INotationPtrList&, muse::io::IODevice&, const Options&)
9371{
94- return muse::Ret (muse:: Ret::Code::NotSupported);
72+ return make_ret ( Ret::Code::NotSupported);
9573}
9674
97- //
98- // LRCWriter::writeMetadata
99- //
100-
10175void LRCWriter::writeMetadata (muse::io::IODevice* device, const engraving::Score* score) const
10276{
10377 QString metadata;
10478
10579 // Title
106- QString title = QString::fromStdString ( score->metaTag (muse::String ( " workTitle" )). toStdString () );
80+ const QString title = score->metaTag (u " workTitle" ). toQString ( );
10781 if (!title.isEmpty ()) {
108- metadata += QString ( " [ti:%1]\n " ) .arg (title);
82+ metadata += u " [ti:%1]\n " _s .arg (title);
10983 }
11084
11185 // Composer/Artist
112- QString artist = QString::fromStdString ( score->metaTag (muse::String ( " composer" )). toStdString () );
86+ const QString artist = score->metaTag (u " composer" ). toQString ( );
11387 if (!artist.isEmpty ()) {
114- metadata += QString ( " [ar:%1]\n " ) .arg (artist);
88+ metadata += u " [ar:%1]\n " _s .arg (artist);
11589 }
11690
11791 if (!metadata.isEmpty ()) {
11892 device->write (metadata.toUtf8 ());
11993 }
12094}
12195
122- //
123- // LRCWriter::write
124- //
125-
126- muse::Ret LRCWriter::write (mu::engraving::Score* score, muse::io::IODevice* device, bool enhancedLrc)
96+ muse::Ret LRCWriter::doWrite (mu::engraving::Score* score, muse::io::IODevice* device, bool enhancedLrc)
12797{
12898 writeMetadata (device, score);
12999
130100 const auto lyrics = collectLyrics (score);
131101
132102 // Write lyrics
133- for (auto it = lyrics. constBegin (); it != lyrics. constEnd (); ++it ) {
103+ for (const auto & [timestamp, text] : lyrics) {
134104 if (enhancedLrc) {
135105 // As there should only be words we replace spaces by "-"
136- QString lyricsText = it. value () ;
106+ QString lyricsText = text ;
137107 lyricsText.replace (QRegularExpression (" \\ s" ), " -" );
138- lyricsText.replace (QChar ( 0x00A0 ), QChar ( ' -' ) );
108+ lyricsText.replace (u ' \u00A0 ' , u ' -' );
139109
140- device->write (QString (" [%1] <%1> %2\n " ).arg (formatTimestamp (it. key () ), lyricsText).toUtf8 ());
110+ device->write (QString (" [%1] <%1> %2\n " ).arg (formatTimestamp (timestamp ), lyricsText).toUtf8 ());
141111 } else {
142- device->write (QString (" [%1]%2\n " ).arg (formatTimestamp (it. key ()), it. value () ).toUtf8 ());
112+ device->write (QString (" [%1]%2\n " ).arg (formatTimestamp (timestamp), text ).toUtf8 ());
143113 }
144114 }
145115
146- return muse::Ret (muse::Ret::Code::Ok );
116+ return make_ok ( );
147117}
148118
149- //
150- // LRCWriter::collectLyrics
151- //
152-
153- QMap<qreal, QString> LRCWriter::collectLyrics (const mu::engraving::Score* score)
119+ std::map<double , QString> LRCWriter::collectLyrics (const mu::engraving::Score* score)
154120{
155- QMap<qreal , QString> lyrics;
121+ std::map< double , QString> lyrics;
156122 const RepeatList& repeats = score->repeatList ();
157123
158124 staff_idx_t lyricsStaff;
@@ -169,25 +135,25 @@ QMap<qreal, QString> LRCWriter::collectLyrics(const mu::engraving::Score* score)
169135 continue ;
170136 }
171137
172- for (Segment* seg = toMeasure (mb)->first (); seg; seg = seg->next ()) {
138+ for (const Segment* seg = toMeasure (mb)->first (); seg; seg = seg->next ()) {
173139 if (!seg->isChordRestType ()) {
174140 continue ;
175141 }
176142
177- for (EngravingItem* e : seg->elist ()) {
143+ for (const EngravingItem* e : seg->elist ()) {
178144 if (!e || !e->isChordRest ()) {
179145 continue ;
180146 }
181147
182- for (Lyrics* l : toChordRest (e)->lyrics ()) {
148+ for (const Lyrics* l : toChordRest (e)->lyrics ()) {
183149 // if (l->text().empty())
184150 if (l->plainText ().isEmpty ()) {
185151 continue ;
186152 }
187153
188154 if ((lyricsStaff == e->staffIdx ()) && (lyricsVoice == e->voice ()) && (lyricNumber == l->subtype ())) {
189- const qreal time = score->utick2utime (l->tick ().ticks () + tickOffset) * 1000 ;
190- lyrics.insert (time, l->plainText ());
155+ const double time = score->utick2utime (l->tick ().ticks () + tickOffset) * 1000 ;
156+ lyrics.insert_or_assign (time, l->plainText ());
191157 }
192158 }
193159 }
@@ -197,23 +163,15 @@ QMap<qreal, QString> LRCWriter::collectLyrics(const mu::engraving::Score* score)
197163 return lyrics;
198164}
199165
200- //
201- // LRCWriter::formatTimestamp
202- //
203-
204- QString LRCWriter::formatTimestamp (qreal ms) const
166+ QString LRCWriter::formatTimestamp (double ms) const
205167{
206168 const int totalSec = static_cast <int >(ms / 1000 );
207- return QString ( " %1:%2.%3" )
169+ return u " %1:%2.%3" _s
208170 .arg (totalSec / 60 , 2 , 10 , QLatin1Char (' 0' ))
209171 .arg (totalSec % 60 , 2 , 10 , QLatin1Char (' 0' ))
210172 .arg (static_cast <int >(ms) % 1000 / 10 , 2 , 10 , QLatin1Char (' 0' ));
211173}
212174
213- //
214- // LRCWriter::findStaffVoiceAndLyricToExport
215- //
216-
217175void LRCWriter::findStaffVoiceAndLyricToExport (const mu::engraving::Score* score, mu::engraving::staff_idx_t & staff,
218176 mu::engraving::voice_idx_t & voice, int & lyricNumber)
219177{
@@ -225,46 +183,45 @@ void LRCWriter::findStaffVoiceAndLyricToExport(const mu::engraving::Score* score
225183 const RepeatList& repeats = score->repeatList ();
226184
227185 for (const RepeatSegment* rs : repeats) {
228- const int tickOffset = rs->utick - rs->tick ;
229-
230186 for (const MeasureBase* mb = rs->firstMeasure (); mb; mb = mb->next ()) {
231187 if (!mb->isMeasure ()) {
232188 continue ;
233189 }
234190
235- for (Segment* seg = toMeasure (mb)->first (); seg; seg = seg->next ()) {
191+ for (const Segment* seg = toMeasure (mb)->first (); seg; seg = seg->next ()) {
236192 if (!seg->isChordRestType ()) {
237193 continue ;
238194 }
239195
240- for (EngravingItem* e : seg->elist ()) {
196+ for (const EngravingItem* e : seg->elist ()) {
241197 if (!e || !e->isChordRest ()) {
242198 continue ;
243199 }
244200
245- for (Lyrics* l : toChordRest (e)->lyrics ()) {
201+ for (const Lyrics* l : toChordRest (e)->lyrics ()) {
246202 // if (l->text().empty())
247203 if (l->plainText ().isEmpty ()) {
248204 continue ;
249- } else {
250- if (!lyricsFound) {
251- lyricsFound = true ;
252- staff = e->staffIdx ();
205+ }
206+
207+ if (!lyricsFound) {
208+ lyricsFound = true ;
209+ staff = e->staffIdx ();
210+ voice = e->voice ();
211+ lyricNumber = l->subtype ();
212+ continue ;
213+ }
214+
215+ if (staff > e->staffIdx ()) {
216+ staff = e->staffIdx ();
217+ voice = e->voice ();
218+ lyricNumber = l->subtype ();
219+ } else if (staff == e->staffIdx ()) {
220+ if (voice > e->voice ()) {
253221 voice = e->voice ();
254222 lyricNumber = l->subtype ();
255- } else {
256- if (staff > e->staffIdx ()) {
257- staff = e->staffIdx ();
258- voice = e->voice ();
259- lyricNumber = l->subtype ();
260- } else if (staff == e->staffIdx ()) {
261- if (voice > e->voice ()) {
262- voice = e->voice ();
263- lyricNumber = l->subtype ();
264- } else if (voice == e->voice ()) {
265- lyricNumber = min (lyricNumber, l->subtype ());
266- }
267- }
223+ } else if (voice == e->voice ()) {
224+ lyricNumber = std::min (lyricNumber, l->subtype ());
268225 }
269226 }
270227 }
@@ -273,4 +230,3 @@ void LRCWriter::findStaffVoiceAndLyricToExport(const mu::engraving::Score* score
273230 }
274231 }
275232}
276- } // namespace mu::iex::lrcexport
0 commit comments