1
Fork 0

save/load loop

This commit is contained in:
Leonard Ritter 2010-02-10 20:50:06 +01:00
parent 363f1a9dbe
commit e52f3b6d67
5 changed files with 191 additions and 31 deletions

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<interface>
<requires lib="gtk+" version="2.16"/>
<!-- interface-naming-policy project-wide -->
<!-- interface-naming-policy project-wide -->
<object class="GtkWindow" id="main">
<property name="title" translatable="yes">Jacker</property>
<property name="window_position">center</property>
@ -402,7 +402,7 @@
</child>
<child>
<object class="GtkDrawingArea" id="track_measure">
<property name="height_request">22</property>
<property name="height_request">28</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="events">GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON1_MOTION_MASK | GDK_BUTTON2_MOTION_MASK | GDK_BUTTON3_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_FOCUS_CHANGE_MASK | GDK_STRUCTURE_MASK | GDK_PROPERTY_CHANGE_MASK | GDK_VISIBILITY_NOTIFY_MASK | GDK_PROXIMITY_IN_MASK | GDK_PROXIMITY_OUT_MASK | GDK_SUBSTRUCTURE_MASK | GDK_SCROLL_MASK</property>

View File

@ -68,6 +68,11 @@ public:
root["events"] = events;
}
}
void collect(Json::Value &root, Loop &loop) {
root["begin"] = loop.get_begin();
root["end"] = loop.get_end();
}
void collect(Json::Value &root, Model &model) {
root["format"] = "jacker-song";
@ -76,6 +81,13 @@ public:
root["frames_per_beat"] = model.frames_per_beat;
root["beats_per_bar"] = model.beats_per_bar;
root["beats_per_minute"] = model.beats_per_minute;
root["enable_loop"] = model.enable_loop;
Json::Value loop;
collect(loop, model.loop);
if (!loop.empty()) {
root["loop"] = loop;
}
Json::Value patterns;
@ -130,6 +142,13 @@ public:
target = value.asInt();
return true;
}
bool extract(const Json::Value &value, bool &target) {
if (!value.isBool())
return false;
target = value.asBool();
return true;
}
void build(const Json::Value &root, Pattern::Event &event) {
extract(root["frame"], event.frame);
@ -177,6 +196,14 @@ public:
song.add_event(event);
}
}
void build(const Json::Value &root, Loop &loop) {
int begin = 0;
int end = 0;
extract(root["begin"], begin);
extract(root["end"], end);
loop.set(begin, end);
}
void build(const Json::Value &root, Model &model) {
model.reset();
@ -184,6 +211,12 @@ public:
extract(root["frames_per_beat"], model.frames_per_beat);
extract(root["beats_per_bar"], model.beats_per_bar);
extract(root["beats_per_minute"], model.beats_per_minute);
extract(root["enable_loop"], model.enable_loop);
const Json::Value loop = root["loop"];
if (!loop.empty()) {
build(loop, model.loop);
}
const Json::Value patterns = root["patterns"];
for (size_t i = 0; i < patterns.size(); ++i) {

View File

@ -1,6 +1,7 @@
#include "measure.hpp"
#include "model.hpp"
#include <cassert>
namespace Jacker {
@ -17,6 +18,7 @@ MeasureView::MeasureView(BaseObjectType* cobject,
: Gtk::Widget(cobject) {
adjustment = NULL;
model = NULL;
interact_mode = InteractNone;
orientation = OrientationHorizontal;
colors.resize(ColorCount);
colors[ColorBlack].set("#000000");
@ -119,26 +121,14 @@ bool MeasureView::on_expose_event(GdkEventExpose* event) {
}
// loop
if ((orientation == OrientationHorizontal) && model->enable_loop) {
if (/*(orientation == OrientationHorizontal) &&*/ model->enable_loop) {
int lc1 = int(((model->loop.get_begin()-value)*scale)+0.5);
int lc2 = int(((model->loop.get_end()-value)*scale)+0.5);
std::vector<Gdk::Point> points;
x = lc1; y = height-1; flip(x,y);
points.push_back(Gdk::Point(x,y));
x = lc1+8; y = height-1; flip(x,y);
points.push_back(Gdk::Point(x,y));
x = lc1; y = height-9; flip(x,y);
points.push_back(Gdk::Point(x,y));
window->draw_polygon(gc, true, points);
points.clear();
x = lc2; y = height-1; flip(x,y);
points.push_back(Gdk::Point(x,y));
x = lc2-8; y = height-1; flip(x,y);
points.push_back(Gdk::Point(x,y));
x = lc2; y = height-9; flip(x,y);
points.push_back(Gdk::Point(x,y));
window->draw_polygon(gc, true, points);
x = lc1; y = height-1;
render_arrow(x,y, true);
x = lc2; y = height-1;
render_arrow(x,y, false);
}
// bottom bar
@ -148,7 +138,60 @@ bool MeasureView::on_expose_event(GdkEventExpose* event) {
return true;
}
Gdk::Point MeasureView::flip(const Gdk::Point &pt) {
int x = pt.get_x();
int y = pt.get_y();
flip(x,y);
return Gdk::Point(x,y);
}
void MeasureView::render_arrow(int x, int y, bool begin) {
std::vector<Gdk::Point> points;
y -= 1;
const int R = 5;
points.push_back(flip(Gdk::Point(x,y)));
points.push_back(flip(Gdk::Point(x+R,y-R)));
points.push_back(flip(Gdk::Point(x+R,y-R*3)));
points.push_back(flip(Gdk::Point(x-R,y-R*3)));
points.push_back(flip(Gdk::Point(x-R,y-R)));
gc->set_foreground(colors[ColorWhite]);
window->draw_polygon(gc, true, points);
gc->set_foreground(colors[ColorBlack]);
window->draw_polygon(gc, false, points);
points.clear();
if (begin) {
points.push_back(flip(Gdk::Point(x-R+2,y-R-2)));
points.push_back(flip(Gdk::Point(x+R-2,y-R-2)));
points.push_back(flip(Gdk::Point(x-R+2,y-R*3+2)));
window->draw_polygon(gc, false, points);
} else {
points.push_back(flip(Gdk::Point(x+R-1,y-R-1)));
points.push_back(flip(Gdk::Point(x-R+1,y-R-1)));
points.push_back(flip(Gdk::Point(x+R-1,y-R*3+1)));
window->draw_polygon(gc, true, points);
}
}
int MeasureView::get_pixel(int frame) {
assert(adjustment);
if (!window)
return 0;
int width = 0;
int height = 0;
window->get_size(width, height);
flip(width, height);
double value = adjustment->get_value();
double page_size = adjustment->get_page_size();
double scale = (double)width / page_size;
return int((frame - value)*scale+0.5);
}
int MeasureView::get_frame(int x, int y) {
assert(adjustment);
if (!window)
return 0;
flip(x,y);
int width = 0;
int height = 0;
@ -157,35 +200,100 @@ int MeasureView::get_frame(int x, int y) {
double value = adjustment->get_value();
double page_size = adjustment->get_page_size();
double scale = (double)width / page_size;
int frame = (x / scale) + value;
frame -= frame % model->get_frames_per_bar();
return ((x / scale) + value);
}
int MeasureView::quantize_frame(int frame) {
assert(model);
int step = model->get_frames_per_bar();
frame += step / 2;
frame -= frame % step;
return frame;
}
bool MeasureView::on_motion_notify_event(GdkEventMotion *event) {
return false;
bool MeasureView::hit_loop_begin(int x) {
if (!model->enable_loop)
return false;
return (std::abs(get_pixel(model->loop.get_begin()) - x) <= 4);
}
bool MeasureView::hit_loop_end(int x) {
if (!model->enable_loop)
return false;
return (std::abs(get_pixel(model->loop.get_end()) - x) <= 4);
}
bool MeasureView::on_button_press_event(GdkEventButton* event) {
bool ctrl_down = event->state & Gdk::CONTROL_MASK;
bool alt_down = event->state & Gdk::MOD1_MASK;
int frame = get_frame(event->x,event->y);
int x = event->x;
int y = event->y;
flip(x,y);
if (ctrl_down) {
model->loop.set_begin(frame);
model->loop.set_begin(quantize_frame(frame));
invalidate();
_loop_changed();
} else if (alt_down) {
model->loop.set_end(frame);
model->loop.set_end(quantize_frame(frame));
invalidate();
_loop_changed();
} else {
_seek_request(frame);
drag.start(event->x, event->y);
interact_mode = InteractDrag;
}
return false;
}
bool MeasureView::on_motion_notify_event(GdkEventMotion *event) {
if (interact_mode == InteractDrag) {
drag.update(event->x, event->y);
if (drag.threshold_reached()) {
int x = drag.start_x;
int y = drag.start_y;
flip(x,y);
if (hit_loop_begin(x))
interact_mode = InteractDragLoopBegin;
else if (hit_loop_end(x))
interact_mode = InteractDragLoopEnd;
else
interact_mode = InteractSeek;
}
}
if (interact_mode == InteractDragLoopBegin) {
int frame = quantize_frame(get_frame(event->x,event->y));
if (frame != model->loop.get_begin()) {
model->loop.set_begin(frame);
invalidate();
_loop_changed();
return true;
}
} else if (interact_mode == InteractDragLoopEnd) {
int frame = quantize_frame(get_frame(event->x,event->y));
if (frame != model->loop.get_end()) {
model->loop.set_end(frame);
invalidate();
_loop_changed();
return true;
}
} else if (interact_mode == InteractNone) {
int x = event->x;
int y = event->y;
flip(x,y);
if (hit_loop_begin(x)||hit_loop_end(x))
window->set_cursor(Gdk::Cursor(Gdk::HAND1));
else
window->set_cursor(Gdk::Cursor(Gdk::ARROW));
}
return false;
}
bool MeasureView::on_button_release_event(GdkEventButton* event) {
if ((interact_mode == InteractSeek)||(interact_mode == InteractDrag)) {
int frame = get_frame(event->x,event->y);
_seek_request(quantize_frame(frame));
}
interact_mode = InteractNone;
return false;
}

View File

@ -6,6 +6,8 @@
#error GLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED must be set.
#endif
#include "drag.hpp"
namespace Jacker {
//=============================================================================
@ -17,6 +19,14 @@ public:
OrientationVertical,
};
enum InteractMode {
InteractNone = 0,
InteractDrag,
InteractDragLoopBegin,
InteractDragLoopEnd,
InteractSeek,
};
typedef sigc::signal<void, int> type_seek_request;
typedef sigc::signal<void> type_loop_changed;
@ -49,13 +59,22 @@ public:
protected:
void on_adjustment_value_changed();
void flip(int &x, int &y);
Gdk::Point flip(const Gdk::Point &pt);
void flip(int &x, int &y, int &w, int &h);
int get_pixel(int frame);
int get_frame(int x, int y);
int quantize_frame(int frame);
bool hit_loop_begin(int x);
bool hit_loop_end(int x);
void render_arrow(int x, int y, bool begin);
Orientation orientation;
Gtk::Adjustment *adjustment;
Model *model;
InteractMode interact_mode;
Drag drag;
type_seek_request _seek_request;
type_loop_changed _loop_changed;
};

View File

@ -331,20 +331,20 @@ Loop::Loop() {
}
void Loop::set(int begin, int end) {
this->begin = begin;
this->end = end;
this->begin = std::max(begin,0);
this->end = std::max(end,0);
if (this->begin > this->end)
std::swap(this->begin, this->end);
}
void Loop::set_begin(int begin) {
this->begin = begin;
this->begin = std::max(begin,0);
if (this->begin > this->end)
this->end = this->begin;
}
void Loop::set_end(int end) {
this->end = end;
this->end = std::max(end,0);
if (this->begin > this->end)
this->begin = this->end;
}