summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--export/Soundcloud upload (lossless).format30
-rw-r--r--export/Soundcloud upload (lossy).format30
-rw-r--r--gtk2_ardour/export_channel_selector.cc8
-rw-r--r--gtk2_ardour/export_channel_selector.h2
-rw-r--r--gtk2_ardour/export_dialog.cc97
-rw-r--r--gtk2_ardour/export_dialog.h22
-rw-r--r--gtk2_ardour/export_format_dialog.cc23
-rw-r--r--gtk2_ardour/export_format_dialog.h8
-rw-r--r--gtk2_ardour/export_range_markers_dialog.cc209
-rw-r--r--gtk2_ardour/export_range_markers_dialog.h66
-rw-r--r--gtk2_ardour/export_timespan_selector.cc19
-rw-r--r--gtk2_ardour/export_timespan_selector.h3
-rw-r--r--gtk2_ardour/icons/soundcloud.pngbin0 -> 1140 bytes
-rw-r--r--gtk2_ardour/soundcloud_export_selector.cc110
-rw-r--r--gtk2_ardour/soundcloud_export_selector.h41
-rw-r--r--gtk2_ardour/wscript1
-rw-r--r--libs/ardour/ardour/export_format_manager.h2
-rw-r--r--libs/ardour/ardour/export_format_specification.h6
-rw-r--r--libs/ardour/ardour/export_handler.h16
-rw-r--r--libs/ardour/ardour/soundcloud_upload.h55
-rw-r--r--libs/ardour/ardour/system_exec.h1
-rw-r--r--libs/ardour/export_format_manager.cc14
-rw-r--r--libs/ardour/export_format_specification.cc24
-rw-r--r--libs/ardour/export_handler.cc77
-rw-r--r--libs/ardour/soundcloud_upload.cc349
-rw-r--r--libs/ardour/system_exec.cc10
-rw-r--r--libs/ardour/wscript1
-rw-r--r--libs/pbd/pbd/system_exec.h21
-rw-r--r--libs/pbd/system_exec.cc114
29 files changed, 992 insertions, 367 deletions
diff --git a/export/Soundcloud upload (lossless).format b/export/Soundcloud upload (lossless).format
new file mode 100644
index 0000000000..eb966af86b
--- /dev/null
+++ b/export/Soundcloud upload (lossless).format
@@ -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
index 0000000000..d948b1d97d
--- /dev/null
+++ b/export/Soundcloud upload (lossy).format
@@ -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>
diff --git a/gtk2_ardour/export_channel_selector.cc b/gtk2_ardour/export_channel_selector.cc
index 10e3135b53..20155471af 100644
--- a/gtk2_ardour/export_channel_selector.cc
+++ b/gtk2_ardour/export_channel_selector.cc
@@ -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);
diff --git a/gtk2_ardour/export_channel_selector.h b/gtk2_ardour/export_channel_selector.h
index bc165273c7..3dbb9b8265 100644
--- a/gtk2_ardour/export_channel_selector.h
+++ b/gtk2_ardour/export_channel_selector.h
@@ -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:
diff --git a/gtk2_ardour/export_dialog.cc b/gtk2_ardour/export_dialog.cc
index 6351c512c1..76b62cde57 100644
--- a/gtk2_ardour/export_dialog.cc
+++ b/gtk2_ardour/export_dialog.cc
@@ -141,68 +141,26 @@ ExportDialog::init ()
}
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 ());
}
@@ -301,10 +260,33 @@ ExportDialog::show_conflicting_files ()
}
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 ());
}
diff --git a/gtk2_ardour/export_dialog.h b/gtk2_ardour/export_dialog.h
index 756a3e7b53..315780750e 100644
--- a/gtk2_ardour/export_dialog.h
+++ b/gtk2_ardour/export_dialog.h
@@ -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 ();
diff --git a/gtk2_ardour/export_format_dialog.cc b/gtk2_ardour/export_format_dialog.cc
index c5d1573d54..69c494e090 100644
--- a/gtk2_ardour/export_format_dialog.cc
+++ b/gtk2_ardour/export_format_dialog.cc
@@ -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
@@ -718,6 +729,18 @@ ExportFormatDialog::update_with_toc ()
}
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()
{
std::string text = ": " + format->description(false);
diff --git a/gtk2_ardour/export_format_dialog.h b/gtk2_ardour/export_format_dialog.h
index 3e38cf09d6..6b92625e7a 100644
--- a/gtk2_ardour/export_format_dialog.h
+++ b/gtk2_ardour/export_format_dialog.h
@@ -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;
diff --git a/gtk2_ardour/export_range_markers_dialog.cc b/gtk2_ardour/export_range_markers_dialog.cc
index 97a8dba25f..e69de29bb2 100644
--- a/gtk2_ardour/export_range_markers_dialog.cc
+++ b/gtk2_ardour/export_range_markers_dialog.cc
@@ -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;
-}
diff --git a/gtk2_ardour/export_range_markers_dialog.h b/gtk2_ardour/export_range_markers_dialog.h
index b0a29b5dc2..e69de29bb2 100644
--- a/gtk2_ardour/export_range_markers_dialog.h
+++ b/gtk2_ardour/export_range_markers_dialog.h
@@ -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__
diff --git a/gtk2_ardour/export_timespan_selector.cc b/gtk2_ardour/export_timespan_selector.cc
index d6ca02fe03..61d813d222 100644
--- a/gtk2_ardour/export_timespan_selector.cc
+++ b/gtk2_ardour/export_timespan_selector.cc
@@ -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)
{
diff --git a/gtk2_ardour/export_timespan_selector.h b/gtk2_ardour/export_timespan_selector.h
index 5556f5f676..1216670991 100644
--- a/gtk2_ardour/export_timespan_selector.h
+++ b/gtk2_ardour/export_timespan_selector.h
@@ -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
index 0000000000..39c50fe7b3
--- /dev/null
+++ b/gtk2_ardour/icons/soundcloud.png
Binary files differ
diff --git a/gtk2_ardour/soundcloud_export_selector.cc b/gtk2_ardour/soundcloud_export_selector.cc
new file mode 100644
index 0000000000..1ecab514ab
--- /dev/null
+++ b/gtk2_ardour/soundcloud_export_selector.cc
@@ -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
index 0000000000..7962ba8b06
--- /dev/null
+++ b/gtk2_ardour/soundcloud_export_selector.h
@@ -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;
+
+};
+
diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript
index 920f71bd7d..2cfd2ea497 100644
--- a/gtk2_ardour/wscript
+++ b/gtk2_ardour/wscript
@@ -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',
diff --git a/libs/ardour/ardour/export_format_manager.h b/libs/ardour/ardour/export_format_manager.h
index 9a95111509..dad7d84b72 100644
--- a/libs/ardour/ardour/export_format_manager.h
+++ b/libs/ardour/ardour/export_format_manager.h
@@ -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);
diff --git a/libs/ardour/ardour/export_format_specification.h b/libs/ardour/ardour/export_format_specification.h
index 1593990d35..d41fe3e97a 100644
--- a/libs/ardour/ardour/export_format_specification.h
+++ b/libs/ardour/ardour/export_format_specification.h
@@ -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 */
diff --git a/libs/ardour/ardour/export_handler.h b/libs/ardour/ardour/export_handler.h
index 1bc80a80e9..25a87045a8 100644
--- a/libs/ardour/ardour/export_handler.h
+++ b/libs/ardour/ardour/export_handler.h
@@ -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
index 0000000000..6b8700e784
--- /dev/null
+++ b/libs/ardour/ardour/soundcloud_upload.h
@@ -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__ */
diff --git a/libs/ardour/ardour/system_exec.h b/libs/ardour/ardour/system_exec.h
index 40e429720e..ae865c7bff 100644
--- a/libs/ardour/ardour/system_exec.h
+++ b/libs/ardour/ardour/system_exec.h
@@ -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) {
diff --git a/libs/ardour/export_format_manager.cc b/libs/ardour/export_format_manager.cc
index 890623c114..3ee940ffb6 100644
--- a/libs/ardour/export_format_manager.cc
+++ b/libs/ardour/export_format_manager.cc
@@ -294,6 +294,20 @@ ExportFormatManager::select_with_toc (bool value)
}
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)
{
current_selection->set_trim_beginning (value);
diff --git a/libs/ardour/export_format_specification.cc b/libs/ardour/export_format_specification.cc
index b139faeee2..8b921519f7 100644
--- a/libs/ardour/export_format_specification.cc
+++ b/libs/ardour/export_format_specification.cc
@@ -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 + ": ";
diff --git a/libs/ardour/export_handler.cc b/libs/ardour/export_handler.cc
index 20abc80de1..c9f20d182e 100644
--- a/libs/ardour/export_handler.cc
+++ b/libs/ardour/export_handler.cc
@@ -33,6 +33,10 @@
#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"
@@ -278,6 +282,13 @@ ExportHandler::process_normalize ()
}
void
+ExportHandler::command_output(std::string output, size_t size)
+{
+ std::cerr << "command: " << size << ", " << output << std::endl;
+ info << output << endmsg;
+}
+
+void
ExportHandler::finish_timespan ()
{
while (config_map.begin() != timespan_bounds.second) {
@@ -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
index 0000000000..f003d5ab65
--- /dev/null
+++ b/libs/ardour/soundcloud_upload.cc
@@ -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);
+}
+
diff --git a/libs/ardour/system_exec.cc b/libs/ardour/system_exec.cc
index 90e729a7f6..760a9b7878 100644
--- a/libs/ardour/system_exec.cc
+++ b/libs/ardour/system_exec.cc
@@ -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() { }
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index 4a50064545..85326e98f8 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -194,6 +194,7 @@ libardour_sources = [
'sndfile_helpers.cc',
'sndfileimportable.cc',
'sndfilesource.cc',
+ 'soundcloud_upload.cc',
'source.cc',
'source_factory.cc',
'speakers.cc',
diff --git a/libs/pbd/pbd/system_exec.h b/libs/pbd/pbd/system_exec.h
index 1232175fec..ce6e5a9c4f 100644
--- a/libs/pbd/pbd/system_exec.h
+++ b/libs/pbd/pbd/system_exec.h
@@ -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.
diff --git a/libs/pbd/system_exec.cc b/libs/pbd/system_exec.cc
index 0102323505..82398af0c8 100644
--- a/libs/pbd/system_exec.cc
+++ b/libs/pbd/system_exec.cc
@@ -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) {