Merge branch 'export-dialog' into cairocanvas
authorColin Fletcher <colin.m.fletcher@googlemail.com>
Mon, 19 May 2014 19:54:36 +0000 (20:54 +0100)
committerColin Fletcher <colin.m.fletcher@googlemail.com>
Mon, 19 May 2014 19:54:36 +0000 (20:54 +0100)
Fix merge conflicts in:
gtk2_ardour/export_range_markers_dialog.cc
gtk2_ardour/wscript
libs/ardour/ardour/export_handler.h
libs/ardour/system_exec.cc
libs/pbd/pbd/system_exec.h
libs/pbd/system_exec.cc

29 files changed:
export/Soundcloud upload (lossless).format [new file with mode: 0644]
export/Soundcloud upload (lossy).format [new file with mode: 0644]
gtk2_ardour/export_channel_selector.cc
gtk2_ardour/export_channel_selector.h
gtk2_ardour/export_dialog.cc
gtk2_ardour/export_dialog.h
gtk2_ardour/export_format_dialog.cc
gtk2_ardour/export_format_dialog.h
gtk2_ardour/export_range_markers_dialog.cc
gtk2_ardour/export_range_markers_dialog.h
gtk2_ardour/export_timespan_selector.cc
gtk2_ardour/export_timespan_selector.h
gtk2_ardour/icons/soundcloud.png [new file with mode: 0644]
gtk2_ardour/soundcloud_export_selector.cc [new file with mode: 0644]
gtk2_ardour/soundcloud_export_selector.h [new file with mode: 0644]
gtk2_ardour/wscript
libs/ardour/ardour/export_format_manager.h
libs/ardour/ardour/export_format_specification.h
libs/ardour/ardour/export_handler.h
libs/ardour/ardour/soundcloud_upload.h [new file with mode: 0644]
libs/ardour/ardour/system_exec.h
libs/ardour/export_format_manager.cc
libs/ardour/export_format_specification.cc
libs/ardour/export_handler.cc
libs/ardour/soundcloud_upload.cc [new file with mode: 0644]
libs/ardour/system_exec.cc
libs/ardour/wscript
libs/pbd/pbd/system_exec.h
libs/pbd/system_exec.cc

diff --git a/export/Soundcloud upload (lossless).format b/export/Soundcloud upload (lossless).format
new file mode 100644 (file)
index 0000000..eb966af
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ExportFormatSpecification name="Soundcloud upload (lossless)" id="14f5b271-86b3-48c6-9ee2-133f46fe06c1" with-cue="false" with-toc="false" upload="true" command="">
+  <Encoding id="F_FLAC" type="T_Sndfile" extension="flac" name="FLAC" has-sample-format="true" channel-limit="8"/>
+  <SampleRate rate="1"/>
+  <SRCQuality quality="SRC_SincBest"/>
+  <EncodingOptions>
+    <Option name="sample-format" value="SF_16"/>
+    <Option name="dithering" value="D_Shaped"/>
+    <Option name="tag-metadata" value="true"/>
+    <Option name="tag-support" value="true"/>
+    <Option name="broadcast-info" value="false"/>
+  </EncodingOptions>
+  <Processing>
+    <Normalize enabled="false" target="1"/>
+    <Silence>
+      <Start>
+        <Trim enabled="false"/>
+        <Add enabled="false">
+          <Duration format="Timecode" hours="0" minutes="0" seconds="0" frames="0"/>
+        </Add>
+      </Start>
+      <End>
+        <Trim enabled="false"/>
+        <Add enabled="false">
+          <Duration format="Timecode" hours="0" minutes="0" seconds="0" frames="0"/>
+        </Add>
+      </End>
+    </Silence>
+  </Processing>
+</ExportFormatSpecification>
diff --git a/export/Soundcloud upload (lossy).format b/export/Soundcloud upload (lossy).format
new file mode 100644 (file)
index 0000000..d948b1d
--- /dev/null
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ExportFormatSpecification name="Soundcloud upload (lossy)" id="a42eb2fe-2470-4aa9-8027-798ba625592a" with-cue="false" with-toc="false" upload="true" command="">
+  <Encoding id="F_Ogg" type="T_Sndfile" extension="ogg" name="Ogg Vorbis" has-sample-format="false" channel-limit="256"/>
+  <SampleRate rate="44100"/>
+  <SRCQuality quality="SRC_SincBest"/>
+  <EncodingOptions>
+    <Option name="sample-format" value="SF_Vorbis"/>
+    <Option name="dithering" value="D_Shaped"/>
+    <Option name="tag-metadata" value="true"/>
+    <Option name="tag-support" value="true"/>
+    <Option name="broadcast-info" value="false"/>
+  </EncodingOptions>
+  <Processing>
+    <Normalize enabled="false" target="1"/>
+    <Silence>
+      <Start>
+        <Trim enabled="false"/>
+        <Add enabled="false">
+          <Duration format="Timecode" hours="0" minutes="0" seconds="0" frames="0"/>
+        </Add>
+      </Start>
+      <End>
+        <Trim enabled="false"/>
+        <Add enabled="false">
+          <Duration format="Timecode" hours="0" minutes="0" seconds="0" frames="0"/>
+        </Add>
+      </End>
+    </Silence>
+  </Processing>
+</ExportFormatSpecification>
index 10e3135b53b7b8f7de3774e7f99cdecef1c9416f..20155471af11c5d87edab625d15c65066a238045 100644 (file)
@@ -458,15 +458,15 @@ RegionExportChannelSelector::RegionExportChannelSelector (ARDOUR::Session * _ses
 
        raw_button.set_label (string_compose (_("Region contents without fades nor region gain (channels: %1)"), region_chans));
        raw_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
-       vbox.pack_start (raw_button);
+       vbox.pack_start (raw_button, false, false);
 
        fades_button.set_label (string_compose (_("Region contents with fades and region gain (channels: %1)"), region_chans));
        fades_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
-       vbox.pack_start (fades_button);
+       vbox.pack_start (fades_button, false, false);
 
        processed_button.set_label (string_compose (_("Track output (channels: %1)"), track_chans));
        processed_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
-       vbox.pack_start (processed_button);
+       vbox.pack_start (processed_button, false, false);
 
        sync_with_manager();
        vbox.show_all_children ();
@@ -541,7 +541,7 @@ TrackExportChannelSelector::TrackExportChannelSelector (ARDOUR::Session * sessio
        // Options
        options_box.pack_start(region_contents_button);
        options_box.pack_start(track_output_button);
-       main_layout.pack_start(options_box);
+       main_layout.pack_start(options_box, false, false);
 
        // Track scroller
        track_scroller.add (track_view);
index bc165273c73f1b66e81d32a6303253bae6348e68..3dbb9b8265494b553c1db24675dde6cc26a0e706 100644 (file)
@@ -126,7 +126,7 @@ class PortExportChannelSelector : public ExportChannelSelector
                typedef Gtk::TreeModelColumn<Glib::RefPtr<Gtk::ListStore> > ComboCol;
                ComboCol                             port_list_col;
 
-               /* Channel struct, that represents the selected port and it's name */
+               /* Channel struct, that represents the selected port and its name */
 
                struct Channel {
                  public:
index 6351c512c19cc1d0dee757e3fd72e50a0cfa9396..76b62cde57fd2b3e397c2e933afe89e63976e55f 100644 (file)
@@ -140,69 +140,27 @@ ExportDialog::init ()
        progress_widget.hide_all();
 }
 
-void
-ExportDialog::expanded_changed ()
-{
-       set_resizable(advanced->get_expanded());
-}
-
 void
 ExportDialog::init_gui ()
 {
        Gtk::Alignment * preset_align = Gtk::manage (new Gtk::Alignment());
        preset_align->add (*preset_selector);
        preset_align->set_padding (0, 12, 0, 0);
-       get_vbox()->pack_start (*preset_align, false, false, 0);
-
-       Gtk::VPaned * advanced_paned = Gtk::manage (new Gtk::VPaned());
-
-       Gtk::VBox* timespan_vbox = Gtk::manage (new Gtk::VBox());
-       timespan_vbox->set_spacing (12);
-       timespan_vbox->set_border_width (12);
-
-       Gtk::Alignment * timespan_align = Gtk::manage (new Gtk::Alignment());
-       timespan_label = Gtk::manage (new Gtk::Label (_("Time Span"), Gtk::ALIGN_LEFT));
-       timespan_align->add (*timespan_selector);
-       timespan_align->set_padding (0, 0, 18, 0);
-       timespan_vbox->pack_start (*timespan_label, false, false, 0);
-       timespan_vbox->pack_start (*timespan_align, true, true, 0);
-       advanced_paned->pack1(*timespan_vbox, true, false);
-
-       Gtk::VBox* channels_vbox = Gtk::manage (new Gtk::VBox());
-       channels_vbox->set_spacing (12);
-       channels_vbox->set_border_width (12);
-
-       Gtk::Alignment * channels_align = Gtk::manage (new Gtk::Alignment());
-       channels_label = Gtk::manage (new Gtk::Label (_("Channels"), Gtk::ALIGN_LEFT));
-       channels_align->add (*channel_selector);
-       channels_align->set_padding (0, 12, 18, 0);
-       channels_vbox->pack_start (*channels_label, false, false, 0);
-       channels_vbox->pack_start (*channels_align, true, true, 0);
-       advanced_paned->pack2(*channels_vbox, channel_selector_is_expandable(), false);
-
-       get_vbox()->pack_start (*file_notebook, false, false, 0);
-       get_vbox()->pack_start (warning_widget, false, false, 0);
-       get_vbox()->pack_start (progress_widget, false, false, 0);
-
-       advanced = Gtk::manage (new Gtk::Expander (_("Time span and channel options")));
-       advanced->property_expanded().signal_changed().connect(
-               sigc::mem_fun(*this, &ExportDialog::expanded_changed));
-       advanced->add (*advanced_paned);
-
-       if (channel_selector_is_expandable()) {
-               advanced_sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_VERTICAL);
-               advanced_sizegroup->add_widget(*timespan_selector);
-               advanced_sizegroup->add_widget(*channel_selector);
-       }
 
-       get_vbox()->pack_start (*advanced, true, true);
+       Gtk::VBox * file_format_selector = Gtk::manage (new Gtk::VBox());
+       file_format_selector->set_homogeneous (false);
+       file_format_selector->pack_start (*preset_align, false, false, 0);
+       file_format_selector->pack_start (*file_notebook, false, false, 0);
+       file_format_selector->pack_start (*soundcloud_selector, false, false, 0);
 
-       Pango::AttrList bold;
-       Pango::Attribute b = Pango::Attribute::create_attr_weight (Pango::WEIGHT_BOLD);
-       bold.insert (b);
+       export_notebook.append_page (*file_format_selector, _("File format"));
+       export_notebook.append_page (*timespan_selector, _("Time Span"));
+       export_notebook.append_page (*channel_selector, _("Channels"));
+       
+       get_vbox()->pack_start (export_notebook, true, true, 0);
+       get_vbox()->pack_end   (warning_widget, false, false, 0);
+       get_vbox()->pack_end   (progress_widget, false, false, 0);
 
-       timespan_label->set_attributes (bold);
-       channels_label->set_attributes (bold);
 }
 
 void
@@ -211,6 +169,7 @@ ExportDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
        channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -300,11 +259,34 @@ ExportDialog::show_conflicting_files ()
        dialog.run();
 }
 
