Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore-Studio-CLA-applies
4 : *
5 : * MuseScore Studio
6 : * Music Composition & Notation
7 : *
8 : * Copyright (C) 2021 MuseScore Limited and others
9 : *
10 : * This program is free software: you can redistribute it and/or modify
11 : * it under the terms of the GNU General Public License version 3 as
12 : * published by the Free Software Foundation.
13 : *
14 : * This program is distributed in the hope that it will be useful,
15 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : * GNU General Public License for more details.
18 : *
19 : * 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/>.
21 : */
22 :
23 : #include "positionswriter.h"
24 :
25 : #include <cmath>
26 : #include <QBuffer>
27 :
28 : #include "global/io/buffer.h"
29 : #include "global/serialization/xmlstreamwriter.h"
30 :
31 : #include "engraving/dom/masterscore.h"
32 : #include "engraving/dom/repeatlist.h"
33 : #include "engraving/dom/system.h"
34 :
35 : #include "engraving/types/types.h"
36 :
37 : #include "inotation.h"
38 : #include "inotationelements.h" // IWYU pragma: keep
39 :
40 : #include "log.h"
41 :
42 : using namespace mu::project;
43 : using namespace mu::notation;
44 : using namespace mu::engraving;
45 : using namespace muse;
46 : using namespace muse::io;
47 :
48 : static constexpr std::string_view SCORE_TAG("score");
49 : static constexpr std::string_view ELEMENTS_TAG("elements");
50 : static constexpr std::string_view ELEMENT_TAG("element");
51 : static constexpr std::string_view EVENTS_TAG("events");
52 : static constexpr std::string_view EVENT_TAG("event");
53 :
54 0 : static void writeElementPosition(XmlStreamWriter& writer, const std::string& id, const muse::PointF& pos, const muse::PointF& sPos,
55 : page_idx_t pageIndex)
56 : {
57 0 : XmlStreamWriter::Attributes attributes;
58 0 : attributes.emplace_back("id", id);
59 0 : attributes.emplace_back("x", pos.x());
60 0 : attributes.emplace_back("y", pos.y());
61 0 : attributes.emplace_back("sx", sPos.x());
62 0 : attributes.emplace_back("sy", sPos.y());
63 0 : attributes.emplace_back("page", pageIndex);
64 :
65 0 : writer.startElement(ELEMENT_TAG, attributes);
66 0 : writer.endElement();
67 0 : }
68 :
69 0 : static void writeEventPosition(XmlStreamWriter& writer, const std::string& id, int time)
70 : {
71 0 : XmlStreamWriter::Attributes attributes;
72 0 : attributes.emplace_back("elid", id);
73 0 : attributes.emplace_back("position", time);
74 :
75 0 : writer.startElement(EVENT_TAG, attributes);
76 0 : writer.endElement();
77 0 : }
78 :
79 0 : static void writeMeasureEvents(XmlStreamWriter& writer, Measure* m, int offset, const QHash<void*, int>& segments)
80 : {
81 0 : for (mu::engraving::Segment* s = m->first(mu::engraving::SegmentType::ChordRest); s;
82 0 : s = s->next(mu::engraving::SegmentType::ChordRest)) {
83 0 : int tick = s->tick().ticks() + offset;
84 0 : int id = segments[(void*)s];
85 0 : int time = lrint(m->score()->utick2utime(tick) * 1000);
86 :
87 0 : writeEventPosition(writer, std::to_string(id), time);
88 : }
89 0 : }
90 :
91 0 : PositionsWriter::PositionsWriter(PositionsWriter::ElementType elementType)
92 0 : : m_elementType(elementType)
93 : {
94 0 : }
95 :
96 0 : std::vector<INotationWriter::UnitType> PositionsWriter::supportedUnitTypes() const
97 : {
98 0 : return { UnitType::PER_PART };
99 : }
100 :
101 0 : bool PositionsWriter::supportsUnitType(UnitType unitType) const
102 : {
103 0 : std::vector<UnitType> unitTypes = supportedUnitTypes();
104 0 : return std::find(unitTypes.cbegin(), unitTypes.cend(), unitType) != unitTypes.cend();
105 0 : }
106 :
107 0 : Ret PositionsWriter::write(INotationPtr notation, io::IODevice& destinationDevice, const Options&)
108 : {
109 0 : IF_ASSERT_FAILED(notation) {
110 0 : return make_ret(Ret::Code::UnknownError);
111 : }
112 :
113 0 : mu::engraving::Score* score = notation->elements()->msScore();
114 :
115 0 : IF_ASSERT_FAILED(score) {
116 0 : return make_ret(Ret::Code::UnknownError);
117 : }
118 :
119 0 : auto buf = Buffer::opened(IODevice::WriteOnly);
120 0 : XmlStreamWriter writer(&buf);
121 :
122 0 : writer.startDocument();
123 0 : writer.startElement(SCORE_TAG);
124 :
125 0 : writeElementsPositions(writer, score);
126 0 : writeEventsPositions(writer, score);
127 :
128 0 : writer.endElement();
129 0 : writer.flush();
130 :
131 0 : destinationDevice.write(buf.data());
132 :
133 0 : return true;
134 0 : }
135 :
136 0 : Ret PositionsWriter::writeList(const INotationPtrList&, io::IODevice&, const Options&)
137 : {
138 0 : NOT_SUPPORTED;
139 0 : return Ret(Ret::Code::NotSupported);
140 : }
141 :
142 0 : qreal PositionsWriter::pngDpiResolution() const
143 : {
144 0 : return (imagesExportConfiguration()->exportPngDpiResolution() / mu::engraving::DPI) * 12.0;
145 : }
146 :
147 0 : QHash<void*, int> PositionsWriter::elementIds(const mu::engraving::Score* score) const
148 : {
149 0 : QHash<void*, int> elementIds;
150 :
151 0 : int id = 0;
152 0 : if (m_elementType == ElementType::SEGMENT) {
153 0 : Measure* m = score->firstMeasureMM();
154 0 : for (mu::engraving::Segment* s = (m ? m->first(mu::engraving::SegmentType::ChordRest) : nullptr);
155 0 : s; s = s->next1MM(mu::engraving::SegmentType::ChordRest)) {
156 0 : elementIds[(void*)s] = id++;
157 : }
158 : } else {
159 0 : for (Measure* m = score->firstMeasureMM(); m; m = m->nextMeasureMM()) {
160 0 : elementIds[(void*)m] = id++;
161 : }
162 : }
163 :
164 0 : return elementIds;
165 0 : }
166 :
167 0 : void PositionsWriter::writeElementsPositions(XmlStreamWriter& writer, const mu::engraving::Score* score) const
168 : {
169 0 : writer.startElement(ELEMENTS_TAG);
170 :
171 0 : switch (m_elementType) {
172 0 : case ElementType::SEGMENT:
173 0 : writeSegmentsPositions(writer, score);
174 0 : break;
175 0 : case ElementType::MEASURE:
176 0 : writeMeasuresPositions(writer, score);
177 0 : break;
178 : }
179 :
180 0 : writer.endElement();
181 0 : }
182 :
183 0 : void PositionsWriter::writeSegmentsPositions(XmlStreamWriter& writer, const mu::engraving::Score* score) const
184 : {
185 0 : int id = 0;
186 0 : qreal ndpi = pngDpiResolution();
187 :
188 0 : Measure* measure = score->firstMeasureMM();
189 0 : for (mu::engraving::Segment* segment = (measure ? measure->first(mu::engraving::SegmentType::ChordRest) : nullptr);
190 0 : segment; segment = segment->next1MM(mu::engraving::SegmentType::ChordRest)) {
191 0 : qreal sx = 0;
192 0 : size_t tracks = score->nstaves() * mu::engraving::VOICES;
193 0 : for (size_t track = 0; track < tracks; track++) {
194 0 : EngravingItem* e = segment->element(static_cast<int>(track));
195 0 : if (e) {
196 0 : sx = qMax(sx, e->width());
197 : }
198 : }
199 :
200 0 : sx *= ndpi;
201 0 : qreal sy = segment->measure()->system()->height() * ndpi;
202 :
203 0 : int x = segment->pagePos().x() * ndpi;
204 0 : int y = segment->pagePos().y() * ndpi;
205 :
206 0 : Page* page = segment->measure()->system()->page();
207 0 : page_idx_t pageIndex = score->pageIdx(page);
208 :
209 0 : writeElementPosition(writer, std::to_string(id), PointF(x, y), PointF(sx, sy), pageIndex);
210 :
211 0 : id++;
212 : }
213 0 : }
214 :
215 0 : void PositionsWriter::writeMeasuresPositions(XmlStreamWriter& writer, const mu::engraving::Score* score) const
216 : {
217 0 : int id = 0;
218 0 : qreal ndpi = pngDpiResolution();
219 :
220 0 : for (Measure* measure = score->firstMeasureMM(); measure; measure = measure->nextMeasureMM()) {
221 0 : qreal sx = measure->ldata()->bbox().width() * ndpi;
222 0 : qreal sy = measure->system()->height() * ndpi;
223 0 : qreal x = measure->pagePos().x() * ndpi;
224 0 : qreal y = measure->system()->pagePos().y() * ndpi;
225 :
226 0 : Page* page = measure->system()->page();
227 0 : page_idx_t pageIndex = score->pageIdx(page);
228 :
229 0 : writeElementPosition(writer, std::to_string(id), PointF(x, y), PointF(sx, sy), pageIndex);
230 :
231 0 : id++;
232 : }
233 0 : }
234 :
235 0 : void PositionsWriter::writeEventsPositions(XmlStreamWriter& writer, const mu::engraving::Score* score) const
236 : {
237 0 : QHash<void*, int> elementIds = this->elementIds(score);
238 :
239 0 : writer.startElement(EVENTS_TAG);
240 :
241 0 : for (const mu::engraving::RepeatSegment* repeatSegment : score->expandedRepeatList()) {
242 0 : int startTick = repeatSegment->tick;
243 0 : int endTick = repeatSegment->endTick();
244 0 : int tickOffset = repeatSegment->utick - repeatSegment->tick;
245 0 : for (Measure* measure = score->tick2measureMM(Fraction::fromTicks(startTick)); measure; measure = measure->nextMeasureMM()) {
246 0 : if (m_elementType == ElementType::SEGMENT) {
247 0 : writeMeasureEvents(writer, measure, tickOffset, elementIds);
248 : } else {
249 0 : int tick = measure->tick().ticks() + tickOffset;
250 0 : int id = elementIds[(void*)measure];
251 0 : int time = std::lrint(measure->score()->utick2utime(tick) * 1000);
252 :
253 0 : writeEventPosition(writer, std::to_string(id), time);
254 : }
255 :
256 0 : if (measure->endTick().ticks() >= endTick) {
257 0 : break;
258 : }
259 : }
260 : }
261 :
262 0 : writer.endElement();
263 0 : }
|