summaryrefslogtreecommitdiff
path: root/muse2/muse/functions.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'muse2/muse/functions.cpp')
-rw-r--r--muse2/muse/functions.cpp675
1 files changed, 675 insertions, 0 deletions
diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp
new file mode 100644
index 00000000..89a66fa7
--- /dev/null
+++ b/muse2/muse/functions.cpp
@@ -0,0 +1,675 @@
+//=========================================================
+// MusE
+// Linux Music Editor
+// $Id: functions.cpp,v 1.20.2.19 2011/05/05 20:10 flo93 Exp $
+// (C) Copyright 2011 Florian Jung (flo93@sourceforge.net)
+//=========================================================
+
+#include "functions.h"
+#include "song.h"
+
+#include "event.h"
+#include "audio.h"
+#include "gconfig.h"
+
+#include <iostream>
+
+#include <QMessageBox>
+
+using namespace std;
+
+GateTime* gatetime_dialog=NULL;
+Velocity* velocity_dialog=NULL;
+Quantize* quantize_dialog=NULL;
+Remove* erase_dialog=NULL;
+DelOverlaps* del_overlaps_dialog=NULL;
+Setlen* set_notelen_dialog=NULL;
+Move* move_notes_dialog=NULL;
+Transpose* transpose_dialog=NULL;
+Crescendo* crescendo_dialog=NULL;
+Legato* legato_dialog=NULL;
+
+void init_function_dialogs(QWidget* parent)
+{
+ gatetime_dialog = new GateTime(parent);
+ velocity_dialog = new Velocity(parent);
+ quantize_dialog = new Quantize(parent);
+ erase_dialog = new Remove(parent);
+ del_overlaps_dialog = new DelOverlaps(parent);
+ set_notelen_dialog = new Setlen(parent);
+ move_notes_dialog = new Move(parent);
+ transpose_dialog = new Transpose(parent);
+ crescendo_dialog = new Crescendo(parent);
+ legato_dialog = new Legato(parent);
+}
+
+set<Part*> partlist_to_set(PartList* pl)
+{
+ set<Part*> result;
+
+ for (PartList::iterator it=pl->begin(); it!=pl->end(); it++)
+ result.insert(it->second);
+
+ return result;
+}
+
+bool is_relevant(const Event& event, const Part* part, int range)
+{
+ unsigned tick;
+
+ if (event.type()!=Note) return false;
+
+ switch (range)
+ {
+ case 0: return true;
+ case 1: return event.selected();
+ case 2: tick=event.tick()+part->tick(); return (tick >= song->lpos()) && (tick < song->rpos());
+ case 3: return is_relevant(event,part,1) && is_relevant(event,part,2);
+ default: cout << "ERROR: ILLEGAL FUNCTION CALL in is_relevant: range is illegal: "<<range<<endl;
+ return false;
+ }
+}
+
+
+map<Event*, Part*> get_events(const set<Part*>& parts, int range)
+{
+ map<Event*, Part*> events;
+
+ for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
+ for (iEvent event=(*part)->events()->begin(); event!=(*part)->events()->end(); event++)
+ if (is_relevant(event->second, *part, range))
+ events.insert(pair<Event*, Part*>(&event->second, *part));
+
+ return events;
+}
+
+
+bool modify_notelen(const set<Part*>& parts)
+{
+ if (!gatetime_dialog->exec())
+ return false;
+
+ modify_notelen(parts,gatetime_dialog->range,gatetime_dialog->rateVal,gatetime_dialog->offsetVal);
+
+ return true;
+}
+
+bool modify_velocity(const set<Part*>& parts)
+{
+ if (!velocity_dialog->exec())
+ return false;
+
+ modify_velocity(parts,velocity_dialog->range,velocity_dialog->rateVal,velocity_dialog->offsetVal);
+
+ return true;
+}
+
+bool quantize_notes(const set<Part*>& parts)
+{
+ if (!quantize_dialog->exec())
+ return false;
+
+ quantize_notes(parts, quantize_dialog->range, (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(const set<Part*>& parts)
+{
+ if (!erase_dialog->exec())
+ return false;
+
+ erase_notes(parts,erase_dialog->range, erase_dialog->velo_threshold, erase_dialog->velo_thres_used,
+ erase_dialog->len_threshold, erase_dialog->len_thres_used );
+
+ return true;
+}
+
+bool delete_overlaps(const set<Part*>& parts)
+{
+ if (!del_overlaps_dialog->exec())
+ return false;
+
+ delete_overlaps(parts,erase_dialog->range);
+
+ return true;
+}
+
+bool set_notelen(const set<Part*>& parts)
+{
+ if (!set_notelen_dialog->exec())
+ return false;
+
+ set_notelen(parts,set_notelen_dialog->range,set_notelen_dialog->len);
+
+ return true;
+}
+
+bool move_notes(const set<Part*>& parts)
+{
+ if (!move_notes_dialog->exec())
+ return false;
+
+ move_notes(parts,move_notes_dialog->range,move_notes_dialog->amount);
+
+ return true;
+}
+
+bool transpose_notes(const set<Part*>& parts)
+{
+ if (!transpose_dialog->exec())
+ return false;
+
+ transpose_notes(parts,transpose_dialog->range,transpose_dialog->amount);
+
+ return true;
+}
+
+bool crescendo(const set<Part*>& parts)
+{
+ 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;
+
+ crescendo(parts,crescendo_dialog->range,crescendo_dialog->start_val,crescendo_dialog->end_val,crescendo_dialog->absolute);
+
+ return true;
+}
+
+bool legato(const set<Part*>& parts)
+{
+ if (!legato_dialog->exec())
+ return false;
+
+ legato(parts,legato_dialog->range, legato_dialog->min_len, !legato_dialog->allow_shortening);
+
+ return true;
+}
+
+
+
+void modify_velocity(const set<Part*>& parts, int range, int rate, int offset)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+
+ if ( (!events.empty()) && ((rate!=100) || (offset!=0)) )
+ {
+ song->startUndo();
+
+ for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++)
+ {
+ Event& event=*(it->first);
+ Part* part=it->second;
+
+ int velo = event.velo();
+
+ velo = (velo * rate) / 100;
+ velo += offset;
+
+ if (velo <= 0)
+ velo = 1;
+ else if (velo > 127)
+ velo = 127;
+
+ if (event.velo() != velo)
+ {
+ Event newEvent = event.clone();
+ newEvent.setVelo(velo);
+ // Indicate no undo, and do not do port controller values and clone parts.
+ audio->msgChangeEvent(event, newEvent, part, false, false, false);
+ }
+ }
+
+ song->endUndo(SC_EVENT_MODIFIED);
+ }
+}
+
+void modify_off_velocity(const set<Part*>& parts, int range, int rate, int offset)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+
+ if ( (!events.empty()) && ((rate!=100) || (offset!=0)) )
+ {
+ song->startUndo();
+
+ for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++)
+ {
+ Event& event=*(it->first);
+ Part* part=it->second;
+
+ int velo = event.veloOff();
+
+ velo = (velo * rate) / 100;
+ velo += offset;
+
+ if (velo <= 0)
+ velo = 1;
+ else if (velo > 127)
+ velo = 127;
+
+ if (event.veloOff() != velo)
+ {
+ Event newEvent = event.clone();
+ newEvent.setVeloOff(velo);
+ // Indicate no undo, and do not do port controller values and clone parts.
+ audio->msgChangeEvent(event, newEvent, part, false, false, false);
+ }
+ }
+
+ song->endUndo(SC_EVENT_MODIFIED);
+ }
+}
+
+void modify_notelen(const set<Part*>& parts, int range, int rate, int offset)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+
+ if ( (!events.empty()) && ((rate!=100) || (offset!=0)) )
+ {
+ song->startUndo();
+
+ for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++)
+ {
+ Event& event=*(it->first);
+ Part* part=it->second;
+
+ unsigned int len = event.lenTick(); //prevent compiler warning: comparison singed/unsigned
+
+ len = (len * rate) / 100;
+ len += offset;
+
+ if (len <= 0)
+ len = 1;
+
+ if (event.lenTick() != len)
+ {
+ Event newEvent = event.clone();
+ newEvent.setLenTick(len);
+ // Indicate no undo, and do not do port controller values and clone parts.
+ audio->msgChangeEvent(event, newEvent, part, false, false, false);
+ }
+ }
+
+ song->endUndo(SC_EVENT_MODIFIED);
+ }
+}
+
+void set_notelen(const set<Part*>& parts, int range, int len)
+{
+ modify_notelen(parts, range, 0, len);
+}
+
+unsigned quantize_tick(unsigned tick, unsigned raster, int swing)
+{
+ //find out the nearest tick and the distance to it:
+ //this is so complicated because this function supports
+ //swing: if swing is 50, the resulting rhythm is not
+ //"daa daa daa daa" but "daaaa da daaaa da"...
+ int tick_dest1 = AL::sigmap.raster1(tick, raster*2); //round down
+ int tick_dest2 = tick_dest1 + raster + raster*swing/100;
+ int tick_dest3 = tick_dest1 + raster*2;
+
+ int tick_diff1 = tick_dest1 - tick;
+ int tick_diff2 = tick_dest2 - tick;
+ int tick_diff3 = tick_dest3 - tick;
+
+ if ((abs(tick_diff1) <= abs(tick_diff2)) && (abs(tick_diff1) <= abs(tick_diff3))) //tick_dest1 is the nearest tick
+ return tick_dest1;
+ else if ((abs(tick_diff2) <= abs(tick_diff1)) && (abs(tick_diff2) <= abs(tick_diff3))) //tick_dest2 is the nearest tick
+ return tick_dest2;
+ else
+ return tick_dest3;
+}
+
+void quantize_notes(const set<Part*>& parts, int range, int raster, bool quant_len, int strength, int swing, int threshold)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+ bool undo_started=false;
+
+ if (!events.empty())
+ {
+ for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++)
+ {
+ Event& event=*(it->first);
+ Part* part=it->second;
+
+ unsigned begin_tick = event.tick() + part->tick();
+ int begin_diff = quantize_tick(begin_tick, raster, swing) - begin_tick;
+
+ if (abs(begin_diff) > threshold)
+ begin_tick = begin_tick + begin_diff*strength/100;
+
+
+ unsigned len=event.lenTick();
+
+ unsigned end_tick = begin_tick + len;
+ int len_diff = quantize_tick(end_tick, raster, swing) - end_tick;
+
+ if ((abs(len_diff) > threshold) && quant_len)
+ len = len + len_diff*strength/100;
+
+ if (len <= 0)
+ len = 1;
+
+
+ if ( (event.lenTick() != len) || (event.tick() + part->tick() != begin_tick) )
+ {
+ if (!undo_started)
+ {
+ song->startUndo();
+ undo_started=true;
+ }
+
+ Event newEvent = event.clone();
+ newEvent.setTick(begin_tick - part->tick());
+ newEvent.setLenTick(len);
+ // Indicate no undo, and do not do port controller values and clone parts.
+ audio->msgChangeEvent(event, newEvent, part, false, false, false);
+ }
+ }
+
+ if (undo_started) song->endUndo(SC_EVENT_MODIFIED);
+ }
+}
+
+void erase_notes(const set<Part*>& parts, int range, int velo_threshold, bool velo_thres_used, int len_threshold, bool len_thres_used)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+
+ if (!events.empty())
+ {
+ song->startUndo();
+
+ for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++)
+ {
+ Event& event=*(it->first);
+ Part* part=it->second;
+ if ( (!velo_thres_used && !len_thres_used) ||
+ (velo_thres_used && event.velo() < velo_threshold) ||
+ (len_thres_used && int(event.lenTick()) < len_threshold) )
+ audio->msgDeleteEvent(event, part, false, false, false);
+ }
+
+ song->endUndo(SC_EVENT_REMOVED);
+ }
+}
+
+void transpose_notes(const set<Part*>& parts, int range, signed int halftonesteps)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+
+ if ( (!events.empty()) && (halftonesteps!=0) )
+ {
+ song->startUndo();
+
+ for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++)
+ {
+ Event& event=*(it->first);
+ Part* part=it->second;
+
+ Event newEvent = event.clone();
+ int pitch = event.pitch()+halftonesteps;
+ if (pitch > 127) pitch=127;
+ if (pitch < 0) pitch=0;
+ newEvent.setPitch(pitch);
+ // Indicate no undo, and do not do port controller values and clone parts.
+ audio->msgChangeEvent(event, newEvent, part, false, false, false);
+ }
+
+ song->endUndo(SC_EVENT_MODIFIED);
+ }
+}
+
+void crescendo(const set<Part*>& parts, int range, int start_val, int end_val, bool absolute)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+
+ int from=song->lpos();
+ int to=song->rpos();
+
+ if ( (!events.empty()) && (to>from) )
+ {
+ song->startUndo();
+
+ for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++)
+ {
+ Event& event=*(it->first);
+ Part* part=it->second;
+
+ unsigned tick = event.tick() + part->tick();
+ float curr_val= (float)start_val + (float)(end_val-start_val) * (tick-from) / (to-from);
+
+ Event newEvent = event.clone();
+ int velo = event.velo();
+
+ if (absolute)
+ velo=curr_val;
+ else
+ velo=curr_val*velo/100;
+
+ if (velo > 127) velo=127;
+ if (velo <= 0) velo=1;
+ newEvent.setVelo(velo);
+ // Indicate no undo, and do not do port controller values and clone parts.
+ audio->msgChangeEvent(event, newEvent, part, false, false, false);
+ }
+
+ song->endUndo(SC_EVENT_MODIFIED);
+ }
+}
+
+void move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO FINDMICH: safety checks
+{
+ map<Event*, Part*> events = get_events(parts, range);
+
+ if ( (!events.empty()) && (ticks!=0) )
+ {
+ song->startUndo();
+
+ for (map<Event*, Part*>::iterator it=events.begin(); it!=events.end(); it++)
+ {
+ Event& event=*(it->first);
+ Part* part=it->second;
+
+ Event newEvent = event.clone();
+ newEvent.setTick(event.tick()+ticks);
+ // Indicate no undo, and do not do port controller values and clone parts.
+ audio->msgChangeEvent(event, newEvent, part, false, false, false);
+ }
+
+ song->endUndo(SC_EVENT_MODIFIED);
+ }
+}
+
+void delete_overlaps(const set<Part*>& parts, int range)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+ bool undo_started=false;
+
+ set<Event*> deleted_events;
+
+ if (!events.empty())
+ {
+ for (map<Event*, Part*>::iterator it1=events.begin(); it1!=events.end(); it1++)
+ {
+ Event& event1=*(it1->first);
+ Part* part1=it1->second;
+
+ // we may NOT optimize by letting it2 start at (it1 +1); this optimisation
+ // is only allowed when events was sorted by time. it is, however, sorted
+ // randomly by pointer.
+ for (map<Event*, Part*>::iterator it2=events.begin(); it2!=events.end(); it2++)
+ {
+ Event& event2=*(it2->first);
+ Part* part2=it2->second;
+
+ if ( (part1->events()==part2->events()) && // part1 and part2 are the same or are duplicates
+ (&event1 != &event2) && // and event1 and event2 aren't the same
+ (deleted_events.find(&event2) == deleted_events.end()) ) //and event2 hasn't been deleted before
+ {
+ if ( (event1.pitch() == event2.pitch()) &&
+ (event1.tick() <= event2.tick()) &&
+ (event1.endTick() > event2.tick()) ) //they overlap
+ {
+ if (undo_started==false)
+ {
+ song->startUndo();
+ undo_started=true;
+ }
+
+ int new_len = event2.tick() - event1.tick();
+
+ if (new_len==0)
+ {
+ audio->msgDeleteEvent(event1, part1, false, false, false);
+ deleted_events.insert(&event1);
+ }
+ else
+ {
+ Event new_event1 = event1.clone();
+ new_event1.setLenTick(new_len);
+
+ audio->msgChangeEvent(event1, new_event1, part1, false, false, false);
+ }
+ }
+ }
+ }
+ }
+
+ if (undo_started) song->endUndo(SC_EVENT_MODIFIED);
+ }
+}
+
+void legato(const set<Part*>& parts, int range, int min_len, bool dont_shorten)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+ bool undo_started=false;
+
+ if (min_len<=0) min_len=1;
+
+ if (!events.empty())
+ {
+ for (map<Event*, Part*>::iterator it1=events.begin(); it1!=events.end(); it1++)
+ {
+ Event& event1=*(it1->first);
+ Part* part1=it1->second;
+
+ unsigned len=MAXINT;
+ // we may NOT optimize by letting it2 start at (it1 +1); this optimisation
+ // is only allowed when events was sorted by time. it is, however, sorted
+ // randomly by pointer.
+ for (map<Event*, Part*>::iterator it2=events.begin(); it2!=events.end(); it2++)
+ {
+ Event& event2=*(it2->first);
+ Part* part2=it2->second;
+
+ bool relevant = (event2.tick() >= event1.tick() + min_len);
+ if (dont_shorten)
+ relevant = relevant && (event2.tick() >= event1.endTick());
+
+ if ( (part1->events()==part2->events()) && // part1 and part2 are the same or are duplicates
+ relevant && // they're not too near (respect min_len and dont_shorten)
+ (event2.tick()-event1.tick() < len ) ) // that's the nearest relevant following note
+ len=event2.tick()-event1.tick();
+ }
+
+ if (len==MAXINT) len=event1.lenTick(); // if no following note was found, keep the length
+
+ if (event1.lenTick() != len)
+ {
+ if (undo_started==false)
+ {
+ song->startUndo();
+ undo_started=true;
+ }
+
+ Event new_event1 = event1.clone();
+ new_event1.setLenTick(len);
+
+ audio->msgChangeEvent(event1, new_event1, part1, false, false, false);
+ }
+ }
+
+ if (undo_started) song->endUndo(SC_EVENT_MODIFIED);
+ }
+}
+
+
+
+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");
+}