Merged with trunk R861
[ardour.git] / libs / ardour / midi_region.cc
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 <sigc++/bind.h>
28 #include <sigc++/class_slot.h>
29
30 #include <glibmm/thread.h>
31
32 #include <pbd/basename.h>
33 #include <pbd/xml++.h>
34
35 #include <ardour/midi_region.h>
36 #include <ardour/session.h>
37 #include <ardour/gain.h>
38 #include <ardour/dB.h>
39 #include <ardour/playlist.h>
40 #include <ardour/midi_source.h>
41 #include <ardour/types.h>
42 #include <ardour/midi_ring_buffer.h>
43
44 #include "i18n.h"
45 #include <locale.h>
46
47 using namespace std;
48 using namespace ARDOUR;
49
50 /** Basic MidiRegion constructor (one channel) */
51 MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, jack_nframes_t start, jack_nframes_t length)
52         : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::MIDI, 0,  Region::Flag(Region::DefaultFlags|Region::External))
53 {
54         save_state ("initial state");
55
56         assert(_name.find("/") == string::npos);
57 }
58
59 /* Basic MidiRegion constructor (one channel) */
60 MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Flag flags)
61         : Region (src, start, length, name, DataType::MIDI, layer, flags)
62 {
63         save_state ("initial state");
64
65         assert(_name.find("/") == string::npos);
66 }
67
68 /* Basic MidiRegion constructor (many channels) */
69 MidiRegion::MidiRegion (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Flag flags)
70         : Region (srcs, start, length, name, DataType::MIDI, layer, flags)
71 {
72         save_state ("initial state");
73
74         assert(_name.find("/") == string::npos);
75 }
76
77
78 /** Create a new MidiRegion, that is part of an existing one */
79 MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other, jack_nframes_t offset, jack_nframes_t length, const string& name, layer_t layer, Flag flags)
80         : Region (other, offset, length, name, layer, flags)
81 {
82         save_state ("initial state");
83
84         assert(_name.find("/") == string::npos);
85 }
86
87 MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other)
88         : Region (other)
89 {
90         save_state ("initial state");
91
92         assert(_name.find("/") == string::npos);
93 }
94
95 MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, const XMLNode& node)
96         : Region (src, node)
97 {
98         if (set_state (node)) {
99                 throw failed_constructor();
100         }
101
102         save_state ("initial state");
103
104         assert(_name.find("/") == string::npos);
105         assert(_type == DataType::MIDI);
106 }
107
108 MidiRegion::MidiRegion (SourceList& srcs, const XMLNode& node)
109         : Region (srcs, node)
110 {
111         if (set_state (node)) {
112                 throw failed_constructor();
113         }
114
115         save_state ("initial state");
116
117         assert(_name.find("/") == string::npos);
118         assert(_type == DataType::MIDI);
119 }
120
121 MidiRegion::~MidiRegion ()
122 {
123         GoingAway (); /* EMIT SIGNAL */
124 }
125
126 StateManager::State*
127 MidiRegion::state_factory (std::string why) const
128 {
129         RegionState* state = new RegionState (why);
130
131         Region::store_state (*state);
132
133         return state;
134 }       
135
136 Change
137 MidiRegion::restore_state (StateManager::State& sstate) 
138 {
139         RegionState* state = dynamic_cast<RegionState*> (&sstate);
140         Change what_changed = Region::restore_and_return_flags (*state);
141         
142         if (_flags != Flag (state->_flags)) {
143                 
144                 //uint32_t old_flags = _flags;
145                 
146                 _flags = Flag (state->_flags);
147         }
148                 
149         what_changed = Change (what_changed);
150
151         return what_changed;
152 }
153
154 UndoAction
155 MidiRegion::get_memento() const
156 {
157         return sigc::bind (mem_fun (*(const_cast<MidiRegion *> (this)), &StateManager::use_state), _current_state_id);
158 }
159
160 jack_nframes_t
161 MidiRegion::read_at (MidiRingBuffer& out, jack_nframes_t position, 
162                       jack_nframes_t dur, 
163                       uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const
164 {
165         return _read_at (_sources, out, position, dur, chan_n, read_frames, skip_frames);
166 }
167
168 jack_nframes_t
169 MidiRegion::master_read_at (MidiRingBuffer& out, jack_nframes_t position, 
170                              jack_nframes_t dur, uint32_t chan_n) const
171 {
172         return _read_at (_master_sources, out, position, dur, chan_n, 0, 0);
173 }
174
175 jack_nframes_t
176 MidiRegion::_read_at (const SourceList& srcs, MidiRingBuffer& dst, 
177                        jack_nframes_t position, jack_nframes_t dur, 
178                        uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const
179 {
180 /*
181         MidiEvent ev;
182         RawMidi data[4];
183
184         const char note = rand()%30 + 30;
185         
186         ev.buffer = data;
187         ev.time = position;
188         ev.size = 3;
189         data[0] = 0x90;
190         data[1] = note;
191         data[2] = 120;
192         dst.write(ev);
193         
194         ev.buffer = data;
195         ev.time = (jack_nframes_t)(position + (9/10.0 * dur));
196         assert(ev.time < position + dur);
197         ev.size = 3;
198         data[0] = 0x80;
199         data[1] = note;
200         data[2] = 64;
201         dst.write(ev);
202
203         _read_data_count += dur;
204
205         return dur;
206 */
207         jack_nframes_t internal_offset = 0;
208         jack_nframes_t src_offset      = 0;
209         jack_nframes_t to_read         = 0;
210         
211         /* precondition: caller has verified that we cover the desired section */
212
213         assert(chan_n == 0);
214         
215         if (position < _position) {
216                 internal_offset = 0;
217                 src_offset = _position - position;
218                 dur -= src_offset;
219         } else {
220                 internal_offset = position - _position;
221                 src_offset = 0;
222         }
223
224         if (internal_offset >= _length) {
225                 return 0; /* read nothing */
226         }
227         
228
229         if ((to_read = min (dur, _length - internal_offset)) == 0) {
230                 return 0; /* read nothing */
231         }
232
233         // FIXME: non-opaque MIDI regions not yet supported
234         assert(opaque());
235
236         if (muted()) {
237                 return 0; /* read nothing */
238         }
239
240         _read_data_count = 0;
241
242         boost::shared_ptr<MidiSource> src = midi_source(chan_n);
243         if (src->read (dst, _start + internal_offset, to_read, _position) != to_read) {
244                 return 0; /* "read nothing" */
245         }
246
247         _read_data_count += src->read_data_count(); // FIXME: semantics?
248
249         return to_read;
250 }
251         
252 XMLNode&
253 MidiRegion::state (bool full)
254 {
255         XMLNode& node (Region::state (full));
256         XMLNode *child;
257         char buf[64];
258         char buf2[64];
259         LocaleGuard lg (X_("POSIX"));
260         
261         snprintf (buf, sizeof (buf), "0x%x", (int) _flags);
262         node.add_property ("flags", buf);
263
264         for (uint32_t n=0; n < _sources.size(); ++n) {
265                 snprintf (buf2, sizeof(buf2), "source-%d", n);
266                 _sources[n]->id().print (buf);
267                 node.add_property (buf2, buf);
268         }
269
270         snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
271         node.add_property ("channels", buf);
272
273         child = node.add_child ("Envelope");
274
275         if ( ! full) {
276                 child->add_property ("default", "yes");
277         }
278
279         if (full && _extra_xml) {
280                 node.add_child_copy (*_extra_xml);
281         }
282
283         return node;
284 }
285
286 int
287 MidiRegion::set_state (const XMLNode& node)
288 {
289         const XMLNodeList& nlist = node.children();
290         const XMLProperty *prop;
291         LocaleGuard lg (X_("POSIX"));
292
293         Region::set_state (node);
294
295         if ((prop = node.property ("flags")) != 0) {
296                 _flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
297         }
298
299         /* Now find child items */
300         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
301                 
302                 XMLNode *child;
303                 //XMLProperty *prop;
304                 
305                 child = (*niter);
306                 
307                 /** Hello, children */
308         }
309
310         return 0;
311 }
312
313 void
314 MidiRegion::recompute_at_end ()
315 {
316         /* our length has changed
317          * (non destructively) "chop" notes that pass the end boundary, to
318          * prevent stuck notes.
319          */
320 }       
321
322 void
323 MidiRegion::recompute_at_start ()
324 {
325         /* as above, but the shift was from the front
326          * maybe bump currently active note's note-ons up so they sound here?
327          * that could be undesireable in certain situations though.. maybe
328          * remove the note entirely, including it's note off?  something needs to
329          * be done to keep the played MIDI sane to avoid messing up voices of
330          * polyhonic things etc........
331          */
332 }
333
334 int
335 MidiRegion::separate_by_channel (Session& session, vector<MidiRegion*>& v) const
336 {
337 #if 0
338         SourceList srcs;
339         string new_name;
340
341         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
342
343                 srcs.clear ();
344                 srcs.push_back (*i);
345
346                 /* generate a new name */
347                 
348                 if (session.region_name (new_name, _name)) {
349                         return -1;
350                 }
351
352                 /* create a copy with just one source */
353
354                 v.push_back (new MidiRegion (srcs, _start, _length, new_name, _layer, _flags));
355         }
356 #endif
357
358         // Actually, I would prefer not if that's alright
359         return -1;
360 }
361
362 boost::shared_ptr<MidiSource>
363 MidiRegion::midi_source (uint32_t n) const
364 {
365         // Guaranteed to succeed (use a static cast?)
366         return boost::dynamic_pointer_cast<MidiSource>(source(n));
367 }
368