+void
+ExportDialog::soundcloud_upload_progress(double total, double now, std::string title)
+{
+       soundcloud_selector->do_progress_callback(total, now, title);
+
+}
+
 void
 ExportDialog::do_export ()
 {
        try {
                profile_manager->prepare_for_export ();
+               handler->upload_username = soundcloud_selector->username();
+               handler->upload_password = soundcloud_selector->password();
+               handler->upload_public   = soundcloud_selector->upload_public();
+               handler->upload_open     = soundcloud_selector->upload_open();
+
+               handler->SoundcloudProgress.connect_same_thread(
+                               *this, 
+                               boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3)
+                               );
+#if 0
+               handler->SoundcloudProgress.connect(
+                               *this, invalidator (*this),
+                               boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3),
+                               gui_context()
+                               );
+#endif
                handler->do_export ();
                show_progress ();
        } catch(std::exception & e) {
@@ -418,6 +400,7 @@ ExportRangeDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, range_id));
        channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -431,6 +414,7 @@ ExportSelectionDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, X_("selection")));
        channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -444,8 +428,7 @@ void
 ExportRegionDialog::init_gui ()
 {
        ExportDialog::init_gui ();
-
-       channels_label->set_text (_("Source"));
+       export_notebook.set_tab_label_text(*export_notebook.get_nth_page(2), _("Source"));
 }
 
 void
@@ -456,6 +439,7 @@ ExportRegionDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, loc_id));
        channel_selector.reset (new RegionExportChannelSelector (_session, profile_manager, region, track));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
 
@@ -471,5 +455,6 @@ StemExportDialog::init_components ()
        preset_selector.reset (new ExportPresetSelector ());
        timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
        channel_selector.reset (new TrackExportChannelSelector (_session, profile_manager));
+       soundcloud_selector.reset (new SoundcloudExportSelector ());
        file_notebook.reset (new ExportFileNotebook ());
 }
index 756a3e7b53a4fda2f5b0288262157b7e894d445c..315780750e6c9813c066502dcdcd5f68465d2b2d 100644 (file)
@@ -32,6 +32,7 @@
 #include "export_file_notebook.h"
 #include "export_preset_selector.h"
 #include "ardour_dialog.h"
+#include "soundcloud_export_selector.h"
 
 #include <gtkmm.h>
 
