--- /dev/null
+<?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>
--- /dev/null
+<?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>
setup_order_hint();
- PBD::ScopedConnection idle_connection;
-
string template_path = add_route_dialog->track_template();
if (!template_path.empty()) {
session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template);
break;
}
-
- /* idle connection will end at scope end */
}
void
_summary->set_dirty ();
}
-TimeAxisView*
+RouteTimeAxisView*
Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
{
TrackViewList::const_iterator j = track_views.begin ();
/* track views */
TrackViewList track_views;
std::pair<TimeAxisView*, double> trackview_by_y_position (double);
- TimeAxisView* axis_view_from_route (boost::shared_ptr<ARDOUR::Route>) const;
+ RouteTimeAxisView* axis_view_from_route (boost::shared_ptr<ARDOUR::Route>) const;
TrackViewList get_tracks_for_range_action () const;
using namespace Gtkmm2ext;
using namespace Editing;
-/* XXX this is a hack. it ought to be the maximum value of an framepos_t */
-
-const double max_canvas_coordinate = (double) UINT32_MAX;
-
void
Editor::initialize_canvas ()
{
}
- _canvas_bottom_rect = new ArdourCanvas::Rectangle (_track_canvas->root(), ArdourCanvas::Rect (0.0, 0.0, max_canvas_coordinate, 20));
+ _canvas_bottom_rect = new ArdourCanvas::Rectangle (_track_canvas->root(), ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 20));
/* this thing is transparent */
_canvas_bottom_rect->set_fill (false);
_canvas_bottom_rect->set_outline (false);
#include "ardour/audioengine.h"
#include "ardour/audioregion.h"
+#include "ardour/audio_track.h"
#include "ardour/dB.h"
#include "ardour/midi_region.h"
+#include "ardour/midi_track.h"
#include "ardour/operations.h"
#include "ardour/region_factory.h"
#include "ardour/session.h"
void
RegionMotionDrag::motion (GdkEvent* event, bool first_move)
{
- assert (!_views.empty ());
+ double delta_layer = 0;
+ int delta_time_axis_view = 0;
- /* Find the TimeAxisView that the pointer is now over */
- pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ assert (!_views.empty ());
- /* Bail early if we're not over a track */
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
+ /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
- if (!rtv || !rtv->is_track()) {
- _editor->verbose_cursor()->hide ();
- return;
- }
+ /* Find the TimeAxisView that the pointer is now over */
+ pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ TimeAxisView* tv = r.first;
- if (first_move && tv.first->view()->layer_display() == Stacked) {
- tv.first->view()->set_layer_display (Expanded);
- }
+ if (tv) {
- /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
+ double layer = r.second;
+
+ if (first_move && tv->view()->layer_display() == Stacked) {
+ tv->view()->set_layer_display (Expanded);
+ }
- /* Here's the current pointer position in terms of time axis view and layer */
- int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
- double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
+ /* Here's the current pointer position in terms of time axis view and layer */
+ int const current_pointer_time_axis_view = find_time_axis_view (tv);
+ double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
+
+ /* Work out the change in y */
+ delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
+ delta_layer = current_pointer_layer - _last_pointer_layer;
+ }
+
/* Work out the change in x */
framepos_t pending_region_position;
double const x_delta = compute_x_delta (event, &pending_region_position);
- /* Work out the change in y */
-
- int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
- double delta_layer = current_pointer_layer - _last_pointer_layer;
-
+ /* Verify change in y */
if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
/* this y movement is not allowed, so do no y movement this time */
delta_time_axis_view = 0;
this_delta_layer = - i->layer;
}
- /* The TimeAxisView that this region is now on */
- TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
+ if (tv) {
- /* Ensure it is moved from stacked -> expanded if appropriate */
- if (tv->view()->layer_display() == Stacked) {
- tv->view()->set_layer_display (Expanded);
- }
+ /* The TimeAxisView that this region is now on */
+ TimeAxisView* current_tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
+
+ /* Ensure it is moved from stacked -> expanded if appropriate */
+ if (current_tv->view()->layer_display() == Stacked) {
+ current_tv->view()->set_layer_display (Expanded);
+ }
- /* We're only allowed to go -ve in layer on Expanded views */
- if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
- this_delta_layer = - i->layer;
- }
+ /* We're only allowed to go -ve in layer on Expanded views */
+ if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
+ this_delta_layer = - i->layer;
+ }
- /* Set height */
- rv->set_height (tv->view()->child_height ());
+ /* Set height */
+ rv->set_height (current_tv->view()->child_height ());
- /* Update show/hidden status as the region view may have come from a hidden track,
- or have moved to one.
- */
- if (tv->hidden ()) {
- rv->get_canvas_group()->hide ();
- } else {
- rv->get_canvas_group()->show ();
- }
+ /* Update show/hidden status as the region view may have come from a hidden track,
+ or have moved to one.
+ */
+ if (current_tv->hidden ()) {
+ rv->get_canvas_group()->hide ();
+ } else {
+ rv->get_canvas_group()->show ();
+ }
- /* Update the DraggingView */
- i->time_axis_view += delta_time_axis_view;
- i->layer += this_delta_layer;
+ /* Update the DraggingView */
+ i->time_axis_view += delta_time_axis_view;
+ i->layer += this_delta_layer;
- if (_brushing) {
- _editor->mouse_brush_insert_region (rv, pending_region_position);
- } else {
- double x = 0;
- double y = 0;
+ if (_brushing) {
+ _editor->mouse_brush_insert_region (rv, pending_region_position);
+ } else {
+ double x = 0;
+ double y = 0;
- /* Get the y coordinate of the top of the track that this region is now on */
- tv->canvas_display()->item_to_canvas (x, y);
+ /* Get the y coordinate of the top of the track that this region is now on */
+ current_tv->canvas_display()->item_to_canvas (x, y);
- /* And adjust for the layer that it should be on */
- StreamView* cv = tv->view ();
- switch (cv->layer_display ()) {
- case Overlaid:
- break;
- case Stacked:
- y += (cv->layers() - i->layer - 1) * cv->child_height ();
- break;
- case Expanded:
- y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
- break;
- }
+ /* And adjust for the layer that it should be on */
+ StreamView* cv = current_tv->view ();
+ switch (cv->layer_display ()) {
+ case Overlaid:
+ break;
+ case Stacked:
+ y += (cv->layers() - i->layer - 1) * cv->child_height ();
+ break;
+ case Expanded:
+ y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
+ break;
+ }
- /* Now move the region view */
+ /* Now move the region view */
+ rv->move (x_delta, y - rv->get_canvas_group()->position().y);
+ }
+ } else {
+ double y = 0;
+ double x = 0;
+
+ TimeAxisView* last = _time_axis_views.back();
+ last->canvas_display()->item_to_canvas (x, y);
+ y += last->effective_height();
rv->move (x_delta, y - rv->get_canvas_group()->position().y);
+ i->time_axis_view = -1;
}
} /* foreach region */
_editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
}
+RouteTimeAxisView*
+RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region)
+{
+ /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
+ new track.
+ */
+
+ try {
+ if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
+ list<boost::shared_ptr<AudioTrack> > audio_tracks;
+ audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
+ return _editor->axis_view_from_route (audio_tracks.front());
+ } else {
+ ChanCount one_midi_port (DataType::MIDI, 1);
+ list<boost::shared_ptr<MidiTrack> > midi_tracks;
+ midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
+ return _editor->axis_view_from_route (midi_tracks.front());
+ }
+ } catch (...) {
+ error << _("Could not create new track after region placed in the drop zone") << endmsg;
+ return 0;
+ }
+}
+
void
RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
{
RegionSelection new_views;
PlaylistSet modified_playlists;
- list<RegionView*> views_to_delete;
+ RouteTimeAxisView* new_time_axis_view = 0;
if (_brushing) {
/* all changes were made during motion event handlers */
}
/* insert the regions into their new playlists */
- for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
+
+ RouteTimeAxisView* dest_rtv = 0;
if (i->view->region()->locked() || i->view->region()->video_locked()) {
continue;
} else {
where = i->view->region()->position();
}
-
- RegionView* new_view = insert_region_into_playlist (
- i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
- );
-
- if (new_view == 0) {
- continue;
+
+ if (i->time_axis_view < 0) {
+ if (!new_time_axis_view) {
+ new_time_axis_view = create_destination_time_axis (i->view->region());
+ }
+ dest_rtv = new_time_axis_view;
+ } else {
+ dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
+ }
+
+ if (dest_rtv != 0) {
+ RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
+ if (new_view != 0) {
+ new_views.push_back (new_view);
+ }
}
+
+ /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
+ since deletion will automagically remove it from _views, thus invalidating i as an iterator.
+ */
- new_views.push_back (new_view);
-
- /* we don't need the copied RegionView any more */
- views_to_delete.push_back (i->view);
- }
-
- /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
- because when views are deleted they are automagically removed from _views, which messes
- up the iteration.
- */
- for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
- delete *i;
+ list<DraggingView>::const_iterator next = i;
+ ++next;
+ delete i->view;
+ i = next;
}
/* If we've created new regions either by copying or moving
PlaylistSet modified_playlists;
PlaylistSet frozen_playlists;
set<RouteTimeAxisView*> views_to_update;
+ RouteTimeAxisView* new_time_axis_view = 0;
if (_brushing) {
/* all changes were made during motion event handlers */
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
RegionView* rv = i->view;
-
- RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
- double const dest_layer = i->layer;
+ RouteTimeAxisView* dest_rtv = 0;
if (rv->region()->locked() || rv->region()->video_locked()) {
++i;
continue;
}
+
+ if (i->time_axis_view < 0) {
+ if (!new_time_axis_view) {
+ new_time_axis_view = create_destination_time_axis (rv->region());
+ }
+ dest_rtv = new_time_axis_view;
+ } else {
+ dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
+ }
+
+ assert (dest_rtv);
+ double const dest_layer = i->layer;
+
views_to_update.insert (dest_rtv);
framepos_t where;
void add_stateful_diff_commands_for_playlists (PlaylistSet const &);
void collect_new_region_view (RegionView *);
+ RouteTimeAxisView* create_destination_time_axis (boost::shared_ptr<ARDOUR::Region>);
bool _copy;
RegionView* _new_region_view;
long millisecs;
left = sample;
- hrs = left / (sample_rate * 60 * 60);
- left -= hrs * sample_rate * 60 * 60;
- mins = left / (sample_rate * 60);
- left -= mins * sample_rate * 60;
- secs = left / sample_rate;
- left -= secs * sample_rate;
- millisecs = left * 1000 / sample_rate;
+ hrs = left / (sample_rate * 60 * 60 * 1000);
+ left -= hrs * sample_rate * 60 * 60 * 1000;
+ mins = left / (sample_rate * 60 * 1000);
+ left -= mins * sample_rate * 60 * 1000;
+ secs = left / (sample_rate * 1000);
+ left -= secs * sample_rate * 1000;
+ millisecs = left / sample_rate;
*millisecs_p = millisecs;
*secs_p = secs;
return;
}
- fr = _session->frame_rate();
+ fr = _session->frame_rate() * 1000;
/* to prevent 'flashing' */
if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
lower = 0;
}
upper += spacer;
- framecnt_t const range = upper - lower;
+ framecnt_t const range = (upper - lower) * 1000;
if (range < (fr / 50)) {
minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
}
*marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * minsec_nmarks);
- pos = ((((framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
+ pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
switch (minsec_ruler_scale) {
case minsec_show_seconds:
for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
(*marks)[n].style = GtkCustomRulerMarkMicro;
}
(*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
+ (*marks)[n].position = pos/1000.0;
}
break;
case minsec_show_minutes:
(*marks)[n].style = GtkCustomRulerMarkMicro;
}
(*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
+ (*marks)[n].position = pos/1000.0;
}
break;
case minsec_show_hours:
(*marks)[n].style = GtkCustomRulerMarkMicro;
}
(*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
+ (*marks)[n].position = pos/1000.0;
}
break;
case minsec_show_frames:
for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
if (millisecs % minsec_mark_modulo == 0) {
- if (secs == 0) {
+ if (millisecs == 0) {
(*marks)[n].style = GtkCustomRulerMarkMajor;
} else {
(*marks)[n].style = GtkCustomRulerMarkMinor;
(*marks)[n].style = GtkCustomRulerMarkMicro;
}
(*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
+ (*marks)[n].position = pos/1000.0;
}
break;
}
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 ();
// 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);
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:
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
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 ());
}
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) {
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 ());
}
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 ());
}
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
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 ());
}
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 ());
}
#include "export_file_notebook.h"
#include "export_preset_selector.h"
#include "ardour_dialog.h"
+#include "soundcloud_export_selector.h"
#include <gtkmm.h>
class ExportTimespanSelector;
class ExportChannelSelector;
-class ExportDialog : public ArdourDialog {
+class ExportDialog : public ArdourDialog, public PBD::ScopedConnectionList
+{
public:
// 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 ();
PublicEditor & editor;
StatusPtr status;
- /*** GUI components ***/
- Glib::RefPtr<Gtk::SizeGroup> advanced_sizegroup;
- Gtk::Expander * advanced;
/* Warning area */
float previous_progress; // Needed for gtk bug workaround
+ void soundcloud_upload_progress(double total, double now, std::string title);
+
/* Buttons */
Gtk::Button * cancel_button;
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 ();
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),
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();
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);
}
tag_checkbox.set_active (spec->tag());
+ upload_checkbox.set_active (spec->upload());
+ command_entry.set_text (spec->command());
}
void
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()
{
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
void update_with_toc ();
void update_with_cue ();
+ void update_upload ();
+ void update_command ();
Gtk::TreeView sample_format_view;
Gtk::TreeView dither_type_view;
-/*
- 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;
-}
-/*
- 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__
/* 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);
}
}
+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)
{
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 ***/
Gtk::ScrolledWindow range_scroller;
};
-/// Allows seleting multiple timespans
+/// Allows selecting multiple timespans
class ExportTimespanSelectorMultiple : public ExportTimespanSelector
{
public:
VSTState* vstfx;
int LXVST_sched_timer_interval = 40; //ms, 25fps
XEvent event;
- struct timeval clock1, clock2;
+ uint64_t clock1, clock2;
- gettimeofday(&clock1, NULL);
+ clock1 = g_get_monotonic_time();
/*The 'Forever' loop - runs the plugin UIs etc - based on the FST gui event loop*/
while (!gui_quit)
/*See if its time for us to do a scheduled event pass on all the plugins*/
- gettimeofday(&clock2, NULL);
- const int elapsed_time = (clock2.tv_sec - clock1.tv_sec) * 1000 + (clock2.tv_usec - clock1.tv_usec) / 1000;
+ clock2 = g_get_monotonic_time();
+ const int64_t elapsed_time_ms = (clock2 - clock1) / 1000;
- if((LXVST_sched_timer_interval != 0) && elapsed_time >= LXVST_sched_timer_interval)
+ if((LXVST_sched_timer_interval != 0) && elapsed_time_ms >= LXVST_sched_timer_interval)
{
- //printf("elapsed %d ms ^= %.2f Hz\n", elapsed_time, 1000.0/(double)elapsed_time); // DEBUG
+ //printf("elapsed %d ms ^= %.2f Hz\n", elapsed_time_ms, 1000.0/(double)elapsed_time_ms); // DEBUG
pthread_mutex_lock (&plugin_mutex);
again:
}
pthread_mutex_unlock (&plugin_mutex);
- gettimeofday(&clock1, NULL);
+ clock1 = g_get_monotonic_time();
}
}
SessionDialog::existing_session_selected ()
{
_existing_session_chooser_used = true;
+ recent_session_display.get_selection()->unselect_all();
/* mark this sensitive in case we come back here after a failed open
* attempt and the user has hacked up the fix. sigh.
*/
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+/*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;
+
+};
+
'session_option_editor.cc',
'sfdb_ui.cc',
'shuttle_control.cc',
+ 'soundcloud_export_selector.cc',
'splash.cc',
'speaker_dialog.cc',
'startup.cc',
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);
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; }
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; }
float _normalize_target;
bool _with_toc;
bool _with_cue;
+ bool _upload;
+ std::string _command;
/* serialization helpers */
#include "ardour/session.h"
#include "ardour/libardour_visibility.h"
#include "ardour/types.h"
+#include "pbd/signals.h"
namespace AudioGrapher {
class BroadcastInfo;
Session & session;
};
-class LIBARDOUR_API ExportHandler : public ExportElementFactory
+class LIBARDOUR_API ExportHandler : public ExportElementFactory, public sigc::trackable
{
public:
struct FileSpec {
friend boost::shared_ptr<ExportHandler> Session::get_export_handler();
ExportHandler (Session & session);
+ void command_output(std::string output, size_t size);
+
public:
~ExportHandler ();
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();
--- /dev/null
+/* 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__ */
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) {
process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!diskstream->record_enabled() && _session.transport_rolling()));
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
+ if (d) {
+ d->flush_buffers (nframes);
+ }
+ }
+
need_butler = diskstream->commit (playback_distance);
return 0;
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)
{
, _normalize_target (1.0)
, _with_toc (false)
, _with_cue (false)
+ , _upload (false)
+ , _command ("")
{
format_ids.insert (F_None);
endiannesses.insert (E_FileDefault);
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()));
_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"))) {
components.push_back ("CUE");
}
+ if (_upload) {
+ components.push_back ("Upload");
+ }
+
+ if (!_command.empty()) {
+ components.push_back ("+");
+ }
+
string desc;
if (include_name) {
desc = _name + ": ";
#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"
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 ()
{
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) {
--- /dev/null
+/* 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);
+}
+
#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() { }
TempoSection* ts;
MeterSection* ms;
double beat_frames;
+ double current_frame_exact;
framepos_t bar_start_frame;
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
}
beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
+ current_frame_exact = current_frame;
while (current_frame < end) {
current.beats++;
- current_frame += beat_frames;
+ current_frame_exact += beat_frames;
+ current_frame = llrint(current_frame_exact);
if (current.beats > meter->divisions_per_bar()) {
current.bars++;
tempo->start(), current_frame, tempo->bar_offset()));
/* back up to previous beat */
- current_frame -= beat_frames;
+ current_frame_exact -= beat_frames;
+ current_frame = llrint(current_frame_exact);
/* set tempo section location
* based on offset from last
double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
- current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+ current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+ current_frame = llrint(current_frame_exact);
/* next metric doesn't have to
* match this precisely to
if (current.beats == 1) {
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
- _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
+ _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
bar_start_frame = current_frame;
} else {
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
- _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
+ _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
}
if (next_metric == metrics.end()) {
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
size_t count = std::max (scratch_buffers->available().get(*t), howmany.get(*t));
- size_t size = _engine->raw_buffer_size (*t) / sizeof (Sample);
+ size_t size = (*t == DataType::MIDI)
+ ? _engine->raw_buffer_size (*t)
+ : _engine->raw_buffer_size (*t) / sizeof (Sample);
scratch_buffers->ensure_buffers (*t, count, size);
mix_buffers->ensure_buffers (*t, count, size);
#include <glib/gstdio.h>
#include <glibmm.h>
+#include "pbd/error.h"
-#ifdef VST_SCANNER_APP
-#define errormsg cerr
-#define warningmsg cerr
-#define endmsg endl
-#else
+#ifndef VST_SCANNER_APP
#include "ardour/plugin_manager.h" // scanner_bin_path
#include "ardour/rc_configuration.h"
#include "ardour/system_exec.h"
-#include "pbd/error.h"
-#define errormsg PBD::error
-#define warningmsg PBD::warning
#endif
#include "ardour/filesystem_paths.h"
} else if (infos->size() == 1) {
vstfx_write_info_block(fp, infos->front());
} else {
- errormsg << "Zero plugins in VST." << endmsg; // XXX here? rather make this impossible before if it ain't already.
+ PBD::error << "Zero plugins in VST." << endmsg; // XXX here? rather make this impossible before if it ain't already.
}
}
::g_unlink(vstfx_blacklist_path (dllpath, 1).c_str());
}
-#ifndef VST_SCANNER_APP
/** remove info file from cache */
static void
vstfx_remove_infofile (const char *dllpath)
::g_unlink(vstfx_infofile_path (dllpath, 0).c_str());
::g_unlink(vstfx_infofile_path (dllpath, 1).c_str());
}
-#endif
/** helper function, check if cache is newer than plugin
* @return path to cache file */
rv = vstfx_load_info_file(infofile, infos);
fclose (infofile);
if (!rv) {
- warningmsg << "Cannot get VST information form " << dllpath << ": info file load failed." << endmsg;
+ PBD::warning << "Cannot get VST information form " << dllpath << ": info file load failed." << endmsg;
}
}
return rv;
VSTHandle* h;
VSTState* vstfx;
if (!(h = vstfx_load(dllpath))) {
- warningmsg << "Cannot get LinuxVST information from " << dllpath << ": load failed." << endmsg;
+ PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": load failed." << endmsg;
return false;
}
if (!(vstfx = vstfx_instantiate(h, simple_master_callback, 0))) {
vstfx_unload(h);
- warningmsg << "Cannot get LinuxVST information from " << dllpath << ": instantiation failed." << endmsg;
+ PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": instantiation failed." << endmsg;
return false;
}
VSTHandle* h;
VSTState* vstfx;
if(!(h = fst_load(dllpath))) {
- warningmsg << "Cannot get Windows VST information from " << dllpath << ": load failed." << endmsg;
+ PBD::warning << "Cannot get Windows VST information from " << dllpath << ": load failed." << endmsg;
return false;
}
if(!(vstfx = fst_instantiate(h, simple_master_callback, 0))) {
fst_unload(&h);
vstfx_current_loading_id = 0;
- warningmsg << "Cannot get Windows VST information from " << dllpath << ": instantiation failed." << endmsg;
+ PBD::warning << "Cannot get Windows VST information from " << dllpath << ": instantiation failed." << endmsg;
return false;
}
vstfx_current_loading_id = 0;
static void parse_scanner_output (std::string msg, size_t /*len*/)
{
if (!_errorlog_fd && !_errorlog_dll) {
- errormsg << "VST scanner: " << msg;
+ PBD::error << "VST scanner: " << msg;
return;
}
if (!_errorlog_fd) {
if (!(_errorlog_fd = fopen(vstfx_errorfile_path(_errorlog_dll, 0).c_str(), "w"))) {
if (!(_errorlog_fd = fopen(vstfx_errorfile_path(_errorlog_dll, 1).c_str(), "w"))) {
- errormsg << "Cannot create plugin error-log for plugin " << _errorlog_dll;
+ PBD::error << "Cannot create plugin error-log for plugin " << _errorlog_dll;
free(_errorlog_dll);
_errorlog_dll = NULL;
}
if (_errorlog_fd) {
fprintf (_errorlog_fd, "%s\n", msg.c_str());
} else {
- errormsg << "VST scanner: " << msg;
+ PBD::error << "VST scanner: " << msg;
}
}
PBD::ScopedConnectionList cons;
scanner.ReadStdout.connect_same_thread (cons, boost::bind (&parse_scanner_output, _1 ,_2));
if (scanner.start (2 /* send stderr&stdout via signal */)) {
- errormsg << "Cannot launch VST scanner app '" << scanner_bin_path << "': "<< strerror(errno) << endmsg;
+ PBD::error << "Cannot launch VST scanner app '" << scanner_bin_path << "': "<< strerror(errno) << endmsg;
close_error_log();
return infos;
} else {
/* crate cache/whitelist */
infofile = vstfx_infofile_for_write (dllpath);
if (!infofile) {
- warningmsg << "Cannot cache VST information for " << dllpath << ": cannot create new FST info file." << endmsg;
+ PBD::warning << "Cannot cache VST information for " << dllpath << ": cannot create new FST info file." << endmsg;
return infos;
} else {
vstfx_write_info_file (infofile, infos);
/* if the directory doesn't exist, try to create it */
if (!Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) {
if (g_mkdir (dir.c_str (), 0700)) {
- errormsg << "Cannot create VST blacklist folder '" << dir << "'" << endmsg;
+ PBD::error << "Cannot create VST blacklist folder '" << dir << "'" << endmsg;
//exit(1);
}
}
/* if the directory doesn't exist, try to create it */
if (!Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) {
if (g_mkdir (dir.c_str (), 0700)) {
- errormsg << "Cannot create VST info folder '" << dir << "'" << endmsg;
+ PBD::error << "Cannot create VST info folder '" << dir << "'" << endmsg;
//exit(1);
}
}
{
VstParameterProperties prop;
+ memset (&prop, 0, sizeof (VstParameterProperties));
desc.min_unbound = false;
desc.max_unbound = false;
prop.flags = 0;
if (_plugin->dispatcher (_plugin, effGetParameterProperties, which, 0, &prop, 0)) {
/* i have yet to find or hear of a VST plugin that uses this */
+ /* RG: faust2vsti does use this :) */
if (prop.flags & kVstParameterUsesIntegerMinMax) {
desc.lower = prop.minInteger;
desc.largestep = desc.step * 10.0f;
}
+ if (strlen(prop.label) == 0) {
+ _plugin->dispatcher (_plugin, effGetParamName, which, 0, prop.label, 0);
+ }
+
desc.toggled = prop.flags & kVstParameterIsSwitch;
desc.logarithmic = false;
desc.sr_dependent = false;
'sndfile_helpers.cc',
'sndfileimportable.cc',
'sndfilesource.cc',
+ 'soundcloud_upload.cc',
'source.cc',
'source_factory.cc',
'speakers.cc',
, _dsp_load (0)
, _n_inputs (0)
, _n_outputs (0)
+ , _n_midi_inputs (0)
+ , _n_midi_outputs (0)
, _systemic_input_latency (0)
, _systemic_output_latency (0)
, _processed_samples (0)
DummyAudioBackend::enumerate_midi_options () const
{
std::vector<std::string> m;
- m.push_back (_("None"));
+ m.push_back (_("1 in, 1 out"));
+ m.push_back (_("2 in, 2 out"));
+ m.push_back (_("8 in, 8 out"));
return m;
}
int
-DummyAudioBackend::set_midi_option (const std::string&)
+DummyAudioBackend::set_midi_option (const std::string& opt)
{
+ if (opt == _("1 in, 1 out")) {
+ _n_midi_inputs = _n_midi_outputs = 1;
+ }
+ else if (opt == _("2 in, 2 out")) {
+ _n_midi_inputs = _n_midi_outputs = 2;
+ }
+ else if (opt == _("8 in, 8 out")) {
+ _n_midi_inputs = _n_midi_outputs = 8;
+ }
+ else {
+ _n_midi_inputs = _n_midi_outputs = 0;
+ }
return -1;
}
const int a_ins = _n_inputs > 0 ? _n_inputs : 8;
const int a_out = _n_outputs > 0 ? _n_outputs : 8;
- const int m_ins = 2; // TODO
- const int m_out = 2;
+ const int m_ins = _n_midi_inputs > 0 ? _n_midi_inputs : 2;
+ const int m_out = _n_midi_outputs > 0 ? _n_midi_outputs : 2;
/* audio ports */
lr.min = lr.max = _samples_per_period + _systemic_input_latency;
_running = true;
_processed_samples = 0;
- struct timeval clock1, clock2;
- ::gettimeofday (&clock1, NULL);
+ uint64_t clock1, clock2;
+ clock1 = g_get_monotonic_time();
while (_running) {
if (engine.process_callback (_samples_per_period)) {
return 0;
}
_processed_samples += _samples_per_period;
if (!_freewheeling) {
- ::gettimeofday (&clock2, NULL);
- const int elapsed_time = (clock2.tv_sec - clock1.tv_sec) * 1000000 + (clock2.tv_usec - clock1.tv_usec);
- const int nomial_time = 1000000 * _samples_per_period / _samplerate;
+ clock2 = g_get_monotonic_time();
+ const int64_t elapsed_time = clock2 - clock1;
+ const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate;
_dsp_load = elapsed_time / (float) nomial_time;
if (elapsed_time < nomial_time) {
Glib::usleep (nomial_time - elapsed_time);
_dsp_load = 1.0;
Glib::usleep (100); // don't hog cpu
}
- ::gettimeofday (&clock1, NULL);
+ clock1 = g_get_monotonic_time();
}
_running = false;
return 0;
uint32_t _n_inputs;
uint32_t _n_outputs;
+ uint32_t _n_midi_inputs;
+ uint32_t _n_midi_outputs;
+
uint32_t _systemic_input_latency;
uint32_t _systemic_output_latency;
namespace wvNS {
UMicroseconds& UMicroseconds::ReadTime()
{
+ // Note: g_get_monotonic_time() may be a viable alternative
+ // (it is on Linux and OSX); if not, this code should really go into libpbd
#ifdef PLATFORM_WINDOWS
LARGE_INTEGER Frequency, Count ;
QueryPerformanceFrequency(&Frequency) ;
QueryPerformanceCounter(&Count);
theTime = uint64_t((Count.QuadPart * 1000000.0 / Frequency.QuadPart));
-#endif
-#if defined(__linux__) || defined(__APPLE__)
-// Mac code replaced by posix calls, to reduce Carbon dependency.
- timeval buf;
+#elif defined __MACH__ // OSX, BSD..
+
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+ host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+ theTime = (uint64_t)mts.tv_sec * 1e6 + (uint64_t)mts.tv_nsec / 1000;
+
+#else // Linux, POSIX
- gettimeofday(&buf,NULL);
+ struct timespec *ts
+ clock_gettime(CLOCK_MONOTONIC, ts);
+ theTime = (uint64_t)ts.tv_sec * 1e6 + (uint64_t)buf.tv_nsec / 1000;
- // micro sec
- theTime = uint64_t(buf.tv_sec) * 1000*1000 + buf.tv_usec;
#endif
return *this;
#include <string.h>
#include <vector>
+#include "pbd/pbd.h"
+#include "pbd/transmitter.h"
+#include "pbd/receiver.h"
+
#include "ardour/filesystem_paths.h"
#ifdef LXVST_SUPPORT
#include "ardour/linux_vst_support.h"
#endif
#include "../ardour/filesystem_paths.cc"
#include "../ardour/directory_names.cc"
-#include "../pbd/error.cc"
-#include "../pbd/basename.cc"
-#include "../pbd/search_path.cc"
-#include "../pbd/transmitter.cc"
-#include "../pbd/whitespace.cc"
+
#ifdef LXVST_SUPPORT
void
vstfx_destroy_editor (VSTState* /*vstfx*/) { }
#endif
+using namespace PBD;
+
+class DummyReceiver : public Receiver {
+ protected:
+ void receive (Transmitter::Channel chn, const char * str) {
+ const char *prefix = "";
+ switch (chn) {
+ case Transmitter::Error:
+ prefix = "[ERROR]: ";
+ break;
+ case Transmitter::Info:
+ /* ignore */
+ return;
+ case Transmitter::Warning:
+ prefix = "[WARNING]: ";
+ break;
+ case Transmitter::Fatal:
+ prefix = "[FATAL]: ";
+ break;
+ case Transmitter::Throw:
+ abort ();
+ }
+
+ std::cerr << prefix << str << std::endl;
+
+ if (chn == Transmitter::Fatal) {
+ ::exit (1);
+ }
+ }
+};
+
+DummyReceiver dummy_receiver;
+
int main (int argc, char **argv) {
- if (argc != 2) {
- fprintf(stderr, "usage: %s <vst>\n", argv[0]);
+ char *dllpath = NULL;
+ if (argc == 3 && !strcmp("-f", argv[1])) {
+ dllpath = argv[2];
+ if (strstr (dllpath, ".so" ) || strstr(dllpath, ".dll")) {
+ vstfx_remove_infofile(dllpath);
+ vstfx_un_blacklist(dllpath);
+ }
+
+ }
+ else if (argc != 2) {
+ fprintf(stderr, "usage: %s [-f] <vst>\n", argv[0]);
return EXIT_FAILURE;
+ } else {
+ dllpath = argv[1];
}
- char *dllpath = argv[1];
+ PBD::init();
+
+ dummy_receiver.listen_to (error);
+ dummy_receiver.listen_to (info);
+ dummy_receiver.listen_to (fatal);
+ dummy_receiver.listen_to (warning);
+
std::vector<VSTInfo *> *infos = 0;
+
+ if (0) { }
#ifdef LXVST_SUPPORT
- if (strstr (dllpath, ".so")) {
+ else if (strstr (dllpath, ".so")) {
infos = vstfx_get_info_lx(dllpath);
}
#endif
#ifdef WINDOWS_VST_SUPPORT
- if (strstr (dllpath, ".dll")) {
+ else if (strstr (dllpath, ".dll")) {
infos = vstfx_get_info_fst(dllpath);
}
#endif
+ else {
+ fprintf(stderr, "'%s' is not a supported VST plugin.\n", dllpath);
+ }
+
+ PBD::cleanup();
if (!infos || infos->empty()) {
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
}
-
private:
void underlying_adjustment_value_changed();
- struct timeval last_vc;
+ int64_t last_vc;
gint timeout_handler();
bool timeout_queued;
};
{
adj.signal_value_changed().connect (mem_fun (*this, &IdleAdjustment::underlying_adjustment_value_changed));
timeout_queued = 0;
- gettimeofday (&last_vc, 0);
+ last_vc = g_get_monotonic_time();
}
IdleAdjustment::~IdleAdjustment ()
void
IdleAdjustment::underlying_adjustment_value_changed ()
{
- gettimeofday (&last_vc, 0);
+ last_vc = g_get_monotonic_time();
if (timeout_queued) {
return;
gint
IdleAdjustment::timeout_handler ()
{
- struct timeval now;
- struct timeval tdiff;
+ int64_t now, tdiff;
+ now = g_get_monotonic_time();
+ tdiff = now - last_vc;
- gettimeofday (&now, 0);
+ std::cerr << "timer elapsed, diff = " << tdiff << " usec" << std::endl;
- timersub (&now, &last_vc, &tdiff);
-
- std::cerr << "timer elapsed, diff = " << tdiff.tv_sec << " + " << tdiff.tv_usec << std::endl;
-
- if (tdiff.tv_sec > 0 || tdiff.tv_usec > 250000) {
+ if (tdiff > 250000) {
std::cerr << "send signal\n";
value_changed ();
timeout_queued = false;
#include <string>
#include <pthread.h>
#include <signal.h>
+#include <map>
+
#ifdef NOPBD /* unit-test outside ardour */
#include <sigc++/bind.h>
#include <sigc++/signal.h>
*
*/
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
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;
#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.
}
#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;
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);
}
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 ();
*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) {
framepos_t last_where;
ARDOUR::gain_t last_track_gain;
uint32_t last_meter_fill;
- struct timeval last_wheel_motion;
+ uint64_t last_wheel_motion;
int last_wheel_dir;
Glib::Mutex io_lock;
*/
unsigned long start()
{
-#ifdef _WIN32
- _start = (unsigned long)::GetTickCount();
-#else
- gettimeofday ( &_start, 0 );
-#endif
- running = true;
-#ifdef _WIN32
- return _start;
-#else
- return ( _start.tv_sec * 1000000 + _start.tv_usec ) / 1000;
-#endif
+ _start = g_get_monotonic_time();
+ return _start / 1000;
}
/**
*/
unsigned long stop()
{
-#ifdef _WIN32
- _stop = (unsigned long)::GetTickCount();
-#else
- gettimeofday ( &_stop, 0 );
-#endif
- running = false;
+ _stop = g_get_monotonic_time();
return elapsed();
}
{
if ( running )
{
-#ifdef _WIN32
- DWORD current = ::GetTickCount();
- return current - _start;
-#else
- struct timeval current;
- gettimeofday ( ¤t, 0 );
- return (
- ( current.tv_sec * 1000000 + current.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
- ) / 1000
- ;
-#endif
+ uint64_t now = g_get_monotonic_time();
+ return (now - _start) / 1000;
}
else
{
-#ifdef _WIN32
- return _stop - _start;
-#else
- return (
- ( _stop.tv_sec * 1000000 + _stop.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
- ) / 1000
- ;
-#endif
+ return (_stop - _start) / 1000;
}
}
}
private:
-#ifdef _WIN32
- unsigned long _start;
- unsigned long _stop;
-#else
- struct timeval _start;
- struct timeval _stop;
-#endif
+ uint64_t _start;
+ uint64_t _stop;
bool running;
};
prev_track ();
}
- timerclear (&last_wheel_motion);
+ last_wheel_motion = 0;
} else if ((buttonmask & ButtonPrev) || (buttonmask & ButtonNext)) {
prev_marker ();
}
- timerclear (&last_wheel_motion);
+ last_wheel_motion = 0;
} else if (buttonmask & ButtonShift) {
}
}
- timerclear (&last_wheel_motion);
+ last_wheel_motion = 0;
} else {
TranzportControlProtocol::scrub ()
{
float speed;
- struct timeval now;
- struct timeval delta;
+ uint64_t now;
int dir;
- gettimeofday (&now, 0);
+ now = g_get_monotonic_time();
if (_datawheel < WheelDirectionThreshold) {
dir = 1;
/* changed direction, start over */
speed = 0.1f;
} else {
- if (timerisset (&last_wheel_motion)) {
-
- timersub (&now, &last_wheel_motion, &delta);
-
+ if (last_wheel_motion != 0) {
/* 10 clicks per second => speed == 1.0 */
- speed = 100000.0f / (delta.tv_sec * 1000000 + delta.tv_usec);
+ speed = 100000.0f / (float) (now - last_wheel_motion)
} else {