503e6195a9c8aeda5a7930689c2fe0712537ef77
[ardour.git] / gtk2_ardour / midi_streamview.cc
1 /*
2     Copyright (C) 2001, 2006 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 #include <cmath>
20 #include <cassert>
21 #include <utility>
22
23 #include <gtkmm.h>
24
25 #include <gtkmm2ext/gtk_ui.h>
26
27 #include <ardour/midi_playlist.h>
28 #include <ardour/midi_region.h>
29 #include <ardour/midi_source.h>
30 #include <ardour/midi_diskstream.h>
31 #include <ardour/midi_track.h>
32 #include <ardour/smf_source.h>
33 #include <ardour/region_factory.h>
34
35 #include "midi_streamview.h"
36 #include "region_view.h"
37 #include "midi_region_view.h"
38 #include "midi_time_axis.h"
39 #include "canvas-simplerect.h"
40 #include "region_selection.h"
41 #include "selection.h"
42 #include "public_editor.h"
43 #include "ardour_ui.h"
44 #include "rgb_macros.h"
45 #include "gui_thread.h"
46 #include "utils.h"
47 #include "color.h"
48 #include "simplerect.h"
49
50 using namespace std;
51 using namespace ARDOUR;
52 using namespace PBD;
53 using namespace Editing;
54
55 MidiStreamView::MidiStreamView (MidiTimeAxisView& tv)
56         : StreamView (tv)
57 {
58         if (tv.is_track())
59                 stream_base_color = Config->canvasvar_MidiTrackBase.get();
60         else
61                 stream_base_color = Config->canvasvar_MidiBusBase.get();
62         
63         canvas_rect->property_fill_color_rgba() = stream_base_color;
64         canvas_rect->property_outline_color_rgba() = RGBA_BLACK;
65
66         //use_rec_regions = tv.editor.show_waveforms_recording ();
67         use_rec_regions = true;
68 }
69
70 MidiStreamView::~MidiStreamView ()
71 {
72 }
73
74
75 RegionView*
76 MidiStreamView::add_region_view_internal (boost::shared_ptr<Region> r, bool wait_for_waves)
77 {
78         boost::shared_ptr<MidiRegion> region = boost::dynamic_pointer_cast<MidiRegion> (r);
79
80         if (region == 0) {
81                 return NULL;
82         }
83
84         MidiRegionView *region_view;
85         list<RegionView *>::iterator i;
86
87         for (i = region_views.begin(); i != region_views.end(); ++i) {
88                 if ((*i)->region() == r) {
89                         
90                         /* great. we already have a MidiRegionView for this Region. use it again. */
91
92                         (*i)->set_valid (true);
93                         return NULL;
94                 }
95         }
96         
97         // can't we all just get along?
98         assert(_trackview.midi_track()->mode() != Destructive);
99
100         region_view = new MidiRegionView (canvas_group, _trackview, region, 
101                         _samples_per_unit, region_color);
102
103         region_view->init (region_color, wait_for_waves);
104         region_views.push_front (region_view);
105         
106         /* follow global waveform setting */
107
108         // FIXME
109         //region_view->set_waveform_visible(_trackview.editor.show_waveforms());
110
111         /* display events */
112         region_view->begin_write();
113         for (size_t i=0; i < region->midi_source(0)->model()->n_events(); ++i)
114                 region_view->add_event(region->midi_source(0)->model()->event_at(i));
115         region_view->end_write();
116
117         /* catch regionview going away */
118         region->GoingAway.connect (bind (mem_fun (*this, &MidiStreamView::remove_region_view), region));
119         
120         RegionViewAdded (region_view);
121
122         return region_view;
123 }
124
125 // FIXME: code duplication with AudioStreamVIew
126 void
127 MidiStreamView::redisplay_diskstream ()
128 {
129         list<RegionView *>::iterator i, tmp;
130
131         for (i = region_views.begin(); i != region_views.end(); ++i) {
132                 (*i)->set_valid (false);
133         }
134
135         if (_trackview.is_midi_track()) {
136                 _trackview.get_diskstream()->playlist()->foreach_region (static_cast<StreamView*>(this), &StreamView::add_region_view);
137         }
138
139         for (i = region_views.begin(); i != region_views.end(); ) {
140                 tmp = i;
141                 tmp++;
142
143                 if (!(*i)->is_valid()) {
144                         delete *i;
145                         region_views.erase (i);
146                 } 
147
148                 i = tmp;
149         }
150
151         /* now fix layering */
152
153         for (RegionViewList::iterator i = region_views.begin(); i != region_views.end(); ++i) {
154                 region_layered (*i);
155         }
156 }
157
158
159 void
160 MidiStreamView::setup_rec_box ()
161 {
162         // cerr << _trackview.name() << " streamview SRB\n";
163
164         if (_trackview.session().transport_rolling()) {
165
166                 if (!rec_active && 
167                     _trackview.session().record_status() == Session::Recording && 
168                     _trackview.get_diskstream()->record_enabled()) {
169
170                         if (use_rec_regions && rec_regions.size() == rec_rects.size()) {
171
172                                 /* add a new region, but don't bother if they set use_rec_regions mid-record */
173
174                                 MidiRegion::SourceList sources;
175                                 
176                                 for (list<sigc::connection>::iterator prc = rec_data_ready_connections.begin(); prc != rec_data_ready_connections.end(); ++prc) {
177                                         (*prc).disconnect();
178                                 }
179                                 rec_data_ready_connections.clear();
180
181                                 // FIXME
182                                 boost::shared_ptr<MidiDiskstream> mds = boost::dynamic_pointer_cast<MidiDiskstream>(_trackview.get_diskstream());
183                                 assert(mds);
184
185                                 sources.push_back(mds->write_source());
186                                 
187                                 rec_data_ready_connections.push_back (mds->write_source()->ViewDataRangeReady.connect (bind (mem_fun (*this, &MidiStreamView::rec_data_range_ready), boost::weak_ptr<Source>(mds->write_source())))); 
188
189                                 // handle multi
190                                 
191                                 jack_nframes_t start = 0;
192                                 if (rec_regions.size() > 0) {
193                                         start = rec_regions.back().first->start() + _trackview.get_diskstream()->get_captured_frames(rec_regions.size()-1);
194                                 }
195                                 
196                                 boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion>
197                                         (RegionFactory::create (sources, start, 1 , "", 0, (Region::Flag)(Region::DefaultFlags | Region::DoNotSaveState), false)));
198                                 assert(region);
199                                 region->set_position (_trackview.session().transport_frame(), this);
200                                 rec_regions.push_back (make_pair(region, (RegionView*)0));
201                                 
202                                 // rec regions are destroyed in setup_rec_box
203
204                                 /* we add the region later */
205                         }
206                         
207                         /* start a new rec box */
208
209                         boost::shared_ptr<MidiTrack> mt = _trackview.midi_track(); /* we know what it is already */
210                         boost::shared_ptr<MidiDiskstream> ds = mt->midi_diskstream();
211                         jack_nframes_t frame_pos = ds->current_capture_start ();
212                         gdouble xstart = _trackview.editor.frame_to_pixel (frame_pos);
213                         gdouble xend;
214                         uint32_t fill_color;
215
216                         assert(_trackview.midi_track()->mode() == Normal);
217                         
218                         xend = xstart;
219                         fill_color = Config->canvasvar_RecordingRect.get();
220                         
221                         ArdourCanvas::SimpleRect * rec_rect = new Gnome::Canvas::SimpleRect (*canvas_group);
222                         rec_rect->property_x1() = xstart;
223                         rec_rect->property_y1() = 1.0;
224                         rec_rect->property_x2() = xend;
225                         rec_rect->property_y2() = (double) _trackview.height - 1;
226                         rec_rect->property_outline_color_rgba() = Config->canvasvar_RecordingRect.get();
227                         rec_rect->property_fill_color_rgba() = fill_color;
228                         rec_rect->lower_to_bottom();
229                         
230                         RecBoxInfo recbox;
231                         recbox.rectangle = rec_rect;
232                         recbox.start = _trackview.session().transport_frame();
233                         recbox.length = 0;
234                         
235                         rec_rects.push_back (recbox);
236                         
237                         screen_update_connection.disconnect();
238                         screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun (*this, &MidiStreamView::update_rec_box));    
239                         rec_updating = true;
240                         rec_active = true;
241
242                 } else if (rec_active &&
243                            (_trackview.session().record_status() != Session::Recording ||
244                             !_trackview.get_diskstream()->record_enabled())) {
245
246                         screen_update_connection.disconnect();
247                         rec_active = false;
248                         rec_updating = false;
249
250                 }
251                 
252         } else {
253
254                 // cerr << "\tNOT rolling, rec_rects = " << rec_rects.size() << " rec_regions = " << rec_regions.size() << endl;
255
256                 if (!rec_rects.empty() || !rec_regions.empty()) {
257
258                         /* disconnect rapid update */
259                         screen_update_connection.disconnect();
260
261                         for (list<sigc::connection>::iterator prc = rec_data_ready_connections.begin(); prc != rec_data_ready_connections.end(); ++prc) {
262                                 (*prc).disconnect();
263                         }
264                         rec_data_ready_connections.clear();
265
266                         rec_updating = false;
267                         rec_active = false;
268                         
269                         /* remove temp regions */
270                         
271                         for (list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator iter = rec_regions.begin(); iter != rec_regions.end();) {
272                                 list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator tmp;
273                                 
274                                 tmp = iter;
275                                 ++tmp;
276
277                                 (*iter).first->drop_references ();
278
279                                 iter = tmp;
280                         }
281                         
282                         rec_regions.clear();
283
284                         // cerr << "\tclear " << rec_rects.size() << " rec rects\n";
285
286                         /* transport stopped, clear boxes */
287                         for (vector<RecBoxInfo>::iterator iter=rec_rects.begin(); iter != rec_rects.end(); ++iter) {
288                                 RecBoxInfo &rect = (*iter);
289                                 delete rect.rectangle;
290                         }
291                         
292                         rec_rects.clear();
293                         
294                 }
295         }
296 }
297
298 void
299 MidiStreamView::update_rec_regions (boost::shared_ptr<MidiBuffer> data, nframes_t start, nframes_t dur)
300 {
301         ENSURE_GUI_THREAD (bind (mem_fun (*this, &MidiStreamView::update_rec_regions), data, start, dur));
302
303         if (use_rec_regions) {
304
305                 uint32_t n = 0;
306
307                 for (list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator iter = rec_regions.begin(); iter != rec_regions.end(); n++) {
308
309                         list<pair<boost::shared_ptr<Region>,RegionView*> >::iterator tmp;
310
311                         tmp = iter;
312                         ++tmp;
313
314                         if (!canvas_item_visible (rec_rects[n].rectangle)) {
315                                 /* rect already hidden, this region is done */
316                                 iter = tmp;
317                                 continue;
318                         }
319                         
320                         boost::shared_ptr<MidiRegion> region = boost::dynamic_pointer_cast<MidiRegion>(iter->first);
321                         if (!region) {
322                                 continue;
323                         }
324
325                         nframes_t origlen = region->length();
326                         
327                         //cerr << "MIDI URR: " << start << " * " << dur
328                         //      << " (origlen " << origlen << ")" << endl;
329
330                         if (region == rec_regions.back().first && rec_active) {
331
332                                 if (start >= region->start()) {
333
334                                         nframes_t nlen = start + dur - region->start();
335
336                                         if (nlen != region->length()) {
337
338                                                 region->freeze ();
339                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
340                                                 region->set_length (nlen, this);
341                                                 region->thaw ("updated");
342
343                                                 if (origlen == 1) {
344                                                         /* our special initial length */
345                                                         iter->second = add_region_view_internal (region, false);
346                                                         ((MidiRegionView*)iter->second)->begin_write();
347                                                 }
348
349                                                 /* also update rect */
350                                                 ArdourCanvas::SimpleRect * rect = rec_rects[n].rectangle;
351                                                 gdouble xend = _trackview.editor.frame_to_pixel (region->position() + region->length());
352                                                 rect->property_x2() = xend;
353
354                                                 /* draw events */
355                                                 MidiRegionView* mrv = (MidiRegionView*)iter->second;
356                                                 for (size_t i = 0; i < data->size(); ++i) {
357                                                         const MidiEvent& ev = (*data.get())[i];
358                                                         mrv->add_event(ev);
359                                                         mrv->extend_active_notes();
360                                                 }
361
362                                         }
363                                 }
364
365                         } else {
366
367                                 nframes_t nlen = _trackview.get_diskstream()->get_captured_frames(n);
368
369                                 if (nlen != region->length()) {
370
371                                         if (region->source(0)->length() >= region->start() + nlen) {
372
373                                                 region->freeze ();
374                                                 region->set_position (_trackview.get_diskstream()->get_capture_start_frame(n), this);
375                                                 region->set_length (nlen, this);
376                                                 region->thaw ("updated");
377                                                 
378                                                 if (origlen == 1) {
379                                                         /* our special initial length */
380                                                         iter->second = add_region_view_internal (region, false);
381                                                 }
382                                                 
383                                                 /* also hide rect */
384                                                 ArdourCanvas::Item * rect = rec_rects[n].rectangle;
385                                                 rect->hide();
386
387                                         }
388                                 }
389                         }
390
391                         iter = tmp;
392                 }
393         }
394 }
395
396 void
397 MidiStreamView::rec_data_range_ready (boost::shared_ptr<MidiBuffer> data, jack_nframes_t start, jack_nframes_t dur, boost::weak_ptr<Source> weak_src)
398 {
399         // this is called from the butler thread for now
400         
401         ENSURE_GUI_THREAD(bind (mem_fun (*this, &MidiStreamView::rec_data_range_ready), data, start, dur, weak_src));
402         
403         boost::shared_ptr<SMFSource> src (boost::dynamic_pointer_cast<SMFSource>(weak_src.lock()));
404         
405         //cerr << src.get() << " MIDI READY: " << start << " * " << dur
406         //      << " -- " << data->size() << " events!" << endl;
407         
408         this->update_rec_regions (data, start, dur);
409 }
410
411 void
412 MidiStreamView::color_handler ()
413 {
414
415         //case cMidiTrackBase:
416         if (_trackview.is_midi_track()) {
417                 canvas_rect->property_fill_color_rgba() = Config->canvasvar_MidiTrackBase.get();
418         } 
419
420         //case cMidiBusBase:
421         if (!_trackview.is_midi_track()) {
422                 canvas_rect->property_fill_color_rgba() = Config->canvasvar_MidiBusBase.get();;
423         }
424 }
425