ardour
ghostregion.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000-2007 Paul Davis
3 
4  This program is free software; you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or
7  (at your option) any later version.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 
18 */
19 
20 #include "evoral/Note.hpp"
21 #include "canvas/container.h"
22 #include "canvas/polygon.h"
23 #include "canvas/rectangle.h"
24 #include "canvas/wave_view.h"
25 #include "canvas/debug.h"
26 
27 #include "ardour_ui.h"
28 #include "automation_time_axis.h"
29 #include "ghostregion.h"
30 #include "midi_streamview.h"
31 #include "midi_time_axis.h"
32 #include "rgb_macros.h"
33 #include "note.h"
34 #include "hit.h"
35 
36 using namespace std;
37 using namespace Editing;
38 using namespace ArdourCanvas;
39 using namespace ARDOUR;
40 
41 PBD::Signal1<void,GhostRegion*> GhostRegion::CatchDeletion;
42 
43 GhostRegion::GhostRegion (ArdourCanvas::Container* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_pos)
44  : trackview (tv)
45  , source_trackview (source_tv)
46 {
47  group = new ArdourCanvas::Container (parent);
48  CANVAS_DEBUG_NAME (group, "ghost region");
49  group->set_position (ArdourCanvas::Duple (initial_pos, 0));
50 
51  base_rect = new ArdourCanvas::Rectangle (group);
52  CANVAS_DEBUG_NAME (base_rect, "ghost region rect");
53  base_rect->set_x0 (0);
54  base_rect->set_y0 (1.0);
56  base_rect->set_outline (false);
57 
58  if (!is_automation_ghost()) {
59  base_rect->hide();
60  }
61 
63 
64  /* the parent group of a ghostregion is a dedicated group for ghosts,
65  so the new ghost would want to get to the top of that group*/
66  group->raise_to_top ();
67 }
68 
70 {
71  CatchDeletion (this);
72  delete base_rect;
73  delete group;
74 }
75 
76 void
78 {
79  base_rect->set_x1 (units);
80 }
81 
82 void
84 {
86 }
87 
88 void
90 {
91  if (is_automation_ghost()) {
92  base_rect->set_fill_color (ARDOUR_UI::config()->color_mod ("ghost track base", "ghost track base"));
93  }
94 }
95 
96 guint
97 GhostRegion::source_track_color(unsigned char alpha)
98 {
99  Gdk::Color color = source_trackview.color();
100  return RGBA_TO_UINT (color.get_red() / 256, color.get_green() / 256, color.get_blue() / 256, alpha);
101 }
102 
103 bool
105 {
106  return (dynamic_cast<AutomationTimeAxisView*>(&trackview)) != 0;
107 }
108 
109 AudioGhostRegion::AudioGhostRegion(TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos)
110  : GhostRegion(tv.ghost_group(), tv, source_tv, initial_unit_pos)
111 {
112 
113 }
114 
115 void
117 {
118  for (vector<WaveView*>::iterator i = waves.begin(); i != waves.end(); ++i) {
119  (*i)->set_samples_per_pixel (fpp);
120  }
121 }
122 
123 void
125 {
126  vector<WaveView*>::iterator i;
127  uint32_t n;
128 
130 
131  double const ht = ((trackview.current_height()) / (double) waves.size());
132 
133  for (n = 0, i = waves.begin(); i != waves.end(); ++i, ++n) {
134  (*i)->set_height (ht);
135  (*i)->set_y_position (n * ht);
136  }
137 }
138 
139 void
141 {
143  guint fill_color;
144 
145  if (is_automation_ghost()) {
146  fill_color = ARDOUR_UI::config()->color ("ghost track wave fill");
147  }
148  else {
149  fill_color = source_track_color(200);
150  }
151 
152  for (uint32_t n=0; n < waves.size(); ++n) {
153  waves[n]->set_outline_color (ARDOUR_UI::config()->color ("ghost track wave"));
154  waves[n]->set_fill_color (fill_color);
155  waves[n]->set_clip_color (ARDOUR_UI::config()->color ("ghost track wave clip"));
156  waves[n]->set_zero_color (ARDOUR_UI::config()->color ("ghost track zero line"));
157  }
158 }
159 
166 MidiGhostRegion::MidiGhostRegion(TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos)
167  : GhostRegion(tv.ghost_group(), tv, source_tv, initial_unit_pos)
168  , _optimization_iterator (events.end ())
169 {
170  base_rect->lower_to_bottom();
171  update_range ();
172 
173  midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiGhostRegion::update_range));
174 }
175 
180 MidiGhostRegion::MidiGhostRegion(MidiStreamView& msv, TimeAxisView& source_tv, double initial_unit_pos)
181  : GhostRegion(msv.midi_underlay_group, msv.trackview(), source_tv, initial_unit_pos)
182  , _optimization_iterator (events.end ())
183 {
184  base_rect->lower_to_bottom();
185  update_range ();
186 
187  midi_view()->NoteRangeChanged.connect (sigc::mem_fun (*this, &MidiGhostRegion::update_range));
188 }
189 
191 {
192  clear_events ();
193 }
194 
195 MidiGhostRegion::GhostEvent::GhostEvent (NoteBase* e, ArdourCanvas::Container* g)
196  : event (e)
197 {
198  Hit* hit = NULL;
199  if (dynamic_cast<Note*>(e)) {
200  item = new ArdourCanvas::Rectangle(
201  g, ArdourCanvas::Rect(e->x0(), e->y0(), e->x1(), e->y1()));
202  } else if ((hit = dynamic_cast<Hit*>(e))) {
203  ArdourCanvas::Polygon* poly = new ArdourCanvas::Polygon(g);
204  poly->set(Hit::points(e->y1() - e->y0()));
205  poly->set_position(hit->position());
206  item = poly;
207  }
208 
209  CANVAS_DEBUG_NAME (item, "ghost note item");
210 }
211 
213 {
214  /* event is not ours to delete */
215  delete item;
216 }
217 
218 void
220 {
221 }
222 
226 {
228  assert (sv);
229  MidiStreamView* msv = dynamic_cast<MidiStreamView*> (sv);
230  assert (msv);
231 
232  return msv;
233 }
234 
235 void
237 {
239  update_range();
240 }
241 
242 void
244 {
246 
247  for (EventList::iterator it = events.begin(); it != events.end(); ++it) {
248  (*it)->item->set_fill_color (ARDOUR_UI::config()->color_mod((*it)->event->base_color(), "ghost track midi fill"));
249  (*it)->item->set_outline_color (ARDOUR_UI::config()->color ("ghost track midi outline"));
250  }
251 }
252 
253 static double
255 {
256  const double tv_height = trackview.current_height();
257  const double note_range = mv->contents_note_range();
258 
259  return std::max(1.0, floor(tv_height / note_range - 1.0));
260 }
261 
262 static double
263 note_y(TimeAxisView& trackview, MidiStreamView* mv, uint8_t note_num)
264 {
265  const double tv_height = trackview.current_height();
266  const double note_range = mv->contents_note_range();
267  const double s = tv_height / note_range;
268 
269  return tv_height - (note_num + 1 - mv->lowest_note()) * s;
270 }
271 
272 void
274 {
275  MidiStreamView* mv = midi_view();
276 
277  if (!mv) {
278  return;
279  }
280 
281  double const h = note_height(trackview, mv);
282 
283  for (EventList::iterator it = events.begin(); it != events.end(); ++it) {
284  uint8_t const note_num = (*it)->event->note()->note();
285 
286  if (note_num < mv->lowest_note() || note_num > mv->highest_note()) {
287  (*it)->item->hide();
288  } else {
289  (*it)->item->show();
290  double const y = note_y(trackview, mv, note_num);
291  ArdourCanvas::Rectangle* rect = NULL;
292  ArdourCanvas::Polygon* poly = NULL;
293  if ((rect = dynamic_cast<ArdourCanvas::Rectangle*>((*it)->item))) {
294  rect->set_y0 (y);
295  rect->set_y1 (y + h);
296  } else if ((poly = dynamic_cast<ArdourCanvas::Polygon*>((*it)->item))) {
297  Duple position = poly->position();
298  position.y = y;
299  poly->set_position(position);
300  poly->set(Hit::points(h));
301  }
302  }
303  }
304 }
305 
306 void
308 {
309  GhostEvent* event = new GhostEvent (n, group);
310  events.push_back (event);
311 
312  event->item->set_fill_color (ARDOUR_UI::config()->color_mod(n->base_color(), "ghost track midi fill"));
313  event->item->set_outline_color (ARDOUR_UI::config()->color ("ghost track midi outline"));
314 
315  MidiStreamView* mv = midi_view();
316 
317  if (mv) {
318  uint8_t const note_num = n->note()->note();
319  double const h = note_height(trackview, mv);
320  double const y = note_y(trackview, mv, note_num);
321 
322  if (note_num < mv->lowest_note() || note_num > mv->highest_note()) {
323  event->item->hide();
324  } else {
325  ArdourCanvas::Rectangle* rect = NULL;
326  ArdourCanvas::Polygon* poly = NULL;
327  if ((rect = dynamic_cast<ArdourCanvas::Rectangle*>(event->item))) {
328  rect->set_y0 (y);
329  rect->set_y1 (y + h);
330  } else if ((poly = dynamic_cast<ArdourCanvas::Polygon*>(event->item))) {
331  Duple position = poly->position();
332  position.y = y;
333  poly->set_position(position);
334  poly->set(Hit::points(h));
335  }
336  }
337  }
338 }
339 
340 void
342 {
343  for (EventList::iterator it = events.begin(); it != events.end(); ++it) {
344  delete *it;
345  }
346 
347  events.clear();
349 }
350 
354 void
356 {
357  GhostEvent* ev = find_event (parent);
358  if (!ev) {
359  return;
360  }
361 
362  Note* note = NULL;
363  ArdourCanvas::Rectangle* rect = NULL;
364  Hit* hit = NULL;
365  ArdourCanvas::Polygon* poly = NULL;
366  if ((note = dynamic_cast<Note*>(parent))) {
367  if ((rect = dynamic_cast<ArdourCanvas::Rectangle*>(ev->item))) {
368  double const x1 = parent->x0 ();
369  double const x2 = parent->x1 ();
370  rect->set_x0 (x1);
371  rect->set_x1 (x2);
372  }
373  } else if ((hit = dynamic_cast<Hit*>(parent))) {
374  if ((poly = dynamic_cast<ArdourCanvas::Polygon*>(ev->item))) {
375  ArdourCanvas::Duple ppos = hit->position();
376  ArdourCanvas::Duple gpos = poly->position();
377  gpos.x = ppos.x;
378  poly->set_position(gpos);
379  }
380  }
381 }
382 
383 void
385 {
386  GhostEvent* ev = find_event (note);
387  if (!ev) {
388  return;
389  }
390 
391  events.remove (ev);
392  delete ev;
394 }
395 
403 {
404  /* we are using _optimization_iterator to speed up the common case where a caller
405  is going through our notes in order.
406  */
407 
408  if (_optimization_iterator != events.end()) {
410  }
411 
412  if (_optimization_iterator != events.end() && (*_optimization_iterator)->event == parent) {
413  return *_optimization_iterator;
414  }
415 
417  if ((*_optimization_iterator)->event == parent) {
418  return *_optimization_iterator;
419  }
420  }
421 
422  return 0;
423 }
void set_duration(double units)
Definition: ghostregion.cc:77
void add_note(NoteBase *)
Definition: ghostregion.cc:307
ArdourCanvas::Color color(const std::string &, bool *failed=0) const
Definition: ui_config.cc:567
void remove_note(NoteBase *)
Definition: ghostregion.cc:384
virtual ArdourCanvas::Coord x1() const =0
GhostEvent(::NoteBase *, ArdourCanvas::Container *)
Definition: ghostregion.cc:195
ArdourCanvas::Duple position()
Definition: hit.cc:102
void set_samples_per_pixel(double spu)
Definition: ghostregion.cc:219
Definition: note.h:32
virtual ArdourCanvas::Coord x0() const =0
MidiGhostRegion::GhostEvent * find_event(NoteBase *)
Definition: ghostregion.cc:402
virtual ArdourCanvas::Coord y1() const =0
void set_samples_per_pixel(double)
Definition: ghostregion.cc:116
virtual ArdourCanvas::Coord y0() const =0
void update_note(NoteBase *)
Definition: ghostregion.cc:355
std::vector< ArdourCanvas::WaveView * > waves
Definition: ghostregion.h:69
virtual StreamView * view() const
guint source_track_color(unsigned char alpha=0xff)
Definition: ghostregion.cc:97
Definition: Beats.hpp:239
virtual ~GhostRegion()
Definition: ghostregion.cc:69
uint32_t current_height() const
EventList events
Definition: ghostregion.h:106
TimeAxisView & source_trackview
Definition: ghostregion.h:54
sigc::signal< void > NoteRangeChanged
uint8_t lowest_note() const
uint8_t contents_note_range() const
Definition: hit.h:30
uint32_t base_color()
Definition: note_base.cc:159
MidiStreamView * midi_view()
Definition: ghostregion.cc:225
Definition: amp.h:29
ArdourCanvas::Item * item
Definition: ghostregion.h:80
TimeAxisView & trackview
Definition: ghostregion.h:52
EventList::iterator _optimization_iterator
Definition: ghostregion.h:107
static double note_y(TimeAxisView &trackview, MidiStreamView *mv, uint8_t note_num)
Definition: ghostregion.cc:263
const boost::shared_ptr< NoteType > note() const
Definition: note_base.h:103
virtual void set_colors()
Definition: ghostregion.cc:89
ArdourCanvas::Rectangle * base_rect
Definition: ghostregion.h:56
static ArdourCanvas::Points points(ArdourCanvas::Distance height)
Definition: hit.cc:80
MidiGhostRegion(TimeAxisView &tv, TimeAxisView &source_tv, double initial_unit_pos)
Definition: ghostregion.cc:166
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > position
Definition: region.cc:65
static UIConfiguration * config()
Definition: ardour_ui.h:188
uint8_t highest_note() const
bool is_automation_ghost()
Definition: ghostregion.cc:104
static double note_height(TimeAxisView &trackview, MidiStreamView *mv)
Definition: ghostregion.cc:254
Gdk::Color color() const
Definition: axis_view.h:50
#define RGBA_TO_UINT(r, g, b, a)
Definition: rgb_macros.h:34
static PBD::Signal1< void, GhostRegion * > CatchDeletion
Definition: ghostregion.h:58
virtual void set_height()
Definition: ghostregion.cc:83
ArdourCanvas::Container * group
Definition: ghostregion.h:55
AudioGhostRegion(TimeAxisView &tv, TimeAxisView &source_tv, double initial_unit_pos)
Definition: ghostregion.cc:109
LIBARDOUR_API PBD::PropertyDescriptor< bool > color
Definition: route_group.cc:50
GhostRegion(ArdourCanvas::Container *parent, TimeAxisView &tv, TimeAxisView &source_tv, double initial_unit_pos)
Definition: ghostregion.cc:43