save/load loop
This commit is contained in:
parent
363f1a9dbe
commit
e52f3b6d67
|
@ -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>
|
||||
|
|
33
jsong.cpp
33
jsong.cpp
|
@ -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) {
|
||||
|
|
158
measure.cpp
158
measure.cpp
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
19
measure.hpp
19
measure.hpp
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue