Initial GUI support to load AAF sessions
This commit is contained in:
parent
36cd1f796f
commit
0a72c1317f
@ -252,6 +252,8 @@ public:
|
|||||||
int save_state_canfail (std::string state_name = "", bool switch_to_it = false);
|
int save_state_canfail (std::string state_name = "", bool switch_to_it = false);
|
||||||
void save_state (const std::string & state_name = "", bool switch_to_it = false);
|
void save_state (const std::string & state_name = "", bool switch_to_it = false);
|
||||||
|
|
||||||
|
int new_session_from_aaf (std::string const&, std::string const&, std::string&, std::string&);
|
||||||
|
|
||||||
static ARDOUR_UI *instance () { return theArdourUI; }
|
static ARDOUR_UI *instance () { return theArdourUI; }
|
||||||
|
|
||||||
/* signal emitted when escape key is pressed. All UI components that
|
/* signal emitted when escape key is pressed. All UI components that
|
||||||
|
633
gtk2_ardour/ardour_ui_aaf.cc
Normal file
633
gtk2_ardour/ardour_ui_aaf.cc
Normal file
@ -0,0 +1,633 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 Robin Gareus <robin@gareus.org>
|
||||||
|
* Copyright (C) 2023 Adrien Gesta-Fline <dev.agfline@posteo.net>
|
||||||
|
*
|
||||||
|
* 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 "pbd/basename.h"
|
||||||
|
#include "pbd/convert.h"
|
||||||
|
#include "pbd/file_utils.h"
|
||||||
|
|
||||||
|
#include "ardour/audio_track.h"
|
||||||
|
#include "ardour/audioengine.h"
|
||||||
|
#include "ardour/audioregion.h"
|
||||||
|
#include "ardour/filename_extensions.h"
|
||||||
|
#include "ardour/import_status.h"
|
||||||
|
#include "ardour/playlist.h"
|
||||||
|
#include "ardour/plugin_manager.h"
|
||||||
|
#include "ardour/region_factory.h"
|
||||||
|
#include "ardour/source_factory.h"
|
||||||
|
#include "ardour/utils.h"
|
||||||
|
|
||||||
|
#include "aaf/libaaf.h"
|
||||||
|
#include "aaf/utils.h"
|
||||||
|
|
||||||
|
#include "ardour_ui.h"
|
||||||
|
#include "public_editor.h"
|
||||||
|
|
||||||
|
#include "pbd/i18n.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace PBD;
|
||||||
|
using namespace ARDOUR;
|
||||||
|
|
||||||
|
static void
|
||||||
|
aaf_debug_callback (struct dbg* dbg, void* ctxdata, int lib, int type, const char* srcfile, const char* srcfunc, int lineno, const char* msg, void* user)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<AudioTrack>
|
||||||
|
get_nth_audio_track (uint32_t nth, std::shared_ptr<RouteList const> routes)
|
||||||
|
{
|
||||||
|
RouteList rl = *(routes);
|
||||||
|
rl.sort (Stripable::Sorter ());
|
||||||
|
|
||||||
|
for (auto const& r : rl) {
|
||||||
|
std::shared_ptr<AudioTrack> at = std::dynamic_pointer_cast<AudioTrack> (r);
|
||||||
|
if (!at) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (nth-- == 0) {
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::shared_ptr<AudioTrack> ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<AudioTrack>
|
||||||
|
prepare_audio_track (aafiAudioTrack* aafTrack, Session* s)
|
||||||
|
{
|
||||||
|
/* Use existing track */
|
||||||
|
std::shared_ptr<AudioTrack> track = get_nth_audio_track ((aafTrack->number - 1), s->get_routes ());
|
||||||
|
|
||||||
|
if (track) {
|
||||||
|
return track;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ..or create a new track */
|
||||||
|
wstring ws_track_name = std::wstring (aafTrack->name);
|
||||||
|
string track_name = string (ws_track_name.begin (), ws_track_name.end ());
|
||||||
|
|
||||||
|
uint32_t outputs = 2;
|
||||||
|
if (s->master_out ()) {
|
||||||
|
outputs = max (outputs, s->master_out ()->n_inputs ().n_audio ());
|
||||||
|
}
|
||||||
|
|
||||||
|
list<std::shared_ptr<AudioTrack>> at (s->new_audio_track (aafTrack->format, outputs, NULL, 1, track_name, PresentationInfo::max_order));
|
||||||
|
|
||||||
|
if (at.empty ()) {
|
||||||
|
PBD::fatal << "AAF: Could not create new audio track." << endmsg;
|
||||||
|
abort (); /*NOTREACHED*/
|
||||||
|
}
|
||||||
|
|
||||||
|
return at.back ();
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
import_sndfile_as_region (Session* s, struct aafiAudioEssence* audioEssence, SrcQuality quality, timepos_t& pos, SourceList& sources, ImportStatus& status, vector<std::shared_ptr<Region>>& regions)
|
||||||
|
{
|
||||||
|
wstring ws (audioEssence->usable_file_path);
|
||||||
|
string usable_file_path (ws.begin (), ws.end ());
|
||||||
|
|
||||||
|
/* Import the source */
|
||||||
|
status.clear ();
|
||||||
|
|
||||||
|
status.current = 1;
|
||||||
|
status.total = 1;
|
||||||
|
status.freeze = false;
|
||||||
|
status.quality = quality;
|
||||||
|
status.replace_existing_source = false;
|
||||||
|
status.split_midi_channels = false;
|
||||||
|
status.import_markers = false;
|
||||||
|
status.done = false;
|
||||||
|
status.cancel = false;
|
||||||
|
|
||||||
|
status.paths.push_back (usable_file_path);
|
||||||
|
|
||||||
|
s->import_files (status);
|
||||||
|
|
||||||
|
status.progress = 1.0;
|
||||||
|
sources.clear ();
|
||||||
|
|
||||||
|
/* FIXME: There is no way to tell if cancel button was pressed
|
||||||
|
* or if the file failed to import, just that one of these occurred.
|
||||||
|
* We want status.cancel to reflect the user's choice only
|
||||||
|
*/
|
||||||
|
if (status.cancel && status.current > 1) {
|
||||||
|
/* Succeeded to import file, assume user hit cancel */
|
||||||
|
return false;
|
||||||
|
} else if (status.cancel && status.current == 1) {
|
||||||
|
/* Failed to import file, assume user did not hit cancel */
|
||||||
|
status.cancel = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < audioEssence->channels; i++) {
|
||||||
|
sources.push_back (status.sources.at (i));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* build peakfiles */
|
||||||
|
for (SourceList::iterator x = sources.begin (); x != sources.end (); ++x) {
|
||||||
|
SourceFactory::setup_peakfile (*x, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Put the source on a region */
|
||||||
|
std::shared_ptr<Region> region;
|
||||||
|
string region_name;
|
||||||
|
|
||||||
|
/* take all the sources we have and package them up as a region */
|
||||||
|
region_name = region_name_from_path (status.paths.front (), (sources.size () > 1), false);
|
||||||
|
|
||||||
|
/* we checked in import_sndfiles() that there were not too many */
|
||||||
|
while (RegionFactory::region_by_name (region_name)) {
|
||||||
|
region_name = bump_name_once (region_name, '.');
|
||||||
|
}
|
||||||
|
|
||||||
|
ws = audioEssence->unique_file_name;
|
||||||
|
string unique_file_name (ws.begin (), ws.end ());
|
||||||
|
|
||||||
|
PropertyList proplist;
|
||||||
|
|
||||||
|
proplist.add (ARDOUR::Properties::start, 0);
|
||||||
|
proplist.add (ARDOUR::Properties::length, timecnt_t (sources[0]->length (), pos));
|
||||||
|
proplist.add (ARDOUR::Properties::name, unique_file_name);
|
||||||
|
proplist.add (ARDOUR::Properties::layer, 0);
|
||||||
|
proplist.add (ARDOUR::Properties::whole_file, true);
|
||||||
|
proplist.add (ARDOUR::Properties::external, true);
|
||||||
|
|
||||||
|
region = RegionFactory::create (sources, proplist);
|
||||||
|
regions.push_back (region);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<Region>
|
||||||
|
create_region (vector<std::shared_ptr<Region>> source_regions, aafiAudioClip* aafAudioClip, SourceList& oneClipSources, aafPosition_t clipOffset, aafRational_t samplerate_r)
|
||||||
|
{
|
||||||
|
wstring ws = aafAudioClip->Essence->unique_file_name;
|
||||||
|
string unique_file_name (ws.begin (), ws.end ());
|
||||||
|
|
||||||
|
aafPosition_t clipPos = convertEditUnit (aafAudioClip->pos, *aafAudioClip->track->edit_rate, samplerate_r);
|
||||||
|
aafPosition_t clipLen = convertEditUnit (aafAudioClip->len, *aafAudioClip->track->edit_rate, samplerate_r);
|
||||||
|
aafPosition_t essenceOffset = convertEditUnit (aafAudioClip->essence_offset, *aafAudioClip->track->edit_rate, samplerate_r);
|
||||||
|
|
||||||
|
PropertyList proplist;
|
||||||
|
|
||||||
|
proplist.add (ARDOUR::Properties::start, essenceOffset);
|
||||||
|
proplist.add (ARDOUR::Properties::length, clipLen);
|
||||||
|
proplist.add (ARDOUR::Properties::name, unique_file_name);
|
||||||
|
proplist.add (ARDOUR::Properties::layer, 0);
|
||||||
|
proplist.add (ARDOUR::Properties::whole_file, false);
|
||||||
|
proplist.add (ARDOUR::Properties::external, true);
|
||||||
|
|
||||||
|
/* NOTE: region position is set when calling add_region() */
|
||||||
|
|
||||||
|
std::shared_ptr<Region> region = RegionFactory::create (oneClipSources, proplist);
|
||||||
|
|
||||||
|
for (SourceList::iterator source = oneClipSources.begin (); source != oneClipSources.end (); ++source) {
|
||||||
|
/* position displayed in Ardour source list */
|
||||||
|
(*source)->set_natural_position (timepos_t (clipPos + clipOffset));
|
||||||
|
|
||||||
|
for (vector<std::shared_ptr<Region>>::iterator region = source_regions.begin (); region != source_regions.end (); ++region) {
|
||||||
|
if ((*region)->source (0) == *source) {
|
||||||
|
/* Enable "Move to Original Position" */
|
||||||
|
(*region)->set_position (timepos_t (clipPos + clipOffset - essenceOffset));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_region_gain (aafiAudioClip* aafAudioClip, std::shared_ptr<Region> region, Session* s)
|
||||||
|
{
|
||||||
|
if (aafAudioClip->gain && aafAudioClip->gain->flags & AAFI_AUDIO_GAIN_CONSTANT) {
|
||||||
|
std::dynamic_pointer_cast<AudioRegion> (region)->set_scale_amplitude (aafRationalToFloat (aafAudioClip->gain->value[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aafAudioClip->automation) {
|
||||||
|
aafiAudioGain* level = aafAudioClip->automation;
|
||||||
|
std::shared_ptr<AudioRegion> ar = std::dynamic_pointer_cast<AudioRegion> (region);
|
||||||
|
std::shared_ptr<AutomationList> al = ar->envelope ();
|
||||||
|
|
||||||
|
for (int i = 0; i < level->pts_cnt; ++i) {
|
||||||
|
al->fast_simple_add (timepos_t (aafRationalToFloat (level->time[i]) * region->length ().samples ()), aafRationalToFloat (level->value[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static FadeShape
|
||||||
|
aaf_fade_interpol_to_ardour_fade_shape (aafiInterpolation_e interpol)
|
||||||
|
{
|
||||||
|
switch (interpol & AAFI_INTERPOL_MASK) {
|
||||||
|
case AAFI_INTERPOL_NONE:
|
||||||
|
return FadeConstantPower;
|
||||||
|
case AAFI_INTERPOL_LINEAR:
|
||||||
|
return FadeLinear;
|
||||||
|
case AAFI_INTERPOL_LOG:
|
||||||
|
return FadeConstantPower;
|
||||||
|
case AAFI_INTERPOL_CONSTANT:
|
||||||
|
return FadeConstantPower;
|
||||||
|
case AAFI_INTERPOL_POWER:
|
||||||
|
return FadeConstantPower;
|
||||||
|
case AAFI_INTERPOL_BSPLINE:
|
||||||
|
return FadeConstantPower;
|
||||||
|
default:
|
||||||
|
return FadeConstantPower;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_region_fade (aafiAudioClip* aafAudioClip, std::shared_ptr<Region> region, aafRational_t const& samplerate)
|
||||||
|
{
|
||||||
|
if (aafAudioClip == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
aafiTransition* fadein = aafi_get_fadein (aafAudioClip->Item);
|
||||||
|
aafiTransition* fadeout = aafi_get_fadeout (aafAudioClip->Item);
|
||||||
|
aafiTransition* xfade = aafi_get_xfade (aafAudioClip->Item);
|
||||||
|
|
||||||
|
if (xfade) {
|
||||||
|
if (fadein == NULL) {
|
||||||
|
fadein = xfade;
|
||||||
|
} else {
|
||||||
|
PBD::warning << "Clip has both fadein and crossfade : crossfade will be ignored." << endmsg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FadeShape fade_shape;
|
||||||
|
samplecnt_t fade_len;
|
||||||
|
|
||||||
|
if (fadein != NULL) {
|
||||||
|
fade_shape = aaf_fade_interpol_to_ardour_fade_shape ((aafiInterpolation_e) (fadein->flags & AAFI_INTERPOL_MASK));
|
||||||
|
fade_len = convertEditUnit (fadein->len, *aafAudioClip->track->edit_rate, samplerate);
|
||||||
|
|
||||||
|
std::dynamic_pointer_cast<AudioRegion> (region)->set_fade_in (fade_shape, fade_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fadeout != NULL) {
|
||||||
|
fade_shape = aaf_fade_interpol_to_ardour_fade_shape ((aafiInterpolation_e) (fadeout->flags & AAFI_INTERPOL_MASK));
|
||||||
|
fade_len = convertEditUnit (fadeout->len, *aafAudioClip->track->edit_rate, samplerate);
|
||||||
|
|
||||||
|
std::dynamic_pointer_cast<AudioRegion> (region)->set_fade_out (fade_shape, fade_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_session_timecode (AAF_Iface* aafi, Session* s)
|
||||||
|
{
|
||||||
|
using namespace Timecode;
|
||||||
|
|
||||||
|
uint16_t aafFPS = aafi->Timecode->fps;
|
||||||
|
TimecodeFormat ardourtc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fractional timecodes are never explicitly set into tc->fps, so we deduce
|
||||||
|
* them based on edit_rate value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
switch (aafFPS) {
|
||||||
|
case 24:
|
||||||
|
if (aafi->Timecode->edit_rate->numerator == 24000 &&
|
||||||
|
aafi->Timecode->edit_rate->denominator == 1001) {
|
||||||
|
ardourtc = timecode_23976;
|
||||||
|
} else {
|
||||||
|
ardourtc = timecode_24;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 25:
|
||||||
|
if (aafi->Timecode->edit_rate->numerator == 25000 &&
|
||||||
|
aafi->Timecode->edit_rate->denominator == 1001) {
|
||||||
|
ardourtc = timecode_24976;
|
||||||
|
} else {
|
||||||
|
ardourtc = timecode_25;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 30:
|
||||||
|
if (aafi->Timecode->edit_rate->numerator == 30000 &&
|
||||||
|
aafi->Timecode->edit_rate->denominator == 1001) {
|
||||||
|
if (aafi->Timecode->drop) {
|
||||||
|
ardourtc = timecode_2997drop;
|
||||||
|
} else {
|
||||||
|
ardourtc = timecode_2997;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (aafi->Timecode->drop) {
|
||||||
|
ardourtc = timecode_30drop;
|
||||||
|
} else {
|
||||||
|
ardourtc = timecode_30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 60:
|
||||||
|
if (aafi->Timecode->edit_rate->numerator == 60000 &&
|
||||||
|
aafi->Timecode->edit_rate->denominator == 1001) {
|
||||||
|
ardourtc = timecode_5994;
|
||||||
|
} else {
|
||||||
|
ardourtc = timecode_60;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
PBD::error << string_compose ("Unknown AAF timecode fps : %1.", aafFPS) << endmsg;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
s->config.set_timecode_format (ardourtc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create and open Sesssion from AAF
|
||||||
|
* return > 0 if file is not a [valid] AAF
|
||||||
|
* return < 0 if session creation failed.
|
||||||
|
* return 0 on success. path and snapshot are set.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ARDOUR_UI::new_session_from_aaf (string const& aaf, string const& target_dir, string& path, string& snapshot)
|
||||||
|
{
|
||||||
|
if (PBD::downcase (aaf).find (advanced_authoring_format_suffix) == string::npos) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_session) {
|
||||||
|
if (unload_session (false)) {
|
||||||
|
/* unload cancelled by user */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AAF_Iface* aafi = aafi_alloc (NULL);
|
||||||
|
|
||||||
|
uint32_t aaf_resolve_options = 0;
|
||||||
|
uint32_t aaf_protools_options = 0;
|
||||||
|
|
||||||
|
aafi_set_option_int (aafi, "trace", 1);
|
||||||
|
aafi_set_option_int (aafi, "protools", aaf_protools_options);
|
||||||
|
aafi_set_option_int (aafi, "resolve", aaf_resolve_options);
|
||||||
|
|
||||||
|
// XXX use Glib::convert_with_fallback
|
||||||
|
aafi->ctx.options.forbid_nonlatin_filenames = 1;
|
||||||
|
|
||||||
|
aafi_set_debug (aafi, VERB_DEBUG, 0, 0, aaf_debug_callback, this);
|
||||||
|
|
||||||
|
//aafi_set_option_str (aafi, "media_location", media_location_path.c_str ());
|
||||||
|
|
||||||
|
if (aafi_load_file (aafi, aaf.c_str ())) {
|
||||||
|
error << "AAF: Could not load AAF file." << endmsg;
|
||||||
|
aafi_release (&aafi);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* extract or set session name */
|
||||||
|
if (aafi->compositionName && aafi->compositionName[0] != 0x00) {
|
||||||
|
wstring ws_session_name = std::wstring (aafi->compositionName);
|
||||||
|
snapshot = string (ws_session_name.begin (), ws_session_name.end ());
|
||||||
|
} else {
|
||||||
|
snapshot = basename_nosuffix (aaf);
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot = legalize_for_universal_path (snapshot);
|
||||||
|
path = Glib::build_filename (target_dir, snapshot);
|
||||||
|
|
||||||
|
if (Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
|
||||||
|
error << string_compose (_("AAF: Destination '%1' already exists."), path) << endmsg;
|
||||||
|
snapshot = ""; // XXX?
|
||||||
|
path = "";
|
||||||
|
aafi_release (&aafi);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create media cache */
|
||||||
|
GError* err = NULL;
|
||||||
|
char* td = g_dir_make_tmp ("aaf-cache-XXXXXX", &err);
|
||||||
|
|
||||||
|
if (!td) {
|
||||||
|
error << string_compose (_("AAF: Could not prepare media cache: %1"), err->message) << endmsg;
|
||||||
|
aafi_release (&aafi);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const string media_cache_path = PBD::canonical_path (td);
|
||||||
|
g_free (td);
|
||||||
|
g_clear_error (&err);
|
||||||
|
|
||||||
|
/* all systems go. create sessions */
|
||||||
|
BusProfile bus_profile;
|
||||||
|
bus_profile.master_out_channels = 2;
|
||||||
|
|
||||||
|
aafRational_t samplerate_r;
|
||||||
|
|
||||||
|
samplerate_r.numerator = aafi->Audio->samplerate;
|
||||||
|
samplerate_r.denominator = 1;
|
||||||
|
|
||||||
|
std::string restore_backend;
|
||||||
|
if (!AudioEngine::instance()->running ()) {
|
||||||
|
AudioEngine* e = AudioEngine::instance();
|
||||||
|
restore_backend = e->current_backend_name ();
|
||||||
|
e->set_backend ("None (Dummy)", "", "");
|
||||||
|
e->start ();
|
||||||
|
PluginManager::instance ().refresh (true);
|
||||||
|
attach_to_engine ();
|
||||||
|
}
|
||||||
|
if (!AudioEngine::instance()->running ()) {
|
||||||
|
error << _("Could not start [dummy] engine for AAF import .") << endmsg;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
build_session_stage_two (path, snapshot, "", bus_profile, false, Temporal::AudioTime, aafi->Audio->samplerate);
|
||||||
|
|
||||||
|
if (!_session) {
|
||||||
|
aafi_release (&aafi);
|
||||||
|
PBD::remove_directory (media_cache_path);
|
||||||
|
if (!restore_backend.empty ()) {
|
||||||
|
AudioEngine::instance()->stop ();
|
||||||
|
AudioEngine::instance()->set_backend (restore_backend, "", "");
|
||||||
|
}
|
||||||
|
error << _("Could not create new session for AAF import .") << endmsg;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (aafi->Audio->samplesize) {
|
||||||
|
case 16:
|
||||||
|
_session->config.set_native_file_data_format (ARDOUR::FormatInt16);
|
||||||
|
break;
|
||||||
|
case 24:
|
||||||
|
_session->config.set_native_file_data_format (ARDOUR::FormatInt24);
|
||||||
|
break;
|
||||||
|
case 32:
|
||||||
|
_session->config.set_native_file_data_format (ARDOUR::FormatFloat);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Import Sources */
|
||||||
|
|
||||||
|
SourceList oneClipSources;
|
||||||
|
ARDOUR::ImportStatus import_status;
|
||||||
|
vector<std::shared_ptr<Region>> source_regions;
|
||||||
|
timepos_t pos = timepos_t::max (Temporal::AudioTime);
|
||||||
|
|
||||||
|
aafiAudioEssence* audioEssence = NULL;
|
||||||
|
|
||||||
|
for (aafiAudioEssence* audioEssence = aafi->Audio->Essences; audioEssence != NULL; audioEssence = audioEssence->next) {
|
||||||
|
/* If we extract embedded essences to `s->session_directory().sound_path()` then we end up with a duplicate on import.
|
||||||
|
* So we extract essence to a cache folder
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (audioEssence->is_embedded) {
|
||||||
|
if (media_cache_path.empty ()) {
|
||||||
|
error << _("Could not extract audio file from AAF: media cache was not set.") << endmsg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (aafi_extract_audio_essence (aafi, audioEssence, media_cache_path.c_str (), NULL) < 0) {
|
||||||
|
error << string_compose (_("AAF: Could not extract audio file '%1' from AAF."), audioEssence->unique_file_name) << endmsg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!audioEssence->usable_file_path) {
|
||||||
|
error << string_compose (_("AAF: Could not locate external audio file: '%1'"), audioEssence->original_file_path) << endmsg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!import_sndfile_as_region (_session, audioEssence, SrcBest, pos, oneClipSources, import_status, source_regions)) {
|
||||||
|
error << string_compose (_("AAF: Could not import '%1' to session."), audioEssence->unique_file_name) << endmsg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
audioEssence->user = new SourceList (oneClipSources);
|
||||||
|
|
||||||
|
info << string_compose ("Source file '%1' successfully imported to session.", audioEssence->unique_file_name) << endmsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
oneClipSources.clear ();
|
||||||
|
|
||||||
|
aafPosition_t sessionStart = convertEditUnit (aafi->compositionStart, aafi->compositionStart_editRate, samplerate_r);
|
||||||
|
|
||||||
|
aafiAudioTrack* aafAudioTrack = NULL;
|
||||||
|
aafiTimelineItem* aafAudioItem = NULL;
|
||||||
|
aafiAudioClip* aafAudioClip = NULL;
|
||||||
|
|
||||||
|
foreach_audioTrack (aafAudioTrack, aafi)
|
||||||
|
{
|
||||||
|
std::shared_ptr<AudioTrack> track = prepare_audio_track (aafAudioTrack, _session);
|
||||||
|
|
||||||
|
foreach_Item (aafAudioItem, aafAudioTrack)
|
||||||
|
{
|
||||||
|
if (aafAudioItem->type != AAFI_AUDIO_CLIP) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
aafAudioClip = (aafiAudioClip*)aafAudioItem->data;
|
||||||
|
|
||||||
|
if (aafAudioClip->Essence == NULL) {
|
||||||
|
error << _("AAF: Clip has no essence.") << endmsg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* converts whatever edit_rate clip is in, to samples */
|
||||||
|
aafPosition_t clipPos = convertEditUnit (aafAudioClip->pos, *aafAudioClip->track->edit_rate, samplerate_r);
|
||||||
|
|
||||||
|
aafiAudioEssence* audioEssence = aafAudioClip->Essence;
|
||||||
|
|
||||||
|
if (!audioEssence || !audioEssence->user) {
|
||||||
|
error << string_compose (_("AAF: Could not create new region for clip '%1': Missing audio essence"), aafAudioClip->Essence->unique_file_name) << endmsg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceList* oneClipSources = static_cast<SourceList*> (audioEssence->user);
|
||||||
|
|
||||||
|
if (oneClipSources->size () == 0) {
|
||||||
|
error << string_compose (_("AAF: Could not create new region for clip '%1': Region has no source"), aafAudioClip->Essence->unique_file_name) << endmsg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Region> region = create_region (source_regions, aafAudioClip, *oneClipSources, sessionStart, samplerate_r);
|
||||||
|
|
||||||
|
if (!region) {
|
||||||
|
error << string_compose (_("AAF: Could not create new region for clip '%2'"), aafAudioClip->Essence->unique_file_name) << endmsg;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
track->playlist ()->add_region (region, timepos_t (clipPos + sessionStart));
|
||||||
|
set_region_gain (aafAudioClip, region, _session);
|
||||||
|
set_region_fade (aafAudioClip, region, samplerate_r);
|
||||||
|
if (aafAudioClip->mute) {
|
||||||
|
region->set_muted (true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (aafiMarker* marker = aafi->Markers; marker != NULL; marker = marker->next) {
|
||||||
|
aafPosition_t markerStart = sessionStart + convertEditUnit (marker->start, *marker->edit_rate, samplerate_r);
|
||||||
|
aafPosition_t markerEnd = sessionStart + convertEditUnit ((marker->start + marker->length), *marker->edit_rate, samplerate_r);
|
||||||
|
|
||||||
|
wstring markerName (marker->name);
|
||||||
|
|
||||||
|
Location* location;
|
||||||
|
|
||||||
|
if (marker->length == 0) {
|
||||||
|
location = new Location (*_session, timepos_t (markerStart), timepos_t (markerStart), string (markerName.begin (), markerName.end ()), Location::Flags (Location::IsMark));
|
||||||
|
} else {
|
||||||
|
location = new Location (*_session, timepos_t (markerStart), timepos_t (markerEnd), string (markerName.begin (), markerName.end ()), Location::Flags (Location::IsRangeMarker));
|
||||||
|
}
|
||||||
|
|
||||||
|
_session->locations ()->add (location, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set session range */
|
||||||
|
samplepos_t start = samplepos_t (eu2sample (_session->nominal_sample_rate (), &aafi->compositionStart_editRate, aafi->compositionStart));
|
||||||
|
samplepos_t end = samplepos_t (eu2sample (_session->nominal_sample_rate (), &aafi->compositionLength_editRate, aafi->compositionLength)) + start;
|
||||||
|
_session->maybe_update_session_range (timepos_t (start), timepos_t (end));
|
||||||
|
|
||||||
|
/* set timecode */
|
||||||
|
set_session_timecode (aafi, _session);
|
||||||
|
|
||||||
|
the_editor ().access_action ("Editor", "zoom-to-session");
|
||||||
|
|
||||||
|
/* Cleanup */
|
||||||
|
import_status.progress = 1.0;
|
||||||
|
import_status.done = true;
|
||||||
|
import_status.sources.clear ();
|
||||||
|
import_status.all_done = true;
|
||||||
|
|
||||||
|
_session->save_state ("");
|
||||||
|
|
||||||
|
/* clear */
|
||||||
|
|
||||||
|
foreachEssence (audioEssence, aafi->Audio->Essences)
|
||||||
|
{
|
||||||
|
if (audioEssence && audioEssence->user) {
|
||||||
|
static_cast<SourceList*> (audioEssence->user)->clear ();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
source_regions.clear ();
|
||||||
|
|
||||||
|
PBD::remove_directory (media_cache_path);
|
||||||
|
|
||||||
|
aafi_release (&aafi);
|
||||||
|
|
||||||
|
if (!restore_backend.empty ()) {
|
||||||
|
AudioEngine::instance()->stop ();
|
||||||
|
AudioEngine::instance()->set_backend (restore_backend, "", "");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
@ -195,6 +195,20 @@ ARDOUR_UI::session_dialog_response_handler (int response, SessionDialog* session
|
|||||||
return; /* back to main event loop */
|
return; /* back to main event loop */
|
||||||
} else if (rv == 0) {
|
} else if (rv == 0) {
|
||||||
session_dialog->set_provided_session (session_name, session_path);
|
session_dialog->set_provided_session (session_name, session_path);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
rv = new_session_from_aaf (session_name, Config->get_default_session_parent_dir(), session_path, session_name);
|
||||||
|
if (rv < 0) {
|
||||||
|
ArdourMessageDialog msg (*session_dialog, _("Extracting AAF failed"));
|
||||||
|
msg.run ();
|
||||||
|
return; /* back to main event loop */
|
||||||
|
} else if (rv == 0) {
|
||||||
|
session_dialog->set_provided_session (session_name, session_path);
|
||||||
|
/* we got a session now */
|
||||||
|
session_dialog->hide ();
|
||||||
|
delete_when_idle (session_dialog);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1171,9 +1185,13 @@ ARDOUR_UI::open_session ()
|
|||||||
FileFilter archive_filter;
|
FileFilter archive_filter;
|
||||||
archive_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::session_archive_suffix));
|
archive_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::session_archive_suffix));
|
||||||
archive_filter.set_name (_("Session Archives"));
|
archive_filter.set_name (_("Session Archives"));
|
||||||
|
|
||||||
open_session_selector.add_filter (archive_filter);
|
open_session_selector.add_filter (archive_filter);
|
||||||
|
|
||||||
|
FileFilter aaf_filter;
|
||||||
|
aaf_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::advanced_authoring_format_suffix));
|
||||||
|
aaf_filter.set_name (_("Advanced Authoring Format (AAF)"));
|
||||||
|
open_session_selector.add_filter (aaf_filter);
|
||||||
|
|
||||||
open_session_selector.set_filter (session_filter);
|
open_session_selector.set_filter (session_filter);
|
||||||
|
|
||||||
int response = open_session_selector.run();
|
int response = open_session_selector.run();
|
||||||
@ -1190,22 +1208,34 @@ ARDOUR_UI::open_session ()
|
|||||||
string path, name;
|
string path, name;
|
||||||
bool isnew;
|
bool isnew;
|
||||||
|
|
||||||
if (session_path.length() > 0) {
|
if (session_path.empty()) {
|
||||||
int rv = ARDOUR::inflate_session (session_path,
|
return;
|
||||||
Config->get_default_session_parent_dir(), path, name);
|
}
|
||||||
if (rv == 0) {
|
int rv = ARDOUR::inflate_session (session_path, Config->get_default_session_parent_dir(), path, name);
|
||||||
_session_is_new = false;
|
if (rv == 0) {
|
||||||
load_session (path, name);
|
_session_is_new = false;
|
||||||
}
|
load_session (path, name);
|
||||||
else if (rv < 0) {
|
return;
|
||||||
ArdourMessageDialog msg (_main_window,
|
}
|
||||||
string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
|
else if (rv < 0) {
|
||||||
msg.run ();
|
ArdourMessageDialog msg (_main_window, string_compose (_("Extracting session-archive failed: %1"), inflate_error (rv)));
|
||||||
}
|
msg.run ();
|
||||||
else if (ARDOUR::find_session (session_path, path, name, isnew) == 0) {
|
return;
|
||||||
_session_is_new = isnew;
|
}
|
||||||
load_session (path, name);
|
|
||||||
}
|
rv = new_session_from_aaf (session_path, Config->get_default_session_parent_dir(), path, name);
|
||||||
|
if (rv == 0) {
|
||||||
|
_session_is_new = false;
|
||||||
|
return;
|
||||||
|
} else if (rv < 0) {
|
||||||
|
ArdourMessageDialog msg (_main_window, _("Extracting AAF failed"));
|
||||||
|
msg.run ();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ARDOUR::find_session (session_path, path, name, isnew) == 0) {
|
||||||
|
_session_is_new = isnew;
|
||||||
|
load_session (path, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,6 +546,10 @@ ARDOUR_UI::sfsm_response (StartupFSM::Result r)
|
|||||||
queue_finish ();
|
queue_finish ();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case StartupFSM::LoadedSession:
|
||||||
|
startup_done ();
|
||||||
|
break;
|
||||||
|
|
||||||
case StartupFSM::LoadSession:
|
case StartupFSM::LoadSession:
|
||||||
|
|
||||||
if (load_session_from_startup_fsm () == 0) {
|
if (load_session_from_startup_fsm () == 0) {
|
||||||
|
@ -468,6 +468,11 @@ SessionDialog::setup_initial_choice_box ()
|
|||||||
archive_filter.set_name (_("Session Archives"));
|
archive_filter.set_name (_("Session Archives"));
|
||||||
existing_session_chooser.add_filter (archive_filter);
|
existing_session_chooser.add_filter (archive_filter);
|
||||||
|
|
||||||
|
FileFilter aaf_filter;
|
||||||
|
aaf_filter.add_pattern (string_compose(X_("*%1"), ARDOUR::advanced_authoring_format_suffix));
|
||||||
|
aaf_filter.set_name (_("Advanced Authoring Format (AAF)"));
|
||||||
|
existing_session_chooser.add_filter (aaf_filter);
|
||||||
|
|
||||||
existing_session_chooser.set_filter (session_filter);
|
existing_session_chooser.set_filter (session_filter);
|
||||||
|
|
||||||
Gtkmm2ext::add_volume_shortcuts (existing_session_chooser);
|
Gtkmm2ext::add_volume_shortcuts (existing_session_chooser);
|
||||||
|
@ -333,7 +333,7 @@ StartupFSM::dialog_response_handler (int response, StartupFSM::DialogID dialog_i
|
|||||||
switch (response) {
|
switch (response) {
|
||||||
case RESPONSE_OK:
|
case RESPONSE_OK:
|
||||||
if (AudioEngine::instance()->running()) {
|
if (AudioEngine::instance()->running()) {
|
||||||
_signal_response (LoadSession);
|
_signal_response (session_loaded ? LoadedSession : LoadSession);
|
||||||
} else {
|
} else {
|
||||||
/* Engine died unexpectedly (it was
|
/* Engine died unexpectedly (it was
|
||||||
* running after
|
* running after
|
||||||
@ -721,6 +721,7 @@ int
|
|||||||
StartupFSM::check_session_parameters (bool must_be_new)
|
StartupFSM::check_session_parameters (bool must_be_new)
|
||||||
{
|
{
|
||||||
bool requested_new = false;
|
bool requested_new = false;
|
||||||
|
session_loaded = false;
|
||||||
|
|
||||||
session_name = session_dialog->session_name (requested_new);
|
session_name = session_dialog->session_name (requested_new);
|
||||||
session_path = session_dialog->session_folder ();
|
session_path = session_dialog->session_folder ();
|
||||||
@ -768,6 +769,21 @@ StartupFSM::check_session_parameters (bool must_be_new)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!must_be_new) {
|
||||||
|
int rv = ARDOUR_UI::instance()->new_session_from_aaf (session_name, Config->get_default_session_parent_dir(), session_path, session_name);
|
||||||
|
if (rv < 0) {
|
||||||
|
ArdourMessageDialog msg (*session_dialog, _("Extracting aaf failed"));
|
||||||
|
msg.run ();
|
||||||
|
return 1;
|
||||||
|
} else if (rv == 0) {
|
||||||
|
if (ARDOUR_UI::instance()->session ()) {
|
||||||
|
session_existing_sample_rate = ARDOUR_UI::instance()->session ()->nominal_sample_rate ();
|
||||||
|
}
|
||||||
|
session_loaded = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* check for ".ardour" in statefile name, because we don't want
|
/* check for ".ardour" in statefile name, because we don't want
|
||||||
* it
|
* it
|
||||||
*
|
*
|
||||||
|
@ -45,6 +45,7 @@ class StartupFSM : public sigc::trackable
|
|||||||
|
|
||||||
enum Result {
|
enum Result {
|
||||||
LoadSession,
|
LoadSession,
|
||||||
|
LoadedSession,
|
||||||
ExitProgram,
|
ExitProgram,
|
||||||
QuitProgram,
|
QuitProgram,
|
||||||
};
|
};
|
||||||
@ -72,6 +73,7 @@ class StartupFSM : public sigc::trackable
|
|||||||
XMLNode session_engine_hints;
|
XMLNode session_engine_hints;
|
||||||
bool session_is_new;
|
bool session_is_new;
|
||||||
bool session_name_edited;
|
bool session_name_edited;
|
||||||
|
bool session_loaded;
|
||||||
|
|
||||||
ARDOUR::BusProfile bus_profile;
|
ARDOUR::BusProfile bus_profile;
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ gtk2_ardour_sources = [
|
|||||||
'ardour_ui.cc',
|
'ardour_ui.cc',
|
||||||
'ardour_ui2.cc',
|
'ardour_ui2.cc',
|
||||||
'ardour_ui3.cc',
|
'ardour_ui3.cc',
|
||||||
|
'ardour_ui_aaf.cc',
|
||||||
'ardour_ui_access_web.cc',
|
'ardour_ui_access_web.cc',
|
||||||
'ardour_ui_dependents.cc',
|
'ardour_ui_dependents.cc',
|
||||||
'ardour_ui_dialogs.cc',
|
'ardour_ui_dialogs.cc',
|
||||||
@ -524,6 +525,12 @@ def build(bld):
|
|||||||
obj.uselib += ' GLIBMM GIOMM PANGOMM PANGOFT2 LIBPNG'
|
obj.uselib += ' GLIBMM GIOMM PANGOMM PANGOFT2 LIBPNG'
|
||||||
else:
|
else:
|
||||||
obj.uselib += ' GTKMM'
|
obj.uselib += ' GTKMM'
|
||||||
|
|
||||||
|
if bld.is_defined('USE_EXTERNAL_LIBS'):
|
||||||
|
obj.uselib += ' LIBAAF'
|
||||||
|
else:
|
||||||
|
obj.use.extend (['libaaf'])
|
||||||
|
|
||||||
if bld.is_defined('HAVE_USB'):
|
if bld.is_defined('HAVE_USB'):
|
||||||
obj.uselib += ' USB'
|
obj.uselib += ' USB'
|
||||||
if bld.is_defined('HAVE_SUIL'):
|
if bld.is_defined('HAVE_SUIL'):
|
||||||
@ -620,6 +627,12 @@ def build(bld):
|
|||||||
obj.uselib += ' GLIBMM GIOMM PANGOFT2 LIBPNG'
|
obj.uselib += ' GLIBMM GIOMM PANGOFT2 LIBPNG'
|
||||||
else:
|
else:
|
||||||
obj.uselib += ' GTKMM GTK'
|
obj.uselib += ' GTKMM GTK'
|
||||||
|
|
||||||
|
if bld.is_defined('USE_EXTERNAL_LIBS'):
|
||||||
|
obj.uselib += ' LIBAAF'
|
||||||
|
else:
|
||||||
|
obj.use.extend (['libaaf'])
|
||||||
|
|
||||||
if bld.is_defined('HAVE_USB'):
|
if bld.is_defined('HAVE_USB'):
|
||||||
obj.uselib += ' USB'
|
obj.uselib += ' USB'
|
||||||
if sys.platform == 'darwin':
|
if sys.platform == 'darwin':
|
||||||
|
Loading…
Reference in New Issue
Block a user