@@ -43,7 +44,8 @@ namespace ARDOUR {
 class ExportTimespanSelector;
 class ExportChannelSelector;
 
-class ExportDialog : public ArdourDialog {
+class ExportDialog : public ArdourDialog, public PBD::ScopedConnectionList 
+{
 
   public:
 
@@ -75,26 +77,22 @@ class ExportDialog : public ArdourDialog {
        // Must initialize all the shared_ptrs below
        virtual void init_components ();
 
-       // Override if the channel selector should not be grown
-       virtual bool channel_selector_is_expandable() { return true; }
-
        boost::scoped_ptr<ExportPresetSelector>   preset_selector;
        boost::scoped_ptr<ExportTimespanSelector> timespan_selector;
        boost::scoped_ptr<ExportChannelSelector>  channel_selector;
        boost::scoped_ptr<ExportFileNotebook>     file_notebook;
+       boost::scoped_ptr<SoundcloudExportSelector> soundcloud_selector;
 
        Gtk::VBox                                 warning_widget;
        Gtk::VBox                                 progress_widget;
 
-       Gtk::Label *                              timespan_label;
-       Gtk::Label *                              channels_label;
+       /*** GUI components ***/
+       Gtk::Notebook export_notebook;
 
   private:
 
        void init ();
 
-       void expanded_changed();
-
        void notify_errors (bool force = false);
        void close_dialog ();
 
@@ -112,10 +110,7 @@ class ExportDialog : public ArdourDialog {
        PublicEditor &  editor;
        StatusPtr       status;
 
-       /*** GUI components ***/
 
-       Glib::RefPtr<Gtk::SizeGroup> advanced_sizegroup;
-       Gtk::Expander * advanced;
 
        /* Warning area */
 
@@ -138,6 +133,8 @@ class ExportDialog : public ArdourDialog {
 
        float previous_progress; // Needed for gtk bug workaround
 
+       void soundcloud_upload_progress(double total, double now, std::string title);
+
        /* Buttons */
 
        Gtk::Button *           cancel_button;
@@ -170,9 +167,6 @@ class ExportRegionDialog : public ExportDialog
   public:
        ExportRegionDialog (PublicEditor & editor, ARDOUR::AudioRegion const & region, ARDOUR::AudioTrack & track);
 
-  protected:
-       virtual bool channel_selector_is_expandable() { return false; }
-
   private:
        void init_gui ();
        void init_components ();
index c5d1573d54bff71de2a897a866fa62eff3e74fa7..69c494e09043528f50347c2b0b0f64ad32d1a863 100644 (file)
@@ -51,6 +51,9 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
   silence_end_checkbox (_("Add silence at end:")),
   silence_end_clock ("silence_end", true, "", true, false, true),
 
+  upload_checkbox(_("Upload to Soundcloud")),
+  command_label(_("Command to run post-export\n(%f=full path & filename, %d=directory, %b=basename, %u=username, %p=password):")),
+
   format_table (3, 4),
   compatibility_label (_("Compatibility"), Gtk::ALIGN_LEFT),
   quality_label (_("Quality"), Gtk::ALIGN_LEFT),
@@ -113,6 +116,10 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
        silence_table.attach (silence_end_checkbox, 1, 2, 2, 3);
        silence_table.attach (silence_end_clock, 2, 3, 2, 3);
 
+       get_vbox()->pack_start (upload_checkbox, false, false);
+       get_vbox()->pack_start (command_label, false, false);
+       get_vbox()->pack_start (command_entry, false, false);
+
        /* Format table */
 
        init_format_table();
@@ -142,6 +149,8 @@ ExportFormatDialog::ExportFormatDialog (FormatPtr format, bool new_dialog) :
 
        with_cue.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_cue));
        with_toc.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_toc));
+       upload_checkbox.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_upload));
+       command_entry.signal_changed().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_command));
 
        cue_toc_vbox.pack_start (with_cue, false, false);
        cue_toc_vbox.pack_start (with_toc, false, false);
@@ -296,6 +305,8 @@ ExportFormatDialog::load_state (FormatPtr spec)
        }
 
        tag_checkbox.set_active (spec->tag());
+       upload_checkbox.set_active (spec->upload());
+       command_entry.set_text (spec->command());
 }
 
 void
@@ -717,6 +728,18 @@ ExportFormatDialog::update_with_toc ()
        manager.select_with_toc (with_toc.get_active());
 }
 
+void
+ExportFormatDialog::update_upload ()
+{
+       manager.select_upload (upload_checkbox.get_active());
+}
+
+void
+ExportFormatDialog::update_command ()
+{
+       manager.set_command (command_entry.get_text());
+}
+
 void
 ExportFormatDialog::update_description()
 {
index 3e38cf09d662a55b3e67c8dbfb9706f7d4e3d9df..6b92625e7a4668503526e2b45effd6e6896b8be9 100644 (file)
@@ -179,6 +179,12 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList
        Gtk::CheckButton silence_end_checkbox;
        AudioClock       silence_end_clock;
 
+       /* Upload */
+       
+       Gtk::CheckButton upload_checkbox;
+       Gtk::Label       command_label;
+       Gtk::Entry       command_entry;
+
        /* Format table */
 
        struct CompatibilityCols : public Gtk::TreeModelColumnRecord
@@ -311,6 +317,8 @@ class ExportFormatDialog : public ArdourDialog, public PBD::ScopedConnectionList
 
        void update_with_toc ();
        void update_with_cue ();
+       void update_upload ();
+       void update_command ();
 
        Gtk::TreeView sample_format_view;
        Gtk::TreeView dither_type_view;
index 97a8dba25fa7de301c4eca26d70e8d2689476989..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,209 +0,0 @@
-/*
-    Copyright (C) 2006 Paul Davis
-    Author: Andre Raue
-
-    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.
-
-*/
-
-#include <sys/stat.h>
-
-#include <sstream>
-
-#include "ardour/audioengine.h"
-#include "ardour/sndfile_helpers.h"
-
-#include "ardour_ui.h"
-#include "export_range_markers_dialog.h"
-
-#include "i18n.h"
-
-using namespace Gtk;
-using namespace ARDOUR;
-using namespace PBD;
-using namespace std;
-
-ExportRangeMarkersDialog::ExportRangeMarkersDialog (PublicEditor& editor)
-       : ExportDialog(editor)
-{
-       set_title (_("Export Ranges"));
-       file_frame.set_label (_("Export to Directory"));
-
-       do_not_allow_export_cd_markers();
-
-       total_duration = 0;
-       current_range_marker_index = 0;
-}
-
-Gtk::FileChooserAction
-ExportRangeMarkersDialog::browse_action () const
-{
-       return Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER;
-}
-
-void
-ExportRangeMarkersDialog::export_data ()
-{
-       getSession().locations()->apply(*this, &ExportRangeMarkersDialog::process_range_markers_export);
-}
-
-void
-ExportRangeMarkersDialog::process_range_markers_export(Locations::LocationList& locations)
-{
-       Locations::LocationList::iterator locationIter;
-       current_range_marker_index = 0;
-       init_progress_computing(locations);
-
-       for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) {
-               Location *currentLocation = (*locationIter);
-
-               if(currentLocation->is_range_marker()){
-                       // init filename
-                       string filepath = get_target_filepath(
-                               get_selected_file_name(),
-                               currentLocation->name(),
-                               get_selected_header_format());
-
-                       initSpec(filepath);
-
-                       spec.start_frame = currentLocation->start();
-                       spec.end_frame = currentLocation->end();
-
-                       if (getSession().start_export(spec)){
-                               // if export fails
-                               return;
-                       }
-
-                       // wait until export of this range finished
-                       gtk_main_iteration();
-
-                       while (spec.running){
-                               if(gtk_events_pending()){
-                                       gtk_main_iteration();
-                               }else {
-                                       Glib::usleep(10000);
-                               }
-                       }
-
-                       current_range_marker_index++;
-
-                       getSession().stop_export (spec);
-               }
-       }
-
-       spec.running = false;
-}
-
-
-string
-ExportRangeMarkersDialog::get_target_filepath(string path, string filename, string postfix)
-{
-       string target_path = path;
-       if ((target_path.find_last_of ('/')) != string::npos) {
-               target_path += '/';
-       }
-
-       string target_filepath = target_path + filename + postfix;
-       struct stat statbuf;
-
-       for(int counter=1; (stat (target_filepath.c_str(), &statbuf) == 0); counter++){
-               // while file exists
-               ostringstream scounter;
-               scounter.flush();
-               scounter << counter;
-
-               target_filepath =
-                       target_path + filename + "_" + scounter.str() + postfix;
-       }
-
-       return target_filepath;
-}
-
-bool
-ExportRangeMarkersDialog::is_filepath_valid(string &filepath)
-{
-       // sanity check file name first
-       struct stat statbuf;
-
-       if (filepath.empty()) {
-               // warning dialog
-               string txt = _("Please enter a valid target directory.");
-               MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
-               msg.run();
-               return false;
-       }
-
-       if ( (stat (filepath.c_str(), &statbuf) != 0) ||
-               (!S_ISDIR (statbuf.st_mode)) ) {
-               string txt = _("Please select an existing target directory. Files are not allowed!");
-               MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
-               msg.run();
-               return false;
-       }
-
-       // directory needs to exist and be writable
-       string dirpath = Glib::path_get_dirname (filepath);
-       if (!exists_and_writable (dirpath)) {
-               string txt = _("Cannot write file in: ") + dirpath;
-               MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
-               msg.run();
-               return false;
-       }
-
-       return true;
-}
-
-void
-ExportRangeMarkersDialog::init_progress_computing(Locations::LocationList& locations)
-{
-       // flush vector
-       range_markers_durations_aggregated.resize(0);
-
-       framecnt_t duration_before_current_location = 0;
-       Locations::LocationList::iterator locationIter;
-
-       for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) {
-               Location *currentLocation = (*locationIter);
-
-               if(currentLocation->is_range_marker()){
-                       range_markers_durations_aggregated.push_back (duration_before_current_location);
-
-                       framecnt_t duration = currentLocation->end() - currentLocation->start();
-
-                       range_markers_durations.push_back (duration);
-                       duration_before_current_location += duration;
-               }
-       }
-
-       total_duration = duration_before_current_location;
-}
-
-
-gint
-ExportRangeMarkersDialog::progress_timeout ()
-{
-       double progress = 0.0;
-
-       if (current_range_marker_index >= range_markers_durations.size()){
-               progress = 1.0;
-       } else{
-               progress = ((double) range_markers_durations_aggregated[current_range_marker_index] +
-                           (spec.progress * (double) range_markers_durations[current_range_marker_index])) /
-                       (double) total_duration;
-       }
-
-       set_progress_fraction( progress );
-       return TRUE;
-}
index b0a29b5dc291e737acde9a06fd1756e756ed7ec5..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 (file)
@@ -1,66 +0,0 @@
-/*
-    Copyright (C) 2006 Andre Raue
-
-    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.
-
-*/
-
-#ifndef __export_range_markers_dialog_h__
-#define __export_range_markers_dialog_h__
-
-#include "ardour/location.h"
-
-#include "export_dialog.h"
-
-
-class ExportRangeMarkersDialog : public ExportDialog
-{
-  public:
-       ExportRangeMarkersDialog (PublicEditor&);
-
-       Gtk::FileChooserAction browse_action() const;
-
-  protected:
-       virtual bool is_filepath_valid(string &filepath);
-
-       void export_data();
-
-       bool wants_dir() { return true; }
-
-  private:
-       // keeps the duration of all range_markers before the current
-       vector<nframes_t>       range_markers_durations_aggregated;
-       vector<nframes_t>       range_markers_durations;
-       // duration of all range markers
-       nframes_t       total_duration;
-       // index of range marker, that get's exported right now
-       unsigned int    current_range_marker_index;
-
-       // sets value of progress bar
-       virtual gint progress_timeout ();
-
-       // initializes range_markers_durations_aggregated, range_markers_durations
-       // and total_duration
-       void init_progress_computing(ARDOUR::Locations::LocationList& locations);
-
-       // searches for a filename like "<filename><nr>.<postfix>" in path, that
-       // does not exist
-    string get_target_filepath(string path, string filename, string postfix);
-
-       void process_range_markers_export(ARDOUR::Locations::LocationList&);
-};
-
-
-#endif // __export_range_markers_dialog_h__
index d6ca02fe0387ab91e5d4395ad2a415608eff5d1a..61d813d2229cf4b751b651517657777d8904eb57 100644 (file)
@@ -105,6 +105,9 @@ ExportTimespanSelector::ExportTimespanSelector (ARDOUR::Session * session, Profi
        /* Range view */
 
        range_list = Gtk::ListStore::create (range_cols);
+       // order by location start times
+       range_list->set_sort_column(range_cols.location, Gtk::SORT_ASCENDING);
+       range_list->set_sort_func(range_cols.location, sigc::mem_fun(*this, &ExportTimespanSelector::location_sorter));
        range_view.set_model (range_list);
        range_view.set_headers_visible (true);
 }
