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.cpp389
1 files changed, 353 insertions, 36 deletions
diff --git a/muse2/muse/functions.cpp b/muse2/muse/functions.cpp
index ba16640c..4a65d19c 100644
--- a/muse2/muse/functions.cpp
+++ b/muse2/muse/functions.cpp
@@ -13,9 +13,19 @@
#include "audio.h"
#include "gconfig.h"
+#include <values.h>
#include <iostream>
-
+#include <errno.h>
+#include <values.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <QMimeData>
+#include <QByteArray>
+#include <QDrag>
#include <QMessageBox>
+#include <QClipboard>
using namespace std;
@@ -28,6 +38,7 @@ 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)
{
@@ -40,6 +51,7 @@ void init_function_dialogs(QWidget* 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)
@@ -52,6 +64,13 @@ set<Part*> partlist_to_set(PartList* pl)
return result;
}
+set<Part*> part_to_set(Part* p)
+{
+ set<Part*> result;
+ result.insert(p);
+ return result;
+}
+
bool is_relevant(const Event& event, const Part* part, int range)
{
unsigned tick;
@@ -109,7 +128,8 @@ bool quantize_notes(const set<Part*>& parts)
return false;
quantize_notes(parts, quantize_dialog->range, (config.division*4)/(1<<quantize_dialog->raster_power2),
- quantize_dialog->strength, quantize_dialog->swing, quantize_dialog->threshold);
+ quantize_dialog->quant_len, quantize_dialog->strength, quantize_dialog->swing,
+ quantize_dialog->threshold);
return true;
}
@@ -119,7 +139,8 @@ bool erase_notes(const set<Part*>& parts)
if (!erase_dialog->exec())
return false;
- erase_notes(parts,erase_dialog->range);
+ 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;
}
@@ -180,9 +201,19 @@ bool crescendo(const set<Part*>& parts)
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)
+bool modify_velocity(const set<Part*>& parts, int range, int rate, int offset)
{
map<Event*, Part*> events = get_events(parts, range);
Undo operations;
@@ -212,12 +243,13 @@ void modify_velocity(const set<Part*>& parts, int range, int rate, int offset)
}
}
- if (!operations.empty())
- song->applyOperationGroup(operations);
+ return song->applyOperationGroup(operations);
}
+ else
+ return false;
}
-void modify_off_velocity(const set<Part*>& parts, int range, int rate, int offset)
+bool modify_off_velocity(const set<Part*>& parts, int range, int rate, int offset)
{
map<Event*, Part*> events = get_events(parts, range);
Undo operations;
@@ -247,12 +279,13 @@ void modify_off_velocity(const set<Part*>& parts, int range, int rate, int offse
}
}
- if (!operations.empty())
- song->applyOperationGroup(operations);
+ return song->applyOperationGroup(operations);
}
+ else
+ return false;
}
-void modify_notelen(const set<Part*>& parts, int range, int rate, int offset)
+bool modify_notelen(const set<Part*>& parts, int range, int rate, int offset)
{
map<Event*, Part*> events = get_events(parts, range);
Undo operations;
@@ -280,14 +313,15 @@ void modify_notelen(const set<Part*>& parts, int range, int rate, int offset)
}
}
- if (!operations.empty())
- song->applyOperationGroup(operations);
+ return song->applyOperationGroup(operations);
}
+ else
+ return false;
}
-void set_notelen(const set<Part*>& parts, int range, int len)
+bool set_notelen(const set<Part*>& parts, int range, int len)
{
- modify_notelen(parts, range, 0, len);
+ return modify_notelen(parts, range, 0, len);
}
unsigned quantize_tick(unsigned tick, unsigned raster, int swing)
@@ -312,7 +346,7 @@ unsigned quantize_tick(unsigned tick, unsigned raster, int swing)
return tick_dest3;
}
-void quantize_notes(const set<Part*>& parts, int range, int raster, int strength, int swing, int threshold)
+bool 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);
Undo operations;
@@ -336,7 +370,7 @@ void quantize_notes(const set<Part*>& parts, int range, int raster, int strength
unsigned end_tick = begin_tick + len;
int len_diff = quantize_tick(end_tick, raster, swing) - end_tick;
- if (abs(len_diff) > threshold)
+ if ((abs(len_diff) > threshold) && quant_len)
len = len + len_diff*strength/100;
if (len <= 0)
@@ -352,12 +386,13 @@ void quantize_notes(const set<Part*>& parts, int range, int raster, int strength
}
}
- if (!operations.empty())
- song->applyOperationGroup(operations);
+ return song->applyOperationGroup(operations);
}
+ else
+ return false;
}
-void erase_notes(const set<Part*>& parts, int range)
+bool 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);
Undo operations;
@@ -369,15 +404,19 @@ void erase_notes(const set<Part*>& parts, int range)
Event& event=*(it->first);
Part* part=it->second;
- operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false));
+ if ( (!velo_thres_used && !len_thres_used) ||
+ (velo_thres_used && event.velo() < velo_threshold) ||
+ (len_thres_used && int(event.lenTick()) < len_threshold) )
+ operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false));
}
- if (!operations.empty())
- song->applyOperationGroup(operations);
+ return song->applyOperationGroup(operations);
}
+ else
+ return false;
}
-void transpose_notes(const set<Part*>& parts, int range, signed int halftonesteps)
+bool transpose_notes(const set<Part*>& parts, int range, signed int halftonesteps)
{
map<Event*, Part*> events = get_events(parts, range);
Undo operations;
@@ -397,12 +436,13 @@ void transpose_notes(const set<Part*>& parts, int range, signed int halftonestep
operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
}
- if (!operations.empty())
- song->applyOperationGroup(operations);
+ return song->applyOperationGroup(operations);
}
+ else
+ return false;
}
-void crescendo(const set<Part*>& parts, int range, int start_val, int end_val, bool absolute)
+bool crescendo(const set<Part*>& parts, int range, int start_val, int end_val, bool absolute)
{
map<Event*, Part*> events = get_events(parts, range);
Undo operations;
@@ -434,12 +474,13 @@ void crescendo(const set<Part*>& parts, int range, int start_val, int end_val, b
operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
}
- if (!operations.empty())
- song->applyOperationGroup(operations);
+ return song->applyOperationGroup(operations);
}
+ else
+ return false;
}
-void move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO FINDMICH: safety checks
+bool move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO: clipping
{
map<Event*, Part*> events = get_events(parts, range);
Undo operations;
@@ -450,18 +491,36 @@ void move_notes(const set<Part*>& parts, int range, signed int ticks) //TODO FIN
{
Event& event=*(it->first);
Part* part=it->second;
+ bool del=false;
Event newEvent = event.clone();
- newEvent.setTick(event.tick()+ticks);
- operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
+ if ((signed)event.tick()+ticks < 0) //don't allow moving before the part's begin
+ newEvent.setTick(0);
+ else
+ newEvent.setTick(event.tick()+ticks);
+
+ if (newEvent.endTick() > part->lenTick()) //if exceeding the part's end, clip
+ {
+ if (part->lenTick() > newEvent.tick())
+ newEvent.setLenTick(part->lenTick() - newEvent.tick());
+ else
+ del=true; //if the new length would be <= 0, erase the note
+ }
+
+ if (del==false)
+ operations.push_back(UndoOp(UndoOp::ModifyEvent, newEvent, event, part, false, false));
+ else
+ operations.push_back(UndoOp(UndoOp::DeleteEvent, event, part, false, false));
}
- if (!operations.empty())
- song->applyOperationGroup(operations);
+ return song->applyOperationGroup(operations);
}
+ else
+ return false;
}
-void delete_overlaps(const set<Part*>& parts, int range)
+
+bool delete_overlaps(const set<Part*>& parts, int range)
{
map<Event*, Part*> events = get_events(parts, range);
Undo operations;
@@ -510,11 +569,266 @@ void delete_overlaps(const set<Part*>& parts, int range)
}
}
- if (!operations.empty())
- song->applyOperationGroup(operations);
+ return song->applyOperationGroup(operations);
+ }
+ else
+ return false;
+}
+
+bool legato(const set<Part*>& parts, int range, int min_len, bool dont_shorten)
+{
+ map<Event*, Part*> events = get_events(parts, range);
+ Undo operations;
+
+ 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)
+ {
+ Event new_event1 = event1.clone();
+ new_event1.setLenTick(len);
+
+ operations.push_back(UndoOp(UndoOp::ModifyEvent, new_event1, event1, part1, false, false));
+ }
+ }
+
+ return song->applyOperationGroup(operations);
+ }
+ else
+ return false;
+}
+
+
+
+void copy_notes(const set<Part*>& parts, int range)
+{
+ QMimeData* drag = selected_events_to_mime(parts,range);
+
+ if (drag)
+ QApplication::clipboard()->setMimeData(drag, QClipboard::Clipboard);
+}
+
+void paste_notes(Part* dest_part)
+{
+ 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());
+}
+
+QMimeData* selected_events_to_mime(const set<Part*>& parts, int range)
+{
+ map<Event*, Part*> events=get_events(parts,range);
+
+ //---------------------------------------------------
+ // generate event list from selected events
+ //---------------------------------------------------
+
+ EventList el;
+ unsigned startTick = MAXINT; //will be the tick of the first event or MAXINT if no events are there
+
+ 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);
+ }
+
+ //---------------------------------------------------
+ // write events as XML into tmp file
+ //---------------------------------------------------
+
+ FILE* tmp = tmpfile();
+ if (tmp == 0)
+ {
+ fprintf(stderr, "EventCanvas::getTextDrag() fopen failed: %s\n", strerror(errno));
+ return 0;
+ }
+
+ 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");
+
+ //---------------------------------------------------
+ // read tmp file into drag Object
+ //---------------------------------------------------
+
+ fflush(tmp);
+ struct stat f_stat;
+ if (fstat(fileno(tmp), &f_stat) == -1)
+ {
+ fprintf(stderr, "PianoCanvas::copy() fstat failed:<%s>\n",
+ strerror(errno));
+ fclose(tmp);
+ return 0;
+ }
+ int n = f_stat.st_size;
+ char* fbuf = (char*)mmap(0, n+1, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE, fileno(tmp), 0);
+ fbuf[n] = 0;
+
+ QByteArray data(fbuf);
+ QMimeData* md = new QMimeData();
+
+ md->setData("text/x-muse-eventlist", data);
+
+ munmap(fbuf, n);
+ fclose(tmp);
+
+ return md;
+}
+
+void paste_at(Part* dest_part, const QString& pt, int pos)
+{
+ Undo operations;
+
+ Xml xml(pt.toLatin1().constData());
+ for (;;)
+ {
+ Xml::Token token = xml.parse();
+ const QString& tag = xml.s1();
+ switch (token)
+ {
+ case Xml::Error:
+ case Xml::End:
+ goto end_of_paste_at;
+
+ case Xml::TagStart:
+ if (tag == "eventlist")
+ {
+ EventList el;
+ el.read(xml, "eventlist", true);
+ for (iEvent i = el.begin(); i != el.end(); ++i)
+ {
+ Event e = i->second;
+ int tick = e.tick() + pos - dest_part->tick();
+ if (tick<0)
+ {
+ printf("ERROR: trying to add event before current part!\n");
+ goto end_of_paste_at;
+ }
+
+ e.setTick(tick);
+ e.setSelected(true);
+ int diff = e.endTick()-dest_part->lenTick();
+ if (diff > 0) // too short part? extend it
+ {
+ Part* newPart = dest_part->clone();
+ newPart->setLenTick(newPart->lenTick()+diff);
+ // Indicate no undo, and do port controller values but not clone parts.
+ operations.push_back(UndoOp(UndoOp::ModifyPart,dest_part, newPart, true, false)); //FINDMICHJETZT oder andersrum?
+ dest_part = newPart; // reassign TODO FINDME does this work, or has dest_part to be a nonconst reference?
+ }
+ // Indicate no undo, and do not do port controller values and clone parts.
+ operations.push_back(UndoOp(UndoOp::AddEvent,e, dest_part, false, false));
+ }
+ song->applyOperationGroup(operations);
+ goto end_of_paste_at;
+ }
+ else
+ xml.unknown("pasteAt");
+ break;
+
+ case Xml::Attribut:
+ case Xml::TagEnd:
+ default:
+ break;
+ }
}
+
+ end_of_paste_at:
+ song->update(SC_SELECTION);
+}
+
+void select_all(const std::set<Part*>& parts)
+{
+ for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
+ for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++)
+ {
+ Event& event=ev_it->second;
+ event.setSelected(true);
+ }
+ song->update(SC_SELECTION);
}
+void select_none(const std::set<Part*>& parts)
+{
+ for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
+ for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++)
+ {
+ Event& event=ev_it->second;
+ event.setSelected(false);
+ }
+ song->update(SC_SELECTION);
+}
+
+void select_invert(const std::set<Part*>& parts)
+{
+ for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
+ for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++)
+ {
+ Event& event=ev_it->second;
+ event.setSelected(!event.selected());
+ }
+ song->update(SC_SELECTION);
+}
+
+void select_in_loop(const std::set<Part*>& parts)
+{
+ select_none(parts);
+ for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
+ for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++)
+ {
+ Event& event=ev_it->second;
+ event.setSelected((event.tick()>=song->lpos() && event.endTick()<=song->rpos()));
+ }
+ song->update(SC_SELECTION);
+}
+
+void select_not_in_loop(const std::set<Part*>& parts)
+{
+ select_none(parts);
+ for (set<Part*>::iterator part=parts.begin(); part!=parts.end(); part++)
+ for (iEvent ev_it=(*part)->events()->begin(); ev_it!=(*part)->events()->end(); ev_it++)
+ {
+ Event& event=ev_it->second;
+ event.setSelected(!(event.tick()>=song->lpos() && event.endTick()<=song->rpos()));
+ }
+ song->update(SC_SELECTION);
+}
void read_function_dialog_config(Xml& xml)
@@ -554,6 +868,8 @@ void read_function_dialog_config(Xml& xml)
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;
@@ -581,6 +897,7 @@ void write_function_dialog_config(int level, Xml& 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");
}