set_size_request (800, -1);
harvid_initialized = false;
- std::string dstdir = video_dest_dir(_session->session_directory().video_path(), Config->get_video_server_docroot());
+ std::string dstdir = video_dest_dir(_session->session_directory().video_path(), video_get_docroot(Config));
+ if (Config->get_video_advanced_setup()) {
- /* Harvid Browser */
- harvid_list_view.append_column("", pixBufRenderer);
- harvid_list_view.append_column(_("Filename"), harvid_list_columns.filename);
+ /* Harvid Browser */
+ harvid_list_view.append_column("", pixBufRenderer);
+ harvid_list_view.append_column(_("Filename"), harvid_list_columns.filename);
- harvid_list_view.get_column(0)->set_alignment(0.5);
- harvid_list_view.get_column(0)->add_attribute(pixBufRenderer, "stock-id", harvid_list_columns.id);
- harvid_list_view.get_column(1)->set_expand(true);
- harvid_list_view.get_column(1)->set_sort_column(harvid_list_columns.filename);
- harvid_list_view.set_enable_search(true);
- harvid_list_view.set_search_column(1);
+ harvid_list_view.get_column(0)->set_alignment(0.5);
+ harvid_list_view.get_column(0)->add_attribute(pixBufRenderer, "stock-id", harvid_list_columns.id);
+ harvid_list_view.get_column(1)->set_expand(true);
+ harvid_list_view.get_column(1)->set_sort_column(harvid_list_columns.filename);
+ harvid_list_view.set_enable_search(true);
+ harvid_list_view.set_search_column(1);
+ harvid_list_view.get_selection()->set_mode (SELECTION_SINGLE);
- //Glib::RefPtr<Gtk::TreeModelSort> refTreeModelSort = Gtk::TreeModelSort::create(harvid_list_view.get_model());
- //refTreeModelSort->set_sort_column(harvid_list_columns.filename, Gtk::SORT_ASCENDING);
- //harvid_list_view.set_model(refTreeModelSort);
+ harvid_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &AddVideoDialog::harvid_list_view_selected));
+ harvid_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &AddVideoDialog::harvid_list_view_activated));
- harvid_list_view.get_selection()->set_mode (SELECTION_SINGLE);
+ VBox* vbox = manage (new VBox);
+ Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
+ scroll->add(harvid_list_view);
+ scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
- harvid_list_view.get_selection()->signal_changed().connect(sigc::mem_fun(*this, &AddVideoDialog::harvid_list_view_selected));
- harvid_list_view.signal_row_activated().connect (sigc::mem_fun (*this, &AddVideoDialog::harvid_list_view_activated));
-
- VBox* vbox = manage (new VBox);
- Gtk::ScrolledWindow *scroll = manage(new ScrolledWindow);
- scroll->add(harvid_list_view);
- scroll->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
-
- HBox* hbox = manage (new HBox);
- harvid_path.set_alignment (0, 0.5);
- hbox->pack_start (harvid_path, true, true);
- hbox->pack_start (harvid_reset, false, false);
-
- vbox->pack_start (*hbox, false, false);
- vbox->pack_start (*scroll, true, true);
-
- notebook.append_page (*vbox, _("VideoServerIndex"));
+ HBox* hbox = manage (new HBox);
+ harvid_path.set_alignment (0, 0.5);
+ hbox->pack_start (harvid_path, true, true);
+ hbox->pack_start (harvid_reset, false, false);
+ vbox->pack_start (*hbox, false, false);
+ vbox->pack_start (*scroll, true, true);
+ notebook.append_page (*vbox, _("VideoServerIndex"));
+ } else {
+ /* dummy entry */
+ VBox* vbox = manage (new VBox);
+ notebook.append_page (*vbox, _("VideoServerIndex"));
+ }
/* file chooser */
chooser.set_border_width (4);
chooser.add_filter (matchall_filter);
chooser.set_select_multiple (false);
- /* file import options */
- import_combo.set_name ("PaddedButton");
- import_combo.append_text(_("Reference From Current Location"));
- import_combo.append_text(_("Hardlink or Copy to Session"));
- import_combo.append_text(_("Transcode to Session"));
- import_combo.set_active(2);
-
- vbox = manage (new VBox);
- vbox->pack_start (chooser, true, true, 0);
- vbox->pack_start (import_combo, false, true, 4);
+ VBox* vboxfb = manage (new VBox);
+ vboxfb->pack_start (chooser, true, true, 0);
- if (Config->get_video_server_docroot().size() > 0) {
- notebook.append_page (*vbox, _("Browse Files"));
+ if (video_get_docroot(Config).size() > 0 &&
+ Config->get_video_advanced_setup()) {
+ notebook.append_page (*vboxfb, _("Browse Files"));
}
/* Global Options*/
preview_image->set(imgbuf);
seek_slider.set_draw_value(false);
- hbox = manage (new HBox);
+ HBox* hbox = manage (new HBox);
hbox->pack_start (*table, true, false);
Gtk::Alignment *al = manage(new Gtk::Alignment());
/* Overall layout */
hbox = manage (new HBox);
- hbox->pack_start (notebook, true, true);
+ if (Config->get_video_advanced_setup()) {
+ hbox->pack_start (notebook, true, true);
+ } else {
+ hbox->pack_start (*vboxfb, true, true);
+ }
hbox->pack_start (*previewpane, false, false);
get_vbox()->set_spacing (4);
/* xjadeo checkbox */
if (ARDOUR_UI::instance()->video_timeline->found_xjadeo()
/* TODO xjadeo setup w/ xjremote */
- && Config->get_video_server_docroot().size() > 0) {
+ && video_get_docroot(Config).size() > 0) {
xjadeo_checkbox.set_active(true); /* set in ardour_ui.cpp ?! */
} else {
printf("xjadeo was not found or video-server docroot is unset (remote video-server)\n");
AddVideoDialog::file_name (bool &local_file)
{
int n = notebook.get_current_page ();
- if (n == 1) {
+ if (n == 1 || ! Config->get_video_advanced_setup()) {
local_file = true;
return chooser.get_filename();
} else {
if(!iter) return "";
std::string uri = (*iter)[harvid_list_columns.uri];
- std::string video_server_url = Config->get_video_server_url();
+ std::string video_server_url = video_get_server_url(Config);
/* check if video server is running locally */
- if (Config->get_video_server_docroot().size() > 0
+ if (video_get_docroot(Config).size() > 0
&& !video_server_url.compare(0, 16, "http://localhost"))
{
/* check if the file can be accessed */
CURL *curl;
curl = curl_easy_init();
char *ue = curl_easy_unescape(curl, uri.c_str(), uri.length(), &plen);
- std::string path = Config->get_video_server_docroot() + ue;
+ std::string path = video_get_docroot(Config) + ue;
if (!::access(path.c_str(), R_OK)) {
uri = path;
local_file = true;
AddVideoDialog::import_option ()
{
int n = notebook.get_current_page ();
- if (n == 0) { return VTL_IMPORT_NONE; }
- int i = import_combo.get_active_row_number();
- return static_cast<VtlImportOption>(i);
+ if (n == 0 && Config->get_video_advanced_setup()) { return VTL_IMPORT_NONE; }
+ return VTL_IMPORT_TRANSCODE;
}
bool
&& !Glib::file_test(path.c_str(), Glib::FILE_TEST_IS_DIR);
set_action_ok(ok);
if (ok) {
- request_preview(video_map_path(Config->get_video_server_docroot(), path));
+ request_preview(video_map_path(video_get_docroot(Config), path));
}
} else {
set_action_ok(false);
AddVideoDialog::harvid_load_docroot() {
set_action_ok(false);
- std::string video_server_url = Config->get_video_server_url();
+ std::string video_server_url = video_get_server_url(Config);
char url[2048];
snprintf(url, sizeof(url), "%s%sindex/"
, video_server_url.c_str()
bool
AddVideoDialog::page_switch() {
- if (notebook.get_current_page () == 1) {
+ if (notebook.get_current_page () == 1 || Config->get_video_advanced_setup()) {
file_selection_changed();
return true;
}
void
AddVideoDialog::request_preview(std::string u)
{
- std::string video_server_url = Config->get_video_server_url();
+ std::string video_server_url = video_get_server_url(Config);
double video_file_fps;
long long int video_duration;
enum VtlImportOption {
VTL_IMPORT_NONE = 0,
- VTL_IMPORT_COPY = 1,
- VTL_IMPORT_TRANSCODE = 2,
+ VTL_IMPORT_TRANSCODE = 1,
};
class AddVideoDialog : public ArdourDialog
Gtk::CheckButton xjadeo_checkbox;
Gtk::CheckButton set_session_fps_checkbox;
- Gtk::ComboBoxText import_combo;
Gtk::Notebook notebook;
Gtk::Button *ok_button;
<!--menuitem action='importFromSession'/-->
#ifdef WITH_VIDEOTIMELINE
<menuitem action='OpenVideo'/>
+ <menuitem action='CloseVideo'/>
<menu name='Video' action='Video'>
<menuitem action='StartVideoServer'/>
<menuitem action='StopVideoServer'/>
<menuitem action='ToggleSummary'/>
<menuitem action='ToggleGroupTabs'/>
<menuitem action='show-marker-lines'/>
-#ifdef WITH_VIDEOTIMELINE
- <menuitem action='ToggleJadeo'/>
-#endif
</menu>
<menu name='JACK' action='JACK'>
<menuitem action='JACKDisconnect'/>
<separator/>
+#ifdef WITH_VIDEOTIMELINE
+ <menuitem action='ToggleJadeo'/>
+ <separator/>
+#endif
+
<menuitem action='NewMIDITracer'/>
<menuitem action='toggle-audio-connection-manager'/>
<menuitem action='toggle-midi-connection-manager'/>
#include "video_server_dialog.h"
#include "add_video_dialog.h"
#include "transcode_video_dialog.h"
-#include "video_copy_dialog.h"
#include "system_exec.h" /* to launch video-server */
#endif
std::string icsd_exec = video_server_dialog->get_exec_path();
std::string icsd_docroot = video_server_dialog->get_docroot();
- if (icsd_docroot.empty()) {icsd_docroot = "/";}
+ if (icsd_docroot.empty()) {icsd_docroot = X_("/");}
struct stat sb;
if (!lstat (icsd_docroot.c_str(), &sb) == 0 || !S_ISDIR(sb.st_mode)) {
argp[8] = 0;
stop_video_server();
- std::ostringstream osstream;
- osstream << "http://localhost:" << video_server_dialog->get_listenport() << "/";
- Config->set_video_server_url(osstream.str());
- Config->set_video_server_docroot(icsd_docroot);
+ if (icsd_docroot == X_("/")) {
+ Config->set_video_advanced_setup(false);
+ } else {
+ std::ostringstream osstream;
+ osstream << "http://localhost:" << video_server_dialog->get_listenport() << "/";
+ Config->set_video_server_url(osstream.str());
+ Config->set_video_server_docroot(icsd_docroot);
+ Config->set_video_advanced_setup(true);
+ }
+
video_server_process = new SystemExec(icsd_exec, argp);
video_server_process->start();
sleep(1);
}
switch (add_video_dialog->import_option()) {
- case VTL_IMPORT_COPY:
- {
- VideoCopyDialog *video_copy_dialog;
- video_copy_dialog = new VideoCopyDialog(_session, path);
- //video_copy_dialog->setup_non_interactive_copy();
- ResponseType r = (ResponseType) video_copy_dialog->run ();
- video_copy_dialog->hide();
- if (r != RESPONSE_ACCEPT) { return; }
- path = video_copy_dialog->get_filename();
- delete video_copy_dialog;
- }
- break;
case VTL_IMPORT_TRANSCODE:
- {
- TranscodeVideoDialog *transcode_video_dialog;
- transcode_video_dialog = new TranscodeVideoDialog (_session, path);
- ResponseType r = (ResponseType) transcode_video_dialog->run ();
- transcode_video_dialog->hide();
- if (r != RESPONSE_ACCEPT) { return; }
- path = transcode_video_dialog->get_filename();
- if (!transcode_video_dialog->get_audiofile().empty()) {
- editor->embed_audio_from_video(transcode_video_dialog->get_audiofile());
+ {
+ TranscodeVideoDialog *transcode_video_dialog;
+ transcode_video_dialog = new TranscodeVideoDialog (_session, path);
+ ResponseType r = (ResponseType) transcode_video_dialog->run ();
+ transcode_video_dialog->hide();
+ if (r != RESPONSE_ACCEPT) {
+ delete transcode_video_dialog;
+ return;
+ }
+ if (!transcode_video_dialog->get_audiofile().empty()) {
+ editor->embed_audio_from_video(transcode_video_dialog->get_audiofile());
+ }
+ switch (transcode_video_dialog->import_option()) {
+ case VTL_IMPORT_TRANSCODED:
+ path = transcode_video_dialog->get_filename();
+ local_file = true;
+ break;
+ case VTL_IMPORT_REFERENCE:
+ break;
+ default:
+ delete transcode_video_dialog;
+ return;
+ }
+ delete transcode_video_dialog;
}
- delete transcode_video_dialog;
- }
break;
default:
case VTL_IMPORT_NONE:
break;
}
- if (path.empty()) {
- /* may have been overriden by 'audio only import'
- * in transcode_video_dialog */
- path = add_video_dialog->file_name(local_file);;
- }
-
/* strip _session->session_directory().video_path() from video file if possible */
if (local_file && !path.compare(0, _session->session_directory().video_path().size(), _session->session_directory().video_path())) {
path=path.substr(_session->session_directory().video_path().size());
}
}
+void
+ARDOUR_UI::remove_video ()
+{
+ video_timeline->close_session();
+ editor->toggle_ruler_video(false);
+
+ /* delete session state */
+ XMLNode* node = new XMLNode(X_("Videotimeline"));
+ _session->add_extra_xml(*node);
+ node = new XMLNode(X_("Videomonitor"));
+ _session->add_extra_xml(*node);
+}
+
void
ARDOUR_UI::flush_videotimeline_cache (bool localcacheonly)
{
void add_routes_thread ();
#ifdef WITH_VIDEOTIMELINE
void add_video (Gtk::Window* float_window);
+ void remove_video ();
void start_video_server_menu (Gtk::Window* float_window);
bool start_video_server (Gtk::Window* float_window, bool popup_msg);
void stop_video_server (bool ask_confirm=false);
act = ActionManager::register_action (main_actions, X_("OpenVideo"), _("Open Video"),
sigc::bind (sigc::mem_fun(*this, &ARDOUR_UI::add_video), (Gtk::Window*) 0));
ActionManager::session_sensitive_actions.push_back (act);
+ act = ActionManager::register_action (main_actions, X_("CloseVideo"), _("Remove Video"),
+ sigc::mem_fun (*this, &ARDOUR_UI::remove_video));
+ ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (main_actions, X_("ExportVideo"), _("Export To Video File"),
sigc::mem_fun (*editor, &PublicEditor::export_video));
ActionManager::session_sensitive_actions.push_back (act);
ArdourWindow::ArdourWindow (string title)
: Window ()
+ , VisibilityTracker (*((Gtk::Window*)this))
{
set_title (title);
init ();
ArdourWindow::ArdourWindow (Gtk::Window& parent, string /*title*/)
: Window ()
+ , VisibilityTracker (*((Gtk::Window*)this))
{
init ();
set_transient_for (parent);
{
set_border_width (10);
}
+
#include <gtkmm/window.h>
#include <gtkmm/window.h>
+#include "gtkmm2ext/visibility_tracker.h"
+
#include "ardour/session_handle.h"
/**
* method of connecting and disconnecting from a Session with
* all other objects that have a handle on a Session.
*/
-class ArdourWindow : public Gtk::Window, public ARDOUR::SessionHandlePtr
+class ArdourWindow : public Gtk::Window, public ARDOUR::SessionHandlePtr, public Gtkmm2ext::VisibilityTracker
{
public:
ArdourWindow (std::string title);
#include "gtkmm2ext/keyboard.h"
#include "canvas-note-event.h"
+#include "midi_channel_selector.h"
#include "midi_region_view.h"
#include "public_editor.h"
#include "editing_syms.h"
void toggle_video_timeline_locked ();
void set_video_timeline_locked (const bool);
void queue_visual_videotimeline_update ();
- void embed_audio_from_video (std::string);
+ void embed_audio_from_video (std::string, framepos_t n = 0);
#endif
bool canvas_imageframe_item_view_event(GdkEvent* event, ArdourCanvas::Item*,ImageFrameView*);
#include "ardour/profile.h"
#include "ardour/rc_configuration.h"
+#include "ardour/smf_source.h"
#include "ardour_ui.h"
#include "editor.h"
Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
{
RouteTimeAxisView* tv;
+
+ /* MIDI files must always be imported, because we consider them
+ * writable. So split paths into two vectors, and follow the import
+ * path on the MIDI part.
+ */
+
+ vector<string> midi_paths;
+ vector<string> audio_paths;
+
+ for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
+ if (SMFSource::safe_midi_file_extension (*i)) {
+ midi_paths.push_back (*i);
+ } else {
+ audio_paths.push_back (*i);
+ }
+ }
+
std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos);
if (tvp.first == 0) {
frame = 0;
+ do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame);
+
if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
- do_import (paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
+ do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
} else {
- do_embed (paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
+ do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
}
} else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
- /* check that its an audio track, not a bus */
+ /* check that its a track, not a bus */
if (tv->track()) {
/* select the track, then embed/import */
selection->set (tv);
+ do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
+
if (Profile->get_sae() || Config->get_only_copy_imported_files() || copy) {
- do_import (paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
+ do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
} else {
- do_embed (paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
+ do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
}
}
}
(void) event_frame (&event, &px, &py);
std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (py);
-
+ bool can_drop = false;
+
if (tv.first != 0) {
+ /* over a time axis view of some kind */
+
rtav = dynamic_cast<RouteTimeAxisView*> (tv.first);
if (rtav != 0 && rtav->is_track ()) {
-
- region = _regions->get_dragged_region ();
+ /* over a track, not a bus */
+ can_drop = true;
+ }
- if (region) {
-
- if ((boost::dynamic_pointer_cast<AudioRegion> (region) != 0 &&
- dynamic_cast<AudioTimeAxisView*> (tv.first) != 0) ||
- (boost::dynamic_pointer_cast<MidiRegion> (region) != 0 &&
- dynamic_cast<MidiTimeAxisView*> (tv.first) != 0)) {
- /* audio to audio
- OR
- midi to midi
- */
+ } else {
+ /* not over a time axis view, so drop is possible */
+ can_drop = true;
+ }
- context->drag_status (context->get_suggested_action(), time);
- return true;
- }
+ if (can_drop) {
+ region = _regions->get_dragged_region ();
+
+ if (region) {
+
+ if ((boost::dynamic_pointer_cast<AudioRegion> (region) != 0 &&
+ dynamic_cast<AudioTimeAxisView*> (tv.first) != 0) ||
+ (boost::dynamic_pointer_cast<MidiRegion> (region) != 0 &&
+ dynamic_cast<MidiTimeAxisView*> (tv.first) != 0)) {
+
+ /* audio to audio
+ OR
+ midi to midi
+ */
+
+ context->drag_status (context->get_suggested_action(), time);
+ return true;
+ }
+ } else {
+ /* DND originating from outside ardour
+ *
+ * TODO: check if file is audio/midi, allow drops on same track-type only,
+ * currently: if audio is dropped on a midi-track, it is only added to the region-list
+ */
+ if (Profile->get_sae() || Config->get_only_copy_imported_files()) {
+ context->drag_status(Gdk::ACTION_COPY, time);
} else {
- /* DND originating from outside ardour
- *
- * TODO: check if file is audio/midi, allow drops on same track-type only,
- * currently: if audio is dropped on a midi-track, it is only added to the region-list
- */
- if (Profile->get_sae() || Config->get_only_copy_imported_files()) {
+ if ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY) {
context->drag_status(Gdk::ACTION_COPY, time);
} else {
- if ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY)
- context->drag_status(Gdk::ACTION_COPY, time);
- else
- context->drag_status(Gdk::ACTION_LINK, time);
+ context->drag_status(Gdk::ACTION_LINK, time);
}
- return true;
}
+ return true;
}
}
#endif
#include <stdint.h>
+#include <algorithm>
#include "pbd/memento_command.h"
#include "pbd/basename.h"
_editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
}
+ _editor->session()->maybe_update_session_range(
+ std::max(ARDOUR_UI::instance()->video_timeline->get_offset(), (ARDOUR::frameoffset_t) 0),
+ std::max(ARDOUR_UI::instance()->video_timeline->get_offset() + ARDOUR_UI::instance()->video_timeline->get_duration(), (ARDOUR::frameoffset_t) 0)
+ );
+
+
_editor->commit_reversible_command ();
_editor->update_canvas_now ();
}
}
void
-Editor::embed_audio_from_video (std::string path)
+Editor::embed_audio_from_video (std::string path, framepos_t n)
{
vector<std::string> paths;
- framepos_t n = 0; /* -1: use file's timestamp - but br0ken with ffmpeg wav extract */
paths.push_back(path);
#if 0
do_embed (paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, n);
CriticalSelectionChanged ();
}
+/* Track export channel selector */
+
TrackExportChannelSelector::TrackExportChannelSelector (ARDOUR::Session * session, ProfileManagerPtr manager)
: ExportChannelSelector(session, manager)
+ , region_contents_button(source_group, _("Export region contents"))
+ , track_output_button(source_group, _("Export track output"))
{
+ pack_start(main_layout);
+
+ // Options
+ options_box.pack_start(region_contents_button);
+ options_box.pack_start(track_output_button);
+ main_layout.pack_start(options_box);
+
+ // Track scroller
track_scroller.add (track_view);
track_scroller.set_size_request (-1, 130);
track_scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
- pack_start(track_scroller);
+ main_layout.pack_start(track_scroller);
// Track list
track_list = Gtk::ListStore::create (track_cols);
boost::shared_ptr<Route> route = row[track_cols.route];
- /* Output of track code. TODO make this an option also
- uint32_t outs = route->n_ports().n_audio();
- for (uint32_t i = 0; i < outs; ++i) {
- AudioPort * port = route->audio (i);
- if (port) {
- ExportChannelPtr channel (new PortExportChannel ());
- PortExportChannel * pec = static_cast<PortExportChannel *> (channel.get());
- pec->add_port(port);
- state->config->register_channel(channel);
+ if (track_output_button.get_active()) {
+ uint32_t outs = route->n_outputs().n_audio();
+ for (uint32_t i = 0; i < outs; ++i) {
+ boost::shared_ptr<AudioPort> port = route->output()->audio (i);
+ if (port) {
+ ExportChannelPtr channel (new PortExportChannel ());
+ PortExportChannel * pec = static_cast<PortExportChannel *> (channel.get());
+ pec->add_port(port);
+ state->config->register_channel(channel);
+ }
}
+ } else {
+ std::list<ExportChannelPtr> list;
+ RouteExportChannel::create_from_route (list, route);
+ state->config->register_channels (list);
}
- */
- std::list<ExportChannelPtr> list;
- RouteExportChannel::create_from_route (list, route);
- state->config->register_channels (list);
state->config->set_name (route->name());
}
private:
void fill_list();
- void add_track (boost::shared_ptr<ARDOUR::Route> route);
+ void add_track (boost::shared_ptr<ARDOUR::Route> route);
void update_config();
ChannelConfigList configs;
+ Gtk::VBox main_layout;
+
struct TrackCols : public Gtk::TreeModelColumnRecord
{
public:
- Gtk::TreeModelColumn<boost::shared_ptr<ARDOUR::Route> > route;
+ Gtk::TreeModelColumn<boost::shared_ptr<ARDOUR::Route> > route;
Gtk::TreeModelColumn<std::string> label;
Gtk::TreeModelColumn<bool> selected;
Gtk::ScrolledWindow track_scroller;
+ Gtk::HBox options_box;
+ Gtk::RadioButton::Group source_group;
+ Gtk::RadioButton region_contents_button;
+ Gtk::RadioButton track_output_button;
};
#endif /* __export_channel_selector_h__ */
{
set_session (s);
- transcoder = 0;
set_name ("ExportVideoDialog");
set_position (Gtk::WIN_POS_MOUSE);
VBox* options_box = manage (new VBox);
HBox* path_hbox;
-#if 0
- l = manage (new Label (_("<b>Export Video File</b>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
- l->set_use_markup ();
- vbox->pack_start (*l, false, false);
- l = manage (new Label (_("The file-format is determined by the extension you choose for the output file."), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
- l->set_size_request(700,-1);
- l->set_line_wrap();
- vbox->pack_start (*l, false, false, 8);
-#endif
+ /* check if ffmpeg can be found */
+ transcoder = new TranscodeFfmpeg("");
+ if (!transcoder->ffexec_ok()) {
+ l = manage (new Label (_("No ffprobe or ffmpeg executables could be found on this system. Video Export is not possible until you install those tools. See the Log widow for more information."), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
+ l->set_line_wrap();
+ vbox->pack_start (*l, false, false, 8);
+ get_vbox()->pack_start (*vbox, false, false);
+ add_button (Stock::OK, RESPONSE_CANCEL);
+ show_all_children ();
+ return;
+ }
+ delete transcoder; transcoder = 0;
l = manage (new Label (_("<b>Files:</b>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
l->set_use_markup ();
*/
transcoder = new TranscodeFfmpeg(invid);
if (!transcoder->ffexec_ok()) {
- warning << _("No ffprobe or ffmpeg executables could be found on this system. Transcoding is not possible until you install those tools.") << endmsg;
+ /* ffmpeg binary was not found. TranscodeFfmpeg prints a warning */
unlink (insnd.c_str());
Gtk::Dialog::response(RESPONSE_CANCEL);
return;
/*
- Copyright (C) 2008 Paul Davis
- Author: Hans Baier
+ Copyright (C) 2008-2013 Paul Davis
+ Original Author: Hans Baier
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
#include <algorithm>
#include <sstream>
+#include <gtkmm/separator.h>
+#include <gtkmm/box.h>
+#include <gtkmm/label.h>
+#include <gtkmm/togglebutton.h>
+#include <gtkmm/radiobutton.h>
+#include <gtkmm/table.h>
+
+#include "pbd/compose.h"
+
+#include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/gui_thread.h"
+#include "gtkmm2ext/utils.h"
+
+#include "ardour/midi_track.h"
#include "midi_channel_selector.h"
-#include "gtkmm/separator.h"
-#include "i18n.h"
#include "rgb_macros.h"
+#include "i18n.h"
+
using namespace std;
using namespace Gtk;
using namespace ARDOUR;
mode_changed.emit(_channel_mode, get_selected_channels());
}
+/*-----------------------------------------*/
+
+MidiChannelSelectorWindow::MidiChannelSelectorWindow (boost::shared_ptr<MidiTrack> mt)
+ : ArdourWindow (_("MIDI Channel Control"))
+ , track (mt)
+ , playback_all_button (playback_button_group, _("Playback all channels"))
+ , playback_filter_button (playback_button_group, _("Play only selected channels"))
+ , playback_force_button (playback_button_group, _("Use a single fixed channel for all playback"))
+ , capture_all_button (capture_button_group, _("Record all channels"))
+ , capture_filter_button (capture_button_group, _("Record only selected channels"))
+ , capture_force_button (capture_button_group, _("Force all channels to 1 channel"))
+ , last_drawn_capture_mode (AllChannels)
+ , last_drawn_playback_mode (AllChannels)
+{
+ build ();
+
+ playback_mode_changed ();
+ capture_mode_changed ();
+
+ playback_mask_changed ();
+ capture_mask_changed ();
+
+ track->PlaybackChannelMaskChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::playback_mask_changed, this), gui_context());
+ track->PlaybackChannelModeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::playback_mode_changed, this), gui_context());
+ track->CaptureChannelMaskChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::capture_mask_changed, this), gui_context());
+ track->CaptureChannelModeChanged.connect (*this, MISSING_INVALIDATOR, boost::bind (&MidiChannelSelectorWindow::capture_mode_changed, this), gui_context());
+}
+
+MidiChannelSelectorWindow::~MidiChannelSelectorWindow()
+{
+}
+
+void
+MidiChannelSelectorWindow::build ()
+{
+ VBox* vpacker;
+ HBox* capture_controls;
+ HBox* playback_controls;
+ Button* b;
+ Label* l;
+
+ vpacker = manage (new VBox);
+ vpacker->set_spacing (6);
+ vpacker->set_border_width (12);
+
+ l = manage (new Label (string_compose (("<span size=\"larger\" weight=\"bold\">%1: %2</span>"), _("MIDI Channel Control"), track->name())));
+ l->set_use_markup (true);
+ l->set_alignment (0.5, 0.0);
+
+ vpacker->pack_start (*l, true, true);
+
+ l = manage (new Label (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Inbound"))));
+ l->set_use_markup (true);
+ vpacker->pack_start (*l);
+
+
+ vpacker->pack_start (capture_all_button);
+ capture_all_button.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::capture_mode_toggled), AllChannels));
+
+ vpacker->pack_start (capture_filter_button);
+ capture_filter_button.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::capture_mode_toggled), FilterChannels));
+
+ vpacker->pack_start (capture_force_button);
+ capture_force_button.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::capture_mode_toggled), ForceChannel));
+
+ vpacker->pack_start (capture_mask_box);
+
+ capture_controls = manage (new HBox);
+ capture_controls->set_spacing (6);
+
+ b = manage (new Button (_("All")));
+ Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to enable recording all channels"));
+ capture_controls->pack_start (*b);
+ capture_mask_controls.push_back (b);
+ b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::fill_capture_mask));
+ b = manage (new Button (_("None")));
+ Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to disable recording all channels"));
+ capture_controls->pack_start (*b);
+ capture_mask_controls.push_back (b);
+ b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::zero_capture_mask));
+ b = manage (new Button (_("Invert")));
+ Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to invert currently selected recording channels"));
+ capture_controls->pack_start (*b);
+ capture_mask_controls.push_back (b);
+ b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::invert_capture_mask));
+
+ vpacker->pack_start (*capture_controls);
+
+ l = manage (new Label (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Playback"))));
+ l->set_use_markup (true);
+ vpacker->pack_start (*l);
+
+ vpacker->pack_start (playback_all_button);
+ playback_all_button.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::playback_mode_toggled), AllChannels));
+
+ vpacker->pack_start (playback_filter_button);
+ playback_filter_button.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::playback_mode_toggled), FilterChannels));
+
+ vpacker->pack_start (playback_force_button);
+ playback_force_button.signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::playback_mode_toggled), ForceChannel));
+
+ vpacker->pack_start (playback_mask_box);
+
+ playback_controls = manage (new HBox);
+ playback_controls->set_spacing (6);
+
+ b = manage (new Button (_("All")));
+ Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to enable playback of all channels"));
+ playback_controls->pack_start (*b);
+ playback_mask_controls.push_back (b);
+ b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::fill_playback_mask));
+ b = manage (new Button (_("None")));
+ Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to disable playback of all channels"));
+ playback_controls->pack_start (*b);
+ playback_mask_controls.push_back (b);
+ b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::zero_playback_mask));
+ b = manage (new Button (_("Invert")));
+ Gtkmm2ext::UI::instance()->set_tip (*b, _("Click to invert current selected playback channels"));
+ playback_controls->pack_start (*b);
+ playback_mask_controls.push_back (b);
+ b->signal_clicked().connect (sigc::mem_fun (*this, &MidiChannelSelectorWindow::invert_playback_mask));
+
+ vpacker->pack_start (*playback_controls);
+
+ add (*vpacker);
+}
+
+void
+MidiChannelSelectorWindow::fill_playback_mask ()
+{
+ if (track->get_playback_channel_mode() == FilterChannels) {
+ track->set_playback_channel_mask (0xffff);
+ }
+}
+
+void
+MidiChannelSelectorWindow::zero_playback_mask ()
+{
+ if (track->get_playback_channel_mode() == FilterChannels) {
+ track->set_playback_channel_mask (0);
+ }
+}
+
+void
+MidiChannelSelectorWindow::invert_playback_mask ()
+{
+ if (track->get_playback_channel_mode() == FilterChannels) {
+ track->set_playback_channel_mask (~track->get_playback_channel_mask());
+ }
+}
+
+void
+MidiChannelSelectorWindow::fill_capture_mask ()
+{
+ if (track->get_capture_channel_mode() == FilterChannels) {
+ track->set_capture_channel_mask (0xffff);
+ }
+}
+
+void
+MidiChannelSelectorWindow::zero_capture_mask ()
+{
+ if (track->get_capture_channel_mode() == FilterChannels) {
+ track->set_capture_channel_mask (0);
+ }
+}
+
+void
+MidiChannelSelectorWindow::invert_capture_mask ()
+{
+ if (track->get_capture_channel_mode() == FilterChannels) {
+ track->set_capture_channel_mask (~track->get_capture_channel_mask());
+ }
+}
+
+void
+MidiChannelSelectorWindow::set_playback_selected_channels (uint16_t mask)
+{
+ switch (track->get_playback_channel_mode()) {
+ case AllChannels:
+ /* they are insensitive, so we don't care */
+ break;
+
+ case FilterChannels:
+ for (uint16_t i = 0; i < 16; i++) {
+ playback_buttons[i]->set_active ((1<<i) & mask);
+ }
+ break;
+
+ case ForceChannel:
+ /* only set the lowest set channel in the mask as active */
+ for (uint16_t i = 0; i < 16; i++) {
+ playback_buttons[i]->set_active (i == (ffs (mask) - 1));
+ }
+ break;
+ }
+}
+
+void
+MidiChannelSelectorWindow::set_capture_selected_channels (uint16_t mask)
+{
+ switch (track->get_capture_channel_mode()) {
+ case AllChannels:
+ /* they are insensitive, so we don't care */
+ break;
+
+ case FilterChannels:
+ for (uint16_t i = 0; i < 16; i++) {
+ capture_buttons[i]->set_active ((1<<i) & mask);
+ }
+ break;
+
+ case ForceChannel:
+ /* only set the lowest set channel in the mask as active */
+ for (uint16_t i = 0; i < 16; i++) {
+ capture_buttons[i]->set_active (i == (ffs (mask) - 1));
+ }
+ break;
+ }
+}
+
+void
+MidiChannelSelectorWindow::playback_mask_changed ()
+{
+ set_playback_selected_channels (track->get_playback_channel_mask());
+}
+
+void
+MidiChannelSelectorWindow::capture_mask_changed ()
+{
+ set_capture_selected_channels (track->get_capture_channel_mask());
+}
+
+void
+MidiChannelSelectorWindow::playback_mode_changed ()
+{
+ uint32_t first_channel = 0;
+ ChannelMode mode = track->get_playback_channel_mode();
+
+ switch (mode) {
+ case AllChannels:
+ if (last_drawn_playback_mode == ForceChannel) {
+ /* force mode used radio buttons. not what we want,
+ * though one could argue that we want no buttons
+ * at since they are insensitive
+ */
+ playback_buttons.clear ();
+ }
+ for (vector<Widget*>::iterator i = playback_mask_controls.begin(); i != playback_mask_controls.end(); ++i) {
+ (*i)->set_sensitive (false);
+ }
+ playback_all_button.set_active ();
+ break;
+
+ case FilterChannels:
+ if (last_drawn_playback_mode == ForceChannel) {
+ playback_buttons.clear ();
+ } else if (last_drawn_playback_mode == AllChannels) {
+ for (vector<ToggleButton*>::iterator i = playback_buttons.begin(); i != playback_buttons.end(); ++i) {
+ (*i)->set_sensitive (true);
+ }
+ }
+ for (vector<Widget*>::iterator i = playback_mask_controls.begin(); i != playback_mask_controls.end(); ++i) {
+ (*i)->set_sensitive (true);
+ }
+ playback_filter_button.set_active ();
+ break;
+
+ case ForceChannel:
+ if (last_drawn_playback_mode == AllChannels || last_drawn_playback_mode == FilterChannels) {
+ playback_buttons.clear ();
+ first_channel = ffs (track->get_playback_channel_mask()) - 1;
+ }
+ for (vector<Widget*>::iterator i = playback_mask_controls.begin(); i != playback_mask_controls.end(); ++i) {
+ (*i)->set_sensitive (false);
+ }
+ playback_force_button.set_active ();
+ break;
+ }
+
+ if (playback_buttons.empty()) {
+
+ Gtkmm2ext::container_clear (playback_mask_box);
+
+ ToggleButton* tb;
+ RadioButtonGroup group;
+
+ for (uint32_t n = 0; n < 16; ++n) {
+ char buf[3];
+ snprintf (buf, sizeof (buf), "%d", n+1);
+
+ switch (mode) {
+ case AllChannels:
+ case FilterChannels:
+ tb = manage (new ToggleButton (buf));
+ Gtkmm2ext::UI::instance()->set_tip (*tb, string_compose (_("Click to toggle playback of channel %1"), n+1));
+ break;
+ case ForceChannel:
+ tb = manage (new RadioButton (group, buf));
+ tb->property_draw_indicator() = false;
+ if (n == first_channel) {
+ tb->set_active (true);
+ }
+ Gtkmm2ext::UI::instance()->set_tip (*tb, string_compose (_("Click to force all MIDI channel messages to channel %1"), n+1));
+ break;
+ }
+ playback_buttons.push_back (tb);
+ tb->set_name (X_("MidiChannelSelectorButton"));
+ playback_mask_box.pack_start (*tb);
+ tb->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::playback_channel_clicked), n));
+ tb->show ();
+
+ if (mode == AllChannels) {
+ tb->set_sensitive (false);
+ }
+ }
+
+ if (mode != ForceChannel) {
+ set_playback_selected_channels (track->get_playback_channel_mask());
+ }
+ }
+
+ if (mode == AllChannels) {
+ for (vector<ToggleButton*>::iterator i = playback_buttons.begin(); i != playback_buttons.end(); ++i) {
+ (*i)->set_sensitive (false);
+ }
+ }
+
+ last_drawn_playback_mode = mode;
+}
+
+void
+MidiChannelSelectorWindow::capture_mode_changed ()
+{
+ uint32_t first_channel = 0;
+ ChannelMode mode = track->get_capture_channel_mode();
+
+ switch (mode) {
+ case AllChannels:
+ if (last_drawn_capture_mode == ForceChannel) {
+ /* force mode used radio buttons. not what we want,
+ * though one could argue that we want no buttons
+ * at since they are insensitive
+ */
+ capture_buttons.clear ();
+ }
+ for (vector<Widget*>::iterator i = capture_mask_controls.begin(); i != capture_mask_controls.end(); ++i) {
+ (*i)->set_sensitive (false);
+ }
+ capture_all_button.set_active ();
+ break;
+
+ case FilterChannels:
+ if (last_drawn_capture_mode == ForceChannel) {
+ capture_buttons.clear ();
+ } else if (last_drawn_capture_mode == AllChannels) {
+ for (vector<ToggleButton*>::iterator i = capture_buttons.begin(); i != capture_buttons.end(); ++i) {
+ (*i)->set_sensitive (true);
+ }
+ }
+ for (vector<Widget*>::iterator i = capture_mask_controls.begin(); i != capture_mask_controls.end(); ++i) {
+ (*i)->set_sensitive (true);
+ }
+ capture_filter_button.set_active ();
+ break;
+
+ case ForceChannel:
+ if (last_drawn_capture_mode == AllChannels || last_drawn_capture_mode == FilterChannels) {
+ capture_buttons.clear ();
+ first_channel = ffs (track->get_capture_channel_mask()) - 1;
+ }
+ for (vector<Widget*>::iterator i = capture_mask_controls.begin(); i != capture_mask_controls.end(); ++i) {
+ (*i)->set_sensitive (false);
+ }
+ capture_force_button.set_active ();
+ break;
+ }
+
+ if (capture_buttons.empty()) {
+
+ Gtkmm2ext::container_clear (capture_mask_box);
+
+ ToggleButton* tb;
+ RadioButtonGroup group;
+
+ for (uint32_t n = 0; n < 16; ++n) {
+ char buf[3];
+ snprintf (buf, sizeof (buf), "%d", n+1);
+
+ switch (mode) {
+ case AllChannels:
+ case FilterChannels:
+ tb = manage (new ToggleButton (buf));
+ Gtkmm2ext::UI::instance()->set_tip (*tb, string_compose (_("Click to toggle recording of channel %1"), n+1));
+ break;
+ case ForceChannel:
+ tb = manage (new RadioButton (group, buf));
+ tb->property_draw_indicator() = false;
+ if (n == first_channel) {
+ tb->set_active (true);
+ }
+ Gtkmm2ext::UI::instance()->set_tip (*tb, string_compose (_("Click to force all recorded channels to %1"), n+1));
+ break;
+ }
+ capture_buttons.push_back (tb);
+ tb->set_name (X_("MidiChannelSelectorButton"));
+ capture_mask_box.pack_start (*tb);
+ tb->signal_toggled().connect (sigc::bind (sigc::mem_fun (*this, &MidiChannelSelectorWindow::capture_channel_clicked), n));
+ tb->show ();
+
+ if (mode == AllChannels) {
+ tb->set_sensitive (false);
+ }
+ }
+
+ if (mode != ForceChannel) {
+ set_capture_selected_channels (track->get_capture_channel_mask());
+ }
+ }
+
+ if (mode == AllChannels) {
+ for (vector<ToggleButton*>::iterator i = capture_buttons.begin(); i != capture_buttons.end(); ++i) {
+ (*i)->set_sensitive (false);
+ }
+ }
+
+ last_drawn_capture_mode = mode;
+}
+
+void
+MidiChannelSelectorWindow::playback_channel_clicked (uint16_t n)
+{
+ if (playback_buttons[n]->get_active()) {
+ switch (track->get_playback_channel_mode()) {
+ case AllChannels:
+ break;
+ case FilterChannels:
+ track->set_playback_channel_mask (track->get_playback_channel_mask() | (1<<n));
+ break;
+ case ForceChannel:
+ track->set_playback_channel_mask (1<<n);
+ break;
+ }
+ } else {
+ if (track->get_playback_channel_mode() == FilterChannels) {
+ track->set_playback_channel_mask (track->get_playback_channel_mask() & ~(1<<n));
+ }
+ }
+}
+
+void
+MidiChannelSelectorWindow::capture_channel_clicked (uint16_t n)
+{
+ if (capture_buttons[n]->get_active()) {
+ switch (track->get_capture_channel_mode()) {
+ case AllChannels:
+ break;
+ case FilterChannels:
+ track->set_capture_channel_mask (track->get_capture_channel_mask() | (1<<n));
+ break;
+ case ForceChannel:
+ track->set_capture_channel_mask (1<<n);
+ break;
+ }
+ } else {
+ if (track->get_capture_channel_mode() == FilterChannels) {
+ track->set_capture_channel_mask (track->get_capture_channel_mask() & ~(1<<n));
+ }
+ }
+}
+
+void
+MidiChannelSelectorWindow::capture_mode_toggled (ChannelMode mode)
+{
+ /* this is called twice for every radio button change. the first time
+ is for the button/mode that has been turned off, and the second is for the
+ button/mode that has been turned on.
+
+ so we take action only if the button is active (i.e it is the one
+ just clicked on)
+ */
+
+ switch (mode) {
+ case AllChannels:
+ if (capture_all_button.get_active()) {
+ track->set_capture_channel_mode (AllChannels, track->get_capture_channel_mask());
+ }
+ break;
+ case FilterChannels:
+ if (capture_filter_button.get_active()) {
+ track->set_capture_channel_mode (FilterChannels, track->get_capture_channel_mask());
+ }
+ break;
+ case ForceChannel:
+ if (capture_force_button.get_active()) {
+ track->set_capture_channel_mode (ForceChannel, track->get_capture_channel_mask());
+ }
+ break;
+ }
+}
+
+void
+MidiChannelSelectorWindow::playback_mode_toggled (ChannelMode mode)
+{
+ /* this is called twice for every radio button change. the first time
+ is for the button/mode that has been turned off, and the second is for the
+ button/mode that has been turned on.
+
+ so we take action only if the button is active (i.e it is the one
+ just clicked on)
+ */
+
+ switch (mode) {
+ case AllChannels:
+ if (playback_all_button.get_active()) {
+ track->set_playback_channel_mode (AllChannels, track->get_playback_channel_mask());
+ }
+ break;
+ case FilterChannels:
+ if (playback_filter_button.get_active()) {
+ track->set_playback_channel_mode (FilterChannels, track->get_playback_channel_mask());
+ }
+ break;
+ case ForceChannel:
+ if (playback_force_button.get_active()) {
+ track->set_playback_channel_mode (ForceChannel, track->get_playback_channel_mask());
+ }
+ break;
+ }
+}
+
+void
+MidiChannelSelectorWindow::set_channel_colors (const uint32_t new_channel_colors[16])
+{
+ for (uint32_t n = 0; n < 16; ++n) {
+
+ char color_normal[8];
+ char color_active[8];
+
+ snprintf(color_normal, 8, "#%x", UINT_INTERPOLATE(new_channel_colors[n], 0x000000ff, 0.6));
+ snprintf(color_active, 8, "#%x", new_channel_colors[n]);
+
+ playback_buttons[n]->modify_bg(STATE_NORMAL, Gdk::Color(color_normal));
+ playback_buttons[n]->modify_bg(STATE_ACTIVE, Gdk::Color(color_active));
+
+ capture_buttons[n]->modify_bg(STATE_NORMAL, Gdk::Color(color_normal));
+ capture_buttons[n]->modify_bg(STATE_ACTIVE, Gdk::Color(color_active));
+ }
+}
+
+void
+MidiChannelSelectorWindow::set_default_channel_color()
+{
+ for (uint32_t n = 0; n < 16; ++n) {
+ playback_buttons[n]->unset_fg (STATE_NORMAL);
+ playback_buttons[n]->unset_bg (STATE_NORMAL);
+ playback_buttons[n]->unset_fg (STATE_ACTIVE);
+ playback_buttons[n]->unset_bg (STATE_ACTIVE);
+
+ capture_buttons[n]->unset_fg (STATE_NORMAL);
+ capture_buttons[n]->unset_bg (STATE_NORMAL);
+ capture_buttons[n]->unset_fg (STATE_ACTIVE);
+ capture_buttons[n]->unset_bg (STATE_ACTIVE);
+ }
+}
#include "gtkmm/table.h"
#include "gtkmm/button.h"
+#include "gtkmm/radiobutton.h"
#include "gtkmm/label.h"
#include "gtkmm2ext/stateful_button.h"
#include "ardour/types.h"
+#include "ardour_window.h"
+
+namespace ARDOUR {
+ class MidiTrack;
+}
+
class MidiChannelSelector : public Gtk::Table
{
-public:
- MidiChannelSelector(int n_rows = 4, int n_columns = 4, int start_row = 0, int start_column = 0);
- virtual ~MidiChannelSelector() = 0;
+ public:
+ MidiChannelSelector(int n_rows = 4, int n_columns = 4, int start_row = 0, int start_column = 0);
+ virtual ~MidiChannelSelector() = 0;
- sigc::signal<void> clicked;
+ sigc::signal<void> clicked;
- void set_channel_colors(const uint32_t new_channel_colors[16]);
- void set_default_channel_color();
+ void set_channel_colors(const uint32_t new_channel_colors[16]);
+ void set_default_channel_color();
-protected:
- virtual void button_toggled(Gtk::ToggleButton* button, uint8_t button_nr) = 0;
- Gtk::Label _button_labels[4][4];
- Gtkmm2ext::StatefulToggleButton _buttons[4][4];
- int _recursion_counter;
+ protected:
+ virtual void button_toggled(Gtk::ToggleButton* button, uint8_t button_nr) = 0;
+ Gtk::Label _button_labels[4][4];
+ Gtkmm2ext::StatefulToggleButton _buttons[4][4];
+ int _recursion_counter;
- bool was_clicked (GdkEventButton*);
+ bool was_clicked (GdkEventButton*);
};
class SingleMidiChannelSelector : public MidiChannelSelector
{
-public:
- SingleMidiChannelSelector(uint8_t active_channel = 0);
+ public:
+ SingleMidiChannelSelector(uint8_t active_channel = 0);
- uint8_t get_active_channel() const { return _active_channel; }
+ uint8_t get_active_channel() const { return _active_channel; }
- sigc::signal<void, uint8_t> channel_selected;
+ sigc::signal<void, uint8_t> channel_selected;
-protected:
- virtual void button_toggled(Gtk::ToggleButton* button, uint8_t button_nr);
+ protected:
+ virtual void button_toggled(Gtk::ToggleButton* button, uint8_t button_nr);
- Gtk::ToggleButton* _last_active_button;
- uint8_t _active_channel;
+ Gtk::ToggleButton* _last_active_button;
+ uint8_t _active_channel;
};
class MidiMultipleChannelSelector : public MidiChannelSelector
{
-public:
- MidiMultipleChannelSelector(ARDOUR::ChannelMode mode = ARDOUR::FilterChannels,
- uint16_t initial_selection = 0xFFFF);
-
- virtual ~MidiMultipleChannelSelector();
-
- /** The channel mode or selected channel(s) has changed.
- * First parameter is the new channel mode, second parameter is a bitmask
- * of the currently selected channels.
- */
- sigc::signal<void, ARDOUR::ChannelMode, uint16_t> mode_changed;
-
- void set_channel_mode(ARDOUR::ChannelMode mode, uint16_t mask);
- ARDOUR::ChannelMode get_channel_mode () const { return _channel_mode; }
-
- /**
- * @return each bit in the returned word represents a midi channel, eg.
- * bit 0 represents channel 0 and bit 15 represents channel 15
- *
- */
- uint16_t get_selected_channels() const;
- void set_selected_channels(uint16_t selected_channels);
-
-protected:
- ARDOUR::ChannelMode _channel_mode;
- ARDOUR::NoteMode _note_mode;
-
- virtual void button_toggled(Gtk::ToggleButton* button, uint8_t button_nr);
- void force_channels_button_toggled();
-
- void select_all(bool on);
- void invert_selection(void);
-
- Gtk::Button _select_all;
- Gtk::Button _select_none;
- Gtk::Button _invert_selection;
- Gtk::ToggleButton _force_channel;
+ public:
+ MidiMultipleChannelSelector(ARDOUR::ChannelMode mode = ARDOUR::FilterChannels,
+ uint16_t initial_selection = 0xFFFF);
+
+ virtual ~MidiMultipleChannelSelector();
+
+ /** The channel mode or selected channel(s) has changed.
+ * First parameter is the new channel mode, second parameter is a bitmask
+ * of the currently selected channels.
+ */
+ sigc::signal<void, ARDOUR::ChannelMode, uint16_t> mode_changed;
+
+ void set_channel_mode(ARDOUR::ChannelMode mode, uint16_t mask);
+ ARDOUR::ChannelMode get_channel_mode () const { return _channel_mode; }
+
+ /**
+ * @return each bit in the returned word represents a midi channel, eg.
+ * bit 0 represents channel 0 and bit 15 represents channel 15
+ *
+ */
+ uint16_t get_selected_channels() const;
+ void set_selected_channels(uint16_t selected_channels);
+
+ protected:
+ ARDOUR::ChannelMode _channel_mode;
+ ARDOUR::NoteMode _note_mode;
+
+ virtual void button_toggled(Gtk::ToggleButton* button, uint8_t button_nr);
+ void force_channels_button_toggled();
+
+ void select_all(bool on);
+ void invert_selection(void);
+
+ Gtk::Button _select_all;
+ Gtk::Button _select_none;
+ Gtk::Button _invert_selection;
+ Gtk::ToggleButton _force_channel;
+};
+
+class MidiChannelSelectorWindow : public ArdourWindow, public PBD::ScopedConnectionList
+{
+ public:
+ MidiChannelSelectorWindow (boost::shared_ptr<ARDOUR::MidiTrack>);
+ ~MidiChannelSelectorWindow ();
+
+ void set_channel_colors (const uint32_t new_channel_colors[16]);
+ void set_default_channel_color();
+
+ private:
+ boost::shared_ptr<ARDOUR::MidiTrack> track;
+ std::vector<Gtk::ToggleButton*> playback_buttons;
+ std::vector<Gtk::ToggleButton*> capture_buttons;
+
+ std::vector<Gtk::Widget*> playback_mask_controls;
+ std::vector<Gtk::Widget*> capture_mask_controls;
+
+ Gtk::HBox capture_mask_box;
+ Gtk::HBox playback_mask_box;
+ Gtk::RadioButtonGroup playback_button_group;
+ Gtk::RadioButton playback_all_button;
+ Gtk::RadioButton playback_filter_button;
+ Gtk::RadioButton playback_force_button;
+ Gtk::RadioButtonGroup capture_button_group;
+ Gtk::RadioButton capture_all_button;
+ Gtk::RadioButton capture_filter_button;
+ Gtk::RadioButton capture_force_button;
+
+ ARDOUR::ChannelMode last_drawn_capture_mode;
+ ARDOUR::ChannelMode last_drawn_playback_mode;
+
+ void build();
+ void set_capture_selected_channels (uint16_t);
+ void set_playback_selected_channels (uint16_t);
+
+ void fill_playback_mask ();
+ void zero_playback_mask ();
+ void invert_playback_mask ();
+
+ void fill_capture_mask ();
+ void zero_capture_mask ();
+ void invert_capture_mask ();
+
+ void playback_mask_changed ();
+ void capture_mask_changed ();
+ void playback_mode_changed ();
+ void capture_mode_changed ();
+
+ void playback_channel_clicked (uint16_t);
+ void capture_channel_clicked (uint16_t);
+
+ void playback_all_clicked();
+ void playback_none_clicked();
+ void playback_invert_clicked();
+
+ void capture_all_clicked();
+ void capture_none_clicked();
+ void capture_invert_clicked();
+
+ void capture_mode_toggled (ARDOUR::ChannelMode);
+ void playback_mode_toggled (ARDOUR::ChannelMode);
};
#endif /*__ardour_ui_midi_channel_selector_h__*/
#include "mouse_cursors.h"
#include "note_player.h"
#include "public_editor.h"
+#include "route_time_axis.h"
#include "rgb_macros.h"
#include "selection.h"
#include "simpleline.h"
MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
: RegionView (parent, tv, r, spu, basic_color)
- , _last_channel_selection(0xFFFF)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
_note_group->raise_to_top();
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
-
- MidiTimeAxisView *time_axis = dynamic_cast<MidiTimeAxisView *>(&tv);
- if (time_axis) {
- _last_channel_mode = time_axis->channel_selector().get_channel_mode();
- _last_channel_selection = time_axis->channel_selector().get_selected_channels();
- }
-
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
connect_to_diskstream ();
boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
TimeAxisViewItem::Visibility visibility)
: RegionView (parent, tv, r, spu, basic_color, false, visibility)
- , _last_channel_selection(0xFFFF)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
_note_group->raise_to_top();
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
- MidiTimeAxisView *time_axis = dynamic_cast<MidiTimeAxisView *>(&tv);
- if (time_axis) {
- _last_channel_mode = time_axis->channel_selector().get_channel_mode();
- _last_channel_selection = time_axis->channel_selector().get_selected_channels();
- }
-
connect_to_diskstream ();
SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
MidiRegionView::MidiRegionView (const MidiRegionView& other)
: sigc::trackable(other)
, RegionView (other)
- , _last_channel_selection(0xFFFF)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
: RegionView (other, boost::shared_ptr<Region> (region))
- , _last_channel_selection(0xFFFF)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
group->raise_to_top();
group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
- midi_view()->signal_channel_mode_changed().connect(
- sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
+
+ midi_view()->midi_track()->PlaybackChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
+ boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
+ gui_context ());
instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
MidiRegionView::display_patch_changes ()
{
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
- uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
+ uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
for (uint8_t i = 0; i < 16; ++i) {
display_patch_changes_on_channel (i, chn_mask & (1 << i));
/* outline all edges */
ev->property_outline_what() = (guint32) 0xF;
}
-
+
if (update_ghost_regions) {
for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
event->show_velocity();
}
- event->on_channel_selection_change(_last_channel_selection);
+ event->on_channel_selection_change (get_selected_channels());
_events.push_back(event);
if (visible) {
}
void
-MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
+MidiRegionView::midi_channel_mode_changed ()
{
+ MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
+ uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
+ ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
+
if (mode == ForceChannel) {
mask = 0xFFFF; // Show all notes as active (below)
}
// Update notes for selection
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
- (*i)->on_channel_selection_change(mask);
+ (*i)->on_channel_selection_change (mask);
}
- _last_channel_selection = mask;
- _last_channel_mode = mode;
-
_patch_changes.clear ();
display_patch_changes ();
}
time_sort_events ();
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
- uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
+ uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
if ((*i)->selected()) {
time_sort_events ();
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
- uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
+ uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
if ((*i)->selected()) {
Events e;
MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
- uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
+ uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
if (ev.is_channel_event()) {
- if (_last_channel_mode == FilterChannels) {
- if (((uint16_t(1) << ev.channel()) & _last_channel_selection) == 0) {
+ if (get_channel_mode() == FilterChannels) {
+ if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
continue;
}
}
delete _note_player;
_note_player = 0;
}
+
+ChannelMode
+MidiRegionView::get_channel_mode () const
+{
+ RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
+ return rtav->midi_track()->get_playback_channel_mode();
+}
+
+uint16_t
+MidiRegionView::get_selected_channels () const
+{
+ RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
+ return rtav->midi_track()->get_playback_channel_mask();
+}
+
bool canvas_event(GdkEvent* ev);
bool note_canvas_event(GdkEvent* ev);
- void midi_channel_mode_changed(ARDOUR::ChannelMode mode, uint16_t mask);
+ void midi_channel_mode_changed ();
+ PBD::ScopedConnection _channel_mode_changed_connection;
void instrument_settings_changed ();
PBD::ScopedConnection _instrument_changed_connection;
void show_verbose_cursor (std::string const &, double, double) const;
void show_verbose_cursor (boost::shared_ptr<NoteType>) const;
- ARDOUR::ChannelMode _last_channel_mode;
- uint16_t _last_channel_selection;
-
uint8_t _current_range_min;
uint8_t _current_range_max;
Gdk::Cursor* pre_press_cursor;
NotePlayer* _note_player;
+
+ ARDOUR::ChannelMode get_channel_mode() const;
+ uint16_t get_selected_channels () const;
};
#include <cstdlib>
#include <cmath>
+#include <strings.h> // for ffs(3)
+
#include <algorithm>
#include <string>
#include <vector>
#include "ghostregion.h"
#include "gui_thread.h"
#include "keyboard.h"
+#include "midi_channel_selector.h"
#include "midi_scroomer.h"
#include "midi_streamview.h"
#include "midi_region_view.h"
using namespace Editing;
// Minimum height at which a control is displayed
-static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 162;
-static const uint32_t KEYBOARD_MIN_HEIGHT = 140;
+static const uint32_t MIDI_CONTROLS_BOX_MIN_HEIGHT = 140;
+static const uint32_t KEYBOARD_MIN_HEIGHT = 130;
MidiTimeAxisView::MidiTimeAxisView (PublicEditor& ed, Session* sess, Canvas& canvas)
: AxisView(sess) // virtually inherited
, _meter_color_mode_item(0)
, _channel_color_mode_item(0)
, _track_color_mode_item(0)
+ , _channel_selector (0)
, _step_edit_item (0)
, controller_menu (0)
, _step_editor (0)
_view->RegionViewAdded.connect (
sigc::mem_fun(*this, &MidiTimeAxisView::region_view_added));
+ midi_track()->PlaybackChannelModeChanged.connect (*this, invalidator (*this),
+ boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
+ gui_context());
+ midi_track()->PlaybackChannelMaskChanged.connect (*this, invalidator (*this),
+ boost::bind (&MidiTimeAxisView::playback_channel_mode_changed, this),
+ gui_context());
+ midi_track()->CaptureChannelModeChanged.connect (*this, invalidator (*this),
+ boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
+ gui_context());
+ midi_track()->CaptureChannelMaskChanged.connect (*this, invalidator (*this),
+ boost::bind (&MidiTimeAxisView::capture_channel_mode_changed, this),
+ gui_context());
+
+ playback_channel_mode_changed ();
+ capture_channel_mode_changed ();
+
if (!_editor.have_idled()) {
/* first idle will do what we need */
} else {
_midi_controls_box.set_homogeneous(false);
_midi_controls_box.set_border_width (10);
- if (!patch_manager.all_models().empty()) {
- _channel_selector.set_border_width(2);
- _channel_selector.show_all ();
+ _channel_status_box.set_homogeneous (false);
+ _channel_status_box.set_spacing (6);
+
+ _channel_selector_button.set_label (_("Chns"));
- _midi_controls_box.resize(3, 2);
- _midi_controls_box.attach(_channel_selector, 0, 2, 0, 1);
-
- _midi_controls_box.attach(*manage(new HSeparator()), 0, 2, 1, 2);
+ /* fixed sized labels to prevent silly nonsense */
+
+ _playback_channel_status.set_size_request (65, -1);
+ _capture_channel_status.set_size_request (60, -1);
+
+ _channel_status_box.pack_start (_playback_channel_status, false, false);
+ _channel_status_box.pack_start (_capture_channel_status, false, false);
+ _channel_status_box.pack_start (_channel_selector_button, false, false);
+ _channel_status_box.show_all ();
+
+ _channel_selector_button.signal_clicked().connect (sigc::mem_fun (*this, &MidiTimeAxisView::toggle_channel_selector));
+
+ _midi_controls_box.pack_start (_channel_status_box, false, false, 10);
+
+ if (!patch_manager.all_models().empty()) {
_midnam_model_selector.set_size_request(22, 30);
_midnam_model_selector.set_border_width(2);
_midnam_model_selector.show ();
- _midi_controls_box.attach(_midnam_model_selector, 0, 1, 2, 3);
+ _midi_controls_box.pack_start (_midnam_model_selector);
_midnam_custom_device_mode_selector.set_size_request(10, 30);
_midnam_custom_device_mode_selector.set_border_width(2);
_midnam_custom_device_mode_selector.show ();
- _midi_controls_box.attach(_midnam_custom_device_mode_selector, 0, 1, 3, 4);
- } else {
- _midi_controls_box.attach(_channel_selector, 0, 1, 0, 1);
- _channel_selector.show_all ();
- }
+ _midi_controls_box.pack_start (_midnam_custom_device_mode_selector);
+ }
model_changed();
custom_device_mode_changed();
controls_vbox.pack_start(_midi_controls_box, false, false);
- // restore channel selector settings
- _channel_selector.set_channel_mode(midi_track()->get_channel_mode(),
- midi_track()->get_channel_mask());
- _channel_selector.mode_changed.connect(
- sigc::mem_fun(*midi_track(), &MidiTrack::set_channel_mode));
- _channel_selector.mode_changed.connect(
- sigc::mem_fun(*this, &MidiTimeAxisView::set_channel_mode));
-
const string color_mode = gui_property ("color-mode");
if (!color_mode.empty()) {
_color_mode = ColorMode (string_2_enum(color_mode, _color_mode));
- if (_color_mode == ChannelColors) {
- _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
+ if (_channel_selector && _color_mode == ChannelColors) {
+ _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
}
}
MidiTimeAxisView::~MidiTimeAxisView ()
{
+ delete _channel_selector;
+
delete _piano_roll_header;
_piano_roll_header = 0;
items.push_back (MenuElem (_("Note Range"), *range_menu));
items.push_back (MenuElem (_("Note Mode"), *build_note_mode_menu()));
+ items.push_back (MenuElem (_("Channel Selector"),
+ sigc::mem_fun(*this, &MidiTimeAxisView::toggle_channel_selector)));
+ color_mode_menu = build_color_mode_menu();
+ if (color_mode_menu) {
+ items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
+ }
+
items.push_back (SeparatorElem ());
}
+void
+MidiTimeAxisView::toggle_channel_selector ()
+{
+ if (!_channel_selector) {
+ _channel_selector = new MidiChannelSelectorWindow (midi_track());
+
+ if (_color_mode == ChannelColors) {
+ _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
+ } else {
+ _channel_selector->set_default_channel_color ();
+ }
+
+ _channel_selector->set_position (WIN_POS_MOUSE);
+ _channel_selector->show_all ();
+ } else {
+ _channel_selector->cycle_visibility ();
+ }
+}
+
void
MidiTimeAxisView::build_automation_action_menu (bool for_selection)
{
MenuList& automation_items = automation_action_menu->items();
- uint16_t selected_channels = _channel_selector.get_selected_channels();
+ uint16_t selected_channels = midi_track()->get_playback_channel_mask();
if (selected_channels != 0) {
void
MidiTimeAxisView::change_all_channel_tracks_visibility (bool yn, Evoral::Parameter param)
{
- const uint16_t selected_channels = _channel_selector.get_selected_channels();
+ const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
for (uint8_t chn = 0; chn < 16; chn++) {
if (selected_channels & (0x0001 << chn)) {
structure if there is more than 1 selected.
*/
- const uint16_t selected_channels = _channel_selector.get_selected_channels();
+ const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
int chn_cnt = 0;
for (uint8_t chn = 0; chn < 16; chn++) {
{
using namespace Menu_Helpers;
- const uint16_t selected_channels = _channel_selector.get_selected_channels();
+ const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
for (uint8_t chn = 0; chn < 16; chn++) {
if (selected_channels & (0x0001 << chn)) {
{
using namespace Menu_Helpers;
- const uint16_t selected_channels = _channel_selector.get_selected_channels();
+ const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
Menu* chn_menu = manage (new Menu);
MenuList& chn_items (chn_menu->items());
combination covering the currently selected channels for this track
*/
- const uint16_t selected_channels = _channel_selector.get_selected_channels();
+ const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
/* count the number of selected channels because we will build a different menu
structure if there is more than 1 selected.
return;
}
- if (mode == ChannelColors) {
- _channel_selector.set_channel_colors(CanvasNoteEvent::midi_channel_colors);
- } else {
- _channel_selector.set_default_channel_color();
+ if (_channel_selector) {
+ if (mode == ChannelColors) {
+ _channel_selector->set_channel_colors(CanvasNoteEvent::midi_channel_colors);
+ } else {
+ _channel_selector->set_default_channel_color();
+ }
}
_color_mode = mode;
return;
}
- uint16_t chn_mask = _channel_selector.get_selected_channels();
+ uint16_t chn_mask = midi_track()->get_playback_channel_mask();
if (_view->num_selected_regionviews() == 0) {
_view->foreach_regionview (
return;
}
- const uint16_t chn_mask = _channel_selector.get_selected_channels();
+ const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
if (_view->num_selected_regionviews() == 0) {
_view->foreach_regionview (
return;
}
- const uint16_t chn_mask = _channel_selector.get_selected_channels();
+ const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
if (_view->num_selected_regionviews() == 0) {
_view->foreach_regionview (
return;
}
- const uint16_t chn_mask = _channel_selector.get_selected_channels();
+ const uint16_t chn_mask = midi_track()->get_playback_channel_mask();
if (_view->num_selected_regionviews() == 0) {
_view->foreach_regionview (
the right ones.
*/
- const uint16_t selected_channels = _channel_selector.get_selected_channels();
+ const uint16_t selected_channels = midi_track()->get_playback_channel_mask();
bool changed = false;
no_redraw = true;
uint8_t
MidiTimeAxisView::get_channel_for_add () const
{
- uint16_t const chn_mask = _channel_selector.get_selected_channels ();
+ uint16_t const chn_mask = midi_track()->get_playback_channel_mask();
int chn_cnt = 0;
uint8_t channel = 0;
{
_range_scroomer->set_size_request (-1, _view->child_height ());
}
+
+void
+MidiTimeAxisView::playback_channel_mode_changed ()
+{
+ switch (midi_track()->get_playback_channel_mode()) {
+ case AllChannels:
+ _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), ("all")));
+ break;
+ case FilterChannels:
+ _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Play"), ("some")));
+ break;
+ case ForceChannel:
+ _playback_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Play"), ("all"), ffs (midi_track()->get_playback_channel_mask())));
+ break;
+ }
+}
+
+void
+MidiTimeAxisView::capture_channel_mode_changed ()
+{
+ switch (midi_track()->get_capture_channel_mode()) {
+ case AllChannels:
+ _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), ("all")));
+ break;
+ case FilterChannels:
+ _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2</i>", _("Rec"), ("some")));
+ break;
+ case ForceChannel:
+ _capture_channel_status.set_markup (string_compose ("<b>%1</b>: <i>%2>%3</i>", _("Rec"), ("all"), ffs (midi_track()->get_capture_channel_mask())));
+ break;
+ }
+}
#include "route_time_axis.h"
#include "canvas.h"
#include "midi_streamview.h"
-#include "midi_channel_selector.h"
namespace MIDI {
namespace Name {
class PianoRollHeader;
class StepEntry;
class StepEditor;
+class MidiChannelSelectorWindow;
class MidiTimeAxisView : public RouteTimeAxisView
{
void update_range();
- sigc::signal<void, ARDOUR::ChannelMode, uint16_t>& signal_channel_mode_changed() {
- return _channel_selector.mode_changed;
- }
-
- const MidiMultipleChannelSelector& channel_selector() { return _channel_selector; }
-
Gtk::CheckMenuItem* automation_child_menu_item (Evoral::Parameter);
StepEditor* step_editor() { return _step_editor; }
Gtk::RadioMenuItem* _meter_color_mode_item;
Gtk::RadioMenuItem* _channel_color_mode_item;
Gtk::RadioMenuItem* _track_color_mode_item;
- Gtk::Table _midi_controls_box;
- MidiMultipleChannelSelector _channel_selector;
+ Gtk::Label _playback_channel_status;
+ Gtk::Label _capture_channel_status;
+ Gtk::HBox _channel_status_box;
+ Gtk::Button _channel_selector_button;
+ Gtk::VBox _midi_controls_box;
+ MidiChannelSelectorWindow* _channel_selector;
Gtk::ComboBoxText _midnam_model_selector;
Gtk::ComboBoxText _midnam_custom_device_mode_selector;
void add_single_channel_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, int ctl, const std::string& name);
void add_multi_channel_controller_item (Gtk::Menu_Helpers::MenuList& ctl_items, int ctl, const std::string& name);
void build_controller_menu ();
+ void toggle_channel_selector ();
+ void channel_selector_hidden ();
void set_channel_mode (ARDOUR::ChannelMode, uint16_t);
void set_note_selection (uint8_t note);
ParameterMenuMap _controller_menu_map;
StepEditor* _step_editor;
+
+ void capture_channel_mode_changed();
+ void playback_channel_mode_changed();
};
#endif /* __ardour_midi_time_axis_h__ */
step = one_degree * 5.0;
}
- /* up/down control width because we consider pan position more "important"
- (and thus having higher "sense" priority) than width.
- */
-
switch (ev->keyval) {
case GDK_Left:
pv -= step;
pv += step;
position_control->set_value (pv);
break;
+ case GDK_0:
+ case GDK_KP_0:
+ position_control->set_value (0.0);
+ break;
default:
return false;
}
related to "hidden"
*/
plugin_display.append_column (_("Fav"), plugin_columns.favorite);
- plugin_display.append_column (_("Hid"), plugin_columns.hidden);
+ plugin_display.append_column (_("Hide"), plugin_columns.hidden);
plugin_display.append_column (_("Available Plugins"), plugin_columns.name);
plugin_display.append_column (_("Type"), plugin_columns.type_name);
plugin_display.append_column (_("Category"), plugin_columns.category);
manager.set_status (pi->type, pi->unique_id, status);
manager.save_statuses ();
+
+ build_plugin_menu ();
}
in_row_change = false;
}
virtual void set_xjadeo_sensitive (bool onoff) = 0;
virtual int get_videotl_bar_height () const = 0;
virtual void set_video_timeline_height (const int h) = 0;
- virtual void embed_audio_from_video (std::string) = 0;
+ virtual void embed_audio_from_video (std::string, framepos_t n = 0) = 0;
virtual void export_video () = 0;
#endif
, _show_xjadeo_setup_button (_("Show Video Monitor Option Dialog"))
, _show_video_export_info_button (_("Show Video Export Info before export"))
, _show_video_server_dialog_button (_("Show Video Server Startup Dialog"))
+ , _video_advanced_setup_button (_("Advanced Setup (remote video server)"))
{
- Table* t = manage (new Table (2, 6));
+ Table* t = manage (new Table (2, 7));
t->set_spacings (4);
+ t->attach (_video_advanced_setup_button, 0, 2, 0, 1);
+ _video_advanced_setup_button.signal_toggled().connect (sigc::mem_fun (*this, &VideoTimelineOptions::video_advanced_setup_toggled));
+ Gtkmm2ext::UI::instance()->set_tip (_video_advanced_setup_button,
+ _("<b>When enabled</b> you can speficify a custom video-server URL and docroot. - Do not enable this option unless you know what you are doing."));
+
Label* l = manage (new Label (_("Video Server URL:")));
l->set_alignment (0, 0.5);
- t->attach (*l, 0, 1, 0, 1, FILL);
- t->attach (_video_server_url_entry, 1, 2, 0, 1, FILL);
+ t->attach (*l, 0, 1, 1, 2, FILL);
+ t->attach (_video_server_url_entry, 1, 2, 1, 2, FILL);
Gtkmm2ext::UI::instance()->set_tip (_video_server_url_entry,
_("Base URL of the video-server including http prefix. This is usually 'http://hostname.example.org:1554/' and defaults to 'http://localhost:1554/' when the video-server is runing locally"));
- l = manage (new Label (_("Video Server Docroot:")));
+ l = manage (new Label (_("Video Folder:")));
l->set_alignment (0, 0.5);
- t->attach (*l, 0, 1, 1, 2, FILL);
- t->attach (_video_server_docroot_entry, 1, 2, 1, 2);
+ t->attach (*l, 0, 1, 2, 3, FILL);
+ t->attach (_video_server_docroot_entry, 1, 2, 2, 3);
Gtkmm2ext::UI::instance()->set_tip (_video_server_docroot_entry,
- _("Local path to the video-server docroot. If the server runs remotely, it should point to a network mounted folder of the server's docroot or be left empty if it is unvailable. It is used for the local video-monitor and file-browsing when opening/adding a video file."));
+ _("Local path to the video-server document-root. Only files below this directory will be accessible by the video-server. If the server run on a remote host, it should point to a network mounted folder of the server's docroot or be left empty if it is unvailable. It is used for the local video-monitor and file-browsing when opening/adding a video file."));
- t->attach (_show_xjadeo_setup_button, 0, 2, 3, 4);
+ /* small vspace y=3..4 */
+
+ t->attach (_show_xjadeo_setup_button, 0, 2, 4, 5);
_show_xjadeo_setup_button.signal_toggled().connect (sigc::mem_fun (*this, &VideoTimelineOptions::show_xjadeo_setup_toggled));
Gtkmm2ext::UI::instance()->set_tip (_show_xjadeo_setup_button,
_("<b>When enabled</b> an option dialog is presented before opening the video monitor"));
- t->attach (_show_video_export_info_button, 0, 2, 4, 5);
+ t->attach (_show_video_export_info_button, 0, 2, 5, 6);
_show_video_export_info_button.signal_toggled().connect (sigc::mem_fun (*this, &VideoTimelineOptions::show_video_export_info_toggled));
Gtkmm2ext::UI::instance()->set_tip (_show_video_export_info_button,
_("<b>When enabled</b> an information window with details is displayed before the video-export dialog."));
- t->attach (_show_video_server_dialog_button, 0, 2, 5, 6);
+ t->attach (_show_video_server_dialog_button, 0, 2, 6, 7);
_show_video_server_dialog_button.signal_toggled().connect (sigc::mem_fun (*this, &VideoTimelineOptions::show_video_server_dialog_toggled));
Gtkmm2ext::UI::instance()->set_tip (_show_video_server_dialog_button,
_("<b>When enabled</b> the video server is never launched automatically without confirmation"));
_rc_config->set_show_video_server_dialog (x);
}
+ void video_advanced_setup_toggled ()
+ {
+ bool const x = _video_advanced_setup_button.get_active ();
+ _rc_config->set_video_advanced_setup(x);
+ }
+
void parameter_changed (string const & p)
{
if (p == "video-server-url") {
} else if (p == "show-video-export-info") {
bool const x = _rc_config->get_show_video_export_info();
_show_video_export_info_button.set_active (x);
+ } else if (p == "show-video-server-dialog") {
+ bool const x = _rc_config->get_show_video_server_dialog();
+ _show_video_server_dialog_button.set_active (x);
+ } else if (p == "video-advanced-setup") {
+ bool const x = _rc_config->get_video_advanced_setup();
+ _video_advanced_setup_button.set_active(x);
+ _video_server_docroot_entry.set_sensitive(x);
+ _video_server_url_entry.set_sensitive(x);
}
}
parameter_changed ("video-server-docroot");
parameter_changed ("video-monitor-setup-dialog");
parameter_changed ("show-video-export-info");
- parameter_changed ("how-video-server-dialog");
+ parameter_changed ("show-video-server-dialog");
+ parameter_changed ("video-advanced-setup");
}
private:
CheckButton _show_xjadeo_setup_button;
CheckButton _show_video_export_info_button;
CheckButton _show_video_server_dialog_button;
+ CheckButton _video_advanced_setup_button;
};
#endif
items.push_back (MenuElem (_("Mode"), *mode_menu));
}
- color_mode_menu = build_color_mode_menu();
- if (color_mode_menu) {
- items.push_back (MenuElem (_("Color Mode"), *color_mode_menu));
- }
items.push_back (SeparatorElem());
ReadStdout(data, bytesRead);/* EMIT SIGNAL */
}
Terminated();/* EMIT SIGNAL */
- terminate();
}
void
void
SystemExec::terminate ()
{
+ ::pthread_mutex_lock(&write_lock);
close_stdin();
if (pid) {
::usleep(100000);
wait();
if (thread_active) pthread_join(thread_id_tt, NULL);
+ thread_active = false;
+ ::pthread_mutex_unlock(&write_lock);
}
int
ReadStdout(rv, r);/* EMIT SIGNAL */
}
Terminated();/* EMIT SIGNAL */
- terminate();
}
void
::close(pin[1]);
::close(pout[0]);
::close(pout[1]);
+ pin[1] = - 1; // mark as closed
}
int
}
c += r;
}
+ fsync(pin[1]);
::pthread_mutex_unlock(&write_lock);
return c;
}
#endif
std::string ff_file_path;
- if (find_file_in_search_path (PBD::SearchPath(Glib::getenv("PATH")), X_("ffmpeg"), ff_file_path)) { ffmpeg_exe = ff_file_path; }
- else if (find_file_in_search_path (PBD::SearchPath(Glib::getenv("PATH")), X_("avconv"), ff_file_path)) { ffmpeg_exe = ff_file_path; }
- else if (find_file_in_search_path (PBD::SearchPath(std::string("/usr/local/bin/")), X_("ffmpeg_harvid"), ff_file_path)) { ffmpeg_exe = ff_file_path; }
+ if (find_file_in_search_path (PBD::SearchPath(Glib::getenv("PATH")), X_("ffmpeg_harvid"), ff_file_path)) { ffmpeg_exe = ff_file_path; }
+ else if (Glib::file_test(X_("C:\\Program Files\\harvid\\ffmpeg.exe"), Glib::FILE_TEST_EXISTS)) {
+ ffmpeg_exe = X_("C:\\Program Files\\ffmpeg\\ffmpeg.exe");
+ }
else if (Glib::file_test(X_("C:\\Program Files\\ffmpeg\\ffmpeg.exe"), Glib::FILE_TEST_EXISTS)) {
ffmpeg_exe = X_("C:\\Program Files\\ffmpeg\\ffmpeg.exe");
}
- if (find_file_in_search_path (PBD::SearchPath(Glib::getenv("PATH")), X_("ffprobe"), ff_file_path)) { ffprobe_exe = ff_file_path; }
-//else if (find_file_in_search_path (PBD::SearchPath(Glib::getenv("PATH")), X_("avprobe"), ff_file_path)) { ffprobe_exe = ff_file_path; }
- else if (find_file_in_search_path (PBD::SearchPath(std::string("/usr/local/bin/")), X_("ffprobe_harvid"), ff_file_path)) { ffprobe_exe = ff_file_path; }
+ if (find_file_in_search_path (PBD::SearchPath(Glib::getenv("PATH")), X_("ffprobe_harvid"), ff_file_path)) { ffprobe_exe = ff_file_path; }
+ else if (Glib::file_test(X_("C:\\Program Files\\harvid\\ffprobe.exe"), Glib::FILE_TEST_EXISTS)) {
+ ffprobe_exe = X_("C:\\Program Files\\ffmpeg\\ffprobe.exe");
+ }
else if (Glib::file_test(X_("C:\\Program Files\\ffmpeg\\ffprobe.exe"), Glib::FILE_TEST_EXISTS)) {
ffprobe_exe = X_("C:\\Program Files\\ffmpeg\\ffprobe.exe");
}
if (ffmpeg_exe.empty() || ffprobe_exe.empty()) {
- PBD::warning << _("No ffprobe or ffmpeg executables could be found on this system. Transcoding is not possible until you install those tools.") << endmsg;
+ PBD::warning << _(
+ "No ffprobe or ffmpeg executables could be found on this system.\n"
+ "Video import and export is not possible until you install those tools.\n"
+ "Ardour requires ffmpeg and ffprobe from ffmpeg.org - version 1.1 or newer.\n"
+ "\n"
+ "The tools are included with the Ardour releases from ardour.org "
+ "and also available with the video-server at http://x42.github.com/harvid/\n"
+ "\n"
+ "Important: the files need to be installed in $PATH and named ffmpeg_harvid and ffprobe_harvid.\n"
+ "If you already have a suitable ffmpeg installation on your system, we recommend creating "
+ "symbolic links from ffmpeg to ffmpeg_harvid and from ffprobe to ffprobe_harvid.\n"
+ ) << endmsg;
return;
}
ffexecok = true;
char **argp;
argp=(char**) calloc(7,sizeof(char*));
argp[0] = strdup(ffprobe_exe.c_str());
- argp[1] = strdup("-print_format"); // "-of" ; new version and avprobe compat but avprobe does not yet support csv
- argp[2] = strdup("csv"); // TODO use "csv=nk=0" and parse key/value pairs -> ffprobe version agnostic or parse XML or JSON key/value
+ argp[1] = strdup("-print_format");
+ argp[2] = strdup("csv=nk=0");
argp[3] = strdup("-show_format");
argp[4] = strdup("-show_streams");
argp[5] = strdup(infile.c_str());
m_codec.clear();
m_audio.clear();
+#define PARSE_FRACTIONAL_FPS \
+ { \
+ std::string::size_type pos; \
+ m_fps = atof(value.c_str()); \
+ pos = value.find_first_of('/'); \
+ if (pos != std::string::npos) { \
+ m_fps = atof(value.substr(0, pos).c_str()) / atof(value.substr(pos+1).c_str()); \
+ } \
+ }
+
for (std::vector<std::vector<std::string> >::iterator i = lines.begin(); i != lines.end(); ++i) {
if (i->at(0) == X_("format")) {
/* format,filename,#streams,format-name,format-long-name,start-time,duration,size,bitrate */
} else
if (i->at(0) == X_("stream")) {
- /*--------- Stream format
- * stream,index,codec-name,codec-name-long,PROFILE,
- * codec_time_base,codec_tag_string,codec_tag[hex],
- * VIDEO:
- * width,height,has_b_frames,sample_aspect_ratio,display_aspect_ratio
- * pix_fmt,level,
- * timecode
- * AUDIO:
- * sample_fmt,sample_rate,channels,bits_per_sample
- *
- * all cont'd;
- * r_frame_rate,avg_frame_rate,time_base,start_time,duration,
- * bit_rate,nb_frames,nb_read_frames,nb_read_packets
- *
- *---------- Example
- * stream,0,mpeg2video,MPEG-2 video,video,1/50,[0][0][0][0],0x0000,720,576,1,16:15,4:3,yuv420p,8,00:02:30:00,0x1e0,25/1,25/1,1/90000,0.360000,N/A,7000000,N/A,N/A,N/A
- * stream,1,ac3,ATSC A/52A (AC-3),audio,1/48000,[0][0][0][0],0x0000,s16,48000,6,0,-1,-1.000000,-1.000000,-1.000000,-1.000000,0x80,0/0,0/0,1/90000,0.280000,312.992000,448000,N/A,N/A,N/A
- * stream,2,ac3,ATSC A/52A (AC-3),audio,1/48000,[0][0][0][0],0x0000,s16,48000,2,0,-1,-1.000000,-1.000000,-1.000000,-1.000000,0x82,0/0,0/0,1/90000,0.280000,312.992000,384000,N/A,N/A,N/A
- * stream,3,ac3,ATSC A/52A (AC-3),audio,1/48000,[0][0][0][0],0x0000,s16,48000,2,0,-1,-1.000000,-1.000000,-1.000000,-1.000000,0x81,0/0,0/0,1/90000,0.280000,312.992000,192000,N/A,N/A,N/A
- */
- if (i->at(4) == X_("video") && m_width == 0) {
- std::string::size_type pos;
-
- m_width = atoi(i->at(8).c_str());
- m_height = atoi(i->at(9).c_str());
- m_codec = i->at(3) + " -- " + i->at(2);
- m_fps = atof(i->at(17).c_str());
-
- pos = i->at(17).find_first_of('/');
- if (pos != std::string::npos) {
- m_fps = atof(i->at(17).substr(0, pos).c_str()) / atof(i->at(17).substr(pos+1).c_str());
+ if (i->at(5) == X_("codec_type=video") && m_width == 0) {
+
+ for (std::vector<std::string>::iterator kv = i->begin(); kv != i->end(); ++kv) {
+ const size_t kvsep = kv->find('=');
+ if(kvsep == std::string::npos) continue;
+ std::string key = kv->substr(0, kvsep);
+ std::string value = kv->substr(kvsep + 1);
+
+ if (key == X_("width")) {
+ m_width = atoi(value.c_str());
+ } else if (key == X_("height")) {
+ m_height = atoi(value.c_str());
+ } else if (key == X_("codec_name")) {
+ if (!m_codec.empty()) m_codec += " ";
+ m_codec += value;
+ } else if (key == X_("codec_long_name")) {
+ if (!m_codec.empty()) m_codec += " ";
+ m_codec += "[" + value + "]";
+ } else if (key == X_("codec_tag_string")) {
+ if (!m_codec.empty()) m_codec += " ";
+ m_codec += "(" + value + ")";
+ } else if (key == X_("r_frame_rate")) {
+ PARSE_FRACTIONAL_FPS
+ } else if (key == X_("time_base") && m_fps == 0) {
+ PARSE_FRACTIONAL_FPS
+ } else if (key == X_("timecode") && m_duration == 0) {
+ int h,m,s; char f[7];
+ if (sscanf(i->at(16).c_str(), "%d:%d:%d:%s",&h,&m,&s,f) == 4) {
+ m_duration = (ARDOUR::framecnt_t) floor(m_fps * (
+ h * 3600.0
+ + m * 60.0
+ + s * 1.0
+ + atoi(f) / pow(10, strlen(f))
+ ));
+ }
+ } else if (key == X_("duration_ts")) {
+ m_duration = atof(value.c_str());
+ } else if (key == X_("duration") && m_duration == 0 && m_fps != 0) {
+ m_duration = atof(value.c_str()) * m_fps;
+ } else if (key == X_("display_aspect_ratio")) {
+ std::string::size_type pos;
+ pos = value.find_first_of(':');
+ if (pos != std::string::npos && atof(value.substr(pos+1).c_str()) != 0) {
+ m_aspect = atof(value.substr(0, pos).c_str()) / atof(value.substr(pos+1).c_str());
+ }
+ }
}
- pos = i->at(12).find_first_of(':');
- m_aspect = 0;
- if (pos != std::string::npos && atof(i->at(12).substr(pos+1).c_str()) != 0) {
- m_aspect = atof(i->at(12).substr(0, pos).c_str()) / atof(i->at(12).substr(pos+1).c_str());
- }
if (m_aspect == 0) {
m_aspect = (double)m_width / (double)m_height;
}
- int h,m,s; char f[7];
- if (sscanf(i->at(15).c_str(), "%d:%d:%d:%s",&h,&m,&s,f) == 4) {
- m_duration = (ARDOUR::framecnt_t) floor(m_fps * (
- h * 3600.0
- + m * 60.0
- + s * 1.0
- + atoi(f) / pow(10, strlen(f))
- ));
- } else {
- m_duration = atof(i->at(21).c_str()) * m_fps;
- }
-
- } else if (i->at(4) == X_("audio")) {
+ } else if (i->at(5) == X_("codec_type=audio")) { /* new ffprobe */
AudioStream as;
- as.name = i->at(3) + " " + i->at(2) + " " + i->at(8) + " " + i->at(9);
- as.stream_id = i->at(1);
- as.channels = atoi(i->at(10).c_str());
- m_audio.push_back(as);
-
- } else if (i->at(5) == X_("video") && m_width == 0) { /* new ffprobe */
- std::string::size_type pos;
+ for (std::vector<std::string>::iterator kv = i->begin(); kv != i->end(); ++kv) {
+ const size_t kvsep = kv->find('=');
+ if(kvsep == std::string::npos) continue;
+ std::string key = kv->substr(0, kvsep);
+ std::string value = kv->substr(kvsep + 1);
+
+ if (key == X_("channels")) {
+ as.channels = atoi(value.c_str());
+ } else if (key == X_("index")) {
+ as.stream_id = value;
+ } else if (key == X_("codec_long_name")) {
+ if (!as.name.empty()) as.name += " ";
+ as.name += value;
+ } else if (key == X_("codec_name")) {
+ if (!as.name.empty()) as.name += " ";
+ as.name += value;
+ } else if (key == X_("sample_fmt")) {
+ if (!as.name.empty()) as.name += " ";
+ as.name += "FMT:" + value;
+ } else if (key == X_("sample_rate")) {
+ if (!as.name.empty()) as.name += " ";
+ as.name += "SR:" + value;
+ }
- m_width = atoi(i->at(9).c_str());
- m_height = atoi(i->at(10).c_str());
- m_codec = i->at(3) + " -- " + i->at(2);
- m_fps = atof(i->at(18).c_str());
-
- pos = i->at(18).find_first_of('/');
- if (pos != std::string::npos) {
- m_fps = atof(i->at(18).substr(0, pos).c_str()) / atof(i->at(18).substr(pos+1).c_str());
- }
-
- pos = i->at(13).find_first_of(':');
- m_aspect = 0;
- if (pos != std::string::npos && atof(i->at(13).substr(pos+1).c_str()) != 0) {
- m_aspect = atof(i->at(13).substr(0, pos).c_str()) / atof(i->at(13).substr(pos+1).c_str());
- }
- if (m_aspect == 0) {
- m_aspect = (double)m_width / (double)m_height;
}
-
- int h,m,s; char f[7];
- if (sscanf(i->at(17).c_str(), "%d:%d:%d:%s",&h,&m,&s,f) == 4) {
- m_duration = (ARDOUR::framecnt_t) floor(m_fps * (
- h * 3600.0
- + m * 60.0
- + s * 1.0
- + atoi(f) / pow(10, strlen(f))
- ));
- } else if (atof(i->at(23).c_str()) != 0) {
- m_duration = atof(i->at(23).c_str());
- } else {
- m_duration = atof(i->at(24).c_str()) * m_fps;
- }
-
- } else if (i->at(5) == X_("audio")) { /* new ffprobe */
- AudioStream as;
- as.name = i->at(3) + " " + i->at(2) + " " + i->at(9) + " " + i->at(10);
- as.stream_id = i->at(1);
- as.channels = atoi(i->at(11).c_str());
m_audio.push_back(as);
}
}
while (ffcmd && --timeout) usleep (1000); // wait until 'ffprobe' terminated.
if (timeout == 0) return false;
-#if 0 /* DEBUG */
+#if 1 /* DEBUG */
printf("FPS: %f\n", m_fps);
printf("Duration: %lu frames\n",(unsigned long)m_duration);
printf("W/H: %ix%i\n",m_width, m_height);
/*
- Copyright (C) 2010 Paul Davis
+ Copyright (C) 2010,2013 Paul Davis
Author: Robin Gareus <robin@gareus.org>
This program is free software; you can redistribute it and/or modify
#include "utils.h"
#include "opts.h"
#include "transcode_video_dialog.h"
-#include "video_copy_dialog.h"
#include "utils_videotl.h"
#include "i18n.h"
, infn (infile)
, path_label (_("Output File:"), Gtk::ALIGN_LEFT)
, browse_button (_("Browse"))
- , transcode_button (_("Transcode Video\n And Import"))
- , copy_button (_("Copy Video\nFile Only"))
- , audio_button (_("Extract and\nImport Audio Only"))
+ , transcode_button (_("OK"))
, abort_button (_("Abort"))
, progress_label ()
, aspect_checkbox (_("Height = "))
, height_adjustment (128, 0, 1920, 1, 16, 0)
, height_spinner (height_adjustment)
, bitrate_checkbox (_("Manual Override"))
- , bitrate_adjustment (2000, 100, 10000, 10, 100, 0)
+ , bitrate_adjustment (2000, 500, 10000, 10, 100, 0)
, bitrate_spinner (bitrate_adjustment)
#if 1 /* tentative debug mode */
, debug_checkbox (_("Debug Mode: Print ffmpeg Command and Output to stdout."))
transcoder = new TranscodeFfmpeg(infile);
audiofile = "";
pending_audio_extract = false;
- pending_copy_file = false;
+ aborted = false;
set_name ("TranscodeVideoDialog");
set_position (Gtk::WIN_POS_MOUSE);
height_spinner.set_sensitive(false);
bitrate_spinner.set_sensitive(false);
- std::string dstdir = video_dest_dir(_session->session_directory().video_path(), Config->get_video_server_docroot());
+ std::string dstdir = video_dest_dir(_session->session_directory().video_path(), video_get_docroot(Config));
std::string dstfn = video_dest_file(dstdir, infile);
path_entry.set_text (dstfn);
options_box->pack_start (*l, false, true, 4);
+ bool ffok = false;
if (!transcoder->ffexec_ok()) {
- l = manage (new Label (_("No ffprobe or ffmpeg executables could be found on this system. Transcoding is not possible until you install those tools."), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
+ l = manage (new Label (_("No ffprobe or ffmpeg executables could be found on this system. Video Import is not possible until you install those tools. See the Log widow for more information."), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
l->set_line_wrap();
options_box->pack_start (*l, false, true, 4);
- transcode_button.set_sensitive(false);
aspect_checkbox.set_sensitive(false);
bitrate_checkbox.set_sensitive(false);
}
else if (!transcoder->probe_ok()) {
- l = manage (new Label (string_compose(_("Video file-info could not be read. Most likely '%1' is not a valid video-file. It could also be a rare unsupported video codec or format."), infn), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
+ l = manage (new Label (string_compose(_("File-info can not be read. Most likely '%1' is not a valid video-file or an unsupported video codec or format."), infn), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
options_box->pack_start (*l, false, true, 4);
- transcode_button.set_sensitive(false);
aspect_checkbox.set_sensitive(false);
bitrate_checkbox.set_sensitive(false);
} else {
+ ffok = true;
w = transcoder->get_width();
h = transcoder->get_height();
as = transcoder->get_audio();
t->attach (*l, 1, 2, 1, 2);
}
- l = manage (new Label (_("<b>Options</b>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
+ l = manage (new Label (_("<b>Video</b>"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
l->set_use_markup ();
options_box->pack_start (*l, false, true, 4);
+ video_combo.set_name ("PaddedButton");
+ video_combo.append_text(_("Do Not Import Video"));
+ video_combo.append_text(_("Reference From Current Location"));
+ if (ffok) {
+ video_combo.append_text(_("Import/Transcode Video to Session"));
+ video_combo.set_active(2);
+ } else {
+ video_combo.set_active(1);
+ video_combo.set_sensitive(false);
+ audio_combo.set_sensitive(false);
+ }
+
+ options_box->pack_start (video_combo, false, false, 4);
+
Table* t = manage (new Table (4, 3));
t->set_spacings (4);
options_box->pack_start (*t, true, true, 4);
get_vbox()->pack_start (*progress_box, false, false);
browse_button.signal_clicked().connect (sigc::mem_fun (*this, &TranscodeVideoDialog::open_browse_dialog));
- copy_button.signal_clicked().connect (sigc::mem_fun (*this, &TranscodeVideoDialog::prepare_copy));
- audio_button.signal_clicked().connect (sigc::mem_fun (*this, &TranscodeVideoDialog::launch_audioonly));
transcode_button.signal_clicked().connect (sigc::mem_fun (*this, &TranscodeVideoDialog::launch_transcode));
abort_button.signal_clicked().connect (sigc::mem_fun (*this, &TranscodeVideoDialog::abort_clicked));
+ video_combo.signal_changed().connect (sigc::mem_fun (*this, &TranscodeVideoDialog::video_combo_changed));
audio_combo.signal_changed().connect (sigc::mem_fun (*this, &TranscodeVideoDialog::audio_combo_changed));
scale_combo.signal_changed().connect (sigc::mem_fun (*this, &TranscodeVideoDialog::scale_combo_changed));
aspect_checkbox.signal_toggled().connect (sigc::mem_fun (*this, &TranscodeVideoDialog::aspect_checkbox_toggled));
update_bitrate();
- audio_button.set_sensitive(false);
-
cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
- get_action_area()->pack_start (audio_button, false, false);
- get_action_area()->pack_start (copy_button, false, false);
get_action_area()->pack_start (transcode_button, false, false);
show_all_children ();
progress_box->hide();
}
Gtk::Dialog::response(RESPONSE_CANCEL);
} else {
- if (pending_audio_extract || pending_copy_file) {
+ if (pending_audio_extract) {
StartNextStage();
} else {
Gtk::Dialog::response(RESPONSE_ACCEPT);
}
void
-TranscodeVideoDialog::prepare_copy ()
+TranscodeVideoDialog::launch_audioonly ()
{
- dialog_progress_mode();
-#if 1 /* tentative debug mode */
- if (debug_checkbox.get_active()) {
- transcoder->set_debug(true);
- }
-#endif
-
if (audio_combo.get_active_row_number() == 0) {
- launch_copy();
- } else {
- aborted = false;
- pending_copy_file = true;
- StartNextStage.connect(*this, invalidator (*this), boost::bind (&TranscodeVideoDialog::launch_copy , this), gui_context());
- transcoder->Progress.connect(*this, invalidator (*this), boost::bind (&TranscodeVideoDialog::update_progress , this, _1, _2), gui_context());
- transcoder->Finished.connect(*this, invalidator (*this), boost::bind (&TranscodeVideoDialog::finished, this), gui_context());
- launch_extract();
+ finished();
+ return;
}
-}
-
-void
-TranscodeVideoDialog::launch_audioonly ()
-{
dialog_progress_mode();
- path_entry.set_text("");
#if 1 /* tentative debug mode */
if (debug_checkbox.get_active()) {
transcoder->set_debug(true);
}
#endif
- if (audio_combo.get_active_row_number() == 0) {
- return;
- }
transcoder->Progress.connect(*this, invalidator (*this), boost::bind (&TranscodeVideoDialog::update_progress , this, _1, _2), gui_context());
transcoder->Finished.connect(*this, invalidator (*this), boost::bind (&TranscodeVideoDialog::finished, this), gui_context());
launch_extract();
}
-void
-TranscodeVideoDialog::launch_copy ()
-{
- hide();
- VideoCopyDialog *video_copy_dialog;
- video_copy_dialog = new VideoCopyDialog(_session, infn);
- video_copy_dialog->setup_non_interactive_copy(path_entry.get_text());
- ResponseType r = (ResponseType) video_copy_dialog->run ();
- video_copy_dialog->hide();
- delete video_copy_dialog;
- Gtk::Dialog::response(r);
-}
-
void
TranscodeVideoDialog::launch_extract ()
{
- audiofile= path_entry.get_text() + ".wav"; /* TODO: mktemp & check if file exists in audiofiles */
- /* think: do_embed() vs do_import() - editor_videotimeline.cc
- * directly use _session->session_directory().sound_path() ?!
- */
+ audiofile= path_entry.get_text() + ".wav"; /* TODO: mktemp */
int audio_stream;
pending_audio_extract = false;
aborted = false;
{
vbox->hide();
cancel_button->hide();
- copy_button.hide();
transcode_button.hide();
pbar.set_size_request(300,-1);
progress_box->show();
void
TranscodeVideoDialog::launch_transcode ()
{
+ if (video_combo.get_active_row_number() != 2) {
+ launch_audioonly();
+ return;
+ }
std::string outfn = path_entry.get_text();
- if (!confirm_video_outfn(outfn, Config->get_video_server_docroot())) return;
+ if (!confirm_video_outfn(outfn, video_get_docroot(Config))) return;
progress_label.set_text (_("Transcoding Video.."));
dialog_progress_mode();
#if 1 /* tentative debug mode */
}
void
-TranscodeVideoDialog::audio_combo_changed ()
+TranscodeVideoDialog::video_combo_changed ()
{
- bool use_audio = audio_combo.get_active_row_number() != 0;
- audio_button.set_sensitive(use_audio);
- if (use_audio) {
- copy_button.set_label(_("Copy File And\nExtract Audio"));
+ int i = video_combo.get_active_row_number();
+ if (i != 2) {
+ scale_combo.set_sensitive(false);
+ aspect_checkbox.set_sensitive(false);
+ height_spinner.set_sensitive(false);
+ bitrate_checkbox.set_sensitive(false);
+ bitrate_spinner.set_sensitive(false);
} else {
- copy_button.set_label(_("Copy Video\nFile Only"));
+ scale_combo.set_sensitive(true);
+ aspect_checkbox.set_sensitive(true);
+ height_spinner.set_sensitive(true);
+ bitrate_checkbox.set_sensitive(true);
+ bitrate_spinner.set_sensitive(true);
}
}
+void
+TranscodeVideoDialog::audio_combo_changed ()
+{
+ ;
+}
+
void
TranscodeVideoDialog::scale_combo_changed ()
{
- update_bitrate();
if (!aspect_checkbox.get_active()) {
int h;
if (scale_combo.get_active_row_number() == 0 ) {
}
height_spinner.set_value(h);
}
+ update_bitrate();
}
void
if (bitrate_checkbox.get_active() || !transcoder->probe_ok()) { return; }
br *= transcoder->get_fps();
br *= height_spinner.get_value();
+
if (scale_combo.get_active_row_number() == 0 ) {
- br *= transcoder->get_height();
+ br *= transcoder->get_width();
} else {
br *= atof(scale_combo.get_active_text().c_str());
}
- bitrate_spinner.set_value(floor(br/10000.0)*10);
+ if (br != 0) {
+ bitrate_spinner.set_value(floor(br/10000.0)*10);
+ }
}
void
}
}
+enum VtlTranscodeOption
+TranscodeVideoDialog::import_option() {
+ int i = video_combo.get_active_row_number();
+ return static_cast<VtlTranscodeOption>(i);
+}
+
#endif /* WITH_VIDEOTIMELINE */
#include "transcode_ffmpeg.h"
+enum VtlTranscodeOption {
+ VTL_IMPORT_NO_VIDEO = 0,
+ VTL_IMPORT_REFERENCE = 1,
+ VTL_IMPORT_TRANSCODED = 2
+};
+
/** @class TranscodeVideoDialog
* @brief dialog-box and controller for importing video-files
*/
std::string get_filename () { return path_entry.get_text(); }
std::string get_audiofile () { return audiofile; }
+ VtlTranscodeOption import_option ();
private:
void on_show ();
void abort_clicked ();
void scale_combo_changed ();
void audio_combo_changed ();
+ void video_combo_changed ();
void aspect_checkbox_toggled ();
void bitrate_checkbox_toggled ();
void update_bitrate ();
void launch_audioonly ();
void launch_transcode ();
void launch_extract ();
- void prepare_copy ();
- void launch_copy ();
void dialog_progress_mode ();
bool aborted;
bool pending_audio_extract;
- bool pending_copy_file;
std::string audiofile;
std::string infn;
double m_aspect;
Gtk::Entry path_entry;
Gtk::Button browse_button;
Gtk::Button transcode_button;
- Gtk::Button copy_button;
- Gtk::Button audio_button;
Gtk::VBox* vbox;
Gtk::Button *cancel_button;
Gtk::Label progress_label;
Gtk::ProgressBar pbar;
+ Gtk::ComboBoxText video_combo;
Gtk::ComboBoxText scale_combo;
Gtk::CheckButton aspect_checkbox;
Gtk::Adjustment height_adjustment;
#include "pbd/error.h"
#include "ardour/ardour.h"
#include "ardour/session_directory.h"
-#include "ardour_dialog.h"
#include "video_image_frame.h"
+#include "utils_videotl.h"
#include "i18n.h"
return dir;
}
+std::string
+video_get_docroot (ARDOUR::RCConfiguration* config)
+{
+ if (config->get_video_advanced_setup()) {
+ return config->get_video_server_docroot();
+ }
+ return X_("/");
+}
+
+std::string
+video_get_server_url (ARDOUR::RCConfiguration* config)
+{
+ if (config->get_video_advanced_setup()) {
+ return config->get_video_server_url();
+ }
+ return X_("http://localhost:1554");
+}
+
+
std::string
strip_file_extension (const std::string infile)
{
#include <string>
#include <gtkmm.h>
+#include "ardour/rc_configuration.h"
#include "ardour/types.h"
#include "ardour/template_utils.h"
#include "ardour_dialog.h"
void ParseCSV(const std::string &csv, std::vector<std::vector<std::string> > &lines);
std::string video_map_path (std::string server_docroot, std::string filepath);
void video_draw_cross (Glib::RefPtr<Gdk::Pixbuf> img);
+std::string video_get_server_url (ARDOUR::RCConfiguration* config);
+std::string video_get_docroot (ARDOUR::RCConfiguration* config);
bool video_query_info (
std::string video_server_url,
+++ /dev/null
-/*
- Copyright (C) 2010 Paul Davis
- Author: Robin Gareus <robin@gareus.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-#ifdef WITH_VIDEOTIMELINE
-
-#include <cstdio>
-#include <string>
-#include <sstream>
-#include <iomanip>
-
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-#include <sigc++/bind.h>
-#include <libgen.h>
-
-#include "pbd/error.h"
-#include "pbd/convert.h"
-#include "gtkmm2ext/utils.h"
-#include "ardour/session_directory.h"
-#include "ardour/profile.h"
-#include "ardour/template_utils.h"
-#include "ardour/session.h"
-#include "ardour_ui.h"
-#include "gui_thread.h"
-
-#include "utils_videotl.h"
-#include "utils.h"
-#include "opts.h"
-#include "video_copy_dialog.h"
-#include "i18n.h"
-
-using namespace Gtk;
-using namespace std;
-using namespace PBD;
-using namespace ARDOUR;
-
-VideoCopyDialog::VideoCopyDialog (Session* s, std::string infile)
- : ArdourDialog (_("Import Video File "))
- , infn (infile)
- , path_label (_("Output File:"), Gtk::ALIGN_LEFT)
- , browse_button (_("Browse"))
- , copy_button (_("Copy/Embed"))
- , abort_button (_("Abort"))
- , progress_label ()
-{
- set_session (s);
- autostart = false;
-
- set_name ("VideoCopyDialog");
- set_position (Gtk::WIN_POS_MOUSE);
- set_modal (true);
- set_skip_taskbar_hint (true);
- set_resizable (false);
- p_connection = sigc::connection();
-
- std::string dstdir = video_dest_dir(_session->session_directory().video_path(), Config->get_video_server_docroot());
- std::string dstfn = dstdir + G_DIR_SEPARATOR + Glib::path_get_basename(infile);
- path_entry.set_text (dstfn);
-
- path_hbox = manage (new HBox);
- path_hbox->pack_start (path_label, false, false, 3);
- path_hbox->pack_start (path_entry, true, true, 3);
- path_hbox->pack_start (browse_button, false, false, 3);
- browse_button.set_name ("PaddedButton");
- path_entry.set_width_chars(38);
-
- browse_button.signal_clicked().connect (sigc::mem_fun (*this, &VideoCopyDialog::open_browse_dialog));
- copy_button.signal_clicked().connect (sigc::mem_fun (*this, &VideoCopyDialog::launch_copy));
- abort_button.signal_clicked().connect (sigc::mem_fun (*this, &VideoCopyDialog::abort_clicked));
-
- progress_box = manage (new VBox);
- progress_box->pack_start (progress_label, false, false);
- progress_box->pack_start (pbar, false, false);
- progress_box->pack_start (abort_button, false, false);
-
- get_vbox()->pack_start (*path_hbox, false, false);
- get_vbox()->pack_start (*progress_box, false, false);
-
-
- cancel_button = add_button (Stock::CANCEL, RESPONSE_CANCEL);
- get_action_area()->pack_start (copy_button, false, false);
- show_all_children ();
- progress_box->hide();
-}
-
-VideoCopyDialog::~VideoCopyDialog ()
-{
-}
-
-void
-VideoCopyDialog::setup_non_interactive_copy (std::string destfn)
-{
- if (destfn.empty()) {
- std::string dstdir = video_dest_dir(_session->session_directory().video_path(), Config->get_video_server_docroot());
- outfn= dstdir + G_DIR_SEPARATOR + Glib::path_get_basename(infn);
- } else {
- outfn=destfn;
- }
- autostart=true;
-}
-
-void
-VideoCopyDialog::on_show ()
-{
- if (autostart) {
- Glib::signal_timeout().connect_once (sigc::mem_fun(*this, &VideoCopyDialog::launch_copy), 200);
- }
- Dialog::on_show ();
-}
-
-void
-VideoCopyDialog::abort_clicked ()
-{
- aborted = true;
-}
-
-gint
-VideoCopyDialog::progress_timeout ()
-{
- if (p_tot == 0) {
- pbar.set_pulse_step(.5);
- pbar.pulse();
- return 1;
- }
- pbar.set_fraction ((double)p_cur / (double) p_tot);
- return 1;
-}
-
-void*
-video_copy_thread (void *arg)
-{
- VideoCopyDialog *cvd = static_cast<VideoCopyDialog*>(arg);
- cvd->do_copy();
- return 0;
-}
-
-
-void
-VideoCopyDialog::launch_copy ()
-{
- if (!autostart) {
- outfn = path_entry.get_text();
- }
- if (!confirm_video_outfn(outfn)) { return; }
- p_cur = 0; p_tot = 0;
-
- p_connection = Glib::signal_timeout().connect (sigc::mem_fun(*this, &VideoCopyDialog::progress_timeout), 80);
-
- pbar.set_size_request(300,-1);
- progress_box->show();
- path_hbox->hide();
- cancel_button->hide();
- copy_button.hide();
- aborted = false;
- finished = false;
-
- pthread_create(&thread, NULL, video_copy_thread ,this);
- while (!finished) {
- if (gtk_events_pending()) {
- gtk_main_iteration ();
- } else {
- usleep (10000);
- }
- }
- pthread_join(thread, NULL);
-
- p_connection.disconnect();
-
- if (aborted) {
- Gtk::Dialog::response(RESPONSE_CANCEL);
- } else {
- Gtk::Dialog::response(RESPONSE_ACCEPT);
- }
-}
-
-void
-VideoCopyDialog::do_copy ()
-{
- progress_label.set_text (_("Linking File."));
-
- unlink (outfn.c_str());
-
- bool try_hardlink = false; // Config->get_try_link_for_embed(); /* XXX */
- struct stat sb;
- if (lstat (infn.c_str(), &sb) == 0) {
- p_tot = sb.st_size;
- /* don't hardlink a symlink */
- if ((sb.st_mode&S_IFMT) == S_IFLNK) {
- try_hardlink = false;
- if (stat (infn.c_str(), &sb) == 0) {
- p_tot = sb.st_size;
- }
- }
- } else {
- /* Can not stat() input file */
- warning << _("Can not read input file.") << endmsg;
- aborted=true;
- finished=true;
- return;
- }
-
- if ( !try_hardlink || link(infn.c_str(), outfn.c_str()) ) {
- /* hard-link failed , try copy */
- progress_label.set_text (_("Copying File."));
- int infd = open (infn.c_str(), O_RDONLY);
- int outfd = open (outfn.c_str(), O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
- if (infd <0 || outfd <0) {
- if (infd != -1) close(infd);
- warning << _("Can not open files for copy.") << endmsg;
- aborted=true;
- finished=true;
- return;
- }
- char buffer[BUFSIZ];
- ssize_t nrb, ret;
- while ((nrb = read(infd, buffer, BUFSIZ)) > 0 && nrb != -1 ) {
- ret = write(outfd, buffer, nrb);
- if(ret != nrb || aborted) {
- warning << _("File copy failed.") << endmsg;
- unlink(outfn.c_str());
- aborted=true;
- finished=true;
- return;
- }
- p_cur+=ret;
- }
- }
- finished=true;
- return;
-}
-
-void
-VideoCopyDialog::open_browse_dialog ()
-{
- Gtk::FileChooserDialog dialog(_("Video File Copy Destination"), Gtk::FILE_CHOOSER_ACTION_SAVE);
- dialog.set_filename (path_entry.get_text());
-
- dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
- dialog.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
-
- int result = dialog.run();
-
- if (result == Gtk::RESPONSE_OK) {
- std::string filename = dialog.get_filename();
-
- if (filename.length()) {
- path_entry.set_text (filename);
- }
- }
-}
-#endif /* WITH_VIDEOTIMELINE */
+++ /dev/null
-/*
- Copyright (C) 2010 Paul Davis
- Author: Robin Gareus <robin@gareus.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-#ifdef WITH_VIDEOTIMELINE
-
-#ifndef __gtk_ardour_video_copy_dialog_h__
-#define __gtk_ardour_video_copy_dialog_h__
-
-#include <string>
-
-#include <gtkmm.h>
-
-#include "ardour/types.h"
-#include "ardour/template_utils.h"
-#include "ardour_dialog.h"
-
-/** @class ExportVideoDialog
- * @brief dialog box and progress report for linking and copying video-files to the session.
- */
-class VideoCopyDialog : public ArdourDialog , public PBD::ScopedConnectionList
-{
- public:
- /** @param infile absolute-path to the file to copy or link */
- VideoCopyDialog (ARDOUR::Session*, std::string infile);
- ~VideoCopyDialog ();
- /** if set to true before calling dialog->show()
- * the dialog will only show the progres report and
- * start copying or linking immediatly
- * @param destfn destination path to copy or link the infile to.
- */
- void setup_non_interactive_copy(std::string destfn ="");
- std::string get_filename () { return outfn; }
-
- /*
- * Note: it's actually 'private' function but used
- * by the internal pthread, which only has a pointer
- * to this instance and thus can only access public fn.
- */
- void do_copy ();
-
- private:
- void on_show ();
- void abort_clicked ();
- bool aborted;
- bool autostart;
- bool finished;
- pthread_t thread;
-
- void launch_copy ();
- std::string infn;
- std::string outfn;
-
- gint progress_timeout ();
- sigc::connection p_connection;
- ssize_t p_cur;
- off_t p_tot;
-
- void open_browse_dialog ();
- Gtk::Label path_label;
- Gtk::Entry path_entry;
- Gtk::Button browse_button;
- Gtk::Button *cancel_button;
- Gtk::Button copy_button;
-
- Gtk::HBox* path_hbox;
- Gtk::VBox* progress_box;
- Gtk::Button abort_button;
- Gtk::Label progress_label;
- Gtk::ProgressBar pbar;
-};
-
-#endif /* __gtk_ardour_video_copy_dialog_h__ */
-
-#endif /* WITH_VIDEOTIMELINE */
vif->get_video_filename().c_str()
);
int status = 0;
- int timeout = 400; // * 5ms -> 2sec
+ int timeout = 1000; // * 5ms -> 5sec
char *res = NULL;
do {
res=curl_http_get(url, &status);
if (status == 503) usleep(5000); // try-again
} while (status == 503 && --timeout > 0);
+ if (status != 200 || !res) {
+ printf("no-video frame: video-server returned http-status: %d\n", status);
+ }
+
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
vif->http_download_done(res);
pthread_exit(0);
clock_connection = sigc::connection();
debug_enable = false;
- process = new SystemExec(xjadeo_bin_path);
+ process = new SystemExec(xjadeo_bin_path, X_("-R"));
process->ReadStdout.connect (*this, invalidator (*this), boost::bind (&VideoMonitor::parse_output, this, _1 ,_2), gui_context());
process->Terminated.connect (*this, invalidator (*this), boost::bind (&VideoMonitor::terminated, this), gui_context());
}
#include "ardour/session.h"
#include "video_server_dialog.h"
+#include "utils_videotl.h"
#include "i18n.h"
using namespace Gtk;
if (find_file_in_search_path (PBD::SearchPath(Glib::getenv("PATH")), X_("harvid"), icsd_file_path)) {
path_entry.set_text(icsd_file_path);
}
- else if (find_file_in_search_path (PBD::SearchPath(Glib::getenv("PATH")), X_("icsd"), icsd_file_path)) {
- path_entry.set_text(icsd_file_path);
- }
else if (Glib::file_test(X_("C:\\Program Files\\harvid\\harvid.exe"), Glib::FILE_TEST_EXISTS)) {
path_entry.set_text(X_("C:\\Program Files\\harvid\\harvid.exe"));
}
else {
- PBD::warning << _("The external video server 'harvid' can not be found, see https://github.com/x42/harvid") << endmsg;
+ PBD::warning <<
+ _("The external video server 'harvid' can not be found. The tool is included with the Ardour releases from ardour.org, "
+ "alternatively you can download it from http://x42.github.com/harvid/ or acquire it from your distribution.") << endmsg;
}
t->attach (*l, 0, 1, 2, 3, FILL);
t->attach (cachesize_spinner, 1, 2, 2, 3);
- l = manage (new Label (_("Ardour relies on an external Video Server for the videotimeline. The server configured in Edit -> Prefereces -> Video is not reachable. Do you want ardour to launch 'harvid' on this machine?"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
+ l = manage (new Label (_("Ardour relies on an external Video Server for the videotimeline.\nThe server configured in Edit -> Prefereces -> Video is not reachable.\nDo you want ardour to launch 'harvid' on this machine?"), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER, false));
+ l->set_max_width_chars(80);
l->set_line_wrap();
- vbox->pack_start (*l, false, true, 4);
+ vbox->pack_start (*l, true, true, 4);
vbox->pack_start (*path_hbox, false, false);
- vbox->pack_start (*docroot_hbox, false, false);
+ if (Config->get_video_advanced_setup()){
+ vbox->pack_start (*docroot_hbox, false, false);
+ } else {
+ docroot_entry.set_text(X_("/"));
+ listenport_spinner.set_sensitive(false);
+ }
vbox->pack_start (*options_box, false, true);
get_vbox()->set_spacing (4);
get_vbox()->pack_start (*vbox, false, false);
get_vbox()->pack_start (showagain_checkbox, false, false);
- showagain_checkbox.set_active(false);
+ showagain_checkbox.set_active(!Config->get_show_video_server_dialog());
path_browse_button.signal_clicked().connect (sigc::mem_fun (*this, &VideoServerDialog::open_path_dialog));
docroot_browse_button.signal_clicked().connect (sigc::mem_fun (*this, &VideoServerDialog::open_docroot_dialog));
if (result == Gtk::RESPONSE_OK) {
std::string dirname = dialog.get_filename();
+ if (dirname.empty() || dirname.at(dirname.length()-1) != G_DIR_SEPARATOR) {
+ dirname += "/";
+ }
+
if (dirname.length()) {
docroot_entry.set_text (dirname);
}
}
}
+std::string
+VideoServerDialog::get_docroot () {
+ return docroot_entry.get_text();
+}
+
#endif /* WITH_VIDEOTIMELINE */
~VideoServerDialog ();
std::string get_exec_path () { return path_entry.get_text(); }
- std::string get_docroot () { return docroot_entry.get_text(); }
+ std::string get_docroot ();
std::string get_listenaddr () { return listenaddr_combo.get_active_text();}
int get_listenport () { return listenport_spinner.get_value_as_int();}
int get_cachesize () { return cachesize_spinner.get_value_as_int();}
*/
#ifdef WITH_VIDEOTIMELINE
+#include <algorithm>
#include <sigc++/bind.h>
#include "ardour/tempo.h"
using namespace PBD;
using namespace Timecode;
-
VideoTimeLine::VideoTimeLine (PublicEditor *ed, ArdourCanvas::Group *vbg, int initial_height)
: editor (ed)
, videotl_bar_group(vbg)
video_aspect_ratio = 4.0/3.0;
open_video_monitor_dialog = 0;
Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&VideoTimeLine::parameter_changed, this, _1), gui_context());
- video_server_url = Config->get_video_server_url();
- server_docroot = Config->get_video_server_docroot();
+ video_server_url = video_get_server_url(Config);
+ server_docroot = video_get_docroot(Config);
video_filename = "";
local_file = true;
video_file_fps = 25.0;
void
VideoTimeLine::close_session ()
{
+ if (video_duration == 0) {
+ return;
+ }
close_video_monitor();
save_session();
remove_frames();
video_filename = "";
- video_duration = 0L;
+ video_duration = 0;
+ GuiUpdate("set-xjadeo-sensitive-off");
}
/** load settings from session */
LocaleGuard lg (X_("POSIX"));
XMLNode* node = _session->extra_xml (X_("Videotimeline"));
+
+ if (!node || !node->property (X_("Filename"))) {
+ return;
+ }
+
if (node) {
ARDOUR_UI::instance()->start_video_server((Gtk::Window*)0, false);
node = _session->extra_xml (X_("Videomonitor"));
if (node) {
const XMLProperty* prop = node->property (X_("active"));
- if (prop->value() == "yes" && found_xjadeo() && !video_filename.empty() && local_file) {
+ if (prop && prop->value() == "yes" && found_xjadeo() && !video_filename.empty() && local_file) {
open_video_monitor(false);
}
}
bool
VideoTimeLine::video_file_info (std::string filename, bool local)
{
+
local_file = local;
if (filename.at(0) == G_DIR_SEPARATOR || !local_file) {
video_filename = filename;
}
flush_local_cache ();
+ _session->maybe_update_session_range(
+ std::max(get_offset(), (ARDOUR::frameoffset_t) 0),
+ std::max(get_offset() + get_duration(), (ARDOUR::frameoffset_t) 0)
+ );
+
if (found_xjadeo() && local_file) {
GuiUpdate("set-xjadeo-sensitive-on");
if (vmonitor && vmonitor->is_started()) {
+#if 1
+ /* xjadeo <= 0.6.4 has a bug where changing the video-file may segfauls
+ * if the geometry changes to a different line-size alignment
+ */
+ reopen_vmonitor = true;
+ vmonitor->quit();
+#else
vmonitor->set_fps(video_file_fps);
vmonitor->open(video_filename);
+#endif
}
} else if (!local_file) {
#if 1 /* temp debug/devel message */
VideoTimeLine::parameter_changed (std::string const & p)
{
if (p == "video-server-url") {
- set_video_server_url (Config->get_video_server_url ());
+ set_video_server_url (video_get_server_url(Config));
} else if (p == "video-server-docroot") {
- set_video_server_docroot (Config->get_video_server_docroot ());
+ set_video_server_docroot (video_get_docroot(Config));
+ } else if (p == "video-advanced-setup") {
+ set_video_server_url (video_get_server_url(Config));
+ set_video_server_docroot (video_get_docroot(Config));
}
if (p == "use-video-file-fps" || p == "videotimeline-pullup" ) { /* session->config parameter */
VtlUpdate();
'transcode_ffmpeg.cc',
'transcode_video_dialog.cc',
'video_server_dialog.cc',
- 'video_copy_dialog.cc',
'utils_videotl.cc',
'export_video_dialog.cc',
'export_video_infobox.cc'
_written = true;
}
+ /** Read @a len frames @a src starting at @a src_offset into self starting at @ dst_offset*/
+ void read_from (const Sample* src, framecnt_t len, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) {
+ assert(src != 0);
+ assert(_capacity > 0);
+ assert(len <= _capacity);
+ memcpy(_data + dst_offset, src + src_offset, sizeof(Sample) * len);
+ _silent = false;
+ _written = true;
+ }
+
+ void read_from_with_gain (const Sample* src, framecnt_t len, gain_t gain, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) {
+ assert(src != 0);
+ assert(_capacity > 0);
+ assert(len <= _capacity);
+ src += src_offset;
+ for (framecnt_t n = 0; n < len; ++n) {
+ _data[dst_offset+n] = src[n] * gain;
+ }
+ _silent = false;
+ _written = true;
+ }
+
/** Read @a len frames @a src starting at @a src_offset into self starting at @ dst_offset*/
void read_from (const Buffer& src, framecnt_t len, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) {
assert(&src != this);
_written = true;
}
+ /** Acumulate (add) @a len frames @a src starting at @a src_offset into self starting at @a dst_offset */
+ void accumulate_from (const Sample* src, framecnt_t len, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) {
+ assert(_capacity > 0);
+ assert(len <= _capacity);
+
+ Sample* const dst_raw = _data + dst_offset;
+ const Sample* const src_raw = src + src_offset;
+
+ mix_buffers_no_gain(dst_raw, src_raw, len);
+
+ _silent = false;
+ _written = true;
+ }
+
/** Acumulate (add) @a len frames @a src starting at @a src_offset into self starting at @dst_offset
* scaling by @a gain_coeff */
void accumulate_with_gain_from (const AudioBuffer& src, framecnt_t len, gain_t gain_coeff, framecnt_t dst_offset = 0, framecnt_t src_offset = 0) {
return _data + offset;
}
+ bool check_silence (pframes_t, pframes_t&) const;
+
void prepare () { _written = false; _silent = false; }
bool written() const { return _written; }
void set_written(bool w) { _written = w; }
protected:
friend class AudioTrack;
- int process (framepos_t transport_frame, pframes_t nframes, framecnt_t &);
+ int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_disk_signal);
bool commit (framecnt_t);
private:
class Session;
class Track;
class Location;
+class BufferSet;
/** Parent class for classes which can stream data to and from disk.
* These are used by Tracks to get playback and put recorded data.
protected:
friend class Track;
- virtual int process (framepos_t transport_frame, pframes_t nframes, framecnt_t &) = 0;
+ virtual int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_disk_signal) = 0;
virtual bool commit (framecnt_t) = 0;
//private:
*/
class PeakMeter : public Processor {
public:
- PeakMeter(Session& s) : Processor(s, "Meter") {}
+ PeakMeter(Session& s, const std::string& name);
void meter();
void reset ();
template<typename BufferType, typename EventType>
class iterator_base {
public:
- iterator_base<BufferType, EventType>(BufferType& b, framecnt_t o) : buffer(b), offset(o) {}
+ iterator_base<BufferType, EventType>(BufferType& b, framecnt_t o)
+ : buffer(&b), offset(o) {}
+ iterator_base<BufferType, EventType>(const iterator_base<BufferType,EventType>& o)
+ : buffer (o.buffer), offset(o.offset) {}
+
+ inline iterator_base<BufferType,EventType> operator= (const iterator_base<BufferType,EventType>& o) {
+ if (&o != this) {
+ buffer = o.buffer;
+ offset = o.offset;
+ }
+ return *this;
+ }
+
inline EventType operator*() const {
- uint8_t* ev_start = buffer._data + offset + sizeof(TimeType);
+ uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType);
int event_size = Evoral::midi_event_size(ev_start);
assert(event_size >= 0);
return EventType(EventTypeMap::instance().midi_event_type(*ev_start),
- *((TimeType*)(buffer._data + offset)),
+ *((TimeType*)(buffer->_data + offset)),
event_size, ev_start);
}
inline EventType operator*() {
- uint8_t* ev_start = buffer._data + offset + sizeof(TimeType);
+ uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType);
int event_size = Evoral::midi_event_size(ev_start);
assert(event_size >= 0);
return EventType(EventTypeMap::instance().midi_event_type(*ev_start),
- *((TimeType*)(buffer._data + offset)),
+ *((TimeType*)(buffer->_data + offset)),
event_size, ev_start);
}
inline iterator_base<BufferType, EventType>& operator++() {
- uint8_t* ev_start = buffer._data + offset + sizeof(TimeType);
+ uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType);
int event_size = Evoral::midi_event_size(ev_start);
assert(event_size >= 0);
offset += sizeof(TimeType) + event_size;
return *this;
}
inline bool operator!=(const iterator_base<BufferType, EventType>& other) const {
- return (&buffer != &other.buffer) || (offset != other.offset);
+ return (buffer != other.buffer) || (offset != other.offset);
}
inline bool operator==(const iterator_base<BufferType, EventType>& other) const {
- return (&buffer == &other.buffer) && (offset == other.offset);
+ return (buffer == other.buffer) && (offset == other.offset);
}
- BufferType& buffer;
+ BufferType* buffer;
size_t offset;
};
const_iterator begin() const { return const_iterator(*this, 0); }
const_iterator end() const { return const_iterator(*this, _size); }
+ iterator erase(const iterator& i) {
+ assert (i.buffer == this);
+ uint8_t* ev_start = _data + i.offset + sizeof (TimeType);
+ int event_size = Evoral::midi_event_size (ev_start);
+
+ if (event_size < 0) {
+ /* unknown size, sysex: return end() */
+ return end();
+ }
+
+ size_t total_data_deleted = sizeof(TimeType) + event_size;
+
+ if (i.offset + total_data_deleted >= _size) {
+ _size = 0;
+ return end();
+ }
+
+ /* we need to avoid the temporary malloc that memmove would do,
+ so copy by hand. remember: this is small amounts of data ...
+ */
+ size_t a, b;
+ for (a = i.offset, b = i.offset + total_data_deleted; b < _size; ++b, ++a) {
+ _data[a] = _data[b];
+ }
+
+ _size -= total_data_deleted;
+
+ /* all subsequent iterators are now invalid, and the one we
+ * return should refer to the event we copied, which was after
+ * the one we just erased.
+ */
+
+ return iterator (*this, i.offset);
+ }
+
uint8_t* data() const { return _data; }
/**
void set_note_mode (NoteMode m);
- uint16_t get_channel_mask() {
- uint16_t playback_mask = _playback_buf->get_channel_mask();
-#ifndef NDEBUG
- uint16_t capture_mask = _capture_buf->get_channel_mask();
- assert(playback_mask == capture_mask);
-#endif
- return playback_mask;
- }
-
- void set_channel_mode(ChannelMode mode, uint16_t mask) {
- _playback_buf->set_channel_mode(mode, mask);
- _capture_buf->set_channel_mode(mode, mask);
- }
-
- ChannelMode get_channel_mode() {
- ChannelMode playback_mode = _playback_buf->get_channel_mode();
-#ifndef NDEBUG
- ChannelMode capture_mode = _capture_buf->get_channel_mode();
- assert(playback_mode == capture_mode);
-#endif
- return playback_mode;
- }
-
/** Emitted when some MIDI data has been received for recording.
* Parameter is the source that it is destined for.
* A caller can get a copy of the data with get_gui_feed_buffer ()
protected:
friend class MidiTrack;
- int process (framepos_t transport_frame, pframes_t nframes, framecnt_t &);
+ int process (BufferSet&, framepos_t transport_frame, pframes_t nframes, framecnt_t &, bool need_diskstream);
bool commit (framecnt_t nframes);
static framecnt_t midi_readahead;
struct NoteChange {
NoteDiffCommand::Property property;
NotePtr note;
+ uint32_t note_id;
+
union {
uint8_t old_value;
TimeType old_time;
private:
struct Change {
boost::shared_ptr<Evoral::Event<TimeType> > sysex;
+ gint sysex_id;
SysExDiffCommand::Property property;
TimeType old_time;
TimeType new_time;
struct Change {
PatchChangePtr patch;
Property property;
+ gint patch_id;
union {
TimeType old_time;
uint8_t old_channel;
/** @param size Size in bytes.
*/
MidiRingBuffer(size_t size)
- : Evoral::EventRingBuffer<T>(size)
- , _channel_mask(0x0000FFFF)
- {}
+ : Evoral::EventRingBuffer<T>(size) {}
inline bool read_prefix(T* time, Evoral::EventType* type, uint32_t* size);
inline bool read_contents(uint32_t size, uint8_t* buf);
size_t read(MidiBuffer& dst, framepos_t start, framepos_t end, framecnt_t offset=0, bool stop_on_overflow_in_destination=false);
- inline uint32_t write(T time, Evoral::EventType type, uint32_t size, const uint8_t* buf);
void dump(std::ostream& dst);
void flush (framepos_t start, framepos_t end);
- /** Set the channel filtering mode.
- * @param mask If mode is FilterChannels, each bit represents a midi channel:
- * bit 0 = channel 0, bit 1 = channel 1 etc. the read and write methods will only
- * process events whose channel bit is 1.
- * If mode is ForceChannel, mask is simply a channel number which all events will
- * be forced to while reading.
- */
- void set_channel_mode(ChannelMode mode, uint16_t mask) {
- g_atomic_int_set(&_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask));
- }
-
- ChannelMode get_channel_mode() const {
- return static_cast<ChannelMode>((g_atomic_int_get(&_channel_mask) & 0xFFFF0000) >> 16);
- }
-
- uint16_t get_channel_mask() const {
- return g_atomic_int_get(&_channel_mask) & 0x0000FFFF;
- }
-
void reset_tracker ();
void loop_resolve (MidiBuffer& dst, framepos_t);
}
private:
- volatile uint32_t _channel_mask; // 16 bits mode, 16 bits mask
MidiStateTracker _tracker;
};
return PBD::RingBufferNPT<uint8_t>::read(buf, size) == size;
}
-template<typename T>
-inline uint32_t
-MidiRingBuffer<T>::write(T time, Evoral::EventType type, uint32_t size, const uint8_t* buf)
-{
- assert(size > 0);
- uint8_t status = buf[0];
-
- // Ignore event if it doesn't match channel filter
- if (is_channel_event(status)) {
- ChannelMode mode = get_channel_mode();
- if (mode == FilterChannels) {
- const uint8_t channel = status & 0x0F;
- if (!(get_channel_mask() & (1L << channel))) {
- return 0;
- }
- } else if (mode == ForceChannel) {
- uint8_t* tmpbuf = (uint8_t*) malloc(size);
- assert(tmpbuf);
- memcpy(tmpbuf, buf, size);
-
- tmpbuf[0] = (tmpbuf[0] & 0xF0) | (get_channel_mask() & 0x0F);
-
- uint32_t bytes_written = Evoral::EventRingBuffer<T>::write(time, type, size, tmpbuf);
- free(tmpbuf);
- return bytes_written;
- }
- }
-
- return Evoral::EventRingBuffer<T>::write(time, type, size, buf);
-}
-
-
} // namespace ARDOUR
#endif // __ardour_midi_ring_buffer_h__
PBD::Signal1<void,bool> StepEditStatusChange;
boost::shared_ptr<SMFSource> write_source (uint32_t n = 0);
- void set_channel_mode (ChannelMode, uint16_t);
- ChannelMode get_channel_mode ();
- uint16_t get_channel_mask ();
+
+ /** Channel filtering mode.
+ * @param mask If mode is FilterChannels, each bit represents a midi channel:
+ * bit 0 = channel 0, bit 1 = channel 1 etc. the read and write methods will only
+ * process events whose channel bit is 1.
+ * If mode is ForceChannel, mask is simply a channel number which all events will
+ * be forced to while reading.
+ */
+ void set_capture_channel_mode (ChannelMode mode, uint16_t mask);
+ void set_playback_channel_mode (ChannelMode mode, uint16_t mask);
+ void set_playback_channel_mask (uint16_t mask);
+ void set_capture_channel_mask (uint16_t mask);
+
+ ChannelMode get_playback_channel_mode() const {
+ return static_cast<ChannelMode>((g_atomic_int_get(&_playback_channel_mask) & 0xffff0000) >> 16);
+ }
+ uint16_t get_playback_channel_mask() const {
+ return g_atomic_int_get(&_playback_channel_mask) & 0x0000ffff;
+ }
+ ChannelMode get_capture_channel_mode() const {
+ return static_cast<ChannelMode>((g_atomic_int_get(&_capture_channel_mask) & 0xffff0000) >> 16);
+ }
+ uint16_t get_capture_channel_mask() const {
+ return g_atomic_int_get(&_capture_channel_mask) & 0x0000ffff;
+ }
+
boost::shared_ptr<MidiPlaylist> midi_playlist ();
+ PBD::Signal0<void> PlaybackChannelMaskChanged;
+ PBD::Signal0<void> PlaybackChannelModeChanged;
+ PBD::Signal0<void> CaptureChannelMaskChanged;
+ PBD::Signal0<void> CaptureChannelModeChanged;
+
PBD::Signal1<void, boost::weak_ptr<MidiSource> > DataRecorded;
boost::shared_ptr<MidiBuffer> get_gui_feed_buffer () const;
void act_on_mute ();
private:
+ MidiRingBuffer<framepos_t> _immediate_events;
+ MidiRingBuffer<framepos_t> _step_edit_ring_buffer;
+ NoteMode _note_mode;
+ bool _step_editing;
+ bool _input_active;
+ uint32_t _playback_channel_mask; // 16 bits mode, 16 bits mask
+ uint32_t _capture_channel_mask; // 16 bits mode, 16 bits mask
virtual boost::shared_ptr<Diskstream> diskstream_factory (XMLNode const &);
void set_state_part_two ();
void set_state_part_three ();
- MidiRingBuffer<framepos_t> _immediate_events;
- MidiRingBuffer<framepos_t> _step_edit_ring_buffer;
- NoteMode _note_mode;
- bool _step_editing;
- bool _input_active;
int no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool state_changing);
void push_midi_input_to_step_edit_ringbuffer (framecnt_t nframes);
void track_input_active (IOChange, void*);
void map_input_active (bool);
+
+ void filter_channels (BufferSet& bufs, ChannelMode mode, uint32_t mask);
+
+/* if mode is ForceChannel, force mask to the lowest set channel or 1 if no
+ * channels are set.
+ */
+#define force_mask(mode,mask) (((mode) == ForceChannel) ? (((mask) ? (1<<(ffs((mask))-1)) : 1)) : mask)
+
+ void _set_playback_channel_mode(ChannelMode mode, uint16_t mask) {
+ mask = force_mask (mode, mask);
+ g_atomic_int_set(&_playback_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask));
+ }
+ void _set_playback_channel_mask (uint16_t mask) {
+ mask = force_mask (get_playback_channel_mode(), mask);
+ g_atomic_int_set(&_playback_channel_mask, (uint32_t(get_playback_channel_mode()) << 16) | uint32_t(mask));
+ }
+ void _set_capture_channel_mode(ChannelMode mode, uint16_t mask) {
+ mask = force_mask (mode, mask);
+ g_atomic_int_set(&_capture_channel_mask, (uint32_t(mode) << 16) | uint32_t(mask));
+ }
+ void _set_capture_channel_mask (uint16_t mask) {
+ mask = force_mask (get_capture_channel_mode(), mask);
+ g_atomic_int_set(&_capture_channel_mask, (uint32_t(get_capture_channel_mode()) << 16) | uint32_t(mask));
+ }
+
+#undef force_mask
};
} /* namespace ARDOUR*/
CONFIG_VARIABLE (std::string, donate_url, "donate-url", "http://ardour.org/donate")
#ifdef WITH_VIDEOTIMELINE
+CONFIG_VARIABLE (bool, video_advanced_setup, "video-advanced-setup", false)
CONFIG_VARIABLE (std::string, video_server_url, "video-server-url", "http://localhost:1554")
CONFIG_VARIABLE (std::string, video_server_docroot, "video-server-docroot", "/")
CONFIG_VARIABLE (bool, video_monitor_setup_dialog, "video-monitor-setup-dialog", false)
CONFIG_VARIABLE (bool, show_video_export_info, "show-video-export-info", true)
-CONFIG_VARIABLE (bool, show_video_server_dialog, "show-video-server-dialog", true)
+CONFIG_VARIABLE (bool, show_video_server_dialog, "show-video-server-dialog", false)
#endif
bool has_external_redirects() const;
+ /* can only be executed by a route for which is_monitor() is true
+ (i.e. the monitor out)
+ */
+ void monitor_run (framepos_t start_frame, framepos_t end_frame,
+ pframes_t nframes, int declick);
+
protected:
friend class Session;
protected:
virtual framecnt_t check_initial_delay (framecnt_t nframes, framepos_t&) { return nframes; }
- void passthru (framepos_t start_frame, framepos_t end_frame,
+ void fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pframes_t nframes);
+
+ void passthru (BufferSet&, framepos_t start_frame, framepos_t end_frame,
pframes_t nframes, int declick);
virtual void write_out_of_band_data (BufferSet& /* bufs */, framepos_t /* start_frame */, framepos_t /* end_frame */,
- framecnt_t /* nframes */) {}
+ framecnt_t /* nframes */) {}
virtual void process_output_buffers (BufferSet& bufs,
framepos_t start_frame, framepos_t end_frame,
void request_play_range (std::list<AudioRange>*, bool leave_rolling = false);
bool get_play_range () const { return _play_range; }
+ void maybe_update_session_range (framepos_t, framepos_t);
+
/* buffers for gain and pan */
gain_t* gain_automation_buffer () const;
Slave* _slave;
bool _silent;
- void maybe_update_session_range (framepos_t, framepos_t);
-
// varispeed playback
double _transport_speed;
double _default_transport_speed;
class Source;
class Region;
class Diskstream;
+class IO;
class Track : public Route, public PublicDiskstream
{
cache_aligned_malloc ((void**) &_data, sizeof (Sample) * _capacity);
}
-
+bool
+AudioBuffer::check_silence (pframes_t nframes, pframes_t& n) const
+{
+ for (n = 0; n < _size && n < nframes; ++n) {
+ if (_data[n] != Sample (0)) {
+ return false;
+ }
+ }
+ return true;
+}
* that someone can read playback_distance worth of data from.
*/
int
-AudioDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance)
+AudioDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal)
{
uint32_t n;
boost::shared_ptr<ChannelList> c = channels.reader();
assert(ap);
assert(rec_nframes <= (framecnt_t) ap->get_audio_buffer(nframes).capacity());
- Sample *buf = ap->get_audio_buffer (nframes).data (rec_offset);
+ Sample *buf = bufs.get_audio (n).data(rec_offset);
memcpy (chaninfo->current_capture_buffer, buf, sizeof (Sample) * rec_nframes);
-
+
} else {
framecnt_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1];
boost::shared_ptr<AudioPort> const ap = _io->audio (n);
assert(ap);
- Sample* buf = ap->get_audio_buffer(nframes).data (rec_offset);
+ Sample *buf = bufs.get_audio (n).data(rec_offset);
framecnt_t first = chaninfo->capture_vector.len[0];
memcpy (chaninfo->capture_wrap_buffer, buf, sizeof (Sample) * first);
_speed = _target_speed;
}
+ if (need_disk_signal) {
+
+ /* copy data over to buffer set */
+
+ size_t n_buffers = bufs.count().n_audio();
+ size_t n_chans = c->size();
+ gain_t scaling = 1.0f;
+
+ if (n_chans > n_buffers) {
+ scaling = ((float) n_buffers)/n_chans;
+ }
+
+ for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+
+ AudioBuffer& buf (bufs.get_audio (n%n_buffers));
+ ChannelInfo* chaninfo (*chan);
+
+ if (n < n_chans) {
+ if (scaling != 1.0f) {
+ buf.read_from_with_gain (chaninfo->current_playback_buffer, nframes, scaling);
+ } else {
+ buf.read_from (chaninfo->current_playback_buffer, nframes);
+ }
+ } else {
+ if (scaling != 1.0f) {
+ buf.accumulate_with_gain_from (chaninfo->current_playback_buffer, nframes, scaling);
+ } else {
+ buf.accumulate_from (chaninfo->current_playback_buffer, nframes);
+ }
+ }
+ }
+
+ /* leave the MIDI count alone */
+ ChanCount cnt (DataType::AUDIO, n_chans);
+ cnt.set (DataType::MIDI, bufs.count().n_midi());
+ bufs.set_count (cnt);
+
+ /* extra buffers will already be silent, so leave them alone */
+ }
+
return 0;
}
return 0;
}
- Sample* b;
- Sample* tmpb;
framepos_t transport_frame;
boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream();
to do nothing.
*/
- dret = diskstream->process (transport_frame, 0, playback_distance);
+ BufferSet bufs; /* empty set, no matter - nothing will happen */
+
+ dret = diskstream->process (bufs, transport_frame, 0, playback_distance, false);
need_butler = diskstream->commit (playback_distance);
return dret;
}
_silent = false;
_amp->apply_gain_automation(false);
- if ((dret = diskstream->process (transport_frame, nframes, playback_distance)) != 0) {
- need_butler = diskstream->commit (playback_distance);
- silence (nframes);
- return dret;
- }
-
- /* special condition applies */
+ BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers ());
+ fill_buffers_with_input (bufs, _input, nframes);
+
if (_meter_point == MeterInput) {
- _input->process_input (_meter, start_frame, end_frame, nframes);
+ _meter->run (bufs, start_frame, end_frame, nframes, true);
}
- if (monitoring_state() == MonitoringInput) {
-
- passthru (start_frame, end_frame, nframes, false);
-
- } else if ((b = diskstream->playback_buffer(0)) != 0) {
-
- /*
- XXX is it true that the earlier test on n_outputs()
- means that we can avoid checking it again here? i think
- so, because changing the i/o configuration of an IO
- requires holding the AudioEngine lock, which we hold
- while in the process() tree.
- */
-
-
- /* copy the diskstream data to all output buffers */
-
- size_t limit = input_streams ().n_audio();
- BufferSet& bufs = _session.get_scratch_buffers ();
- const size_t blimit = bufs.count().n_audio();
-
- uint32_t n;
- uint32_t i;
-
- if (limit > blimit) {
-
- /* example case: auditioner configured for stereo output,
- but loaded with an 8 channel file. there are only
- 2 passthrough buffers, but n_process_buffers() will
- return 8.
-
- arbitrary decision: map all channels in the diskstream
- to the outputs available.
- */
-
- float scaling = limit/blimit;
-
- for (i = 0, n = 1; i < blimit; ++i, ++n) {
-
- /* first time through just copy a channel into
- the output buffer.
- */
-
- Sample* bb = bufs.get_audio (i).data();
-
- for (pframes_t xx = 0; xx < nframes; ++xx) {
- bb[xx] = b[xx] * scaling;
- }
-
- if (n < diskstream->n_channels().n_audio()) {
- tmpb = diskstream->playback_buffer(n);
- if (tmpb!=0) {
- b = tmpb;
- }
- }
- }
-
- for (;i < limit; ++i, ++n) {
-
- /* for all remaining channels, sum with existing
- data in the output buffers
- */
-
- bufs.get_audio (i%blimit).accumulate_with_gain_from (b, nframes, 0, scaling);
-
- if (n < diskstream->n_channels().n_audio()) {
- tmpb = diskstream->playback_buffer(n);
- if (tmpb!=0) {
- b = tmpb;
- }
- }
-
- }
-
- limit = blimit;
-
- } else {
- for (i = 0, n = 1; i < limit; ++i, ++n) {
- memcpy (bufs.get_audio (i).data(), b, sizeof (Sample) * nframes);
- if (n < diskstream->n_channels().n_audio()) {
- tmpb = diskstream->playback_buffer(n);
- if (tmpb!=0) {
- b = tmpb;
- }
- }
- }
-
- /* try to leave any MIDI buffers alone */
-
- ChanCount chn;
- chn.set_audio (limit);
- chn.set_midi (_input->n_ports().n_midi());
- bufs.set_count (chn);
- }
-
- /* final argument: don't waste time with automation if we're recording or we've just stopped (yes it can happen) */
-
- process_output_buffers (
- bufs, start_frame, end_frame, nframes,
- declick,
- (!diskstream->record_enabled() && _session.transport_rolling())
- );
-
- } else {
- /* problem with the diskstream; just be quiet for a bit */
+ if ((dret = diskstream->process (bufs, transport_frame, nframes, playback_distance, (monitoring_state() == MonitoringDisk))) != 0) {
+ need_butler = diskstream->commit (playback_distance);
silence (nframes);
+ return dret;
}
+ process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!diskstream->record_enabled() && _session.transport_rolling()));
+
need_butler = diskstream->commit (playback_distance);
return 0;
lilv_node_free(lv2_appliesTo);
}
-bool
-LV2Plugin::load_preset(PresetRecord r)
+static void
+set_port_value(const char* port_symbol,
+ void* user_data,
+ const void* value,
+ uint32_t /*size*/,
+ uint32_t type)
{
- std::map<std::string,uint32_t>::iterator it;
-
- LilvNode* lv2_port = lilv_new_uri(_world.world, LILV_NS_LV2 "port");
- LilvNode* lv2_symbol = lilv_new_uri(_world.world, LILV_NS_LV2 "symbol");
- LilvNode* preset = lilv_new_uri(_world.world, r.uri.c_str());
- LilvNode* pset_value = lilv_new_uri(_world.world, LV2_PRESETS__value);
-
- LilvNodes* ports = lilv_world_find_nodes(_world.world, preset, lv2_port, NULL);
- LILV_FOREACH(nodes, i, ports) {
- const LilvNode* port = lilv_nodes_get(ports, i);
- const LilvNode* symbol = get_value(_world.world, port, lv2_symbol);
- const LilvNode* value = get_value(_world.world, port, pset_value);
- if (value && lilv_node_is_float(value)) {
- it = _port_indices.find(lilv_node_as_string(symbol));
- if (it != _port_indices.end()) {
- set_parameter(it->second,lilv_node_as_float(value));
- }
- }
+ LV2Plugin* self = (LV2Plugin*)user_data;
+ if (type != 0 && type != self->_uri_map.uri_to_id(LV2_ATOM__Float)) {
+ return; // TODO: Support non-float ports
}
- lilv_nodes_free(ports);
- lilv_node_free(pset_value);
- lilv_node_free(preset);
- lilv_node_free(lv2_symbol);
- lilv_node_free(lv2_port);
+ const uint32_t port_index = self->port_index(port_symbol);
+ if (port_index != (uint32_t)-1) {
+ self->set_parameter(port_index, *(const float*)value);
+ }
+}
- Plugin::load_preset(r);
+bool
+LV2Plugin::load_preset(PresetRecord r)
+{
+ LilvWorld* world = _world.world;
+ LilvNode* pset = lilv_new_uri(world, r.uri.c_str());
+ LilvState* state = lilv_state_new_from_world(world, _uri_map.urid_map(), pset);
- return true;
+ if (state) {
+ lilv_state_restore(state, _impl->instance, set_port_value, this, 0, NULL);
+ lilv_state_free(state);
+ }
+
+ lilv_node_free(pset);
+ return state;
}
const void*
#include <algorithm>
#include <cmath>
+#include "pbd/compose.h"
+
#include "ardour/audio_buffer.h"
#include "ardour/buffer_set.h"
#include "ardour/dB.h"
PBD::Signal0<void> Metering::Meter;
+PeakMeter::PeakMeter (Session& s, const std::string& name)
+ : Processor (s, string_compose ("meter-%1", name))
+{
+}
+
+
/** Get peaks from @a bufs
* Input acceptance is lenient - the first n buffers from @a bufs will
* be metered, where n was set by the last call to setup(), excess meters will
// Meter MIDI in to the first n_midi peaks
for (uint32_t i = 0; i < n_midi; ++i, ++n) {
float val = 0.0f;
- for (MidiBuffer::iterator e = bufs.get_midi(i).begin(); e != bufs.get_midi(i).end(); ++e) {
+ MidiBuffer& buf (bufs.get_midi(i));
+
+ for (MidiBuffer::iterator e = buf.begin(); e != buf.end(); ++e) {
const Evoral::MIDIEvent<framepos_t> ev(*e, false);
if (ev.is_note_on()) {
const float this_vel = log(ev.buffer()[2] / 127.0 * (M_E*M_E-M_E) + M_E) - 1.0;
#include <fcntl.h>
#include <cstdlib>
#include <ctime>
+#include <strings.h> // for ffs(3)
#include <sys/stat.h>
#include <sys/mman.h>
}
int
-MidiDiskstream::process (framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance)
+MidiDiskstream::process (BufferSet& bufs, framepos_t transport_frame, pframes_t nframes, framecnt_t& playback_distance, bool need_disk_signal)
{
framecnt_t rec_offset = 0;
framecnt_t rec_nframes = 0;
// Pump entire port buffer into the ring buffer (FIXME: split cycles?)
MidiBuffer& buf = sp->get_midi_buffer(nframes);
+ ChannelMode mode = AllChannels; // _track->get_capture_channel_mode ();
+ uint32_t mask = 0xffff; // _track->get_capture_channel_mask ();
+
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
- const Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false);
+ Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false);
#ifndef NDEBUG
if (DEBUG::MidiIO & PBD::debug_bits) {
const uint8_t* __data = ev.buffer();
probabl be implemented in the source instead of here.
*/
const framecnt_t loop_offset = _num_captured_loops * loop_length;
- _capture_buf->write(transport_frame + loop_offset + ev.time(),
- ev.type(), ev.size(), ev.buffer());
+
+ switch (mode) {
+ case AllChannels:
+ _capture_buf->write(transport_frame + loop_offset + ev.time(),
+ ev.type(), ev.size(), ev.buffer());
+ break;
+ case FilterChannels:
+ if (ev.is_channel_event()) {
+ if ((1<<ev.channel()) & mask) {
+ _capture_buf->write(transport_frame + loop_offset + ev.time(),
+ ev.type(), ev.size(), ev.buffer());
+ }
+ } else {
+ _capture_buf->write(transport_frame + loop_offset + ev.time(),
+ ev.type(), ev.size(), ev.buffer());
+ }
+ break;
+ case ForceChannel:
+ if (ev.is_channel_event()) {
+ ev.set_channel (ffs(mask) - 1);
+ }
+ _capture_buf->write(transport_frame + loop_offset + ev.time(),
+ ev.type(), ev.size(), ev.buffer());
+ break;
+ }
}
g_atomic_int_add(&_frames_pending_write, nframes);
}
+ if (need_disk_signal) {
+ /* copy the diskstream data to all output buffers */
+
+ MidiBuffer& mbuf (bufs.get_midi (0));
+ get_playback (mbuf, nframes);
+
+ /* leave the audio count alone */
+ ChanCount cnt (DataType::MIDI, 1);
+ cnt.set (DataType::AUDIO, bufs.count().n_audio());
+ bufs.set_count (cnt);
+ }
+
return 0;
}
char buf[64];
LocaleGuard lg (X_("POSIX"));
- node.add_property("channel-mode", enum_2_string(get_channel_mode()));
- snprintf (buf, sizeof(buf), "0x%x", get_channel_mask());
- node.add_property("channel-mask", buf);
-
if (_write_source && _session.get_record_enabled()) {
XMLNode* cs_child = new XMLNode (X_("CapturingSources"));
int
MidiDiskstream::set_state (const XMLNode& node, int version)
{
- const XMLProperty* prop;
XMLNodeList nlist = node.children();
XMLNodeIterator niter;
XMLNode* capture_pending_node = 0;
return -1;
}
- ChannelMode channel_mode = AllChannels;
- if ((prop = node.property ("channel-mode")) != 0) {
- channel_mode = ChannelMode (string_2_enum(prop->value(), channel_mode));
- }
-
- unsigned int channel_mask = 0xFFFF;
- if ((prop = node.property ("channel-mask")) != 0) {
- sscanf (prop->value().c_str(), "0x%x", &channel_mask);
- if (channel_mask & (~0xFFFF)) {
- warning << _("MidiDiskstream: XML property channel-mask out of range") << endmsg;
- }
- }
-
-
if (capture_pending_node) {
use_pending_capture_data (*capture_pending_node);
}
- set_channel_mode (channel_mode, channel_mask);
-
in_set_state = false;
return 0;
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
Property prop = i->property;
+
+ if (!i->note) {
+ /* note found during deserialization, so try
+ again now that the model state is different.
+ */
+ i->note = _model->find_note (i->note_id);
+ assert (i->note);
+ }
+
switch (prop) {
case NoteNumber:
if (temporary_removals.find (i->note) == temporary_removals.end()) {
_removed_notes.push_back (*i);
}
}
-
+
if (!side_effect_removals.empty()) {
cerr << "SER: \n";
for (set<NotePtr>::iterator i = side_effect_removals.begin(); i != side_effect_removals.end(); ++i) {
/* notes we modify in a way that requires remove-then-add to maintain ordering */
set<NotePtr> temporary_removals;
+
+ /* lazily discover any affected notes that were not discovered when
+ * loading the history because of deletions, etc.
+ */
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ if (!i->note) {
+ i->note = _model->find_note (i->note_id);
+ assert (i->note);
+ }
+ }
+
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
Property prop = i->property;
- switch (prop) {
+ switch (prop) {
case NoteNumber:
- if (
- temporary_removals.find (i->note) == temporary_removals.end() &&
- find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()
- ) {
+ if (temporary_removals.find (i->note) == temporary_removals.end() &&
+ find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
/* We only need to mark this note for re-add if (a) we haven't
already marked it and (b) it isn't on the _removed_notes
break;
case StartTime:
- if (
- temporary_removals.find (i->note) == temporary_removals.end() &&
- find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()
- ) {
+ if (temporary_removals.find (i->note) == temporary_removals.end() &&
+ find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
/* See above ... */
break;
case Channel:
- if (
- temporary_removals.find (i->note) == temporary_removals.end() &&
- find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()
- ) {
+ if (temporary_removals.find (i->note) == temporary_removals.end() &&
+ find (_removed_notes.begin(), _removed_notes.end(), i->note) == _removed_notes.end()) {
/* See above ... */
_model->add_note_unlocked (*i);
}
}
-
+
_model->ContentsChanged(); /* EMIT SIGNAL */
}
}
/* we must point at the instance of the note that is actually in the model.
- so go look for it ...
+ so go look for it ... it may not be there (it could have been
+ deleted in a later operation, so store the note id so that we can
+ look it up again later).
*/
change.note = _model->find_note (note_id);
-
- if (!change.note) {
- warning << "MIDI note #" << note_id << " not found in model - programmers should investigate this" << endmsg;
- return change;
- }
+ change.note_id = note_id;
return change;
}
_model->remove_sysex_unlocked (*i);
}
+ /* find any sysex events that were missing when unmarshalling */
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ if (!i->sysex) {
+ i->sysex = _model->find_sysex (i->sysex_id);
+ assert (i->sysex);
+ }
+ }
+
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
switch (i->property) {
case Time:
_model->add_sysex_unlocked (*i);
}
+ /* find any sysex events that were missing when unmarshalling */
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ if (!i->sysex) {
+ i->sysex = _model->find_sysex (i->sysex_id);
+ assert (i->sysex);
+ }
+ }
+
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
switch (i->property) {
case Time:
*/
change.sysex = _model->find_sysex (sysex_id);
-
- if (!change.sysex) {
- warning << "Sys-ex #" << sysex_id << " not found in model - programmers should investigate this" << endmsg;
- return change;
- }
+ change.sysex_id = sysex_id;
return change;
}
_model->remove_patch_change_unlocked (*i);
}
+ /* find any patch change events that were missing when unmarshalling */
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ if (!i->patch) {
+ i->patch = _model->find_patch_change (i->patch_id);
+ assert (i->patch);
+ }
+ }
+
set<PatchChangePtr> temporary_removals;
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
_model->add_patch_change_unlocked (*i);
}
+ /* find any patch change events that were missing when unmarshalling */
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ if (!i->patch) {
+ i->patch = _model->find_patch_change (i->patch_id);
+ assert (i->patch);
+ }
+ }
+
set<PatchChangePtr> temporary_removals;
for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
XMLProperty* prop;
Evoral::event_id_t id;
Evoral::MusicalTime time = 0;
- uint8_t channel = 0;
- uint8_t program = 0;
+ int channel = 0;
+ int program = 0;
int bank = 0;
-
+
if ((prop = n->property ("id")) != 0) {
istringstream s (prop->value());
s >> id;
{
XMLProperty* prop;
Change c;
+ int an_int;
prop = n->property ("property");
assert (prop);
assert (prop);
Evoral::event_id_t const id = atoi (prop->value().c_str());
+ /* we need to load via an int intermediate for all properties that are
+ actually uint8_t (char/byte).
+ */
+
prop = n->property ("old");
assert (prop);
{
if (c.property == Time) {
s >> c.old_time;
} else if (c.property == Channel) {
- s >> c.old_channel;
+ s >> an_int;
+ c.old_channel = an_int;
} else if (c.property == Program) {
- s >> c.old_program;
+ s >> an_int;
+ c.old_program = an_int;
} else if (c.property == Bank) {
- s >> c.old_bank;
+ s >> an_int;
+ c.old_bank = an_int;
}
}
assert (prop);
{
istringstream s (prop->value ());
+
if (c.property == Time) {
s >> c.new_time;
} else if (c.property == Channel) {
- s >> c.new_channel;
+ s >> an_int;
+ c.new_channel = an_int;
} else if (c.property == Program) {
- s >> c.new_program;
+ s >> an_int;
+ c.new_program = an_int;
} else if (c.property == Bank) {
- s >> c.new_bank;
+ s >> an_int;
+ c.new_bank = an_int;
}
}
c.patch = _model->find_patch_change (id);
- assert (c.patch);
+ c.patch_id = id;
return c;
}
assert (ms);
assert (!ms->mutex().trylock ());
- return WriteLock(new WriteLockImpl(NULL, _lock, _control_lock));
+ return WriteLock(new WriteLockImpl(0, _lock, _control_lock));
}
int
return *_buffer;
}
-
void
MidiPort::cycle_end (pframes_t /*nframes*/)
{
*/
#include "pbd/compose.h"
+#include "pbd/enumwriter.h"
#include "pbd/error.h"
#include "ardour/debug.h"
bool r = this->peek (&status, sizeof(uint8_t));
assert (r); // If this failed, buffer is corrupt, all hope is lost
- // Ignore event if it doesn't match channel filter
- if (is_channel_event(status) && get_channel_mode() == FilterChannels) {
- const uint8_t channel = status & 0x0F;
- if (!(get_channel_mask() & (1L << channel))) {
- DEBUG_TRACE (DEBUG::MidiDiskstreamIO, string_compose ("MRB skipping event (%3 bytes) due to channel mask (mask = %1 chn = %2)\n",
- get_channel_mask(), (int) channel, ev_size));
- this->increment_read_ptr (ev_size); // Advance read pointer to next event
- continue;
- }
- }
-
/* lets see if we are going to be able to write this event into dst.
*/
uint8_t* write_loc = dst.reserve (ev_time, ev_size);
} else if (is_note_off(write_loc[0])) {
_tracker.remove (write_loc[1], write_loc[0] & 0xf);
}
-
- if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
- write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
- }
+
++count;
} else {
cerr << "WARNING: error reading event contents from MIDI ring" << endl;
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <strings.h> // for ffs(3)
+
#include "pbd/enumwriter.h"
#include "pbd/convert.h"
#include "evoral/midi_util.h"
, _note_mode(Sustained)
, _step_editing (false)
, _input_active (true)
+ , _playback_channel_mask(0x0000ffff)
+ , _capture_channel_mask(0x0000ffff)
{
}
set_input_active (string_is_affirmative (prop->value()));
}
+ ChannelMode playback_channel_mode = AllChannels;
+ ChannelMode capture_channel_mode = AllChannels;
+
+ if ((prop = node.property ("playback-channel-mode")) != 0) {
+ playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode));
+ }
+ if ((prop = node.property ("capture-channel-mode")) != 0) {
+ capture_channel_mode = ChannelMode (string_2_enum(prop->value(), capture_channel_mode));
+ }
+ if ((prop = node.property ("channel-mode")) != 0) {
+ /* 3.0 behaviour where capture and playback modes were not separated */
+ playback_channel_mode = ChannelMode (string_2_enum(prop->value(), playback_channel_mode));
+ capture_channel_mode = playback_channel_mode;
+ }
+
+ unsigned int playback_channel_mask = 0xffff;
+ unsigned int capture_channel_mask = 0xffff;
+
+ if ((prop = node.property ("playback-channel-mask")) != 0) {
+ sscanf (prop->value().c_str(), "0x%x", &playback_channel_mask);
+ }
+ if ((prop = node.property ("capture-channel-mask")) != 0) {
+ sscanf (prop->value().c_str(), "0x%x", &capture_channel_mask);
+ }
+ if ((prop = node.property ("channel-mask")) != 0) {
+ sscanf (prop->value().c_str(), "0x%x", &playback_channel_mask);
+ capture_channel_mask = playback_channel_mask;
+ }
+
+ set_playback_channel_mode (playback_channel_mode, playback_channel_mask);
+ set_capture_channel_mode (capture_channel_mode, capture_channel_mask);
+
pending_state = const_cast<XMLNode*> (&node);
if (_session.state_of_the_state() & Session::Loading) {
root.add_child_nocopy (*freeze_node);
}
+ root.add_property("playback_channel-mode", enum_2_string(get_playback_channel_mode()));
+ root.add_property("capture_channel-mode", enum_2_string(get_capture_channel_mode()));
+ snprintf (buf, sizeof(buf), "0x%x", get_playback_channel_mask());
+ root.add_property("playback-channel-mask", buf);
+ snprintf (buf, sizeof(buf), "0x%x", get_capture_channel_mask());
+ root.add_property("capture-channel-mask", buf);
+
root.add_property ("note-mode", enum_2_string (_note_mode));
root.add_property ("step-editing", (_step_editing ? "yes" : "no"));
root.add_property ("input-active", (_input_active ? "yes" : "no"));
playback distance to zero, thus causing diskstream::commit
to do nothing.
*/
- dret = diskstream->process (transport_frame, 0, playback_distance);
+ BufferSet bufs; /* empty set - is OK, since nothing will happen */
+
+ dret = diskstream->process (bufs, transport_frame, 0, playback_distance, false);
need_butler = diskstream->commit (playback_distance);
return dret;
}
+ BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
+
+ fill_buffers_with_input (bufs, _input, nframes);
+
+ if (_meter_point == MeterInput) {
+ _meter->run (bufs, start_frame, end_frame, nframes, true);
+ }
+
+ /* filter captured data before the diskstream sees it */
+
+ filter_channels (bufs, get_capture_channel_mode(), get_capture_channel_mask());
_silent = false;
- if ((dret = diskstream->process (transport_frame, nframes, playback_distance)) != 0) {
+ if ((dret = diskstream->process (bufs, transport_frame, nframes, playback_distance, (monitoring_state() == MonitoringDisk))) != 0) {
need_butler = diskstream->commit (playback_distance);
silence (nframes);
return dret;
}
- /* special condition applies */
-
- if (_meter_point == MeterInput) {
- _input->process_input (_meter, start_frame, end_frame, nframes);
- }
+ /* filter playback data before we do anything else */
+
+ filter_channels (bufs, get_playback_channel_mode(), get_playback_channel_mask ());
if (monitoring_state() == MonitoringInput) {
diskstream->flush_playback (start_frame, end_frame);
- passthru (start_frame, end_frame, nframes, 0);
-
- } else {
-
- /*
- XXX is it true that the earlier test on n_outputs()
- means that we can avoid checking it again here? i think
- so, because changing the i/o configuration of an IO
- requires holding the AudioEngine lock, which we hold
- while in the process() tree.
- */
-
-
- /* copy the diskstream data to all output buffers */
-
- BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
- MidiBuffer& mbuf (bufs.get_midi (0));
-
- /* we are a MIDI track, so we always start the chain with a
- * single-MIDI-channel diskstream
- */
- ChanCount c;
- c.set_audio (0);
- c.set_midi (1);
- bufs.set_count (c);
-
- assert (nframes > 0);
-
- diskstream->get_playback (mbuf, nframes);
-
- /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */
-
- write_out_of_band_data (bufs, start_frame, end_frame, nframes);
-
- /* final argument: don't waste time with automation if we're recording or we've just stopped (yes it can happen) */
+ }
- process_output_buffers (
- bufs, start_frame, end_frame, nframes,
- declick, (!diskstream->record_enabled() && !_session.transport_stopped())
- );
- }
+
+ /* append immediate messages to the first MIDI buffer (thus sending it to the first output port) */
+
+ write_out_of_band_data (bufs, start_frame, end_frame, nframes);
+
+ /* final argument: don't waste time with automation if we're not recording or rolling */
+
+ process_output_buffers (bufs, start_frame, end_frame, nframes,
+ declick, (!diskstream->record_enabled() && !_session.transport_stopped()));
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
}
}
+void
+MidiTrack::filter_channels (BufferSet& bufs, ChannelMode mode, uint32_t mask)
+{
+ if (mode == AllChannels) {
+ return;
+ }
+
+ MidiBuffer& buf (bufs.get_midi (0));
+
+ for (MidiBuffer::iterator e = buf.begin(); e != buf.end(); ) {
+
+ Evoral::MIDIEvent<framepos_t> ev(*e, false);
+
+ if (ev.is_channel_event()) {
+ switch (mode) {
+ case FilterChannels:
+ if (0 == ((1<<ev.channel()) & mask)) {
+ e = buf.erase (e);
+ } else {
+ ++e;
+ }
+ break;
+ case ForceChannel:
+ ev.set_channel (ffs (mask) - 1);
+ ++e;
+ break;
+ case AllChannels:
+ /* handled by the opening if() */
+ ++e;
+ break;
+ }
+ } else {
+ ++e;
+ }
+ }
+}
+
void
MidiTrack::write_out_of_band_data (BufferSet& bufs, framepos_t /*start*/, framepos_t /*end*/, framecnt_t nframes)
{
}
void
-MidiTrack::set_channel_mode (ChannelMode mode, uint16_t mask)
+MidiTrack::set_playback_channel_mode(ChannelMode mode, uint16_t mask)
{
- midi_diskstream()->set_channel_mode (mode, mask);
+ ChannelMode old = get_playback_channel_mode ();
+ uint16_t old_mask = get_playback_channel_mask ();
+
+ if (old != mode || mask != old_mask) {
+ _set_playback_channel_mode (mode, mask);
+ PlaybackChannelModeChanged ();
+ _session.set_dirty ();
+ }
}
-ChannelMode
-MidiTrack::get_channel_mode ()
+void
+MidiTrack::set_capture_channel_mode(ChannelMode mode, uint16_t mask)
{
- return midi_diskstream()->get_channel_mode ();
+ ChannelMode old = get_capture_channel_mode ();
+ uint16_t old_mask = get_capture_channel_mask ();
+
+ if (old != mode || mask != old_mask) {
+ _set_capture_channel_mode (mode, mask);
+ CaptureChannelModeChanged ();
+ _session.set_dirty ();
+ }
}
-uint16_t
-MidiTrack::get_channel_mask ()
+void
+MidiTrack::set_playback_channel_mask (uint16_t mask)
{
- return midi_diskstream()->get_channel_mask ();
+ uint16_t old = get_playback_channel_mask();
+
+ if (old != mask) {
+ _set_playback_channel_mask (mask);
+ PlaybackChannelMaskChanged ();
+ _session.set_dirty ();
+ }
+}
+
+void
+MidiTrack::set_capture_channel_mask (uint16_t mask)
+{
+ uint16_t old = get_capture_channel_mask();
+
+ if (old != mask) {
+ _set_capture_channel_mask (mask);
+ CaptureChannelMaskChanged ();
+ _session.set_dirty ();
+ }
}
boost::shared_ptr<MidiPlaylist>
if (muted()) {
/* only send messages for channels we are using */
- uint16_t mask = get_channel_mask();
+ uint16_t mask = get_playback_channel_mask();
for (uint8_t channel = 0; channel <= 0xF; channel++) {
}
return ms;
}
+
VSTInfo* finfo;
char buf[32];
+ DEBUG_TRACE (DEBUG::PluginManager, string_compose ("checking apparent LXVST plugin at %1\n", path));
+
if ((finfo = vstfx_get_info (const_cast<char *> (path.c_str()))) == 0) {
return -1;
}
/* never muted */
_amp.reset (new Amp (_session));
- _meter.reset (new PeakMeter (_session));
+ _meter.reset (new PeakMeter (_session, name()));
}
Return::~Return ()
#include "ardour/amp.h"
#include "ardour/audio_buffer.h"
+#include "ardour/audio_port.h"
#include "ardour/audioengine.h"
#include "ardour/buffer.h"
#include "ardour/buffer_set.h"
#include "ardour/internal_return.h"
#include "ardour/internal_send.h"
#include "ardour/meter.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/midi_port.h"
#include "ardour/monitor_processor.h"
#include "ardour/pannable.h"
#include "ardour/panner_shell.h"
they will be added to _processors by setup_invisible_processors ()
*/
- _meter.reset (new PeakMeter (_session));
+ _meter.reset (new PeakMeter (_session, _name));
_meter->set_display_to_user (false);
_meter->activate ();
on a transition between monitoring states we get a de-clicking gain
change in the _main_outs delivery.
*/
- _main_outs->no_outs_cuz_we_no_monitor (monitoring_state () == MonitoringSilence);
+ _main_outs->no_outs_cuz_we_no_monitor (monitoring_state () == MonitoringSilence);
/* -------------------------------------------------------------------------------------------
GLOBAL DECLICK (for transport changes etc.)
}
void
-Route::passthru (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
+Route::monitor_run (framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
{
- BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
+ assert (is_monitor());
+ BufferSet& bufs (_session.get_scratch_buffers (n_process_buffers()));
+ passthru (bufs, start_frame, end_frame, nframes, declick);
+}
+void
+Route::passthru (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, int declick)
+{
_silent = false;
- assert (bufs.available() >= input_streams());
-
- if (_input->n_ports() == ChanCount::ZERO) {
- silence_unlocked (nframes);
- }
-
- bufs.set_count (input_streams());
-
if (is_monitor() && _session.listening() && !_session.is_auditioning()) {
/* control/monitor bus ignores input ports when something is
feeding the listen "stream". data will "arrive" into the
route from the intreturn processor element.
*/
- bufs.silence (nframes, 0);
-
- } else {
-
- for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
- BufferSet::iterator o = bufs.begin(*t);
- PortSet& ports (_input->ports());
-
- for (PortSet::iterator i = ports.begin(*t); i != ports.end(*t); ++i, ++o) {
- o->read_from (i->get_buffer(nframes), nframes);
- }
- }
+ bufs.silence (nframes, 0);
}
write_out_of_band_data (bufs, start_frame, end_frame, nframes);
Route::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing)
{
Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
+
if (!lm.locked()) {
return 0;
}
silence_unlocked (nframes);
return 0;
}
+
if (session_state_changing) {
if (_session.transport_speed() != 0.0f) {
/* we're rolling but some state is changing (e.g. our diskstream contents)
*/
}
+ BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
+
+ fill_buffers_with_input (bufs, _input, nframes);
+
+ if (_meter_point == MeterInput) {
+ _meter->run (bufs, start_frame, end_frame, nframes, true);
+ }
+
_amp->apply_gain_automation (false);
- passthru (start_frame, end_frame, nframes, 0);
+ passthru (bufs, start_frame, end_frame, nframes, 0);
return 0;
}
_silent = false;
- passthru (start_frame, end_frame, nframes, declick);
+ BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
+
+ fill_buffers_with_input (bufs, _input, nframes);
+
+ if (_meter_point == MeterInput) {
+ _meter->run (bufs, start_frame, end_frame, nframes, true);
+ }
+
+ passthru (bufs, start_frame, end_frame, nframes, declick);
return 0;
}
}
}
}
+
+void
+Route::fill_buffers_with_input (BufferSet& bufs, boost::shared_ptr<IO> io, pframes_t nframes)
+{
+ size_t n_buffers;
+ size_t i;
+
+ /* MIDI
+ *
+ * We don't currently mix MIDI input together, so we don't need the
+ * complex logic of the audio case.
+ */
+
+ n_buffers = bufs.count().n_midi ();
+
+ for (i = 0; i < n_buffers; ++i) {
+
+ boost::shared_ptr<MidiPort> source_port = io->midi (i);
+ MidiBuffer& buf (bufs.get_midi (i));
+
+ if (source_port) {
+ buf.copy (source_port->get_midi_buffer(nframes));
+ } else {
+ buf.silence (nframes);
+ }
+ }
+
+ /* AUDIO */
+
+ n_buffers = bufs.count().n_audio();
+
+ size_t n_ports = io->n_ports().n_audio();
+ float scaling = 1.0f;
+
+ if (n_ports > n_buffers) {
+ scaling = ((float) n_buffers) / n_ports;
+ }
+
+ for (i = 0; i < n_ports; ++i) {
+
+ /* if there are more ports than buffers, map them onto buffers
+ * in a round-robin fashion
+ */
+
+ boost::shared_ptr<AudioPort> source_port = io->audio (i);
+ AudioBuffer& buf (bufs.get_audio (i%n_buffers));
+
+
+ if (i < n_buffers) {
+
+ /* first time through just copy a channel into
+ the output buffer.
+ */
+
+ buf.read_from (source_port->get_audio_buffer (nframes), nframes);
+
+ if (scaling != 1.0f) {
+ buf.apply_gain (scaling, nframes);
+ }
+
+ } else {
+
+ /* on subsequent times around, merge data from
+ * the port with what is already there
+ */
+
+ if (scaling != 1.0f) {
+ buf.accumulate_with_gain_from (source_port->get_audio_buffer (nframes), nframes, 0, scaling);
+ } else {
+ buf.accumulate_from (source_port->get_audio_buffer (nframes), nframes);
+ }
+ }
+ }
+
+ /* silence any remaining buffers */
+
+ for (; i < n_buffers; ++i) {
+ AudioBuffer& buf (bufs.get_audio (i));
+ buf.silence (nframes);
+ }
+
+ /* establish the initial setup of the buffer set, reflecting what was
+ copied into it.
+ */
+
+ bufs.set_count (io->n_ports());
+}
boost_debug_shared_ptr_mark_interesting (this, "send");
_amp.reset (new Amp (_session));
- _meter.reset (new PeakMeter (_session));
+ _meter.reset (new PeakMeter (_session, name()));
add_control (_amp->gain_control ());
}
/* if using a monitor section, run it because otherwise we don't hear anything */
if (auditioner->needs_monitor()) {
- _monitor_out->passthru (_transport_frame, _transport_frame + nframes, nframes, false);
+ _monitor_out->monitor_run (_transport_frame, _transport_frame + nframes, nframes, false);
}
/* handle pending events */
Track::no_roll (pframes_t nframes, framepos_t start_frame, framepos_t end_frame, bool session_state_changing)
{
Glib::Threads::RWLock::ReaderLock lm (_processor_lock, Glib::Threads::TRY_LOCK);
+
if (!lm.locked()) {
return 0;
}
bool can_record = _session.actively_recording ();
+ /* no outputs? nothing to do ... what happens if we have sends etc. ? */
+
if (n_outputs().n_total() == 0) {
return 0;
}
+ /* not active ... do the minimum possible by just outputting silence */
+
if (!_active) {
silence (nframes);
return 0;
}
}
- if (!_have_internal_generator && metering_state() == MeteringInput) {
- _input->process_input (_meter, start_frame, end_frame, nframes);
- }
-
_amp->apply_gain_automation (false);
/* if have_internal_generator, or .. */
- //_input->process_input (_meter, start_frame, end_frame, nframes);
if (be_silent) {
} else {
- /* we're sending signal, but we may still want to meter the input.
- */
+ BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
+
+ fill_buffers_with_input (bufs, _input, nframes);
- passthru (start_frame, end_frame, nframes, false);
+ if (_meter_point == MeterInput) {
+ _meter->run (bufs, start_frame, end_frame, nframes, true);
+ }
+
+ passthru (bufs, start_frame, end_frame, nframes, false);
}
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
silence (nframes);
framecnt_t playback_distance;
- int const dret = _diskstream->process (_session.transport_frame(), nframes, playback_distance);
+
+ BufferSet& bufs (_session.get_silent_buffers (n_process_buffers()));
+
+ int const dret = _diskstream->process (bufs, _session.transport_frame(), nframes, playback_distance, false);
need_butler = _diskstream->commit (playback_distance);
return dret;
}
{
return _diskstream->record_enabled() ? MeteringInput : MeteringRoute;
}
+
void what_has_data(std::set<Parameter>&) const;
- Glib::Threads::Mutex& control_lock() const { return _control_lock; }
+ Glib::Threads::Mutex& control_lock() const { return _control_lock; }
protected:
virtual void control_list_marked_dirty () {}
uint8_t channel () const { return _program_change.buffer()[0] & 0xf; }
inline bool operator< (const PatchChange<Time>& o) const {
- if (time() != o.time()) {
+ if (!musical_time_equal (time(), o.time())) {
return time() < o.time();
}
}
inline bool operator== (const PatchChange<Time>& o) const {
- return (time() == o.time() && program() == o.program() && bank() == o.bank());
+ return (musical_time_equal (time(), o.time()) && program() == o.program() && bank() == o.bank());
}
/** The PatchChange is made up of messages() MIDI messages; this method returns them by index.
}
+template<typename Time>
+std::ostream& operator<< (std::ostream& o, const Evoral::PatchChange<Time>& p) {
+ o << "Patch Change " << p.id() << " @ " << p.time() << " bank " << (int) p.bank() << " program " << (int) p.program();
+ return o;
+}
+
#endif
struct WriteLockImpl {
WriteLockImpl(Glib::Threads::RWLock& s, Glib::Threads::Mutex& c)
: sequence_lock(new Glib::Threads::RWLock::WriterLock(s))
- , control_lock(new Glib::Threads::Mutex::Lock(c))
- { }
+ , control_lock(new Glib::Threads::Mutex::Lock(c)) { }
~WriteLockImpl() {
delete sequence_lock;
delete control_lock;
typedef boost::shared_ptr<WriteLockImpl> WriteLock;
virtual ReadLock read_lock() const { return ReadLock(new Glib::Threads::RWLock::ReaderLock(_lock)); }
- virtual WriteLock write_lock() { return WriteLock(new WriteLockImpl(_lock, _control_lock)); }
+ virtual WriteLock write_lock() { return WriteLock(new WriteLockImpl(_lock, _control_lock)); }
void clear();
struct EarlierNoteComparator {
inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
const boost::shared_ptr< const Note<Time> > b) const {
- return a->time() < b->time();
+ return musical_time_less_than (a->time(), b->time());
}
};
typedef const Note<Time>* value_type;
inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
const boost::shared_ptr< const Note<Time> > b) const {
+ return musical_time_greater_than (a->time(), b->time());
return a->time() > b->time();
}
};
typedef const Note<Time>* value_type;
inline bool operator()(const boost::shared_ptr< const Note<Time> > a,
const boost::shared_ptr< const Note<Time> > b) const {
- return a->end_time() > b->end_time();
+ return musical_time_less_than (a->end_time(), b->end_time());
}
};
struct EarlierSysExComparator {
inline bool operator() (constSysExPtr a, constSysExPtr b) const {
- return a->time() < b->time();
+ return musical_time_less_than (a->time(), b->time());
}
};
struct EarlierPatchChangeComparator {
inline bool operator() (constPatchChangePtr a, constPatchChangePtr b) const {
- return a->time() < b->time();
+ return musical_time_less_than (a->time(), b->time());
}
};
return fabs (a - b) <= (1.0/1920.0);
}
+static inline bool musical_time_less_than (MusicalTime a, MusicalTime b) {
+ /* acceptable tolerance is 1 tick. Nice if there was no magic number here */
+ if (fabs (a - b) <= (1.0/1920.0)) {
+ return false; /* effectively identical */
+ } else {
+ return a < b;
+ }
+}
+
+static inline bool musical_time_greater_than (MusicalTime a, MusicalTime b) {
+ /* acceptable tolerance is 1 tick. Nice if there was no magic number here */
+ if (fabs (a - b) <= (1.0/1920.0)) {
+ return false; /* effectively identical */
+ } else {
+ return a > b;
+ }
+}
+
+static inline bool musical_time_greater_or_equal_to (MusicalTime a, MusicalTime b) {
+ /* acceptable tolerance is 1 tick. Nice if there was no magic number here */
+ if (fabs (a - b) <= (1.0/1920.0)) {
+ return true; /* effectively identical, note the "or_equal_to" */
+ } else {
+ return a >= b;
+ }
+}
+
/** Type of an event (opaque, mapped by application) */
typedef uint32_t EventType;
Sequence<Time>::remove_note_unlocked(const constNotePtr note)
{
bool erased = false;
+ bool id_matched = false;
- _edited = true;
-
- DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 remove note %2 @ %3\n", this, (int)note->note(), note->time()));
+ DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1 remove note #%2 %3 @ %4\n", this, note->id(), (int)note->note(), note->time()));
- typename Sequence<Time>::Notes::iterator i = note_lower_bound(note->time());
- while (i != _notes.end() && (*i)->time() == note->time()) {
+ /* first try searching for the note using the time index, which is
+ * faster since the container is "indexed" by time. (technically, this
+ * means that lower_bound() can do a binary search rather than linear)
+ *
+ * this may not work, for reasons explained below.
+ */
- typename Sequence<Time>::Notes::iterator tmp = i;
- ++tmp;
+ typename Sequence<Time>::Notes::iterator i;
+
+ for (i = note_lower_bound(note->time()); i != _notes.end() && musical_time_equal ((*i)->time(), note->time()); ++i) {
if (*i == note) {
- NotePtr n = *i;
-
- DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1\terasing note %2 @ %3\n", this, (int)(*i)->note(), (*i)->time()));
+ DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1\terasing note #%2 %3 @ %4\n", this, (*i)->id(), (int)(*i)->note(), (*i)->time()));
_notes.erase (i);
- if (n->note() == _lowest_note || n->note() == _highest_note) {
+ if (note->note() == _lowest_note || note->note() == _highest_note) {
_lowest_note = 127;
_highest_note = 0;
}
erased = true;
+ break;
}
+ }
- i = tmp;
+ DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1\ttime-based lookup did not find note #%2 %3 @ %4\n", this, note->id(), (int)note->note(), note->time()));
+
+ if (!erased) {
+
+ /* if the note's time property was changed in tandem with some
+ * other property as the next operation after it was added to
+ * the sequence, then at the point where we call this to undo
+ * the add, the note we are targetting currently has a
+ * different time property than the one we we passed via
+ * the argument.
+ *
+ * in this scenario, we have no choice other than to linear
+ * search the list of notes and find the note by ID.
+ */
+
+ for (i = _notes.begin(); i != _notes.end(); ++i) {
+
+ if ((*i)->id() == note->id()) {
+
+ DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1\tID-based pass, erasing note #%2 %3 @ %4\n", this, (*i)->id(), (int)(*i)->note(), (*i)->time()));
+ _notes.erase (i);
+
+ if (note->note() == _lowest_note || note->note() == _highest_note) {
+
+ _lowest_note = 127;
+ _highest_note = 0;
+
+ for (typename Sequence<Time>::Notes::iterator ii = _notes.begin(); ii != _notes.end(); ++ii) {
+ if ((*ii)->note() < _lowest_note)
+ _lowest_note = (*ii)->note();
+ if ((*ii)->note() > _highest_note)
+ _highest_note = (*ii)->note();
+ }
+ }
+
+ erased = true;
+ id_matched = true;
+ break;
+ }
+ }
}
+
+ if (erased) {
- Pitches& p (pitches (note->channel()));
+ Pitches& p (pitches (note->channel()));
+
+ typename Pitches::iterator j;
- NotePtr search_note(new Note<Time>(0, 0, 0, note->note(), 0));
+ /* if we had to ID-match above, we can't expect to find it in
+ * pitches via note comparison either. so do another linear
+ * search to locate it. otherwise, we can use the note index
+ * to potentially speed things up.
+ */
- typename Pitches::iterator j = p.lower_bound (search_note);
- while (j != p.end() && (*j)->note() == note->note()) {
- typename Pitches::iterator tmp = j;
- ++tmp;
+ if (id_matched) {
+
+ for (j = p.begin(); j != p.end(); ++j) {
+ if ((*j)->id() == note->id()) {
+ p.erase (j);
+ break;
+ }
+ }
+
+ } else {
- if (*j == note) {
- DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1\terasing pitch %2 @ %3\n", this, (int)(*j)->note(), (*j)->time()));
- p.erase (j);
+ /* Now find the same note in the "pitches" list (which indexes
+ * notes by channel+time. We care only about its note number
+ * so the search_note has all other properties unset.
+ */
+
+ NotePtr search_note (new Note<Time>(0, 0, 0, note->note(), 0));
+
+ for (j = p.lower_bound (search_note); j != p.end() && (*j)->note() == note->note(); ++j) {
+
+ if ((*j) == note) {
+ DEBUG_TRACE (DEBUG::Sequence, string_compose ("%1\terasing pitch %2 @ %3\n", this, (int)(*j)->note(), (*j)->time()));
+ p.erase (j);
+ break;
+ }
+ }
}
- j = tmp;
- }
+ if (j == p.end()) {
+ warning << string_compose ("erased note %1 not found in pitches for channel %2", *note, (int) note->channel()) << endmsg;
+ }
- if (!erased) {
- cerr << "Unable to find note to erase matching " << *note.get() << endl;
+ _edited = true;
+
+ } else {
+ cerr << "Unable to find note to erase matching " << *note.get() << endmsg;
}
}
Sequence<Time>::remove_patch_change_unlocked (const constPatchChangePtr p)
{
typename Sequence<Time>::PatchChanges::iterator i = patch_change_lower_bound (p->time ());
- while (i != _patch_changes.end() && (*i)->time() == p->time()) {
+
+ while (i != _patch_changes.end() && (musical_time_equal ((*i)->time(), p->time()))) {
typename Sequence<Time>::PatchChanges::iterator tmp = i;
++tmp;
- if (*i == p) {
+ if (**i == *p) {
_patch_changes.erase (i);
}
{
PatchChangePtr search (new PatchChange<Time> (t, 0, 0, 0));
typename Sequence<Time>::PatchChanges::const_iterator i = _patch_changes.lower_bound (search);
- assert (i == _patch_changes.end() || (*i)->time() >= t);
+ assert (i == _patch_changes.end() || musical_time_greater_or_equal_to ((*i)->time(), t));
return i;
}
--- /dev/null
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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 __libgtkmm2ext_visibility_tracker__
+#define __libgtkmm2ext_visibility_tracker__
+
+#include <gdk/gdkevents.h>
+
+namespace GTK {
+ class Window;
+}
+
+namespace Gtkmm2ext {
+
+class VisibilityTracker {
+ public:
+ VisibilityTracker (Gtk::Window&);
+ virtual ~VisibilityTracker() {}
+
+ void cycle_visibility ();
+
+ private:
+ Gtk::Window& window;
+ GdkVisibilityState _visibility;
+ bool handle_visibility_notify_event (GdkEventVisibility*);
+};
+
+}
+
+#endif /* __libgtkmm2ext_visibility_tracker__ */
--- /dev/null
+/*
+ Copyright (C) 2013 Paul Davis
+
+ 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 <gtkmm/window.h>
+
+#include "gtkmm2ext/visibility_tracker.h"
+
+using namespace Gtkmm2ext;
+
+VisibilityTracker::VisibilityTracker (Gtk::Window& win)
+ : window (win)
+ , _visibility (GdkVisibilityState (0))
+{
+ window.add_events (Gdk::VISIBILITY_NOTIFY_MASK);
+ window.signal_visibility_notify_event().connect (sigc::mem_fun (*this, &VisibilityTracker::handle_visibility_notify_event));
+}
+
+bool
+VisibilityTracker::handle_visibility_notify_event (GdkEventVisibility* ev)
+{
+ _visibility = ev->state;
+ return false;
+}
+
+void
+VisibilityTracker::cycle_visibility ()
+{
+ if (window.is_mapped() && (_visibility == GDK_VISIBILITY_UNOBSCURED)) {
+ window.hide ();
+ } else {
+ window.present ();
+ }
+}
+
'treeutils.cc',
'utils.cc',
'version.cc',
+ 'visibility_tracker.cc',
'window_title.cc'
]
typedef std::map<const char*,uint64_t> DebugMap;
namespace PBD {
- DebugMap _debug_bit_map;
+ DebugMap & _debug_bit_map()
+ {
+ static DebugMap map;
+ return map;
+ }
}
uint64_t PBD::DEBUG::Stateful = PBD::new_debug_bit ("stateful");
PBD::new_debug_bit (const char* name)
{
uint64_t ret;
- _debug_bit_map.insert (make_pair (name, _debug_bit));
+ _debug_bit_map().insert (make_pair (name, _debug_bit));
ret = _debug_bit;
_debug_bit <<= 1;
return ret;
return 0;
}
- for (map<const char*,uint64_t>::iterator i = _debug_bit_map.begin(); i != _debug_bit_map.end(); ++i) {
+ for (map<const char*,uint64_t>::iterator i = _debug_bit_map().begin(); i != _debug_bit_map().end(); ++i) {
if (strncasecmp (p, i->first, strlen (p)) == 0) {
bits |= i->second;
}
vector<string> options;
- for (map<const char*,uint64_t>::iterator i = _debug_bit_map.begin(); i != _debug_bit_map.end(); ++i) {
+ for (map<const char*,uint64_t>::iterator i = _debug_bit_map().begin(); i != _debug_bit_map().end(); ++i) {
options.push_back (i->first);
}
# Library
if bld.is_defined ('INTERNAL_SHARED_LIBS'):
- print 'BUILD SHARED LIB'
+ print('BUILD SHARED LIB')
obj = bld.shlib(features = 'cxx cxxshlib', source=libpbd_sources)
else:
- print 'BUILD STATIC LIB'
+ print('BUILD STATIC LIB')
obj = bld.stlib(features = 'cxx cxxstlib', source=libpbd_sources)
obj.cxxflags = [ '-fPIC' ]
mkdir -p $USER_ARDOUR_DIR || exit 1
fi
+PATH="${BIN_DIR}:${PATH}"
+export PATH
+
# this triggers code in main() that will reset runtime environment variables
# to point to directories inside the ardour package
MIXBUS=
WITH_LADSPA=0
+WITH_HARVID=0
STRIP=all
PRINT_SYSDEPS=
WITH_NLS=
--strip) STRIP=$2 ; shift ; shift ;;
--sysdeps) PRINT_SYSDEPS=1; shift ;;
--nls) WITH_NLS=1 ; shift ;;
+ --harvid) WITH_HARVID=1 ; shift ;;
*)
#catch all for unknown arguments
exit 1
fi
-release_version=`grep -m 1 '[^A-Za-z_]LINUX_VERSION = ' ../../wscript | awk '{ print $3 }' | sed "s/'//g"`
-revision=`grep -m 1 'revision =' ../../libs/ardour/revision.cc | cut -d'"' -f 2`
-echo "Version is $release_version / $revision"
-info_string="$release_version/$revision built on `hostname` by `whoami` on `date`"
+. ./define_versions.sh
+
+echo "Version is $version / $commit"
+info_string="$version ($commit) built on `hostname` by `whoami` on `date`"
echo "Info string is $info_string"
# Figure out our CPU type
echo "Architecture is x86"
ARCH='x86'
ARCH_BITS='32-bit'
+ MULTIARCH='i386-linux-gnu'
;;
x86_64|amd64|AMD64)
echo "Architecture is x86_64"
ARCH='x86_64'
ARCH_BITS='64-bit'
+ MULTIARCH='x86_64-linux-gnu'
;;
*)
echo ""
;;
esac
-# Figure out the Build Type
-if grep -q "DEBUG = True" ../../build/c4che/default.cache.py; then
- DEBUG="T"
-else
- DEBUG="F"
-fi
-
if [ x$DEBUG = xT ]; then
BUILDTYPE="dbg"
if [ x$STRIP = xall ] ; then
# setup directory structure
if [ -z "${BUILDTYPE}" ]; then
- APPDIR=${APPNAME}_${ARCH}-${release_version}_${revision}
- APP_VER_NAME=${APPNAME}-${release_version}_${revision}
+ APPDIR=${APPNAME}_${ARCH}-${version}
+ APP_VER_NAME=${APPNAME}-${version}
else
- APPDIR=${APPNAME}_${ARCH}-${release_version}_${revision}-${BUILDTYPE}
- APP_VER_NAME=${APPNAME}-${release_version}_${revision}-${BUILDTYPE}
+ APPDIR=${APPNAME}_${ARCH}-${version}-${BUILDTYPE}
+ APP_VER_NAME=${APPNAME}-${version}-${BUILDTYPE}
fi
APPBIN=$APPDIR/bin
rm -rf $svndir
done
+if test x$WITH_HARVID != x ; then
+ cd $APPBIN
+ HARVID_VERSION="v0.7.0" # todo make 'latest' symlink on github work somehow.
+ curl http://x42.github.com/harvid/releases/harvid-${MULTIARCH}-${HARVID_VERSION}.tgz \
+ | tar -x -z --exclude=README --exclude=harvid.1 --strip-components=1 || exit 1
+ cd -
+fi
+
#
# Add the uninstaller
#
-sed -e "s/%REPLACE_PGM%/${APPNAME}/" -e "s/%REPLACE_VENDOR%/${VENDOR}/" -e "s/%REPLACE_VERSION%/${release_version}/" -e "s/%REPLACE_BUILD%/${revision}/" -e "s/%REPLACE_TYPE%/${BUILDTYPE}/" < uninstall.sh.in > $APPBIN/${APP_VER_NAME}.uninstall.sh
+sed -e "s/%REPLACE_PGM%/${APPNAME}/" -e "s/%REPLACE_VENDOR%/${VENDOR}/" -e "s/%REPLACE_VERSION%/${version}/" -e "s/%REPLACE_TYPE%/${BUILDTYPE}/" < uninstall.sh.in > $APPBIN/${APP_VER_NAME}.uninstall.sh
chmod a+x $APPBIN/${APP_VER_NAME}.uninstall.sh
#Sanity Check file
--- /dev/null
+#
+# this is sourced by build and package, and executed from within build/linux_packaging
+#
+
+release_version=`grep -m 1 '[^A-Za-z_]LINUX_VERSION = ' ../../wscript | awk '{print $3}' | sed "s/'//g"`
+r=`cut -d'"' -f2 < ../../libs/ardour/revision.cc | sed -e 1d -e "s/$release_version-//"`
+revcount=`echo $r | cut -d- -f1`
+commit=`echo $r | cut -d- -f2`
+version=${release_version}.${revcount}
+
+#
+# Figure out the Build Type
+#
+# Note that the name of the cache file may vary from to time
+#
+
+if grep -q "DEBUG = True" ../../build/c4che/_cache.py; then
+ DEBUG="T"
+else
+ DEBUG="F"
+fi
esac
done
-
-# Figure out the Build Type
-if grep -q "DEBUG = True" ../../build/c4che/default.cache.py; then
- DEBUG="T"
-else
- DEBUG="F"
-fi
+. ./define_versions.sh
if [ x$DEBUG = xT ]; then
BUILDTYPE="dbg"
fi
-release_version=`grep -m 1 '[^A-Za-z_]LINUX_VERSION = ' ../../wscript | awk '{print $3}' | sed "s/'//g"`
-revision=`grep -m 1 'revision =' ../../libs/ardour/revision.cc | cut -d'"' -f 2`
-
-X86_BUNDLE="${APPNAME}_x86-${release_version}_${revision}"
-X86_64_BUNDLE="${APPNAME}_x86_64-${release_version}_${revision}"
+X86_BUNDLE="${APPNAME}_x86-${version}"
+X86_64_BUNDLE="${APPNAME}_x86_64-${version}"
if [ ! -z ${BUILDTYPE} ]; then
X86_BUNDLE="${X86_BUNDLE}-${BUILDTYPE}"
X86_64_BUNDLE="${X86_64_BUNDLE}-${BUILDTYPE}"
fi
-
if [ ! -e ${X86_BUNDLE}.tar.bz2 ] ; then
echo ""
echo "Can't locate x86 bundle file ${X86_BUNDLE}.tar.bz2"
if [ -z ${BUILDTYPE} ]; then
if [ "${SINGLE_ARCH}" = "T" ]; then
if [ "${X86_BUNDLE_OK}" = "T" ]; then
- PACKAGE="${APPNAME}_32bit-${release_version}_${revision}"
+ PACKAGE="${APPNAME}_32bit-${version}"
else
- PACKAGE="${APPNAME}_64bit-${release_version}_${revision}"
+ PACKAGE="${APPNAME}_64bit-${version}"
fi
else
- PACKAGE="${APPNAME}-${release_version}_${revision}"
+ PACKAGE="${APPNAME}-${version}"
fi
else
if [ "${SINGLE_ARCH}" = "T" ]; then
if [ "${X86_BUNDLE_OK}" = "T" ]; then
- PACKAGE="${APPNAME}_32bit-${release_version}_${revision}-${BUILDTYPE}"
+ PACKAGE="${APPNAME}_32bit-${version}-${BUILDTYPE}"
else
- PACKAGE="${APPNAME}_64bit-${release_version}_${revision}-${BUILDTYPE}"
+ PACKAGE="${APPNAME}_64bit-${version}-${BUILDTYPE}"
fi
else
- PACKAGE="${APPNAME}-${release_version}_${revision}-${BUILDTYPE}"
+ PACKAGE="${APPNAME}-${version}-${BUILDTYPE}"
fi
fi
################################
# Setup derived variables
################################
-PGM_VERSION=$(echo ${BUNDLE_DIR} | awk 'BEGIN { FS = "-" } ; { print $2 }' | awk 'BEGIN { FS = "_"} ; { print $1}')
-PGM_BUILD=$(echo ${BUNDLE_DIR} | awk 'BEGIN { FS = "-" } ; { print $2 }' | awk 'BEGIN { FS = "_"} ; { print $2}')
-PGM_BUILDTYPE=$(echo ${BUNDLE_DIR} | awk 'BEGIN { FS = "-" } ; { print $3 }')
+PGM_VERSION=$(echo ${BUNDLE_DIR} | cut -d- -f2)
+PGM_BUILDTYPE=$(echo ${BUNDLE_DIR} | cut -d- -f3)
if [ -z ${PGM_BUILDTYPE} ];
then
- PGM_FULL_NAME="${PGM_NAME}-${PGM_VERSION}_${PGM_BUILD}"
+ PGM_FULL_NAME="${PGM_NAME}-${PGM_VERSION}"
ICON_NAME="${PGM_VENDOR}-${PGM_NAME}_${PGM_VERSION}" #no dash between name and version since dash seperates vendor from program
MENU_FILE="${PGM_VENDOR}-${PGM_NAME}_${PGM_VERSION}.desktop" #no dash between name and version since dash seperates vendor from program
DESKTOP_LINK_FILE="${PGM_NAME}_${PGM_VERSION}.desktop"
else
- PGM_FULL_NAME="${PGM_NAME}-${PGM_VERSION}_${PGM_BUILD}-${PGM_BUILDTYPE}"
+ PGM_FULL_NAME="${PGM_NAME}-${PGM_VERSION}-${PGM_BUILDTYPE}"
ICON_NAME="${PGM_VENDOR}-${PGM_NAME}_${PGM_VERSION}_${PGM_BUILDTYPE}" #no dash between name and version since dash seperates vendor from program
MENU_FILE="${PGM_VENDOR}-${PGM_NAME}_${PGM_VERSION}_${PGM_BUILDTYPE}.desktop" #no dash between name and version since dash seperates vendor from program
DESKTOP_LINK_FILE="${PGM_NAME}_${PGM_VERSION}_${PGM_BUILDTYPE}.desktop"
# install
echo ""
-echo "Installing ${PGM_NAME} ${PGM_VERSION} built from ${PGM_BUILD} in ${INSTALL_DEST_BASE}"
+echo "Installing ${PGM_NAME} ${PGM_VERSION} in ${INSTALL_DEST_BASE}"
echo ""
# Copy the new version in the install directory
PGM_NAME="%REPLACE_PGM%"
PGM_VENDOR="%REPLACE_VENDOR%"
PGM_VERSION="%REPLACE_VERSION%"
-PGM_BUILD="%REPLACE_BUILD%"
PGM_BUILDTYPE="%REPLACE_TYPE%"
INSTALL_DEST_BASE=/opt
#### Derived Variables ####
if [ -z "${PGM_BUILDTYPE}" ]; then
- PGM_PATH=${INSTALL_DEST_BASE}/${PGM_NAME}-${PGM_VERSION}_${PGM_BUILD}
+ PGM_PATH=${INSTALL_DEST_BASE}/${PGM_NAME}-${PGM_VERSION}
ICON_NAME="${PGM_VENDOR}-${PGM_NAME}_${PGM_VERSION}" #no dash between name and version since dash seperates vendor from program
MENU_FILE="${PGM_VENDOR}-${PGM_NAME}_${PGM_VERSION}.desktop" #no dash between name and version since dash seperates vendor from program
DESKTOP_LINK_FILE="${PGM_NAME}_${PGM_VERSION}.desktop"
else
- PGM_PATH=${INSTALL_DEST_BASE}/${PGM_NAME}-${PGM_VERSION}_${PGM_BUILD}-${PGM_BUILDTYPE}
+ PGM_PATH=${INSTALL_DEST_BASE}/${PGM_NAME}-${PGM_VERSION}-${PGM_BUILDTYPE}
ICON_NAME="${PGM_VENDOR}-${PGM_NAME}_${PGM_VERSION}_${PGM_BUILDTYPE}" #no dash between name and version since dash seperates vendor from program
MENU_FILE="${PGM_VENDOR}-${PGM_NAME}_${PGM_VERSION}_${PGM_BUILDTYPE}.desktop" #no dash between name and version since dash seperates vendor from program
DESKTOP_LINK_FILE="${PGM_NAME}_${PGM_VERSION}_${PGM_BUILDTYPE}.desktop"