Move EventRingBuffer to libardour.
[ardour.git] / libs / ardour / midi_source.cc
1 /*
2     Copyright (C) 2006 Paul Davis
3     Author: David Robillard
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <fcntl.h>
23 #include <float.h>
24 #include <cerrno>
25 #include <ctime>
26 #include <cmath>
27 #include <iomanip>
28 #include <algorithm>
29
30 #include <glibmm/fileutils.h>
31 #include <glibmm/miscutils.h>
32
33 #include "pbd/xml++.h"
34 #include "pbd/pthread_utils.h"
35 #include "pbd/basename.h"
36
37 #include "evoral/EventSink.hpp"
38
39 #include "ardour/debug.h"
40 #include "ardour/midi_model.h"
41 #include "ardour/midi_state_tracker.h"
42 #include "ardour/midi_source.h"
43 #include "ardour/file_source.h"
44 #include "ardour/session.h"
45 #include "ardour/session_directory.h"
46 #include "ardour/source_factory.h"
47
48 #include "i18n.h"
49
50 namespace ARDOUR { template <typename T> class MidiRingBuffer; }
51
52 using namespace std;
53 using namespace ARDOUR;
54 using namespace PBD;
55
56 PBD::Signal1<void,MidiSource*> MidiSource::MidiSourceCreated;
57
58 MidiSource::MidiSource (Session& s, string name, Source::Flag flags)
59         : Source(s, DataType::MIDI, name, flags)
60         , _writing(false)
61         , _model_iter_valid(false)
62         , _length_beats(0.0)
63         , _last_read_end(0)
64         , _capture_length(0)
65         , _capture_loop_length(0)
66 {
67 }
68
69 MidiSource::MidiSource (Session& s, const XMLNode& node)
70         : Source(s, node)
71         , _writing(false)
72         , _model_iter_valid(false)
73         , _length_beats(0.0)
74         , _last_read_end(0)
75         , _capture_length(0)
76         , _capture_loop_length(0)
77 {
78         if (set_state (node, Stateful::loading_state_version)) {
79                 throw failed_constructor();
80         }
81 }
82
83 MidiSource::~MidiSource ()
84 {
85 }
86
87 XMLNode&
88 MidiSource::get_state ()
89 {
90         XMLNode& node (Source::get_state());
91
92         if (_captured_for.length()) {
93                 node.add_property ("captured-for", _captured_for);
94         }
95
96         for (InterpolationStyleMap::const_iterator i = _interpolation_style.begin(); i != _interpolation_style.end(); ++i) {
97                 XMLNode* child = node.add_child (X_("InterpolationStyle"));
98                 child->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
99                 child->add_property (X_("style"), enum_2_string (i->second));
100         }
101
102         for (AutomationStateMap::const_iterator i = _automation_state.begin(); i != _automation_state.end(); ++i) {
103                 XMLNode* child = node.add_child (X_("AutomationState"));
104                 child->add_property (X_("parameter"), EventTypeMap::instance().to_symbol (i->first));
105                 child->add_property (X_("state"), enum_2_string (i->second));
106         }
107
108         return node;
109 }
110
111 int
112 MidiSource::set_state (const XMLNode& node, int /*version*/)
113 {
114         const XMLProperty* prop;
115         if ((prop = node.property ("captured-for")) != 0) {
116                 _captured_for = prop->value();
117         }
118
119         XMLNodeList children = node.children ();
120         for (XMLNodeConstIterator i = children.begin(); i != children.end(); ++i) {
121                 if ((*i)->name() == X_("InterpolationStyle")) {
122                         if ((prop = (*i)->property (X_("parameter"))) == 0) {
123                                 error << _("Missing parameter property on InterpolationStyle") << endmsg;
124                                 return -1;
125                         }
126                         Evoral::Parameter p = EventTypeMap::instance().new_parameter (prop->value());
127
128                         if ((prop = (*i)->property (X_("style"))) == 0) {
129                                 error << _("Missing style property on InterpolationStyle") << endmsg;
130                                 return -1;
131                         }
132                         Evoral::ControlList::InterpolationStyle s = static_cast<Evoral::ControlList::InterpolationStyle>(
133                                 string_2_enum (prop->value(), s));
134                         set_interpolation_of (p, s);
135
136                 } else if ((*i)->name() == X_("AutomationState")) {
137                         if ((prop = (*i)->property (X_("parameter"))) == 0) {
138                                 error << _("Missing parameter property on AutomationState") << endmsg;
139                                 return -1;
140                         }
141                         Evoral::Parameter p = EventTypeMap::instance().new_parameter (prop->value());
142
143                         if ((prop = (*i)->property (X_("state"))) == 0) {
144                                 error << _("Missing state property on AutomationState") << endmsg;
145                                 return -1;
146                         }
147                         AutoState s = static_cast<AutoState> (string_2_enum (prop->value(), s));
148                         set_automation_state_of (p, s);
149                 }
150         }
151
152         return 0;
153 }
154
155 bool
156 MidiSource::empty () const
157 {
158         return !_length_beats;
159 }
160
161 framecnt_t
162 MidiSource::length (framepos_t pos) const
163 {
164         if (!_length_beats) {
165                 return 0;
166         }
167
168         BeatsFramesConverter converter(_session.tempo_map(), pos);
169         return converter.to(_length_beats);
170 }
171
172 void
173 MidiSource::update_length (framecnt_t)
174 {
175         // You're not the boss of me!
176 }
177
178 void
179 MidiSource::invalidate ()
180 {
181         _model_iter_valid = false;
182         _model_iter.invalidate();
183 }
184
185 framecnt_t
186 MidiSource::midi_read (Evoral::EventSink<framepos_t>&     dst,
187                        framepos_t                         source_start,
188                        framepos_t                         start,
189                        framecnt_t                         cnt,
190                        MidiStateTracker*                  tracker,
191                        const std::set<Evoral::Parameter>& filtered) const
192 {
193         Glib::Threads::Mutex::Lock lm (_lock);
194
195         BeatsFramesConverter converter(_session.tempo_map(), source_start);
196
197         DEBUG_TRACE (DEBUG::MidiSourceIO,
198                      string_compose ("MidiSource::midi_read() %5 sstart %1 start %2 cnt %3 tracker %4\n",
199                                      source_start, start, cnt, tracker, name()));
200
201         if (_model) {
202                 // Find appropriate model iterator
203                 Evoral::Sequence<Evoral::MusicalTime>::const_iterator& i = _model_iter;
204                 if (_last_read_end == 0 || start != _last_read_end || !_model_iter_valid) {
205                         // Cached iterator is invalid, search for the first event past start
206                         i                 = _model->begin(converter.from(start), false, filtered);
207                         _model_iter_valid = true;
208                 }
209
210                 _last_read_end = start + cnt;
211
212                 // Copy events in [start, start + cnt) into dst
213                 for (; i != _model->end(); ++i) {
214                         const framecnt_t time_frames = converter.to(i->time());
215                         if (time_frames < start + cnt) {
216                                 // Offset by source start to convert event time to session time
217                                 dst.write (time_frames + source_start, i->event_type(), i->size(), i->buffer());
218
219                                 DEBUG_TRACE (DEBUG::MidiSourceIO,
220                                              string_compose ("%1: add event @ %2 type %3 size %4\n",
221                                                              _name, time_frames + source_start, i->event_type(), i->size()));
222
223                                 if (tracker) {
224                                         tracker->track (*i);
225                                 }
226                         } else {
227                                 DEBUG_TRACE (DEBUG::MidiSourceIO,
228                                              string_compose ("%1: reached end with event @ %2 vs. %3\n",
229                                                              _name, time_frames, start+cnt));
230                                 break;
231                         }
232                 }
233                 return cnt;
234         } else {
235                 return read_unlocked (dst, source_start, start, cnt, tracker);
236         }
237 }
238
239 framecnt_t
240 MidiSource::midi_write (MidiRingBuffer<framepos_t>& source,
241                         framepos_t                  source_start,
242                         framecnt_t                  cnt)
243 {
244         Glib::Threads::Mutex::Lock lm (_lock);
245
246         const framecnt_t ret = write_unlocked (source, source_start, cnt);
247
248         if (cnt == max_framecnt) {
249                 _last_read_end = 0;
250                 invalidate();
251         } else {
252                 _capture_length += cnt;
253         }
254
255         return ret;
256 }
257
258 void
259 MidiSource::mark_streaming_midi_write_started (NoteMode mode)
260 {
261         if (_model) {
262                 _model->set_note_mode (mode);
263                 _model->start_write ();
264         }
265
266         _writing = true;
267 }
268
269 void
270 MidiSource::mark_write_starting_now (framecnt_t position,
271                                      framecnt_t capture_length,
272                                      framecnt_t loop_length)
273 {
274         /* I'm not sure if this is the best way to approach this, but
275            _capture_length needs to be set up with the transport frame
276            when a record actually starts, as it is used by
277            SMFSource::write_unlocked to decide whether incoming notes
278            are within the correct time range.
279            mark_streaming_midi_write_started (perhaps a more logical
280            place to do this) is not called at exactly the time when
281            record starts, and I don't think it necessarily can be
282            because it is not RT-safe.
283         */
284
285         set_timeline_position(position);
286         _capture_length      = capture_length;
287         _capture_loop_length = loop_length;
288
289         BeatsFramesConverter converter(_session.tempo_map(), position);
290         _length_beats = converter.from(capture_length);
291 }
292
293 void
294 MidiSource::mark_streaming_write_started ()
295 {
296         NoteMode note_mode = _model ? _model->note_mode() : Sustained;
297         mark_streaming_midi_write_started (note_mode);
298 }
299
300 void
301 MidiSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption option,
302                                                  Evoral::MusicalTime                                    end)
303 {
304         if (_model) {
305                 _model->end_write (option, end);
306         }
307
308         _writing = false;
309 }
310
311 void
312 MidiSource::mark_streaming_write_completed ()
313 {
314         mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
315 }
316
317 int
318 MidiSource::write_to (boost::shared_ptr<MidiSource> newsrc, Evoral::MusicalTime begin, Evoral::MusicalTime end)
319 {
320         newsrc->set_timeline_position (_timeline_position);
321         newsrc->copy_interpolation_from (this);
322         newsrc->copy_automation_state_from (this);
323
324         if (_model) {
325                 if (begin == Evoral::MinMusicalTime && end == Evoral::MaxMusicalTime) {
326                         _model->write_to (newsrc);
327                 } else {
328                         _model->write_section_to (newsrc, begin, end);
329                 }
330         } else {
331                 error << string_compose (_("programming error: %1"), X_("no model for MidiSource during ::clone()"));
332                 return -1;
333         }
334
335         newsrc->flush_midi();
336
337         /* force a reload of the model if the range is partial */
338
339         if (begin != Evoral::MinMusicalTime || end != Evoral::MaxMusicalTime) {
340                 newsrc->load_model (true, true);
341         } else {
342                 newsrc->set_model (_model);
343         }
344
345         /* this file is not removable (but since it is MIDI, it is mutable) */
346
347         boost::dynamic_pointer_cast<FileSource> (newsrc)->prevent_deletion ();
348
349         return 0;
350 }
351
352 void
353 MidiSource::session_saved()
354 {
355         /* this writes a copy of the data to disk.
356            XXX do we need to do this every time?
357         */
358
359         if (_model && _model->edited()) {
360                 /* The model is edited, write its contents into the current source
361                    file (overwiting previous contents). */
362
363                 /* Temporarily drop our reference to the model so that as the model
364                    pushes its current state to us, we don't try to update it. */
365                 boost::shared_ptr<MidiModel> mm = _model;
366                 _model.reset ();
367
368                 /* Flush model contents to disk. */
369                 mm->sync_to_source ();
370
371                 /* Reacquire model. */
372                 _model = mm;
373
374         } else {
375                 flush_midi();
376         }
377 }
378
379 void
380 MidiSource::set_note_mode(NoteMode mode)
381 {
382         if (_model) {
383                 _model->set_note_mode(mode);
384         }
385 }
386
387 void
388 MidiSource::drop_model ()
389 {
390         _model.reset();
391         ModelChanged (); /* EMIT SIGNAL */
392 }
393
394 void
395 MidiSource::set_model (boost::shared_ptr<MidiModel> m)
396 {
397         _model = m;
398         ModelChanged (); /* EMIT SIGNAL */
399 }
400
401 Evoral::ControlList::InterpolationStyle
402 MidiSource::interpolation_of (Evoral::Parameter p) const
403 {
404         InterpolationStyleMap::const_iterator i = _interpolation_style.find (p);
405         if (i == _interpolation_style.end()) {
406                 return EventTypeMap::instance().interpolation_of (p);
407         }
408
409         return i->second;
410 }
411
412 AutoState
413 MidiSource::automation_state_of (Evoral::Parameter p) const
414 {
415         AutomationStateMap::const_iterator i = _automation_state.find (p);
416         if (i == _automation_state.end()) {
417                 /* default to `play', otherwise if MIDI is recorded /
418                    imported with controllers etc. they are by default
419                    not played back, which is a little surprising.
420                 */
421                 return Play;
422         }
423
424         return i->second;
425 }
426
427 /** Set interpolation style to be used for a given parameter.  This change will be
428  *  propagated to anyone who needs to know.
429  */
430 void
431 MidiSource::set_interpolation_of (Evoral::Parameter p, Evoral::ControlList::InterpolationStyle s)
432 {
433         if (interpolation_of (p) == s) {
434                 return;
435         }
436
437         if (EventTypeMap::instance().interpolation_of (p) == s) {
438                 /* interpolation type is being set to the default, so we don't need a note in our map */
439                 _interpolation_style.erase (p);
440         } else {
441                 _interpolation_style[p] = s;
442         }
443
444         InterpolationChanged (p, s); /* EMIT SIGNAL */
445 }
446
447 void
448 MidiSource::set_automation_state_of (Evoral::Parameter p, AutoState s)
449 {
450         if (automation_state_of (p) == s) {
451                 return;
452         }
453
454         if (s == Play) {
455                 /* automation state is being set to the default, so we don't need a note in our map */
456                 _automation_state.erase (p);
457         } else {
458                 _automation_state[p] = s;
459         }
460
461         AutomationStateChanged (p, s); /* EMIT SIGNAL */
462 }
463
464 void
465 MidiSource::copy_interpolation_from (boost::shared_ptr<MidiSource> s)
466 {
467         copy_interpolation_from (s.get ());
468 }
469
470 void
471 MidiSource::copy_automation_state_from (boost::shared_ptr<MidiSource> s)
472 {
473         copy_automation_state_from (s.get ());
474 }
475
476 void
477 MidiSource::copy_interpolation_from (MidiSource* s)
478 {
479         _interpolation_style = s->_interpolation_style;
480
481         /* XXX: should probably emit signals here */
482 }
483
484 void
485 MidiSource::copy_automation_state_from (MidiSource* s)
486 {
487         _automation_state = s->_automation_state;
488
489         /* XXX: should probably emit signals here */
490 }