ardour
export_handler.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008-2009 Paul Davis
3  Author: Sakari Bergen
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 
19 */
20 
21 #include "ardour/export_handler.h"
22 
23 #include <glib/gstdio.h>
24 #include <glibmm.h>
25 #include <glibmm/convert.h>
26 
27 #include "pbd/convert.h"
28 
30 #include "ardour/debug.h"
32 #include "ardour/export_timespan.h"
34 #include "ardour/export_status.h"
36 #include "ardour/export_filename.h"
38 #include "ardour/system_exec.h"
39 #include "pbd/openuri.h"
40 #include "pbd/basename.h"
42 
43 #include "i18n.h"
44 
45 using namespace std;
46 using namespace PBD;
47 
48 namespace ARDOUR
49 {
50 
51 /*** ExportElementFactory ***/
52 
53 ExportElementFactory::ExportElementFactory (Session & session) :
54  session (session)
55 {
56 
57 }
58 
60 {
61 
62 }
63 
66 {
68 }
69 
72 {
74 }
75 
78 {
80 }
81 
84 {
86 }
87 
90 {
91  return ExportFormatSpecPtr (new ExportFormatSpecification (*other));
92 }
93 
96 {
98 }
99 
102 {
103  return ExportFilenamePtr (new ExportFilename (*other));
104 }
105 
106 /*** ExportHandler ***/
107 
109  : ExportElementFactory (session)
110  , session (session)
111  , graph_builder (new ExportGraphBuilder (session))
112  , export_status (session.get_export_status ())
113  , normalizing (false)
114  , cue_tracknum (0)
115  , cue_indexnum (0)
116 {
117 }
118 
120 {
121  // TODO remove files that were written but not finished
122 }
123 
125 bool
127  ExportFormatSpecPtr format, ExportFilenamePtr filename,
128  BroadcastInfoPtr broadcast_info)
129 {
130  FileSpec spec (channel_config, format, filename, broadcast_info);
131  config_map.insert (make_pair (timespan, spec));
132 
133  return true;
134 }
135 
136 void
138 {
139  /* Count timespans */
140 
141  export_status->init();
142  std::set<ExportTimespanPtr> timespan_set;
143  for (ConfigMap::iterator it = config_map.begin(); it != config_map.end(); ++it) {
144  bool new_timespan = timespan_set.insert (it->first).second;
145  if (new_timespan) {
146  export_status->total_frames += it->first->get_length();
147  }
148  }
149  export_status->total_timespans = timespan_set.size();
150 
151  /* Start export */
152 
153  start_timespan ();
154 }
155 
156 void
158 {
159  export_status->timespan++;
160 
161  if (config_map.empty()) {
162  // freewheeling has to be stopped from outside the process cycle
163  export_status->running = false;
164  return;
165  }
166 
167  /* finish_timespan pops the config_map entry that has been done, so
168  this is the timespan to do this time
169  */
170  current_timespan = config_map.begin()->first;
171 
172  export_status->total_frames_current_timespan = current_timespan->get_length();
173  export_status->timespan_name = current_timespan->name();
174  export_status->processed_frames_current_timespan = 0;
175 
176  /* Register file configurations to graph builder */
177 
178  /* Here's the config_map entries that use this timespan */
180  graph_builder->reset ();
181  graph_builder->set_current_timespan (current_timespan);
183  for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second; ++it) {
184  // Filenames can be shared across timespans
185  FileSpec & spec = it->second;
186  spec.filename->set_timespan (it->first);
187  graph_builder->add_config (spec);
188  }
189 
190  /* start export */
191 
192  normalizing = false;
193  session.ProcessExport.connect_same_thread (process_connection, boost::bind (&ExportHandler::process, this, _1));
194  process_position = current_timespan->get_start();
196 }
197 
198 void
200 {
201  typedef std::map<std::string, int> ExtCountMap;
202 
203  ExtCountMap counts;
204  for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second; ++it) {
205  counts[it->second.format->extension()]++;
206  }
207 
208  bool duplicates_found = false;
209  for (ExtCountMap::iterator it = counts.begin(); it != counts.end(); ++it) {
210  if (it->second > 1) { duplicates_found = true; }
211  }
212 
213  // Set this always, as the filenames are shared...
214  for (ConfigMap::iterator it = timespan_bounds.first; it != timespan_bounds.second; ++it) {
215  it->second.filename->include_format_name = duplicates_found;
216  }
217 }
218 
219 int
221 {
222  if (!export_status->running) {
223  return 0;
224  } else if (normalizing) {
225  return process_normalize ();
226  } else {
227  return process_timespan (frames);
228  }
229 }
230 
231 int
233 {
234  /* update position */
235 
236  framecnt_t frames_to_read = 0;
237  framepos_t const end = current_timespan->get_end();
238 
239  bool const last_cycle = (process_position + frames >= end);
240 
241  if (last_cycle) {
242  frames_to_read = end - process_position;
243  export_status->stop = true;
244  } else {
245  frames_to_read = frames;
246  }
247 
248  process_position += frames_to_read;
249  export_status->processed_frames += frames_to_read;
250  export_status->processed_frames_current_timespan += frames_to_read;
251 
252  /* Do actual processing */
253  int ret = graph_builder->process (frames_to_read, last_cycle);
254 
255  /* Start normalizing if necessary */
256  if (last_cycle) {
257  normalizing = graph_builder->will_normalize();
258  if (normalizing) {
259  export_status->total_normalize_cycles = graph_builder->get_normalize_cycle_count();
260  export_status->current_normalize_cycle = 0;
261  } else {
262  finish_timespan ();
263  return 0;
264  }
265  }
266 
267  return ret;
268 }
269 
270 int
272 {
273  if (graph_builder->process_normalize ()) {
274  finish_timespan ();
275  export_status->normalizing = false;
276  } else {
277  export_status->normalizing = true;
278  }
279 
280  export_status->current_normalize_cycle++;
281 
282  return 0;
283 }
284 
285 void
286 ExportHandler::command_output(std::string output, size_t size)
287 {
288  std::cerr << "command: " << size << ", " << output << std::endl;
289  info << output << endmsg;
290 }
291 
292 void
294 {
295  while (config_map.begin() != timespan_bounds.second) {
296 
297  ExportFormatSpecPtr fmt = config_map.begin()->second.format;
298  std::string filename = config_map.begin()->second.filename->get_path(fmt);
299 
300  if (fmt->with_cue()) {
302  }
303 
304  if (fmt->with_toc()) {
306  }
307 
308  if (fmt->with_mp4chaps()) {
310  }
311 
312  if (fmt->tag()) {
314  }
315 
316  if (!fmt->command().empty()) {
317 
318 #if 0 // would be nicer with C++11 initialiser...
319  std::map<char, std::string> subs {
320  { 'f', filename },
321  { 'd', Glib::path_get_dirname(filename) + G_DIR_SEPARATOR },
322  { 'b', PBD::basename_nosuffix(filename) },
323  ...
324  };
325 #endif
326 
327  PBD::ScopedConnection command_connection;
328  std::map<char, std::string> subs;
329  subs.insert (std::pair<char, std::string> ('f', filename));
330  subs.insert (std::pair<char, std::string> ('d', Glib::path_get_dirname (filename) + G_DIR_SEPARATOR));
331  subs.insert (std::pair<char, std::string> ('b', PBD::basename_nosuffix (filename)));
332  subs.insert (std::pair<char, std::string> ('s', session.path ()));
333  subs.insert (std::pair<char, std::string> ('n', session.name ()));
334 
335  ARDOUR::SystemExec *se = new ARDOUR::SystemExec(fmt->command(), subs);
336  se->ReadStdout.connect_same_thread(command_connection, boost::bind(&ExportHandler::command_output, this, _1, _2));
337  if (se->start (2) == 0) {
338  // successfully started
339  while (se->is_running ()) {
340  // wait for system exec to terminate
341  Glib::usleep (1000);
342  }
343  } else {
344  error << "post-export hook failed! " << fmt->command() << endmsg;
345  }
346  delete (se);
347  }
348 
349  if (fmt->soundcloud_upload()) {
350  SoundcloudUploader *soundcloud_uploader = new SoundcloudUploader;
351  std::string token = soundcloud_uploader->Get_Auth_Token(soundcloud_username, soundcloud_password);
353  "uploading %1 - username=%2, password=%3, token=%4",
354  filename, soundcloud_username, soundcloud_password, token) );
355  std::string path = soundcloud_uploader->Upload (
356  filename,
357  PBD::basename_nosuffix(filename), // title
358  token,
361  this);
362 
363  if (path.length() != 0) {
364  info << string_compose ( _("File %1 uploaded to %2"), filename, path) << endmsg;
365  if (soundcloud_open_page) {
366  DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("opening %1", path) );
367  open_uri(path.c_str()); // open the soundcloud website to the new file
368  }
369  } else {
370  error << _("upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
371  }
372  delete soundcloud_uploader;
373  }
374  config_map.erase (config_map.begin());
375  }
376 
377  start_timespan ();
378 }
379 
380 /*** CD Marker stuff ***/
381 
384  return a->start() < b->start();
385  }
386 };
387 
388 void
390  std::string filename, CDMarkerFormat format)
391 {
392  string filepath = get_cd_marker_filename(filename, format);
393 
394  try {
395  void (ExportHandler::*header_func) (CDMarkerStatus &);
396  void (ExportHandler::*track_func) (CDMarkerStatus &);
397  void (ExportHandler::*index_func) (CDMarkerStatus &);
398 
399  switch (format) {
400  case CDMarkerTOC:
401  header_func = &ExportHandler::write_toc_header;
404  break;
405  case CDMarkerCUE:
406  header_func = &ExportHandler::write_cue_header;
409  break;
410  case MP4Chaps:
411  header_func = &ExportHandler::write_mp4ch_header;
414  break;
415  default:
416  return;
417  }
418 
419  CDMarkerStatus status (filepath, timespan, file_format, filename);
420 
421  if (!status.out) {
422  error << string_compose(_("Editor: cannot open \"%1\" as export file for CD marker file"), filepath) << endmsg;
423  return;
424  }
425 
426  (this->*header_func) (status);
427 
428  /* Get locations and sort */
429 
430  Locations::LocationList const & locations (session.locations()->list());
431  Locations::LocationList::const_iterator i;
433 
434  for (i = locations.begin(); i != locations.end(); ++i) {
435  if ((*i)->start() >= timespan->get_start() && (*i)->end() <= timespan->get_end() && (*i)->is_cd_marker() && !(*i)->is_session_range()) {
436  temp.push_back (*i);
437  }
438  }
439 
440  if (temp.empty()) {
441  // TODO One index marker for whole thing
442  return;
443  }
444 
446  temp.sort (cmp);
447  Locations::LocationList::const_iterator nexti;
448 
449  /* Start actual marker stuff */
450 
451  framepos_t last_end_time = timespan->get_start();
452  status.track_position = 0;
453 
454  for (i = temp.begin(); i != temp.end(); ++i) {
455 
456  status.marker = *i;
457 
458  if ((*i)->start() < last_end_time) {
459  if ((*i)->is_mark()) {
460  /* Index within track */
461 
462  status.index_position = (*i)->start() - timespan->get_start();
463  (this->*index_func) (status);
464  }
465 
466  continue;
467  }
468 
469  /* A track, defined by a cd range marker or a cd location marker outside of a cd range */
470 
471  status.track_position = last_end_time - timespan->get_start();
472  status.track_start_frame = (*i)->start() - timespan->get_start(); // everything before this is the pregap
473  status.track_duration = 0;
474 
475  if ((*i)->is_mark()) {
476  // a mark track location needs to look ahead to the next marker's start to determine length
477  nexti = i;
478  ++nexti;
479 
480  if (nexti != temp.end()) {
481  status.track_duration = (*nexti)->start() - last_end_time;
482 
483  last_end_time = (*nexti)->start();
484  } else {
485  // this was the last marker, use timespan end
486  status.track_duration = timespan->get_end() - last_end_time;
487 
488  last_end_time = timespan->get_end();
489  }
490  } else {
491  // range
492  status.track_duration = (*i)->end() - last_end_time;
493 
494  last_end_time = (*i)->end();
495  }
496 
497  (this->*track_func) (status);
498  }
499 
500  } catch (std::exception& e) {
501  error << string_compose (_("an error occured while writing a TOC/CUE file: %1"), e.what()) << endmsg;
502  ::g_unlink (filepath.c_str());
503  } catch (Glib::Exception& e) {
504  error << string_compose (_("an error occured while writing a TOC/CUE file: %1"), e.what()) << endmsg;
505  ::g_unlink (filepath.c_str());
506  }
507 }
508 
509 string
511 {
512  /* do not strip file suffix because there may be more than one format,
513  and we do not want the CD marker file from one format to overwrite
514  another (e.g. foo.wav.cue > foo.aiff.cue)
515  */
516 
517  switch (format) {
518  case CDMarkerTOC:
519  return filename + ".toc";
520  case CDMarkerCUE:
521  return filename + ".cue";
522  case MP4Chaps:
523  {
524  unsigned lastdot = filename.find_last_of('.');
525  return filename.substr(0,lastdot) + ".chapters.txt";
526  }
527  default:
528  return filename + ".marker"; // Should not be reached when actually creating a file
529  }
530 }
531 
532 void
534 {
535  string title = status.timespan->name().compare ("Session") ? status.timespan->name() : (string) session.name();
536 
537  // Album metadata
538  string barcode = SessionMetadata::Metadata()->barcode();
539  string album_artist = SessionMetadata::Metadata()->album_artist();
540  string album_title = SessionMetadata::Metadata()->album();
541 
542  status.out << "REM Cue file generated by " << PROGRAM_NAME << endl;
543 
544  if (barcode != "")
545  status.out << "CATALOG " << barcode << endl;
546 
547  if (album_artist != "")
548  status.out << "PERFORMER " << cue_escape_cdtext (album_artist) << endl;
549 
550  if (album_title != "")
551  title = album_title;
552 
553  status.out << "TITLE " << cue_escape_cdtext (title) << endl;
554 
555  /* The original cue sheet spec mentions five file types
556  WAVE, AIFF,
557  BINARY = "header-less" audio (44.1 kHz, 16 Bit, little endian),
558  MOTOROLA = "header-less" audio (44.1 kHz, 16 Bit, big endian),
559  and MP3
560 
561  We try to use these file types whenever appropriate and
562  default to our own names otherwise.
563  */
564  status.out << "FILE \"" << Glib::path_get_basename(status.filename) << "\" ";
565  if (!status.format->format_name().compare ("WAV") || !status.format->format_name().compare ("BWF")) {
566  status.out << "WAVE";
567  } else if (status.format->format_id() == ExportFormatBase::F_RAW &&
568  status.format->sample_format() == ExportFormatBase::SF_16 &&
569  status.format->sample_rate() == ExportFormatBase::SR_44_1) {
570  // Format is RAW 16bit 44.1kHz
571  if (status.format->endianness() == ExportFormatBase::E_Little) {
572  status.out << "BINARY";
573  } else {
574  status.out << "MOTOROLA";
575  }
576  } else {
577  // no special case for AIFF format it's name is already "AIFF"
578  status.out << status.format->format_name();
579  }
580  status.out << endl;
581 }
582 
583 void
585 {
586  string title = status.timespan->name().compare ("Session") ? status.timespan->name() : (string) session.name();
587 
588  // Album metadata
589  string barcode = SessionMetadata::Metadata()->barcode();
590  string album_artist = SessionMetadata::Metadata()->album_artist();
591  string album_title = SessionMetadata::Metadata()->album();
592 
593  if (barcode != "")
594  status.out << "CATALOG " << barcode << endl;
595 
596  if (album_title != "")
597  title = album_title;
598 
599  status.out << "CD_DA" << endl;
600  status.out << "CD_TEXT {" << endl << " LANGUAGE_MAP {" << endl << " 0 : EN" << endl << " }" << endl;
601  status.out << " LANGUAGE 0 {" << endl << " TITLE " << toc_escape_cdtext (title) << endl ;
602  status.out << " PERFORMER \"" << toc_escape_cdtext (album_artist) << "\"" << endl;
603  status.out << " }" << endl << "}" << endl;
604 }
605 
606 void
608 {
609  status.out << "00:00:00.000 Intro" << endl;
610 }
611 
612 void
614 {
615  gchar buf[18];
616 
617  snprintf (buf, sizeof(buf), " TRACK %02d AUDIO", status.track_number);
618  status.out << buf << endl;
619 
620  status.out << " FLAGS" ;
621  if (status.marker->cd_info.find("scms") != status.marker->cd_info.end()) {
622  status.out << " SCMS ";
623  } else {
624  status.out << " DCP ";
625  }
626 
627  if (status.marker->cd_info.find("preemph") != status.marker->cd_info.end()) {
628  status.out << " PRE";
629  }
630  status.out << endl;
631 
632  if (status.marker->cd_info.find("isrc") != status.marker->cd_info.end()) {
633  status.out << " ISRC " << status.marker->cd_info["isrc"] << endl;
634  }
635 
636  if (status.marker->name() != "") {
637  status.out << " TITLE " << cue_escape_cdtext (status.marker->name()) << endl;
638  }
639 
640  if (status.marker->cd_info.find("performer") != status.marker->cd_info.end()) {
641  status.out << " PERFORMER " << cue_escape_cdtext (status.marker->cd_info["performer"]) << endl;
642  }
643 
644  if (status.marker->cd_info.find("composer") != status.marker->cd_info.end()) {
645  status.out << " SONGWRITER " << cue_escape_cdtext (status.marker->cd_info["composer"]) << endl;
646  }
647 
648  if (status.track_position != status.track_start_frame) {
650  status.out << " INDEX 00" << buf << endl;
651  }
652 
654  status.out << " INDEX 01" << buf << endl;
655 
656  status.index_number = 2;
657  status.track_number++;
658 }
659 
660 void
662 {
663  gchar buf[18];
664 
665  status.out << endl << "TRACK AUDIO" << endl;
666 
667  if (status.marker->cd_info.find("scms") != status.marker->cd_info.end()) {
668  status.out << "NO ";
669  }
670  status.out << "COPY" << endl;
671 
672  if (status.marker->cd_info.find("preemph") != status.marker->cd_info.end()) {
673  status.out << "PRE_EMPHASIS" << endl;
674  } else {
675  status.out << "NO PRE_EMPHASIS" << endl;
676  }
677 
678  if (status.marker->cd_info.find("isrc") != status.marker->cd_info.end()) {
679  status.out << "ISRC \"" << status.marker->cd_info["isrc"] << "\"" << endl;
680  }
681 
682  status.out << "CD_TEXT {" << endl << " LANGUAGE 0 {" << endl;
683  status.out << " TITLE " << toc_escape_cdtext (status.marker->name()) << endl;
684 
685  status.out << " PERFORMER ";
686  if (status.marker->cd_info.find("performer") != status.marker->cd_info.end()) {
687  status.out << toc_escape_cdtext (status.marker->cd_info["performer"]) << endl;
688  } else {
689  status.out << "\"\"" << endl;
690  }
691 
692  if (status.marker->cd_info.find("composer") != status.marker->cd_info.end()) {
693  status.out << " SONGWRITER " << toc_escape_cdtext (status.marker->cd_info["composer"]) << endl;
694  }
695 
696  if (status.marker->cd_info.find("isrc") != status.marker->cd_info.end()) {
697  status.out << " ISRC \"";
698  status.out << status.marker->cd_info["isrc"].substr(0,2) << "-";
699  status.out << status.marker->cd_info["isrc"].substr(2,3) << "-";
700  status.out << status.marker->cd_info["isrc"].substr(5,2) << "-";
701  status.out << status.marker->cd_info["isrc"].substr(7,5) << "\"" << endl;
702  }
703 
704  status.out << " }" << endl << "}" << endl;
705 
707  status.out << "FILE " << toc_escape_filename (status.filename) << ' ' << buf;
708 
710  status.out << buf << endl;
711 
713  status.out << "START" << buf << endl;
714 }
715 
717 {
718  gchar buf[18];
719 
721  status.out << buf << " " << status.marker->name() << endl;
722 }
723 
724 void
726 {
727  gchar buf[18];
728 
729  snprintf (buf, sizeof(buf), " INDEX %02d", cue_indexnum);
730  status.out << buf;
732  status.out << buf << endl;
733 
734  cue_indexnum++;
735 }
736 
737 void
739 {
740  gchar buf[18];
741 
743  status.out << "INDEX" << buf << endl;
744 }
745 
746 void
748 {
749 }
750 
751 void
753 {
754  framecnt_t remainder;
756  int mins, secs, frames;
757 
758  mins = when / (60 * fr);
759  remainder = when - (mins * 60 * fr);
760  secs = remainder / fr;
761  remainder -= secs * fr;
762  frames = remainder / (fr / 75);
763  sprintf (buf, " %02d:%02d:%02d", mins, secs, frames);
764 }
765 
766 void
768 {
769  framecnt_t remainder;
771  int hours, mins, secs, msecs;
772 
773  hours = when / (3600 * fr);
774  remainder = when - (hours * 3600 * fr);
775  mins = remainder / (60 * fr);
776  remainder -= mins * 60 * fr;
777  secs = remainder / fr;
778  remainder -= secs * fr;
779  msecs = (remainder * 1000) / fr;
780  sprintf (buf, "%02d:%02d:%02d.%03d", hours, mins, secs, msecs);
781 }
782 
783 std::string
784 ExportHandler::toc_escape_cdtext (const std::string& txt)
785 {
786  Glib::ustring check (txt);
787  std::string out;
788  std::string latin1_txt;
789  char buf[5];
790 
791  try {
792  latin1_txt = Glib::convert (txt, "ISO-8859-1", "UTF-8");
793  } catch (Glib::ConvertError& err) {
794  throw Glib::ConvertError (err.code(), string_compose (_("Cannot convert %1 to Latin-1 text"), txt));
795  }
796 
797  out = '"';
798 
799  for (std::string::const_iterator c = latin1_txt.begin(); c != latin1_txt.end(); ++c) {
800 
801  if ((*c) == '"') {
802  out += "\\\"";
803  } else if ((*c) == '\\') {
804  out += "\\134";
805  } else if (isprint (*c)) {
806  out += *c;
807  } else {
808  snprintf (buf, sizeof (buf), "\\%03o", (int) (unsigned char) *c);
809  out += buf;
810  }
811  }
812 
813  out += '"';
814 
815  return out;
816 }
817 
818 std::string
819 ExportHandler::toc_escape_filename (const std::string& txt)
820 {
821  std::string out;
822 
823  out = '"';
824 
825  // We iterate byte-wise not character-wise over a UTF-8 string here,
826  // because we only want to translate backslashes and double quotes
827  for (std::string::const_iterator c = txt.begin(); c != txt.end(); ++c) {
828 
829  if (*c == '"') {
830  out += "\\\"";
831  } else if (*c == '\\') {
832  out += "\\134";
833  } else {
834  out += *c;
835  }
836  }
837 
838  out += '"';
839 
840  return out;
841 }
842 
843 std::string
844 ExportHandler::cue_escape_cdtext (const std::string& txt)
845 {
846  std::string latin1_txt;
847  std::string out;
848 
849  try {
850  latin1_txt = Glib::convert (txt, "ISO-8859-1", "UTF-8");
851  } catch (Glib::ConvertError& err) {
852  throw Glib::ConvertError (err.code(), string_compose (_("Cannot convert %1 to Latin-1 text"), txt));
853  }
854 
855  // does not do much mor than UTF-8 to Latin1 translation yet, but
856  // that may have to change if cue parsers in burning programs change
857  out = '"' + latin1_txt + '"';
858 
859  return out;
860 }
861 
862 } // namespace ARDOUR
void write_toc_header(CDMarkerStatus &status)
bool add_export_config(ExportTimespanPtr timespan, ExportChannelConfigPtr channel_config, ExportFormatSpecPtr format, ExportFilenamePtr filename, BroadcastInfoPtr broadcast_info)
framecnt_t nominal_frame_rate() const
Definition: session.h:367
ExportTimespanPtr current_timespan
ExportFormatSpecPtr add_format_copy(ExportFormatSpecPtr other)
ExportChannelConfigPtr add_channel_config()
ExportTimespanPtr add_timespan()
int start_audio_export(framepos_t position)
int process(framecnt_t frames)
std::list< Location * > LocationList
Definition: location.h:167
std::string toc_escape_filename(const std::string &)
std::string soundcloud_username
ComparableSharedPtr< ExportTimespan > ExportTimespanPtr
void write_index_info_toc(CDMarkerStatus &status)
std::string name() const
Definition: session.h:166
PBD::ScopedConnection process_connection
Definition: Beats.hpp:239
void handle_duplicate_format_extensions()
LIBPBD_API Transmitter error
bool operator()(Location *a, Location *b)
std::ostream & endmsg(std::ostream &ostr)
Definition: transmitter.h:71
void write_index_info_mp4ch(CDMarkerStatus &status)
framecnt_t frame_rate() const
Definition: session.h:365
boost::shared_ptr< ExportStatus > get_export_status()
void write_track_info_cue(CDMarkerStatus &status)
void command_output(std::string output, size_t size)
Locations * locations()
Definition: session.h:382
#define _(Text)
Definition: i18n.h:11
std::string barcode() const
ExportStatusPtr export_status
int64_t framecnt_t
Definition: types.h:76
void write_cue_header(CDMarkerStatus &status)
std::map< std::string, std::string > cd_info
Definition: location.h:136
void write_track_info_toc(CDMarkerStatus &status)
void write_track_info_mp4ch(CDMarkerStatus &status)
const LocationList & list()
Definition: location.h:172
static bool tag_file(std::string const &filename, SessionMetadata const &metadata)
Definition: amp.h:29
void write_mp4ch_header(CDMarkerStatus &status)
std::string path() const
Definition: session.h:165
std::string cue_escape_cdtext(const std::string &txt)
void export_cd_marker_file(ExportTimespanPtr timespan, ExportFormatSpecPtr file_format, std::string filename, CDMarkerFormat format)
std::string album_artist() const
void frames_to_cd_frames_string(char *buf, framepos_t when)
#define DEBUG_TRACE(bits, str)
Definition: debug.h:55
int64_t framepos_t
Definition: types.h:66
boost::shared_ptr< ExportFilename > ExportFilenamePtr
static SessionMetadata * Metadata()
LIBPBD_API Transmitter info
LIBPBD_API bool open_uri(const char *)
Definition: openuri.cc:41
std::string soundcloud_password
PBD::Signal1< int, framecnt_t > ProcessExport
Definition: session.h:621
std::string Get_Auth_Token(std::string username, std::string password)
boost::shared_ptr< ExportChannelConfiguration > ExportChannelConfigPtr
LIBPBD_API Glib::ustring basename_nosuffix(Glib::ustring)
ExportFilenamePtr add_filename_copy(ExportFilenamePtr other)
int start(int stderr_mode=1)
Definition: system_exec.h:38
const std::string & name() const
Definition: location.h:81
Definition: xml++.h:95
std::string album() const
void frames_to_chapter_marks_string(char *buf, framepos_t when)
Definition: debug.h:30
int process_timespan(framecnt_t frames)
ExportFilenamePtr add_filename()
PBD::Signal2< void, std::string, size_t > ReadStdout
Definition: system_exec.h:176
framepos_t start() const
Definition: location.h:71
boost::shared_ptr< ExportGraphBuilder > graph_builder
std::string Upload(std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller)
std::string get_cd_marker_filename(std::string filename, CDMarkerFormat format)
void write_index_info_cue(CDMarkerStatus &status)
std::string toc_escape_cdtext(const std::string &)
LIBARDOUR_API uint64_t Soundcloud
Definition: debug.cc:66
ExportHandler(Session &session)
TimespanBounds timespan_bounds
CDMarkerFormat
Definition: types.h:468
boost::shared_ptr< ExportFormatSpecification > ExportFormatSpecPtr
std::string string_compose(const std::string &fmt, const T1 &o1)
Definition: compose.h:208
ExportFormatSpecPtr add_format()