2 Copyright (C) 2006 Paul Davis
3 Written by Dave Robillard, 2006
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.
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.
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.
31 #include "pbd/xml++.h"
32 #include "pbd/pthread_utils.h"
33 #include "pbd/basename.h"
35 #include "ardour/audioengine.h"
36 #include "ardour/debug.h"
37 #include "ardour/midi_model.h"
38 #include "ardour/midi_ring_buffer.h"
39 #include "ardour/midi_state_tracker.h"
40 #include "ardour/midi_source.h"
41 #include "ardour/session.h"
42 #include "ardour/session_directory.h"
43 #include "ardour/source_factory.h"
44 #include "ardour/tempo.h"
49 using namespace ARDOUR
;
52 sigc::signal
<void,MidiSource
*> MidiSource::MidiSourceCreated
;
54 MidiSource::MidiSource (Session
& s
, string name
, Source::Flag flags
)
55 : Source(s
, DataType::MIDI
, name
, flags
)
57 , _write_data_count(0)
59 , _model_iter_valid(false)
66 MidiSource::MidiSource (Session
& s
, const XMLNode
& node
)
69 , _write_data_count(0)
71 , _model_iter_valid(false)
77 _write_data_count
= 0;
79 if (set_state (node
, Stateful::loading_state_version
)) {
80 throw failed_constructor();
84 MidiSource::~MidiSource ()
89 MidiSource::get_state ()
91 XMLNode
& node (Source::get_state());
93 if (_captured_for
.length()) {
94 node
.add_property ("captured-for", _captured_for
);
101 MidiSource::set_state (const XMLNode
& node
, int /*version*/)
103 const XMLProperty
* prop
;
105 if ((prop
= node
.property ("captured-for")) != 0) {
106 _captured_for
= prop
->value();
113 MidiSource::length (sframes_t pos
) const
115 BeatsFramesConverter
converter(_session
.tempo_map(), pos
);
116 return converter
.to(_length_beats
);
120 MidiSource::update_length (sframes_t
/*pos*/, sframes_t
/*cnt*/)
122 // You're not the boss of me!
126 MidiSource::invalidate ()
128 _model_iter_valid
= false;
129 _model_iter
.invalidate();
133 MidiSource::midi_read (Evoral::EventSink
<nframes_t
>& dst
, sframes_t source_start
,
134 sframes_t start
, nframes_t cnt
,
135 sframes_t stamp_offset
, sframes_t negative_stamp_offset
,
136 MidiStateTracker
* tracker
) const
138 Glib::Mutex::Lock
lm (_lock
);
140 BeatsFramesConverter
converter(_session
.tempo_map(), source_start
);
143 Evoral::Sequence
<double>::const_iterator
& i
= _model_iter
;
145 // If the cached iterator is invalid, search for the first event past start
146 if (_last_read_end
== 0 || start
!= _last_read_end
|| !_model_iter_valid
) {
147 DEBUG_TRACE (DEBUG::MidiSourceIO
, string_compose ("*** %1 search for relevant iterator for %1 / %2\n", _name
, source_start
, start
));
148 for (i
= _model
->begin(); i
!= _model
->end(); ++i
) {
149 if (converter
.to(i
->time()) >= start
) {
153 _model_iter_valid
= true;
155 DEBUG_TRACE (DEBUG::MidiSourceIO
, string_compose ("*** %1 use cached iterator for %1 / %2\n", _name
, source_start
, start
));
158 _last_read_end
= start
+ cnt
;
160 // Read events up to end
161 for (; i
!= _model
->end(); ++i
) {
162 const sframes_t time_frames
= converter
.to(i
->time());
163 if (time_frames
< start
+ cnt
) {
164 dst
.write(time_frames
+ stamp_offset
- negative_stamp_offset
,
165 i
->event_type(), i
->size(), i
->buffer());
167 Evoral::MIDIEvent
<Evoral::MusicalTime
>& ev (*(Evoral::MIDIEvent
<Evoral::MusicalTime
>*) (&(*i
)));
168 if (ev
.is_note_on()) {
169 DEBUG_TRACE (DEBUG::MidiSourceIO
, string_compose ("\t%1 add note on %2 @ %3\n", _name
, ev
.note(), time_frames
));
170 tracker
->add (ev
.note(), ev
.channel());
171 } else if (ev
.is_note_off()) {
172 DEBUG_TRACE (DEBUG::MidiSourceIO
, string_compose ("\t%1 add note off %2 @ %3\n", _name
, ev
.note(), time_frames
));
173 tracker
->remove (ev
.note(), ev
.channel());
182 return read_unlocked (dst
, source_start
, start
, cnt
, stamp_offset
, negative_stamp_offset
, tracker
);
187 MidiSource::midi_write (MidiRingBuffer
<nframes_t
>& source
, sframes_t source_start
, nframes_t duration
)
189 Glib::Mutex::Lock
lm (_lock
);
190 const nframes_t ret
= write_unlocked (source
, source_start
, duration
);
191 _last_write_end
+= duration
;
196 MidiSource::mark_streaming_midi_write_started (NoteMode mode
, sframes_t start_frame
)
198 set_timeline_position(start_frame
);
201 _model
->set_note_mode(mode
);
202 _model
->start_write();
205 _last_write_end
= start_frame
;
210 MidiSource::mark_streaming_write_started ()
212 NoteMode note_mode
= _model
? _model
->note_mode() : Sustained
;
213 mark_streaming_midi_write_started(note_mode
, _session
.transport_frame());
217 MidiSource::mark_streaming_write_completed ()
220 _model
->end_write(false);
227 MidiSource::session_saved()
231 if (_model
&& _model
->edited()) {
233 const string basename
= PBD::basename_nosuffix(_name
);
234 string::size_type last_dash
= basename
.find_last_of("-");
235 if (last_dash
== string::npos
|| last_dash
== basename
.find_first_of("-")) {
236 newname
= basename
+ "-1";
238 stringstream
ss(basename
.substr(last_dash
+1));
239 unsigned write_count
= 0;
241 // cerr << "WRITE COUNT: " << write_count << endl;
242 ++write_count
; // start at 1
244 ss
<< basename
.substr(0, last_dash
) << "-" << write_count
;
248 string newpath
= _session
.session_directory().midi_path().to_string() +"/"+ newname
+ ".mid";
250 boost::shared_ptr
<MidiSource
> newsrc
= boost::dynamic_pointer_cast
<MidiSource
>(
251 SourceFactory::createWritable(DataType::MIDI
, _session
,
252 newpath
, true, false, _session
.frame_rate()));
254 newsrc
->set_timeline_position(_timeline_position
);
255 _model
->write_to(newsrc
);
257 // cyclic dependency here, ugly :(
258 newsrc
->set_model(_model
);
259 _model
->set_midi_source(newsrc
.get());
261 newsrc
->flush_midi();
263 Switched
.emit(newsrc
);
268 MidiSource::set_note_mode(NoteMode mode
)
271 _model
->set_note_mode(mode
);