ardour
midi_region.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000-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  $Id: midiregion.cc 746 2006-08-02 02:44:23Z drobilla $
19 */
20 
21 #include <cmath>
22 #include <climits>
23 #include <cfloat>
24 
25 #include <set>
26 
27 #include <glibmm/threads.h>
28 #include <glibmm/fileutils.h>
29 #include <glibmm/miscutils.h>
30 
31 #include "evoral/types.hpp"
32 
33 #include "pbd/xml++.h"
34 #include "pbd/basename.h"
35 
37 #include "ardour/midi_model.h"
38 #include "ardour/midi_region.h"
40 #include "ardour/midi_source.h"
41 #include "ardour/region_factory.h"
42 #include "ardour/session.h"
43 #include "ardour/source_factory.h"
44 #include "ardour/tempo.h"
45 #include "ardour/types.h"
46 
47 #include "i18n.h"
48 #include <locale.h>
49 
50 using namespace std;
51 using namespace ARDOUR;
52 using namespace PBD;
53 
54 namespace ARDOUR {
55  namespace Properties {
58  }
59 }
60 
61 void
63 {
64  Properties::start_beats.property_id = g_quark_from_static_string (X_("start-beats"));
65  DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for start-beats = %1\n", Properties::start_beats.property_id));
66  Properties::length_beats.property_id = g_quark_from_static_string (X_("length-beats"));
67  DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for length-beats = %1\n", Properties::length_beats.property_id));
68 }
69 
70 void
71 MidiRegion::register_properties ()
72 {
73  add_property (_start_beats);
74  add_property (_length_beats);
75 }
76 
77 /* Basic MidiRegion constructor (many channels) */
78 MidiRegion::MidiRegion (const SourceList& srcs)
79  : Region (srcs)
80  , _start_beats (Properties::start_beats, Evoral::Beats())
81  , _length_beats (Properties::length_beats, midi_source(0)->length_beats())
82 {
84 
85  midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
86  model_changed ();
87  assert(_name.val().find("/") == string::npos);
88  assert(_type == DataType::MIDI);
89 }
90 
92  : Region (other)
93  , _start_beats (Properties::start_beats, other->_start_beats)
94  , _length_beats (Properties::length_beats, Evoral::Beats())
95 {
98 
99  assert(_name.val().find("/") == string::npos);
100  midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
101  model_changed ();
102 }
103 
106  : Region (other, offset)
107  , _start_beats (Properties::start_beats, Evoral::Beats())
108  , _length_beats (Properties::length_beats, Evoral::Beats())
109 {
111  Evoral::Beats const offset_beats = bfc.from (offset);
112 
113  _start_beats = other->_start_beats.val() + offset_beats;
114  _length_beats = other->_length_beats.val() - offset_beats;
115 
117 
118  assert(_name.val().find("/") == string::npos);
119  midi_source(0)->ModelChanged.connect_same_thread (_source_connection, boost::bind (&MidiRegion::model_changed, this));
120  model_changed ();
121 }
122 
124 {
125 }
126 
130 MidiRegion::clone (string path) const
131 {
133 
134  /* caller must check for pre-existing file */
135  assert (!path.empty());
136  assert (!Glib::file_test (path, Glib::FILE_TEST_EXISTS));
139  path, false, _session.frame_rate()));
140  return clone (newsrc);
141 }
142 
145 {
147  Evoral::Beats const bbegin = bfc.from (_start);
148  Evoral::Beats const bend = bfc.from (_start + _length);
149 
150  {
151  /* Lock our source since we'll be reading from it. write_to() will
152  take a lock on newsrc. */
153  Source::Lock lm (midi_source(0)->mutex());
154  if (midi_source(0)->write_to (lm, newsrc, bbegin, bend)) {
156  }
157  }
158 
159  PropertyList plist;
160 
161  plist.add (Properties::name, PBD::basename_nosuffix (newsrc->name()));
162  plist.add (Properties::whole_file, true);
163  plist.add (Properties::start, _start);
165  plist.add (Properties::length, _length);
167  plist.add (Properties::layer, 0);
168 
169  return boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (newsrc, plist, true));
170 }
171 
172 void
174 {
175  Region::post_set (pc);
176 
179  } else if (pc.contains (Properties::start) && !pc.contains (Properties::start_beats)) {
181  }
182 }
183 
184 void
186 {
188  _start_beats = c.from (_start);
189 }
190 
191 void
193 {
196 }
197 
198 void
200 {
202 
203  /* _position has now been updated for the new tempo map */
205 
207 }
208 
209 void
211 {
213  _length_beats = converter.from (_length);
214 }
215 
216 void
217 MidiRegion::set_position_internal (framepos_t pos, bool allow_bbt_recompute)
218 {
219  Region::set_position_internal (pos, allow_bbt_recompute);
220  /* zero length regions don't exist - so if _length_beats is zero, this object
221  is under construction.
222  */
223  if (_length_beats.val() == Evoral::Beats()) {
224  /* leave _length_beats alone, and change _length to reflect the state of things
225  at the new position (tempo map may dictate a different number of frames
226  */
229  }
230 }
231 
235  framecnt_t dur,
236  uint32_t chan_n,
237  NoteMode mode,
238  MidiStateTracker* tracker,
239  MidiChannelFilter* filter) const
240 {
241  return _read_at (_sources, out, position, dur, chan_n, mode, tracker, filter);
242 }
243 
246 {
247  return _read_at (_master_sources, out, position, dur, chan_n, mode); /* no tracker */
248 }
249 
254  framecnt_t dur,
255  uint32_t chan_n,
256  NoteMode mode,
257  MidiStateTracker* tracker,
258  MidiChannelFilter* filter) const
259 {
260  frameoffset_t internal_offset = 0;
261  framecnt_t to_read = 0;
262 
263  /* precondition: caller has verified that we cover the desired section */
264 
265  assert(chan_n == 0);
266 
267  if (muted()) {
268  return 0; /* read nothing */
269  }
270 
271  if (position < _position) {
272  /* we are starting the read from before the start of the region */
273  internal_offset = 0;
274  dur -= _position - position;
275  } else {
276  /* we are starting the read from after the start of the region */
277  internal_offset = position - _position;
278  }
279 
280  if (internal_offset >= _length) {
281  return 0; /* read nothing */
282  }
283 
284  if ((to_read = min (dur, _length - internal_offset)) == 0) {
285  return 0; /* read nothing */
286  }
287 
289 
291 
292  src->set_note_mode(lm, mode);
293 
294  /*
295  cerr << "MR " << name () << " read @ " << position << " * " << to_read
296  << " _position = " << _position
297  << " _start = " << _start
298  << " intoffset = " << internal_offset
299  << endl;
300  */
301 
302  /* This call reads events from a source and writes them to `dst' timed in session frames */
303 
304  if (src->midi_read (
305  lm, // source lock
306  dst, // destination buffer
307  _position - _start, // start position of the source in session frames
308  _start + internal_offset, // where to start reading in the source
309  to_read, // read duration in frames
310  tracker,
311  filter,
313  ) != to_read) {
314  return 0; /* "read nothing" */
315  }
316 
317  return to_read;
318 }
319 
320 XMLNode&
322 {
323  return Region::state ();
324 }
325 
326 int
327 MidiRegion::set_state (const XMLNode& node, int version)
328 {
329  int ret = Region::set_state (node, version);
330 
331  if (ret == 0) {
333  }
334 
335  return ret;
336 }
337 
338 void
340 {
341  /* our length has changed
342  * so what? stuck notes are dealt with via a note state tracker
343  */
344 }
345 
346 void
348 {
349  /* as above, but the shift was from the front
350  * maybe bump currently active note's note-ons up so they sound here?
351  * that could be undesireable in certain situations though.. maybe
352  * remove the note entirely, including it's note off? something needs to
353  * be done to keep the played MIDI sane to avoid messing up voices of
354  * polyhonic things etc........
355  */
356 }
357 
358 int
360 {
361  // TODO
362  return -1;
363 }
364 
366 MidiRegion::control (const Evoral::Parameter& id, bool create)
367 {
368  return model()->control(id, create);
369 }
370 
373 {
374  return model()->control(id);
375 }
376 
379 {
380  return midi_source()->model();
381 }
382 
385 {
386  return midi_source()->model();
387 }
388 
390 MidiRegion::midi_source (uint32_t n) const
391 {
392  // Guaranteed to succeed (use a static cast?)
394 }
395 
396 void
398 {
399  if (!model()) {
400  return;
401  }
402 
403  /* build list of filtered Parameters, being those whose automation state is not `Play' */
404 
405  _filtered_parameters.clear ();
406 
407  Automatable::Controls const & c = model()->controls();
408 
409  for (Automatable::Controls::const_iterator i = c.begin(); i != c.end(); ++i) {
411  assert (ac);
412  if (ac->alist()->automation_state() != Play) {
413  _filtered_parameters.insert (ac->parameter ());
414  }
415  }
416 
417  /* watch for changes to controls' AutoState */
418  midi_source()->AutomationStateChanged.connect_same_thread (
420  );
421 }
422 
423 void
425 {
426  /* Update our filtered parameters list after a change to a parameter's AutoState */
427 
428  boost::shared_ptr<AutomationControl> ac = model()->automation_control (p);
429  if (!ac || ac->alist()->automation_state() == Play) {
430  /* It should be "impossible" for ac to be NULL, but if it is, don't
431  filter the parameter so events aren't lost. */
432  _filtered_parameters.erase (p);
433  } else {
434  _filtered_parameters.insert (p);
435  }
436 
437  /* the source will have an iterator into the model, and that iterator will have been set up
438  for a given set of filtered_parameters, so now that we've changed that list we must invalidate
439  the iterator.
440  */
441  Glib::Threads::Mutex::Lock lm (midi_source(0)->mutex(), Glib::Threads::TRY_LOCK);
442  if (lm.locked()) {
443  /* TODO: This is too aggressive, we need more fine-grained invalidation. */
444  midi_source(0)->invalidate (lm);
445  }
446 }
447 
451 void
453 {
455 
456  model()->insert_silence_at_start (c.from (-_start));
457  _start = 0;
459 }
460 
462 void
463 MidiRegion::transpose (int semitones)
464 {
466  model()->transpose (c.from (_start), c.from (_start + _length), semitones);
467 }
468 
469 void
471 {
474 }
ARDOUR::Session & _session
virtual framecnt_t midi_read(const Lock &lock, Evoral::EventSink< framepos_t > &dst, framepos_t source_start, framepos_t start, framecnt_t cnt, MidiStateTracker *tracker, MidiChannelFilter *filter, const std::set< Evoral::Parameter > &filtered) const
Definition: midi_source.cc:188
framecnt_t read_at(Evoral::EventSink< framepos_t > &dst, framepos_t position, framecnt_t dur, uint32_t chan_n=0, NoteMode mode=Sustained, MidiStateTracker *tracker=0, MidiChannelFilter *filter=0) const
Definition: midi_region.cc:233
NoteMode
Definition: types.h:204
Glib::Threads::Mutex::Lock Lock
Definition: source.h:54
void register_properties()
Definition: midi_region.cc:71
LIBARDOUR_API PBD::PropertyDescriptor< layer_t > layer
Definition: region.cc:67
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
LIBARDOUR_API PBD::PropertyDescriptor< std::string > name
T const & val() const
Definition: properties.h:96
void post_set(const PBD::PropertyChange &)
Definition: region.cc:1700
TempoMap & tempo_map()
Definition: session.h:596
Definition: Beats.hpp:239
boost::shared_ptr< MidiRegion > clone(std::string path=std::string()) const
void update_length_beats()
Definition: midi_region.cc:210
MidiRegion(const SourceList &)
Definition: midi_region.cc:78
boost::shared_ptr< Source > source(uint32_t n=0) const
Definition: region.h:258
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > start
Definition: region.cc:63
framecnt_t _read_at(const SourceList &, Evoral::EventSink< framepos_t > &dst, framepos_t position, framecnt_t dur, uint32_t chan_n=0, NoteMode mode=Sustained, MidiStateTracker *tracker=0, MidiChannelFilter *filter=0) const
Definition: midi_region.cc:251
framecnt_t frame_rate() const
Definition: session.h:365
XMLNode & state()
Definition: midi_region.cc:321
boost::shared_ptr< Evoral::Control > control(const Evoral::Parameter &id, bool create=false)
Definition: midi_region.cc:366
void update_after_tempo_map_change()
Definition: midi_region.cc:199
framepos_t framepos_minus_beats(framepos_t, Evoral::Beats) const
Definition: tempo.cc:2052
virtual void update_after_tempo_map_change()
Definition: region.cc:560
SourceList _master_sources
Definition: region.h:372
virtual int set_state(const XMLNode &, int version)
Definition: region.cc:1234
void set_start_beats_from_start_frames()
Definition: midi_region.cc:185
SourceList _sources
Definition: region.h:370
#define X_(Text)
Definition: i18n.h:13
PBD::Property< framepos_t > _start
Definition: region.h:364
int64_t framecnt_t
Definition: types.h:76
virtual void set_position_internal(framepos_t pos, bool allow_bbt_recompute)
Definition: region.cc:595
void post_set(const PBD::PropertyChange &)
Definition: midi_region.cc:173
AutoState automation_state() const
Definition: amp.h:29
bool muted() const
Definition: region.h:162
std::set< Evoral::Parameter > _filtered_parameters
parameters that we ask our source not to return when reading
Definition: midi_region.h:146
std::vector< boost::shared_ptr< Source > > SourceList
Definition: region.h:91
LIBARDOUR_API PBD::PropertyDescriptor< Evoral::Beats > start_beats
Definition: midi_region.cc:56
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
LIBARDOUR_API void make_property_quarks()
int64_t framepos_t
Definition: types.h:66
RouteGroup::RouteGroup(Session &s, const string &n) add_property(_relative)
Definition: route_group.cc:101
int64_t frameoffset_t
Definition: types.h:71
PBD::Property< std::string > _name
std::map< Parameter, boost::shared_ptr< Control > > Controls
Definition: ControlSet.hpp:57
PBD::ScopedConnection _model_connection
Definition: midi_region.h:147
const Parameter & parameter() const
Definition: Control.hpp:69
framepos_t position() const
Definition: region.h:112
LIBPBD_API uint64_t Properties
Definition: debug.cc:47
framecnt_t master_read_at(MidiRingBuffer< framepos_t > &dst, framepos_t position, framecnt_t dur, uint32_t chan_n=0, NoteMode mode=Sustained) const
Definition: midi_region.cc:245
Glib::Threads::Mutex & mutex()
Definition: source.h:105
LIBPBD_API Glib::ustring basename_nosuffix(Glib::ustring)
int set_state(const XMLNode &, int version)
Definition: midi_region.cc:327
virtual XMLNode & state()
Definition: region.cc:1136
PBD::ScopedConnection _source_connection
Definition: midi_region.h:148
Definition: xml++.h:95
DataType _type
Definition: region.h:358
LIBARDOUR_API PBD::PropertyDescriptor< Evoral::Beats > length_beats
Definition: midi_region.cc:57
std::string name() const
LIBARDOUR_API PBD::PropertyDescriptor< framepos_t > position
Definition: region.cc:65
virtual void set_start_internal(framecnt_t)
Definition: region.cc:1708
Definition: debug.h:30
void send_change(const PBD::PropertyChange &)
Definition: region.cc:1311
LIBEVORAL_API uint64_t Beats
boost::shared_ptr< AutomationList > alist() const
bool contains(PropertyDescriptor< T > p) const
void model_automation_state_changed(Evoral::Parameter const &)
Definition: midi_region.cc:424
boost::shared_ptr< MidiSource > midi_source(uint32_t n=0) const
Definition: midi_region.cc:390
PBD::Property< Evoral::Beats > _length_beats
Definition: midi_region.h:115
int separate_by_channel(ARDOUR::Session &, std::vector< boost::shared_ptr< Region > > &) const
Definition: midi_region.cc:359
virtual void set_length_internal(framecnt_t)
Definition: region.cc:468
PBD::Property< Evoral::Beats > _start_beats
Definition: midi_region.h:114
void set_note_mode(const Glib::Threads::Mutex::Lock &lock, NoteMode mode)
Definition: midi_source.cc:407
static boost::shared_ptr< Region > create(boost::shared_ptr< const Region > other, bool announce=false)
bool add(PropertyBase *prop)
LIBARDOUR_API PBD::PropertyDescriptor< bool > whole_file
Definition: region.cc:54
boost::shared_ptr< MidiModel > model()
Definition: midi_region.cc:378
static boost::shared_ptr< Source > createWritable(DataType type, Session &, const std::string &path, bool destructive, framecnt_t rate, bool announce=true, bool async=false)
void set_position_internal(framepos_t pos, bool allow_bbt_recompute)
Definition: midi_region.cc:217
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
void set_length_internal(framecnt_t len)
Definition: midi_region.cc:192
PBD::Property< framepos_t > _position
Definition: region.h:366
PBD::Property< framecnt_t > _length
Definition: region.h:365
LIBARDOUR_API PBD::PropertyDescriptor< framecnt_t > length
Definition: region.cc:64
void set_start_internal(framecnt_t)
Definition: midi_region.cc:470