@@ -114,6 +117,22 @@ ExportTimespanSelector::~ExportTimespanSelector ()
 
 }
 
+int
+ExportTimespanSelector::location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b)
+{
+       Location *l1 = (*a)[range_cols.location];
+       Location *l2 = (*b)[range_cols.location];
+       const Location *ls = _session->locations()->session_range_location();
+
+       // always sort session range first
+       if (l1 == ls)
+               return -1;
+       if (l2 == ls)
+               return +1;
+
+       return l1->start() - l2->start();
+}
+
 void
 ExportTimespanSelector::add_range_to_selection (ARDOUR::Location const * loc)
 {
index 5556f5f676eb4dae77abfa03a16b0e628b592d69..12166709911688721999564836dc900eb2aea847 100644 (file)
@@ -89,6 +89,7 @@ class ExportTimespanSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
        void update_range_name (std::string const & path, std::string const & new_text);
 
        void set_selection_state_of_all_timespans (bool);
+       int location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b);
 
        /*** GUI components ***/
 
@@ -132,7 +133,7 @@ class ExportTimespanSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
        Gtk::ScrolledWindow          range_scroller;
 };
 
-/// Allows seleting multiple timespans
+/// Allows selecting multiple timespans
 class ExportTimespanSelectorMultiple : public ExportTimespanSelector
 {
   public:
diff --git a/gtk2_ardour/icons/soundcloud.png b/gtk2_ardour/icons/soundcloud.png
new file mode 100644 (file)
index 0000000..39c50fe
Binary files /dev/null and b/gtk2_ardour/icons/soundcloud.png differ
diff --git a/gtk2_ardour/soundcloud_export_selector.cc b/gtk2_ardour/soundcloud_export_selector.cc
new file mode 100644 (file)
index 0000000..1ecab51
--- /dev/null
@@ -0,0 +1,110 @@
+/* soundcloud_export_selector.cpp ***************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+       Licence GPL:
+
+       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 "ardour/soundcloud_upload.h"
+#include "soundcloud_export_selector.h"
+
+#include <pbd/error.h>
+#include "pbd/openuri.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+#include "ardour/session_metadata.h"
+#include "utils.h"
+
+SoundcloudExportSelector::SoundcloudExportSelector() :
+         sc_table (4, 3),
+         soundcloud_public_checkbox (_("Make file(s) public")),
+         soundcloud_username_label (_("User Email"), 1.0, 0.5),
+         soundcloud_password_label (_("Password"), 1.0, 0.5),
+         soundcloud_open_checkbox (_("Open uploaded files in browser")),
+         progress_bar()
+{
+
+
+       soundcloud_public_checkbox.set_name ("ExportCheckbox");
+       soundcloud_username_label.set_name ("ExportFormatLabel");
+       soundcloud_username_entry.set_name ("ExportFormatDisplay");
+       soundcloud_password_label.set_name ("ExportFormatLabel");
+       soundcloud_password_entry.set_name ("ExportFormatDisplay");
+
+       soundcloud_username_entry.set_text (ARDOUR::SessionMetadata::Metadata()->user_email());
+       soundcloud_password_entry.set_visibility(false);
+
+       Gtk::Frame *sc_frame = manage(new Gtk::Frame);
+       sc_frame->set_border_width(4);
+       sc_frame->set_shadow_type(Gtk::SHADOW_ETCHED_OUT);
+       sc_frame->set_name("soundcloud_export_box");
+       pack_start(*sc_frame, false, false);
+
+       sc_table.set_border_width(4);
+       sc_table.set_col_spacings (5);
+       sc_table.set_row_spacings (5);
+       sc_frame->add (sc_table);
+
+       //              sc_table.attach ( *( manage (new EventBox (::get_icon (X_("soundcloud"))))) , 0, 1,  0, 1);
+       sc_table.attach ( *(Gtk::manage (new Gtk::Image (get_icon (X_("soundcloud"))))) , 0, 1,  0, 2);
+
+       sc_table.attach (soundcloud_public_checkbox, 2, 3,  1, 2);
+       sc_table.attach (soundcloud_username_label, 0, 1,  3, 4);
+       sc_table.attach (soundcloud_username_entry, 1, 3,  3, 4);
+       sc_table.attach (soundcloud_password_label, 0, 1,  5, 6);
+       sc_table.attach (soundcloud_password_entry, 1, 3,  5, 6);
+       sc_table.attach (soundcloud_open_checkbox, 2, 3,  7, 8);
+
+       pack_end(progress_bar, false, false);
+       sc_frame->show_all();
+}
+
+
+int
+SoundcloudExportSelector::do_progress_callback(double ultotal, double ulnow, const std::string &filename)
+{
+       std::cerr << "SoundcloudExportSelector::do_progress_callback(" << ultotal << ", " << ulnow << ", " << filename << ")..." << std::endl; 
+       if (soundcloud_cancel) {
+               progress_bar.set_fraction (0);
+               // cancel_button.set_label ("");
+               return -1;
+       }
+
+       double fraction = 0.0;
+       if (ultotal != 0) {
+               fraction = ulnow / ultotal;
+       }
+
+       progress_bar.set_fraction ( fraction );
+
+       std::string prog;
+       prog = string_compose (_("%1: %2 of %3 bytes uploaded"), filename, ulnow, ultotal);
+       progress_bar.set_text( prog );
+
+
+       return 0;
+}
+
diff --git a/gtk2_ardour/soundcloud_export_selector.h b/gtk2_ardour/soundcloud_export_selector.h
new file mode 100644 (file)
index 0000000..7962ba8
--- /dev/null
@@ -0,0 +1,41 @@
+/*soundcloud_export_selector.h***********************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+*****************************************************************************/
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <gtkmm.h>
+#include <gtkmm/progressbar.h>
+
+class SoundcloudExportSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
+{
+  public:
+       SoundcloudExportSelector ();
+       int do_progress_callback (double ultotal, double ulnow, const std::string &filename);
+       std::string username () { return soundcloud_username_entry.get_text (); }
+       std::string password () { return soundcloud_password_entry.get_text (); }
+       bool upload_public () { return soundcloud_public_checkbox.get_active (); }
+       bool upload_open () { return soundcloud_open_checkbox.get_active (); }
+       void cancel () { soundcloud_cancel = true; }
+
+  private:
+       Gtk::Table  sc_table;
+       Gtk::CheckButton soundcloud_public_checkbox;
+       Gtk::Label soundcloud_username_label;
+       Gtk::Entry soundcloud_username_entry;
+       Gtk::Label soundcloud_password_label;
+       Gtk::Entry soundcloud_password_entry;
+       Gtk::CheckButton soundcloud_open_checkbox;
+       bool soundcloud_cancel;
+       Gtk::ProgressBar progress_bar;
+       
+};
+
index 920f71bd7db343efb917b1aa7fe376e92a34e5d8..2cfd2ea4977da5b2fbd801337aac28c19e2e6afa 100644 (file)
@@ -204,6 +204,7 @@ gtk2_ardour_sources = [
         'session_option_editor.cc',
         'sfdb_ui.cc',
         'shuttle_control.cc',
+        'soundcloud_export_selector.cc',
         'splash.cc',
         'speaker_dialog.cc',
         'startup.cc',
index 9a95111509b888b6f148bf3460dc58598ef7d41c..dad7d84b72f58b4406318fee6c98f7a8a475e48c 100644 (file)
@@ -100,6 +100,8 @@ class LIBARDOUR_API ExportFormatManager : public PBD::ScopedConnectionList
 
        void select_with_cue (bool);
        void select_with_toc (bool);
+       void select_upload (bool);
+       void set_command (std::string);
        void select_src_quality (ExportFormatBase::SRCQuality value);
        void select_trim_beginning (bool value);
        void select_silence_beginning (AnyTime const & time);
index 1593990d3592c43e9732b3b3fec5c21941a41397..d41fe3e97ac8edf756394a42208b2966109f520d 100644 (file)
@@ -96,6 +96,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
        void set_tag (bool tag_it) { _tag = tag_it; }
        void set_with_cue (bool yn) { _with_cue = yn; }
        void set_with_toc (bool yn) { _with_toc = yn; }
+       void set_upload (bool yn) { _upload = yn; }
+       void set_command (std::string command) { _command = command; }
 
        void set_silence_beginning (AnyTime const & value) { _silence_beginning = value; }
        void set_silence_end (AnyTime const & value) { _silence_end = value; }
@@ -125,6 +127,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
        float normalize_target () const { return _normalize_target; }
        bool with_toc() const { return _with_toc; }
        bool with_cue() const { return _with_cue; }
+       bool upload() const { return _upload; }
+       std::string command() const { return _command; }
 
        bool tag () const { return _tag && supports_tagging; }
 
@@ -174,6 +178,8 @@ class LIBARDOUR_API ExportFormatSpecification : public ExportFormatBase {
        float           _normalize_target;
        bool            _with_toc;
        bool            _with_cue;
+       bool            _upload;
+       std::string     _command;
 
        /* serialization helpers */
 
index 1bc80a80e976693c7e194ca7c2fcfaf23634680e..25a87045a8d815b66c36fba9f27a6410e0886c73 100644 (file)
@@ -31,6 +31,7 @@
 #include "ardour/session.h"
 #include "ardour/libardour_visibility.h"
 #include "ardour/types.h"
+#include "pbd/signals.h"
 
 namespace AudioGrapher {
        class BroadcastInfo;
@@ -68,7 +69,7 @@ class LIBARDOUR_API ExportElementFactory
        Session & session;
 };
 
-class LIBARDOUR_API ExportHandler : public ExportElementFactory
+class LIBARDOUR_API ExportHandler : public ExportElementFactory, public sigc::trackable
 {
   public:
        struct FileSpec {
@@ -95,6 +96,8 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory
        friend boost::shared_ptr<ExportHandler> Session::get_export_handler();
        ExportHandler (Session & session);
 
+       void command_output(std::string output, size_t size);
+
   public:
        ~ExportHandler ();
 
@@ -105,6 +108,17 @@ class LIBARDOUR_API ExportHandler : public ExportElementFactory
 
        std::string get_cd_marker_filename(std::string filename, CDMarkerFormat format);
 
+       /** signal emitted when soundcloud export reports progress updates during upload.
+        * The parameters are total and current bytes downloaded, and the current filename
+        */
+       PBD::Signal3<void, double, double, std::string> SoundcloudProgress;
+
+       /* upload credentials & preferences */
+       std::string upload_username;
+       std::string upload_password;
+       bool upload_public;
+       bool upload_open;
+
   private:
 
        void handle_duplicate_format_extensions();
diff --git a/libs/ardour/ardour/soundcloud_upload.h b/libs/ardour/ardour/soundcloud_upload.h
new file mode 100644 (file)
index 0000000..6b8700e
--- /dev/null
@@ -0,0 +1,55 @@
+/* soundcloud_upload.h ******************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+*****************************************************************************/
+
+#ifndef __ardour_soundcloud_upload_h__
+#define __ardour_soundcloud_upload_h__
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "curl/curl.h"
+#include "ardour/session_handle.h"
+#include "ardour/export_handler.h"
+#include "pbd/signals.h"
+
+//--- struct to store XML file
+struct MemoryStruct {
+       char *memory;
+       size_t size;
+};
+
+
+class SoundcloudUploader
+{
+public:
+       SoundcloudUploader();
+       ~SoundcloudUploader();
+
+       std::string     Get_Auth_Token(std::string username, std::string password);
+       std::string Upload (std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller);
+       static int progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow);
+
+
+private:
+
+       void            setcUrlOptions();
+
+       CURL *curl_handle;
+       CURLM *multi_handle;
+       char errorBuffer[CURL_ERROR_SIZE];      // storage for cUrl error message
+
+       std::string title;
+       ARDOUR::ExportHandler *caller;
+
+};
+
+#endif /* __ardour_soundcloud_upload_h__ */
index 40e429720ef9371448b0315a38baf1532ffaa4a6..ae865c7bff7ce040b87449688dfa8a54ae898c0e 100644 (file)
@@ -32,6 +32,7 @@ class LIBARDOUR_API SystemExec
 public:
        SystemExec (std::string c, std::string a = "");
        SystemExec (std::string c, char ** a);
+       SystemExec (std::string c, const std::map<char, std::string> subs);
        ~SystemExec ();
 
        int start (int stderr_mode = 1) {
index 890623c114b0f5a562aa28705f3e225b58dd94ea..3ee940ffb6367ff50c5ddf2e7d6dda521deda007 100644 (file)
@@ -293,6 +293,20 @@ ExportFormatManager::select_with_toc (bool value)
        check_for_description_change ();
 }
 
+void
+ExportFormatManager::select_upload (bool value)
+{
+       current_selection->set_upload (value);
+       check_for_description_change ();
+}
+
+void
+ExportFormatManager::set_command (std::string command)
+{
+       current_selection->set_command (command);
+       check_for_description_change ();
+}
+
 void
 ExportFormatManager::select_trim_beginning (bool value)
 {
index b139faeee2997d8401a9e4de77dfe2547254ff74..8b921519f71fd9ccf60ab44f4651d85f93f166db 100644 (file)
@@ -170,6 +170,8 @@ ExportFormatSpecification::ExportFormatSpecification (Session & s)
        , _normalize_target (1.0)
        , _with_toc (false)
        , _with_cue (false)
+       , _upload (false)
+       , _command ("")
 {
        format_ids.insert (F_None);
        endiannesses.insert (E_FileDefault);
@@ -244,6 +246,8 @@ ExportFormatSpecification::get_state ()
        root->add_property ("id", _id.to_s());
        root->add_property ("with-cue", _with_cue ? "true" : "false");
        root->add_property ("with-toc", _with_toc ? "true" : "false");
+       root->add_property ("upload", _upload ? "true" : "false");
+       root->add_property ("command", _command);
 
        node = root->add_child ("Encoding");
        node->add_property ("id", enum_2_string (format_id()));
@@ -321,6 +325,18 @@ ExportFormatSpecification::set_state (const XMLNode & root)
                _with_toc = false;
        }
        
+       if ((prop = root.property ("upload"))) {
+               _upload = string_is_affirmative (prop->value());
+       } else {
+               _upload = false;
+       }
+       
+       if ((prop = root.property ("command"))) {
+               _command = prop->value();
+       } else {
+               _command = "";
+       }
+
        /* Encoding and SRC */
 
        if ((child = root.child ("Encoding"))) {
@@ -590,6 +606,14 @@ ExportFormatSpecification::description (bool include_name)
                components.push_back ("CUE");
        }
 
+       if (_upload) {
+               components.push_back ("Upload");
+       }
+
+       if (!_command.empty()) {
+               components.push_back ("+");
+       }
+
        string desc;
        if (include_name) {
                desc = _name + ": ";
index 20abc80de1995866ff8befb89228d04bd1e83845..c9f20d182e7bc4dce63a82be58d39f4d8cdd2bcd 100644 (file)
 #include "ardour/export_status.h"
 #include "ardour/export_format_specification.h"
 #include "ardour/export_filename.h"
+#include "ardour/soundcloud_upload.h"
+#include "ardour/system_exec.h"
+#include "pbd/openuri.h"
+#include "pbd/basename.h"
 #include "ardour/session_metadata.h"
 
 #include "i18n.h"
@@ -277,6 +281,13 @@ ExportHandler::process_normalize ()
        return 0;
 }
 
+void
+ExportHandler::command_output(std::string output, size_t size)
+{
+       std::cerr << "command: " << size << ", " << output << std::endl;
+       info << output << endmsg;
+}
+
 void
 ExportHandler::finish_timespan ()
 {
@@ -297,13 +308,77 @@ ExportHandler::finish_timespan ()
                        AudiofileTagger::tag_file(filename, *SessionMetadata::Metadata());
                }
 
+               if (!fmt->command().empty()) {
+
+#if 0                  // would be nicer with C++11 initialiser...
+                       std::map<char, std::string> subs {
+                               { 'f', filename },
+                               { 'd', Glib::path_get_dirname(filename) },
+                               { 'b', PBD::basename_nosuffix(filename) },
+                               { 'u', upload_username },
+                               { 'p', upload_password}
+                       };
+#endif
+
+                       PBD::ScopedConnection command_connection;
+                       std::map<char, std::string> subs;
+                       subs.insert (std::pair<char, std::string> ('f', filename));
+                       subs.insert (std::pair<char, std::string> ('d', Glib::path_get_dirname(filename)));
+                       subs.insert (std::pair<char, std::string> ('b', PBD::basename_nosuffix(filename)));
+                       subs.insert (std::pair<char, std::string> ('u', upload_username));
+                       subs.insert (std::pair<char, std::string> ('p', upload_password));
+
+
+                       std::cerr << "running command: " << fmt->command() << "..." << std::endl;
+                       ARDOUR::SystemExec *se = new ARDOUR::SystemExec(fmt->command(), subs);
+                       se->ReadStdout.connect_same_thread(command_connection, boost::bind(&ExportHandler::command_output, this, _1, _2));
+                       if (se->start (2) == 0) {
+                               // successfully started
+                               std::cerr << "started!" << std::endl;
+                               while (se->is_running ()) {
+                                       // wait for system exec to terminate
+                                       // std::cerr << "waiting..." << std::endl;
+                                       usleep (1000);
+                               }
+                       }
+                       std::cerr << "done! deleting..." << std::endl;
+                       delete (se);
+               }
+
+               if (fmt->upload()) {
+                       SoundcloudUploader *soundcloud_uploader = new SoundcloudUploader;
+                       std::string token = soundcloud_uploader->Get_Auth_Token(upload_username, upload_password);
+                       std::cerr
+                               << "uploading "
+                               << filename << std::endl
+                               << "username = " << upload_username
+                               << ", password = " << upload_password
+                               << " - token = " << token << " ..."
+                               << std::endl;
+                       std::string path = soundcloud_uploader->Upload (
+                                       filename,
+                                       PBD::basename_nosuffix(filename), // title
+                                       token,
+                                       upload_public,
+                                       this);
+
+                       if (path.length() != 0) {
+                               if (upload_open) {
+                               std::cerr << "opening " << path << " ..." << std::endl;
+                               open_uri(path.c_str());  // open the soundcloud website to the new file
+                               }
+                       } else {
+                               error << _("upload to Soundcloud failed.  Perhaps your email or password are incorrect?\n") << endmsg;
+                       }
+                       delete soundcloud_uploader;
+               }
                config_map.erase (config_map.begin());
        }
 
        start_timespan ();
 }
 
-/*** CD Marker sutff ***/
+/*** CD Marker stuff ***/
 
 struct LocationSortByStart {
     bool operator() (Location *a, Location *b) {
diff --git a/libs/ardour/soundcloud_upload.cc b/libs/ardour/soundcloud_upload.cc
new file mode 100644 (file)
index 0000000..f003d5a
--- /dev/null
@@ -0,0 +1,349 @@
+/* soundcloud_export.cpp **********************************************************************
+
+       Adapted for Ardour by Ben Loftis, March 2012
+
+       Licence GPL:
+
+       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 "ardour/soundcloud_upload.h"
+
+#include "pbd/xml++.h"
+#include <pbd/error.h>
+//#include "pbd/filesystem.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+// static const std::string base_url = "http://api.soundcloud.com/tracks/13158665?client_id=";
+
+size_t
+WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+       register int realsize = (int)(size * nmemb);
+       struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+       mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+
+       if (mem->memory) {
+               memcpy(&(mem->memory[mem->size]), ptr, realsize);
+               mem->size += realsize;
+               mem->memory[mem->size] = 0;
+       }
+       return realsize;
+}
+
+SoundcloudUploader::SoundcloudUploader()
+{
+       curl_handle = curl_easy_init();
+       multi_handle = curl_multi_init();
+}
+
+std::string
+SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
+{
+       struct MemoryStruct xml_page;
+       xml_page.memory = NULL;
+       xml_page.size = 0;
+
+       setcUrlOptions();
+
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+       struct curl_httppost *formpost=NULL;
+       struct curl_httppost *lastptr=NULL;
+
+       /* Fill in the filename field */ 
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "client_id",
+                       CURLFORM_COPYCONTENTS, "e7ac891eef866f139773cf8102b7a719",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "client_secret",
+                       CURLFORM_COPYCONTENTS, "d78f34d19f09d26731801a0cb0f382c4",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "grant_type",
+                       CURLFORM_COPYCONTENTS, "password",
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "username",
+                       CURLFORM_COPYCONTENTS, username.c_str(),
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "password",
+                       CURLFORM_COPYCONTENTS, password.c_str(),
+                       CURLFORM_END);
+
+       struct curl_slist *headerlist=NULL;
+       headerlist = curl_slist_append(headerlist, "Expect:");
+       headerlist = curl_slist_append(headerlist, "Accept: application/xml");
+       curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+
+       /* what URL that receives this POST */ 
+       std::string url = "https://api.soundcloud.com/oauth2/token";
+       curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+       curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+       // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+       // perform online request
+       CURLcode res = curl_easy_perform(curl_handle);
+       if( res != 0 ) {
+               std::cerr << "curl error " << res << " (" << curl_easy_strerror(res) << ")" << std::endl;
+               return "";
+       }
+
+       if(xml_page.memory){
+               //cheesy way to parse the json return value.  find access_token, then advance 3 quotes
+
+               if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
+                       error << _("Upload to Soundcloud failed.  Perhaps your email or password are incorrect?\n") << endmsg;
+                       return "";
+               }
+
+               std::string token = strtok( xml_page.memory, "access_token" );
+               token = strtok( NULL, "\"" );
+               token = strtok( NULL, "\"" );
+               token = strtok( NULL, "\"" );
+
+               free( xml_page.memory );
+               return token;
+       }
+
+       return "";
+}
+
+int
+SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
+{
+       SoundcloudUploader *scu = (SoundcloudUploader *) caller;
+       std::cerr << scu->title << ": uploaded " << ulnow << " of " << ultotal << std::endl;
+       scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
+       return 0;
+}
+
+
+std::string
+SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, ARDOUR::ExportHandler *caller)
+{
+       int still_running;
+
+       struct MemoryStruct xml_page;
+       xml_page.memory = NULL;
+       xml_page.size = 0;
+
+       setcUrlOptions();
+
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+       curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+       struct curl_httppost *formpost=NULL;
+       struct curl_httppost *lastptr=NULL;
+
+       /* Fill in the file upload field. This makes libcurl load data from
+          the given file name when curl_easy_perform() is called. */ 
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[asset_data]",
+                       CURLFORM_FILE, file_path.c_str(),
+                       CURLFORM_END);
+
+       /* Fill in the filename field */ 
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "oauth_token",
+                       CURLFORM_COPYCONTENTS, token.c_str(),
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[title]",
+                       CURLFORM_COPYCONTENTS, title.c_str(),
+                       CURLFORM_END);
+
+       curl_formadd(&formpost,
+                       &lastptr,
+                       CURLFORM_COPYNAME, "track[sharing]",
+                       CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
+                       CURLFORM_END);
+
+       /* initalize custom header list (stating that Expect: 100-continue is not
+          wanted */ 
+       struct curl_slist *headerlist=NULL;
+       static const char buf[] = "Expect:";
+       headerlist = curl_slist_append(headerlist, buf);
+
+
+       if (curl_handle && multi_handle) {
+
+               /* what URL that receives this POST */ 
+               std::string url = "https://api.soundcloud.com/tracks";
+               curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+               // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+               curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+               curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+               this->title = title; // save title to show in progress bar
+               this->caller = caller;
+
+               curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
+               curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
+               curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
+
+               curl_multi_add_handle(multi_handle, curl_handle);
+
+               curl_multi_perform(multi_handle, &still_running);
+
+
+               while(still_running) {
+                       struct timeval timeout;
+                       int rc; /* select() return code */ 
+
+                       fd_set fdread;
+                       fd_set fdwrite;
+                       fd_set fdexcep;
+                       int maxfd = -1;
+
+                       long curl_timeo = -1;
+
+                       FD_ZERO(&fdread);
+                       FD_ZERO(&fdwrite);
+                       FD_ZERO(&fdexcep);
+
+                       /* set a suitable timeout to play around with */ 
+                       timeout.tv_sec = 1;
+                       timeout.tv_usec = 0;
+
+                       curl_multi_timeout(multi_handle, &curl_timeo);
+                       if(curl_timeo >= 0) {
+                               timeout.tv_sec = curl_timeo / 1000;
+                               if(timeout.tv_sec > 1)
+                                       timeout.tv_sec = 1;
+                               else
+                                       timeout.tv_usec = (curl_timeo % 1000) * 1000;
+                       }
+
+                       /* get file descriptors from the transfers */ 
+                       curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
+
+                       /* In a real-world program you OF COURSE check the return code of the
+                          function calls.  On success, the value of maxfd is guaranteed to be
+                          greater or equal than -1.  We call select(maxfd + 1, ...), specially in
+                          case of (maxfd == -1), we call select(0, ...), which is basically equal
+                          to sleep. */ 
+
+                       rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+
+                       switch(rc) {
+                               case -1:
+                                       /* select error */ 
+                                       break;
+                               case 0:
+                               default:
+                                       /* timeout or readable/writable sockets */ 
+                                       curl_multi_perform(multi_handle, &still_running);
+                                       break;
+                       }
+               } 
+
+               /* then cleanup the formpost chain */ 
+               curl_formfree(formpost);
+
+               /* free slist */ 
+               curl_slist_free_all (headerlist);
+       }
+
+       curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
+
+       if(xml_page.memory){
+
+               std::cout << xml_page.memory << std::endl;
+
+               XMLTree doc;
+               doc.read_buffer( xml_page.memory );
+               XMLNode *root = doc.root();
+
+               if (!root) {
+                       std::cout << "no root XML node!" << std::endl;
+                       return "";
+               }
+
+               XMLNode *url_node = root->child("permalink-url");
+               if (!url_node) {
+                       std::cout << "no child node \"permalink-url\" found!" << std::endl;
+                       return "";
+               }
+
+               XMLNode *text_node = url_node->child("text");
+               if (!text_node) {
+                       std::cout << "no text node found!" << std::endl;
+                       return "";
+               }
+
+               free( xml_page.memory );
+               return text_node->content();
+       }
+
+       return "";
+};
+
+
+SoundcloudUploader:: ~SoundcloudUploader()
+{
+       curl_easy_cleanup(curl_handle);
+       curl_multi_cleanup(multi_handle);
+}
+
+
+void
+SoundcloudUploader::setcUrlOptions()
+{
+       // basic init for curl
+       curl_global_init(CURL_GLOBAL_ALL);
+       // some servers don't like requests that are made without a user-agent field, so we provide one
+       curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+       // setup curl error buffer
+       curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
+       // Allow redirection
+       curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
+       
+       // Allow connections to time out (without using signals)
+       curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+       curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
+
+       curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
+       curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
+}
+
index 90e729a7f683d51df0f761fb3947ebf942afd6f1..760a9b787805f31c6db223b65540a6503c9afc8e 100644 (file)
@@ -65,4 +65,14 @@ SystemExec::SystemExec (std::string c, std::string a)
 #endif
 }
 
