Merge lp:~zequence/ubuntu/utopic/ardour3/fix-for-1450992 into lp:ubuntu/utopic/ardour3

Proposed by Kaj Ailomaa
Status: Needs review
Proposed branch: lp:~zequence/ubuntu/utopic/ardour3/fix-for-1450992
Merge into: lp:ubuntu/utopic/ardour3
Diff against target: 2954 lines (+1560/-389)
39 files modified
.pc/applied-patches (+1/-0)
.pc/midi-data-loss.patch/libs/ardour/smf_source.cc (+764/-0)
debian/changelog (+20/-0)
debian/control (+1/-1)
debian/control.in (+1/-1)
debian/patches/midi-data-loss.patch (+62/-0)
debian/patches/series (+1/-0)
gtk2_ardour/add_video_dialog.cc (+4/-1)
gtk2_ardour/editor.h (+1/-1)
gtk2_ardour/editor_audio_import.cc (+30/-18)
gtk2_ardour/editor_rulers.cc (+15/-15)
gtk2_ardour/midi_time_axis.cc (+41/-1)
gtk2_ardour/mnemonic-us.bindings.in (+1/-1)
gtk2_ardour/video_timeline.cc (+33/-4)
libs/ardour/ardour/audiofilesource.h (+6/-0)
libs/ardour/ardour/file_source.h (+4/-0)
libs/ardour/ardour/session.h (+6/-8)
libs/ardour/ardour/smf_source.h (+0/-9)
libs/ardour/ardour/sndfilesource.h (+10/-1)
libs/ardour/ardour/source.h (+2/-1)
libs/ardour/ardour/source_factory.h (+3/-0)
libs/ardour/audio_diskstream.cc (+26/-12)
libs/ardour/audiofilesource.cc (+17/-0)
libs/ardour/diskstream.cc (+0/-1)
libs/ardour/enums.cc (+1/-0)
libs/ardour/file_source.cc (+35/-11)
libs/ardour/filter.cc (+2/-3)
libs/ardour/import.cc (+26/-76)
libs/ardour/midi_diskstream.cc (+2/-2)
libs/ardour/session.cc (+100/-120)
libs/ardour/session_state.cc (+49/-41)
libs/ardour/smf_source.cc (+78/-52)
libs/ardour/sndfilesource.cc (+36/-0)
libs/ardour/source_factory.cc (+33/-0)
libs/ardour/tempo.cc (+10/-5)
libs/midi++2/midnam_patch.cc (+3/-4)
patchfiles/Korg_Volca_Bass.midnam (+37/-0)
patchfiles/Korg_Volca_Beats.midnam (+58/-0)
patchfiles/Korg_Volca_Keys.midnam (+41/-0)
To merge this branch: bzr merge lp:~zequence/ubuntu/utopic/ardour3/fix-for-1450992
Reviewer Review Type Date Requested Status
Micah Gersten (community) Disapprove
Review via email: mp+258100@code.launchpad.net

Description of the change

Fix for bug 1450992.

This is the last release of ardour3.

To post a comment you must log in.
Revision history for this message
Micah Gersten (micahg) wrote :

Rejecting in favor of a backport SRU

review: Disapprove

Unmerged revisions

15. By Adrian Knoth

