2022-10-18 10:53:04 -04:00
/*
* Copyright ( C ) 2022 Robin Gareus < robin @ gareus . org >
*
* 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 <gtkmm/messagedialog.h>
# include <gtkmm/stock.h>
# include <glib.h>
# include "pbd/openuri.h"
# include "ardour/export_channel_configuration.h"
# include "ardour/export_filename.h"
# include "ardour/export_preset.h"
# include "ardour/export_profile_manager.h"
# include "ardour/export_status.h"
# include "ardour/export_timespan.h"
# include "ardour/profile.h"
# include "ardour/session_directory.h"
2024-02-26 16:13:07 -05:00
# include "ardour/surround_return.h"
2022-10-18 10:53:04 -04:00
# include "nag.h"
# include "simple_export_dialog.h"
# include "pbd/i18n.h"
using namespace std ;
using namespace ARDOUR ;
using namespace PBD ;
using namespace Gtk ;
2023-12-07 11:43:26 -05:00
SimpleExportDialog : : SimpleExportDialog ( PublicEditor & editor , bool vapor_export )
2022-10-21 13:52:40 -04:00
: ARDOUR : : SimpleExport ( )
2023-12-07 11:43:26 -05:00
, ArdourDialog ( vapor_export ? _ ( " Surround Master Export " ) : _ ( " Quick Audio Export " ) , true , false )
2022-10-21 13:52:40 -04:00
, _editor ( editor )
2022-10-18 10:53:04 -04:00
, _eps ( true )
2023-12-07 11:43:26 -05:00
, _vapor_export ( vapor_export )
2022-10-18 10:53:04 -04:00
{
if ( _eps . the_combo ( ) . get_parent ( ) ) {
_eps . the_combo ( ) . get_parent ( ) - > remove ( _eps . the_combo ( ) ) ;
}
2022-12-09 13:09:32 -05:00
_range_list = ListStore : : create ( _range_cols ) ;
_range_combo . set_model ( _range_list ) ;
_range_combo . pack_start ( _range_cols . label ) ;
2022-10-18 10:53:04 -04:00
Table * t = manage ( new Table ) ;
int r = 0 ;
t - > set_spacings ( 4 ) ;
# define LBL(TXT) *manage (new Label (_(TXT), ALIGN_END))
/* clang-format off */
t - > attach ( LBL ( " Format preset: " ) , 0 , 1 , r , r + 1 , FILL , SHRINK , 0 , 0 ) ;
2023-12-07 11:43:26 -05:00
if ( _vapor_export ) {
2024-03-08 12:21:25 -05:00
t - > attach ( LBL ( " ADM BWF " ) , 1 , 2 , r , r + 1 , EXPAND , SHRINK , 0 , 0 ) ;
2023-12-07 11:43:26 -05:00
} else {
t - > attach ( _eps . the_combo ( ) , 1 , 2 , r , r + 1 , EXPAND | FILL , SHRINK , 0 , 0 ) ;
}
2022-10-18 10:53:04 -04:00
+ + r ;
t - > attach ( LBL ( " Export range: " ) , 0 , 1 , r , r + 1 , FILL , SHRINK , 0 , 0 ) ;
t - > attach ( _range_combo , 1 , 2 , r , r + 1 , EXPAND | FILL , SHRINK , 0 , 0 ) ;
+ + r ;
t - > attach ( LBL ( " After export: " ) , 0 , 1 , r , r + 1 , FILL , SHRINK , 0 , 0 ) ;
t - > attach ( _post_export_combo , 1 , 2 , r , r + 1 , EXPAND | FILL , SHRINK , 0 , 0 ) ;
+ + r ;
t - > attach ( _error_label , 0 , 2 , r , r + 1 , EXPAND | FILL , SHRINK , 0 , 0 ) ;
+ + r ;
t - > attach ( _progress_bar , 0 , 2 , r , r + 1 , EXPAND | FILL , SHRINK , 0 , 0 ) ;
/* clang-format on */
# undef LBL
2022-11-02 18:21:04 -04:00
_post_export_combo . append ( _ ( " Open the folder where files are exported " ) ) ;
_post_export_combo . append ( _ ( " Do nothing " ) ) ;
2022-10-18 10:53:04 -04:00
_post_export_combo . set_active ( 0 ) ;
get_vbox ( ) - > pack_start ( * t , false , false ) ;
_cancel_button = add_button ( Gtk : : Stock : : CANCEL , RESPONSE_CANCEL ) ;
2022-11-02 18:21:04 -04:00
_export_button = add_button ( _ ( " _Export " ) , RESPONSE_OK ) ;
2022-10-18 10:53:04 -04:00
_cancel_button - > signal_clicked ( ) . connect ( sigc : : mem_fun ( * this , & SimpleExportDialog : : close_dialog ) ) ;
_export_button - > signal_clicked ( ) . connect ( sigc : : mem_fun ( * this , & SimpleExportDialog : : start_export ) ) ;
_progress_bar . set_no_show_all ( true ) ;
_error_label . set_no_show_all ( true ) ;
_export_button - > set_sensitive ( false ) ;
_range_combo . set_sensitive ( false ) ;
t - > show_all ( ) ;
}
XMLNode &
SimpleExportDialog : : get_state ( ) const
{
XMLNode * node = new XMLNode ( X_ ( " QuickExport " ) ) ;
node - > set_property ( X_ ( " PresetUUID " ) , preset_uuid ( ) ) ;
node - > set_property ( X_ ( " PostExport " ) , _post_export_combo . get_active_row_number ( ) ) ;
return * node ;
}
void
SimpleExportDialog : : set_state ( XMLNode const & node )
{
int post_export ;
std : : string pset_uuid ;
if ( node . get_property ( X_ ( " PresetUUID " ) , pset_uuid ) ) {
set_preset ( pset_uuid ) ;
}
if ( node . get_property ( X_ ( " PostExport " ) , post_export ) ) {
_post_export_combo . set_active ( post_export ) ;
}
}
void
SimpleExportDialog : : set_session ( ARDOUR : : Session * s )
{
SimpleExport : : set_session ( s ) ;
ArdourDialog : : set_session ( s ) ;
2022-12-09 13:09:32 -05:00
_range_list - > clear ( ) ;
2022-11-04 16:57:53 -04:00
_preset_cfg_connection . disconnect ( ) ;
2022-10-18 10:53:04 -04:00
if ( ! s ) {
_export_button - > set_sensitive ( false ) ;
_range_combo . set_sensitive ( false ) ;
return ;
}
XMLNode * node = s - > extra_xml ( X_ ( " QuickExport " ) ) ;
if ( node ) {
set_state ( * node ) ;
}
_eps . set_manager ( _manager ) ;
if ( ! check_outputs ( ) ) {
2022-11-02 18:21:04 -04:00
set_error ( " Error: Session has no master bus " ) ;
2022-10-18 10:53:04 -04:00
return ;
}
2023-12-07 11:43:26 -05:00
if ( _vapor_export & & ( ! s - > surround_master ( ) | | ! s - > vapor_export_barrier ( ) ) ) {
set_error ( " Error: Session has no exportable surround master. " ) ;
return ;
}
2024-02-26 16:13:07 -05:00
if ( _vapor_export & & ( s - > surround_master ( ) - > surround_return ( ) - > total_n_channels ( ) > 128 ) ) {
2024-03-08 12:21:25 -05:00
set_error ( " Error: ADM BWF files cannot contain more than 128 channels. " ) ;
2024-02-26 16:13:07 -05:00
return ;
}
2022-10-18 10:53:04 -04:00
/* check range */
Location * srl ( s - > locations ( ) - > session_range_location ( ) ) ;
TimeSelection const & tsel ( _editor . get_selection ( ) . time ) ;
if ( ! tsel . empty ( ) ) {
2022-12-09 13:09:32 -05:00
TreeModel : : Row row = * _range_list - > append ( ) ;
row [ _range_cols . label ] = _ ( " Using time selection " ) ;
row [ _range_cols . start ] = tsel . start_sample ( ) ;
row [ _range_cols . end ] = tsel . end_sample ( ) ;
row [ _range_cols . name ] = string_compose ( _ ( " %1 (selection) " ) , SimpleExport : : _session - > snap_name ( ) ) ;
2022-10-18 10:53:04 -04:00
}
2022-12-09 13:09:32 -05:00
2022-10-18 10:53:04 -04:00
if ( srl ) {
2022-12-09 13:09:32 -05:00
TreeModel : : Row row = * _range_list - > append ( ) ;
row [ _range_cols . label ] = _ ( " Session start to session end " ) ; // same text as ExportVideoDialog::apply_state
row [ _range_cols . start ] = srl - > start_sample ( ) ;
row [ _range_cols . end ] = srl - > end_sample ( ) ;
row [ _range_cols . name ] = SimpleExport : : _session - > snap_name ( ) ;
}
2022-12-30 10:21:59 -05:00
struct LocationSorter {
bool operator ( ) ( Location const * a , Location const * b ) {
return a - > start_sample ( ) < b - > start_sample ( ) ;
}
} ;
Locations : : LocationList ll ( s - > locations ( ) - > list ( ) ) ;
ll . sort ( LocationSorter ( ) ) ;
for ( auto const & l : ll ) {
2022-12-09 13:09:32 -05:00
if ( l - > is_session_range ( ) | | ! l - > is_range_marker ( ) | | l - > name ( ) . empty ( ) ) {
continue ;
}
TreeModel : : Row row = * _range_list - > append ( ) ;
row [ _range_cols . label ] = l - > name ( ) ; // string_compose (_("Range '%1'"), l->name ());
row [ _range_cols . start ] = l - > start_sample ( ) ;
row [ _range_cols . end ] = l - > end_sample ( ) ;
row [ _range_cols . name ] = string_compose ( _ ( " %1 - %2 " ) , SimpleExport : : _session - > snap_name ( ) , l - > name ( ) ) ;
2022-10-18 10:53:04 -04:00
}
2022-12-09 13:09:32 -05:00
if ( _range_list - > children ( ) . size ( ) = = 0 ) {
2022-10-18 10:53:04 -04:00
set_error ( " Error: No valid range to export. Select a range or create session start/end markers " ) ;
return ;
}
_range_combo . set_active ( 0 ) ;
_range_combo . set_sensitive ( true ) ;
_export_button - > set_sensitive ( true ) ;
2022-11-04 16:57:53 -04:00
_preset_cfg_connection = _eps . CriticalSelectionChanged . connect ( sigc : : mem_fun ( * this , & SimpleExportDialog : : check_manager ) ) ;
}
void
SimpleExportDialog : : check_manager ( )
{
bool ok = _manager & & _manager - > preset ( ) ;
if ( ok & & _manager - > get_formats ( ) . empty ( ) ) {
ok = false ;
}
if ( ok ) {
/* check for NULL ExportFormatSpecPtr */
auto fms = _manager - > get_formats ( ) ;
for ( auto const & fm : fms ) {
if ( ! fm - > format ) {
ok = false ;
break ;
}
}
}
_export_button - > set_sensitive ( ok ) ;
2022-10-18 10:53:04 -04:00
}
void
SimpleExportDialog : : set_error ( std : : string const & err )
{
_export_button - > set_sensitive ( false ) ;
_range_combo . set_sensitive ( false ) ;
_error_label . set_text ( err ) ;
_error_label . show ( ) ;
}
void
SimpleExportDialog : : close_dialog ( )
{
if ( _status - > running ( ) ) {
_status - > abort ( ) ;
}
}
void
SimpleExportDialog : : start_export ( )
{
2022-12-09 13:09:32 -05:00
TreeModel : : iterator r = _range_combo . get_active ( ) ;
2023-12-07 11:43:26 -05:00
std : : string range_name = ( * r ) [ _range_cols . name ] ;
2022-12-09 13:09:32 -05:00
set_range ( ( * r ) [ _range_cols . start ] , ( * r ) [ _range_cols . end ] ) ;
2023-12-07 11:43:26 -05:00
SimpleExport : : set_name ( range_name ) ;
if ( _vapor_export ) {
if ( range_name . empty ( ) ) {
range_name = SimpleExport : : _session - > snap_name ( ) ;
}
2024-03-07 11:24:12 -05:00
samplepos_t rend = ( * r ) [ _range_cols . end ] ;
samplepos_t t24h ;
Timecode : : Time tc ( SimpleExport : : _session - > timecode_frames_per_second ( ) ) ;
tc . hours = 24 ;
SimpleExport : : _session - > timecode_to_sample ( tc , t24h , false /* use_offset */ , false /* use_subframes */ ) ;
if ( rend > = t24h ) {
hide ( ) ;
2024-03-11 10:14:44 -04:00
std : : string txt = _ ( " Error: ADM BWF files timecode cannot be past 24h. " ) ;
2024-03-07 11:24:12 -05:00
Gtk : : MessageDialog msg ( txt , false , Gtk : : MESSAGE_ERROR , Gtk : : BUTTONS_OK , true ) ;
msg . run ( ) ;
return ;
}
2024-03-11 15:52:30 -04:00
/* C_Ex_08 - prevent export that might fail on some systems - 23.976 vs. 24/1001 */
switch ( SimpleExport : : _session - > config . get_timecode_format ( ) ) {
case Timecode : : timecode_23976 :
case Timecode : : timecode_2997 :
case Timecode : : timecode_2997drop :
case Timecode : : timecode_2997000drop :
tc . hours = 23 ;
tc . minutes = 58 ;
tc . seconds = 35 ;
tc . frames = 0 ;
SimpleExport : : _session - > timecode_to_sample ( tc , t24h , false /* use_offset */ , false /* use_subframes */ ) ;
if ( rend > = t24h ) {
hide ( ) ;
2024-03-11 18:55:37 -04:00
std : : string txt = _ ( " Error: The file to be exported contains an illegal timecode value near the midnight boundary. Try moving the export-range earlier on the product timeline. " ) ;
2024-03-11 15:52:30 -04:00
Gtk : : MessageDialog msg ( txt , false , Gtk : : MESSAGE_ERROR , Gtk : : BUTTONS_OK , true ) ;
msg . run ( ) ;
return ;
}
break ;
default :
break ;
}
2023-12-07 11:43:26 -05:00
/* Ensure timespan exists, see also SimpleExport::run_export */
auto ts = _manager - > get_timespans ( ) ;
assert ( ts . size ( ) = = 1 ) ;
assert ( ts . front ( ) - > timespans - > size ( ) < 2 ) ;
if ( ts . front ( ) - > timespans - > size ( ) < 1 ) {
ExportTimespanPtr timespan = _handler - > add_timespan ( ) ;
ts . front ( ) - > timespans - > push_back ( timespan ) ;
}
/* https://professional.dolby.com/siteassets/content-creation/dolby-atmos/dolby_atmos_renderer_guide.pdf
* chapter 13.9 , page 155 suggests . wav .
* There may however already be a . wav file with the given name , so - adm . wav is used .
*/
std : : string vapor = Glib : : build_filename ( SimpleExport : : _session - > session_directory ( ) . export_path ( ) , range_name + " -adm.wav " ) ;
_manager - > get_timespans ( ) . front ( ) - > timespans - > front ( ) - > set_vapor ( vapor ) ;
}
2022-10-18 10:53:04 -04:00
SimpleExport : : _session - > add_extra_xml ( get_state ( ) ) ;
2022-11-02 18:21:04 -04:00
_cancel_button - > set_label ( _ ( " _Abort " ) ) ;
2022-10-18 10:53:04 -04:00
_export_button - > set_sensitive ( false ) ;
_progress_bar . set_fraction ( 0.0 ) ;
_progress_bar . show ( ) ;
#if 0
_eps . hide ( ) ;
_range_combo . hide ( ) ;
_post_export_combo , . hide ( ) ;
# endif
_progress_connection = Glib : : signal_timeout ( ) . connect ( sigc : : mem_fun ( * this , & SimpleExportDialog : : progress_timeout ) , 100 ) ;
if ( run_export ( ) ) {
hide ( ) ;
if ( _post_export_combo . get_active_row_number ( ) = = 0 ) {
PBD : : open_folder ( folder ( ) ) ;
}
if ( ! ARDOUR : : Profile - > get_mixbus ( ) ) {
2022-11-02 18:21:04 -04:00
NagScreen * ns = NagScreen : : maybe_nag ( _ ( " Export " ) ) ;
2022-10-18 10:53:04 -04:00
if ( ns ) {
ns - > nag ( ) ;
delete ns ;
}
}
} else if ( ! _status - > aborted ( ) ) {
hide ( ) ;
2022-11-02 18:21:04 -04:00
std : : string txt = _ ( " Export has been aborted due to an error! \n See the Log window for details. " ) ;
2022-10-18 10:53:04 -04:00
Gtk : : MessageDialog msg ( txt , false , Gtk : : MESSAGE_ERROR , Gtk : : BUTTONS_OK , true ) ;
msg . run ( ) ;
}
}
bool
SimpleExportDialog : : progress_timeout ( )
{
std : : string status_text ;
float progress = - 1 ;
switch ( _status - > active_job ) {
case ExportStatus : : Exporting :
status_text = string_compose ( _ ( " Exporting '%3' (timespan %1 of %2) " ) ,
_status - > timespan , _status - > total_timespans , _status - > timespan_name ) ;
progress = ( ( float ) _status - > processed_samples_current_timespan ) / _status - > total_samples_current_timespan ;
break ;
case ExportStatus : : Normalizing :
status_text = string_compose ( _ ( " Normalizing '%3' (timespan %1 of %2) " ) ,
_status - > timespan , _status - > total_timespans , _status - > timespan_name ) ;
progress = ( ( float ) _status - > current_postprocessing_cycle ) / _status - > total_postprocessing_cycles ;
break ;
case ExportStatus : : Encoding :
status_text = string_compose ( _ ( " Encoding '%3' (timespan %1 of %2) " ) ,
_status - > timespan , _status - > total_timespans , _status - > timespan_name ) ;
progress = ( ( float ) _status - > current_postprocessing_cycle ) / _status - > total_postprocessing_cycles ;
break ;
case ExportStatus : : Tagging :
status_text = string_compose ( _ ( " Tagging '%3' (timespan %1 of %2) " ) ,
_status - > timespan , _status - > total_timespans , _status - > timespan_name ) ;
case ExportStatus : : Uploading :
break ;
case ExportStatus : : Command :
2022-11-02 18:21:04 -04:00
status_text = string_compose ( _ ( " Running Post-Export Command for '%1' " ) , _status - > timespan_name ) ;
2022-10-18 10:53:04 -04:00
break ;
}
_progress_bar . set_text ( status_text ) ;
if ( progress > = 0 ) {
_progress_bar . set_fraction ( progress ) ;
} else {
_progress_bar . set_pulse_step ( .1 ) ;
_progress_bar . pulse ( ) ;
}
return true ;
}