1 //=========================================================
2 //  MusE
3 //  Linux Music Editor
4 //    operations.cpp
5 //  (C) Copyright 2014, 2016 Tim E. Real (terminator356 on users dot sourceforge dot net)
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; version 2 of
10 //  the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 //=========================================================
22 
23 #include "operations.h"
24 #include "song.h"
25 #include "globals.h"
26 #include "synth.h"
27 #include "muse_time.h"
28 #include "config.h"
29 
30 // Forwards from header:
31 #include "tempo.h"
32 #include "sig.h"
33 #include "keyevent.h"
34 #include "midiport.h"
35 #include "metronome_class.h"
36 #include "audio_convert/audio_converter_plugin.h"
37 #include "audio_convert/audio_converter_settings_group.h"
38 
39 // Enable for debugging:
40 //#define _PENDING_OPS_DEBUG_
41 
42 // For debugging output: Uncomment the fprintf section.
43 #define ERROR_OPERATIONS(dev, format, args...)  fprintf(dev, format, ##args)
44 #define DEBUG_OPERATIONS(dev, format, args...)  //fprintf(dev, format, ##args)
45 
46 namespace MusECore {
47 
48 //-----------------------------------
49 //  MidiCtrlValListIterators
50 //-----------------------------------
51 
findList(const MidiCtrlValList * valList)52 MidiCtrlValListIterators::iterator MidiCtrlValListIterators::findList(const MidiCtrlValList* valList)
53 {
54   for(iterator i = begin(); i != end(); ++i)
55     if((*i)->second == valList)
56       return i;
57   return end();
58 }
59 
findList(const MidiCtrlValList * valList) const60 MidiCtrlValListIterators::const_iterator MidiCtrlValListIterators::findList(const MidiCtrlValList* valList) const
61 {
62   for(const_iterator i = begin(); i != end(); ++i)
63     if((*i)->second == valList)
64       return i;
65   return end();
66 }
67 
68 //-----------------------------------
69 //  MidiCtrlValLists2bErased
70 //-----------------------------------
71 
add(int port,const iMidiCtrlValList & item)72 void MidiCtrlValLists2bErased::add(int port, const iMidiCtrlValList& item)
73 {
74   iterator i = find(port);
75   if(i == end())
76   {
77     MidiCtrlValListIterators mcvli;
78     mcvli.push_back(item);
79     insert(MidiCtrlValLists2bErasedInsertPair_t(port, mcvli));
80     return;
81   }
82   MidiCtrlValListIterators& mcvli = i->second;
83   for(iMidiCtrlValListIterators_t imcvli = mcvli.begin(); imcvli != mcvli.end(); ++imcvli)
84   {
85     iMidiCtrlValList imcvl = *imcvli;
86     // Compare list pointers.
87     if(imcvl->second == item->second)
88       return; // Already exists.
89   }
90   mcvli.push_back(item);
91 }
92 
findList(int port,const MidiCtrlValList * valList)93 MidiCtrlValLists2bErased::iterator MidiCtrlValLists2bErased::findList(int port, const MidiCtrlValList* valList)
94 {
95   iterator i = find(port);
96   if(i == end())
97     return end();
98   if(i->second.findList(valList) != i->second.end())
99     return i;
100   return end();
101 }
102 
findList(int port,const MidiCtrlValList * valList) const103 MidiCtrlValLists2bErased::const_iterator MidiCtrlValLists2bErased::findList(int port, const MidiCtrlValList* valList) const
104 {
105   const_iterator i = find(port);
106   if(i == end())
107     return end();
108   if(i->second.findList(valList) != i->second.end())
109     return i;
110   return end();
111 }
112 
113 //-----------------------------------
114 //  PendingOperationItem
115 //-----------------------------------
116 
isAllocationOp(const PendingOperationItem & op) const117 bool PendingOperationItem::isAllocationOp(const PendingOperationItem& op) const
118 {
119   switch(op._type)
120   {
121     case AddMidiCtrlValList:
122       // A is channel B is control.
123       if(_type == AddMidiCtrlValList && _mcvll == op._mcvll && _intA == op._intA && _intB == op._intB)
124         return true;
125     break;
126 
127     // In the case of type AddMidiDevice, this searches for the name only.
128     case AddMidiDevice:
129       if(_type == AddMidiDevice && _midi_device_list == op._midi_device_list &&
130          _midi_device->name() == op._midi_device->name())
131         return true;
132     break;
133 
134     default:
135     break;
136   }
137 
138   return false;
139 }
140 
getIndex() const141 unsigned int PendingOperationItem::getIndex() const
142 {
143   switch(_type)
144   {
145     case Uninitialized:
146     case AddAuxSendValue:
147     case AddMidiInstrument:
148     case DeleteMidiInstrument:
149     case ReplaceMidiInstrument:
150     case AddMidiDevice:
151     case DeleteMidiDevice:
152     case ModifyMidiDeviceAddress:
153     case ModifyMidiDeviceFlags:
154     case ModifyMidiDeviceName:
155     case SetInstrument:
156     case AddTrack:
157     case DeleteTrack:
158     case MoveTrack:
159     case ModifyTrackName:
160     case ModifyTrackDrumMapItem:
161     case ReplaceTrackDrumMapPatchList:
162     case RemapDrumControllers:
163     case UpdateDrumMaps:
164     case SetTrackRecord:
165     case SetTrackMute:
166     case SetTrackSolo:
167     case SetTrackRecMonitor:
168     case SetTrackOff:
169     case ModifyPartName:
170     case ModifySongLength:
171     case AddMidiCtrlValList:
172     case ModifyAudioCtrlValList:
173     case SetGlobalTempo:
174     case AddRoute:
175     case DeleteRoute:
176     case AddRouteNode:
177     case DeleteRouteNode:
178     case ModifyRouteNode:
179     case UpdateSoloStates:
180     case EnableAllAudioControllers:
181     case GlobalSelectAllEvents:
182     case SwitchMetronomeSettings:
183     case ModifyMetronomeAccentMap:
184     case SetExternalSyncFlag:
185     case SetUseJackTransport:
186     case SetUseMasterTrack:
187     case ModifyAudioSamples:
188     case SetStaticTempo:
189     case ModifyLocalAudioConverterSettings:
190     case ModifyLocalAudioConverter:
191     case ModifyDefaultAudioConverterSettings:
192     case ModifyStretchListRatio:
193     case SetAudioConverterOfflineMode:
194     case ModifyMarkerList:
195     case ModifyTempoList:
196     case ModifySigList:
197     case ModifyKeyList:
198     case ModifyEventList:
199     case ModifyMidiCtrlValList:
200 
201       // To help speed up searches of these ops, let's (arbitrarily) set index = type instead of all of them being at index 0!
202       return _type;
203 
204     case ModifyPartStart:
205       return _part->posValue();
206 
207     case ModifyPartLength:
208       return _part->posValue();
209 
210     case MovePart:
211       // _part is used here rather than _iPart since _iPart can be end().
212       return _part->posValue();
213 
214     case AddPart:
215       return _part->posValue();
216 
217     case DeletePart:
218       return _iPart->second->posValue();
219 
220     case SelectPart:
221       return _part->posValue();
222 
223 
224     case AddEvent:
225       return _ev.posValue();
226 
227     case DeleteEvent:
228       return _ev.posValue();
229 
230     case SelectEvent:
231       return _ev.posValue();
232 
233 
234     case AddMidiCtrlVal:
235       return _posLenVal;  // Tick
236 
237     case DeleteMidiCtrlVal:
238       return _imcv->first;  // Tick
239 
240     case ModifyMidiCtrlVal:
241       return _imcv->first;  // Tick
242 
243 
244     case AddAudioCtrlVal:
245       return _posLenVal;  // Frame
246 
247     case DeleteAudioCtrlVal:
248       return _iCtrl->first;  // Frame
249 
250     case ModifyAudioCtrlVal:
251       return _iCtrl->first;  // Frame
252 
253 
254     case AddStretchListRatioAt:
255       return _museFrame;  // Frame
256 
257     case DeleteStretchListRatioAt:
258       return _iStretchEvent->first;  // Frame
259 
260     case ModifyStretchListRatioAt:
261       return _iStretchEvent->first;  // Frame
262 
263     default:
264       ERROR_OPERATIONS(stderr, "PendingOperationItem::getIndex unknown op type: %d\n", _type);
265       return 0;
266     break;
267   }
268 }
269 
executeRTStage()270 SongChangedStruct_t PendingOperationItem::executeRTStage()
271 {
272   SongChangedStruct_t flags = 0;
273   switch(_type)
274   {
275     case ModifyDefaultAudioConverterSettings:
276       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyDefaultAudioConverterSettings: "
277                                 "settings:%p\n", _audio_converter_settings);
278       if(_audio_converter_settings)
279       {
280         // Grab the current settings.
281         AudioConverterSettingsGroup* cur_settings = MusEGlobal::defaultAudioConverterSettings;
282         // Now change the actual global pointer variable.
283         MusEGlobal::defaultAudioConverterSettings = _audio_converter_settings;
284         // Transfer the original pointer into the member, so it can be deleted in the non-RT stage.
285         _audio_converter_settings = cur_settings;
286         flags |= SC_AUDIO_CONVERTER;
287       }
288     break;
289 
290     case ModifyLocalAudioConverterSettings:
291     {
292       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyLocalAudioConverterSettings: "
293                                 "sndFile:%p settings:%p\n",
294                                 *_sndFileR, _audio_converter_settings);
295 
296       // _audio_converter_settings can be NULL meaning don't touch, settings can only be
297       //  'replaced' but not deleted.
298       if(_audio_converter_settings)
299       {
300         AudioConverterSettingsGroup* cur_settings = _sndFileR.audioConverterSettings();
301         _sndFileR.setAudioConverterSettings(_audio_converter_settings);
302         // Transfer the original pointer into the member, so it can be deleted in the non-RT stage.
303         _audio_converter_settings = cur_settings;
304         flags |= SC_AUDIO_CONVERTER;
305       }
306     }
307     break;
308 
309     case ModifyLocalAudioConverter:
310     {
311       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyLocalAudioConverter: "
312                                 "sndFile:%p audio_converter:%p audio_converter_ui:%p\n",
313                                 *_sndFileR, _audio_converter, _audio_converter_ui);
314 
315       // _audio_converter and _audio_converter_ui can be NULL meaning delete them.
316       AudioConverterPluginI* cur_conv = _sndFileR.staticAudioConverter(AudioConverterSettings::RealtimeMode);
317       //if(_audio_converter)
318         _sndFileR.setStaticAudioConverter(_audio_converter, AudioConverterSettings::RealtimeMode);
319       // Transfer the original pointer into the member, so it can be deleted in the non-RT stage.
320       _audio_converter = cur_conv;
321 
322       AudioConverterPluginI* cur_convUI = _sndFileR.staticAudioConverter(AudioConverterSettings::GuiMode);
323       //if(_audio_converter_ui)
324         _sndFileR.setStaticAudioConverter(_audio_converter_ui, AudioConverterSettings::GuiMode);
325       // Transfer the original pointer into the member, so it can be deleted in the non-RT stage.
326       _audio_converter_ui = cur_convUI;
327       flags |= SC_AUDIO_CONVERTER;
328     }
329     break;
330 
331     case SetAudioConverterOfflineMode:
332     {
333 //      DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetAudioConverterOfflineMode: "
334 //                                "sndFile:%p audio_converter:%p\n",
335 //                                *_sndFile, _audio_converter);
336 
337       AudioConverterPluginI* cur_conv = _sndFileR.staticAudioConverter(AudioConverterSettings::RealtimeMode);
338       //if(_audio_converter)
339         _sndFileR.setStaticAudioConverter(_audio_converter, AudioConverterSettings::RealtimeMode);
340       // Transfer the original pointer into the member, so it can be deleted in the non-RT stage.
341       _audio_converter = cur_conv;
342       flags |= SC_AUDIO_CONVERTER;
343     }
344     break;
345 
346 
347     case ModifyTrackDrumMapItem:
348     {
349 #ifdef _PENDING_OPS_DEBUG_
350       fprintf(stderr, "PendingOperationItem::executeRTStage ModifyTrackDrumMapItem drummap operation:%p\n",
351               _drum_map_track_operation);
352 #endif
353 
354       MidiTrack* mt;
355       MidiTrackList& mtl = _drum_map_track_operation->_tracks;
356       for(iMidiTrack imt = mtl.begin(); imt != mtl.end(); ++imt)
357       {
358         mt = *imt;
359         // FIXME Possible non realtime-friendly allocation.
360         mt->modifyWorkingDrumMap(_drum_map_track_operation->_workingItemList,
361                                  _drum_map_track_operation->_isReset,
362                                  _drum_map_track_operation->_includeDefault,
363                                  _drum_map_track_operation->_isInstrumentMod,
364                                  _drum_map_track_operation->_doWholeMap);
365         flags |= (SC_DRUMMAP);
366       }
367 
368       // If this is an instrument modification we must now do a
369       //  general update of all drum track drum maps.
370       // Ideally we would like to update only the required ones,
371       //  but it is too difficult to tell which maps need updating
372       //  from inside the above loop (or inside modifyWorkingDrumMap).
373       if(_drum_map_track_operation->_isInstrumentMod)
374       {
375         MidiTrackList* mtlp = MusEGlobal::song->midis();
376         for(iMidiTrack imt = mtlp->begin(); imt != mtlp->end(); ++imt)
377         {
378           mt = *imt;
379           if(mt->type() != Track::DRUM)
380             continue;
381           if(mt->updateDrummap(false))
382             flags |= (SC_DRUMMAP);
383         }
384       }
385       flags |= (SC_DRUMMAP);
386     }
387     break;
388 
389     case ReplaceTrackDrumMapPatchList:
390     {
391 #ifdef _PENDING_OPS_DEBUG_
392       fprintf(stderr, "PendingOperationItem::executeRTStage ReplaceTrackDrumMapPatchList drummap operation:%p\n",
393               _drum_map_track_patch_replace_operation);
394 #endif
395 
396       MidiTrack* mt = _drum_map_track_patch_replace_operation->_track;
397       WorkingDrumMapPatchList* orig_wdmpl = mt->workingDrumMap();
398       // Simply switch pointers. Be sure to delete the original pointers later in the non-realtime stage.
399       // After the list pointers have been switched, swap with the replacement so that it can be deleted later.
400       mt->setWorkingDrumMap(_drum_map_track_patch_replace_operation->_workingItemPatchList,
401                             _drum_map_track_patch_replace_operation->_isInstrumentMod);
402       _drum_map_track_patch_replace_operation->_workingItemPatchList = orig_wdmpl;
403 
404       // If this is an instrument modification we must now do a
405       //  general update of all drum track drum maps.
406       // Ideally we would like to update only the required ones,
407       //  but it is too difficult to tell which maps need updating
408       //  from inside the above loop (or inside modifyWorkingDrumMap).
409       if(_drum_map_track_patch_replace_operation->_isInstrumentMod)
410       {
411         MidiTrackList* mtlp = MusEGlobal::song->midis();
412         for(iMidiTrack imt = mtlp->begin(); imt != mtlp->end(); ++imt)
413         {
414           mt = *imt;
415           if(mt->type() != Track::DRUM)
416             continue;
417           if(mt->updateDrummap(false))
418             flags |= (SC_DRUMMAP);
419         }
420       }
421       flags |= (SC_DRUMMAP);
422     }
423     break;
424 
425     case RemapDrumControllers:
426     {
427 #ifdef _PENDING_OPS_DEBUG_
428       fprintf(stderr, "PendingOperationItem::executeRTStage RemapDrumControllers remap operation:%p\n",
429               _midi_ctrl_val_remap_operation);
430 #endif
431 
432       for(iMidiCtrlValLists2bErased_t imcvle = _midi_ctrl_val_remap_operation->_midiCtrlValLists2bErased.begin();
433           imcvle != _midi_ctrl_val_remap_operation->_midiCtrlValLists2bErased.end(); ++imcvle)
434       {
435         const int port = imcvle->first;
436         MidiCtrlValListIterators& mcvli = imcvle->second;
437         MidiPort* mp = &MusEGlobal::midiPorts[port];
438         MidiCtrlValListList* mcvll = mp->controller();
439         for(iMidiCtrlValListIterators_t imcvli = mcvli.begin(); imcvli != mcvli.end(); ++imcvli)
440           mcvll->del(*imcvli);
441       }
442 
443       for(iMidiCtrlValLists2bAdded_t imcvla = _midi_ctrl_val_remap_operation->_midiCtrlValLists2bAdded.begin();
444           imcvla != _midi_ctrl_val_remap_operation->_midiCtrlValLists2bAdded.end(); ++imcvla)
445       {
446         const int port = imcvla->first;
447         MidiCtrlValListList* mcvll_a = imcvla->second;
448         MidiPort* mp = &MusEGlobal::midiPorts[port];
449         MidiCtrlValListList* mcvll = mp->controller();
450         for(iMidiCtrlValList imcvl = mcvll_a->begin(); imcvl != mcvll_a->end(); ++imcvl)
451           mcvll->add(imcvl->first >> 24, imcvl->second);
452       }
453 
454       // TODO: What to use here? We don't have anything SC_* related... yet.
455       //flags |= (SC_MIDI_CONTROLLER_ADD);
456       flags |= (SC_EVERYTHING);
457     }
458     break;
459 
460     case UpdateDrumMaps:
461 #ifdef _PENDING_OPS_DEBUG_
462       fprintf(stderr, "PendingOperationItem::executeRTStage UpdateDrumMaps: midi_port:%p:\n", _midi_port);
463 #endif
464       if(_midi_port->updateDrumMaps())
465         flags |= SC_DRUMMAP;
466     break;
467 
468     case UpdateSoloStates:
469       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage UpdateSoloStates: track_list:%p:\n", _track_list);
470       // TODO Use the track_list, or simply keep as dummy parameter to identify UpdateSoloStates?
471       MusEGlobal::song->updateSoloStates();
472       flags |= SC_SOLO;
473     break;
474 
475     // TODO: Try to break this operation down so that only the actual operation is executed stage-2.
476     case AddRoute:
477 #ifdef _PENDING_OPS_DEBUG_
478       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddRoute: src/dst routes:\n");
479       _src_route.dump();
480       _dst_route.dump();
481 #endif
482       if(addRoute(_src_route, _dst_route))
483         flags |= SC_ROUTE;
484     break;
485 
486     // TODO: Try to break this operation down so that only the actual operation is executed stage-2.
487     case DeleteRoute:
488 #ifdef _PENDING_OPS_DEBUG_
489       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteRoute: src/dst routes:\n");
490       _src_route.dump();
491       _dst_route.dump();
492 #endif
493       if(removeRoute(_src_route, _dst_route))
494         flags |= SC_ROUTE;
495     break;
496 
497     case AddRouteNode:
498 #ifdef _PENDING_OPS_DEBUG_
499       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddRouteNode: route_list:%p route:\n", _route_list);
500       _src_route.dump();
501 #endif
502       _route_list->push_back(_src_route);
503       flags |= SC_ROUTE;
504     break;
505 
506     case DeleteRouteNode:
507 #ifdef _PENDING_OPS_DEBUG_
508       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteRouteNode: route_list:%p route:\n", _route_list);
509       _iRoute->dump();
510 #endif
511       _route_list->erase(_iRoute);
512       flags |= SC_ROUTE;
513     break;
514 
515     case ModifyRouteNode:
516 #ifdef _PENDING_OPS_DEBUG_
517       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyRouteNode: src/dst routes:\n");
518       _src_route.dump();
519       _dst_route_pointer->dump();
520 #endif
521       *_dst_route_pointer = _src_route;
522       flags |= SC_ROUTE;
523     break;
524 
525     case AddAuxSendValue:
526       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddAuxSendValue aux_send_value_list:%p val:%f\n", _aux_send_value_list, _aux_send_value);
527       _aux_send_value_list->push_back(_aux_send_value);
528     break;
529 
530 
531     case AddMidiInstrument:
532       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddMidiInstrument instrument_list:%p instrument:%p\n", _midi_instrument_list, _midi_instrument);
533       _midi_instrument_list->push_back(_midi_instrument);
534       flags |= SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD;
535     break;
536 
537     case DeleteMidiInstrument:
538       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteMidiInstrument instrument_list:%p instrument:%p\n", _midi_instrument_list, *_iMidiInstrument);
539       _midi_instrument_list->erase(_iMidiInstrument);
540       flags |= SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD;
541     break;
542 
543     case ReplaceMidiInstrument:
544     {
545 #ifdef _PENDING_OPS_DEBUG_
546       fprintf(stderr, "PendingOperationItem::executeRTStage ReplaceMidiInstrument instrument_list:%p instrument:%p new_ instrument:%p\n",
547               _midi_instrument_list, *_iMidiInstrument, _midi_instrument);
548 #endif
549       // Grab the existing pointer to be deleted.
550       MidiInstrument* orig = *_iMidiInstrument;
551       // Erase from the list.
552       _midi_instrument_list->erase(_iMidiInstrument);
553       // Add the new instrument.
554       _midi_instrument_list->push_back(_midi_instrument);
555 
556       // Change all ports which used the original instrument.
557       for(int port = 0; port < MusECore::MIDI_PORTS; ++port)
558       {
559         MidiPort* mp = &MusEGlobal::midiPorts[port];
560         if(mp->instrument() != orig)
561           continue;
562         // Set the new instrument and nothing more (ie. don't use MidiPort::changeInstrument()).
563         // Here we will flag the initializations, and normalize and update the drum maps.
564         mp->setInstrument(_midi_instrument);
565         // Flag the port to send initializations next time it bothers to check.
566         // TODO: Optimize: We only need this if the user changed the initialization
567         //        lists or sysex list. Find a way to pass that info here.
568         mp->clearInitSent();
569       }
570 
571       // Since this is an instrument modification we must now do a
572       //  general update of all drum track drum maps using this instrument.
573       // Ideally we would like to update only the required ones,
574       //  but it is too difficult to tell which maps need updating
575       //  from inside the above loop (or inside modifyWorkingDrumMap).
576       MidiTrack* mt;
577       int mt_port;
578       MidiPort* mt_mp;
579       MidiTrackList* mtlp = MusEGlobal::song->midis();
580       for(iMidiTrack imt = mtlp->begin(); imt != mtlp->end(); ++imt)
581       {
582         mt = *imt;
583         if(mt->type() != Track::DRUM)
584           continue;
585         mt_port = mt->outPort();
586         if(mt_port < 0 || mt_port >= MusECore::MIDI_PORTS)
587           continue;
588         mt_mp = &MusEGlobal::midiPorts[mt_port];
589         // We are looking for tracks which are now using the new instrument.
590         if(mt_mp->instrument() != _midi_instrument)
591           continue;
592         // Ensure there are NO duplicate enote fields.
593         //mt->normalizeWorkingDrumMapPatchList();
594         // Finally, update the track's drum map (and drum in map).
595         mt->updateDrummap(false);
596       }
597 
598       // Transfer the original pointer back to _midi_instrument so it can be deleted in the non-RT stage.
599       _midi_instrument = orig;
600 
601       flags |= SC_CONFIG | SC_MIDI_INSTRUMENT | SC_DRUMMAP | SC_MIDI_CONTROLLER_ADD;
602     }
603     break;
604 
605     case AddMidiDevice:
606       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddMidiDevice devicelist:%p device:%p\n", _midi_device_list, _midi_device);
607       _midi_device_list->push_back(_midi_device);
608       flags |= SC_CONFIG;
609     break;
610 
611     case DeleteMidiDevice:
612       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteMidiDevice devicelist:%p device:%p\n", _midi_device_list, *_iMidiDevice);
613       _midi_device_list->erase(_iMidiDevice);
614       flags |= SC_CONFIG;
615     break;
616 
617     case ModifyMidiDeviceAddress:
618       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiDeviceAddress device:%p client:%d port:%d\n", _midi_device, _address_client, _address_port);
619       _midi_device->setAddressClient(_address_client);
620       _midi_device->setAddressPort(_address_port);
621       _midi_device->setOpenFlags(_open_flags);
622       flags |= SC_CONFIG;
623     break;
624 
625     case ModifyMidiDeviceFlags:
626       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiDeviceFlags device:%p rwFlags:%d openFlags:%d\n", _midi_device, _rw_flags, _open_flags);
627       _midi_device->setrwFlags(_rw_flags);
628       _midi_device->setOpenFlags(_open_flags);
629       flags |= SC_CONFIG;
630     break;
631 
632     case ModifyMidiDeviceName:
633       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiDeviceName device:%p name:%s\n", _midi_device, _name->toLocal8Bit().data());
634       _midi_device->setName(*_name);
635       flags |= SC_CONFIG;
636     break;
637 
638     case SetInstrument:
639 #ifdef _PENDING_OPS_DEBUG_
640       fprintf(stderr, "PendingOperationItem::executeRTStage SetInstrument port:%p instr:%p\n",
641               _midi_port, _midi_instrument);
642 #endif
643       _midi_port->setInstrument(_midi_instrument);
644       // Flag the port to send initializations next time it bothers to check.
645       _midi_port->clearInitSent();
646       flags |= SC_MIDI_INSTRUMENT;
647     break;
648 
649     case AddTrack:
650     {
651       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddTrack track_list:%p track:%p\n", _track_list, _track);
652       if(_void_track_list)
653       {
654         switch(_track->type())
655         {
656               case Track::MIDI:
657               case Track::DRUM:
658                     static_cast<MidiTrackList*>(_void_track_list)->push_back(static_cast<MidiTrack*>(_track));
659                     break;
660               case Track::WAVE:
661                     static_cast<WaveTrackList*>(_void_track_list)->push_back(static_cast<WaveTrack*>(_track));
662                     break;
663               case Track::AUDIO_OUTPUT:
664                     //--------------------------------------------------------------
665                     // Connect metronome audio click
666                     //--------------------------------------------------------------
667                     // Are there no other existing AudioOutput tracks?
668                     if(static_cast<OutputList*>(_void_track_list)->empty())
669                       // Do the user a favour: Auto-connect the metronome to the track.
670                       static_cast<AudioOutput*>(_track)->setSendMetronome(true);
671 
672                     static_cast<OutputList*>(_void_track_list)->push_back(static_cast<AudioOutput*>(_track));
673                     break;
674               case Track::AUDIO_GROUP:
675                     static_cast<GroupList*>(_void_track_list)->push_back(static_cast<AudioGroup*>(_track));
676                     break;
677               case Track::AUDIO_AUX:
678                     static_cast<AuxList*>(_void_track_list)->push_back(static_cast<AudioAux*>(_track));
679                     // Special for aux, make it easier to detect their changes.
680                     flags |= SC_AUX;
681                     break;
682               case Track::AUDIO_INPUT:
683                     static_cast<InputList*>(_void_track_list)->push_back(static_cast<AudioInput*>(_track));
684                     break;
685               case Track::AUDIO_SOFTSYNTH:
686                     static_cast<SynthIList*>(_void_track_list)->push_back(static_cast<SynthI*>(_track));
687                     break;
688               default:
689                     ERROR_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddTrack: Unknown track type %d\n", _track->type());
690                     return flags;
691         }
692       }
693 
694       iTrack track_it = _track_list->index2iterator(_insert_at);
695       _track_list->insert(track_it, _track);
696       flags |= SC_TRACK_INSERTED;
697 
698       // Add routes:
699       if(_track->isMidiTrack())
700       {
701             // Add any port output routes to this track
702             const RouteList* rl = _track->inRoutes();
703             for(ciRoute r = rl->begin(); r != rl->end(); ++r)
704             {
705                   switch(r->type)
706                   {
707                     case Route::MIDI_PORT_ROUTE:  {
708                       Route src(_track, r->channel);
709                       MusEGlobal::midiPorts[r->midiPort].outRoutes()->push_back(src);
710                       flags |= SC_ROUTE; }
711                     break;
712                     case Route::TRACK_ROUTE:
713                     case Route::JACK_ROUTE:
714                     case Route::MIDI_DEVICE_ROUTE:
715                     break;
716                   }
717             }
718             // Add any port input routes from this track
719             rl = _track->outRoutes();
720             for(ciRoute r = rl->begin(); r != rl->end(); ++r)
721             {
722                   switch(r->type)
723                   {
724                     case Route::MIDI_PORT_ROUTE:  {
725                       Route src(_track, r->channel);
726                       MusEGlobal::midiPorts[r->midiPort].inRoutes()->push_back(src);
727                       flags |= SC_ROUTE; }
728                     break;
729                     case Route::TRACK_ROUTE:
730                     case Route::JACK_ROUTE:
731                     case Route::MIDI_DEVICE_ROUTE:
732                     break;
733                   }
734             }
735       }
736       else
737       {
738             // Add other tracks' output routes to this track
739             const RouteList* rl = _track->inRoutes();
740             for(ciRoute r = rl->begin(); r != rl->end(); ++r)
741             {
742                   switch(r->type)
743                   {
744                     case Route::TRACK_ROUTE: {
745                       Route src(_track, r->remoteChannel, r->channels);
746                       src.remoteChannel = r->channel;
747                       r->track->outRoutes()->push_back(src);
748                       flags |= SC_ROUTE;
749                       // Is the source an Aux Track or else does it have Aux Tracks routed to it?
750                       // Update this track's aux ref count.
751                       if(r->track->auxRefCount())
752                       {
753                         _track->updateAuxRoute(r->track->auxRefCount(), NULL);
754                       }
755                       else if(r->track->type() == Track::AUDIO_AUX)
756                       {
757                         _track->updateAuxRoute(1, NULL);
758                       }
759                     }
760                     break;
761                     case Route::MIDI_PORT_ROUTE:
762                     case Route::JACK_ROUTE:
763                     case Route::MIDI_DEVICE_ROUTE:
764                     break;
765                   }
766             }
767             // Add other tracks' input routes from this track
768             rl = _track->outRoutes();
769             for(ciRoute r = rl->begin(); r != rl->end(); ++r)
770             {
771                   switch(r->type)
772                   {
773                     case Route::TRACK_ROUTE: {
774                       Route src(_track, r->remoteChannel, r->channels);
775                       src.remoteChannel = r->channel;
776                       r->track->inRoutes()->push_back(src);
777                       flags |= SC_ROUTE;
778                       // Is this track an Aux Track or else does it have Aux Tracks routed to it?
779                       // Update the other track's aux ref count and all tracks it is connected to.
780                       if(_track->auxRefCount())
781                       {
782                         r->track->updateAuxRoute(_track->auxRefCount(), NULL);
783                       }
784                       else if(_track->type() == Track::AUDIO_AUX)
785                       {
786                         r->track->updateAuxRoute(1, NULL);
787                       }
788                     }
789                     break;
790                     case Route::MIDI_PORT_ROUTE:
791                     case Route::JACK_ROUTE:
792                     case Route::MIDI_DEVICE_ROUTE:
793                     break;
794                   }
795             }
796       }
797       chainTrackParts(_track);
798 
799       // Be sure to mark the parts as not deleted if they exist in the global copy/paste clone list.
800       const PartList* pl = _track->cparts();
801       for(ciPart ip = pl->begin(); ip != pl->end(); ++ip)
802       {
803         for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i)
804         {
805           if(i->cp == ip->second)
806             i->is_deleted = false;
807         }
808       }
809     }
810     break;
811 
812     case DeleteTrack:
813     {
814       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteTrack track_list:%p track:%p sec_track_list:%p\n", _track_list, _track, _void_track_list);
815       unchainTrackParts(_track);
816       if(_void_track_list)
817       {
818         switch(_track->type())
819         {
820               case Track::MIDI:
821               case Track::DRUM:
822                     static_cast<MidiTrackList*>(_void_track_list)->erase(_track);
823                     break;
824               case Track::WAVE:
825                     static_cast<WaveTrackList*>(_void_track_list)->erase(_track);
826                     break;
827               case Track::AUDIO_OUTPUT:
828                     static_cast<OutputList*>(_void_track_list)->erase(_track);
829                     break;
830               case Track::AUDIO_GROUP:
831                     static_cast<GroupList*>(_void_track_list)->erase(_track);
832                     break;
833               case Track::AUDIO_AUX:
834                     static_cast<AuxList*>(_void_track_list)->erase(_track);
835                     // Special for aux, make it easier to detect their changes.
836                     flags |= SC_AUX;
837                     break;
838               case Track::AUDIO_INPUT:
839                     static_cast<InputList*>(_void_track_list)->erase(_track);
840                     break;
841               case Track::AUDIO_SOFTSYNTH:
842                     {
843                       static_cast<SynthIList*>(_void_track_list)->erase(_track);
844 
845                       // Change all ports which used the instrument.
846                       // FIXME TODO: We want to make this undoable but ATM a few other things can
847                       //  set the instrument without an undo operation so the undo sequence would
848                       //  not be correct. So we don't have much choice but to just reset for now.
849                       // Still, everything else is in place for undoable setting of instrument...
850                       const SynthI* si = static_cast<const SynthI*>(_track);
851                       for(int port = 0; port < MusECore::MIDI_PORTS; ++port)
852                       {
853                         MidiPort* mp = &MusEGlobal::midiPorts[port];
854                         if(mp->instrument() != si)
855                           continue;
856                         // Just revert to GM.
857                         mp->setInstrument(registerMidiInstrument("GM"));
858                         // Flag the port to send initializations next time it bothers to check.
859                         mp->clearInitSent();
860                         flags |= SC_MIDI_INSTRUMENT;
861                       }
862                     }
863                     break;
864               default:
865                     ERROR_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteTrack: Unknown track type %d\n", _track->type());
866                     return flags;
867         }
868       }
869       _track_list->erase(_track);
870       flags |= SC_TRACK_REMOVED;
871 
872       // Remove routes:
873       if(_track->type() == Track::AUDIO_OUTPUT)
874       {
875             // Clear the track's jack ports
876             for(int ch = 0; ch < _track->channels(); ++ch)
877             {
878               ((AudioOutput*)_track)->setJackPort(ch, 0);
879               flags |= SC_ROUTE;
880             }
881 
882             // Clear the track's output routes' jack ports
883             RouteList* orl = _track->outRoutes();
884             for(iRoute r = orl->begin(); r != orl->end(); ++r)
885             {
886               if(r->type != Route::JACK_ROUTE)
887                 continue;
888               r->jackPort = 0;
889               flags |= SC_ROUTE;
890             }
891       }
892       else if(_track->type() == Track::AUDIO_INPUT)
893       {
894             // Clear the track's jack ports
895             for(int ch = 0; ch < _track->channels(); ++ch)
896             {
897               ((AudioInput*)_track)->setJackPort(ch, 0);
898               flags |= SC_ROUTE;
899             }
900 
901             // Clear the track's input routes' jack ports
902             RouteList* irl = _track->inRoutes();
903             for(iRoute r = irl->begin(); r != irl->end(); ++r)
904             {
905               if(r->type != Route::JACK_ROUTE)
906                 continue;
907               r->jackPort = 0;
908               flags |= SC_ROUTE;
909             }
910       }
911 
912       if(_track->isMidiTrack())
913       {
914             // Remove any port output routes to this track
915             const RouteList* rl = _track->inRoutes();
916             for(ciRoute r = rl->begin(); r != rl->end(); ++r)
917             {
918                   switch(r->type)
919                   {
920                     case Route::MIDI_PORT_ROUTE:  {
921                       Route src(_track, r->channel);
922                       MusEGlobal::midiPorts[r->midiPort].outRoutes()->removeRoute(src);
923                       flags |= SC_ROUTE; }
924                     break;
925                     case Route::TRACK_ROUTE:
926                     case Route::JACK_ROUTE:
927                     case Route::MIDI_DEVICE_ROUTE:
928                     break;
929                   }
930             }
931             // Remove any port input routes from this track
932             rl = _track->outRoutes();
933             for(ciRoute r = rl->begin(); r != rl->end(); ++r)
934             {
935                   switch(r->type)
936                   {
937                     case Route::MIDI_PORT_ROUTE:  {
938                       Route src(_track, r->channel);
939                       MusEGlobal::midiPorts[r->midiPort].inRoutes()->removeRoute(src);
940                       flags |= SC_ROUTE; }
941                     break;
942                     case Route::TRACK_ROUTE:
943                     case Route::JACK_ROUTE:
944                     case Route::MIDI_DEVICE_ROUTE:
945                     break;
946                   }
947             }
948       }
949       else
950       {
951             // Remove other tracks' output routes to this track
952             const RouteList* rl = _track->inRoutes();
953             for(ciRoute r = rl->begin(); r != rl->end(); ++r)
954             {
955                   switch(r->type)
956                   {
957                     case Route::TRACK_ROUTE: {
958                       Route src(_track, r->remoteChannel, r->channels);
959                       src.remoteChannel = r->channel;
960                       r->track->outRoutes()->removeRoute(src);
961                       flags |= SC_ROUTE;
962                       // Is the source an Aux Track or else does it have Aux Tracks routed to it?
963                       // Update this track's aux ref count.
964                       if(r->track->auxRefCount())
965                       {
966                         _track->updateAuxRoute(-r->track->auxRefCount(), NULL);
967                       }
968                       else if(r->track->type() == Track::AUDIO_AUX)
969                       {
970                         _track->updateAuxRoute(-1, NULL);
971                       }
972                     }
973                     break;
974                     case Route::MIDI_PORT_ROUTE:
975                     case Route::JACK_ROUTE:
976                     case Route::MIDI_DEVICE_ROUTE:
977                     break;
978                   }
979             }
980             // Remove other tracks' input routes from this track
981             rl = _track->outRoutes();
982             for(ciRoute r = rl->begin(); r != rl->end(); ++r)
983             {
984                   switch(r->type)
985                   {
986                     case Route::TRACK_ROUTE: {
987                       Route src(_track, r->remoteChannel, r->channels);
988                       src.remoteChannel = r->channel;
989                       r->track->inRoutes()->removeRoute(src);
990                       flags |= SC_ROUTE;
991                       // Is this track an Aux Track or else does it have Aux Tracks routed to it?
992                       // Update the other track's aux ref count and all tracks it is connected to.
993                       if(_track->auxRefCount())
994                       {
995                         r->track->updateAuxRoute(-_track->auxRefCount(), NULL);
996                       }
997                       else if(_track->type() == Track::AUDIO_AUX)
998                       {
999                         r->track->updateAuxRoute(-1, NULL);
1000                       }
1001                     }
1002                     break;
1003                     case Route::MIDI_PORT_ROUTE:
1004                     case Route::JACK_ROUTE:
1005                     case Route::MIDI_DEVICE_ROUTE:
1006                     break;
1007                   }
1008             }
1009       }
1010 
1011       // Be sure to mark the parts as deleted if they exist in the global copy/paste clone list.
1012       const PartList* pl = _track->cparts();
1013       for(ciPart ip = pl->begin(); ip != pl->end(); ++ip)
1014       {
1015         for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i)
1016         {
1017           if(i->cp == ip->second)
1018             i->is_deleted = true;
1019         }
1020       }
1021     }
1022     break;
1023 
1024     case MoveTrack:
1025     {
1026       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage MoveTrack from:%d to:%d\n", _from_idx, _to_idx);
1027       const int sz = _track_list->size();
1028       if(_from_idx >= sz)
1029       {
1030         ERROR_OPERATIONS(stderr, "MusE error: PendingOperationItem::executeRTStage MoveTrack from index out of range:%d\n", _from_idx);
1031         return flags;
1032       }
1033       ciTrack fromIt = _track_list->cbegin() + _from_idx;
1034       Track* track = *fromIt;
1035       _track_list->erase(fromIt);
1036       ciTrack toIt = (_to_idx >= sz) ? _track_list->cend() : _track_list->cbegin() + _to_idx;
1037       _track_list->insert(toIt, track);
1038       flags |= SC_TRACK_MOVED;
1039     }
1040     break;
1041 
1042     case ModifyTrackName:
1043       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyTrackName track:%p new_val:%s\n", _track, _name->toLocal8Bit().data());
1044       _track->setName(*_name);
1045       flags |= (SC_TRACK_MODIFIED | SC_MIDI_TRACK_PROP);
1046       // If it's an aux track, notify aux UI controls to reload, or change their names etc.
1047       if(_track->type() == Track::AUDIO_AUX)
1048         flags |= SC_AUX;
1049     break;
1050 
1051     case SetTrackRecord:
1052     {
1053       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetTrackRecord track:%p new_val:%d\n", _track, _boolA);
1054       const bool mon = _track->setRecordFlag2AndCheckMonitor(_boolA);
1055       flags |= SC_RECFLAG;
1056       if(mon)
1057         flags |= SC_TRACK_REC_MONITOR;
1058     }
1059     break;
1060 
1061     case SetTrackMute:
1062       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetTrackMute track:%p new_val:%d\n", _track, _boolA);
1063       _track->setMute(_boolA);
1064       flags |= SC_MUTE;
1065     break;
1066 
1067     case SetTrackSolo:
1068       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetTrackSolo track:%p new_val:%d\n", _track, _boolA);
1069       _track->setSolo(_boolA);
1070       flags |= SC_SOLO;
1071     break;
1072 
1073     case SetTrackRecMonitor:
1074 #ifdef _PENDING_OPS_DEBUG_
1075       fprintf(stderr, "PendingOperationItem::executeRTStage SetTrackRecMonitor track:%p new_val:%d\n", _track, _boolA);
1076 #endif
1077       _track->setRecMonitor(_boolA);
1078       flags |= SC_TRACK_REC_MONITOR;
1079     break;
1080 
1081     case SetTrackOff:
1082 #ifdef _PENDING_OPS_DEBUG_
1083       fprintf(stderr, "PendingOperationItem::executeRTStage SetTrackOff track:%p new_val:%d\n", _track, _boolA);
1084 #endif
1085       _track->setOff(_boolA);
1086       flags |= SC_MUTE;
1087     break;
1088 
1089 
1090     case AddPart:
1091       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddPart part:%p\n", _part);
1092       _part_list->add(_part);
1093       _part->rechainClone();
1094       // Be sure to mark the part as not deleted if it exists in the global copy/paste clone list.
1095       for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i)
1096       {
1097         if(i->cp == _part)
1098           i->is_deleted = false;
1099       }
1100       flags |= SC_PART_INSERTED;
1101       // If the part has events, then treat it as if they were inserted with separate AddEvent operations.
1102       // Even if some will be inserted later in this operations group with actual separate AddEvent operations,
1103       //  that's an SC_EVENT_INSERTED anyway, so hopefully no harm.
1104       if(!_part->events().empty())
1105         flags |= SC_EVENT_INSERTED;
1106     break;
1107 
1108     case DeletePart:
1109     {
1110       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeletePart part:%p\n", _iPart->second);
1111       Part* p = _iPart->second;
1112       _part_list->erase(_iPart);
1113       p->unchainClone();
1114       // Be sure to mark the part as deleted if it exists in the global copy/paste clone list.
1115       for(iClone i = MusEGlobal::cloneList.begin(); i != MusEGlobal::cloneList.end(); ++i)
1116       {
1117         if(i->cp == p)
1118           i->is_deleted = true;
1119       }
1120       flags |= SC_PART_REMOVED;
1121       // If the part had events, then treat it as if they were removed with separate DeleteEvent operations.
1122       // Even if they will be deleted later in this operations group with actual separate DeleteEvent operations,
1123       //  that's an SC_EVENT_REMOVED anyway, so hopefully no harm. This fixes a problem with midi controller canvas
1124       //  not updating after such a 'delete part with events, no separate AddEvents were used when creating the part'.
1125       if(!p->events().empty())
1126         flags |= SC_EVENT_REMOVED;
1127     }
1128     break;
1129 
1130     case ModifyPartStart:
1131     {
1132       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyPartStart part:%p old_val:%d new_val:%u\n", _part, _part->posValue(), _posLenVal);
1133 
1134       // Since we are modifying a part's position we must remove the part from the list
1135       //  and reinsert it afterwards for proper sorting (position is the sorting key).
1136       if(_part->track() && _iPart != _part->track()->parts()->end())
1137       {
1138         _part->track()->parts()->erase(_iPart);
1139         flags |= SC_PART_REMOVED;
1140       }
1141 
1142       // Was an event list supplied to be wholesale swapped?
1143       if(_event_list)
1144       {
1145         // Since the original event list is not an allocated pointer, there are no pointers to quickly exchange.
1146         // Instead use swap() which is also constant in time. Just like quickly exchanging pointers.
1147         // Transfers the original list back to _event_list so it can be deleted in the non-RT stage.
1148         (&_part->nonconst_events())->swap(*_event_list);
1149         flags |= (SC_EVENT_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED);
1150       }
1151 
1152       _part->setLenValue(_lenVal);
1153       _part->setPosValue(_posLenVal);
1154       if(_part->track())
1155       {
1156         _part->track()->parts()->add(_part);
1157         flags |= SC_PART_INSERTED;
1158       }
1159       flags |= SC_PART_MODIFIED;
1160     }
1161     break;
1162 
1163     case ModifyPartLength:
1164     {
1165       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyPartLength part:%p old_val:%d new_val:%u\n", _part, _part->lenValue(), _posLenVal);
1166 
1167       // Was an event list supplied to be wholesale swapped?
1168       if(_event_list)
1169       {
1170         // Since the original event list is not an allocated pointer, there are no pointers to quickly exchange.
1171         // Instead use swap() which is also constant in time. Just like quickly exchanging pointers.
1172         // Transfers the original list back to _event_list so it can be deleted in the non-RT stage.
1173         (&_part->nonconst_events())->swap(*_event_list);
1174         flags |= (SC_EVENT_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED);
1175       }
1176       _part->setLenValue(_posLenVal);
1177       flags |= SC_PART_MODIFIED;
1178     }
1179     break;
1180 
1181     case MovePart:
1182       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage MovePart part:%p track:%p new_pos:%u\n", _part, _track, _posLenVal);
1183       if(_track)
1184       {
1185         if(_part->track() && _iPart != _part->track()->parts()->end())
1186         {
1187           _part->track()->parts()->erase(_iPart);
1188           flags |= SC_PART_REMOVED;
1189         }
1190         _part->setTrack(_track);
1191         //_part->setTick(_posLenVal);
1192         _part->setPosValue(_posLenVal);
1193         _track->parts()->add(_part);
1194         flags |= SC_PART_INSERTED;
1195       }
1196       else
1197       {
1198         //_part->setTick(_posLenVal);
1199         _part->setPosValue(_posLenVal);
1200       }
1201       flags |= SC_PART_MODIFIED;
1202     break;
1203 
1204     case SelectPart:
1205 #ifdef _PENDING_OPS_DEBUG_
1206       fprintf(stderr, "PendingOperationItem::executeRTStage SelectPart part:%p select:%u\n", _part, _boolA);
1207 #endif
1208       if(_part)
1209         _part->setSelected(_boolA);
1210 
1211       flags |= SC_PART_SELECTION;
1212     break;
1213 
1214     case ModifyPartName:
1215       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyPartName part:%p new_val:%s\n", _part, _name->toLocal8Bit().data());
1216       _part->setName(*_name);
1217       flags |= SC_PART_MODIFIED;
1218     break;
1219 
1220 
1221     case AddEvent:
1222 #ifdef _PENDING_OPS_DEBUG_
1223       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddEvent pre:    ");
1224       _ev.dump();
1225 #endif
1226       _part->addEvent(_ev);
1227 #ifdef _PENDING_OPS_DEBUG_
1228       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddEvent post:   ");
1229       _ev.dump();
1230 #endif
1231       flags |= SC_EVENT_INSERTED;
1232     break;
1233 
1234     case DeleteEvent:
1235 #ifdef _PENDING_OPS_DEBUG_
1236       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteEvent pre:    ");
1237       _ev.dump();
1238 #endif
1239       _part->nonconst_events().erase(_iev);
1240 #ifdef _PENDING_OPS_DEBUG_
1241       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteEvent post:   ");
1242       _ev.dump();
1243 #endif
1244       flags |= SC_EVENT_REMOVED;
1245     break;
1246 
1247     case SelectEvent:
1248 #ifdef _PENDING_OPS_DEBUG_
1249       fprintf(stderr, "PendingOperationItem::executeRTStage SelectEvent part:%p select:%d\n", _part, _intA);
1250 #endif
1251       // Make sure we let song handle this important job, it selects corresponding events in clone parts.
1252       MusEGlobal::song->selectEvent(_ev, _part, _intA);
1253       flags |= SC_SELECTION;
1254     break;
1255 
1256 
1257     case ModifyEventList:
1258       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyEventList: orig eventlist:%p new eventlist:%p\n",
1259                        _orig_event_list, _event_list);
1260       if(_orig_event_list && _event_list)
1261       {
1262         // Since the original event list is not an allocated pointer, there are no pointers to quickly exchange.
1263         // Instead use swap() which is also constant in time. Just like quickly exchanging pointers.
1264         // Transfers the original list back to _event_list so it can be deleted in the non-RT stage.
1265         _orig_event_list->swap(*_event_list);
1266         flags |= (SC_EVENT_MODIFIED | SC_EVENT_INSERTED | SC_EVENT_REMOVED);
1267       }
1268     break;
1269 
1270     case AddMidiCtrlValList:
1271       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddMidiCtrlValList: mcvll:%p mcvl:%p chan:%d\n", _mcvll, _mcvl, _intA);
1272       _mcvll->add(_intA, _mcvl);
1273       flags |= SC_MIDI_CONTROLLER_ADD;
1274     break;
1275     case ModifyMidiCtrlValList:
1276       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiCtrlValList: orig_mcvl:%p mcvl:%p\n", _orig_mcvl, _mcvl);
1277       if(_orig_mcvl && _mcvl)
1278       {
1279         // Since the original list is not an allocated pointer, there are no pointers to quickly exchange.
1280         // Instead use swap() which is also constant in time. Just like quickly exchanging pointers.
1281         // Transfers the original list back to _mcvl so it can be deleted in the non-RT stage.
1282         _orig_mcvl->swap(*_mcvl);
1283         // No song changed flags are required to be set here.
1284       }
1285     break;
1286     case AddMidiCtrlVal:
1287       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddMidiCtrlVal: mcvl:%p part:%p tick:%u val:%d\n", _mcvl, _part, _posLenVal, _intB);
1288       // Do not attempt to add cached events which are outside of the part.
1289       // But do allow muted parts, and muted tracks, and 'off' tracks. Otherwise adding values
1290       //  to muted parts fails to add them when unmuted. The cache mechanism catches this anyways.
1291 #ifdef ALLOW_LEFT_HIDDEN_EVENTS
1292       if((int)_posLenVal >= (int)_part->posValue() &&
1293          (int)_posLenVal < (int)_part->posValue() + (int)_part->lenValue())
1294 #else
1295       if(_posLenVal >= _part->posValue() &&
1296          _posLenVal < _part->posValue() + _part->lenValue())
1297 #endif
1298          // FIXME FINDMICHJETZT XTicks!!
1299         _mcvl->insert(MidiCtrlValListInsertPair_t(_posLenVal, MidiCtrlVal(_part, _intB)));
1300       // No song changed flags are required to be set here.
1301     break;
1302     case DeleteMidiCtrlVal:
1303       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteMidiCtrlVal: mcvl:%p tick:%u part:%p val:%d\n",
1304                        _mcvl, _imcv->first, _imcv->second.part, _imcv->second.val);
1305       _mcvl->erase(_imcv);
1306       // No song changed flags are required to be set here.
1307     break;
1308     case ModifyMidiCtrlVal:
1309       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyMidiCtrlVal: part:%p old_val:%d new_val:%d\n",
1310                        _imcv->second.part, _imcv->second.val, _intA);
1311       _imcv->second.val = _intA;
1312     break;
1313 
1314 
1315     case ModifyAudioCtrlValList:
1316     {
1317       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyAudioCtrlValList: old ctrl_l:%p new ctrl_l:%p\n", _iCtrlList->second, _aud_ctrl_list);
1318       CtrlList* orig = _iCtrlList->second;
1319       _iCtrlList->second = _aud_ctrl_list;
1320       // Transfer the original pointer back to _aud_ctrl_list so it can be deleted in the non-RT stage.
1321       _aud_ctrl_list = orig;
1322       flags |= SC_AUDIO_CONTROLLER_LIST;
1323     }
1324     break;
1325     case AddAudioCtrlVal:
1326       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddAudioCtrlVal: ctrl_l:%p frame:%u val:%f\n",
1327               _aud_ctrl_list, _posLenVal, _ctl_dbl_val);
1328       //_aud_ctrl_list->insert(CtrlListInsertPair_t(_posLenVal, CtrlVal(_posLenVal, _ctl_dbl_val)));
1329       // Add will replace if found.
1330       _aud_ctrl_list->add(_posLenVal, _ctl_dbl_val);
1331       flags |= SC_AUDIO_CONTROLLER;
1332     break;
1333     case DeleteAudioCtrlVal:
1334       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteAudioCtrlVal: ctrl_l:%p ctrl_num:%d frame:%d val:%f\n",
1335                        _aud_ctrl_list, _aud_ctrl_list->id(), _iCtrl->first, _iCtrl->second.val);
1336       _aud_ctrl_list->erase(_iCtrl);
1337       flags |= SC_AUDIO_CONTROLLER;
1338     break;
1339     case ModifyAudioCtrlVal:
1340       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyAudioCtrlVal: frame:%u old_val:%f new_val:%f\n",
1341                        _iCtrl->first, _iCtrl->second.val, _ctl_dbl_val);
1342       // If the frame is the same, just change the value.
1343       if(_iCtrl->second.frame == _posLenVal)
1344       {
1345         _iCtrl->second.val = _ctl_dbl_val;
1346       }
1347       // Otherwise erase + add is required.
1348       else
1349       {
1350         _aud_ctrl_list->erase(_iCtrl);
1351         _aud_ctrl_list->insert(CtrlListInsertPair_t(_posLenVal, CtrlVal(_posLenVal, _ctl_dbl_val)));
1352       }
1353       flags |= SC_AUDIO_CONTROLLER;
1354     break;
1355 
1356 
1357     case ModifyTempoList:
1358       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyTempoList: orig tempolist:%p new tempolist:%p\n",
1359                        _orig_tempo_list, _tempo_list);
1360       if(_orig_tempo_list && _tempo_list)
1361       {
1362         // Since the original tempo list is not an allocated pointer, there are no pointers to quickly exchange.
1363         // Instead use swap() which is also constant in time. Just like quickly exchanging pointers.
1364         // Transfers the original list back to _tempo_list so it can be deleted in the non-RT stage.
1365         _orig_tempo_list->swap(*_tempo_list);
1366         flags |= SC_TEMPO;
1367       }
1368     break;
1369 
1370 
1371     case SetStaticTempo:
1372 #ifdef _PENDING_OPS_DEBUG_
1373       fprintf(stderr, "PendingOperationItem::executeRTStage SetStaticTempo: tempolist:%p new_tempo:%d\n", _tempo_list, _intA);
1374 #endif
1375       _tempo_list->setStaticTempo(_intA);
1376       flags |= SC_TEMPO;
1377     break;
1378 
1379     case SetGlobalTempo:
1380       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage SetGlobalTempo: tempolist:%p new_tempo:%d\n", _tempo_list, _intA);
1381       _tempo_list->setGlobalTempo(_intA);
1382       flags |= SC_TEMPO;
1383     break;
1384 
1385 
1386     case ModifySigList:
1387       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifySigList: orig siglist:%p new siglist:%p\n",
1388                        _orig_sig_list, _sig_list);
1389       if(_orig_sig_list && _sig_list)
1390       {
1391         // Since the original sig list is not an allocated pointer, there are no pointers to quickly exchange.
1392         // Instead use swap() which is also constant in time. Just like quickly exchanging pointers.
1393         // Transfers the original list back to _sig_list so it can be deleted in the non-RT stage.
1394         _orig_sig_list->swap(*_sig_list);
1395         flags |= SC_SIG;
1396       }
1397     break;
1398 
1399 
1400 
1401     case ModifyKeyList:
1402       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyKeyList: orig keylist:%p new keylist:%p\n",
1403                        _orig_key_list, _key_list);
1404       if(_orig_key_list && _key_list)
1405       {
1406         // Since the original key list is not an allocated pointer, there are no pointers to quickly exchange.
1407         // Instead use swap() which is also constant in time. Just like quickly exchanging pointers.
1408         // Transfers the original list back to _sig_list so it can be deleted in the non-RT stage.
1409         _orig_key_list->swap(*_key_list);
1410         flags |= SC_KEY;
1411       }
1412     break;
1413 
1414 
1415     case ModifyStretchListRatio:
1416       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyStretchListRatio: stretchType:%d stretchlist:%p new_ratio:%f\n",
1417                        _stretch_type, _stretch_list, _audio_converter_value);
1418       // Defer normalize until end of stage 2.
1419       _stretch_list->setRatio(StretchListItem::StretchEventType(_stretch_type), _audio_converter_value, false);
1420       flags |= SC_AUDIO_STRETCH;
1421     break;
1422 
1423     case AddStretchListRatioAt:
1424       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage AddStretchListRatioAt: stretchType:%d stretchlist:%p ratio:%f frame:%ld\n",
1425                        _stretch_type, _stretch_list, _audio_converter_value, _museFrame);
1426       // Defer normalize until end of stage 2.
1427       _stretch_list->addRatioAt(StretchListItem::StretchEventType(_stretch_type), _museFrame, _audio_converter_value, false);
1428 
1429       flags |= SC_AUDIO_STRETCH;
1430     break;
1431 
1432     case DeleteStretchListRatioAt:
1433       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage DeleteStretchListRatioAt: stretchlist:%p frame:%ld types:%d\n",
1434                         _stretch_list, _iStretchEvent->first, _stretch_type);
1435       // Defer normalize until end of stage 2.
1436       _stretch_list->del(_stretch_type, _iStretchEvent, false);
1437       flags |= SC_AUDIO_STRETCH;
1438     break;
1439 
1440     case ModifyStretchListRatioAt:
1441       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifyStretchListRatioAt: "
1442                                "stretchType:%d stretchlist:%p frame:%ld new_frame:%ld new_ratio:%f\n",
1443                        _stretch_type, _stretch_list, _iStretchEvent->first, _museFrame, _audio_converter_value);
1444 
1445       // If the frame is the same, just change the value.
1446       if(_iStretchEvent->first == _museFrame)
1447         // Defer normalize until end of stage 2.
1448         _stretch_list->setRatioAt(StretchListItem::StretchEventType(_stretch_type), _iStretchEvent, _audio_converter_value, false);
1449       // Otherwise erase + add is required.
1450       else
1451       {
1452         // Defer normalize until end of stage 2.
1453         _stretch_list->del(_stretch_type, _iStretchEvent, false);
1454         _stretch_list->add(StretchListItem::StretchEventType(_stretch_type), _museFrame, _audio_converter_value, false);
1455       }
1456 
1457       flags |= SC_AUDIO_STRETCH;
1458     break;
1459 
1460 
1461     case ModifySongLength:
1462       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage ModifySongLength: len:%d\n", _posLenVal);
1463       MusEGlobal::song->setLen(_posLenVal, false); // false = Do not emit update signals here !
1464       flags |= SC_EVERYTHING;
1465     break;
1466 
1467     case EnableAllAudioControllers:
1468     {
1469       DEBUG_OPERATIONS(stderr, "PendingOperationItem::executeRTStage EnableAllAudioControllers\n");
1470       TrackList* tl = MusEGlobal::song->tracks();
1471       for (iTrack it = tl->begin(); it != tl->end(); ++it)
1472       {
1473         Track* t = *it;
1474         if(t->isMidiTrack())
1475           continue;
1476         AudioTrack *at = static_cast<AudioTrack*>(t);
1477         // Re-enable all track and plugin controllers, and synth controllers if applicable.
1478         at->enableAllControllers();
1479         flags |= SC_AUDIO_CONTROLLER;
1480       }
1481     }
1482     break;
1483 
1484     case GlobalSelectAllEvents:
1485     {
1486 #ifdef _PENDING_OPS_DEBUG_
1487       fprintf(stderr, "PendingOperationItem::executeRTStage GlobalSelectAllEvents\n");
1488 #endif
1489       for (iTrack it = _track_list->begin(); it != _track_list->end(); ++it)
1490       {
1491         //Track* t = *it;
1492         //if(t->isMidiTrack())
1493         //  continue;
1494         if((*it)->selectEvents(_boolA))
1495           flags |= SC_SELECTION;
1496       }
1497     }
1498     break;
1499 
1500     case ModifyAudioSamples:
1501     {
1502 #ifdef _PENDING_OPS_DEBUG_
1503       fprintf(stderr, "PendingOperationItem::executeRTStage ModifyAudioSamples: "
1504                       "audioSamplesPointer:%p newAudioSamples:%p audioSamplesLen:%p newAudioSamplesLen:%d\n",
1505               _audioSamplesPointer, _newAudioSamples, _audioSamplesLen, _newAudioSamplesLen);
1506 #endif
1507       if(_audioSamplesPointer)
1508       {
1509         float* orig = *_audioSamplesPointer;
1510         *_audioSamplesPointer = _newAudioSamples;
1511         // Transfer the original pointer back to _audioSamplesPointer so it can be deleted in the non-RT stage.
1512         _newAudioSamples = orig;
1513       }
1514 
1515       if(_audioSamplesLen)
1516         *_audioSamplesLen = _newAudioSamplesLen;
1517 
1518       // Currently no flags for this.
1519       //flags |= SC_;
1520     }
1521     break;
1522 
1523     case ModifyMarkerList:
1524     {
1525 #ifdef _PENDING_OPS_DEBUG_
1526       fprintf(stderr, "PendingOperationItem::executeRTStage ModifyMarkerList: "
1527                       "orig list:%p new list:%p\n", _orig_marker_list, _marker_list);
1528 #endif
1529       if(_orig_marker_list && _marker_list)
1530       {
1531         MarkerList* orig = *_orig_marker_list;
1532         *_orig_marker_list = _marker_list;
1533         // Transfer the original pointer back to _marker_list so it can be deleted in the non-RT stage.
1534         _marker_list = orig;
1535       }
1536       // Currently no flags for this.
1537       //flags |= SC_MARKERS_REBUILT;
1538     }
1539     break;
1540 
1541     case SwitchMetronomeSettings:
1542     {
1543 #ifdef _PENDING_OPS_DEBUG_
1544       fprintf(stderr, "PendingOperationItem::executeRTStage SwitchMetronomeSettings: settings:%p val:%d\n", _bool_pointer, _boolA);
1545 #endif
1546       *_bool_pointer = _boolA;
1547       flags |= SC_METRONOME;
1548     }
1549     break;
1550 
1551     case ModifyMetronomeAccentMap:
1552     {
1553 #ifdef _PENDING_OPS_DEBUG_
1554       fprintf(stderr, "PendingOperationItem::executeRTStage ModifyMetronomeAccentMap: old map:%p new map:%p\n", _metroAccentsMap, _newMetroAccentsMap);
1555 #endif
1556       MetroAccentsMap* orig = *_metroAccentsMap;
1557       *_metroAccentsMap = _newMetroAccentsMap;
1558       // Transfer the original pointer back to _newMetroAccentsMap so it can be deleted in the non-RT stage.
1559       _newMetroAccentsMap = orig;
1560       flags |= SC_METRONOME;
1561     }
1562     break;
1563 
1564     case SetExternalSyncFlag:
1565     {
1566 #ifdef _PENDING_OPS_DEBUG_
1567       fprintf(stderr, "PendingOperationItem::executeRTStage SetExternalSyncFlag: pointer:%p val:%d\n", _bool_pointer, _boolA);
1568 #endif
1569       *_bool_pointer = _boolA;
1570       flags |= SC_EXTERNAL_MIDI_SYNC;
1571     }
1572     break;
1573 
1574     case SetUseJackTransport:
1575     {
1576 #ifdef _PENDING_OPS_DEBUG_
1577       fprintf(stderr, "PendingOperationItem::executeRTStage SetUseJackTransport: pointer:%p val:%d\n", _bool_pointer, _boolA);
1578 #endif
1579       *_bool_pointer = _boolA;
1580       flags |= SC_USE_JACK_TRANSPORT;
1581     }
1582     break;
1583 
1584     case SetUseMasterTrack:
1585     {
1586 #ifdef _PENDING_OPS_DEBUG_
1587       fprintf(stderr, "PendingOperationItem::executeRTStage SetUseMasterTrack: pointer:%p val:%d\n", _tempo_list, _boolA);
1588 #endif
1589       _tempo_list->setMasterFlag(0, _boolA);
1590       flags |= SC_MASTER;
1591     }
1592     break;
1593 
1594     case Uninitialized:
1595     break;
1596 
1597     default:
1598       ERROR_OPERATIONS(stderr, "PendingOperationItem::executeRTStage unknown type %d\n", _type);
1599     break;
1600   }
1601   return flags;
1602 }
1603 
executeNonRTStage()1604 SongChangedStruct_t PendingOperationItem::executeNonRTStage()
1605 {
1606   SongChangedStruct_t flags = 0;
1607   switch(_type)
1608   {
1609     case ModifyPartStart:
1610     case ModifyPartLength:
1611       // At this point _event_list contains all the items that were in the original event list, via swap(). Delete it now.
1612       if(_event_list)
1613         delete _event_list;
1614     break;
1615 
1616     case ModifyEventList:
1617       // At this point _event_list contains all the items that were in the original event list, via swap(). Delete it now.
1618       if(_event_list)
1619         delete _event_list;
1620     break;
1621 
1622     case ModifyMidiCtrlValList:
1623       // At this point _mcvl contains all the items that were in the original event list, via swap(). Delete it now.
1624       if(_mcvl)
1625         delete _mcvl;
1626     break;
1627 
1628     case AddRoute:
1629       if(MusEGlobal::song->connectJackRoutes(_src_route, _dst_route))
1630         flags |= SC_ROUTE;
1631     break;
1632 
1633     case DeleteRoute:
1634       if(MusEGlobal::song->connectJackRoutes(_src_route, _dst_route, true))
1635         flags |= SC_ROUTE;
1636     break;
1637 
1638     case ModifyTempoList:
1639       // At this point _tempo_list contains all the items that were in the original tempo list, via swap(). Delete it now.
1640       if(_tempo_list)
1641         delete _tempo_list;
1642     break;
1643 
1644 
1645     case ModifySigList:
1646       // At this point _sig_list contains all the items that were in the original sig list, via swap(). Delete it now.
1647       if(_sig_list)
1648         delete _sig_list;
1649     break;
1650 
1651 
1652     case ModifyKeyList:
1653       // At this point _key_list contains all the items that were in the original key list, via swap(). Delete it now.
1654       if(_key_list)
1655         delete _key_list;
1656     break;
1657 
1658 
1659     case ModifyLocalAudioConverterSettings:
1660       // At this point these are the original pointers that were replaced. Delete the original objects now.
1661       if(_audio_converter_settings)
1662         delete _audio_converter_settings;
1663     break;
1664 
1665     case ModifyLocalAudioConverter:
1666       // At this point these are the original pointers that were replaced. Delete the original objects now.
1667       if(_audio_converter)
1668         delete _audio_converter;
1669       if(_audio_converter_ui)
1670         delete _audio_converter_ui;
1671     break;
1672 
1673     case SetAudioConverterOfflineMode:
1674       // At this point this is the original pointer that were replaced. Delete the original object now.
1675       if(_audio_converter)
1676         delete _audio_converter;
1677     break;
1678 
1679     case ModifyDefaultAudioConverterSettings:
1680       // At this point this is the original pointer that was replaced. Delete the original object now.
1681       if(_audio_converter_settings)
1682         delete _audio_converter_settings;
1683     break;
1684 
1685     case ReplaceMidiInstrument:
1686       // At this point _midi_instrument is the original instrument that was replaced. Delete it now.
1687       if(_midi_instrument)
1688         delete _midi_instrument;
1689     break;
1690 
1691     case ModifyAudioCtrlValList:
1692       // At this point _aud_ctrl_list is the original list that was replaced. Delete it now.
1693       if(_aud_ctrl_list)
1694         delete _aud_ctrl_list;
1695     break;
1696 
1697     case ModifyTrackDrumMapItem:
1698       // Discard the operation, it has already completed.
1699       if(_drum_map_track_operation)
1700         delete _drum_map_track_operation;
1701     break;
1702 
1703     case ReplaceTrackDrumMapPatchList:
1704       // Discard the operation, it has already completed.
1705       if(_drum_map_track_patch_operation)
1706       {
1707         // At this point _workingItemPatchList is the original list that was replaced. Delete it now.
1708         if(_drum_map_track_patch_replace_operation->_workingItemPatchList)
1709           delete _drum_map_track_patch_replace_operation->_workingItemPatchList;
1710 
1711         delete _drum_map_track_patch_replace_operation;
1712       }
1713     break;
1714 
1715     case RemapDrumControllers:
1716       // Discard the operation, it has already completed.
1717       if(_midi_ctrl_val_remap_operation)
1718       {
1719         // At this point _midiCtrlValLists2bDeleted contains the original lists that were replaced. Delete them now.
1720         for(iMidiCtrlValLists2bDeleted_t imvld = _midi_ctrl_val_remap_operation->_midiCtrlValLists2bDeleted.begin();
1721             imvld != _midi_ctrl_val_remap_operation->_midiCtrlValLists2bDeleted.end(); ++imvld)
1722           delete *imvld;
1723 
1724         delete _midi_ctrl_val_remap_operation;
1725       }
1726     break;
1727 
1728     case ModifyAudioSamples:
1729       // At this point _newAudioSamples points to the original memory that was replaced. Delete it now.
1730       if(_newAudioSamples)
1731         delete _newAudioSamples;
1732     break;
1733 
1734     case ModifyMarkerList:
1735       // At this point _marker_list points to the original memory that was replaced. Delete it now.
1736       if(_marker_list)
1737         delete _marker_list;
1738     break;
1739 
1740     case ModifyMetronomeAccentMap:
1741       // At this point _newMetroAccentsMap is the original list that was replaced. Delete it now.
1742       if(_newMetroAccentsMap)
1743         delete _newMetroAccentsMap;
1744     break;
1745 
1746     default:
1747     break;
1748   }
1749   return flags;
1750 }
1751 
executeRTStage()1752 SongChangedStruct_t PendingOperationList::executeRTStage()
1753 {
1754   DEBUG_OPERATIONS(stderr, "PendingOperationList::executeRTStage executing...\n");
1755   for(iPendingOperation ip = begin(); ip != end(); ++ip)
1756     _sc_flags |= ip->executeRTStage();
1757 
1758   // To avoid doing this item by item, do it here.
1759   if(_sc_flags & (SC_TRACK_INSERTED | SC_TRACK_REMOVED | SC_ROUTE))
1760   {
1761     MusEGlobal::song->updateSoloStates();
1762     _sc_flags |= SC_SOLO;
1763   }
1764 
1765   // To avoid doing this item by item, do it here.
1766   StretchList* sl;
1767   for(iPendingOperation ip = begin(); ip != end(); ++ip)
1768   {
1769     const PendingOperationItem& poi = *ip;
1770     switch(poi._type)
1771     {
1772       //case PendingOperationItem::AddSamplerateRatioAt:
1773       //case PendingOperationItem::DeleteSamplerateRatioAt:
1774       //case PendingOperationItem::ModifySamplerateRatioAt:
1775       case PendingOperationItem::AddStretchListRatioAt:
1776       case PendingOperationItem::DeleteStretchListRatioAt:
1777       case PendingOperationItem::ModifyStretchListRatioAt:
1778       case PendingOperationItem::ModifyStretchListRatio:
1779         sl = poi._stretch_list;
1780         if(sl && !sl->isNormalized())
1781         {
1782           sl->normalizeListFrames();
1783           _sc_flags |= SC_AUDIO_STRETCH;
1784         }
1785       break;
1786 
1787       default:
1788       break;
1789     }
1790   }
1791 
1792   return _sc_flags;
1793 }
1794 
executeNonRTStage()1795 SongChangedStruct_t PendingOperationList::executeNonRTStage()
1796 {
1797   DEBUG_OPERATIONS(stderr, "PendingOperationList::executeNonRTStage executing...\n");
1798   for(iPendingOperation ip = begin(); ip != end(); ++ip)
1799     _sc_flags |= ip->executeNonRTStage();
1800   return _sc_flags;
1801 }
1802 
clear()1803 void PendingOperationList::clear()
1804 {
1805   _sc_flags = 0;
1806   _map.clear();
1807   std::list<PendingOperationItem>::clear();
1808   DEBUG_OPERATIONS(stderr, "PendingOperationList::clear * post map size:%d list size:%d\n", (int)_map.size(), (int)size());
1809 }
1810 
add(PendingOperationItem op)1811 bool PendingOperationList::add(PendingOperationItem op)
1812 {
1813   unsigned int t = op.getIndex();
1814 
1815   switch(op._type)
1816   {
1817     // For these special allocation ops, searching has already been done before hand. Just add them.
1818     case PendingOperationItem::AddMidiCtrlValList:
1819     {
1820       iPendingOperation iipo = insert(end(), op);
1821       _map.insert(std::pair<unsigned int, iPendingOperation>(t, iipo));
1822       return true;
1823     }
1824     break;
1825 
1826     default:
1827     break;
1828   }
1829 
1830   iPendingOperationSortedRange r = _map.equal_range(t);
1831   iPendingOperationSorted ipos = r.second;
1832   while(ipos != r.first)
1833   {
1834     --ipos;
1835     PendingOperationItem& poi = *ipos->second;
1836 
1837     switch(op._type)
1838     {
1839       case PendingOperationItem::ModifyDefaultAudioConverterSettings:
1840         if(poi._type == PendingOperationItem::ModifyDefaultAudioConverterSettings &&
1841            poi._audio_converter_settings == op._audio_converter_settings)
1842         {
1843           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyDefaultAudioConverterSettings. Ignoring.\n");
1844           return false;
1845         }
1846       break;
1847 
1848       case PendingOperationItem::ModifyLocalAudioConverterSettings:
1849         if(poi._type == PendingOperationItem::ModifyLocalAudioConverterSettings && *poi._sndFileR == *op._sndFileR &&
1850            poi._audio_converter_settings == op._audio_converter_settings)
1851         {
1852           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyLocalAudioConverterSettings. Ignoring.\n");
1853           return false;
1854         }
1855       break;
1856 
1857       case PendingOperationItem::ModifyLocalAudioConverter:
1858         if(poi._type == PendingOperationItem::ModifyLocalAudioConverter && *poi._sndFileR == *op._sndFileR &&
1859            poi._audio_converter == op._audio_converter &&
1860            poi._audio_converter_ui == op._audio_converter_ui)
1861         {
1862           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyLocalAudioConverter. Ignoring.\n");
1863           return false;
1864         }
1865       break;
1866 
1867       case PendingOperationItem::SetAudioConverterOfflineMode:
1868         if(poi._type == PendingOperationItem::SetAudioConverterOfflineMode && *poi._sndFileR == *op._sndFileR &&
1869            poi._audio_converter == op._audio_converter)
1870         {
1871           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double SetAudioConverterOfflineMode. Ignoring.\n");
1872           return false;
1873         }
1874       break;
1875 
1876 
1877       case PendingOperationItem::ModifyTrackDrumMapItem:
1878         if(poi._type == PendingOperationItem::ModifyTrackDrumMapItem &&
1879            poi._drum_map_track_operation == op._drum_map_track_operation)
1880         {
1881           fprintf(stderr, "MusE error: PendingOperationList::add(): Double ModifyTrackDrumMapItem. Ignoring.\n");
1882           return false;
1883         }
1884       break;
1885 
1886       case PendingOperationItem::ReplaceTrackDrumMapPatchList:
1887         if(poi._type == PendingOperationItem::ReplaceTrackDrumMapPatchList &&
1888            poi._drum_map_track_patch_replace_operation == op._drum_map_track_patch_replace_operation)
1889         {
1890           fprintf(stderr, "MusE error: PendingOperationList::add(): Double ReplaceTrackDrumMapPatchList. Ignoring.\n");
1891           return false;
1892         }
1893       break;
1894 
1895       case PendingOperationItem::RemapDrumControllers:
1896         if(poi._type == PendingOperationItem::RemapDrumControllers &&
1897            poi._midi_ctrl_val_remap_operation == op._midi_ctrl_val_remap_operation)
1898         {
1899           fprintf(stderr, "MusE error: PendingOperationList::add(): Double RemapDrumControllers. Ignoring.\n");
1900           return false;
1901         }
1902       break;
1903 
1904       case PendingOperationItem::UpdateDrumMaps:
1905         if(poi._type == PendingOperationItem::UpdateDrumMaps && poi._midi_port == op._midi_port)
1906         {
1907           fprintf(stderr, "MusE error: PendingOperationList::add(): Double UpdateDrumMaps. Ignoring.\n");
1908           return false;
1909         }
1910       break;
1911 
1912       case PendingOperationItem::UpdateSoloStates:
1913         if(poi._type == PendingOperationItem::UpdateSoloStates && poi._track_list == op._track_list)
1914         {
1915           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double UpdateSoloStates. Ignoring.\n");
1916           return false;
1917         }
1918       break;
1919 
1920       case PendingOperationItem::AddRoute:
1921         if(poi._type == PendingOperationItem::AddRoute && poi._src_route == op._src_route && poi._dst_route == op._dst_route)
1922         {
1923           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddRoute. Ignoring.\n");
1924           return false;
1925         }
1926       break;
1927 
1928       case PendingOperationItem::DeleteRoute:
1929         if(poi._type == PendingOperationItem::DeleteRoute && poi._src_route == op._src_route && poi._dst_route == op._dst_route)
1930         {
1931           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteRoute. Ignoring.\n");
1932           return false;
1933         }
1934       break;
1935 
1936       case PendingOperationItem::AddRouteNode:
1937         if(poi._type == PendingOperationItem::AddRouteNode && poi._route_list == op._route_list && poi._src_route == op._src_route)
1938         {
1939           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddRouteNode. Ignoring.\n");
1940           return false;
1941         }
1942       break;
1943 
1944       case PendingOperationItem::DeleteRouteNode:
1945         if(poi._type == PendingOperationItem::DeleteRouteNode && poi._route_list == op._route_list && poi._iRoute == op._iRoute)
1946         {
1947           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteRouteNode. Ignoring.\n");
1948           return false;
1949         }
1950       break;
1951 
1952       case PendingOperationItem::ModifyRouteNode:
1953         if(poi._type == PendingOperationItem::ModifyRouteNode && poi._src_route == op._src_route && poi._dst_route_pointer == op._dst_route_pointer)
1954         {
1955           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyRouteNode. Ignoring.\n");
1956           return false;
1957         }
1958       break;
1959 
1960 
1961       case PendingOperationItem::AddAuxSendValue:
1962         if(poi._type == PendingOperationItem::AddAuxSendValue && poi._aux_send_value_list == op._aux_send_value_list)
1963         {
1964           // Do nothing. So far.
1965         }
1966       break;
1967 
1968       case PendingOperationItem::AddMidiInstrument:
1969         if(poi._type == PendingOperationItem::AddMidiInstrument && poi._midi_instrument_list == op._midi_instrument_list &&
1970            poi._midi_instrument == op._midi_instrument)
1971         {
1972           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddMidiInstrument. Ignoring.\n");
1973           return false;
1974         }
1975       break;
1976 
1977       case PendingOperationItem::DeleteMidiInstrument:
1978         if(poi._type == PendingOperationItem::DeleteMidiInstrument && poi._midi_instrument_list == op._midi_instrument_list &&
1979            poi._iMidiInstrument == op._iMidiInstrument)
1980         {
1981           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteMidiInstrument. Ignoring.\n");
1982           return false;
1983         }
1984       break;
1985 
1986       case PendingOperationItem::ReplaceMidiInstrument:
1987         if(poi._type == PendingOperationItem::ReplaceMidiInstrument && poi._midi_instrument_list == op._midi_instrument_list &&
1988            (poi._midi_instrument == op._midi_instrument || poi._iMidiInstrument == op._iMidiInstrument))
1989         {
1990           fprintf(stderr, "MusE error: PendingOperationList::add(): Double ReplaceMidiInstrument. Ignoring.\n");
1991           return false;
1992         }
1993       break;
1994 
1995       case PendingOperationItem::AddMidiDevice:
1996         if(poi._type == PendingOperationItem::AddMidiDevice && poi._midi_device_list == op._midi_device_list && poi._midi_device == op._midi_device)
1997         {
1998           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddMidiDevice. Ignoring.\n");
1999           return false;
2000         }
2001       break;
2002 
2003       case PendingOperationItem::DeleteMidiDevice:
2004         if(poi._type == PendingOperationItem::DeleteMidiDevice && poi._midi_device_list == op._midi_device_list && poi._iMidiDevice == op._iMidiDevice)
2005         {
2006           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteMidiDevice. Ignoring.\n");
2007           return false;
2008         }
2009       break;
2010 
2011       case PendingOperationItem::ModifyMidiDeviceAddress:
2012         if(poi._type == PendingOperationItem::ModifyMidiDeviceAddress && poi._midi_device == op._midi_device &&
2013            poi._address_client == op._address_client && poi._address_port == op._address_port)
2014         {
2015           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyMidiDeviceAddress. Ignoring.\n");
2016           return false;
2017         }
2018       break;
2019 
2020       case PendingOperationItem::ModifyMidiDeviceFlags:
2021         if(poi._type == PendingOperationItem::ModifyMidiDeviceFlags && poi._midi_device == op._midi_device &&
2022            poi._rw_flags == op._rw_flags && poi._open_flags == op._open_flags)
2023         {
2024           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyMidiDeviceFlags. Ignoring.\n");
2025           return false;
2026         }
2027       break;
2028 
2029       case PendingOperationItem::ModifyMidiDeviceName:
2030         if(poi._type == PendingOperationItem::ModifyMidiDeviceName && poi._midi_device == op._midi_device &&
2031            poi._name == op._name)
2032         {
2033           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyMidiDeviceName. Ignoring.\n");
2034           return false;
2035         }
2036       break;
2037 
2038       case PendingOperationItem::SetInstrument:
2039         if(poi._type == PendingOperationItem::SetInstrument && poi._midi_port == op._midi_port &&
2040            poi._midi_instrument == op._midi_instrument)
2041         {
2042           fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetInstrument. Ignoring.\n");
2043           return false;
2044         }
2045       break;
2046 
2047 
2048       case PendingOperationItem::AddTrack:
2049         if(poi._type == PendingOperationItem::AddTrack && poi._track_list == op._track_list && poi._track == op._track)
2050         {
2051           // Simply replace the insert point.
2052           poi._insert_at = op._insert_at;
2053           // An operation will still take place.
2054           return true;
2055         }
2056         else if(poi._type == PendingOperationItem::DeleteTrack && poi._track_list == op._track_list && poi._track == op._track)
2057         {
2058           // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command.
2059           //erase(ipos->second);
2060           //_map.erase(ipos);
2061           // No operation will take place.
2062           //return false;
2063         }
2064       break;
2065 
2066       case PendingOperationItem::DeleteTrack:
2067         if(poi._type == PendingOperationItem::DeleteTrack && poi._track_list == op._track_list && poi._track == op._track)
2068         {
2069           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteTrack. Ignoring.\n");
2070           return false;
2071         }
2072         else if(poi._type == PendingOperationItem::AddTrack && poi._track_list == op._track_list && poi._track == op._track)
2073         {
2074           // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
2075           //erase(ipos->second);
2076           //_map.erase(ipos);
2077           // No operation will take place.
2078           //return false;
2079         }
2080       break;
2081 
2082       case PendingOperationItem::MoveTrack:
2083         if(poi._type == PendingOperationItem::MoveTrack && poi._track == op._track && poi._track_list == op._track_list)
2084         {
2085           // Simply replace the 'to' index.
2086           poi._to_idx = op._to_idx;
2087           // An operation will still take place.
2088           return true;
2089         }
2090       break;
2091 
2092       case PendingOperationItem::ModifyTrackName:
2093         if(poi._type == PendingOperationItem::ModifyTrackName && poi._track == op._track &&
2094            poi._name == op._name)
2095         {
2096           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyTrackName. Ignoring.\n");
2097           return false;
2098         }
2099       break;
2100 
2101       case PendingOperationItem::SetTrackRecord:
2102         if(poi._type == PendingOperationItem::SetTrackRecord && poi._track == op._track)
2103         {
2104           if(poi._boolA == op._boolA)
2105           {
2106             ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double SetTrackRecord. Ignoring.\n");
2107             return false;
2108           }
2109           else
2110           {
2111             // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
2112             erase(ipos->second);
2113             _map.erase(ipos);
2114             // No operation will take place.
2115             return false;
2116           }
2117         }
2118       break;
2119 
2120       case PendingOperationItem::SetTrackMute:
2121         if(poi._type == PendingOperationItem::SetTrackMute && poi._track == op._track)
2122         {
2123           if(poi._boolA == op._boolA)
2124           {
2125             ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double SetTrackMute. Ignoring.\n");
2126             return false;
2127           }
2128           else
2129           {
2130             // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
2131             erase(ipos->second);
2132             _map.erase(ipos);
2133             // No operation will take place.
2134             return false;
2135           }
2136         }
2137       break;
2138 
2139       case PendingOperationItem::SetTrackSolo:
2140         if(poi._type == PendingOperationItem::SetTrackSolo && poi._track == op._track)
2141         {
2142           if(poi._boolA == op._boolA)
2143           {
2144             ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double SetTrackSolo. Ignoring.\n");
2145             return false;
2146           }
2147           else
2148           {
2149             // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
2150             erase(ipos->second);
2151             _map.erase(ipos);
2152             // No operation will take place.
2153             return false;
2154           }
2155         }
2156       break;
2157 
2158       case PendingOperationItem::SetTrackRecMonitor:
2159         if(poi._type == PendingOperationItem::SetTrackRecMonitor && poi._track == op._track)
2160         {
2161           if(poi._boolA == op._boolA)
2162           {
2163             fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetTrackRecMonitor. Ignoring.\n");
2164             return false;
2165           }
2166           else
2167           {
2168             // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
2169             erase(ipos->second);
2170             _map.erase(ipos);
2171             // No operation will take place.
2172             return false;
2173           }
2174         }
2175       break;
2176 
2177       case PendingOperationItem::SetTrackOff:
2178         if(poi._type == PendingOperationItem::SetTrackOff && poi._track == op._track)
2179         {
2180           if(poi._boolA == op._boolA)
2181           {
2182             fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetTrackOff. Ignoring.\n");
2183             return false;
2184           }
2185           else
2186           {
2187             // On/off followed by off/on is useless. Cancel out the on/off + off/on by erasing the command.
2188             erase(ipos->second);
2189             _map.erase(ipos);
2190             // No operation will take place.
2191             return false;
2192           }
2193         }
2194       break;
2195 
2196       case PendingOperationItem::AddPart:
2197         if(poi._type == PendingOperationItem::AddPart && poi._part_list == op._part_list && poi._part == op._part)
2198         {
2199           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddPart. Ignoring.\n");
2200           return false;
2201         }
2202         else if(poi._type == PendingOperationItem::DeletePart && poi._part_list == op._part_list && poi._iPart->second == op._part)
2203         {
2204           // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command.
2205           erase(ipos->second);
2206           _map.erase(ipos);
2207           // No operation will take place.
2208           return false;
2209         }
2210       break;
2211 
2212       case PendingOperationItem::DeletePart:
2213         if(poi._type == PendingOperationItem::DeletePart && poi._part_list == op._part_list && poi._iPart->second == op._iPart->second)
2214         {
2215           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeletePart. Ignoring.\n");
2216           return false;
2217         }
2218         else if(poi._type == PendingOperationItem::AddPart && poi._part_list == op._part_list && poi._part == op._iPart->second)
2219         {
2220           // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
2221           erase(ipos->second);
2222           _map.erase(ipos);
2223           // No operation will take place.
2224           return false;
2225         }
2226       break;
2227 
2228       case PendingOperationItem::SelectPart:
2229         if(poi._type == PendingOperationItem::SelectPart && poi._part == op._part)
2230         {
2231           // Simply replace the value.
2232           poi._boolA = op._boolA;
2233           // An operation will still take place.
2234           return true;
2235         }
2236       break;
2237 
2238       case PendingOperationItem::MovePart:
2239         if(poi._type == PendingOperationItem::MovePart && poi._part == op._part)
2240         {
2241           // Simply replace the values.
2242           poi._iPart = op._iPart;
2243           poi._track = op._track;
2244           poi._posLenVal = op._posLenVal;
2245           // An operation will still take place.
2246           return true;
2247         }
2248       break;
2249 
2250       case PendingOperationItem::ModifyPartStart:
2251         if(poi._type == PendingOperationItem::ModifyPartStart)
2252         {
2253           // If the given list is not null and is already part of a previous ModifyPartStart command,
2254           //  it's an error, the list would be deleted twice.
2255           if(poi._part != op._part && op._event_list && op._event_list == poi._event_list)
2256           {
2257             ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): ModifyPartStart: Same _event_list for two different parts. Ignoring.\n");
2258             return false;
2259           }
2260 
2261           if(poi._part == op._part)
2262           {
2263             // From here on in this block no matter what, we are re-using the existing command.
2264 
2265             // Simply replace the values.
2266             poi._posLenVal = op._posLenVal;
2267             poi._lenVal = op._lenVal;
2268 
2269             // If a list was given use it otherwise if no list was given don't touch the existing list.
2270             if(op._event_list)
2271             {
2272               // If the given list is the same as the existing list, it's really an error. We'll let it go but don't touch the existing list.
2273               // It should be safe to proceed without worrying about deleting the existing or given list here since it would be impossible
2274               //  to allocate the same pointer twice, beforehand.
2275               if(op._event_list == poi._event_list)
2276               {
2277                 //ERROR_OPERATIONS(stderr, "MusE warning: PendingOperationList::add(): ModifyPartStart: Double _event_list. Ignoring second list.\n");
2278                 //return false;
2279               }
2280               else
2281               {
2282                 // Done with the existing original replacement list. If it exists, delete it.
2283                 if(poi._event_list)
2284                   delete poi._event_list;
2285                 // Replace the existing list pointer with the given one.
2286                 poi._event_list = op._event_list;
2287               }
2288             }
2289             // An operation will still take place.
2290             return true;
2291           }
2292         }
2293       break;
2294 
2295       case PendingOperationItem::ModifyPartLength:
2296         if(poi._type == PendingOperationItem::ModifyPartLength)
2297         {
2298           // If the given list is not null and is already part of a previous ModifyPartLength command,
2299           //  it's an error, the list would be deleted twice.
2300           if(poi._part != op._part && op._event_list && op._event_list == poi._event_list)
2301           {
2302             ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): ModifyPartLength: Same _event_list for two different parts. Ignoring.\n");
2303             return false;
2304           }
2305 
2306           if(poi._part == op._part)
2307           {
2308             // From here on in this block no matter what, we are re-using the existing command.
2309 
2310             // Simply replace the value.
2311             poi._posLenVal = op._posLenVal;
2312 
2313             // If a list was given use it otherwise if no list was given don't touch the existing list.
2314             if(op._event_list)
2315             {
2316               // If the given list is the same as the existing list, it's really an error. We'll let it go but don't touch the existing list.
2317               // It should be safe to proceed without worrying about deleting the existing or given list here since it would be impossible
2318               //  to allocate the same pointer twice, beforehand.
2319               if(op._event_list == poi._event_list)
2320               {
2321                 //ERROR_OPERATIONS(stderr, "MusE warning: PendingOperationList::add(): ModifyPartLength: Double _event_list. Ignoring second list.\n");
2322                 //return false;
2323               }
2324               else
2325               {
2326                 // Done with the existing original replacement list. If it exists, delete it.
2327                 if(poi._event_list)
2328                   delete poi._event_list;
2329                 // Replace the existing list pointer with the given one.
2330                 poi._event_list = op._event_list;
2331               }
2332             }
2333             // An operation will still take place.
2334             return true;
2335           }
2336         }
2337       break;
2338 
2339       case PendingOperationItem::ModifyPartName:
2340         if(poi._type == PendingOperationItem::ModifyPartName && poi._part == op._part &&
2341            poi._name == op._name)
2342         {
2343           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double ModifyPartName. Ignoring.\n");
2344           return false;
2345         }
2346       break;
2347 
2348 
2349       case PendingOperationItem::AddEvent:
2350         if(poi._type == PendingOperationItem::AddEvent && poi._part == op._part && poi._ev == op._ev)
2351         {
2352           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double AddEvent. Ignoring.\n");
2353           return false;
2354         }
2355         else if(poi._type == PendingOperationItem::DeleteEvent && poi._part == op._part && poi._iev->second == op._ev)
2356         {
2357           // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command.
2358           erase(ipos->second);
2359           _map.erase(ipos);
2360           // No operation will take place.
2361           return false;
2362         }
2363       break;
2364 
2365       case PendingOperationItem::DeleteEvent:
2366         if(poi._type == PendingOperationItem::DeleteEvent && poi._part == op._part && poi._iev->second == op._iev->second)
2367         {
2368           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteEvent. Ignoring.\n");
2369           return false;
2370         }
2371         else if(poi._type == PendingOperationItem::AddEvent && poi._part == op._part && poi._ev == op._iev->second)
2372         {
2373           // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
2374           erase(ipos->second);
2375           _map.erase(ipos);
2376           // No operation will take place.
2377           return false;
2378         }
2379       break;
2380 
2381       case PendingOperationItem::SelectEvent:
2382         if(poi._type == PendingOperationItem::SelectEvent &&
2383            poi._part == op._part && poi._ev == op._ev)
2384         {
2385           // Simply replace the value.
2386           poi._intA = op._intA;
2387           // An operation will still take place.
2388           return true;
2389         }
2390       break;
2391 
2392       case PendingOperationItem::ModifyEventList:
2393 // TODO Not quite right yet.
2394 //         if(poi._type == PendingOperationItem::ModifyEventList &&
2395 //           // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc).
2396 //           (poi._orig_event_list == op._orig_event_list || poi._event_list == op._event_list))
2397 //         {
2398 //           // Simply replace the list.
2399 //           poi._event_list = op._event_list;
2400       break;
2401 
2402 
2403       case PendingOperationItem::ModifyMidiCtrlValList:
2404 // TODO Not quite right yet.
2405 //         if(poi._type == PendingOperationItem::ModifyMidiCtrlValList &&
2406 //           // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc).
2407 //           (poi._orig_mcvl == op._orig_mcvl || poi._mcvl == op._mcvl))
2408 //         {
2409 //           // Simply replace the list.
2410 //           poi._mcvl = op._mcvl;
2411       break;
2412 
2413       case PendingOperationItem::AddMidiCtrlVal:
2414         if(poi._type == PendingOperationItem::DeleteMidiCtrlVal &&
2415            poi._mcvl == op._mcvl &&
2416            poi._imcv->second.part == op._part &&
2417            poi._imcv->second.val == op._intB)
2418         {
2419           // Delete followed by add is useless. Cancel out the delete + add by erasing the delete command.
2420           erase(ipos->second);
2421           _map.erase(ipos);
2422           // No operation will take place.
2423           return false;
2424         }
2425       break;
2426 
2427       case PendingOperationItem::DeleteMidiCtrlVal:
2428         // Be sure _intB is set.
2429         if(poi._type == PendingOperationItem::AddMidiCtrlVal &&
2430            poi._mcvl == op._mcvl &&
2431            poi._part == op._imcv->second.part &&
2432            poi._intB == op._imcv->second.val)
2433         {
2434           // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
2435           erase(ipos->second);
2436           _map.erase(ipos);
2437           // No operation will take place.
2438           return false;
2439         }
2440       break;
2441 
2442       case PendingOperationItem::ModifyMidiCtrlVal:
2443 // TODO FIXME Finish this
2444 
2445            // Be sure _intB/A is set
2446 //         if(poi._type == PendingOperationItem::ModifyMidiCtrlVal &&
2447 //            poi._mcvl == op._mcvl &&
2448 //            poi._imcv->second.part == op._imcv->second.part &&
2449 //            poi._imcv->second.val == op._imcv->second.val)
2450 //         {
2451 //           // Simply replace the value.
2452 //           poi._intA = op._intA;
2453 //           return true;
2454 //         }
2455 //         else if(poi._type == PendingOperationItem::DeleteMidiCtrlVal && poi._mcvl == op._mcvl && poi._imcv->second.part == op._imcv->second.part)
2456 //         {
2457 //           // Transform existing delete command into a modify command.
2458 //           poi._type = PendingOperationItem::ModifyMidiCtrlVal;
2459 //           poi._intA = op._intA;
2460 //           return true;
2461 //         }
2462 //         else if(poi._type == PendingOperationItem::AddMidiCtrlVal && poi._mcvl == op._mcvl && poi._part == op._imcv->second.part)
2463 //         {
2464 //           // Simply replace the add value with the modify value.
2465 //           poi._intB = op._intA;
2466 //           return true;
2467 //         }
2468       break;
2469 
2470 
2471       case PendingOperationItem::ModifyAudioCtrlValList:
2472         if(poi._type == PendingOperationItem::ModifyAudioCtrlValList &&
2473           // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc).
2474           (poi._iCtrlList->second == op._iCtrlList->second || poi._aud_ctrl_list == op._iCtrlList->second))
2475         {
2476           // Simply replace the list.
2477           poi._aud_ctrl_list = op._aud_ctrl_list;
2478           // An operation will still take place.
2479           return true;
2480         }
2481       break;
2482 
2483       case PendingOperationItem::AddAudioCtrlVal:
2484         if(poi._type == PendingOperationItem::AddAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list)
2485         {
2486           // Simply replace the value.
2487           poi._ctl_dbl_val = op._ctl_dbl_val;
2488           // An operation will still take place.
2489           return true;
2490         }
2491         else if(poi._type == PendingOperationItem::DeleteAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list)
2492         {
2493           // Transform existing delete command into a modify command.
2494           poi._type = PendingOperationItem::ModifyAudioCtrlVal;
2495           poi._ctl_dbl_val = op._ctl_dbl_val;
2496           // An operation will still take place.
2497           return true;
2498         }
2499         else if(poi._type == PendingOperationItem::ModifyAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list)
2500         {
2501           // Simply replace the value.
2502           poi._ctl_dbl_val = op._ctl_dbl_val;
2503           // An operation will still take place.
2504           return true;
2505         }
2506       break;
2507 
2508       case PendingOperationItem::DeleteAudioCtrlVal:
2509         if(poi._type == PendingOperationItem::DeleteAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list)
2510         {
2511           // Multiple delete commands not allowed!
2512           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteAudioCtrlVal. Ignoring.\n");
2513           return false;
2514         }
2515         else if(poi._type == PendingOperationItem::AddAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list)
2516         {
2517           // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
2518           erase(ipos->second);
2519           _map.erase(ipos);
2520           // No operation will take place.
2521           return false;
2522         }
2523         else if(poi._type == PendingOperationItem::ModifyAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list)
2524         {
2525           // Modify followed by delete is equivalent to just deleting.
2526           // Transform existing modify command into a delete command.
2527           poi._type = PendingOperationItem::DeleteAudioCtrlVal;
2528           // An operation will still take place.
2529           return true;
2530         }
2531       break;
2532 
2533       case PendingOperationItem::ModifyAudioCtrlVal:
2534         if(poi._type == PendingOperationItem::ModifyAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list)
2535         {
2536           // Simply replace the value.
2537           poi._ctl_dbl_val = op._ctl_dbl_val;
2538           // An operation will still take place.
2539           return true;
2540         }
2541         else if(poi._type == PendingOperationItem::DeleteAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list)
2542         {
2543           // Transform existing delete command into a modify command.
2544           poi._type = PendingOperationItem::ModifyAudioCtrlVal;
2545           poi._ctl_dbl_val = op._ctl_dbl_val;
2546           // An operation will still take place.
2547           return true;
2548         }
2549         else if(poi._type == PendingOperationItem::AddAudioCtrlVal && poi._aud_ctrl_list == op._aud_ctrl_list)
2550         {
2551           // Simply replace the add value with the modify value.
2552           poi._ctl_dbl_val = op._ctl_dbl_val;
2553           // An operation will still take place.
2554           return true;
2555         }
2556       break;
2557 
2558 
2559       case PendingOperationItem::ModifyTempoList:
2560 // TODO Not quite right yet.
2561 //         if(poi._type == PendingOperationItem::ModifyTempoList &&
2562 //           // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc).
2563 //           (poi._orig_tempo_list == op._orig_tempo_list || poi._tempo_list == op._tempo_list))
2564 //         {
2565 //           // Simply replace the list.
2566 //           poi._tempo_list = op._tempo_list;
2567       break;
2568 
2569 
2570       case PendingOperationItem::SetStaticTempo:
2571 #ifdef _PENDING_OPS_DEBUG_
2572         fprintf(stderr, "PendingOperationList::add() SetStaticTempo\n");
2573 #endif
2574         if(poi._type == PendingOperationItem::SetStaticTempo && poi._tempo_list == op._tempo_list)
2575         {
2576           // Simply replace the value.
2577           poi._intA = op._intA;
2578           // An operation will still take place.
2579           return true;
2580         }
2581       break;
2582 
2583       case PendingOperationItem::SetGlobalTempo:
2584         DEBUG_OPERATIONS(stderr, "PendingOperationList::add() SetGlobalTempo\n");
2585         if(poi._type == PendingOperationItem::SetGlobalTempo && poi._tempo_list == op._tempo_list)
2586         {
2587           // Simply replace the new value.
2588           poi._intA = op._intA;
2589           // An operation will still take place.
2590           return true;
2591         }
2592       break;
2593 
2594 
2595       case PendingOperationItem::ModifySigList:
2596 // TODO Not quite right yet.
2597 //         if(poi._type == PendingOperationItem::ModifySigList &&
2598 //           // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc).
2599 //           (poi._orig_sig_list == op._orig_sig_list || poi._sig_list == op._sig_list))
2600 //         {
2601 //           // Simply replace the list.
2602 //           poi._sig_list = op._sig_list;
2603       break;
2604 
2605 
2606       case PendingOperationItem::ModifyKeyList:
2607 // TODO Not quite right yet.
2608 //         if(poi._type == PendingOperationItem::ModifyKeyList &&
2609 //           // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc).
2610 //           (poi._orig_key_list == op._orig_key_list || poi._key_list == op._key_list))
2611 //         {
2612 //           // Simply replace the list.
2613 //           poi._key_list = op._key_list;
2614       break;
2615 
2616 
2617       case PendingOperationItem::AddStretchListRatioAt:
2618         if(poi._type == PendingOperationItem::AddStretchListRatioAt && poi._stretch_list == op._stretch_list &&
2619            poi._stretch_type == op._stretch_type)
2620         {
2621           // Simply replace the value.
2622           poi._audio_converter_value = op._audio_converter_value;
2623           return true;
2624         }
2625 // Todo?
2626 //         else if(poi._type == PendingOperationItem::DeleteStretchListRatioAt && poi._stretch_list == op._stretch_list &&
2627 //            poi._stretch_type == op._stretch_type)
2628 //         {
2629 //           // Transform existing delete command into a modify command.
2630 //           poi._type = PendingOperationItem::ModifyStretchListRatioAt;
2631 //           poi._audio_converter_value = op._audio_converter_value;
2632 //           return true;
2633 //         }
2634         else if(poi._type == PendingOperationItem::ModifyStretchListRatioAt && poi._stretch_list == op._stretch_list &&
2635            poi._stretch_type == op._stretch_type)
2636         {
2637           // Simply replace the value.
2638           poi._audio_converter_value = op._audio_converter_value;
2639           return true;
2640         }
2641       break;
2642 
2643       case PendingOperationItem::DeleteStretchListRatioAt:
2644         if(poi._type == PendingOperationItem::DeleteStretchListRatioAt && poi._stretch_list == op._stretch_list &&
2645            poi._stretch_type == op._stretch_type)
2646         {
2647           // Multiple delete commands not allowed!
2648           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double DeleteStretchRatioAt. Ignoring.\n");
2649           return false;
2650         }
2651 // Todo?
2652 //         else if(poi._type == PendingOperationItem::AddStretchListRatioAt && poi._stretch_list == op._stretch_list)
2653 //         {
2654 //           // Add followed by delete is useless. Cancel out the add + delete by erasing the add command.
2655 //           erase(ipos->second);
2656 //           _map.erase(ipos);
2657 //           return true;
2658 //         }
2659 //         else if(poi._type == PendingOperationItem::ModifyStretchListRatioAt && poi._stretch_list == op._stretch_list)
2660 //         {
2661 //           // Modify followed by delete is equivalent to just deleting.
2662 //           // Transform existing modify command into a delete command.
2663 //           poi._type = PendingOperationItem::DeleteStretchListRatioAt;
2664 //           return true;
2665 //         }
2666       break;
2667 
2668       case PendingOperationItem::ModifyStretchListRatioAt:
2669         if(poi._type == PendingOperationItem::ModifyStretchListRatioAt && poi._stretch_list == op._stretch_list &&
2670            poi._stretch_type == op._stretch_type)
2671         {
2672           // Simply replace the value.
2673           poi._audio_converter_value = op._audio_converter_value;
2674           return true;
2675         }
2676 // Todo?
2677 //         else if(poi._type == PendingOperationItem::DeleteStretchListRatioAt && poi._stretch_list == op._stretch_list &&
2678 //            poi._stretch_type == op._stretch_type)
2679 //         {
2680 //           // Transform existing delete command into a modify command.
2681 //           poi._type = PendingOperationItem::ModifyStretchListRatioAt;
2682 //           poi._audio_converter_value = op._audio_converter_value;
2683 //           return true;
2684 //         }
2685         else if(poi._type == PendingOperationItem::AddStretchListRatioAt && poi._stretch_list == op._stretch_list &&
2686            poi._stretch_type == op._stretch_type)
2687         {
2688           // Simply replace the add value with the modify value.
2689           poi._audio_converter_value = op._audio_converter_value;
2690           return true;
2691         }
2692       break;
2693 
2694 
2695       case PendingOperationItem::ModifyStretchListRatio:
2696         if(poi._type == PendingOperationItem::ModifyStretchListRatio && poi._stretch_list == op._stretch_list &&
2697            poi._stretch_type == op._stretch_type)
2698         {
2699           // Simply replace the value.
2700           poi._audio_converter_value = op._audio_converter_value;
2701           return true;
2702         }
2703       break;
2704 
2705 
2706       case PendingOperationItem::ModifySongLength:
2707         DEBUG_OPERATIONS(stderr, "PendingOperationList::add() ModifySongLength\n");
2708         if(poi._type == PendingOperationItem::ModifySongLength)
2709         {
2710           // Simply replace the value.
2711           poi._intA = op._intA;
2712           // An operation will still take place.
2713           return true;
2714         }
2715       break;
2716 
2717       case PendingOperationItem::EnableAllAudioControllers:
2718         DEBUG_OPERATIONS(stderr, "PendingOperationList::add() EnableAllAudioControllers\n");
2719         if(poi._type == PendingOperationItem::EnableAllAudioControllers)
2720         {
2721           ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Double EnableAllAudioControllers. Ignoring.\n");
2722           return false;
2723         }
2724       break;
2725 
2726       case PendingOperationItem::GlobalSelectAllEvents:
2727 #ifdef _PENDING_OPS_DEBUG_
2728         fprintf(stderr, "PendingOperationList::add() GlobalSelectAllEvents\n");
2729 #endif
2730         if(poi._type == PendingOperationItem::GlobalSelectAllEvents && poi._track_list == op._track_list)
2731         {
2732           if(poi._boolA == op._boolA)
2733           {
2734             fprintf(stderr, "MusE error: PendingOperationList::add(): Double GlobalSelectAllEvents. Ignoring.\n");
2735             return false;
2736           }
2737           else
2738           {
2739             // Special: Do not 'cancel' out this one. The selecions may need to affect all events.
2740             // Simply replace the value.
2741             poi._boolA = op._boolA;
2742             // An operation will still take place.
2743             return true;
2744           }
2745         }
2746       break;
2747 
2748       case PendingOperationItem::ModifyAudioSamples:
2749 // TODO Not quite right yet.
2750 //         if(poi._type == PendingOperationItem::ModifyAudioSamples &&
2751 //           // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc).
2752 //           poi._audioSamplesPointer && op._audioSamplesPointer &&
2753 //           (*poi._audioSamplesPointer == *op._audioSamplesPointer || poi._newAudioSamples == op._newAudioSamples))
2754 //         {
2755 //           // Simply replace the list.
2756 //           poi._newAudioSamples = op._newAudioSamples;
2757 //           poi._newAudioSamplesLen = op._newAudioSamplesLen;
2758 //           return true;
2759 //         }
2760       break;
2761 
2762       case PendingOperationItem::ModifyMarkerList:
2763 // TODO Not quite right yet.
2764 //         if(poi._type == PendingOperationItem::ModifyMarkerList &&
2765 //           // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc).
2766 //           poi._orig_marker_list && op._orig_marker_list &&
2767 //           (*poi._orig_marker_list == *op._orig_marker_list || poi._marker_list == op._marker_list))
2768 //         {
2769 //           // Simply replace the list.
2770 //           poi._newAudioSamples = op._newAudioSamples;
2771 //           poi._newAudioSamplesLen = op._newAudioSamplesLen;
2772       break;
2773 
2774       case PendingOperationItem::SwitchMetronomeSettings:
2775         if(poi._type == PendingOperationItem::SwitchMetronomeSettings &&
2776           (poi._bool_pointer == op._bool_pointer))
2777         {
2778           if(poi._boolA == op._boolA)
2779           {
2780             fprintf(stderr, "MusE error: PendingOperationList::add(): Double SwitchMetronomeSettings. Ignoring.\n");
2781             // No operation will take place.
2782             return false;
2783           }
2784           else
2785           {
2786             // Enable or disable followed by disable or enable is useless. Cancel out both by erasing the command.
2787             erase(ipos->second);
2788             _map.erase(ipos);
2789             // No operation will take place.
2790             return false;
2791           }
2792         }
2793       break;
2794 
2795       case PendingOperationItem::ModifyMetronomeAccentMap:
2796 // TODO Not quite right yet.
2797 //         if(poi._type == PendingOperationItem::ModifyMetronomeAccentMap &&
2798 //           // If attempting to repeatedly modify the same list, or, if progressively modifying (list to list to list etc).
2799 //           (poi._metroAccentsMap == op._metroAccentsMap || poi._newMetroAccentsMap == op._newMetroAccentsMap))
2800 //         {
2801 //           // Simply replace the list.
2802 //           poi._newMetroAccentsMap = op._newMetroAccentsMap;
2803 //           // An operation will still take place.
2804 //           return true;
2805 //         }
2806       break;
2807 
2808       case PendingOperationItem::SetExternalSyncFlag:
2809         if(poi._type == PendingOperationItem::SetExternalSyncFlag &&
2810           (poi._bool_pointer == op._bool_pointer))
2811         {
2812           if(poi._boolA == op._boolA)
2813           {
2814             fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetExternalSyncFlag. Ignoring.\n");
2815             // No operation will take place.
2816             return false;
2817           }
2818           else
2819           {
2820             // Enable or disable followed by disable or enable is useless. Cancel out both by erasing the command.
2821             erase(ipos->second);
2822             _map.erase(ipos);
2823             // No operation will take place.
2824             return false;
2825           }
2826         }
2827       break;
2828 
2829       case PendingOperationItem::SetUseJackTransport:
2830         if(poi._type == PendingOperationItem::SetUseJackTransport &&
2831           (poi._bool_pointer == op._bool_pointer))
2832         {
2833           if(poi._boolA == op._boolA)
2834           {
2835             fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetUseJackTransport. Ignoring.\n");
2836             // No operation will take place.
2837             return false;
2838           }
2839           else
2840           {
2841             // Enable or disable followed by disable or enable is useless. Cancel out both by erasing the command.
2842             erase(ipos->second);
2843             _map.erase(ipos);
2844             // No operation will take place.
2845             return false;
2846           }
2847         }
2848       break;
2849 
2850       case PendingOperationItem::SetUseMasterTrack:
2851         if(poi._type == PendingOperationItem::SetUseMasterTrack &&
2852           (poi._tempo_list == op._tempo_list))
2853         {
2854           if(poi._boolA == op._boolA)
2855           {
2856             fprintf(stderr, "MusE error: PendingOperationList::add(): Double SetUseMasterTrack. Ignoring.\n");
2857             // No operation will take place.
2858             return false;
2859           }
2860           else
2861           {
2862             // Enable or disable followed by disable or enable is useless. Cancel out both by erasing the command.
2863             erase(ipos->second);
2864             _map.erase(ipos);
2865             // No operation will take place.
2866             return false;
2867           }
2868         }
2869       break;
2870 
2871       case PendingOperationItem::Uninitialized:
2872         ERROR_OPERATIONS(stderr, "MusE error: PendingOperationList::add(): Uninitialized item. Ignoring.\n");
2873         return false;
2874       break;
2875 
2876       default:
2877       break;
2878     }
2879   }
2880 
2881   iPendingOperation iipo = insert(end(), op);
2882   _map.insert(std::pair<unsigned int, iPendingOperation>(t, iipo));
2883   return true;
2884 }
2885 
findAllocationOp(const PendingOperationItem & op)2886 iPendingOperation PendingOperationList::findAllocationOp(const PendingOperationItem& op)
2887 {
2888   iPendingOperationSortedRange r = _map.equal_range(op.getIndex());
2889   iPendingOperationSorted ipos = r.second;
2890   while(ipos != r.first)
2891   {
2892     --ipos;
2893     const PendingOperationItem& poi = *ipos->second;
2894     if(poi.isAllocationOp(op))  // Comparison.
2895       return ipos->second;
2896   }
2897   return end();
2898 }
2899 
2900 
2901 //========================================================================
2902 
2903 
2904 //---------------------------------------------------------
2905 //   addDeviceOperation
2906 //---------------------------------------------------------
2907 
addDeviceOperation(MidiDeviceList * devlist,MidiDevice * dev)2908 void PendingOperationList::addDeviceOperation(MidiDeviceList* devlist, MidiDevice* dev)
2909 {
2910   bool gotUniqueName=false;
2911   int increment = 0;
2912   const QString origname = dev->name();
2913   QString newName = origname;
2914   PendingOperationItem poi(devlist, dev, PendingOperationItem::AddMidiDevice);
2915   // check if the name's been taken
2916   while(!gotUniqueName)
2917   {
2918     if(increment >= 10000)
2919     {
2920       fprintf(stderr, "MusE Error: PendingOperationList::addDeviceOperation(): Out of 10000 unique midi device names!\n");
2921       return;
2922     }
2923     gotUniqueName = true;
2924     // In the case of type AddMidiDevice, this searches for the name only.
2925     iPendingOperation ipo = findAllocationOp(poi);
2926     if(ipo != end())
2927     {
2928       PendingOperationItem& poif = *ipo;
2929       if(poif._midi_device == poi._midi_device)
2930         return;  // Device itself is already added!
2931       newName = origname + QString("_%1").arg(++increment);
2932       gotUniqueName = false;
2933     }
2934 
2935     for(ciMidiDevice i = devlist->cbegin(); i != devlist->cend(); ++i)
2936     {
2937       const QString s = (*i)->name();
2938       if(s == newName)
2939       {
2940         newName = origname + QString("_%1").arg(++increment);
2941         gotUniqueName = false;
2942       }
2943     }
2944   }
2945 
2946   if(origname != newName)
2947     dev->setName(newName);
2948 
2949   add(poi);
2950 }
2951 
2952 //---------------------------------------------------------
2953 //   addPartPortCtrlEvents
2954 //---------------------------------------------------------
2955 
addPartPortCtrlEvents(const Event & event,Part * part,unsigned int tick,unsigned int,Track * track)2956 void PendingOperationList::addPartPortCtrlEvents(
2957   const Event& event, Part* part, unsigned int tick, unsigned int /*len*/, Track* track)
2958 {
2959   if(!track || !track->isMidiTrack())
2960     return;
2961 
2962   if(event.type() == Controller)
2963   {
2964     unsigned int tck  = event.tick() + tick;
2965     int cntrl = event.dataA();
2966     int val   = event.dataB();
2967     MidiTrack* mt = (MidiTrack*)track;
2968     MidiPort* mp;
2969     int ch;
2970     mt->mappedPortChanCtrl(&cntrl, nullptr, &mp, &ch);
2971 
2972     MidiCtrlValListList* mcvll = mp->controller();
2973     MidiCtrlValList* mcvl = NULL;
2974     iMidiCtrlValList imcvll = mcvll->find(ch, cntrl);
2975     if(imcvll == mcvll->end())
2976     {
2977       PendingOperationItem poi(mcvll, 0, ch, cntrl, PendingOperationItem::AddMidiCtrlValList);
2978       if(findAllocationOp(poi) == end())
2979       {
2980         mcvl = new MidiCtrlValList(cntrl);
2981         poi._mcvl = mcvl;
2982         add(poi);
2983       }
2984     }
2985     else
2986     {
2987       mcvl = imcvll->second;
2988     }
2989 
2990     //assert(mcvl != NULL); //FIXME: Can this happen? (danvd). UPDATE: Yes, it can (danvd)
2991     if(mcvl != NULL)
2992     {
2993       // The operation will catch and ignore events which are past the end of the part.
2994       add(PendingOperationItem(mcvl, part, tck, val, PendingOperationItem::AddMidiCtrlVal));
2995     }
2996   }
2997 }
2998 
addPartPortCtrlEvents(Part * part,unsigned int tick,unsigned int len,Track * track)2999 void PendingOperationList::addPartPortCtrlEvents(Part* part, unsigned int tick, unsigned int len, Track* track)
3000 {
3001   if(!track || !track->isMidiTrack())
3002     return;
3003   for(ciEvent ie = part->events().begin(); ie != part->events().end(); ++ie)
3004   {
3005     // The operation will catch and ignore events which are past the end of the part.
3006     addPartPortCtrlEvents(ie->second, part, tick, len, track);
3007   }
3008 }
3009 
3010 //---------------------------------------------------------
3011 //   removePartPortCtrlEvents
3012 //---------------------------------------------------------
3013 
removePartPortCtrlEvents(const Event & event,Part * part,Track * track)3014 bool PendingOperationList::removePartPortCtrlEvents(const Event& event, Part* part, Track* track)
3015 {
3016   if(!track || !track->isMidiTrack())
3017     return false;
3018 
3019   if(event.type() == Controller)
3020   {
3021     MidiTrack* mt = (MidiTrack*)track;
3022 //     MidiPort* mp = &MusEGlobal::midiPorts[mt->outPort()];
3023 //     int ch = mt->outChannel();
3024 
3025     unsigned int tck  = event.tick() + part->tick();
3026     int cntrl = event.dataA();
3027     int val   = event.dataB();
3028 
3029     // Is it a drum controller event, according to the track port's instrument?
3030     MidiPort* mp;
3031     int ch;
3032     mt->mappedPortChanCtrl(&cntrl, nullptr, &mp, &ch);
3033 
3034 
3035     MidiCtrlValListList* mcvll = mp->controller();
3036     iMidiCtrlValList cl = mcvll->find(ch, cntrl);
3037     if (cl == mcvll->end()) {
3038                 fprintf(stderr, "removePartPortCtrlEvents: controller %d(0x%x) for channel %d not found size %zd\n",
3039                     cntrl, cntrl, ch, mcvll->size());
3040           return false;
3041           }
3042     MidiCtrlValList* mcvl = cl->second;
3043     iMidiCtrlVal imcv = mcvl->findMCtlVal(tck, part, val);
3044     if (imcv == mcvl->end()) {
3045           // Let's throw up the error only if we were expecting the cache event to be there,
3046           //  as is the case when the tick is inside the part. When the tick is NOT inside the part
3047           //  a cache event should really not be there. But if one is found it should be deleted anyway.
3048 #ifdef ALLOW_LEFT_HIDDEN_EVENTS
3049           if((int)tck >= (int)part->tick() && (int)tck < (int)part->tick() + (int)part->lenTick())
3050 #else
3051           if(tck < part->tick() + part->lenTick())
3052 #endif
3053             fprintf(stderr, "removePartPortCtrlEvents: (tick: %u): not found (size %zd)\n", tck, mcvl->size());
3054           return false;
3055           }
3056     return add(PendingOperationItem(mcvl, imcv, PendingOperationItem::DeleteMidiCtrlVal));
3057   }
3058   return false;
3059 }
3060 
removePartPortCtrlEvents(Part * part,Track * track)3061 void PendingOperationList::removePartPortCtrlEvents(Part* part, Track* track)
3062 {
3063   if(!track || !track->isMidiTrack())
3064     return;
3065   for(ciEvent ie = part->events().begin(); ie != part->events().end(); ++ie)
3066   {
3067     removePartPortCtrlEvents(ie->second, part, track);
3068   }
3069 }
3070 
3071 //---------------------------------------------------------
3072 //   addPortCtrlEvents
3073 //---------------------------------------------------------
3074 
addTrackPortCtrlEvents(Track * track)3075 void PendingOperationList::addTrackPortCtrlEvents(Track* track)
3076 {
3077   if(!track || !track->isMidiTrack())
3078     return;
3079   const PartList* pl = track->cparts();
3080   for(ciPart ip = pl->begin(); ip != pl->end(); ++ip)
3081   {
3082     Part* part = ip->second;
3083     addPartPortCtrlEvents(part, part->tick(), part->lenTick(), track);
3084   }
3085 }
3086 
3087 //---------------------------------------------------------
3088 //   removePortCtrlEvents
3089 //---------------------------------------------------------
3090 
removeTrackPortCtrlEvents(Track * track)3091 void PendingOperationList::removeTrackPortCtrlEvents(Track* track)
3092 {
3093   if(!track || !track->isMidiTrack())
3094     return;
3095   const PartList* pl = track->cparts();
3096   for(ciPart ip = pl->begin(); ip != pl->end(); ++ip)
3097   {
3098     Part* part = ip->second;
3099     removePartPortCtrlEvents(part, track);
3100   }
3101 }
3102 
modifyPartPortCtrlEvents(const Event & old_event,const Event & event,Part * part)3103 void PendingOperationList::modifyPartPortCtrlEvents(const Event& old_event, const Event& event, Part* part)
3104 {
3105   Track* t = part->track();
3106   if(!t || !t->isMidiTrack())
3107     return;
3108   if(old_event.type() != Controller || event.type() != Controller)
3109     return;
3110   MidiTrack* mt = static_cast<MidiTrack*>(t);
3111 
3112   unsigned int tck_erase  = old_event.tick() + part->tick();
3113   int cntrl_erase = old_event.dataA();
3114   int val_erase = old_event.dataB();
3115   iMidiCtrlVal imcv_erase;
3116   bool found_erase = false;
3117 
3118   // Is it a drum controller old_event, according to the track port's instrument?
3119   int ch_erase;
3120   MidiPort* mp_erase;
3121   mt->mappedPortChanCtrl(&cntrl_erase, nullptr, &mp_erase, &ch_erase);
3122 
3123 
3124   MidiCtrlValListList* mcvll_erase = mp_erase->controller();
3125   MidiCtrlValList* mcvl_erase = 0;
3126   iMidiCtrlValList cl_erase = mcvll_erase->find(ch_erase, cntrl_erase);
3127   if(cl_erase == mcvll_erase->end())
3128   {
3129     if(MusEGlobal::debugMsg)
3130       printf("modifyPartPortCtrlEvents: controller %d(0x%x) for channel %d not found size %zd\n",
3131               cntrl_erase, cntrl_erase, ch_erase, mcvll_erase->size());
3132   }
3133   else
3134   {
3135     mcvl_erase = cl_erase->second;
3136     imcv_erase = mcvl_erase->findMCtlVal(tck_erase, part, val_erase);
3137     if(imcv_erase == mcvl_erase->end())
3138     {
3139       if(MusEGlobal::debugMsg)
3140         printf("modifyPartPortCtrlEvents(tick:%u val:%d): not found (size %zd)\n", tck_erase, val_erase, mcvl_erase->size());
3141     }
3142     else
3143       found_erase = true;
3144   }
3145 
3146   unsigned int tck_add  = event.tick() + part->tick();
3147   int cntrl_add = event.dataA();
3148   int val_add   = event.dataB();
3149 
3150 
3151   // FIXME FIXME CHECK THIS
3152   //
3153   //  Why wasn't 'ch' given its own 'ch_add' variable in the original code?
3154   //  And why did 'mp_add' default to mp_erase above.
3155   //  That means the channel and port would have defaulted to the ones
3156   //   being erased above, not the track's. That can't be right !
3157 
3158 
3159   // Is it a drum controller event, according to the track port's instrument?
3160   int ch_add;
3161   MidiPort* mp_add;
3162   mt->mappedPortChanCtrl(&cntrl_add, nullptr, &mp_add, &ch_add);
3163 
3164   MidiCtrlValList* mcvl_add;
3165   MidiCtrlValListList* mcvll_add = mp_add->controller();
3166   iMidiCtrlValList imcvll_add = mcvll_add->find(ch_add, cntrl_add);
3167   if(imcvll_add == mcvll_add->end())
3168   {
3169     if(found_erase)
3170       add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal));
3171     PendingOperationItem poi(mcvll_add, 0, ch_add, cntrl_add, PendingOperationItem::AddMidiCtrlValList);
3172     if(findAllocationOp(poi) == end())
3173     {
3174       poi._mcvl = new MidiCtrlValList(cntrl_add);
3175       add(poi);
3176     }
3177     // The operation will catch and ignore events which are past the end of the part.
3178     add(PendingOperationItem(poi._mcvl, part, tck_add, val_add, PendingOperationItem::AddMidiCtrlVal));
3179     return;
3180   }
3181   else
3182   {
3183     mcvl_add = imcvll_add->second;
3184     iMidiCtrlVal imcv_add = mcvl_add->findMCtlVal(tck_add, part, val_add);
3185     if(imcv_add != mcvl_add->end())
3186     {
3187       if(tck_erase == tck_add && mcvl_erase == mcvl_add)
3188       {
3189         // The operation will catch and ignore events which are past the end of the part.
3190         add(PendingOperationItem(mcvl_add, imcv_add, val_add, PendingOperationItem::ModifyMidiCtrlVal));
3191       }
3192       else
3193       {
3194         if(found_erase)
3195         {
3196           add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal));
3197         }
3198         // The operation will catch and ignore events which are past the end of the part.
3199         add(PendingOperationItem(mcvl_add, part, tck_add, val_add, PendingOperationItem::AddMidiCtrlVal));
3200       }
3201       return;
3202     }
3203     else
3204     {
3205       if(found_erase)
3206         add(PendingOperationItem(mcvl_erase, imcv_erase, PendingOperationItem::DeleteMidiCtrlVal));
3207       // The operation will catch and ignore events which are past the end of the part.
3208       add(PendingOperationItem(mcvl_add, part, tck_add, val_add, PendingOperationItem::AddMidiCtrlVal));
3209     }
3210   }
3211 }
3212 
addPartOperation(PartList * partlist,Part * part)3213 void PendingOperationList::addPartOperation(PartList *partlist, Part* part)
3214 {
3215   // There is protection, in the catch-all Undo::insert(), from failure here (such as double add, del + add, add + del)
3216   //  which might cause addPortCtrlEvents() without parts or without corresponding removePortCtrlEvents etc.
3217   add(PendingOperationItem(partlist, part, PendingOperationItem::AddPart));
3218   addPartPortCtrlEvents(part, part->posValue(), part->lenValue(), part->track());
3219 }
3220 
delPartOperation(PartList * partlist,Part * part)3221 void PendingOperationList::delPartOperation(PartList *partlist, Part* part)
3222 {
3223   // There is protection, in the catch-all Undo::insert(), from failure here (such as double del, del + add, add + del)
3224   //  which might cause addPortCtrlEvents() without parts or without corresponding removePortCtrlEvents etc.
3225   removePartPortCtrlEvents(part, part->track());
3226   iPart i;
3227   for (i = partlist->begin(); i != partlist->end(); ++i) {
3228         if (i->second == part) {
3229               add(PendingOperationItem(partlist, i, PendingOperationItem::DeletePart));
3230               return;
3231               }
3232         }
3233   printf("THIS SHOULD NEVER HAPPEN: could not find the part in PendingOperationList::delPartOperation()!\n");
3234 }
3235 
movePartOperation(PartList * partlist,Part * part,unsigned int new_pos,Track * track)3236 void PendingOperationList::movePartOperation(PartList *partlist, Part* part, unsigned int new_pos, Track* track)
3237 {
3238   removePartPortCtrlEvents(part, part->track());
3239   iPart i = partlist->end();
3240   if(track)
3241   {
3242     for (i = partlist->begin(); i != partlist->end(); ++i) {
3243           if (i->second == part)
3244                 break;
3245           }
3246     if(i == partlist->end())
3247       printf("THIS SHOULD NEVER HAPPEN: could not find the part in PendingOperationList::movePartOperation()!\n");
3248   }
3249 
3250   add(PendingOperationItem(i, part, new_pos, PendingOperationItem::MovePart, track));
3251 
3252   if(!track)
3253     track = part->track();
3254 
3255   addPartPortCtrlEvents(part, new_pos, part->lenValue(), track);
3256 }
3257 
modifyPartStartOperation(Part * part,unsigned int new_pos,unsigned int new_len,int64_t events_offset,Pos::TType events_offset_time_type)3258 void PendingOperationList::modifyPartStartOperation(
3259   Part* part, unsigned int new_pos, unsigned int new_len, int64_t events_offset, Pos::TType events_offset_time_type)
3260 {
3261   if(!part->track())
3262     return;
3263 
3264   PartList* partlist = part->track()->parts();
3265   iPart ip = partlist->end();
3266   for (ip = partlist->begin(); ip != partlist->end(); ++ip) {
3267         if (ip->second == part)
3268               break;
3269         }
3270   if(ip == partlist->end())
3271   {
3272     fprintf(stderr, "THIS SHOULD NEVER HAPPEN: could not find part in PendingOperationList::modifyPartStartOperation()!\n");
3273     return;
3274   }
3275 
3276   EventList* new_el = nullptr;
3277   // If we are dragging the part's events with the border, their positions relative to the border don't change.
3278   // If we are not dragging the events, their positions relative to the border change so we MUST move ALL the events.
3279   if(events_offset != 0)
3280   {
3281     // Compose a complete new list to quickly swap with the existing list.
3282     const EventList& el = part->events();
3283     new_el = new EventList();
3284     for(ciEvent ie = el.cbegin(); ie != el.cend(); ++ie)
3285     {
3286       Event e = ie->second.clone();
3287       if(e.pos().type() == events_offset_time_type)
3288       {
3289         // NOTE: Don't alter the offset here or below in the conversions. It messes with the ability of the undo system
3290         //        to properly undo a movement. It also breaks the rule that all clone parts MUST have the same event times.
3291         //       Checks and limits should be done before calling this function.
3292         //if((int64_t)e.posValue() + events_offset < 0)
3293         //  e.setPosValue(0);
3294         //else
3295           e.setPosValue(e.posValue() + events_offset);
3296       }
3297       else
3298       {
3299         // In case the event and part pos types differ, the event dominates.
3300         const unsigned int new_part_pos_val = Pos::convert(new_pos, part->type(), e.pos().type());
3301         const unsigned int old_abs_ev_pos_val = Pos::convert(e.posValue() + new_part_pos_val, e.pos().type(), events_offset_time_type);
3302         const unsigned int new_abs_ev_pos_val = Pos::convert(old_abs_ev_pos_val + events_offset, events_offset_time_type, e.pos().type());
3303         const unsigned int new_ev_pos_val = new_abs_ev_pos_val - new_part_pos_val;
3304         e.setPosValue(new_ev_pos_val);
3305       }
3306       new_el->add(e);
3307     }
3308   }
3309 
3310   // If we are dragging the part's events with the border, we must update the midi controller cache.
3311   // If we are not dragging the events, their absolute positions don't change so there should be no need to update the cache.
3312   // First half of the midi controller cache update:
3313   removePartPortCtrlEvents(part, part->track());
3314 
3315   add(PendingOperationItem(ip, part, new_pos, new_len, new_el, PendingOperationItem::ModifyPartStart));
3316 
3317   // Second half of the midi controller cache update:
3318   // The operation will catch and ignore events which are outside of the part.
3319   // In case the new_pos and events_offset types differ, the events_offset dominates.
3320   const unsigned int new_cache_offset =
3321     Pos::convert(events_offset + Pos::convert(new_pos, part->type(), events_offset_time_type),
3322                  events_offset_time_type, Pos::TICKS);
3323   addPartPortCtrlEvents(part, new_cache_offset, part->lenValue(), part->track());
3324 }
3325 
modifyPartLengthOperation(Part * part,unsigned int new_len,int64_t events_offset,Pos::TType events_offset_time_type)3326 void PendingOperationList::modifyPartLengthOperation(
3327   Part* part, unsigned int new_len, int64_t events_offset, Pos::TType events_offset_time_type)
3328 {
3329   if(!part->track())
3330     return;
3331 
3332   PartList* partlist = part->track()->parts();
3333   iPart ip = partlist->end();
3334   for (ip = partlist->begin(); ip != partlist->end(); ++ip) {
3335         if (ip->second == part)
3336               break;
3337         }
3338   if(ip == partlist->end())
3339   {
3340     fprintf(stderr, "THIS SHOULD NEVER HAPPEN: could not find part in PendingOperationList::modifyPartLengthOperation()!\n");
3341     return;
3342   }
3343 
3344   EventList* new_el = nullptr;
3345   // If we are dragging the part's events with the border, their positions relative to the border change so we MUST move ALL the events.
3346   // If we are not dragging the events, their positions relative to the border don't change.
3347   if(events_offset != 0)
3348   {
3349     // Compose a complete new list to quickly swap with the existing list.
3350     const EventList& el = part->events();
3351     new_el = new EventList();
3352     for(ciEvent ie = el.cbegin(); ie != el.cend(); ++ie)
3353     {
3354       Event e = ie->second.clone();
3355       if(e.pos().type() == events_offset_time_type)
3356       {
3357         // NOTE: Don't alter the offset here or below in the conversions. It messes with the ability of the undo system
3358         //        to properly undo a movement. It also breaks the rule that all clone parts MUST have the same event times.
3359         //       Checks and limits should be done before calling this function.
3360         //if((int64_t)e.posValue() + events_offset < 0)
3361         //  e.setPosValue(0);
3362         //else
3363           e.setPosValue(e.posValue() + events_offset);
3364       }
3365       else
3366       {
3367         // In case the event and part pos types differ, the event dominates.
3368         const unsigned int part_pos_val = part->posValue(e.pos().type());
3369         const unsigned int old_abs_ev_pos_val = Pos::convert(e.posValue() + part_pos_val, e.pos().type(), events_offset_time_type);
3370         const unsigned int new_abs_ev_pos_val = Pos::convert(old_abs_ev_pos_val + events_offset, events_offset_time_type, e.pos().type());
3371         const unsigned int new_ev_pos_val = new_abs_ev_pos_val - part_pos_val;
3372         e.setPosValue(new_ev_pos_val);
3373       }
3374       new_el->add(e);
3375     }
3376   }
3377 
3378   // If we are dragging the part's events with the border, we must update the midi controller cache.
3379   // If we are not dragging the events, their absolute positions don't change so there should be no need to update the cache.
3380   // First half of the midi controller cache update:
3381   removePartPortCtrlEvents(part, part->track());
3382 
3383   add(PendingOperationItem(ip, part, new_len, new_el, PendingOperationItem::ModifyPartLength));
3384 
3385   // Second half of the midi controller cache update:
3386   // The operation will catch and ignore events which are outside of the part.
3387   // In case the new_pos and events_offset types differ, the events_offset dominates.
3388   const unsigned int new_cache_offset =
3389     Pos::convert(events_offset + part->posValue(events_offset_time_type), events_offset_time_type, Pos::TICKS);
3390 
3391   addPartPortCtrlEvents(part, new_cache_offset, part->lenValue(), part->track());
3392 }
3393 
3394 
3395 //---------------------------------------------------------
3396 //   addTrackAuxSendOperation
3397 //---------------------------------------------------------
3398 
addTrackAuxSendOperation(AudioTrack * atrack,int n)3399 void PendingOperationList::addTrackAuxSendOperation(AudioTrack *atrack, int n)
3400       {
3401       AuxSendValueList *vl = atrack->getAuxSendValueList();
3402       const int nn = vl->size();
3403       for (int i = nn; i < n; ++i)
3404             add(PendingOperationItem(vl, 0.0, PendingOperationItem::AddAuxSendValue));
3405       }
3406 
3407 //---------------------------------------------------------
3408 //   TrackMidiCtrlRemapOperation
3409 //---------------------------------------------------------
3410 
TrackMidiCtrlRemapOperation(MidiTrack * mtrack,int index,int newPort,int newChan,int newNote,MidiCtrlValRemapOperation * rmop)3411 void TrackMidiCtrlRemapOperation(
3412   MidiTrack *mtrack, int index, int newPort, int newChan, int newNote, MidiCtrlValRemapOperation* rmop)
3413 {
3414   const int out_port = mtrack->outPort();
3415   const int out_chan = mtrack->outChannel();
3416 
3417   if(mtrack->type() != Track::DRUM || out_port < 0 || out_port >= MusECore::MIDI_PORTS)
3418     return;
3419 
3420   // Default to track port if -1 and track channel if -1.
3421   if(newPort == -1)
3422     newPort = out_port;
3423 
3424   if(newChan == -1)
3425     newChan = out_chan;
3426 
3427   MidiPort* trackmp = &MusEGlobal::midiPorts[out_port];
3428 
3429   const DrumMap *drum_map = mtrack->drummap();
3430 
3431   int dm_ch = drum_map[index].channel;
3432   if(dm_ch == -1)
3433     dm_ch = out_chan;
3434   int dm_port = drum_map[index].port;
3435   if(dm_port == -1)
3436     dm_port = out_port;
3437   MidiPort* dm_mp = &MusEGlobal::midiPorts[dm_port];
3438 
3439   MidiCtrlValListList* dm_mcvll = dm_mp->controller();
3440   MidiCtrlValList* v_mcvl;
3441   int v_ch, v_ctrl, v_idx;
3442   for(iMidiCtrlValList idm_mcvl = dm_mcvll->begin(); idm_mcvl != dm_mcvll->end(); ++idm_mcvl)
3443   {
3444     v_ch = idm_mcvl->first >> 24;
3445     if(v_ch != dm_ch)
3446       continue;
3447     v_mcvl = idm_mcvl->second;
3448     v_ctrl = v_mcvl->num();
3449 
3450     // Is it a drum controller, according to the track port's instrument?
3451     if(!trackmp->drumController(v_ctrl))
3452       continue;
3453 
3454     v_idx = v_ctrl & 0xff;
3455     if(v_idx != drum_map[index].anote)
3456       continue;
3457 
3458     // Does this midi control value list need to be changed (values moved etc)?
3459     iMidiCtrlVal imcv = v_mcvl->begin();
3460     for( ; imcv != v_mcvl->end(); ++imcv)
3461     {
3462       const MidiCtrlVal& mcv = imcv->second;
3463       if(mcv.part && mcv.part->track() == mtrack)
3464         break;
3465     }
3466     if(imcv != v_mcvl->end())
3467     {
3468       // A contribution from a part on this track was found.
3469       // We must compose a new list, or get an existing one and schedule the existing
3470       //  one for iterator erasure and pointer deletion.
3471       // Add the erase iterator. Add will ignore if the erase iterator already exists.
3472       rmop->_midiCtrlValLists2bErased.add(dm_port, idm_mcvl);
3473       // Insert the delete pointer. Insert will ignore if the delete pointer already exists.
3474       rmop->_midiCtrlValLists2bDeleted.insert(v_mcvl);
3475 
3476       MidiCtrlValListList* op_mcvll;
3477       iMidiCtrlValLists2bAdded_t imcvla = rmop->_midiCtrlValLists2bAdded.find(dm_port);
3478       if(imcvla == rmop->_midiCtrlValLists2bAdded.end())
3479       {
3480         op_mcvll = new MidiCtrlValListList();
3481         rmop->_midiCtrlValLists2bAdded.insert(MidiCtrlValLists2bAddedInsertPair_t(dm_port, op_mcvll));
3482       }
3483       else
3484         op_mcvll = imcvla->second;
3485 
3486       MidiCtrlValList* op_mcvl;
3487       iMidiCtrlValList imcvl = op_mcvll->find(dm_ch, v_ctrl);
3488       if(imcvl == op_mcvll->end())
3489       {
3490         op_mcvl = new MidiCtrlValList(v_ctrl);
3491         op_mcvll->add(dm_ch, op_mcvl);
3492         // Assign the contents of the original list to the new list.
3493         *op_mcvl = *v_mcvl;
3494       }
3495       else
3496         op_mcvl = imcvl->second;
3497 
3498       // Remove from the list any contributions from this track.
3499       iMidiCtrlVal iopmcv = op_mcvl->begin();
3500       for( ; iopmcv != op_mcvl->end(); )
3501       {
3502         const MidiCtrlVal& mcv = iopmcv->second;
3503         if(mcv.part && mcv.part->track() == mtrack)
3504         {
3505           iMidiCtrlVal iopmcv_save = iopmcv;
3506           ++iopmcv_save;
3507           op_mcvl->erase(iopmcv);
3508           iopmcv = iopmcv_save;
3509         }
3510         else
3511           ++iopmcv;
3512       }
3513     }
3514 
3515     // We will be making changes to the list pointed to by the new settings.
3516     // We must schedule the existing one for iterator erasure and pointer deletion.
3517     MidiPort* dm_mp_new = &MusEGlobal::midiPorts[newPort];
3518     MidiCtrlValListList* dm_mcvll_new = dm_mp_new->controller();
3519     MidiCtrlValList* v_mcvl_new = 0;
3520     const int v_ctrl_new = (v_ctrl & ~0xff) | newNote;
3521     iMidiCtrlValList idm_mcvl_new = dm_mcvll_new->find(newChan, v_ctrl_new);
3522     if(idm_mcvl_new != dm_mcvll_new->end())
3523     {
3524       v_mcvl_new = idm_mcvl_new->second;
3525       // Add the erase iterator. Add will ignore if the erase iterator already exists.
3526       rmop->_midiCtrlValLists2bErased.add(newPort, idm_mcvl_new);
3527       // Insert the delete pointer. Insert will ignore if the delete pointer already exists.
3528       rmop->_midiCtrlValLists2bDeleted.insert(v_mcvl_new);
3529     }
3530 
3531     // Create a new list of lists, or get an existing one.
3532     MidiCtrlValListList* op_mcvll_new;
3533     iMidiCtrlValLists2bAdded_t imcvla_new = rmop->_midiCtrlValLists2bAdded.find(newPort);
3534     if(imcvla_new == rmop->_midiCtrlValLists2bAdded.end())
3535     {
3536       op_mcvll_new = new MidiCtrlValListList();
3537       rmop->_midiCtrlValLists2bAdded.insert(MidiCtrlValLists2bAddedInsertPair_t(newPort, op_mcvll_new));
3538     }
3539     else
3540       op_mcvll_new = imcvla_new->second;
3541 
3542     // Compose a new list for replacement, or get an existing one.
3543     MidiCtrlValList* op_mcvl_new;
3544     iMidiCtrlValList imcvl_new = op_mcvll_new->find(newChan, v_ctrl_new);
3545     if(imcvl_new == op_mcvll_new->end())
3546     {
3547       op_mcvl_new = new MidiCtrlValList(v_ctrl_new);
3548       op_mcvll_new->add(newChan, op_mcvl_new);
3549       // Assign the contents of the original list to the new list.
3550       if(v_mcvl_new)
3551         *op_mcvl_new = *v_mcvl_new;
3552     }
3553     else
3554       op_mcvl_new = imcvl_new->second;
3555 
3556     // Add to the list any contributions from this track.
3557     for(ciMidiCtrlVal imcv_new = v_mcvl->begin(); imcv_new != v_mcvl->end(); ++imcv_new)
3558     {
3559       const MidiCtrlVal& mcv = imcv_new->second;
3560       if(mcv.part && mcv.part->track() == mtrack)
3561       {
3562         op_mcvl_new->addMCtlVal(imcv_new->first, mcv.val, mcv.part);
3563       }
3564     }
3565   }
3566 }
3567 
3568 } // namespace MusECore
3569 
3570