Fix MIDI data loss when editing (Closes: #772118)

14. By Adrian Knoth

Reupload with waf fixes from 3.5.380~dfsg-2 and -3

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.pc/applied-patches'
2--- .pc/applied-patches 2014-07-11 12:46:37 +0000
3+++ .pc/applied-patches 2015-05-02 14:25:06 +0000
4@@ -1,3 +1,4 @@
5 waf.patch
6 wscript.patch
7 log-stdout.patch
8+midi-data-loss.patch
9
10=== added directory '.pc/midi-data-loss.patch'
11=== added directory '.pc/midi-data-loss.patch/libs'
12=== added directory '.pc/midi-data-loss.patch/libs/ardour'
13=== added file '.pc/midi-data-loss.patch/libs/ardour/smf_source.cc'
14--- .pc/midi-data-loss.patch/libs/ardour/smf_source.cc 1970-01-01 00:00:00 +0000
15+++ .pc/midi-data-loss.patch/libs/ardour/smf_source.cc 2015-05-02 14:25:06 +0000
16@@ -0,0 +1,764 @@
17+/*
18+ Copyright (C) 2006 Paul Davis
19+ Author: David Robillard
20+
21+ This program is free software; you can redistribute it and/or modify
22+ it under the terms of the GNU General Public License as published by
23+ the Free Software Foundation; either version 2 of the License, or
24+ (at your option) any later version.
25+
26+ This program is distributed in the hope that it will be useful,
27+ but WITHOUT ANY WARRANTY; without even the implied warranty of
28+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29+ GNU General Public License for more details.
30+
31+ You should have received a copy of the GNU General Public License
32+ along with this program; if not, write to the Free Software
33+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34+
35+*/
36+
37+#include <vector>
38+
39+#include <sys/time.h>
40+#include <sys/stat.h>
41+#include <unistd.h>
42+#include <errno.h>
43+#include <regex.h>
44+
45+#include "pbd/pathscanner.h"
46+#include "pbd/stl_delete.h"
47+#include "pbd/strsplit.h"
48+
49+#include <glibmm/miscutils.h>
50+#include <glibmm/fileutils.h>
51+
52+#include "evoral/Control.hpp"
53+#include "evoral/evoral/SMF.hpp"
54+
55+#include "ardour/event_type_map.h"
56+#include "ardour/midi_model.h"
57+#include "ardour/midi_ring_buffer.h"
58+#include "ardour/midi_state_tracker.h"
59+#include "ardour/session.h"
60+#include "ardour/smf_source.h"
61+#include "ardour/debug.h"
62+
63+#include "i18n.h"
64+
65+using namespace ARDOUR;
66+using namespace Glib;
67+using namespace PBD;
68+using namespace Evoral;
69+
70+/** Constructor used for new internal-to-session files. File cannot exist. */
71+SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
72+ : Source(s, DataType::MIDI, path, flags)
73+ , MidiSource(s, path, flags)
74+ , FileSource(s, DataType::MIDI, path, string(), flags)
75+ , Evoral::SMF()
76+ , _last_ev_time_beats(0.0)
77+ , _last_ev_time_frames(0)
78+ , _smf_last_read_end (0)
79+ , _smf_last_read_time (0)
80+{
81+ /* note that origin remains empty */
82+
83+ if (init (_path, false)) {
84+ throw failed_constructor ();
85+ }
86+
87+ assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
88+ existence_check ();
89+
90+ _flags = Source::Flag (_flags | Empty);
91+
92+ /* file is not opened until write */
93+
94+ if (flags & Writable) {
95+ return;
96+ }
97+
98+ if (open (_path)) {
99+ throw failed_constructor ();
100+ }
101+
102+ _open = true;
103+}
104+
105+/** Constructor used for external-to-session files. File must exist. */
106+SMFSource::SMFSource (Session& s, const string& path)
107+ : Source(s, DataType::MIDI, path, Source::Flag (0))
108+ , MidiSource(s, path, Source::Flag (0))
109+ , FileSource(s, DataType::MIDI, path, string(), Source::Flag (0))
110+ , Evoral::SMF()
111+ , _last_ev_time_beats(0.0)
112+ , _last_ev_time_frames(0)
113+ , _smf_last_read_end (0)
114+ , _smf_last_read_time (0)
115+{
116+ /* note that origin remains empty */
117+
118+ if (init (_path, true)) {
119+ throw failed_constructor ();
120+ }
121+
122+ assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
123+ existence_check ();
124+
125+ if (_flags & Writable) {
126+ /* file is not opened until write */
127+ return;
128+ }
129+
130+ if (open (_path)) {
131+ throw failed_constructor ();
132+ }
133+
134+ _open = true;
135+}
136+
137+/** Constructor used for existing internal-to-session files. */
138+SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
139+ : Source(s, node)
140+ , MidiSource(s, node)
141+ , FileSource(s, node, must_exist)
142+ , _last_ev_time_beats(0.0)
143+ , _last_ev_time_frames(0)
144+ , _smf_last_read_end (0)
145+ , _smf_last_read_time (0)
146+{
147+ if (set_state(node, Stateful::loading_state_version)) {
148+ throw failed_constructor ();
149+ }
150+
151+ /* we expect the file to exist, but if no MIDI data was ever added
152+ it will have been removed at last session close. so, we don't
153+ require it to exist if it was marked Empty.
154+ */
155+
156+ try {
157+
158+ if (init (_path, true)) {
159+ throw failed_constructor ();
160+ }
161+
162+ } catch (MissingSource& err) {
163+
164+ if (_flags & Source::Empty) {
165+ /* we don't care that the file was not found, because
166+ it was empty. But FileSource::init() will have
167+ failed to set our _path correctly, so we have to do
168+ this ourselves. Use the first entry in the search
169+ path for MIDI files, which is assumed to be the
170+ correct "main" location.
171+ */
172+ std::vector<Glib::ustring> sdirs;
173+ split (s.source_search_path (DataType::MIDI), sdirs, ':');
174+ if (sdirs.empty()) {
175+ fatal << _("Empty MIDI source search path!") << endmsg;
176+ /*NOTREACHED*/
177+ }
178+ _path = Glib::build_filename (sdirs.front(), _path);
179+ /* This might be important, too */
180+ _file_is_new = true;
181+ } else {
182+ /* pass it on */
183+ throw;
184+ }
185+ }
186+
187+ if (!(_flags & Source::Empty)) {
188+ assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
189+ existence_check ();
190+ } else {
191+ assert (_flags & Source::Writable);
192+ /* file will be opened on write */
193+ return;
194+ }
195+
196+ if (open(_path)) {
197+ throw failed_constructor ();
198+ }
199+
200+ _open = true;
201+}
202+
203+SMFSource::~SMFSource ()
204+{
205+ if (removable()) {
206+ unlink (_path.c_str());
207+ }
208+}
209+
210+int
211+SMFSource::open_for_write ()
212+{
213+ if (create (_path)) {
214+ return -1;
215+ }
216+ _open = true;
217+ return 0;
218+}
219+
220+/** All stamps in audio frames */
221+framecnt_t
222+SMFSource::read_unlocked (Evoral::EventSink<framepos_t>& destination,
223+ framepos_t const source_start,
224+ framepos_t start,
225+ framecnt_t duration,
226+ MidiStateTracker* tracker) const
227+{
228+ int ret = 0;
229+ uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
230+
231+ if (writable() && !_open) {
232+ /* nothing to read since nothing has ben written */
233+ return duration;
234+ }
235+
236+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start %1 duration %2\n", start, duration));
237+
238+ // Output parameters for read_event (which will allocate scratch in buffer as needed)
239+ uint32_t ev_delta_t = 0;
240+ uint32_t ev_type = 0;
241+ uint32_t ev_size = 0;
242+ uint8_t* ev_buffer = 0;
243+
244+ size_t scratch_size = 0; // keep track of scratch to minimize reallocs
245+
246+ BeatsFramesConverter converter(_session.tempo_map(), source_start);
247+
248+ const uint64_t start_ticks = (uint64_t)(converter.from(start) * ppqn());
249+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: start in ticks %1\n", start_ticks));
250+
251+ if (_smf_last_read_end == 0 || start != _smf_last_read_end) {
252+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: seek to %1\n", start));
253+ Evoral::SMF::seek_to_start();
254+ while (time < start_ticks) {
255+ gint ignored;
256+
257+ ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
258+ if (ret == -1) { // EOF
259+ _smf_last_read_end = start + duration;
260+ return duration;
261+ }
262+ time += ev_delta_t; // accumulate delta time
263+ }
264+ } else {
265+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked: set time to %1\n", _smf_last_read_time));
266+ time = _smf_last_read_time;
267+ }
268+
269+ _smf_last_read_end = start + duration;
270+
271+ while (true) {
272+ gint ignored; /* XXX don't ignore note id's ??*/
273+
274+ ret = read_event(&ev_delta_t, &ev_size, &ev_buffer, &ignored);
275+ if (ret == -1) { // EOF
276+ break;
277+ }
278+
279+ time += ev_delta_t; // accumulate delta time
280+ _smf_last_read_time = time;
281+
282+ if (ret == 0) { // meta-event (skipped, just accumulate time)
283+ continue;
284+ }
285+
286+ ev_type = EventTypeMap::instance().midi_event_type(ev_buffer[0]);
287+
288+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF read_unlocked delta %1, time %2, buf[0] %3, type %4\n",
289+ ev_delta_t, time, ev_buffer[0], ev_type));
290+
291+ assert(time >= start_ticks);
292+
293+ /* Note that we add on the source start time (in session frames) here so that ev_frame_time
294+ is in session frames.
295+ */
296+ const framepos_t ev_frame_time = converter.to(time / (double)ppqn()) + source_start;
297+
298+ if (ev_frame_time < start + duration) {
299+ destination.write (ev_frame_time, ev_type, ev_size, ev_buffer);
300+
301+ if (tracker) {
302+ if (ev_buffer[0] & MIDI_CMD_NOTE_ON) {
303+ tracker->add (ev_buffer[1], ev_buffer[0] & 0xf);
304+ } else if (ev_buffer[0] & MIDI_CMD_NOTE_OFF) {
305+ tracker->remove (ev_buffer[1], ev_buffer[0] & 0xf);
306+ }
307+ }
308+ } else {
309+ break;
310+ }
311+
312+ if (ev_size > scratch_size) {
313+ scratch_size = ev_size;
314+ }
315+ ev_size = scratch_size; // ensure read_event only allocates if necessary
316+ }
317+
318+ return duration;
319+}
320+
321+framecnt_t
322+SMFSource::write_unlocked (MidiRingBuffer<framepos_t>& source,
323+ framepos_t position,
324+ framecnt_t cnt)
325+{
326+ if (!_writing) {
327+ mark_streaming_write_started ();
328+ }
329+
330+ framepos_t time;
331+ Evoral::EventType type;
332+ uint32_t size;
333+
334+ size_t buf_capacity = 4;
335+ uint8_t* buf = (uint8_t*)malloc(buf_capacity);
336+
337+ if (_model && !_model->writing()) {
338+ _model->start_write();
339+ }
340+
341+ Evoral::MIDIEvent<framepos_t> ev;
342+ while (true) {
343+ /* Get the event time, in frames since session start but ignoring looping. */
344+ bool ret;
345+ if (!(ret = source.peek ((uint8_t*)&time, sizeof (time)))) {
346+ /* Ring is empty, no more events. */
347+ break;
348+ }
349+
350+ if ((cnt != max_framecnt) &&
351+ (time > position + _capture_length + cnt)) {
352+ /* The diskstream doesn't want us to write everything, and this
353+ event is past the end of this block, so we're done for now. */
354+ break;
355+ }
356+
357+ /* Read the time, type, and size of the event. */
358+ if (!(ret = source.read_prefix (&time, &type, &size))) {
359+ error << _("Unable to read event prefix, corrupt MIDI ring") << endmsg;
360+ break;
361+ }
362+
363+ /* Enlarge body buffer if necessary now that we know the size. */
364+ if (size > buf_capacity) {
365+ buf_capacity = size;
366+ buf = (uint8_t*)realloc(buf, size);
367+ }
368+
369+ /* Read the event body into buffer. */
370+ ret = source.read_contents(size, buf);
371+ if (!ret) {
372+ error << _("Event has time and size but no body, corrupt MIDI ring") << endmsg;
373+ break;
374+ }
375+
376+ /* Convert event time from absolute to source relative. */
377+ if (time < position) {
378+ error << _("Event time is before MIDI source position") << endmsg;
379+ break;
380+ }
381+ time -= position;
382+
383+ ev.set(buf, size, time);
384+ ev.set_event_type(EventTypeMap::instance().midi_event_type(ev.buffer()[0]));
385+ ev.set_id(Evoral::next_event_id());
386+
387+ if (!(ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex())) {
388+ continue;
389+ }
390+
391+ append_event_unlocked_frames(ev, position);
392+ }
393+
394+ Evoral::SMF::flush ();
395+ free (buf);
396+
397+ return cnt;
398+}
399+
400+/** Append an event with a timestamp in beats (double) */
401+void
402+SMFSource::append_event_unlocked_beats (const Evoral::Event<double>& ev)
403+{
404+ if (!_writing || ev.size() == 0) {
405+ return;
406+ }
407+
408+ /*printf("SMFSource: %s - append_event_unlocked_beats ID = %d time = %lf, size = %u, data = ",
409+ name().c_str(), ev.id(), ev.time(), ev.size());
410+ for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/
411+
412+ if (ev.time() < _last_ev_time_beats) {
413+ warning << string_compose(_("Skipping event with unordered time %1"), ev.time())
414+ << endmsg;
415+ return;
416+ }
417+
418+ Evoral::event_id_t event_id;
419+
420+ if (ev.id() < 0) {
421+ event_id = Evoral::next_event_id();
422+ } else {
423+ event_id = ev.id();
424+ }
425+
426+ if (_model) {
427+ _model->append (ev, event_id);
428+ }
429+
430+ _length_beats = max(_length_beats, ev.time());
431+
432+ const double delta_time_beats = ev.time() - _last_ev_time_beats;
433+ const uint32_t delta_time_ticks = (uint32_t)lrint(delta_time_beats * (double)ppqn());
434+
435+ Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
436+ _last_ev_time_beats = ev.time();
437+ _flags = Source::Flag (_flags & ~Empty);
438+}
439+
440+/** Append an event with a timestamp in frames (framepos_t) */
441+void
442+SMFSource::append_event_unlocked_frames (const Evoral::Event<framepos_t>& ev, framepos_t position)
443+{
444+ if (!_writing || ev.size() == 0) {
445+ return;
446+ }
447+
448+ // printf("SMFSource: %s - append_event_unlocked_frames ID = %d time = %u, size = %u, data = ",
449+ // name().c_str(), ev.id(), ev.time(), ev.size());
450+ // for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
451+
452+ if (ev.time() < _last_ev_time_frames) {
453+ warning << string_compose(_("Skipping event with unordered time %1"), ev.time())
454+ << endmsg;
455+ return;
456+ }
457+
458+ BeatsFramesConverter converter(_session.tempo_map(), position);
459+ const double ev_time_beats = converter.from(ev.time());
460+ Evoral::event_id_t event_id;
461+
462+ if (ev.id() < 0) {
463+ event_id = Evoral::next_event_id();
464+ } else {
465+ event_id = ev.id();
466+ }
467+
468+ if (_model) {
469+ const Evoral::Event<double> beat_ev (ev.event_type(),
470+ ev_time_beats,
471+ ev.size(),
472+ const_cast<uint8_t*>(ev.buffer()));
473+ _model->append (beat_ev, event_id);
474+ }
475+
476+ _length_beats = max(_length_beats, ev_time_beats);
477+
478+ const Evoral::MusicalTime last_time_beats = converter.from (_last_ev_time_frames);
479+ const Evoral::MusicalTime delta_time_beats = ev_time_beats - last_time_beats;
480+ const uint32_t delta_time_ticks = (uint32_t)(lrint(delta_time_beats * (double)ppqn()));
481+
482+ Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
483+ _last_ev_time_frames = ev.time();
484+ _flags = Source::Flag (_flags & ~Empty);
485+}
486+
487+XMLNode&
488+SMFSource::get_state ()
489+{
490+ XMLNode& node = MidiSource::get_state();
491+ node.add_property (X_("origin"), _origin);
492+ return node;
493+}
494+
495+int
496+SMFSource::set_state (const XMLNode& node, int version)
497+{
498+ if (Source::set_state (node, version)) {
499+ return -1;
500+ }
501+
502+ if (MidiSource::set_state (node, version)) {
503+ return -1;
504+ }
505+
506+ if (FileSource::set_state (node, version)) {
507+ return -1;
508+ }
509+
510+ return 0;
511+}
512+
513+void
514+SMFSource::mark_streaming_midi_write_started (NoteMode mode)
515+{
516+ /* CALLER MUST HOLD LOCK */
517+
518+ if (!_open && open_for_write()) {
519+ error << string_compose (_("cannot open MIDI file %1 for write"), _path) << endmsg;
520+ /* XXX should probably throw or return something */
521+ return;
522+ }
523+
524+ MidiSource::mark_streaming_midi_write_started (mode);
525+ Evoral::SMF::begin_write ();
526+ _last_ev_time_beats = 0.0;
527+ _last_ev_time_frames = 0;
528+}
529+
530+void
531+SMFSource::mark_streaming_write_completed ()
532+{
533+ mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::DeleteStuckNotes);
534+}
535+
536+void
537+SMFSource::mark_midi_streaming_write_completed (Evoral::Sequence<Evoral::MusicalTime>::StuckNoteOption stuck_notes_option, Evoral::MusicalTime when)
538+{
539+ Glib::Threads::Mutex::Lock lm (_lock);
540+ MidiSource::mark_midi_streaming_write_completed (stuck_notes_option, when);
541+
542+ if (!writable()) {
543+ warning << string_compose ("attempt to write to unwritable SMF file %1", _path) << endmsg;
544+ return;
545+ }
546+
547+ if (_model) {
548+ _model->set_edited(false);
549+ }
550+
551+ Evoral::SMF::end_write ();
552+
553+ /* data in the file now, not removable */
554+
555+ mark_nonremovable ();
556+}
557+
558+bool
559+SMFSource::valid_midi_file (const string& file)
560+{
561+ if (safe_midi_file_extension (file) ) {
562+ return (SMF::test (file) );
563+ }
564+ return false;
565+}
566+
567+bool
568+SMFSource::safe_midi_file_extension (const string& file)
569+{
570+ static regex_t compiled_pattern;
571+ static bool compile = true;
572+ const int nmatches = 2;
573+ regmatch_t matches[nmatches];
574+
575+ if (Glib::file_test (file, Glib::FILE_TEST_EXISTS)) {
576+ if (!Glib::file_test (file, Glib::FILE_TEST_IS_REGULAR)) {
577+ /* exists but is not a regular file */
578+ return false;
579+ }
580+ }
581+
582+ if (compile && regcomp (&compiled_pattern, "\\.[mM][iI][dD][iI]?$", REG_EXTENDED)) {
583+ return false;
584+ } else {
585+ compile = false;
586+ }
587+
588+ if (regexec (&compiled_pattern, file.c_str(), nmatches, matches, 0)) {
589+ return false;
590+ }
591+
592+ return true;
593+}
594+
595+static bool compare_eventlist (
596+ const std::pair< Evoral::Event<double>*, gint >& a,
597+ const std::pair< Evoral::Event<double>*, gint >& b) {
598+ return ( a.first->time() < b.first->time() );
599+}
600+
601+void
602+SMFSource::load_model (bool lock, bool force_reload)
603+{
604+ if (_writing) {
605+ return;
606+ }
607+
608+ boost::shared_ptr<Glib::Threads::Mutex::Lock> lm;
609+ if (lock)
610+ lm = boost::shared_ptr<Glib::Threads::Mutex::Lock>(new Glib::Threads::Mutex::Lock(_lock));
611+
612+ if (_model && !force_reload) {
613+ return;
614+ }
615+
616+ if (!_model) {
617+ _model = boost::shared_ptr<MidiModel> (new MidiModel (shared_from_this ()));
618+ } else {
619+ _model->clear();
620+ }
621+
622+ if (writable() && !_open) {
623+ return;
624+ }
625+
626+ _model->start_write();
627+ Evoral::SMF::seek_to_start();
628+
629+ uint64_t time = 0; /* in SMF ticks */
630+ Evoral::Event<double> ev;
631+
632+ uint32_t scratch_size = 0; // keep track of scratch and minimize reallocs
633+
634+ uint32_t delta_t = 0;
635+ uint32_t size = 0;
636+ uint8_t* buf = NULL;
637+ int ret;
638+ gint event_id;
639+ bool have_event_id;
640+
641+ // TODO simplify event allocation
642+ std::list< std::pair< Evoral::Event<double>*, gint > > eventlist;
643+
644+ for (unsigned i = 1; i <= num_tracks(); ++i) {
645+ if (seek_to_track(i)) continue;
646+
647+ time = 0;
648+ have_event_id = false;
649+
650+ while ((ret = read_event (&delta_t, &size, &buf, &event_id)) >= 0) {
651+
652+ time += delta_t;
653+
654+ if (ret == 0) {
655+ /* meta-event : did we get an event ID ? */
656+ if (event_id >= 0) {
657+ have_event_id = true;
658+ }
659+ continue;
660+ }
661+
662+ if (ret > 0) {
663+ /* not a meta-event */
664+
665+ if (!have_event_id) {
666+ event_id = Evoral::next_event_id();
667+ }
668+ uint32_t event_type = EventTypeMap::instance().midi_event_type(buf[0]);
669+ double event_time = time / (double) ppqn();
670+#ifndef NDEBUG
671+ std::string ss;
672+
673+ for (uint32_t xx = 0; xx < size; ++xx) {
674+ char b[8];
675+ snprintf (b, sizeof (b), "0x%x ", buf[xx]);
676+ ss += b;
677+ }
678+
679+ DEBUG_TRACE (DEBUG::MidiSourceIO, string_compose ("SMF %6 load model delta %1, time %2, size %3 buf %4, type %5\n",
680+ delta_t, time, size, ss , event_type, name()));
681+#endif
682+
683+ eventlist.push_back(make_pair (
684+ new Evoral::Event<double> (
685+ event_type, event_time,
686+ size, buf, true)
687+ , event_id));
688+
689+ // Set size to max capacity to minimize allocs in read_event
690+ scratch_size = std::max(size, scratch_size);
691+ size = scratch_size;
692+
693+ _length_beats = max(_length_beats, event_time);
694+ }
695+
696+ /* event ID's must immediately precede the event they are for */
697+ have_event_id = false;
698+ }
699+ }
700+
701+ eventlist.sort(compare_eventlist);
702+
703+ std::list< std::pair< Evoral::Event<double>*, gint > >::iterator it;
704+ for (it=eventlist.begin(); it!=eventlist.end(); ++it) {
705+ _model->append (*it->first, it->second);
706+ delete it->first;
707+ }
708+
709+ _model->end_write (Evoral::Sequence<Evoral::MusicalTime>::ResolveStuckNotes, _length_beats);
710+ _model->set_edited (false);
711+
712+ _model_iter = _model->begin();
713+
714+ free(buf);
715+}
716+
717+void
718+SMFSource::destroy_model ()
719+{
720+ //cerr << _name << " destroying model " << _model.get() << endl;
721+ _model.reset();
722+}
723+
724+void
725+SMFSource::flush_midi ()
726+{
727+ if (!writable() || _length_beats == 0.0) {
728+ return;
729+ }
730+
731+ ensure_disk_file ();
732+
733+ Evoral::SMF::end_write ();
734+ /* data in the file means its no longer removable */
735+ mark_nonremovable ();
736+}
737+
738+void
739+SMFSource::set_path (const string& p)
740+{
741+ FileSource::set_path (p);
742+ SMF::set_path (_path);
743+}
744+
745+/** Ensure that this source has some file on disk, even if it's just a SMF header */
746+void
747+SMFSource::ensure_disk_file ()
748+{
749+ if (!writable()) {
750+ return;
751+ }
752+
753+ if (_model) {
754+ /* We have a model, so write it to disk; see MidiSource::session_saved
755+ for an explanation of what we are doing here.
756+ */
757+ boost::shared_ptr<MidiModel> mm = _model;
758+ _model.reset ();
759+ mm->sync_to_source ();
760+ _model = mm;
761+ } else {
762+ /* No model; if it's not already open, it's an empty source, so create
763+ and open it for writing.
764+ */
765+ if (!_open) {
766+ open_for_write ();
767+ }
768+ }
769+}
770+
771+void
772+SMFSource::prevent_deletion ()
773+{
774+ /* Unlike the audio case, the MIDI file remains mutable (because we can
775+ edit MIDI data)
776+ */
777+
778+ _flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));
779+}
780+
781
782=== modified file 'debian/changelog'
783--- debian/changelog 2014-07-11 12:46:37 +0000
784+++ debian/changelog 2015-05-02 14:25:06 +0000
785@@ -1,3 +1,23 @@
786+ardour3 (3.5.403~dfsg-3) unstable; urgency=medium
787+
788+ * Fix MIDI data loss when editing (Closes: #772118)
789+
790+ -- Adrian Knoth <adi@drcomp.erfurt.thur.de> Thu, 04 Dec 2014 19:34:45 +0100
791+
792+ardour3 (3.5.403~dfsg-2) unstable; urgency=high
793+
794+ * Reupload with waf fixes from 3.5.380~dfsg-2 and -3
795+
796+ -- Adrian Knoth <adi@drcomp.erfurt.thur.de> Fri, 03 Oct 2014 14:01:07 +0200
797+
798+ardour3 (3.5.403~dfsg-1) unstable; urgency=high
799+
800+ * Imported Upstream version 3.5.403~dfsg
801+ * Critical bugfix release. All users are recommended to upgrade.
802+ * Details: https://community.ardour.org/node/8457
803+
804+ -- Adrian Knoth <adi@drcomp.erfurt.thur.de> Fri, 03 Oct 2014 13:02:45 +0200
805+
806 ardour3 (3.5.380~dfsg-3) unstable; urgency=medium
807
808 * Prevent build failures in slower architectures by
809
810=== modified file 'debian/control'
811--- debian/control 2014-01-24 16:31:54 +0000
812+++ debian/control 2015-05-02 14:25:06 +0000
813@@ -49,7 +49,7 @@
814 python-isodate,
815 libpcre3-dev,
816 python-rdflib
817-Standards-Version: 3.9.4
818+Standards-Version: 3.9.6
819 Homepage: http://www.ardour.org/
820 Vcs-Git: git://anonscm.debian.org/pkg-multimedia/ardour3.git
821 Vcs-Browser: http://anonscm.debian.org/gitweb/?p=pkg-multimedia/ardour3.git
822
823=== modified file 'debian/control.in'
824--- debian/control.in 2014-01-24 16:31:54 +0000
825+++ debian/control.in 2015-05-02 14:25:06 +0000
826@@ -7,7 +7,7 @@
827 Jaromír Mikeš <mira.mikes@seznam.cz>
828 Build-Depends:
829 @cdbs@
830-Standards-Version: 3.9.4
831+Standards-Version: 3.9.6
832 Homepage: http://www.ardour.org/
833 Vcs-Git: git://anonscm.debian.org/pkg-multimedia/ardour3.git
834 Vcs-Browser: http://anonscm.debian.org/gitweb/?p=pkg-multimedia/ardour3.git
835
836=== added file 'debian/patches/midi-data-loss.patch'
837--- debian/patches/midi-data-loss.patch 1970-01-01 00:00:00 +0000
838+++ debian/patches/midi-data-loss.patch 2015-05-02 14:25:06 +0000
839@@ -0,0 +1,62 @@
840+From: David Robillard <d@drobilla.net>
841+Description: Fix MIDI data loss when editing
842+Forwarded: not-needed
843+Last-Update: 2014-12-04
844+Bug-Vendor: http://tracker.ardour.org/view.php?id=5669
845+Applied-Upstream: 3.5.404; https://github.com/Ardour/ardour/commit/94954f852ead97bcda7afa548d543222733228ef
846+--- ardour3.orig/libs/ardour/smf_source.cc
847++++ ardour3/libs/ardour/smf_source.cc
848+@@ -393,10 +393,22 @@ SMFSource::append_event_unlocked_beats (
849+ name().c_str(), ev.id(), ev.time(), ev.size());
850+ for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/
851+
852+- if (ev.time() < _last_ev_time_beats) {
853+- warning << string_compose(_("Skipping event with unordered time %1"), ev.time())
854+- << endmsg;
855+- return;
856++ double time = ev.time();
857++ if (time < _last_ev_time_beats) {
858++ const double difference = _last_ev_time_beats - time;
859++ if (difference / (double)ppqn() < 1.0) {
860++ /* Close enough. This problem occurs because Sequence is not
861++ actually ordered due to fuzzy time comparison. I'm pretty sure
862++ this is inherently a bad idea which causes problems all over the
863++ place, but tolerate it here for now anyway. */
864++ time = _last_ev_time_beats;
865++ } else {
866++ /* Out of order by more than a tick. */
867++ warning << string_compose(_("Skipping event with unordered beat time %1 < %2 (off by %3 beats, %4 ticks)"),
868++ ev.time(), _last_ev_time_beats, difference, difference / (double)ppqn())
869++ << endmsg;
870++ return;
871++ }
872+ }
873+
874+ Evoral::event_id_t event_id;
875+@@ -411,13 +423,13 @@ SMFSource::append_event_unlocked_beats (
876+ _model->append (ev, event_id);
877+ }
878+
879+- _length_beats = max(_length_beats, ev.time());
880++ _length_beats = max(_length_beats, time);
881+
882+- const double delta_time_beats = ev.time() - _last_ev_time_beats;
883++ const double delta_time_beats = time - _last_ev_time_beats;
884+ const uint32_t delta_time_ticks = (uint32_t)lrint(delta_time_beats * (double)ppqn());
885+
886+ Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
887+- _last_ev_time_beats = ev.time();
888++ _last_ev_time_beats = time;
889+ _flags = Source::Flag (_flags & ~Empty);
890+ }
891+
892+@@ -434,7 +446,8 @@ SMFSource::append_event_unlocked_frames
893+ // for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
894+
895+ if (ev.time() < _last_ev_time_frames) {
896+- warning << string_compose(_("Skipping event with unordered time %1"), ev.time())
897++ warning << string_compose(_("Skipping event with unordered frame time %1 < %2"),
898++ ev.time(), _last_ev_time_frames)
899+ << endmsg;
900+ return;
901+ }
902
903=== modified file 'debian/patches/series'
904--- debian/patches/series 2014-07-11 12:46:37 +0000
905+++ debian/patches/series 2015-05-02 14:25:06 +0000
906@@ -1,3 +1,4 @@
907 waf.patch
908 wscript.patch
909 log-stdout.patch
910+midi-data-loss.patch
911
912=== modified file 'gtk2_ardour/add_video_dialog.cc'
913--- gtk2_ardour/add_video_dialog.cc 2013-09-21 19:05:02 +0000
914+++ gtk2_ardour/add_video_dialog.cc 2015-05-02 14:25:06 +0000
915@@ -258,7 +258,8 @@
916 ".ogg" , ".OGG" ,
917 ".ogv" , ".OGV" ,
918 ".mpg" , ".MPG" ,
919- ".mov" , ".MOV" ,
920+ ".mpeg" , ".MPEG" ,
921+ ".mts" , ".MTS" ,
922 ".mp4" , ".MP4" ,
923 ".mkv" , ".MKV" ,
924 ".vob" , ".VOB" ,
925@@ -272,6 +273,8 @@
926 ".dv" , ".DV" ,
927 ".dirac" , ".DIRAC" ,
928 ".webm" , ".WEBM" ,
929+ ".wmv" , ".wmv" ,
930+ ".ts" , ".ts" ,
931 };
932
933 for (size_t n = 0; n < sizeof(suffixes)/sizeof(suffixes[0]); ++n) {
934
935=== modified file 'gtk2_ardour/editor.h'
936--- gtk2_ardour/editor.h 2014-01-24 16:31:54 +0000
937+++ gtk2_ardour/editor.h 2015-05-02 14:25:06 +0000
938@@ -1262,7 +1262,7 @@
939 int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::Track>&, bool add_channel_suffix);
940
941 int finish_bringing_in_material (boost::shared_ptr<ARDOUR::Region> region, uint32_t, uint32_t, framepos_t& pos, Editing::ImportMode mode,
942- boost::shared_ptr<ARDOUR::Track>& existing_track);
943+ boost::shared_ptr<ARDOUR::Track>& existing_track, const std::string& new_track_name);
944
945 boost::shared_ptr<ARDOUR::AudioTrack> get_nth_selected_audio_track (int nth) const;
946 boost::shared_ptr<ARDOUR::MidiTrack> get_nth_selected_midi_track (int nth) const;
947
948=== modified file 'gtk2_ardour/editor_audio_import.cc'
949--- gtk2_ardour/editor_audio_import.cc 2014-01-24 16:31:54 +0000
950+++ gtk2_ardour/editor_audio_import.cc 2015-05-02 14:25:06 +0000
951@@ -611,7 +611,7 @@
952
953 boost::shared_ptr<Source> s;
954
955- if ((s = _session->source_by_path_and_channel (path, n)) == 0) {
956+ if ((s = _session->audio_source_by_path_and_channel (path, n)) == 0) {
957
958 source = boost::dynamic_pointer_cast<AudioFileSource> (
959 SourceFactory::createExternal (DataType::AUDIO, *_session,
960@@ -657,7 +657,8 @@
961 uint32_t input_chan = 0;
962 uint32_t output_chan = 0;
963 bool use_timestamp;
964-
965+ vector<string> track_names;
966+
967 use_timestamp = (pos == -1);
968
969 // kludge (for MIDI we're abusing "channel" for "track" here)
970@@ -694,6 +695,11 @@
971
972 regions.push_back (r);
973
974+ /* if we're creating a new track, name it after the cleaned-up
975+ * and "merged" region name.
976+ */
977+
978+ track_names.push_back (region_name);
979
980 } else if (target_regions == -1 || target_regions > 1) {
981
982@@ -724,29 +730,26 @@
983 region_name = (*x)->name();
984 }
985
986- switch (sources.size()) {
987- /* zero and one channel handled
988- by previous if() condition
989- */
990- case 2:
991+ if (sources.size() == 2) {
992 if (n == 0) {
993 region_name += "-L";
994 } else {
995 region_name += "-R";
996 }
997- break;
998- default:
999- region_name += (char) '-';
1000- region_name += (char) ('1' + n);
1001- break;
1002+ } else if (sources.size() > 2) {
1003+ region_name += string_compose ("-%1", n+1);
1004 }
1005
1006+ track_names.push_back (region_name);
1007+
1008 } else {
1009 if (fs) {
1010 region_name = region_name_from_path (fs->path(), false, false, sources.size(), n);
1011- } else{
1012+ } else {
1013 region_name = (*x)->name();
1014 }
1015+
1016+ track_names.push_back (PBD::basename_nosuffix (paths[n]));
1017 }
1018
1019 PropertyList plist;
1020@@ -798,6 +801,12 @@
1021 framepos_t rlen = 0;
1022
1023 begin_reversible_command (Operations::insert_file);
1024+
1025+ /* we only use tracks names when importing to new tracks, but we
1026+ * require that one is defined for every region, just to keep
1027+ * the API simpler.
1028+ */
1029+ assert (regions.size() == track_names.size());
1030
1031 for (vector<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) {
1032 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*r);
1033@@ -830,9 +839,8 @@
1034 pos = get_preferred_edit_position ();
1035 }
1036 }
1037-
1038-
1039- finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track);
1040+
1041+ finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track, track_names[n]);
1042
1043 rlen = (*r)->length();
1044
1045@@ -859,7 +867,7 @@
1046
1047 int
1048 Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t in_chans, uint32_t out_chans, framepos_t& pos,
1049- ImportMode mode, boost::shared_ptr<Track>& existing_track)
1050+ ImportMode mode, boost::shared_ptr<Track>& existing_track, const string& new_track_name)
1051 {
1052 boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
1053 boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(region);
1054@@ -916,7 +924,11 @@
1055 existing_track = mt.front();
1056 }
1057
1058- existing_track->set_name (region->name());
1059+ if (!new_track_name.empty()) {
1060+ existing_track->set_name (new_track_name);
1061+ } else {
1062+ existing_track->set_name (region->name());
1063+ }
1064 }
1065
1066 boost::shared_ptr<Playlist> playlist = existing_track->playlist();
1067
1068=== modified file 'gtk2_ardour/editor_rulers.cc'
1069--- gtk2_ardour/editor_rulers.cc 2013-09-21 19:05:02 +0000
1070+++ gtk2_ardour/editor_rulers.cc 2015-05-02 14:25:06 +0000
1071@@ -1862,13 +1862,13 @@
1072 long millisecs;
1073
1074 left = sample;
1075- hrs = left / (sample_rate * 60 * 60);
1076- left -= hrs * sample_rate * 60 * 60;
1077- mins = left / (sample_rate * 60);
1078- left -= mins * sample_rate * 60;
1079- secs = left / sample_rate;
1080- left -= secs * sample_rate;
1081- millisecs = left * 1000 / sample_rate;
1082+ hrs = left / (sample_rate * 60 * 60 * 1000);
1083+ left -= hrs * sample_rate * 60 * 60 * 1000;
1084+ mins = left / (sample_rate * 60 * 1000);
1085+ left -= mins * sample_rate * 60 * 1000;
1086+ secs = left / (sample_rate * 1000);
1087+ left -= secs * sample_rate * 1000;
1088+ millisecs = left / sample_rate;
1089
1090 *millisecs_p = millisecs;
1091 *secs_p = secs;
1092@@ -1888,7 +1888,7 @@
1093 return;
1094 }
1095
1096- fr = _session->frame_rate();
1097+ fr = _session->frame_rate() * 1000;
1098
1099 /* to prevent 'flashing' */
1100 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1101@@ -1897,7 +1897,7 @@
1102 lower = 0;
1103 }
1104 upper += spacer;
1105- framecnt_t const range = upper - lower;
1106+ framecnt_t const range = (upper - lower) * 1000;
1107
1108 if (range < (fr / 50)) {
1109 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1110@@ -1997,7 +1997,7 @@
1111 }
1112
1113 *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * minsec_nmarks);
1114- pos = ((((framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1115+ pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1116 switch (minsec_ruler_scale) {
1117 case minsec_show_seconds:
1118 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1119@@ -2014,7 +2014,7 @@
1120 (*marks)[n].style = GtkCustomRulerMarkMicro;
1121 }
1122 (*marks)[n].label = g_strdup (buf);
1123- (*marks)[n].position = pos;
1124+ (*marks)[n].position = pos/1000.0;
1125 }
1126 break;
1127 case minsec_show_minutes:
1128@@ -2032,7 +2032,7 @@
1129 (*marks)[n].style = GtkCustomRulerMarkMicro;
1130 }
1131 (*marks)[n].label = g_strdup (buf);
1132- (*marks)[n].position = pos;
1133+ (*marks)[n].position = pos/1000.0;
1134 }
1135 break;
1136 case minsec_show_hours:
1137@@ -2046,14 +2046,14 @@
1138 (*marks)[n].style = GtkCustomRulerMarkMicro;
1139 }
1140 (*marks)[n].label = g_strdup (buf);
1141- (*marks)[n].position = pos;
1142+ (*marks)[n].position = pos/1000.0;
1143 }
1144 break;
1145 case minsec_show_frames:
1146 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1147 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1148 if (millisecs % minsec_mark_modulo == 0) {
1149- if (secs == 0) {
1150+ if (millisecs == 0) {
1151 (*marks)[n].style = GtkCustomRulerMarkMajor;
1152 } else {
1153 (*marks)[n].style = GtkCustomRulerMarkMinor;
1154@@ -2064,7 +2064,7 @@
1155 (*marks)[n].style = GtkCustomRulerMarkMicro;
1156 }
1157 (*marks)[n].label = g_strdup (buf);
1158- (*marks)[n].position = pos;
1159+ (*marks)[n].position = pos/1000.0;
1160 }
1161 break;
1162 }
1163
1164=== modified file 'gtk2_ardour/midi_time_axis.cc'
1165--- gtk2_ardour/midi_time_axis.cc 2014-05-15 14:33:48 +0000
1166+++ gtk2_ardour/midi_time_axis.cc 2015-05-02 14:25:06 +0000
1167@@ -757,7 +757,7 @@
1168 Evoral::Parameter fully_qualified_param (MidiCCAutomation, chn, ctl);
1169 ctl_items.push_back (
1170 CheckMenuElem (
1171- string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn)),
1172+ string_compose ("<b>%1</b>: %2 [%3]", ctl, name, int (chn + 1)),
1173 sigc::bind (
1174 sigc::mem_fun (*this, &RouteTimeAxisView::toggle_automation_track),
1175 fully_qualified_param)));
1176@@ -1199,16 +1199,56 @@
1177 void
1178 MidiTimeAxisView::show_all_automation (bool apply_to_selection)
1179 {
1180+ using namespace MIDI::Name;
1181+
1182 if (apply_to_selection) {
1183 _editor.get_selection().tracks.foreach_midi_time_axis (
1184 boost::bind (&MidiTimeAxisView::show_all_automation, _1, false));
1185 } else {
1186 if (midi_track()) {
1187+ // Show existing automation
1188 const set<Evoral::Parameter> params = midi_track()->midi_playlist()->contained_automation();
1189
1190 for (set<Evoral::Parameter>::const_iterator i = params.begin(); i != params.end(); ++i) {
1191 create_automation_child(*i, true);
1192 }
1193+
1194+ // Show automation for all controllers named in midnam file
1195+ boost::shared_ptr<MasterDeviceNames> device_names = get_device_names();
1196+ if (gui_property (X_("midnam-model-name")) != "Generic" &&
1197+ device_names && !device_names->controls().empty()) {
1198+ const std::string device_mode = _midnam_custom_device_mode_selector.get_active_text();
1199+ const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
1200+ for (uint32_t chn = 0; chn < 16; ++chn) {
1201+ if ((selected_channels & (0x0001 << chn)) == 0) {
1202+ // Channel not in use
1203+ continue;
1204+ }
1205+
1206+ boost::shared_ptr<ChannelNameSet> chan_names = device_names->channel_name_set_by_channel(
1207+ device_mode, chn);
1208+ if (!chan_names) {
1209+ continue;
1210+ }
1211+
1212+ boost::shared_ptr<ControlNameList> control_names = device_names->control_name_list(
1213+ chan_names->control_list_name());
1214+ if (!control_names) {
1215+ continue;
1216+ }
1217+
1218+ for (ControlNameList::Controls::const_iterator c = control_names->controls().begin();
1219+ c != control_names->controls().end();
1220+ ++c) {
1221+ const uint16_t ctl = c->second->number();
1222+ if (ctl != MIDI_CTL_MSB_BANK && ctl != MIDI_CTL_LSB_BANK) {
1223+ /* Skip bank select controllers since they're handled specially */
1224+ const Evoral::Parameter param(MidiCCAutomation, chn, ctl);
1225+ create_automation_child(param, true);
1226+ }
1227+ }
1228+ }
1229+ }
1230 }
1231
1232 RouteTimeAxisView::show_all_automation ();
1233
1234=== modified file 'gtk2_ardour/mnemonic-us.bindings.in'
1235--- gtk2_ardour/mnemonic-us.bindings.in 2014-05-15 14:33:48 +0000
1236+++ gtk2_ardour/mnemonic-us.bindings.in 2015-05-02 14:25:06 +0000
1237@@ -339,7 +339,7 @@
1238 @trans|Transport/ToggleAutoPlay|5|toggle auto play
1239 @trans|Transport/ToggleAutoReturn|6|toggle auto return
1240 @trans|Transport/ToggleClick|7|toggle click (metronome)
1241-@ranges|Editor/set-tempo-from-region|9|set tempo (1 bar) from region(s)
1242+@ranges|Region/set-tempo-from-region|9|set tempo (1 bar) from region(s)
1243 @ranges|Editor/set-tempo-from-edit-range|0|set tempo (1 bar) from edit range
1244
1245 ; mouse stuff
1246
1247=== modified file 'gtk2_ardour/video_timeline.cc'
1248--- gtk2_ardour/video_timeline.cc 2014-05-15 14:33:48 +0000
1249+++ gtk2_ardour/video_timeline.cc 2015-05-02 14:25:06 +0000
1250@@ -39,6 +39,10 @@
1251 #include <pthread.h>
1252 #include <curl/curl.h>
1253
1254+#ifdef PLATFORM_WINDOWS
1255+#include <windows.h>
1256+#endif
1257+
1258 #include "i18n.h"
1259
1260 using namespace std;
1261@@ -718,20 +722,45 @@
1262 VideoTimeLine::find_xjadeo () {
1263 std::string xjadeo_file_path;
1264 if (getenv("XJREMOTE")) {
1265- _xjadeo_bin = strdup(getenv("XJREMOTE")); // XXX TODO: free it?!
1266- } else if (find_file_in_search_path (SearchPath(Glib::getenv("PATH")), X_("xjremote"), xjadeo_file_path)) {
1267- _xjadeo_bin = xjadeo_file_path;
1268+ _xjadeo_bin = getenv("XJREMOTE");
1269+ }
1270+ else if (find_file_in_search_path (SearchPath(Glib::getenv("PATH")), X_("xjremote"), xjadeo_file_path)) {
1271+ _xjadeo_bin = xjadeo_file_path;
1272+ }
1273+ else if (find_file_in_search_path (SearchPath(Glib::getenv("PATH")), X_("xjadeo"), xjadeo_file_path)) {
1274+ _xjadeo_bin = xjadeo_file_path;
1275+ }
1276+#ifdef __APPLE__
1277+ else if (Glib::file_test(X_("/Applications/Xjadeo.app/Contents/MacOS/xjremote"), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_EXECUTABLE)) {
1278+ _xjadeo_bin = X_("/Applications/Xjadeo.app/Contents/MacOS/xjremote");
1279 }
1280 else if (Glib::file_test(X_("/Applications/Jadeo.app/Contents/MacOS/xjremote"), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_EXECUTABLE)) {
1281 _xjadeo_bin = X_("/Applications/Jadeo.app/Contents/MacOS/xjremote");
1282 }
1283- /* TODO: win32: allow to configure PATH to xjremote */
1284+#endif
1285+#ifdef PLATFORM_WINDOWS
1286+ else {
1287+ HKEY key;
1288+ DWORD size = PATH_MAX;
1289+ char path[PATH_MAX+1];
1290+ xjadeo_file_path = X_("");
1291+ if ( (ERROR_SUCCESS == RegOpenKeyExA (HKEY_LOCAL_MACHINE, "Software\\RSS\\xjadeo", 0, KEY_READ, &key))
1292+ && (ERROR_SUCCESS == RegQueryValueExA (key, "Install_Dir", 0, NULL, (LPBYTE)path, &size))
1293+ )
1294+ {
1295+ xjadeo_file_path = g_build_filename(path, X_("xjadeo.exe"));
1296+ }
1297+ }
1298+ if (Glib::file_test(xjadeo_file_path, Glib::FILE_TEST_EXISTS)) {
1299+ _xjadeo_bin = xjadeo_file_path;
1300+ }
1301 else if (Glib::file_test(X_("C:\\Program Files\\xjadeo\\xjremote.exe"), Glib::FILE_TEST_EXISTS)) {
1302 _xjadeo_bin = X_("C:\\Program Files\\xjadeo\\xjremote.exe");
1303 }
1304 else if (Glib::file_test(X_("C:\\Program Files\\xjadeo\\xjremote.bat"), Glib::FILE_TEST_EXISTS)) {
1305 _xjadeo_bin = X_("C:\\Program Files\\xjadeo\\xjremote.bat");
1306 }
1307+#endif
1308 else {
1309 _xjadeo_bin = X_("");
1310 warning << _("Video-monitor 'xjadeo' was not found. Please install http://xjadeo.sf.net/ "
1311
1312=== modified file 'libs/ardour/ardour/audiofilesource.h'
1313--- libs/ardour/ardour/audiofilesource.h 2014-05-15 14:33:48 +0000
1314+++ libs/ardour/ardour/audiofilesource.h 2015-05-02 14:25:06 +0000
1315@@ -93,6 +93,12 @@
1316 /** Constructor to be called for existing in-session files */
1317 AudioFileSource (Session&, const XMLNode&, bool must_exist = true);
1318
1319+ /** Constructor to be called for crash recovery. Final argument is not
1320+ * used but exists to differentiate from the external-to-session
1321+ * constructor above.
1322+ */
1323+ AudioFileSource (Session&, const std::string& path, Source::Flag flags, bool);
1324+
1325 int init (const std::string& idstr, bool must_exist);
1326
1327 virtual void set_header_timeline_position () = 0;
1328
1329=== modified file 'libs/ardour/ardour/file_source.h'
1330--- libs/ardour/ardour/file_source.h 2014-05-15 14:33:48 +0000
1331+++ libs/ardour/ardour/file_source.h 2015-05-02 14:25:06 +0000
1332@@ -85,6 +85,10 @@
1333 void existence_check ();
1334 virtual void prevent_deletion ();
1335
1336+ /** Rename the file on disk referenced by this source to \param newname
1337+ */
1338+ int rename (const std::string& name);
1339+
1340 protected:
1341 FileSource (Session& session, DataType type,
1342 const std::string& path,
1343
1344=== modified file 'libs/ardour/ardour/session.h'
1345--- libs/ardour/ardour/session.h 2014-05-15 14:33:48 +0000
1346+++ libs/ardour/ardour/session.h 2015-05-02 14:25:06 +0000
1347@@ -195,10 +195,10 @@
1348 std::string peak_path (std::string) const;
1349
1350 std::string peak_path_from_audio_path (std::string) const;
1351- std::string new_audio_source_name (const std::string&, uint32_t nchans, uint32_t chan, bool destructive);
1352- std::string new_midi_source_name (const std::string&);
1353- std::string new_source_path_from_name (DataType type, const std::string&);
1354+ std::string new_audio_source_path (const std::string&, uint32_t nchans, uint32_t chan, bool destructive, bool take_required);
1355+ std::string new_midi_source_path (const std::string&);
1356 RouteList new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name);
1357+ std::vector<std::string> get_paths_for_new_sources (bool allow_replacing, const std::string& import_file_path, uint32_t channels);
1358
1359 void process (pframes_t nframes);
1360
1361@@ -526,8 +526,6 @@
1362
1363 boost::shared_ptr<Region> find_whole_file_parent (boost::shared_ptr<Region const>) const;
1364
1365- std::string path_from_region_name (DataType type, std::string name, std::string identifier);
1366-
1367 boost::shared_ptr<Region> XMLRegionFactory (const XMLNode&, bool full);
1368 boost::shared_ptr<AudioRegion> XMLAudioRegionFactory (const XMLNode&, bool full);
1369 boost::shared_ptr<MidiRegion> XMLMidiRegionFactory (const XMLNode&, bool full);
1370@@ -584,8 +582,8 @@
1371 boost::shared_ptr<MidiSource> create_midi_source_by_stealing_name (boost::shared_ptr<Track>);
1372
1373 boost::shared_ptr<Source> source_by_id (const PBD::ID&);
1374- boost::shared_ptr<AudioFileSource> source_by_path_and_channel (const std::string&, uint16_t) const;
1375- boost::shared_ptr<MidiSource> source_by_path (const std::string&) const;
1376+ boost::shared_ptr<AudioFileSource> audio_source_by_path_and_channel (const std::string&, uint16_t) const;
1377+ boost::shared_ptr<MidiSource> midi_source_by_path (const std::string&) const;
1378 uint32_t count_sources_by_origin (const std::string&);
1379
1380 void add_playlist (boost::shared_ptr<Playlist>, bool unused = false);
1381@@ -1433,7 +1431,7 @@
1382
1383 bool no_questions_about_missing_files;
1384
1385- std::string get_best_session_directory_for_new_source ();
1386+ std::string get_best_session_directory_for_new_audio ();
1387
1388 mutable gint _playback_load;
1389 mutable gint _capture_load;
1390
1391=== modified file 'libs/ardour/ardour/smf_source.h'
1392--- libs/ardour/ardour/smf_source.h 2014-05-15 14:33:48 +0000
1393+++ libs/ardour/ardour/smf_source.h 2015-05-02 14:25:06 +0000
1394@@ -47,15 +47,6 @@
1395
1396 virtual ~SMFSource ();
1397
1398- /** Rename the file on disk referenced by this source to \param newname
1399- *
1400- * This method exists only for MIDI file sources, not for audio, which
1401- * can never be renamed. It exists for MIDI so that we can get
1402- * consistent and sane region/source numbering when regions are added
1403- * manually (which never happens with audio).
1404- */
1405- int rename (const std::string& name);
1406-
1407 bool safe_file_extension (const std::string& path) const {
1408 return safe_midi_file_extension(path);
1409 }
1410
1411=== modified file 'libs/ardour/ardour/sndfilesource.h'
1412--- libs/ardour/ardour/sndfilesource.h 2013-09-21 19:05:02 +0000
1413+++ libs/ardour/ardour/sndfilesource.h 2015-05-02 14:25:06 +0000
1414@@ -38,7 +38,16 @@
1415 SampleFormat samp_format, HeaderFormat hdr_format, framecnt_t rate,
1416 Flag flags = SndFileSource::default_writable_flags);
1417
1418- /** Constructor to be called for existing in-session files */
1419+ /* Constructor to be called for recovering files being used for
1420+ * capture. They are in-session, they already exist, they should not
1421+ * be writable. They are an odd hybrid (from a constructor point of
1422+ * view) of the previous two constructors.
1423+ */
1424+ SndFileSource (Session&, const std::string& path, int chn);
1425+
1426+ /** Constructor to be called for existing in-session files during
1427+ * session loading
1428+ */
1429 SndFileSource (Session&, const XMLNode&);
1430
1431 ~SndFileSource ();
1432
1433=== modified file 'libs/ardour/ardour/source.h'
1434--- libs/ardour/ardour/source.h 2013-09-21 19:05:02 +0000
1435+++ libs/ardour/ardour/source.h 2015-05-02 14:25:06 +0000
1436@@ -47,7 +47,8 @@
1437 RemovableIfEmpty = 0x10,
1438 RemoveAtDestroy = 0x20,
1439 NoPeakFile = 0x40,
1440- Destructive = 0x80
1441+ Destructive = 0x80,
1442+ Empty = 0x100, /* used for MIDI only */
1443 };
1444
1445 Source (Session&, DataType type, const std::string& name, Flag flags=Flag(0));
1446
1447=== modified file 'libs/ardour/ardour/source_factory.h'
1448--- libs/ardour/ardour/source_factory.h 2013-09-21 19:05:02 +0000
1449+++ libs/ardour/ardour/source_factory.h 2015-05-02 14:25:06 +0000
1450@@ -57,6 +57,9 @@
1451 bool destructive, framecnt_t rate, bool announce = true, bool async = false);
1452
1453
1454+ static boost::shared_ptr<Source> createForRecovery
1455+ (DataType type, Session&, const std::string& path, int chn);
1456+
1457 static boost::shared_ptr<Source> createFromPlaylist
1458 (DataType type, Session& s, boost::shared_ptr<Playlist> p, const PBD::ID& orig, const std::string& name,
1459 uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks);
1460
1461=== modified file 'libs/ardour/audio_diskstream.cc'
1462--- libs/ardour/audio_diskstream.cc 2014-05-15 14:33:48 +0000
1463+++ libs/ardour/audio_diskstream.cc 2015-05-02 14:25:06 +0000
1464@@ -2181,11 +2181,16 @@
1465 continue;
1466 }
1467
1468+ /* XXX as of June 2014, we always record to mono
1469+ files. Since this Source is being created as part of
1470+ crash recovery, we know that we need the first
1471+ channel (the final argument to the SourceFactory
1472+ call below). If we ever support non-mono files for
1473+ capture, this will need rethinking.
1474+ */
1475+
1476 try {
1477- fs = boost::dynamic_pointer_cast<AudioFileSource> (
1478- SourceFactory::createWritable (
1479- DataType::AUDIO, _session,
1480- prop->value(), false, _session.frame_rate()));
1481+ fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createForRecovery (DataType::AUDIO, _session, prop->value(), 0));
1482 }
1483
1484 catch (failed_constructor& err) {
1485@@ -2216,21 +2221,31 @@
1486 return -1;
1487 }
1488
1489- boost::shared_ptr<AudioRegion> region;
1490-
1491 try {
1492
1493+ boost::shared_ptr<AudioRegion> wf_region;
1494+ boost::shared_ptr<AudioRegion> region;
1495+
1496+ /* First create the whole file region */
1497+
1498 PropertyList plist;
1499-
1500+
1501 plist.add (Properties::start, 0);
1502 plist.add (Properties::length, first_fs->length (first_fs->timeline_position()));
1503 plist.add (Properties::name, region_name_from_path (first_fs->name(), true));
1504
1505+ wf_region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
1506+
1507+ wf_region->set_automatic (true);
1508+ wf_region->set_whole_file (true);
1509+ wf_region->special_set_position (position);
1510+
1511+ /* Now create a region that isn't the whole file for adding to
1512+ * the playlist */
1513+
1514 region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
1515-
1516- region->set_automatic (true);
1517- region->set_whole_file (true);
1518- region->special_set_position (0);
1519+
1520+ _playlist->add_region (region, position);
1521 }
1522
1523 catch (failed_constructor& err) {
1524@@ -2241,7 +2256,6 @@
1525 return -1;
1526 }
1527
1528- _playlist->add_region (region, position);
1529
1530 return 0;
1531 }
1532
1533=== modified file 'libs/ardour/audiofilesource.cc'
1534--- libs/ardour/audiofilesource.cc 2013-09-21 19:05:02 +0000
1535+++ libs/ardour/audiofilesource.cc 2015-05-02 14:25:06 +0000
1536@@ -36,6 +36,7 @@
1537 #include "pbd/stl_delete.h"
1538 #include "pbd/strsplit.h"
1539 #include "pbd/shortpath.h"
1540+#include "pbd/stacktrace.h"
1541 #include "pbd/enumwriter.h"
1542
1543 #include <sndfile.h>
1544@@ -114,6 +115,22 @@
1545 }
1546 }
1547
1548+/** Constructor used for existing internal-to-session files during crash
1549+ * recovery. File must exist
1550+ */
1551+AudioFileSource::AudioFileSource (Session& s, const string& path, Source::Flag flags, bool /* ignored-exists-for-prototype differentiation */)
1552+ : Source (s, DataType::AUDIO, path, flags)
1553+ , AudioSource (s, path)
1554+ , FileSource (s, DataType::AUDIO, path, string(), flags)
1555+{
1556+ /* note that origin remains empty */
1557+
1558+ if (init (_path, true)) {
1559+ throw failed_constructor ();
1560+ }
1561+}
1562+
1563+
1564 /** Constructor used for existing internal-to-session files via XML. File must exist. */
1565 AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist)
1566 : Source (s, node)
1567
1568=== modified file 'libs/ardour/diskstream.cc'
1569--- libs/ardour/diskstream.cc 2013-09-21 19:05:02 +0000
1570+++ libs/ardour/diskstream.cc 2015-05-02 14:25:06 +0000
1571@@ -739,4 +739,3 @@
1572 {
1573 g_atomic_int_set (&_record_enabled, 0);
1574 }
1575-
1576
1577=== modified file 'libs/ardour/enums.cc'
1578--- libs/ardour/enums.cc 2013-12-23 22:07:52 +0000
1579+++ libs/ardour/enums.cc 2015-05-02 14:25:06 +0000
1580@@ -447,6 +447,7 @@
1581 REGISTER_CLASS_ENUM (Source, RemoveAtDestroy);
1582 REGISTER_CLASS_ENUM (Source, NoPeakFile);
1583 REGISTER_CLASS_ENUM (Source, Destructive);
1584+ REGISTER_CLASS_ENUM (Source, Empty);
1585 REGISTER_BITS (_Source_Flag);
1586
1587 REGISTER_ENUM (FadeLinear);
1588
1589=== modified file 'libs/ardour/file_source.cc'
1590--- libs/ardour/file_source.cc 2014-05-15 14:33:48 +0000
1591+++ libs/ardour/file_source.cc 2015-05-02 14:25:06 +0000
1592@@ -56,7 +56,7 @@
1593 FileSource::FileSource (Session& session, DataType type, const string& path, const string& origin, Source::Flag flag)
1594 : Source(session, type, path, flag)
1595 , _path (path)
1596- , _file_is_new (!origin.empty()) // origin empty => new file VS. origin !empty => new file
1597+ , _file_is_new (!origin.empty()) // if origin is left unspecified (empty string) then file must exist
1598 , _channel (0)
1599 , _origin (origin)
1600 , _open (false)
1601@@ -214,7 +214,7 @@
1602
1603 if (move_dependents_to_trash() != 0) {
1604 /* try to back out */
1605- rename (newpath.c_str(), _path.c_str());
1606+ ::rename (newpath.c_str(), _path.c_str());
1607 return -1;
1608 }
1609
1610@@ -256,8 +256,6 @@
1611
1612 split (search_path, dirs, ':');
1613
1614- hits.clear ();
1615-
1616 for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
1617
1618 fullpath = Glib::build_filename (*i, path);
1619@@ -312,9 +310,9 @@
1620 /* no match: error */
1621
1622 if (must_exist) {
1623- error << string_compose(
1624- _("Filesource: cannot find required file (%1): while searching %2"),
1625- path, search_path) << endmsg;
1626+ /* do not generate an error here, leave that to
1627+ whoever deals with the false return value.
1628+ */
1629 goto out;
1630 } else {
1631 isnew = true;
1632@@ -325,16 +323,17 @@
1633
1634 keeppath = de_duped_hits[0];
1635 }
1636-
1637- } else {
1638+
1639+ } else {
1640 keeppath = path;
1641 }
1642
1643 /* Current find() is unable to parse relative path names to yet non-existant
1644 sources. QuickFix(tm)
1645 */
1646- if (keeppath == "") {
1647- if (must_exist) {
1648+
1649+ if (keeppath.empty()) {
1650+ if (must_exist) {
1651 error << "FileSource::find(), keeppath = \"\", but the file must exist" << endl;
1652 } else {
1653 keeppath = path;
1654@@ -581,3 +580,28 @@
1655 return true;
1656 }
1657
1658+int
1659+FileSource::rename (const string& newpath)
1660+{
1661+ Glib::Threads::Mutex::Lock lm (_lock);
1662+ string oldpath = _path;
1663+
1664+ // Test whether newpath exists, if yes notify the user but continue.
1665+ if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
1666+ error << string_compose (_("Programming error! %1 tried to rename a file over another file! It's safe to continue working, but please report this to the developers."), PROGRAM_NAME) << endmsg;
1667+ return -1;
1668+ }
1669+
1670+ if (Glib::file_test (oldpath.c_str(), Glib::FILE_TEST_EXISTS)) {
1671+ /* rename only needed if file exists on disk */
1672+ if (::rename (oldpath.c_str(), newpath.c_str()) != 0) {
1673+ error << string_compose (_("cannot rename file %1 to %2 (%3)"), oldpath, newpath, strerror(errno)) << endmsg;
1674+ return -1;
1675+ }
1676+ }
1677+
1678+ _name = Glib::path_get_basename (newpath);
1679+ _path = newpath;
1680+
1681+ return 0;
1682+}
1683
1684=== modified file 'libs/ardour/filter.cc'
1685--- libs/ardour/filter.cc 2013-09-21 19:05:02 +0000
1686+++ libs/ardour/filter.cc 2015-05-02 14:25:06 +0000
1687@@ -59,10 +59,9 @@
1688 }
1689 }
1690
1691- string path = session.path_from_region_name (region->data_type(),
1692- PBD::basename_nosuffix (names[i]), string (""));
1693+ string path = session.new_audio_source_path (name, region->n_channels(), i, false, false);
1694
1695- if (path.length() == 0) {
1696+ if (path.empty()) {
1697 error << string_compose (_("filter: error creating name for new file based on %1"), region->name())
1698 << endmsg;
1699 return -1;
1700
1701=== modified file 'libs/ardour/import.cc'
1702--- libs/ardour/import.cc 2013-09-21 19:05:02 +0000
1703+++ libs/ardour/import.cc 2015-05-02 14:25:06 +0000
1704@@ -116,78 +116,31 @@
1705 }
1706 }
1707
1708-static std::string
1709-get_non_existent_filename (HeaderFormat hf, DataType type, const bool allow_replacing, const std::string& destdir, const std::string& basename, uint channel, uint channels)
1710-{
1711- char buf[PATH_MAX+1];
1712- bool goodfile = false;
1713- string base = basename;
1714- string ext = native_header_format_extension (hf, type);
1715- uint32_t cnt = 1;
1716-
1717- do {
1718-
1719- if (type == DataType::AUDIO && channels == 2) {
1720- if (channel == 0) {
1721- if (cnt == 1) {
1722- snprintf (buf, sizeof(buf), "%s-L%s", base.c_str(), ext.c_str());
1723- } else {
1724- snprintf (buf, sizeof(buf), "%s-%d-L%s", base.c_str(), cnt, ext.c_str());
1725- }
1726- } else {
1727- if (cnt == 1) {
1728- snprintf (buf, sizeof(buf), "%s-R%s", base.c_str(), ext.c_str());
1729- } else {
1730- snprintf (buf, sizeof(buf), "%s-%d-R%s", base.c_str(), cnt, ext.c_str());
1731- }
1732- }
1733- } else if (channels > 1) {
1734- if (cnt == 1) {
1735- snprintf (buf, sizeof(buf), "%s-c%d%s", base.c_str(), channel, ext.c_str());
1736- } else {
1737- snprintf (buf, sizeof(buf), "%s-%d-c%d%s", base.c_str(), cnt, channel, ext.c_str());
1738- }
1739- } else {
1740- if (cnt == 1) {
1741- snprintf (buf, sizeof(buf), "%s%s", base.c_str(), ext.c_str());
1742- } else {
1743- snprintf (buf, sizeof(buf), "%s-%d%s", base.c_str(), cnt, ext.c_str());
1744- }
1745- }
1746-
1747- string tempname = destdir + "/" + buf;
1748-
1749- if (!allow_replacing && Glib::file_test (tempname, Glib::FILE_TEST_EXISTS)) {
1750-
1751- cnt++;
1752-
1753- } else {
1754-
1755- goodfile = true;
1756- }
1757-
1758- } while (!goodfile);
1759-
1760- return buf;
1761-}
1762-
1763-static vector<string>
1764-get_paths_for_new_sources (HeaderFormat hf, const bool allow_replacing, const string& import_file_path, const string& session_dir, uint channels)
1765+vector<string>
1766+Session::get_paths_for_new_sources (bool /*allow_replacing*/, const string& import_file_path, uint32_t channels)
1767 {
1768 vector<string> new_paths;
1769 const string basename = basename_nosuffix (import_file_path);
1770
1771- SessionDirectory sdir(session_dir);
1772-
1773 for (uint n = 0; n < channels; ++n) {
1774
1775 const DataType type = SMFSource::safe_midi_file_extension (import_file_path) ? DataType::MIDI : DataType::AUDIO;
1776-
1777- std::string filepath = (type == DataType::MIDI)
1778- ? sdir.midi_path() : sdir.sound_path();
1779-
1780- filepath = Glib::build_filename (filepath,
1781- get_non_existent_filename (hf, type, allow_replacing, filepath, basename, n, channels));
1782+ string filepath;
1783+
1784+ switch (type) {
1785+ case DataType::MIDI:
1786+ filepath = new_midi_source_path (basename);
1787+ break;
1788+ case DataType::AUDIO:
1789+ filepath = new_audio_source_path (basename, channels, n, false, false);
1790+ break;
1791+ }
1792+
1793+ if (filepath.empty()) {
1794+ error << string_compose (_("Cannot find new filename for imported file %1"), import_file_path) << endmsg;
1795+ return vector<string>();
1796+ }
1797+
1798 new_paths.push_back (filepath);
1799 }
1800
1801@@ -201,7 +154,7 @@
1802 for (vector<string>::const_iterator i = new_paths.begin();
1803 i != new_paths.end(); ++i)
1804 {
1805- boost::shared_ptr<Source> source = session->source_by_path_and_channel(*i, 0);
1806+ boost::shared_ptr<Source> source = session->audio_source_by_path_and_channel(*i, 0);
1807
1808 if (source == 0) {
1809 error << string_compose(_("Could not find a source for %1 even though we are updating this file!"), (*i)) << endl;
1810@@ -274,12 +227,12 @@
1811 {
1812 const framecnt_t nframes = ResampledImportableSource::blocksize;
1813 boost::shared_ptr<AudioFileSource> afs;
1814- uint channels = source->channels();
1815+ uint32_t channels = source->channels();
1816
1817 boost::scoped_array<float> data(new float[nframes * channels]);
1818 vector<boost::shared_array<Sample> > channel_data;
1819
1820- for (uint n = 0; n < channels; ++n) {
1821+ for (uint32_t n = 0; n < channels; ++n) {
1822 channel_data.push_back(boost::shared_array<Sample>(new Sample[nframes]));
1823 }
1824
1825@@ -323,14 +276,14 @@
1826 progress_multiplier = 0.5;
1827 progress_base = 0.5;
1828 }
1829-
1830- uint read_count = 0;
1831+
1832+ framecnt_t read_count = 0;
1833
1834 while (!status.cancel) {
1835
1836 framecnt_t nread, nfread;
1837- uint x;
1838- uint chn;
1839+ uint32_t x;
1840+ uint32_t chn;
1841
1842 if ((nread = source->read (data.get(), nframes)) == 0) {
1843 break;
1844@@ -513,10 +466,7 @@
1845 }
1846 }
1847
1848- vector<string> new_paths = get_paths_for_new_sources (config.get_native_file_header_format(),
1849- status.replace_existing_source, *p,
1850- get_best_session_directory_for_new_source (),
1851- channels);
1852+ vector<string> new_paths = get_paths_for_new_sources (status.replace_existing_source, *p, channels);
1853 Sources newfiles;
1854 framepos_t natural_position = source ? source->natural_position() : 0;
1855
1856
1857=== modified file 'libs/ardour/midi_diskstream.cc'
1858--- libs/ardour/midi_diskstream.cc 2014-05-15 14:33:48 +0000
1859+++ libs/ardour/midi_diskstream.cc 2015-05-02 14:25:06 +0000
1860@@ -1234,9 +1234,9 @@
1861 */
1862
1863 try {
1864- string new_name = _session.new_midi_source_name (name());
1865+ string new_path = _session.new_midi_source_path (name());
1866
1867- if (_write_source->rename (new_name)) {
1868+ if (_write_source->rename (new_path)) {
1869 return string();
1870 }
1871 } catch (...) {
1872
1873=== modified file 'libs/ardour/session.cc'
1874--- libs/ardour/session.cc 2014-05-15 14:33:48 +0000
1875+++ libs/ardour/session.cc 2015-05-02 14:25:06 +0000
1876@@ -36,6 +36,7 @@
1877
1878 #include <boost/algorithm/string/erase.hpp>
1879
1880+#include "pbd/convert.h"
1881 #include "pbd/error.h"
1882 #include "pbd/boost_debug.h"
1883 #include "pbd/pathscanner.h"
1884@@ -3305,7 +3306,7 @@
1885 }
1886
1887 boost::shared_ptr<AudioFileSource>
1888-Session::source_by_path_and_channel (const string& path, uint16_t chn) const
1889+Session::audio_source_by_path_and_channel (const string& path, uint16_t chn) const
1890 {
1891 /* Restricted to audio files because only audio sources have channel
1892 as a property.
1893@@ -3326,7 +3327,7 @@
1894 }
1895
1896 boost::shared_ptr<MidiSource>
1897-Session::source_by_path (const std::string& path) const
1898+Session::midi_source_by_path (const std::string& path) const
1899 {
1900 /* Restricted to MIDI files because audio sources require a channel
1901 for unique identification, in addition to a path.
1902@@ -3366,30 +3367,6 @@
1903 return cnt;
1904 }
1905
1906-/** Return the full path (in some session directory) for a new within-session source.
1907- * \a name must be a session-unique name that does not contain slashes
1908- * (e.g. as returned by new_*_source_name)
1909- */
1910-string
1911-Session::new_source_path_from_name (DataType type, const string& name)
1912-{
1913- assert(name.find("/") == string::npos);
1914-
1915- SessionDirectory sdir(get_best_session_directory_for_new_source());
1916-
1917- std::string p;
1918- if (type == DataType::AUDIO) {
1919- p = sdir.sound_path();
1920- } else if (type == DataType::MIDI) {
1921- p = sdir.midi_path();
1922- } else {
1923- error << "Unknown source type, unable to create file path" << endmsg;
1924- return "";
1925- }
1926-
1927- return Glib::build_filename (p, name);
1928-}
1929-
1930 string
1931 Session::peak_path (string base) const
1932 {
1933@@ -3398,18 +3375,20 @@
1934
1935 /** Return a unique name based on \a base for a new internal audio source */
1936 string
1937-Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t chan, bool destructive)
1938+Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t chan, bool destructive, bool take_required)
1939 {
1940 uint32_t cnt;
1941- char buf[PATH_MAX+1];
1942- const uint32_t limit = 10000;
1943+ string possible_name;
1944+ const uint32_t limit = 9999; // arbitrary limit on number of files with the same basic name
1945 string legalized;
1946 string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
1947+ bool some_related_source_name_exists = false;
1948
1949- buf[0] = '\0';
1950+ possible_name[0] = '\0';
1951 legalized = legalize_for_path (base);
1952
1953 // Find a "version" of the base name that doesn't exist in any of the possible directories.
1954+
1955 for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) {
1956
1957 vector<space_and_path>::iterator i;
1958@@ -3417,47 +3396,37 @@
1959
1960 for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
1961
1962+ ostringstream sstr;
1963+
1964 if (destructive) {
1965-
1966- if (nchan < 2) {
1967- snprintf (buf, sizeof(buf), "T%04d-%s%s",
1968- cnt, legalized.c_str(), ext.c_str());
1969- } else if (nchan == 2) {
1970- if (chan == 0) {
1971- snprintf (buf, sizeof(buf), "T%04d-%s%%L%s",
1972- cnt, legalized.c_str(), ext.c_str());
1973- } else {
1974- snprintf (buf, sizeof(buf), "T%04d-%s%%R%s",
1975- cnt, legalized.c_str(), ext.c_str());
1976- }
1977- } else if (nchan < 26) {
1978- snprintf (buf, sizeof(buf), "T%04d-%s%%%c%s",
1979- cnt, legalized.c_str(), 'a' + chan, ext.c_str());
1980- } else {
1981- snprintf (buf, sizeof(buf), "T%04d-%s%s",
1982- cnt, legalized.c_str(), ext.c_str());
1983- }
1984-
1985+ sstr << 'T';
1986+ sstr << setfill ('0') << setw (4) << cnt;
1987+ sstr << legalized;
1988 } else {
1989-
1990- if (nchan < 2) {
1991- snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
1992- } else if (nchan == 2) {
1993- if (chan == 0) {
1994- snprintf (buf, sizeof(buf), "%s-%u%%L%s", legalized.c_str(), cnt, ext.c_str());
1995- } else {
1996- snprintf (buf, sizeof(buf), "%s-%u%%R%s", legalized.c_str(), cnt, ext.c_str());
1997- }
1998- } else if (nchan < 26) {
1999- snprintf (buf, sizeof(buf), "%s-%u%%%c%s", legalized.c_str(), cnt, 'a' + chan, ext.c_str());
2000- } else {
2001- snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
2002+ sstr << legalized;
2003+
2004+ if (take_required || some_related_source_name_exists) {
2005+ sstr << '-';
2006+ sstr << cnt;
2007 }
2008 }
2009-
2010+
2011+ if (nchan == 2) {
2012+ if (chan == 0) {
2013+ sstr << "%L";
2014+ } else {
2015+ sstr << "%R";
2016+ }
2017+ } else if (nchan > 2 && nchan < 26) {
2018+ sstr << '%';
2019+ sstr << 'a' + chan;
2020+ }
2021+
2022+ sstr << ext;
2023+
2024+ possible_name = sstr.str();
2025 SessionDirectory sdir((*i).path);
2026-
2027- string spath = sdir.sound_path();
2028+ const string spath = sdir.sound_path();
2029
2030 /* note that we search *without* the extension so that
2031 we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf"
2032@@ -3465,7 +3434,7 @@
2033 a file format change.
2034 */
2035
2036- if (matching_unsuffixed_filename_exists_in (spath, buf)) {
2037+ if (matching_unsuffixed_filename_exists_in (spath, possible_name)) {
2038 existing++;
2039 break;
2040 }
2041@@ -3479,9 +3448,9 @@
2042 * notions of their removability.
2043 */
2044
2045- string possible_path = Glib::build_filename (spath, buf);
2046+ string possible_path = Glib::build_filename (spath, possible_name);
2047
2048- if (source_by_path (possible_path)) {
2049+ if (audio_source_by_path_and_channel (possible_path, chan)) {
2050 existing++;
2051 break;
2052 }
2053@@ -3491,6 +3460,8 @@
2054 break;
2055 }
2056
2057+ some_related_source_name_exists = true;
2058+
2059 if (cnt > limit) {
2060 error << string_compose(
2061 _("There are already %1 recordings for %2, which I consider too many."),
2062@@ -3500,32 +3471,31 @@
2063 }
2064 }
2065
2066- return Glib::path_get_basename (buf);
2067-}
2068-
2069-/** Create a new within-session audio source */
2070-boost::shared_ptr<AudioFileSource>
2071-Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive)
2072-{
2073- const string name = new_audio_source_name (n, n_chans, chan, destructive);
2074- const string path = new_source_path_from_name(DataType::AUDIO, name);
2075-
2076- return boost::dynamic_pointer_cast<AudioFileSource> (
2077- SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
2078+ /* We've established that the new name does not exist in any session
2079+ * directory, so now find out which one we should use for this new
2080+ * audio source.
2081+ */
2082+
2083+ SessionDirectory sdir (get_best_session_directory_for_new_audio());
2084+
2085+ std::string s = Glib::build_filename (sdir.sound_path(), possible_name);
2086+
2087+ return s;
2088 }
2089
2090 /** Return a unique name based on \a owner_name for a new internal MIDI source */
2091 string
2092-Session::new_midi_source_name (const string& owner_name)
2093+Session::new_midi_source_path (const string& base)
2094 {
2095 uint32_t cnt;
2096 char buf[PATH_MAX+1];
2097 const uint32_t limit = 10000;
2098 string legalized;
2099+ string possible_path;
2100 string possible_name;
2101
2102 buf[0] = '\0';
2103- legalized = legalize_for_path (owner_name);
2104+ legalized = legalize_for_path (base);
2105
2106 // Find a "version" of the file name that doesn't exist in any of the possible directories.
2107
2108@@ -3533,7 +3503,7 @@
2109
2110 vector<space_and_path>::iterator i;
2111 uint32_t existing = 0;
2112-
2113+
2114 for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
2115
2116 SessionDirectory sdir((*i).path);
2117@@ -3541,13 +3511,13 @@
2118 snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt);
2119 possible_name = buf;
2120
2121- std::string possible_path = Glib::build_filename (sdir.midi_path(), possible_name);
2122+ possible_path = Glib::build_filename (sdir.midi_path(), possible_name);
2123
2124 if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) {
2125 existing++;
2126 }
2127
2128- if (source_by_path (possible_path)) {
2129+ if (midi_source_by_path (possible_path)) {
2130 existing++;
2131 }
2132 }
2133@@ -3559,31 +3529,47 @@
2134 if (cnt > limit) {
2135 error << string_compose(
2136 _("There are already %1 recordings for %2, which I consider too many."),
2137- limit, owner_name) << endmsg;
2138+ limit, base) << endmsg;
2139 destroy ();
2140- throw failed_constructor();
2141+ return 0;
2142 }
2143 }
2144
2145- return possible_name;
2146-}
2147-
2148+ /* No need to "find best location" for software/app-based RAID, because
2149+ MIDI is so small that we always put it in the same place.
2150+ */
2151+
2152+ return possible_path;
2153+}
2154+
2155+
2156+/** Create a new within-session audio source */
2157+boost::shared_ptr<AudioFileSource>
2158+Session::create_audio_source_for_session (size_t n_chans, string const & base, uint32_t chan, bool destructive)
2159+{
2160+ const string path = new_audio_source_path (base, n_chans, chan, destructive, true);
2161+
2162+ if (!path.empty()) {
2163+ return boost::dynamic_pointer_cast<AudioFileSource> (
2164+ SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
2165+ } else {
2166+ throw failed_constructor ();
2167+ }
2168+}
2169
2170 /** Create a new within-session MIDI source */
2171 boost::shared_ptr<MidiSource>
2172 Session::create_midi_source_for_session (string const & basic_name)
2173 {
2174- std::string name;
2175-
2176- if (name.empty()) {
2177- name = new_midi_source_name (basic_name);
2178+ const string path = new_midi_source_path (basic_name);
2179+
2180+ if (!path.empty()) {
2181+ return boost::dynamic_pointer_cast<SMFSource> (
2182+ SourceFactory::createWritable (
2183+ DataType::MIDI, *this, path, false, frame_rate()));
2184+ } else {
2185+ throw failed_constructor ();
2186 }
2187-
2188- const string path = new_source_path_from_name (DataType::MIDI, name);
2189-
2190- return boost::dynamic_pointer_cast<SMFSource> (
2191- SourceFactory::createWritable (
2192- DataType::MIDI, *this, path, false, frame_rate()));
2193 }
2194
2195 /** Create a new within-session MIDI source */
2196@@ -3617,7 +3603,13 @@
2197 return boost::shared_ptr<MidiSource>();
2198 }
2199
2200- const string path = new_source_path_from_name (DataType::MIDI, name);
2201+ /* MIDI files are small, just put them in the first location of the
2202+ session source search path.
2203+ */
2204+ vector<string> dirs;
2205+
2206+ split (source_search_path(DataType::MIDI), dirs, ':');
2207+ const string path = Glib::build_filename (dirs.front(), name);
2208
2209 return boost::dynamic_pointer_cast<SMFSource> (
2210 SourceFactory::createWritable (
2211@@ -4123,18 +4115,13 @@
2212 boost::shared_ptr<Region> result;
2213 boost::shared_ptr<Playlist> playlist;
2214 boost::shared_ptr<AudioFileSource> fsource;
2215- uint32_t x;
2216- char buf[PATH_MAX+1];
2217 ChanCount diskstream_channels (track.n_channels());
2218 framepos_t position;
2219 framecnt_t this_chunk;
2220 framepos_t to_do;
2221 BufferSet buffers;
2222- SessionDirectory sdir(get_best_session_directory_for_new_source ());
2223- const string sound_dir = sdir.sound_path();
2224 framepos_t len = end - start;
2225 bool need_block_size_reset = false;
2226- string ext;
2227 ChanCount const max_proc = track.max_processor_streams ();
2228
2229 if (end <= start) {
2230@@ -4155,29 +4142,22 @@
2231 goto out;
2232 }
2233
2234- ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
2235-
2236 for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) {
2237
2238- for (x = 0; x < 99999; ++x) {
2239- snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 "%s", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1, ext.c_str());
2240- if (!Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) {
2241- break;
2242- }
2243- }
2244-
2245- if (x == 99999) {
2246- error << string_compose (_("too many bounced versions of playlist \"%1\""), playlist->name()) << endmsg;
2247+ string base_name = string_compose ("%1-%2-bounce", playlist->name(), chan_n);
2248+ string path = new_audio_source_path (base_name, diskstream_channels.n_audio(), chan_n, false, true);
2249+
2250+ if (path.empty()) {
2251 goto out;
2252 }
2253
2254 try {
2255 fsource = boost::dynamic_pointer_cast<AudioFileSource> (
2256- SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate()));
2257+ SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate()));
2258 }
2259
2260 catch (failed_constructor& err) {
2261- error << string_compose (_("cannot create new audio file \"%1\" for %2"), buf, track.name()) << endmsg;
2262+ error << string_compose (_("cannot create new audio file \"%1\" for %2"), path, track.name()) << endmsg;
2263 goto out;
2264 }
2265
2266
2267=== modified file 'libs/ardour/session_state.cc'
2268--- libs/ardour/session_state.cc 2014-02-25 14:13:18 +0000
2269+++ libs/ardour/session_state.cc 2015-05-02 14:25:06 +0000
2270@@ -74,6 +74,7 @@
2271 #include "pbd/stacktrace.h"
2272 #include "pbd/convert.h"
2273 #include "pbd/clear_dir.h"
2274+#include "pbd/strsplit.h"
2275
2276 #include "ardour/amp.h"
2277 #include "ardour/audio_diskstream.h"
2278@@ -1830,47 +1831,15 @@
2279 return *node;
2280 }
2281
2282-string
2283-Session::path_from_region_name (DataType type, string name, string identifier)
2284-{
2285- char buf[PATH_MAX+1];
2286- uint32_t n;
2287- SessionDirectory sdir(get_best_session_directory_for_new_source());
2288- std::string source_dir = ((type == DataType::AUDIO)
2289- ? sdir.sound_path() : sdir.midi_path());
2290-
2291- string ext = native_header_format_extension (config.get_native_file_header_format(), type);
2292-
2293- for (n = 0; n < 999999; ++n) {
2294- if (identifier.length()) {
2295- snprintf (buf, sizeof(buf), "%s%s%" PRIu32 "%s", name.c_str(),
2296- identifier.c_str(), n, ext.c_str());
2297- } else {
2298- snprintf (buf, sizeof(buf), "%s-%" PRIu32 "%s", name.c_str(),
2299- n, ext.c_str());
2300- }
2301-
2302- std::string source_path = Glib::build_filename (source_dir, buf);
2303-
2304- if (!Glib::file_test (source_path, Glib::FILE_TEST_EXISTS)) {
2305- return source_path;
2306- }
2307- }
2308-
2309- error << string_compose (_("cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"),
2310- name, identifier)
2311- << endmsg;
2312-
2313- return "";
2314-}
2315-
2316-
2317 int
2318 Session::load_sources (const XMLNode& node)
2319 {
2320 XMLNodeList nlist;
2321 XMLNodeConstIterator niter;
2322- boost::shared_ptr<Source> source;
2323+ boost::shared_ptr<Source> source; /* don't need this but it stops some
2324+ * versions of gcc complaining about
2325+ * discarded return values.
2326+ */
2327
2328 nlist = node.children();
2329
2330@@ -1887,9 +1856,15 @@
2331
2332 int user_choice;
2333
2334+ if (err.type == DataType::MIDI && Glib::path_is_absolute (err.path)) {
2335+ error << string_compose (_("A external MIDI file is missing. %1 cannot currently recover from missing external MIDI files"),
2336+ PROGRAM_NAME) << endmsg;
2337+ return -1;
2338+ }
2339+
2340 if (!no_questions_about_missing_files) {
2341- user_choice = MissingFile (this, err.path, err.type).get_value_or (-1);
2342- } else {
2343+ user_choice = MissingFile (this, err.path, err.type).get_value_or (-1);
2344+ } else {
2345 user_choice = -2;
2346 }
2347
2348@@ -1914,8 +1889,41 @@
2349
2350 case -1:
2351 default:
2352- warning << _("A sound file is missing. It will be replaced by silence.") << endmsg;
2353- source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate);
2354+ switch (err.type) {
2355+
2356+ case DataType::AUDIO:
2357+ source = SourceFactory::createSilent (*this, **niter, max_framecnt, _current_frame_rate);
2358+ break;
2359+
2360+ case DataType::MIDI:
2361+ /* The MIDI file is actually missing so
2362+ * just create a new one in the same
2363+ * location. Do not announce its
2364+ */
2365+ string fullpath;
2366+
2367+ if (!Glib::path_is_absolute (err.path)) {
2368+ vector<Glib::ustring> sdirs;
2369+ split (source_search_path (DataType::MIDI), sdirs, ':');
2370+ if (sdirs.empty()) {
2371+ fatal << _("Empty MIDI source search path!") << endmsg;
2372+ /*NOTREACHED*/
2373+ }
2374+ fullpath = Glib::build_filename (sdirs.front(), err.path);
2375+ } else {
2376+ /* this should be an unrecoverable error: we would be creating a MIDI file outside
2377+ the session tree.
2378+ */
2379+ return -1;
2380+ }
2381+ /* Note that we do not announce the source just yet - we need to reset its ID before we do that */
2382+ source = SourceFactory::createWritable (DataType::MIDI, *this, fullpath, false, _current_frame_rate, false, false);
2383+ /* reset ID to match the missing one */
2384+ source->set_id (**niter);
2385+ /* Now we can announce it */
2386+ SourceFactory::SourceCreated (source);
2387+ break;
2388+ }
2389 break;
2390 }
2391 }
2392@@ -2053,7 +2061,7 @@
2393 }
2394
2395 string
2396-Session::get_best_session_directory_for_new_source ()
2397+Session::get_best_session_directory_for_new_audio ()
2398 {
2399 vector<space_and_path>::iterator i;
2400 string result = _session_dir->root_path();
2401
2402=== modified file 'libs/ardour/smf_source.cc'
2403--- libs/ardour/smf_source.cc 2014-05-15 14:33:48 +0000
2404+++ libs/ardour/smf_source.cc 2015-05-02 14:25:06 +0000
2405@@ -71,6 +71,8 @@
2406 assert (!Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
2407 existence_check ();
2408
2409+ _flags = Source::Flag (_flags | Empty);
2410+
2411 /* file is not opened until write */
2412
2413 if (flags & Writable) {
2414@@ -97,16 +99,15 @@
2415 {
2416 /* note that origin remains empty */
2417
2418- if (init (_path, false)) {
2419+ if (init (_path, true)) {
2420 throw failed_constructor ();
2421 }
2422
2423 assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
2424 existence_check ();
2425
2426- /* file is not opened until write */
2427-
2428 if (_flags & Writable) {
2429+ /* file is not opened until write */
2430 return;
2431 }
2432
2433@@ -131,12 +132,50 @@
2434 throw failed_constructor ();
2435 }
2436
2437- if (init (_path, true)) {
2438- throw failed_constructor ();
2439- }
2440-
2441- assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
2442- existence_check ();
2443+ /* we expect the file to exist, but if no MIDI data was ever added
2444+ it will have been removed at last session close. so, we don't
2445+ require it to exist if it was marked Empty.
2446+ */
2447+
2448+ try {
2449+
2450+ if (init (_path, true)) {
2451+ throw failed_constructor ();
2452+ }
2453+
2454+ } catch (MissingSource& err) {
2455+
2456+ if (_flags & Source::Empty) {
2457+ /* we don't care that the file was not found, because
2458+ it was empty. But FileSource::init() will have
2459+ failed to set our _path correctly, so we have to do
2460+ this ourselves. Use the first entry in the search
2461+ path for MIDI files, which is assumed to be the
2462+ correct "main" location.
2463+ */
2464+ std::vector<Glib::ustring> sdirs;
2465+ split (s.source_search_path (DataType::MIDI), sdirs, ':');
2466+ if (sdirs.empty()) {
2467+ fatal << _("Empty MIDI source search path!") << endmsg;
2468+ /*NOTREACHED*/
2469+ }
2470+ _path = Glib::build_filename (sdirs.front(), _path);
2471+ /* This might be important, too */
2472+ _file_is_new = true;
2473+ } else {
2474+ /* pass it on */
2475+ throw;
2476+ }
2477+ }
2478+
2479+ if (!(_flags & Source::Empty)) {
2480+ assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
2481+ existence_check ();
2482+ } else {
2483+ assert (_flags & Source::Writable);
2484+ /* file will be opened on write */
2485+ return;
2486+ }
2487
2488 if (open(_path)) {
2489 throw failed_constructor ();
2490@@ -354,10 +393,22 @@
2491 name().c_str(), ev.id(), ev.time(), ev.size());
2492 for (size_t i = 0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");*/
2493
2494- if (ev.time() < _last_ev_time_beats) {
2495- warning << string_compose(_("Skipping event with unordered time %1"), ev.time())
2496- << endmsg;
2497- return;
2498+ double time = ev.time();
2499+ if (time < _last_ev_time_beats) {
2500+ const double difference = _last_ev_time_beats - time;
2501+ if (difference / (double)ppqn() < 1.0) {
2502+ /* Close enough. This problem occurs because Sequence is not
2503+ actually ordered due to fuzzy time comparison. I'm pretty sure
2504+ this is inherently a bad idea which causes problems all over the
2505+ place, but tolerate it here for now anyway. */
2506+ time = _last_ev_time_beats;
2507+ } else {
2508+ /* Out of order by more than a tick. */
2509+ warning << string_compose(_("Skipping event with unordered beat time %1 < %2 (off by %3 beats, %4 ticks)"),
2510+ ev.time(), _last_ev_time_beats, difference, difference / (double)ppqn())
2511+ << endmsg;
2512+ return;
2513+ }
2514 }
2515
2516 Evoral::event_id_t event_id;
2517@@ -372,13 +423,14 @@
2518 _model->append (ev, event_id);
2519 }
2520
2521- _length_beats = max(_length_beats, ev.time());
2522+ _length_beats = max(_length_beats, time);
2523
2524- const double delta_time_beats = ev.time() - _last_ev_time_beats;
2525+ const double delta_time_beats = time - _last_ev_time_beats;
2526 const uint32_t delta_time_ticks = (uint32_t)lrint(delta_time_beats * (double)ppqn());
2527
2528 Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
2529- _last_ev_time_beats = ev.time();
2530+ _last_ev_time_beats = time;
2531+ _flags = Source::Flag (_flags & ~Empty);
2532 }
2533
2534 /** Append an event with a timestamp in frames (framepos_t) */
2535@@ -394,7 +446,8 @@
2536 // for (size_t i=0; i < ev.size(); ++i) printf("%X ", ev.buffer()[i]); printf("\n");
2537
2538 if (ev.time() < _last_ev_time_frames) {
2539- warning << string_compose(_("Skipping event with unordered time %1"), ev.time())
2540+ warning << string_compose(_("Skipping event with unordered frame time %1 < %2"),
2541+ ev.time(), _last_ev_time_frames)
2542 << endmsg;
2543 return;
2544 }
2545@@ -425,6 +478,7 @@
2546
2547 Evoral::SMF::append_event_delta(delta_time_ticks, ev.size(), ev.buffer(), event_id);
2548 _last_ev_time_frames = ev.time();
2549+ _flags = Source::Flag (_flags & ~Empty);
2550 }
2551
2552 XMLNode&
2553@@ -667,10 +721,12 @@
2554 void
2555 SMFSource::flush_midi ()
2556 {
2557- if (!writable() || (writable() && !_open)) {
2558+ if (!writable() || _length_beats == 0.0) {
2559 return;
2560 }
2561
2562+ ensure_disk_file ();
2563+
2564 Evoral::SMF::end_write ();
2565 /* data in the file means its no longer removable */
2566 mark_nonremovable ();
2567@@ -687,6 +743,10 @@
2568 void
2569 SMFSource::ensure_disk_file ()
2570 {
2571+ if (!writable()) {
2572+ return;
2573+ }
2574+
2575 if (_model) {
2576 /* We have a model, so write it to disk; see MidiSource::session_saved
2577 for an explanation of what we are doing here.
2578@@ -702,9 +762,6 @@
2579 if (!_open) {
2580 open_for_write ();
2581 }
2582-
2583- /* Flush, which will definitely put something on disk */
2584- flush_midi ();
2585 }
2586 }
2587
2588@@ -718,34 +775,3 @@
2589 _flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));
2590 }
2591
2592-int
2593-SMFSource::rename (const string& newname)
2594-{
2595- Glib::Threads::Mutex::Lock lm (_lock);
2596- string oldpath = _path;
2597- string newpath = _session.new_source_path_from_name (DataType::MIDI, newname);
2598-
2599- if (newpath.empty()) {
2600- error << string_compose (_("programming error: %1"), "cannot generate a changed file path") << endmsg;
2601- return -1;
2602- }
2603-
2604- // Test whether newpath exists, if yes notify the user but continue.
2605- if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
2606- error << string_compose (_("Programming error! %1 tried to rename a file over another file! It's safe to continue working, but please report this to the developers."), PROGRAM_NAME) << endmsg;
2607- return -1;
2608- }
2609-
2610- if (Glib::file_test (oldpath.c_str(), Glib::FILE_TEST_EXISTS)) {
2611- /* rename only needed if file exists on disk */
2612- if (::rename (oldpath.c_str(), newpath.c_str()) != 0) {
2613- error << string_compose (_("cannot rename file %1 to %2 (%3)"), oldpath, newpath, strerror(errno)) << endmsg;
2614- return -1;
2615- }
2616- }
2617-
2618- _name = Glib::path_get_basename (newpath);
2619- _path = newpath;
2620-
2621- return 0;
2622-}
2623
2624=== modified file 'libs/ardour/sndfilesource.cc'
2625--- libs/ardour/sndfilesource.cc 2014-05-15 14:33:48 +0000
2626+++ libs/ardour/sndfilesource.cc 2015-05-02 14:25:06 +0000
2627@@ -183,6 +183,34 @@
2628 }
2629 }
2630
2631+/** Constructor to be called for recovering files being used for
2632+ * capture. They are in-session, they already exist, they should not
2633+ * be writable. They are an odd hybrid (from a constructor point of
2634+ * view) of the previous two constructors.
2635+ */
2636+SndFileSource::SndFileSource (Session& s, const string& path, int chn)
2637+ : Source (s, DataType::AUDIO, path, Flag (0))
2638+ /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
2639+ , AudioFileSource (s, path, Flag (0))
2640+ , _descriptor (0)
2641+ , _broadcast_info (0)
2642+ , _capture_start (false)
2643+ , _capture_end (false)
2644+ , file_pos (0)
2645+ , xfade_buf (0)
2646+{
2647+ _channel = chn;
2648+
2649+ init_sndfile ();
2650+
2651+ assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
2652+ existence_check ();
2653+
2654+ if (open()) {
2655+ throw failed_constructor ();
2656+ }
2657+}
2658+
2659 void
2660 SndFileSource::init_sndfile ()
2661 {
2662@@ -256,6 +284,14 @@
2663 delete _broadcast_info;
2664 _broadcast_info = 0;
2665 _flags = Flag (_flags & ~Broadcast);
2666+ }
2667+
2668+ /* Set the broadcast flag if the BWF info is already there. We need
2669+ * this when recovering or using existing files.
2670+ */
2671+
2672+ if (bwf_info_exists) {
2673+ _flags = Flag (_flags | Broadcast);
2674 }
2675
2676 if (writable()) {
2677
2678=== modified file 'libs/ardour/source_factory.cc'
2679--- libs/ardour/source_factory.cc 2014-05-15 14:33:48 +0000
2680+++ libs/ardour/source_factory.cc 2015-05-02 14:25:06 +0000
2681@@ -342,6 +342,39 @@
2682 }
2683
2684 boost::shared_ptr<Source>
2685+SourceFactory::createForRecovery (DataType type, Session& s, const std::string& path, int chn)
2686+{
2687+ /* this might throw failed_constructor(), which is OK */
2688+
2689+ if (type == DataType::AUDIO) {
2690+ Source* src = new SndFileSource (s, path, chn);
2691+
2692+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
2693+ // boost_debug_shared_ptr_mark_interesting (src, "Source");
2694+#endif
2695+ boost::shared_ptr<Source> ret (src);
2696+
2697+ if (setup_peakfile (ret, false)) {
2698+ return boost::shared_ptr<Source>();
2699+ }
2700+
2701+ // no analysis data - this is still basically a new file (we
2702+ // crashed while recording.
2703+
2704+ // always announce these files
2705+
2706+ SourceCreated (ret);
2707+
2708+ return ret;
2709+
2710+ } else if (type == DataType::MIDI) {
2711+ error << _("Recovery attempted on a MIDI file - not implemented") << endmsg;
2712+ }
2713+
2714+ return boost::shared_ptr<Source> ();
2715+}
2716+
2717+boost::shared_ptr<Source>
2718 SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr<Playlist> p, const PBD::ID& orig, const std::string& name,
2719 uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks)
2720 {
2721
2722=== modified file 'libs/ardour/tempo.cc'
2723--- libs/ardour/tempo.cc 2013-09-21 19:05:02 +0000
2724+++ libs/ardour/tempo.cc 2015-05-02 14:25:06 +0000
2725@@ -884,6 +884,7 @@
2726 TempoSection* ts;
2727 MeterSection* ms;
2728 double beat_frames;
2729+ double current_frame_exact;
2730 framepos_t bar_start_frame;
2731
2732 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
2733@@ -895,11 +896,13 @@
2734 }
2735
2736 beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
2737+ current_frame_exact = current_frame;
2738
2739 while (current_frame < end) {
2740
2741 current.beats++;
2742- current_frame += beat_frames;
2743+ current_frame_exact += beat_frames;
2744+ current_frame = llrint(current_frame_exact);
2745
2746 if (current.beats > meter->divisions_per_bar()) {
2747 current.bars++;
2748@@ -942,7 +945,8 @@
2749 tempo->start(), current_frame, tempo->bar_offset()));
2750
2751 /* back up to previous beat */
2752- current_frame -= beat_frames;
2753+ current_frame_exact -= beat_frames;
2754+ current_frame = llrint(current_frame_exact);
2755
2756 /* set tempo section location
2757 * based on offset from last
2758@@ -963,7 +967,8 @@
2759
2760 double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
2761
2762- current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
2763+ current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
2764+ current_frame = llrint(current_frame_exact);
2765
2766 /* next metric doesn't have to
2767 * match this precisely to
2768@@ -1012,11 +1017,11 @@
2769
2770 if (current.beats == 1) {
2771 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
2772- _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
2773+ _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
2774 bar_start_frame = current_frame;
2775 } else {
2776 DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
2777- _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
2778+ _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
2779 }
2780
2781 if (next_metric == metrics.end()) {
2782
2783=== modified file 'libs/midi++2/midnam_patch.cc'
2784--- libs/midi++2/midnam_patch.cc 2014-01-24 16:31:54 +0000
2785+++ libs/midi++2/midnam_patch.cc 2015-05-02 14:25:06 +0000
2786@@ -724,14 +724,13 @@
2787 return "";
2788 }
2789
2790+ boost::shared_ptr<const NoteNameList> note_names;
2791 boost::shared_ptr<const Patch> patch(
2792 find_patch(mode_name, channel, PatchPrimaryKey(program, bank)));
2793- if (!patch) {
2794- return "";
2795+ if (patch) {
2796+ note_names = note_name_list(patch->note_list_name());
2797 }
2798
2799- boost::shared_ptr<const NoteNameList> note_names(
2800- note_name_list(patch->note_list_name()));
2801 if (!note_names) {
2802 /* No note names specific to this patch, check the ChannelNameSet */
2803 boost::shared_ptr<ChannelNameSet> chan_names = channel_name_set_by_channel(
2804
2805=== added file 'patchfiles/Korg_Volca_Bass.midnam'
2806--- patchfiles/Korg_Volca_Bass.midnam 1970-01-01 00:00:00 +0000
2807+++ patchfiles/Korg_Volca_Bass.midnam 2015-05-02 14:25:06 +0000
2808@@ -0,0 +1,37 @@
2809+<?xml version="1.0" encoding="UTF-8"?>
2810+<!DOCTYPE MIDINameDocument PUBLIC "-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN" "http://www.midi.org/dtds/MIDINameDocument10.dtd">
2811+<MIDINameDocument>
2812+ <Author>David Robillard</Author>
2813+ <MasterDeviceNames>
2814+ <Manufacturer>Korg</Manufacturer>
2815+ <Model>Volca Bass</Model>
2816+ <CustomDeviceMode Name="Default">
2817+ <ChannelNameSetAssignments>
2818+ <ChannelNameSetAssign Channel="1" NameSet="Names"/>
2819+ </ChannelNameSetAssignments>
2820+ </CustomDeviceMode>
2821+ <ChannelNameSet Name="Names">
2822+ <AvailableForChannels>
2823+ <AvailableChannel Channel="1" Available="true"/>
2824+ </AvailableForChannels>
2825+ <UsesControlNameList Name="Controls"/>
2826+ <PatchBank Name="User Patches">
2827+ <PatchNameList Name="User Patches"/>
2828+ </PatchBank>
2829+ </ChannelNameSet>
2830+ <ControlNameList Name="Controls">
2831+ <Control Type="7bit" Number="5" Name="Slide Time"/>
2832+ <Control Type="7bit" Number="11" Name="Expression"/>
2833+ <Control Type="7bit" Number="40" Name="Octave"/>
2834+ <Control Type="7bit" Number="41" Name="LFO Rate"/>
2835+ <Control Type="7bit" Number="42" Name="LFO Int"/>
2836+ <Control Type="7bit" Number="43" Name="VCO Pitch 1"/>
2837+ <Control Type="7bit" Number="44" Name="VCO Pitch 2"/>
2838+ <Control Type="7bit" Number="45" Name="VCO Pitch 3"/>
2839+ <Control Type="7bit" Number="46" Name="EG Attack"/>
2840+ <Control Type="7bit" Number="47" Name="EG Decay/Release"/>
2841+ <Control Type="7bit" Number="48" Name="Cutoff EG Int"/>
2842+ <Control Type="7bit" Number="49" Name="Gate Time"/>
2843+ </ControlNameList>
2844+ </MasterDeviceNames>
2845+</MIDINameDocument>
2846
2847=== added file 'patchfiles/Korg_Volca_Beats.midnam'
2848--- patchfiles/Korg_Volca_Beats.midnam 1970-01-01 00:00:00 +0000
2849+++ patchfiles/Korg_Volca_Beats.midnam 2015-05-02 14:25:06 +0000
2850@@ -0,0 +1,58 @@
2851+<?xml version="1.0" encoding="UTF-8"?>
2852+<!DOCTYPE MIDINameDocument PUBLIC "-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN" "http://www.midi.org/dtds/MIDINameDocument10.dtd">
2853+<MIDINameDocument>
2854+ <Author>David Robillard</Author>
2855+ <MasterDeviceNames>
2856+ <Manufacturer>Korg</Manufacturer>
2857+ <Model>Volca Beats</Model>
2858+ <CustomDeviceMode Name="Default">
2859+ <ChannelNameSetAssignments>
2860+ <ChannelNameSetAssign Channel="10" NameSet="Names"/>
2861+ </ChannelNameSetAssignments>
2862+ </CustomDeviceMode>
2863+ <ChannelNameSet Name="Names">
2864+ <AvailableForChannels>
2865+ <AvailableChannel Channel="10" Available="true"/>
2866+ </AvailableForChannels>
2867+ <UsesNoteNameList Name="Notes"/>
2868+ <UsesControlNameList Name="Controls"/>
2869+ <PatchBank Name="User Patches">
2870+ <PatchNameList Name="User Patches"/>
2871+ </PatchBank>
2872+ </ChannelNameSet>
2873+ <NoteNameList Name="Notes">
2874+ <Note Number="37" Name="Kick"/>
2875+ <Note Number="39" Name="Snare"/>
2876+ <Note Number="40" Name="Clap"/>
2877+ <Note Number="43" Name="Closed Hi-hat"/>
2878+ <Note Number="44" Name="Low Tom"/>
2879+ <Note Number="47" Name="Open Hi-hat"/>
2880+ <Note Number="50" Name="Crash Cymbal"/>
2881+ <Note Number="51" Name="High Tom"/>
2882+ <Note Number="68" Name="AgogĂ´"/>
2883+ <Note Number="76" Name="Claves"/>
2884+ </NoteNameList>
2885+ <ControlNameList Name="Controls">
2886+ <Control Type="7bit" Number="40" Name="Kick Level"/>
2887+ <Control Type="7bit" Number="41" Name="Snare Level"/>
2888+ <Control Type="7bit" Number="42" Name="Low Tom Level"/>
2889+ <Control Type="7bit" Number="43" Name="High Tom Level"/>
2890+ <Control Type="7bit" Number="44" Name="Closed Hat Level"/>
2891+ <Control Type="7bit" Number="45" Name="Open Hat Level"/>
2892+ <Control Type="7bit" Number="46" Name="Clap Level"/>
2893+ <Control Type="7bit" Number="47" Name="Claves Level"/>
2894+ <Control Type="7bit" Number="48" Name="Agogo Level"/>
2895+ <Control Type="7bit" Number="49" Name="Crash Level"/>
2896+ <Control Type="7bit" Number="50" Name="Clap PCM Speed"/>
2897+ <Control Type="7bit" Number="51" Name="Claves PCM Speed"/>
2898+ <Control Type="7bit" Number="52" Name="Agogo PCM Speed"/>
2899+ <Control Type="7bit" Number="53" Name="Crash PCM Speed"/>
2900+ <Control Type="7bit" Number="54" Name="Stutter Time"/>
2901+ <Control Type="7bit" Number="55" Name="Stutter Depth"/>
2902+ <Control Type="7bit" Number="56" Name="Tom Decay"/>
2903+ <Control Type="7bit" Number="57" Name="Closed Hat Decay"/>
2904+ <Control Type="7bit" Number="58" Name="Open Hat Decay"/>
2905+ <Control Type="7bit" Number="59" Name="Hat Grain"/>
2906+ </ControlNameList>
2907+ </MasterDeviceNames>
2908+</MIDINameDocument>
2909
2910=== added file 'patchfiles/Korg_Volca_Keys.midnam'
2911--- patchfiles/Korg_Volca_Keys.midnam 1970-01-01 00:00:00 +0000
2912+++ patchfiles/Korg_Volca_Keys.midnam 2015-05-02 14:25:06 +0000
2913@@ -0,0 +1,41 @@
2914+<?xml version="1.0" encoding="UTF-8"?>
2915+<!DOCTYPE MIDINameDocument PUBLIC "-//MIDI Manufacturers Association//DTD MIDINameDocument 1.0//EN" "http://www.midi.org/dtds/MIDINameDocument10.dtd">
2916+<MIDINameDocument>
2917+ <Author>David Robillard</Author>
2918+ <MasterDeviceNames>
2919+ <Manufacturer>Korg</Manufacturer>
2920+ <Model>Volca Keys</Model>
2921+ <CustomDeviceMode Name="Default">
2922+ <ChannelNameSetAssignments>
2923+ <ChannelNameSetAssign Channel="1" NameSet="Names"/>
2924+ </ChannelNameSetAssignments>
2925+ </CustomDeviceMode>
2926+ <ChannelNameSet Name="Names">
2927+ <AvailableForChannels>
2928+ <AvailableChannel Channel="1" Available="true"/>
2929+ </AvailableForChannels>
2930+ <UsesControlNameList Name="Controls"/>
2931+ <PatchBank Name="User Patches">
2932+ <PatchNameList Name="User Patches"/>
2933+ </PatchBank>
2934+ </ChannelNameSet>
2935+ <ControlNameList Name="Controls">
2936+ <Control Type="7bit" Number="5" Name="Portamento"/>
2937+ <Control Type="7bit" Number="11" Name="Expression"/>
2938+ <Control Type="7bit" Number="40" Name="Voice"/>
2939+ <Control Type="7bit" Number="41" Name="Octave"/>
2940+ <Control Type="7bit" Number="42" Name="Detune"/>
2941+ <Control Type="7bit" Number="43" Name="VCO EG Int"/>
2942+ <Control Type="7bit" Number="44" Name="Cutoff"/>
2943+ <Control Type="7bit" Number="45" Name="VCF EG Int"/>
2944+ <Control Type="7bit" Number="46" Name="LFO Rate"/>
2945+ <Control Type="7bit" Number="47" Name="LFO Pitch Int"/>
2946+ <Control Type="7bit" Number="48" Name="LFO Cutoff Int"/>
2947+ <Control Type="7bit" Number="49" Name="EG Attack"/>
2948+ <Control Type="7bit" Number="50" Name="EG Decay/Release"/>
2949+ <Control Type="7bit" Number="51" Name="EG Sustain"/>
2950+ <Control Type="7bit" Number="52" Name="Delay Time"/>
2951+ <Control Type="7bit" Number="53" Name="Delay Feedback"/>
2952+ </ControlNameList>
2953+ </MasterDeviceNames>
2954+</MIDINameDocument>

Subscribers

People subscribed via source and target branches

to all changes: