pre-process (silence) before export to flush reverb tails etc.
[ardour.git] / libs / ardour / session_export.cc
index f696c6b293f5006ad1a8d43289e69dc34acb24b2..6b54db3826b8d923e16b78896d2ac39dea1a8214 100644 (file)
 
 
 #include "pbd/error.h"
-#include <glibmm/thread.h>
+#include <glibmm/threads.h>
+
+#include <midi++/mmc.h>
 
 #include "ardour/audioengine.h"
 #include "ardour/butler.h"
-#include "ardour/export_failed.h"
 #include "ardour/export_handler.h"
 #include "ardour/export_status.h"
+#include "ardour/process_thread.h"
 #include "ardour/session.h"
 #include "ardour/track.h"
-#include "ardour/process_thread.h"
 
 #include "i18n.h"
 
@@ -62,8 +63,6 @@ Session::pre_export ()
 {
        get_export_status (); // Init export_status
 
-       _butler->wait_until_finished ();
-
        /* take everyone out of awrite to avoid disasters */
 
        {
@@ -74,12 +73,16 @@ Session::pre_export ()
                }
        }
 
-       /* make sure we are actually rolling */
+       /* prepare transport */
+
+       realtime_stop (true, true);
 
        if (get_record_enabled()) {
                disable_record (false);
        }
 
+       unset_play_loop ();
+
        /* no slaving */
 
        post_export_sync = config.get_external_sync ();
@@ -88,20 +91,34 @@ Session::pre_export ()
        config.set_external_sync (false);
 
        _exporting = true;
-       export_status->running = true;
-       export_status->Aborting.connect_same_thread (*this, boost::bind (&Session::stop_audio_export, this));
+       export_status->set_running (true);
        export_status->Finished.connect_same_thread (*this, boost::bind (&Session::finalize_audio_export, this));
 
+       /* disable MMC output early */
+
+       _pre_export_mmc_enabled = _mmc->send_enabled ();
+       _mmc->enable_send (false);
+
        return 0;
 }
 
+/** Called for each range that is being exported */
 int
-Session::start_audio_export (nframes_t position, bool /* realtime */)
+Session::start_audio_export (framepos_t position)
 {
        if (!_exporting) {
                pre_export ();
        }
 
+       _export_preroll = 10.0 * nominal_frame_rate (); // TODO make configurable
+
+       /* We're about to call Track::seek, so the butler must have finished everything
+          up otherwise it could be doing do_refill in its thread while we are doing
+          it here.
+       */
+
+       _butler->wait_until_finished ();
+
        /* get everyone to the right position */
 
        {
@@ -132,11 +149,6 @@ Session::start_audio_export (nframes_t position, bool /* realtime */)
           since then has re-awakened it.
         */
 
-       set_transport_speed (1.0, false);
-       butler_transport_work ();
-       g_atomic_int_set (&_butler->should_do_transport_work, 0);
-       post_transport ();
-
        /* we are ready to go ... */
 
        if (!_engine.connected()) {
@@ -148,8 +160,8 @@ Session::start_audio_export (nframes_t position, bool /* realtime */)
        return _engine.freewheel (true);
 }
 
-void
-Session::process_export (nframes_t nframes)
+int
+Session::process_export (pframes_t nframes)
 {
        if (_export_rolling && export_status->stop) {
                stop_audio_export ();
@@ -165,24 +177,48 @@ Session::process_export (nframes_t nframes)
 
                process_without_events (nframes);
        }
-       
+
        try {
                /* handle export - XXX what about error handling? */
 
                ProcessExport (nframes);
 
        } catch (std::exception & e) {
-               std::cout << e.what() << std::endl;
+               error << string_compose (_("Export ended unexpectedly: %1"), e.what()) << endmsg;
                export_status->abort (true);
+               return -1;
        }
+
+       return 0;
 }
 
 int
-Session::process_export_fw (nframes_t nframes)
+Session::process_export_fw (pframes_t nframes)
 {
-        _engine.main_thread()->get_buffers ();
+       if (_export_preroll > 0) {
+
+               _engine.main_thread()->get_buffers ();
+               fail_roll (nframes);
+               _engine.main_thread()->drop_buffers ();
+
+               _export_preroll -= std::min ((framepos_t)nframes, _export_preroll);
+
+               if (_export_preroll > 0) {
+                       // clear out buffers (reverb tails etc).
+                       return 0;
+               }
+
+               set_transport_speed (1.0, 0, false);
+               butler_transport_work ();
+               g_atomic_int_set (&_butler->should_do_transport_work, 0);
+               post_transport ();
+               return 0;
+       }
+
+       _engine.main_thread()->get_buffers ();
        process_export (nframes);
-        _engine.main_thread()->drop_buffers ();
+       _engine.main_thread()->drop_buffers ();
+
        return 0;
 }
 
@@ -198,24 +234,28 @@ Session::stop_audio_export ()
        _export_rolling = false;
        _butler->schedule_transport_work ();
 
-       if (export_status->aborted()) {
-               finalize_audio_export ();
-       }
-
        return 0;
-
 }
 
 void
 Session::finalize_audio_export ()
 {
        _exporting = false;
-       _export_rolling = false;
+
+       if (_export_rolling) {
+               stop_audio_export ();
+       }
 
        /* Clean up */
 
        _engine.freewheel (false);
+
        export_freewheel_connection.disconnect();
+
+       _mmc->enable_send (_pre_export_mmc_enabled);
+
+       /* maybe write CUE/TOC */
+
        export_handler.reset();
        export_status.reset();
 
@@ -224,6 +264,6 @@ Session::finalize_audio_export ()
        if (post_export_sync) {
                config.set_external_sync (true);
        } else {
-               locate (post_export_position, false, false, false);
+               locate (post_export_position, false, false, false, false, false);
        }
 }