2020 * along with this program. If not, see <https://www.gnu.org/licenses/>.
2121 */
2222
23- #include " iex_lyricsexport .h"
23+ #include " io/file .h"
2424
2525#include < QBuffer>
2626
2727#include " engraving/dom/masterscore.h"
2828#include " engraving/dom/repeatlist.h"
2929#include " engraving/dom/lyrics.h"
3030
31+ #include " iex_lyricsexport.h"
32+
3133using namespace mu ::engraving;
34+ using namespace muse ::io;
3235
3336namespace mu ::iex::lrcexport {
3437// Interface implementation
@@ -37,43 +40,65 @@ std::vector<project::INotationWriter::UnitType> LRCWriter::supportedUnitTypes()
3740 return { UnitType::PER_PART };
3841}
3942
43+ //
44+ // LRCWriter::supportsUnitType
45+ //
46+
4047bool LRCWriter::supportsUnitType (UnitType ut) const { return ut == UnitType::PER_PART; }
4148
49+ //
50+ // LRCWriter::write
51+ //
52+
4253muse::Ret LRCWriter::write (notation::INotationPtr notation, muse::io::IODevice& device, const Options&)
4354{
4455 Score* score = notation->elements ()->msScore ();
45- QByteArray data;
46- QBuffer buffer (&data);
47- bool enhancedFormat = configuration ()->lrcUseEnhancedFormat ();
56+ bool enhancedLrc = configuration ()->lrcUseEnhancedFormat ();
4857
49- /* **********
50- *
51- * PENDING....
52- Is there any advantage to writing to a buffer first, and then writing that buffer to the device? It would seem more efficient to me to write to the device directly.
53-
54- ******/
58+ return write (score, &device, enhancedLrc);
59+ }
5560
56- buffer.open (QIODevice::WriteOnly);
61+ //
62+ // LRCWriter::exportLrc
63+ //
5764
58- writeMetadata (buffer, score);
65+ bool LRCWriter::exportLrc (mu::engraving::Score* score, muse::io::IODevice* device, bool enhancedLrc)
66+ {
67+ write (score, device, enhancedLrc);
68+ return true ;
69+ }
5970
60- const auto lyrics = collectLyrics (score);
71+ //
72+ // LRCWriter::writeScore
73+ //
6174
62- // Write lyrics
63- for (auto it = lyrics.constBegin (); it != lyrics.constEnd (); ++it) {
64- buffer.write (QString (" [%1]%2\n " ).arg (formatTimestamp (it.key ()), it.value ()).toUtf8 ());
75+ bool LRCWriter::writeScore (mu::engraving::Score* score, const muse::io::path_t & path, bool enhancedLrc)
76+ {
77+ File f (path);
78+ if (!f.open (IODevice::WriteOnly)) {
79+ return false ;
6580 }
6681
67- device.write (data);
68- return muse::Ret (muse::Ret::Code::Ok);
82+ bool res = exportLrc (score, &f, enhancedLrc) && !f.hasError ();
83+ f.close ();
84+
85+ return res;
6986}
7087
88+ //
89+ // LRCWriter::writeList
90+ //
91+
7192muse::Ret LRCWriter::writeList (const notation::INotationPtrList&, muse::io::IODevice&, const Options&)
7293{
7394 return muse::Ret (muse::Ret::Code::NotSupported);
7495}
7596
76- void LRCWriter::writeMetadata (QIODevice& device, const engraving::Score* score) const
97+ //
98+ // LRCWriter::writeMetadata
99+ //
100+
101+ void LRCWriter::writeMetadata (muse::io::IODevice* device, const engraving::Score* score) const
77102{
78103 QString metadata;
79104
@@ -90,69 +115,80 @@ void LRCWriter::writeMetadata(QIODevice& device, const engraving::Score* score)
90115 }
91116
92117 if (!metadata.isEmpty ()) {
93- device. write (metadata.toUtf8 ());
118+ device-> write (metadata.toUtf8 ());
94119 }
95120}
96121
97- // Core lyric collection (simplified)
98- QMap<qreal, QString> LRCWriter::collectLyrics (const Score* score) const
122+ //
123+ // LRCWriter::write
124+ //
125+
126+ muse::Ret LRCWriter::write (mu::engraving::Score* score, muse::io::IODevice* device, bool enhancedLrc)
99127{
100- QMap<qreal, QString> lyrics;
101- const RepeatList& repeats = score->repeatList ();
128+ writeMetadata (device, score);
102129
103- LOGI () << " tpacebes " ;
130+ const auto lyrics = collectLyrics (score) ;
104131
105- int paabRepeatSegment = 0 ;
132+ // Write lyrics
133+ for (auto it = lyrics.constBegin (); it != lyrics.constEnd (); ++it) {
134+ if (enhancedLrc) {
135+ // As there should only be words we replace spaces by "-"
136+ QString lyricsText = it.value ();
137+ lyricsText.replace (QRegularExpression (" \\ s" ), " -" );
138+ lyricsText.replace (QChar (0x00A0 ), QChar (' -' ));
139+
140+ device->write (QString (" [%1] <%1> %2\n " ).arg (formatTimestamp (it.key ()), lyricsText).toUtf8 ());
141+ } else {
142+ device->write (QString (" [%1]%2\n " ).arg (formatTimestamp (it.key ()), it.value ()).toUtf8 ());
143+ }
144+ }
106145
107- for ( const RepeatSegment* rs : repeats) {
108- const int tickOffset = rs-> utick - rs-> tick ;
146+ return muse::Ret (muse::Ret::Code::Ok);
147+ }
109148
110- ++paabRepeatSegment;
149+ //
150+ // LRCWriter::collectLyrics
151+ //
111152
112- LOGI () << " tpacebes repeat segment " << paabRepeatSegment;
153+ QMap<qreal, QString> LRCWriter::collectLyrics (const mu::engraving::Score* score)
154+ {
155+ QMap<qreal, QString> lyrics;
156+ const RepeatList& repeats = score->repeatList ();
113157
114- int paabMeasureBase = 0 ;
158+ staff_idx_t lyricsStaff;
159+ voice_idx_t lyricsVoice;
160+ int lyricNumber;
115161
116- for ( const MeasureBase* mb = rs-> firstMeasure (); mb; mb = mb-> next ()) {
162+ findStaffVoiceAndLyricToExport (score, lyricsStaff, lyricsVoice, lyricNumber);
117163
118- ++paabMeasureBase;
164+ for (const RepeatSegment* rs : repeats) {
165+ const int tickOffset = rs->utick - rs->tick ;
119166
167+ for (const MeasureBase* mb = rs->firstMeasure (); mb; mb = mb->next ()) {
120168 if (!mb->isMeasure ()) {
121169 continue ;
122170 }
123171
124- LOGI () << " tpacebes measure base " << paabMeasureBase;
125-
126- int paabSegment = 0 ;
127172 for (Segment* seg = toMeasure (mb)->first (); seg; seg = seg->next ()) {
128- ++paabSegment;
129173 if (!seg->isChordRestType ()) {
130174 continue ;
131175 }
132176
133- LOGI () << " tpacebes paabSegment " << paabSegment;
134-
135- int paabEngravingItem = 0 ;
136177 for (EngravingItem* e : seg->elist ()) {
137- ++paabEngravingItem;
138178 if (!e || !e->isChordRest ()) {
139179 continue ;
140180 }
141181
142- LOGI () << " tpacebes paabEngravingItem " << paabEngravingItem;
143-
144- int paabLyrics = 0 ;
145182 for (Lyrics* l : toChordRest (e)->lyrics ()) {
146- ++paabLyrics;
147183 // if (l->text().empty())
148184 if (l->plainText ().isEmpty ()) {
149185 continue ;
150186 }
151- LOGI () << " tpacebes paabLyrics " << paabLyrics;
152187
153- const qreal time = score->utick2utime (l->tick ().ticks () + tickOffset) * 1000 ;
154- lyrics.insert (time, l->plainText ());
155- LOGI () << " tpacebes insertamos Time " << time << " ==>" << l->plainText () << " <==" ;
188+ 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 ());
191+ }
156192 }
157193 }
158194 }
@@ -161,6 +197,10 @@ QMap<qreal, QString> LRCWriter::collectLyrics(const Score* score) const
161197 return lyrics;
162198}
163199
200+ //
201+ // LRCWriter::formatTimestamp
202+ //
203+
164204QString LRCWriter::formatTimestamp (qreal ms) const
165205{
166206 const int totalSec = static_cast <int >(ms / 1000 );
@@ -169,4 +209,68 @@ QString LRCWriter::formatTimestamp(qreal ms) const
169209 .arg (totalSec % 60 , 2 , 10 , QLatin1Char (' 0' ))
170210 .arg (static_cast <int >(ms) % 1000 / 10 , 2 , 10 , QLatin1Char (' 0' ));
171211}
212+
213+ //
214+ // LRCWriter::findStaffVoiceAndLyricToExport
215+ //
216+
217+ void LRCWriter::findStaffVoiceAndLyricToExport (const mu::engraving::Score* score, mu::engraving::staff_idx_t & staff,
218+ mu::engraving::voice_idx_t & voice, int & lyricNumber)
219+ {
220+ bool lyricsFound = false ;
221+ staff = 0 ;
222+ voice = 0 ;
223+ lyricNumber = 0 ;
224+
225+ const RepeatList& repeats = score->repeatList ();
226+
227+ for (const RepeatSegment* rs : repeats) {
228+ const int tickOffset = rs->utick - rs->tick ;
229+
230+ for (const MeasureBase* mb = rs->firstMeasure (); mb; mb = mb->next ()) {
231+ if (!mb->isMeasure ()) {
232+ continue ;
233+ }
234+
235+ for (Segment* seg = toMeasure (mb)->first (); seg; seg = seg->next ()) {
236+ if (!seg->isChordRestType ()) {
237+ continue ;
238+ }
239+
240+ for (EngravingItem* e : seg->elist ()) {
241+ if (!e || !e->isChordRest ()) {
242+ continue ;
243+ }
244+
245+ for (Lyrics* l : toChordRest (e)->lyrics ()) {
246+ // if (l->text().empty())
247+ if (l->plainText ().isEmpty ()) {
248+ continue ;
249+ } else {
250+ if (!lyricsFound) {
251+ lyricsFound = true ;
252+ staff = e->staffIdx ();
253+ voice = e->voice ();
254+ 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+ }
268+ }
269+ }
270+ }
271+ }
272+ }
273+ }
274+ }
275+ }
172276} // namespace mu::iex::lrcexport
0 commit comments