f881b65d2a7d4f73fdad4696ae91b0476811db0d
[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         assert(_name.find("/") == string::npos);
55 }
56
57 /* Basic MidiRegion constructor (one channel) */
58 MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Flag flags)
59         : Region (src, start, length, name, DataType::MIDI, layer, flags)
60 {
61         assert(_name.find("/") == string::npos);
62 }
63
64 /* Basic MidiRegion constructor (many channels) */
65 MidiRegion::MidiRegion (SourceList& srcs, jack_nframes_t start, jack_nframes_t length, const string& name, layer_t layer, Flag flags)
66         : Region (srcs, start, length, name, DataType::MIDI, layer, flags)
67 {
68         assert(_name.find("/") == string::npos);
69 }
70
71
72 /** Create a new MidiRegion, that is part of an existing one */
73 MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other, jack_nframes_t offset, jack_nframes_t length, const string& name, layer_t layer, Flag flags)
74         : Region (other, offset, length, name, layer, flags)
75 {
76         assert(_name.find("/") == string::npos);
77 }
78
79 MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other)
80         : Region (other)
81 {
82         assert(_name.find("/") == string::npos);
83 }
84
85 MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, const XMLNode& node)
86         : Region (src, node)
87 {
88         if (set_state (node)) {
89                 throw failed_constructor();
90         }
91
92         assert(_name.find("/") == string::npos);
93         assert(_type == DataType::MIDI);
94 }
95
96 MidiRegion::MidiRegion (SourceList& srcs, const XMLNode& node)
97         : Region (srcs, node)
98 {
99         if (set_state (node)) {
100                 throw failed_constructor();
101         }
102
103         assert(_name.find("/") == string::npos);
104         assert(_type == DataType::MIDI);
105 }
106
107 MidiRegion::~MidiRegion ()
108 {
109         GoingAway (); /* EMIT SIGNAL */
110 }
111
112 jack_nframes_t
113 MidiRegion::read_at (MidiRingBuffer& out, jack_nframes_t position, 
114                       jack_nframes_t dur, 
115                       uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const
116 {
117         return _read_at (_sources, out, position, dur, chan_n, read_frames, skip_frames);
118 }
119
120 jack_nframes_t
121 MidiRegion::master_read_at (MidiRingBuffer& out, jack_nframes_t position, 
122                              jack_nframes_t dur, uint32_t chan_n) const
123 {
124         return _read_at (_master_sources, out, position, dur, chan_n, 0, 0);
125 }
126
127 jack_nframes_t
128 MidiRegion::_read_at (const SourceList& srcs, MidiRingBuffer& dst, 
129                        jack_nframes_t position, jack_nframes_t dur, 
130                        uint32_t chan_n, jack_nframes_t read_frames, jack_nframes_t skip_frames) const
131 {
132 /*
133         MidiEvent ev;
134         RawMidi data[4];
135
136         const char note = rand()%30 + 30;
137         
138         ev.buffer = data;
139         ev.time = position;
140         ev.size = 3;
141         data[0] = 0x90;
142         data[1] = note;
143         data[2] = 120;
144         dst.write(ev);
145         
146         ev.buffer = data;
147         ev.time = (jack_nframes_t)(position + (9/10.0 * dur));
148         assert(ev.time < position + dur);
149         ev.size = 3;
150         data[0] = 0x80;
151         data[1] = note;
152         data[2] = 64;
153         dst.write(ev);
154
155         _read_data_count += dur;
156
157         return dur;
158 */
159         jack_nframes_t internal_offset = 0;
160         jack_nframes_t src_offset      = 0;
161         jack_nframes_t to_read         = 0;
162         
163         /* precondition: caller has verified that we cover the desired section */
164
165         assert(chan_n == 0);
166         
167         if (position < _position) {
168                 internal_offset = 0;
169                 src_offset = _position - position;
170                 dur -= src_offset;
171         } else {
172                 internal_offset = position - _position;
173                 src_offset = 0;
174         }
175
176         if (internal_offset >= _length) {
177                 return 0; /* read nothing */
178         }
179         
180
181         if ((to_read = min (dur, _length - internal_offset)) == 0) {
182                 return 0; /* read nothing */
183         }
184
185         // FIXME: non-opaque MIDI regions not yet supported
186         assert(opaque());
187
188         if (muted()) {
189                 return 0; /* read nothing */
190         }
191
192         _read_data_count = 0;
193
194         boost::shared_ptr<MidiSource> src = midi_source(chan_n);
195         if (src->read (dst, _start + internal_offset, to_read, _position) != to_read) {
196                 return 0; /* "read nothing" */
197         }
198
199         _read_data_count += src->read_data_count(); // FIXME: semantics?
200
201         return to_read;
202 }
203         
204 XMLNode&
205 MidiRegion::state (bool full)
206 {
207         XMLNode& node (Region::state (full));
208         XMLNode *child;
209         char buf[64];
210         char buf2[64];
211         LocaleGuard lg (X_("POSIX"));
212         
213         snprintf (buf, sizeof (buf), "0x%x", (int) _flags);
214         node.add_property ("flags", buf);
215
216         for (uint32_t n=0; n < _sources.size(); ++n) {
217                 snprintf (buf2, sizeof(buf2), "source-%d", n);
218                 _sources[n]->id().print (buf, sizeof(buf));
219                 node.add_property (buf2, buf);
220         }
221
222         snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
223         node.add_property ("channels", buf);
224
225         child = node.add_child ("Envelope");
226
227         if ( ! full) {
228                 child->add_property ("default", "yes");
229         }
230
231         if (full && _extra_xml) {
232                 node.add_child_copy (*_extra_xml);
233         }
234
235         return node;
236 }
237
238 int
239 MidiRegion::set_state (const XMLNode& node)
240 {
241         const XMLNodeList& nlist = node.children();
242         const XMLProperty *prop;
243         LocaleGuard lg (X_("POSIX"));
244
245         Region::set_state (node);
246
247         if ((prop = node.property ("flags")) != 0) {
248                 _flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
249         }
250
251         /* Now find child items */
252         for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
253                 
254                 XMLNode *child;
255                 //XMLProperty *prop;
256                 
257                 child = (*niter);
258                 
259                 /** Hello, children */
260         }
261
262         return 0;
263 }
264
265 void
266 MidiRegion::recompute_at_end ()
267 {
268         /* our length has changed
269          * (non destructively) "chop" notes that pass the end boundary, to
270          * prevent stuck notes.
271          */
272 }       
273
274 void
275 MidiRegion::recompute_at_start ()
276 {
277         /* as above, but the shift was from the front
278          * maybe bump currently active note's note-ons up so they sound here?
279          * that could be undesireable in certain situations though.. maybe
280          * remove the note entirely, including it's note off?  something needs to
281          * be done to keep the played MIDI sane to avoid messing up voices of
282          * polyhonic things etc........
283          */
284 }
285
286 int
287 MidiRegion::separate_by_channel (Session& session, vector<MidiRegion*>& v) const
288 {
289         // Separate by MIDI channel?  bit different from audio since this is separating based
290         // on the actual contained data and destructively modifies and creates new sources..
291         
292 #if 0
293         SourceList srcs;
294         string new_name;
295
296         for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
297
298                 srcs.clear ();
299                 srcs.push_back (*i);
300
301                 /* generate a new name */
302                 
303                 if (session.region_name (new_name, _name)) {
304                         return -1;
305                 }
306
307                 /* create a copy with just one source */
308
309                 v.push_back (new MidiRegion (srcs, _start, _length, new_name, _layer, _flags));
310         }
311 #endif
312
313         // Actually, I would prefer not if that's alright
314         return -1;
315 }
316
317 boost::shared_ptr<MidiSource>
318 MidiRegion::midi_source (uint32_t n) const
319 {
320         // Guaranteed to succeed (use a static cast?)
321         return boost::dynamic_pointer_cast<MidiSource>(source(n));
322 }
323