diff options
Diffstat (limited to 'muse2/muse/functions.cpp')
-rw-r--r-- | muse2/muse/functions.cpp | 605 |
1 files changed, 449 insertions, 156 deletions
diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp index b178bcb6..8e9a7cd5 100644 --- a/muse2/muse/functions.cpp +++ b/muse2/muse/functions.cpp @@ -23,11 +23,24 @@ #include "functions.h" #include "song.h" #include "undo.h" +#include "helper.h" #include "event.h" #include "audio.h" #include "gconfig.h" +#include "widgets/function_dialogs/velocity.h" +#include "widgets/function_dialogs/quantize.h" +#include "widgets/function_dialogs/crescendo.h" +#include "widgets/function_dialogs/gatetime.h" +#include "widgets/function_dialogs/remove.h" +#include "widgets/function_dialogs/transpose.h" +#include "widgets/function_dialogs/setlen.h" +#include "widgets/function_dialogs/move.h" +#include "widgets/function_dialogs/deloverlaps.h" +#include "widgets/function_dialogs/legato.h" +#include "widgets/pasteeventsdialog.h" + #include <values.h> #include <iostream> #include <errno.h> @@ -43,32 +56,20 @@ #include <QMessageBox> #include <QClipboard> + + using namespace std; -MusEDialog::GateTime* gatetime_dialog=NULL; -MusEDialog::Velocity* velocity_dialog=NULL; -MusEDialog::Quantize* quantize_dialog=NULL; -MusEDialog::Remove* erase_dialog=NULL; -MusEDialog::DelOverlaps* del_overlaps_dialog=NULL; -MusEDialog::Setlen* set_notelen_dialog=NULL; -MusEDialog::Move* move_notes_dialog=NULL; -MusEDialog::Transpose* transpose_dialog=NULL; -MusEDialog::Crescendo* crescendo_dialog=NULL; -MusEDialog::Legato* legato_dialog=NULL; - -void init_function_dialogs(QWidget* parent) -{ - gatetime_dialog = new MusEDialog::GateTime(parent); - velocity_dialog = new MusEDialog::Velocity(parent); - quantize_dialog = new MusEDialog::Quantize(parent); - erase_dialog = new MusEDialog::Remove(parent); - del_overlaps_dialog = new MusEDialog::DelOverlaps(parent); - set_notelen_dialog = new MusEDialog::Setlen(parent); - move_notes_dialog = new MusEDialog::Move(parent); - transpose_dialog = new MusEDialog::Transpose(parent); - crescendo_dialog = new MusEDialog::Crescendo(parent); - legato_dialog = new MusEDialog::Legato(parent); -} +using MusEConfig::config; + + +// unit private functions: + +bool read_eventlist_and_part(Xml& xml, EventList* el, int* part_id); + +// ----------------------- + + set<Part*> partlist_to_set(PartList* pl) { @@ -87,6 +88,37 @@ set<Part*> part_to_set(Part* p) return result; } +set<Part*> get_all_parts() +{ + set<Part*> result; + + TrackList* tracks=song->tracks(); + for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++) + { + const PartList* parts=(*t_it)->cparts(); + for (ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++) + result.insert(p_it->second); + } + + return result; +} + +set<Part*> get_all_selected_parts() +{ + set<Part*> result; + + TrackList* tracks=song->tracks(); + for (TrackList::const_iterator t_it=tracks->begin(); t_it!=tracks->end(); t_it++) + { + const PartList* parts=(*t_it)->cparts(); + for (ciPart p_it=parts->begin(); p_it!=parts->end(); p_it++) + if (p_it->second->selected()) + result.insert(p_it->second); + } + + return result; +} + bool is_relevant(const Event& event, const Part* part, int range) { unsigned tick; @@ -118,6 +150,8 @@ map<Event*, Part*> get_events(const set<Part*>& parts, int range) } + + bool modify_notelen(const set<Part*>& parts) { if (!gatetime_dialog->exec()) @@ -229,6 +263,180 @@ bool legato(const set<Part*>& parts) +bool modify_notelen() +{ + if (!gatetime_dialog->exec()) + return false; + + set<Part*> parts; + if (gatetime_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + modify_notelen(parts,gatetime_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, gatetime_dialog->rateVal,gatetime_dialog->offsetVal); + + return true; +} + +bool modify_velocity() +{ + if (!velocity_dialog->exec()) + return false; + + set<Part*> parts; + if (velocity_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + modify_velocity(parts,velocity_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS,velocity_dialog->rateVal,velocity_dialog->offsetVal); + + return true; +} + +bool quantize_notes() +{ + if (!quantize_dialog->exec()) + return false; + + set<Part*> parts; + if (quantize_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + quantize_notes(parts, quantize_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, (config.division*4)/(1<<quantize_dialog->raster_power2), + quantize_dialog->quant_len, quantize_dialog->strength, quantize_dialog->swing, + quantize_dialog->threshold); + + return true; +} + +bool erase_notes() +{ + if (!erase_dialog->exec()) + return false; + + set<Part*> parts; + if (erase_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + erase_notes(parts,erase_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, erase_dialog->velo_threshold, erase_dialog->velo_thres_used, + erase_dialog->len_threshold, erase_dialog->len_thres_used ); + + return true; +} + +bool delete_overlaps() +{ + if (!del_overlaps_dialog->exec()) + return false; + + set<Part*> parts; + if (del_overlaps_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + delete_overlaps(parts,erase_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS); + + return true; +} + +bool set_notelen() +{ + if (!set_notelen_dialog->exec()) + return false; + + set<Part*> parts; + if (set_notelen_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + set_notelen(parts,set_notelen_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, set_notelen_dialog->len); + + return true; +} + +bool move_notes() +{ + if (!move_notes_dialog->exec()) + return false; + + set<Part*> parts; + if (move_notes_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + move_notes(parts,move_notes_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, move_notes_dialog->amount); + + return true; +} + +bool transpose_notes() +{ + if (!transpose_dialog->exec()) + return false; + + set<Part*> parts; + if (transpose_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + transpose_notes(parts,transpose_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, transpose_dialog->amount); + + return true; +} + +bool crescendo() +{ + if (song->rpos() <= song->lpos()) + { + QMessageBox::warning(NULL, QObject::tr("Error"), QObject::tr("Please first select the range for crescendo with the loop markers.")); + return false; + } + + if (!crescendo_dialog->exec()) + return false; + + set<Part*> parts; + if (crescendo_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + crescendo(parts,crescendo_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, crescendo_dialog->start_val,crescendo_dialog->end_val,crescendo_dialog->absolute); + + return true; +} + +bool legato() +{ + if (!legato_dialog->exec()) + return false; + + set<Part*> parts; + if (legato_dialog->range & FUNCTION_RANGE_ONLY_SELECTED) + parts=get_all_selected_parts(); + else + parts=get_all_parts(); + + legato(parts,legato_dialog->range & FUNCTION_RANGE_ONLY_BETWEEN_MARKERS, legato_dialog->min_len, !legato_dialog->allow_shortening); + + return true; +} + + + + + + bool modify_velocity(const set<Part*>& parts, int range, int rate, int offset) { map<Event*, Part*> events = get_events(parts, range); @@ -667,34 +875,93 @@ void copy_notes(const set<Part*>& parts, int range) QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard); } -void paste_notes(Part* dest_part) +unsigned get_groupedevents_len(const QString& pt) { - QString tmp="x-muse-eventlist"; // QClipboard::text() expects a QString&, not a QString :( - QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); // TODO CHECK Tim. - paste_at(dest_part, s, song->cpos()); + unsigned maxlen=0; + + Xml xml(pt.toLatin1().constData()); + for (;;) + { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) + { + case Xml::Error: + case Xml::End: + return maxlen; + + case Xml::TagStart: + if (tag == "eventlist") + { + EventList el; + int part_id; + if (read_eventlist_and_part(xml, &el, &part_id)) + { + unsigned len = el.rbegin()->first; + if (len > maxlen) maxlen=len; + } + } + else + xml.unknown("get_clipboard_len"); + break; + + case Xml::Attribut: + case Xml::TagEnd: + default: + break; + } + } + + return maxlen; // see also the return statement above! } -QMimeData* selected_events_to_mime(const set<Part*>& parts, int range) +unsigned get_clipboard_len() { - map<Event*, Part*> events=get_events(parts,range); + QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :( + QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); // TODO CHECK Tim. + + return get_groupedevents_len(s); +} - //--------------------------------------------------- - // generate event list from selected events - //--------------------------------------------------- +bool paste_notes(Part* paste_into_part) +{ + unsigned temp_begin = AL::sigmap.raster1(song->cpos(),0); + unsigned temp_end = AL::sigmap.raster2(temp_begin + get_clipboard_len(), 0); + paste_events_dialog->raster = temp_end - temp_begin; + paste_events_dialog->into_single_part_allowed = (paste_into_part!=NULL); + + if (!paste_events_dialog->exec()) + return false; + + paste_notes(paste_events_dialog->max_distance, paste_events_dialog->always_new_part, + paste_events_dialog->never_new_part, paste_events_dialog->into_single_part ? paste_into_part : NULL, + paste_events_dialog->number, paste_events_dialog->raster); + + return true; +} - EventList el; - unsigned startTick = MAXINT; //will be the tick of the first event or MAXINT if no events are there +void paste_notes(int max_distance, bool always_new_part, bool never_new_part, Part* paste_into_part, int amount, int raster) +{ + QString tmp="x-muse-groupedeventlists"; // QClipboard::text() expects a QString&, not a QString :( + QString s = QApplication::clipboard()->text(tmp, QClipboard::Clipboard); // TODO CHECK Tim. + paste_at(s, song->cpos(), max_distance, always_new_part, never_new_part, paste_into_part, amount, raster); +} - for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++) - { - Event& e = *it->first; - - if (e.tick() < startTick) - startTick = e.tick(); - - el.add(e); - } +// if nothing is selected/relevant, this function returns NULL +QMimeData* selected_events_to_mime(const set<Part*>& parts, int range) +{ + unsigned start_tick = MAXINT; //will be the tick of the first event or MAXINT if no events are there + + for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++) + for (iEvent ev=(*part)->events()->begin(); ev!=(*part)->events()->end(); ev++) + if (is_relevant(ev->second, *part, range)) + if (ev->second.tick() < start_tick) + start_tick=ev->second.tick(); + + if (start_tick == MAXINT) + return NULL; + //--------------------------------------------------- // write events as XML into tmp file //--------------------------------------------------- @@ -709,10 +976,14 @@ QMimeData* selected_events_to_mime(const set<Part*>& parts, int range) Xml xml(tmp); int level = 0; - xml.tag(level++, "eventlist"); - for (ciEvent e = el.begin(); e != el.end(); ++e) - e->second.write(level, xml, -startTick); - xml.etag(--level, "eventlist"); + for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++) + { + xml.tag(level++, "eventlist part_id=\"%d\"", (*part)->sn()); + for (iEvent ev=(*part)->events()->begin(); ev!=(*part)->events()->end(); ev++) + if (is_relevant(ev->second, *part, range)) + ev->second.write(level, xml, -start_tick); + xml.etag(--level, "eventlist"); + } //--------------------------------------------------- // read tmp file into drag Object @@ -722,7 +993,7 @@ QMimeData* selected_events_to_mime(const set<Part*>& parts, int range) struct stat f_stat; if (fstat(fileno(tmp), &f_stat) == -1) { - fprintf(stderr, "PianoCanvas::copy() fstat failed:<%s>\n", + fprintf(stderr, "copy_notes() fstat failed:<%s>\n", strerror(errno)); fclose(tmp); return 0; @@ -735,7 +1006,7 @@ QMimeData* selected_events_to_mime(const set<Part*>& parts, int range) QByteArray data(fbuf); QMimeData* md = new QMimeData(); - md->setData("text/x-muse-eventlist", data); + md->setData("text/x-muse-groupedeventlists", data); munmap(fbuf, n); fclose(tmp); @@ -743,10 +1014,53 @@ QMimeData* selected_events_to_mime(const set<Part*>& parts, int range) return md; } -void paste_at(Part* dest_part, const QString& pt, int pos) +bool read_eventlist_and_part(Xml& xml, EventList* el, int* part_id) // true on success, false on failure +{ + *part_id = -1; + + for (;;) + { + Xml::Token token = xml.parse(); + const QString& tag = xml.s1(); + switch (token) + { + case Xml::Error: + case Xml::End: + return false; + + case Xml::Attribut: + if (tag == "part_id") + *part_id = xml.s2().toInt(); + else + printf("unknown attribute '%s' in read_eventlist_and_part(), ignoring it...\n", tag.toAscii().data()); + break; + + case Xml::TagStart: + if (tag == "event") + { + Event e(Note); + e.read(xml); + el->add(e); + } + else + xml.unknown("read_eventlist_and_part"); + break; + + case Xml::TagEnd: + if (tag == "eventlist") + return true; + + default: + break; + } + } +} + +void paste_at(const QString& pt, int pos, int max_distance, bool always_new_part, bool never_new_part, Part* paste_into_part, int amount, int raster) { Undo operations; - unsigned newpartlen=dest_part->lenTick(); + map<Part*, unsigned> expand_map; + map<Part*, set<Part*> > new_part_map; Xml xml(pt.toLatin1().constData()); for (;;) @@ -757,53 +1071,95 @@ void paste_at(Part* dest_part, const QString& pt, int pos) { case Xml::Error: case Xml::End: - goto end_of_paste_at; + goto out_of_paste_at_for; case Xml::TagStart: if (tag == "eventlist") { EventList el; - el.read(xml, "eventlist", true); - for (iEvent i = el.begin(); i != el.end(); ++i) + int part_id; + + if (read_eventlist_and_part(xml, &el, &part_id)) { - Event e = i->second; - int tick = e.tick() + pos - dest_part->tick(); - if (tick<0) + Part* dest_part; + Track* dest_track; + Part* old_dest_part; + + if (paste_into_part == NULL) + dest_part = MusEUtil::partFromSerialNumber(part_id); + else + dest_part=paste_into_part; + + if (dest_part == NULL) { - printf("ERROR: trying to add event before current part!\n"); - goto end_of_paste_at; + printf("ERROR: destination part wasn't found. ignoring these events\n"); } - - e.setTick(tick); - e.setSelected(true); - - if (e.endTick() > dest_part->lenTick()) // event exceeds part? + else { - if (dest_part->hasHiddenEvents()) // auto-expanding is forbidden? - { - if (e.tick() < dest_part->lenTick()) - e.setLenTick(dest_part->lenTick() - e.tick()); // clip - else - e.setLenTick(0); // don't insert that note at all - } - else + dest_track=dest_part->track(); + old_dest_part=dest_part; + unsigned first_paste_tick = el.begin()->first + pos; + bool create_new_part = ( (dest_part->tick() > first_paste_tick) || // dest_part begins too late + ( ( (dest_part->endTick() + max_distance < first_paste_tick) || // dest_part is too far away + always_new_part ) && !never_new_part ) ); // respect function arguments + + for (int i=0;i<amount;i++) { - if (e.endTick() > newpartlen) - newpartlen=e.endTick(); + unsigned curr_pos = pos + i*raster; + first_paste_tick = el.begin()->first + curr_pos; + + if (create_new_part) + { + dest_part = dest_track->newPart(); + dest_part->events()->incARef(-1); // the later song->applyOperationGroup() will increment it + // so we must decrement it first :/ + dest_part->setTick(AL::sigmap.raster1(first_paste_tick, config.division)); + + new_part_map[old_dest_part].insert(dest_part); + operations.push_back(UndoOp(UndoOp::AddPart, dest_part)); + } + + for (iEvent i = el.begin(); i != el.end(); ++i) + { + Event e = i->second.clone(); + int tick = e.tick() + curr_pos - dest_part->tick(); + if (tick<0) + { + printf("ERROR: trying to add event before current part! ignoring this event\n"); + continue; + } + + e.setTick(tick); + e.setSelected(true); + + if (e.endTick() > dest_part->lenTick()) // event exceeds part? + { + if (dest_part->hasHiddenEvents()) // auto-expanding is forbidden? + { + if (e.tick() < dest_part->lenTick()) + e.setLenTick(dest_part->lenTick() - e.tick()); // clip + else + e.setLenTick(0); // don't insert that note at all + } + else + { + if (e.endTick() > expand_map[dest_part]) + expand_map[dest_part]=e.endTick(); + } + } + + if (e.lenTick() != 0) operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false)); + } } } - - if (e.lenTick() != 0) operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false)); } - - if (newpartlen != dest_part->lenTick()) - schedule_resize_all_same_len_clone_parts(dest_part, newpartlen, operations); - - song->applyOperationGroup(operations); - goto end_of_paste_at; + else + { + printf("ERROR: reading eventlist from clipboard failed. ignoring this one...\n"); + } } else - xml.unknown("paste_at"); + xml.unknown("paste_at"); break; case Xml::Attribut: @@ -813,7 +1169,15 @@ void paste_at(Part* dest_part, const QString& pt, int pos) } } - end_of_paste_at: + out_of_paste_at_for: + + for (map<Part*, unsigned>::iterator it = expand_map.begin(); it!=expand_map.end(); it++) + if (it->second != it->first->lenTick()) + schedule_resize_all_same_len_clone_parts(it->first, it->second, operations); + + song->informAboutNewParts(new_part_map); // must be called before apply. otherwise + // pointer changes (by resize) screw it up + song->applyOperationGroup(operations); song->update(SC_SELECTION); } @@ -1026,74 +1390,3 @@ void clean_parts() song->applyOperationGroup(operations); } - -void read_function_dialog_config(Xml& xml) -{ - if (erase_dialog==NULL) - { - cout << "ERROR: THIS SHOULD NEVER HAPPEN: read_function_dialog_config() called, but\n" - " dialogs are still uninitalized (NULL)!"<<endl; - return; - } - - for (;;) - { - Xml::Token token = xml.parse(); - if (token == Xml::Error || token == Xml::End) - break; - - const QString& tag = xml.s1(); - switch (token) - { - case Xml::TagStart: - if (tag == "mod_len") - gatetime_dialog->read_configuration(xml); - else if (tag == "mod_velo") - velocity_dialog->read_configuration(xml); - else if (tag == "quantize") - quantize_dialog->read_configuration(xml); - else if (tag == "erase") - erase_dialog->read_configuration(xml); - else if (tag == "del_overlaps") - del_overlaps_dialog->read_configuration(xml); - else if (tag == "setlen") - set_notelen_dialog->read_configuration(xml); - else if (tag == "move") - move_notes_dialog->read_configuration(xml); - else if (tag == "transpose") - transpose_dialog->read_configuration(xml); - else if (tag == "crescendo") - crescendo_dialog->read_configuration(xml); - else if (tag == "legato") - legato_dialog->read_configuration(xml); - else - xml.unknown("function_dialogs"); - break; - - case Xml::TagEnd: - if (tag == "dialogs") - return; - - default: - break; - } - } -} - -void write_function_dialog_config(int level, Xml& xml) -{ - xml.tag(level++, "dialogs"); - - gatetime_dialog->write_configuration(level, xml); - velocity_dialog->write_configuration(level, xml); - quantize_dialog->write_configuration(level, xml); - erase_dialog->write_configuration(level, xml); - del_overlaps_dialog->write_configuration(level, xml); - set_notelen_dialog->write_configuration(level, xml); - move_notes_dialog->write_configuration(level, xml); - transpose_dialog->write_configuration(level, xml); - crescendo_dialog->write_configuration(level, xml); - legato_dialog->write_configuration(level, xml); - - xml.tag(level, "/dialogs"); -} |