/*
- Copyright (C) 2000-2006 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
+ * Copyright (C) 2006-2014 David Robillard <d@drobilla.net>
+ * Copyright (C) 2006-2017 Paul Davis <paul@linuxaudiosystems.com>
+ * Copyright (C) 2006 Taybin Rutkin <taybin@taybin.com>
+ * Copyright (C) 2007 Tim Mayberry <mojofunk@gmail.com>
+ * Copyright (C) 2009-2012 Carl Hetherington <carl@carlh.net>
+ * Copyright (C) 2012-2019 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013-2014 Colin Fletcher <colin.m.fletcher@googlemail.com>
+ * Copyright (C) 2015-2017 Nick Mainsbridge <mainsbridge@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
#include <sys/types.h>
#include <sys/stat.h>
#include "pbd/shortpath.h"
#include "pbd/stateful_diff_command.h"
-#include <gtkmm2ext/choice.h>
+#include "widgets/choice.h"
#include "ardour/audio_track.h"
#include "ardour/audiofilesource.h"
#include "ardour/midi_region.h"
#include "ardour/midi_track.h"
#include "ardour/operations.h"
+#include "ardour/profile.h"
#include "ardour/region_factory.h"
#include "ardour/smf_source.h"
#include "ardour/source_factory.h"
#include "ardour/utils.h"
#include "pbd/memento_command.h"
+#include "ardour_message.h"
#include "ardour_ui.h"
#include "cursor_context.h"
#include "editor.h"
#include "mouse_cursors.h"
#include "editor_cursors.h"
-#include "i18n.h"
+#include "pbd/i18n.h"
using namespace std;
using namespace ARDOUR;
Editor::add_external_audio_action (ImportMode mode_hint)
{
if (_session == 0) {
- MessageDialog msg (_("You can't import or embed an audiofile until you have a session loaded."));
+ ArdourMessageDialog msg (_("You can't import or embed an audiofile until you have a session loaded."));
msg.run ();
return;
}
if (sfbrowser == 0) {
- sfbrowser = new SoundFileOmega (_("Add Existing Media"), _session, 0, true, mode_hint);
+ sfbrowser = new SoundFileOmega (_("Add Existing Media"), _session, 0, 0, true, mode_hint);
} else {
sfbrowser->set_mode (mode_hint);
}
uint32_t midi_track_cnt;
if (_session == 0) {
- MessageDialog msg (_("You can't import or embed an audiofile until you have a session loaded."));
+ ArdourMessageDialog msg (_("You can't import or embed an audiofile until you have a session loaded."));
msg.run ();
return;
}
if (atv) {
if (atv->is_audio_track()) {
audio_track_cnt++;
- }
+ }
} else {
MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(*x);
Editor::session_import_dialog ()
{
SessionImportDialog dialog (_session);
- ensure_float (dialog);
dialog.run ();
}
message = string_compose (_("The session already contains a source file named %1. Do you want to import %2 as a new source, or skip it?"), wave_name, wave_name);
}
- MessageDialog dialog(message, false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, true);
+ ArdourMessageDialog dialog(message, false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, true);
if (all_or_nothing) {
// disabled
//dialog.add_button("Skip all", 4); // All or rest?
- dialog.show();
-
function = dialog.run ();
-
dialog.hide();
}
}
void
-Editor::do_import (vector<string> paths,
- ImportDisposition disposition,
- ImportMode mode,
- SrcQuality quality,
- framepos_t& pos,
- ARDOUR::PluginInfoPtr instrument)
+Editor::import_smf_tempo_map (Evoral::SMF const & smf, samplepos_t pos)
+{
+ if (!_session) {
+ return;
+ }
+
+ const size_t num_tempos = smf.num_tempos ();
+
+ if (num_tempos == 0) {
+ return;
+ }
+
+ const samplecnt_t sample_rate = _session->sample_rate ();
+ TempoMap new_map (sample_rate);
+ Meter last_meter (4.0, 4.0);
+ bool have_initial_meter = false;
+
+ for (size_t n = 0; n < num_tempos; ++n) {
+
+ Evoral::SMF::Tempo* t = smf.nth_tempo (n);
+ assert (t);
+
+ Tempo tempo (t->tempo(), 32.0 / (double) t->notes_per_note);
+ Meter meter (t->numerator, t->denominator);
+ Timecode::BBT_Time bbt; /* 1|1|0 which is correct for the no-meter case */
+
+ if (have_initial_meter) {
+ new_map.add_tempo (tempo, t->time_pulses/ (double)smf.ppqn() / 4.0, 0, MusicTime);
+ if (!(meter == last_meter)) {
+ bbt = new_map.bbt_at_quarter_note (t->time_pulses/(double)smf.ppqn());
+ new_map.add_meter (meter, bbt, 0, MusicTime);
+ }
+
+ } else {
+ new_map.replace_meter (new_map.meter_section_at_sample (0), meter, bbt, pos, AudioTime);
+ new_map.replace_tempo (new_map.tempo_section_at_sample (0), tempo, 0.0, pos, AudioTime);
+ have_initial_meter = true;
+
+ }
+
+ last_meter = meter;
+
+ cerr << "@ " << t->time_pulses/(double)smf.ppqn() << " ("
+ << t->time_seconds << ") Add T " << tempo << " M " << meter << endl;
+ }
+
+ cerr << "NEW MAP:\n";
+ new_map.dump (cerr);
+
+ _session->tempo_map() = new_map;
+}
+
+void
+Editor::do_import (vector<string> paths,
+ ImportDisposition disposition,
+ ImportMode mode,
+ SrcQuality quality,
+ MidiTrackNameSource midi_track_name_source,
+ MidiTempoMapDisposition smf_tempo_disposition,
+ samplepos_t& pos,
+ ARDOUR::PluginInfoPtr instrument)
{
boost::shared_ptr<Track> track;
vector<string> to_import;
int nth = 0;
bool use_timestamp = (pos == -1);
+ if (smf_tempo_disposition == SMFTempoUse) {
+ /* Find the first MIDI file with a tempo map, and import it
+ before we do anything else.
+ */
+
+ for (vector<string>::iterator a = paths.begin(); a != paths.end(); ++a) {
+ Evoral::SMF smf;
+ if (smf.open (*a)) {
+ continue;
+ }
+ if (smf.num_tempos() > 0) {
+ import_smf_tempo_map (smf, pos);
+ smf.close ();
+ break;
+ }
+ smf.close ();
+ }
+ }
+
current_interthread_info = &import_status;
import_status.current = 1;
import_status.total = paths.size ();
import_status.all_done = false;
+ import_status.midi_track_name_source = midi_track_name_source;
ImportProgressWindow ipw (&import_status, _("Import"), _("Cancel Import"));
} else {
ipw.show ();
ok = (import_sndfiles (paths, disposition, mode, quality, pos, 1, 1, track, false, instrument) == 0);
+ import_status.sources.clear();
}
} else {
}
ok = (import_sndfiles (to_import, disposition, mode, quality, pos, 1, -1, track, replace, instrument) == 0);
+ import_status.sources.clear();
break;
case Editing::ImportDistinctChannels:
to_import.push_back (*a);
ok = (import_sndfiles (to_import, disposition, mode, quality, pos, -1, -1, track, replace, instrument) == 0);
+ import_status.sources.clear();
break;
case Editing::ImportSerializeFiles:
to_import.push_back (*a);
ok = (import_sndfiles (to_import, disposition, mode, quality, pos, 1, 1, track, replace, instrument) == 0);
+ import_status.sources.clear();
break;
case Editing::ImportMergeFiles:
}
void
-Editor::do_embed (vector<string> paths, ImportDisposition import_as, ImportMode mode, framepos_t& pos, ARDOUR::PluginInfoPtr instrument)
+Editor::do_embed (vector<string> paths, ImportDisposition import_as, ImportMode mode, samplepos_t& pos, ARDOUR::PluginInfoPtr instrument)
{
boost::shared_ptr<Track> track;
bool check_sample_rate = true;
ImportDisposition disposition,
ImportMode mode,
SrcQuality quality,
- framepos_t& pos,
+ samplepos_t& pos,
int target_regions,
int target_tracks,
boost::shared_ptr<Track>& track,
import_status.freeze = false;
import_status.quality = quality;
import_status.replace_existing_source = replace;
+ import_status.split_midi_channels = (disposition == Editing::ImportDistinctChannels);
import_status.mode = mode;
import_status.pos = pos;
gtk_main_iteration ();
}
- import_status.done = true;
+ // wait for thread to terminate
+ while (!import_status.done) {
+ gtk_main_iteration ();
+ }
int result = -1;
pos = import_status.pos;
}
- import_status.sources.clear();
return result;
}
bool& check_sample_rate,
ImportDisposition disposition,
ImportMode mode,
- framepos_t& pos,
+ samplepos_t& pos,
int target_regions,
int target_tracks,
boost::shared_ptr<Track>& track,
SoundFileInfo finfo;
CursorContext::Handle cursor_ctx = CursorContext::create(*this, _cursors->wait);
- gdk_flush ();
+ gdk_flush ();
for (vector<string>::iterator p = paths.begin(); p != paths.end(); ++p) {
return -3;
}
- if (check_sample_rate && (finfo.samplerate != (int) _session->frame_rate())) {
+ if (!finfo.seekable) {
+ ArdourMessageDialog msg (string_compose (_("%1\nThis audiofile cannot be embedded. It must be imported!"), short_path (path, 40)), false, Gtk::MESSAGE_ERROR);
+ msg.run ();
+ return -2;
+ }
+
+ if (check_sample_rate && (finfo.samplerate != (int) _session->sample_rate())) {
vector<string> choices;
if (multifile) {
choices.push_back (_("Don't embed it"));
choices.push_back (_("Embed all without questions"));
- Gtkmm2ext::Choice rate_choice (
+ ArdourWidgets::Choice rate_choice (
_("Sample rate"),
string_compose (_("%1\nThis audiofile's sample rate doesn't match the session sample rate!"),
short_path (path, 40)),
choices.push_back (_("Cancel"));
choices.push_back (_("Embed it anyway"));
- Gtkmm2ext::Choice rate_choice (
+ ArdourWidgets::Choice rate_choice (
_("Sample rate"),
string_compose (_("%1\nThis audiofile's sample rate doesn't match the session sample rate!"), path),
choices, false
source = boost::dynamic_pointer_cast<AudioFileSource> (
SourceFactory::createExternal (DataType::AUDIO, *_session,
- path, n,
+ path, n,
(mode == ImportAsTapeTrack
? Source::Destructive
: Source::Flag (0)),
int
Editor::add_sources (vector<string> paths,
SourceList& sources,
- framepos_t& pos,
+ samplepos_t& pos,
ImportDisposition disposition,
ImportMode mode,
int target_regions,
if (use_timestamp && boost::dynamic_pointer_cast<AudioRegion>(r)) {
boost::dynamic_pointer_cast<AudioRegion>(r)->special_set_position(sources[0]->natural_position());
- }
+ }
regions.push_back (r);
/* generate a per-channel region name so that things work as
* intended
*/
-
+
string path;
if (fs) {
} else {
region_name = (*x)->name();
}
-
+
if (sources.size() == 2) {
if (n == 0) {
region_name += "-L";
} else if (sources.size() > 2) {
region_name += string_compose ("-%1", n+1);
}
-
+
track_names.push_back (region_name);
-
+
} else {
if (fs) {
region_name = region_name_from_path (fs->path(), false, false, sources.size(), n);
/* Fudge region length to ensure it is non-zero; make it 1 beat at 120bpm
for want of a better idea. It can't be too small, otherwise if this
- is a MIDI region the conversion from frames -> beats -> frames will
+ is a MIDI region the conversion from samples -> beats -> samples will
round it back down to 0 again.
*/
- framecnt_t len = (*x)->length (pos);
+ samplecnt_t len = (*x)->length (pos);
if (len == 0) {
- len = (60.0 / 120.0) * _session->frame_rate ();
+ len = (60.0 / 120.0) * _session->sample_rate ();
}
plist.add (ARDOUR::Properties::start, 0);
}
int n = 0;
- framepos_t rlen = 0;
+ samplepos_t rlen = 0;
begin_reversible_command (Operations::insert_file);
* the API simpler.
*/
assert (regions.size() == track_names.size());
-
+
for (vector<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) {
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*r);
if (use_timestamp) {
- if (ar) {
-
- /* get timestamp for this region */
-
- const boost::shared_ptr<Source> s (ar->sources().front());
- const boost::shared_ptr<AudioSource> as = boost::dynamic_pointer_cast<AudioSource> (s);
-
- assert (as);
-
- if (as->natural_position() != 0) {
- pos = as->natural_position();
- } else if (target_tracks == 1) {
- /* hmm, no timestamp available, put it after the previous region
- */
- if (n == 0) {
- pos = get_preferred_edit_position ();
- } else {
- pos += rlen;
- }
- } else {
- pos = get_preferred_edit_position ();
- }
- } else {
- /* should really get first position in MIDI file, but for now, use edit position*/
- pos = get_preferred_edit_position ();
- }
- }
-
+ if (ar) {
+
+ /* get timestamp for this region */
+
+ const boost::shared_ptr<Source> s (ar->sources().front());
+ const boost::shared_ptr<AudioSource> as = boost::dynamic_pointer_cast<AudioSource> (s);
+
+ assert (as);
+
+ if (as->natural_position() != 0) {
+ pos = as->natural_position();
+ } else if (target_tracks == 1) {
+ /* hmm, no timestamp available, put it after the previous region
+ */
+ if (n == 0) {
+ pos = get_preferred_edit_position ();
+ } else {
+ pos += rlen;
+ }
+ } else {
+ pos = get_preferred_edit_position ();
+ }
+ } else {
+ /* should really get first position in MIDI file, but for now, use edit position*/
+ pos = get_preferred_edit_position ();
+ }
+ }
+
finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track, track_names[n], instrument);
rlen = (*r)->length();
}
commit_reversible_command ();
-
+
/* setup peak file building in another thread */
for (SourceList::iterator x = sources.begin(); x != sources.end(); ++x) {
Editor::finish_bringing_in_material (boost::shared_ptr<Region> region,
uint32_t in_chans,
uint32_t out_chans,
- framepos_t& pos,
+ samplepos_t& pos,
ImportMode mode,
boost::shared_ptr<Track>& existing_track,
const string& new_track_name,
{
if (!existing_track) {
if (ar) {
- list<boost::shared_ptr<AudioTrack> > at (_session->new_audio_track (in_chans, out_chans, Normal, 0, 1));
+ list<boost::shared_ptr<AudioTrack> > at (_session->new_audio_track (in_chans, out_chans, 0, 1, string(), PresentationInfo::max_order, Normal));
if (at.empty()) {
return -1;
}
+ if (Config->get_strict_io ()) {
+ for (list<boost::shared_ptr<AudioTrack> >::iterator i = at.begin(); i != at.end(); ++i) {
+ (*i)->set_strict_io (true);
+ }
+ }
existing_track = at.front();
} else if (mr) {
list<boost::shared_ptr<MidiTrack> > mt (
_session->new_midi_track (ChanCount (DataType::MIDI, 1),
- ChanCount (DataType::MIDI, 1),
- instrument,
- Normal, 0, 1));
+ ChanCount (DataType::MIDI, 1),
+ Config->get_strict_io () || Profile->get_mixbus (),
+ instrument, (Plugin::PresetRecord*) 0,
+ (RouteGroup*) 0,
+ 1,
+ string(),
+ PresentationInfo::max_order));
if (mt.empty()) {
return -1;
}
+ // TODO set strict_io from preferences
existing_track = mt.front();
}
return -1;
}
- list<boost::shared_ptr<AudioTrack> > at (_session->new_audio_track (in_chans, out_chans, Destructive));
+ list<boost::shared_ptr<AudioTrack> > at (_session->new_audio_track (in_chans, out_chans, 0, 1, string(), PresentationInfo::max_order, Destructive));
if (!at.empty()) {
boost::shared_ptr<Playlist> playlist = at.front()->playlist();
boost::shared_ptr<Region> copy (RegionFactory::create (region, true));
playlist->add_region (copy, pos);
_session->add_command (new StatefulDiffCommand (playlist));
}
+ if (Config->get_strict_io ()) {
+ for (list<boost::shared_ptr<AudioTrack> >::iterator i = at.begin(); i != at.end(); ++i) {
+ (*i)->set_strict_io (true);
+ }
+ }
break;
}
}