Skip to content

Commit 1d4cca9

Browse files
committed
Implement smart voice selection for implode
1 parent 5ab5e82 commit 1d4cca9

File tree

3 files changed

+167
-67
lines changed

3 files changed

+167
-67
lines changed

src/engraving/editing/cmd.cpp

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4172,6 +4172,20 @@ bool Score::cmdImplode()
41724172
// if single staff selected, combine voices
41734173
// otherwise combine staves
41744174
if (dstStaff == endStaff - 1) {
4175+
// find first voice with actual notes to use as destination
4176+
track_idx_t actualDstTrack = dstTrack;
4177+
for (voice_idx_t v = 0; v < VOICES; ++v) {
4178+
track_idx_t testTrack = dstStaff * VOICES + v;
4179+
for (Measure* m = startMeasure; m && m->tick() < endTick; m = m->nextMeasure()) {
4180+
if (m->hasVoice(testTrack) && !m->isOnlyRests(testTrack)) {
4181+
actualDstTrack = testTrack;
4182+
goto found_voice;
4183+
}
4184+
}
4185+
}
4186+
found_voice:
4187+
dstTrack = actualDstTrack;
4188+
41754189
// loop through segments adding notes to chord on top staff
41764190
for (Segment* s = startSegment; s && s != endSegment; s = s->next1()) {
41774191
if (!s->isChordRestType()) {
@@ -4190,7 +4204,10 @@ bool Score::cmdImplode()
41904204
}
41914205
// loop through each subsequent staff (or track within staff)
41924206
// looking for notes to add
4193-
for (track_idx_t srcTrack = startTrack + 1; srcTrack < endTrack; srcTrack++) {
4207+
for (track_idx_t srcTrack = startTrack; srcTrack < endTrack; srcTrack++) {
4208+
if (srcTrack == dstTrack) {
4209+
continue;
4210+
}
41944211
EngravingItem* src = s->element(srcTrack);
41954212
if (src && src->isChord()) {
41964213
Chord* srcChord = toChord(src);
@@ -4231,22 +4248,66 @@ bool Score::cmdImplode()
42314248
undoRemoveElement(src);
42324249
}
42334250
}
4234-
}
4235-
// TODO - use first voice that actually has a note and implode remaining voices on it?
4236-
// see https://musescore.org/en/node/174111
4237-
else if (dst) {
4238-
// destination track has something, but it isn't a chord
4239-
// remove rests from other voices if in "voice mode"
4240-
for (voice_idx_t i = 1; i < VOICES; ++i) {
4241-
EngravingItem* e = s->element(dstTrack + i);
4242-
if (e && e->isRest()) {
4243-
undoRemoveElement(e);
4251+
} else {
4252+
// destination track has rest or nothing - find first chord in other voices and move it to dstTrack
4253+
Chord* firstChord = nullptr;
4254+
track_idx_t firstChordTrack = muse::nidx;
4255+
for (track_idx_t track = startTrack; track < endTrack; track++) {
4256+
EngravingItem* e = s->element(track);
4257+
if (e && e->isChord()) {
4258+
firstChord = toChord(e);
4259+
firstChordTrack = track;
4260+
break;
4261+
}
4262+
}
4263+
4264+
if (firstChord) {
4265+
// move first chord to dstTrack and merge others into it
4266+
if (firstChordTrack != dstTrack) {
4267+
firstChord->undoChangeProperty(Pid::TRACK, dstTrack);
4268+
}
4269+
4270+
for (track_idx_t srcTrack = startTrack; srcTrack < endTrack; srcTrack++) {
4271+
if (srcTrack == firstChordTrack) {
4272+
continue;
4273+
}
4274+
EngravingItem* src = s->element(srcTrack);
4275+
if (src && src->isChord()) {
4276+
Chord* srcChord = toChord(src);
4277+
if (srcChord->ticks() != firstChord->ticks()) {
4278+
continue;
4279+
}
4280+
for (Note* n : srcChord->notes()) {
4281+
NoteVal nv(n->pitch());
4282+
nv.tpc1 = n->tpc1();
4283+
if (firstChord->findNote(nv.pitch)) {
4284+
continue;
4285+
}
4286+
bool forceAccidental = n->accidental() && n->accidental()->role() == AccidentalRole::USER;
4287+
addNote(firstChord, nv, forceAccidental);
4288+
}
4289+
}
4290+
if (src && src->voice()) {
4291+
undoRemoveElement(src);
4292+
}
4293+
}
4294+
4295+
// remove rest from dstTrack if present
4296+
if (dst && dst->isRest()) {
4297+
undoRemoveElement(dst);
42444298
}
42454299
}
42464300
}
42474301
}
42484302
// delete orphaned spanners (TODO: figure out solution to reconnect orphaned spanners to their cloned notes)
42494303
checkSpanner(startTick, endTick);
4304+
4305+
// if destination is not voice 1, swap it to voice 1
4306+
if (dstTrack != startTrack) {
4307+
for (Measure* m = startMeasure; m && m->tick() < endTick; m = m->nextMeasure()) {
4308+
undo(new ExchangeVoice(m, startTrack, dstTrack, dstStaff));
4309+
}
4310+
}
42504311
} else {
42514312
track_idx_t tracks[VOICES];
42524313
for (voice_idx_t i = 0; i < VOICES; i++) {

src/engraving/tests/implode_explode_data/undoImplodeVoice01-ref.mscx

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -269,12 +269,15 @@
269269
</Chord>
270270
<Chord>
271271
<eid>o_o</eid>
272+
<track>0</track>
272273
<durationType>quarter</durationType>
273274
<Note>
274275
<eid>p_p</eid>
276+
<track>0</track>
275277
<Accidental>
276278
<subtype>accidentalFlat</subtype>
277279
<eid>q_q</eid>
280+
<track>0</track>
278281
</Accidental>
279282
<pitch>68</pitch>
280283
<tpc>10</tpc>
@@ -320,39 +323,75 @@
320323
<durationType>half</durationType>
321324
</Rest>
322325
</voice>
326+
<voice>
327+
<location>
328+
<fractions>1/2</fractions>
329+
</location>
330+
<Rest>
331+
<eid>w_w</eid>
332+
<durationType>half</durationType>
333+
</Rest>
334+
</voice>
335+
<voice>
336+
<location>
337+
<fractions>1/2</fractions>
338+
</location>
339+
<Rest>
340+
<eid>x_x</eid>
341+
<durationType>half</durationType>
342+
</Rest>
343+
</voice>
323344
</Measure>
324345
<Measure>
325-
<eid>w_w</eid>
346+
<eid>y_y</eid>
326347
<voice>
327348
<Chord>
328-
<eid>x_x</eid>
349+
<eid>z_z</eid>
329350
<durationType>half</durationType>
330351
<Note>
331-
<eid>y_y</eid>
352+
<eid>0_0</eid>
332353
<pitch>64</pitch>
333354
<tpc>18</tpc>
334355
</Note>
335356
<Note>
336-
<eid>z_z</eid>
357+
<eid>1_1</eid>
337358
<pitch>67</pitch>
338359
<tpc>15</tpc>
339360
</Note>
340361
<Note>
341-
<eid>0_0</eid>
362+
<eid>2_2</eid>
342363
<pitch>72</pitch>
343364
<tpc>14</tpc>
344365
</Note>
345366
</Chord>
346367
<Rest>
347-
<eid>1_1</eid>
368+
<eid>3_3</eid>
348369
<durationType>half</durationType>
349370
</Rest>
350371
<BarLine>
351372
<subtype>end</subtype>
352373
<span>1</span>
353-
<eid>2_2</eid>
374+
<eid>4_4</eid>
354375
</BarLine>
355376
</voice>
377+
<voice>
378+
<location>
379+
<fractions>1/2</fractions>
380+
</location>
381+
<Rest>
382+
<eid>5_5</eid>
383+
<durationType>half</durationType>
384+
</Rest>
385+
</voice>
386+
<voice>
387+
<location>
388+
<fractions>1/2</fractions>
389+
</location>
390+
<Rest>
391+
<eid>6_6</eid>
392+
<durationType>half</durationType>
393+
</Rest>
394+
</voice>
356395
</Measure>
357396
</Staff>
358397
</Score>

0 commit comments

Comments
 (0)