+SystemExec::SystemExec (std::string c, const std::map<char, std::string> subs)
+       : PBD::SystemExec(c, subs)
+{
+#ifndef PLATFORM_WINDOWS
+       if (!_vfork_exec_wrapper) {
+               _vfork_exec_wrapper = vfork_exec_wrapper_path();
+       }
+#endif
+}
+
 SystemExec::~SystemExec() { }
index 4a500645452f046d68c7855799d602e579539dcc..85326e98f8cf2cef164dba85b4e98501df13bc38 100644 (file)
@@ -194,6 +194,7 @@ libardour_sources = [
         'sndfile_helpers.cc',
         'sndfileimportable.cc',
         'sndfilesource.cc',
+        'soundcloud_upload.cc',
         'source.cc',
         'source_factory.cc',
         'speakers.cc',
index 1232175fecc8192643c4bc2db22c3b5afbe56bc7..ce6e5a9c4f9367664107aa47b40340d8d39a89f0 100644 (file)
@@ -42,6 +42,8 @@
 #include <string>
 #include <pthread.h>
 #include <signal.h>
+#include <map>
+
 #ifdef NOPBD  /* unit-test outside ardour */
 #include <sigc++/bind.h>
 #include <sigc++/signal.h>
@@ -94,6 +96,23 @@ class LIBPBD_API SystemExec
                 *
                 */
                SystemExec (std::string c, char ** a);
+
+               /** similar to \ref SystemExec but expects a whole command line, and
+                * handles some simple escape sequences.
+                *
+                * @param command complete command-line to be executed
+                * @param subs a map of <char, std::string> listing the % substitutions to
+                *             be made.
+                *
+                * creates an argv array from the given command string, splitting into
+                * parameters at spaces.
+                * "\ " is non-splitting space, "\\" (and "\" at end of command) as "\",
+                * for "%<char>", <char> is looked up in subs and the corresponding string
+                * substituted. "%%" (and "%" at end of command)
+                * returns an argv array suitable for creating a new SystemExec with
+                */
+               SystemExec (std::string command, const std::map<char, std::string> subs);
+
                virtual ~SystemExec ();
 
                /** fork and execute the given program
@@ -182,6 +201,7 @@ class LIBPBD_API SystemExec
                int nicelevel; ///< process nice level - defaults to 0
 
                void make_argp(std::string);
+               void make_argp_escaped(std::string command, const std::map<char, std::string> subs);
                void make_envp();
 
                char **argp;
@@ -198,6 +218,7 @@ class LIBPBD_API SystemExec
 #else
                pid_t pid;
 #endif
+               void init ();
                pthread_mutex_t write_lock;
 
                int fdin; ///< file-descriptor for writing to child's STDIN. This variable is identical to pin[1] but also used as status check if the stdin pipe is open: <0 means closed.
index 0102323505cf651336b2900987ef09e3868fc9f3..82398af0c84c7854fbd640901dab2a441ce89d73 100644 (file)
@@ -151,9 +151,8 @@ static int close_allv(const int except_fds[]) {
 }
 #endif /* not on windows, nor vfork */
 
-
-SystemExec::SystemExec (std::string c, std::string a)
-       : cmd(c)
+void
+SystemExec::init ()
 {
        pthread_mutex_init(&write_lock, NULL);
        thread_active=false;
@@ -161,12 +160,19 @@ SystemExec::SystemExec (std::string c, std::string a)
        pin[1] = -1;
        nicelevel = 0;
        envp = NULL;
-       argp = NULL;
 #ifdef PLATFORM_WINDOWS
        stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
        stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
        stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
 #endif
+}
+
+SystemExec::SystemExec (std::string c, std::string a)
+       : cmd(c)
+{
+       init ();
+
+       argp = NULL;
        make_envp();
        make_argp(a);
 }
@@ -174,21 +180,101 @@ SystemExec::SystemExec (std::string c, std::string a)
 SystemExec::SystemExec (std::string c, char **a)
        : cmd(c) , argp(a)
 {
-       pthread_mutex_init(&write_lock, NULL);
-       thread_active=false;
-       pid = 0;
-       pin[1] = -1;
-       nicelevel = 0;
-       envp = NULL;
+       init ();
+
 #ifdef PLATFORM_WINDOWS
-       stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
-       stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
-       stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
        make_wargs(a);
 #endif
        make_envp();
 }
 
+SystemExec::SystemExec (std::string command, const std::map<char, std::string> subs)
+{
+       init ();
+       make_argp_escaped(command, subs);
+       cmd = argp[0];
+       // cmd = strdup(argp[0]);
+       make_envp();
+}
+
+void
+SystemExec::make_argp_escaped(std::string command, const std::map<char, std::string> subs)
+{
+
+       int inquotes = 0;
+       int n = 0;
+       size_t i = 0;
+       std::string arg = "";
+
+       argp = (char **) malloc(sizeof(char *));
+
+       for (i = 0; i <= command.length(); i++) { // include terminating '\0'
+               char c = command.c_str()[i];
+               if (inquotes) {
+                       if (c == '"') {
+                               inquotes = 0;
+                       } else {
+                               // still in quotes - just copy
+                               arg += c;
+                       }
+               } else switch (c) {
+                       case '%' :
+                               c = command.c_str()[++i];
+                               if (c == '%' || c == '\0') {
+                                       // "%%", "%" at end-of-string => "%"
+                                       arg += '%';
+                               } else {
+                                       // search subs for string to substitute for char
+                                       std::map<char, std::string>::const_iterator s = subs.find(c);
+                                       if (s != subs.end()) {
+                                               // found substitution
+                                               arg += s->second;
+                                       } else {
+                                               // not a valid substitution, just copy
+                                               arg += '%';
+                                               arg += c;
+                                       }
+                               }
+                               break;
+                       case '\\':
+                               c = command.c_str()[++i];
+                               switch (c) {
+                                       case ' ' :
+                                       case '"' : arg += c; break; // "\\", "\" at end-of-string => "\"
+                                       case '\0': 
+                                       case '\\': arg += '\\'; break;
+                                       default  : arg += '\\'; arg += c; break;
+                               }
+                               break;
+                       case '"' :
+                               inquotes = 1;
+                               break;
+                       case ' ' :
+                       case '\t':
+                       case '\0':
+                               if (arg.length() > 0) {
+                                       // if there wasn't already a space or tab, start a new parameter
+                                       argp = (char **) realloc(argp, (n + 2) * sizeof(char *));
+                                       argp[n++] = strdup (arg.c_str());
+                                       arg = "";
+                               }
+                               break;
+                       default :
+                               arg += c;
+                               break;
+               }
+       }
+       argp[n] = NULL;
+
+       char *p = argp[0];
+       n = 0;
+       do {
+               std::cerr << "argv[" << n << "] == \"" << p << "\"" << std::endl;
+               p = argp[n++];
+       } while (p);
+
+}
+
 SystemExec::~SystemExec ()
 {
        terminate ();
@@ -522,7 +608,7 @@ SystemExec::make_argp(std::string args) {
                        *cp2 = '\0';
                        argp[argn++] = strdup(cp1);
                        cp1 = cp2 + 1;
-           argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
+                       argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
                }
        }
        if (cp2 != cp1) {