summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2008-02-01 04:26:22 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2008-02-01 04:26:22 +0000
commitbd3b9d763b0409c8e59d5ea38857d604e917818a (patch)
treed9d5dbb001901da5ab5522777055513a79522a77
parent76c658ea48e1ad11b7f407d15e5e8c1e3e55ebb4 (diff)
large chunks of code to deal with pre-analysis of audio; transient/perconset data used for new tab-to-transient; all ArdourDialogs push the splash screen out of the way; try to keep verbose canvas cursor within the editor canvas visible area; fix template use from NSD
git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2983 d708f5d6-7413-0410-9779-e7cbd77b26cf
-rw-r--r--gtk2_ardour/ardour-sae-de.bindings.in2
-rw-r--r--gtk2_ardour/ardour.bindings.in2
-rw-r--r--gtk2_ardour/ardour_dialog.cc16
-rw-r--r--gtk2_ardour/ardour_dialog.h1
-rw-r--r--gtk2_ardour/ardour_ui.cc48
-rw-r--r--gtk2_ardour/ardour_ui.h1
-rw-r--r--gtk2_ardour/ardour_ui_dependents.cc1
-rw-r--r--gtk2_ardour/editor.cc39
-rw-r--r--gtk2_ardour/editor.h5
-rw-r--r--gtk2_ardour/editor_actions.cc3
-rw-r--r--gtk2_ardour/editor_canvas_events.cc6
-rw-r--r--gtk2_ardour/editor_mouse.cc2
-rw-r--r--gtk2_ardour/editor_ops.cc127
-rw-r--r--gtk2_ardour/editor_tempodisplay.cc4
-rw-r--r--gtk2_ardour/new_session_dialog.cc6
-rw-r--r--gtk2_ardour/public_editor.h2
-rw-r--r--gtk2_ardour/rhythm_ferret.cc34
-rw-r--r--gtk2_ardour/rhythm_ferret.h6
-rw-r--r--gtk2_ardour/splash.cc10
-rw-r--r--gtk2_ardour/splash.h8
-rw-r--r--gtk2_ardour/tempo_dialog.cc1
-rw-r--r--gtk2_ardour/time_axis_view.cc4
-rw-r--r--gtk2_ardour/time_axis_view.h2
-rw-r--r--gtk2_ardour/utils.cc10
-rw-r--r--libs/ardour/SConscript1
-rw-r--r--libs/ardour/analyser.cc119
-rw-r--r--libs/ardour/ardour/analyser.h35
-rw-r--r--libs/ardour/ardour/audioanalyser.h2
-rw-r--r--libs/ardour/ardour/audiofilesource.h2
-rw-r--r--libs/ardour/ardour/audioregion.h8
-rw-r--r--libs/ardour/ardour/audiosource.h10
-rw-r--r--libs/ardour/ardour/configuration_vars.h2
-rw-r--r--libs/ardour/ardour/playlist.h3
-rw-r--r--libs/ardour/ardour/region.h61
-rw-r--r--libs/ardour/ardour/session.h10
-rw-r--r--libs/ardour/ardour/silentfilesource.h1
-rw-r--r--libs/ardour/ardour/source.h19
-rw-r--r--libs/ardour/ardour/tempo.h18
-rw-r--r--libs/ardour/ardour/transient_detector.h12
-rw-r--r--libs/ardour/ardour/types.h2
-rw-r--r--libs/ardour/audio_diskstream.cc2
-rw-r--r--libs/ardour/audioanalyser.cc46
-rw-r--r--libs/ardour/audioengine.cc5
-rw-r--r--libs/ardour/audioregion.cc108
-rw-r--r--libs/ardour/audiosource.cc30
-rw-r--r--libs/ardour/enums.cc5
-rw-r--r--libs/ardour/globals.cc2
-rw-r--r--libs/ardour/playlist.cc75
-rw-r--r--libs/ardour/recent_sessions.cc7
-rw-r--r--libs/ardour/region.cc100
-rw-r--r--libs/ardour/session.cc32
-rw-r--r--libs/ardour/session_state.cc24
-rw-r--r--libs/ardour/source.cc23
-rw-r--r--libs/ardour/source_factory.cc12
-rw-r--r--libs/ardour/tempo.cc187
-rw-r--r--libs/ardour/transient_detector.cc57
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp34
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h2
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.cpp8
-rw-r--r--libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp30
60 files changed, 1166 insertions, 268 deletions
diff --git a/gtk2_ardour/ardour-sae-de.bindings.in b/gtk2_ardour/ardour-sae-de.bindings.in
index 68f9b13b0b..ab3a6824b6 100644
--- a/gtk2_ardour/ardour-sae-de.bindings.in
+++ b/gtk2_ardour/ardour-sae-de.bindings.in
@@ -14,6 +14,8 @@
(gtk_accel_path "<Actions>/Editor/toggle-edit-mode" "1")
(gtk_accel_path "<Actions>/Editor/cycle-snap-mode" "2")
(gtk_accel_path "<Actions>/Editor/cycle-snap-choice" "3")
+(gtk_accel_path "<Actions>/Editor/tab-to-transient-forwards" "nabla")
+(gtk_accel_path "<Actions>/Editor/tab-to-transient-backwards" "<%TERTIARY%>nabla")
; (gtk_accel_path "<Actions>/redirectmenu/copy" "")
; (gtk_accel_path "<Actions>/options/MeterFalloffFaster" "")
(gtk_accel_path "<Actions>/Transport/ToggleRollForgetCapture" "<%PRIMARY%>space")
diff --git a/gtk2_ardour/ardour.bindings.in b/gtk2_ardour/ardour.bindings.in
index 3d488c1ec4..73a24c3836 100644
--- a/gtk2_ardour/ardour.bindings.in
+++ b/gtk2_ardour/ardour.bindings.in
@@ -182,6 +182,8 @@
(gtk_accel_path "<Actions>/Editor/cycle-snap-choice" "3")
(gtk_accel_path "<Actions>/Transport/ToggleAutoReturn" "4")
(gtk_accel_path "<Actions>/Transport/ToggleClick" "5")
+(gtk_accel_path "<Actions>/Editor/tab-to-transient-forwards" "nabla")
+(gtk_accel_path "<Actions>/Editor/tab-to-transient-backwards" "<%TERTIARY%>nabla")
(gtk_accel_path "<Actions>/Editor/set-tempo-from-region" "9")
(gtk_accel_path "<Actions>/Editor/set-tempo-from-edit-range" "0")
diff --git a/gtk2_ardour/ardour_dialog.cc b/gtk2_ardour/ardour_dialog.cc
index 795b924075..bc24389948 100644
--- a/gtk2_ardour/ardour_dialog.cc
+++ b/gtk2_ardour/ardour_dialog.cc
@@ -24,7 +24,7 @@
#include "ardour_dialog.h"
#include "keyboard.h"
#include "ardour_ui.h"
-
+#include "splash.h"
ArdourDialog::ArdourDialog (string title, bool modal, bool use_seperator)
: Dialog (title, modal, use_seperator)
@@ -66,3 +66,17 @@ ArdourDialog::on_unmap ()
{
Dialog::on_unmap ();
}
+
+void
+ArdourDialog::on_show ()
+{
+ // never allow the splash screen to obscure any dialog
+
+ Splash* spl = Splash::instance();
+
+ if (spl) {
+ spl->pop_back ();
+ }
+
+ Dialog::on_show ();
+}
diff --git a/gtk2_ardour/ardour_dialog.h b/gtk2_ardour/ardour_dialog.h
index e0cdb97cdc..f2859717e0 100644
--- a/gtk2_ardour/ardour_dialog.h
+++ b/gtk2_ardour/ardour_dialog.h
@@ -43,6 +43,7 @@ class ArdourDialog : public Gtk::Dialog
bool on_enter_notify_event (GdkEventCrossing*);
bool on_leave_notify_event (GdkEventCrossing*);
void on_unmap ();
+ void on_show ();
ARDOUR::Session *session;
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc
index 93d69cc626..ad89f1cd15 100644
--- a/gtk2_ardour/ardour_ui.cc
+++ b/gtk2_ardour/ardour_ui.cc
@@ -231,6 +231,10 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[])
ARDOUR::Session::AskAboutPendingState.connect (mem_fun(*this, &ARDOUR_UI::pending_state_dialog));
+ /* handle sr mismatch with a dialog */
+
+ ARDOUR::Session::AskAboutSampleRateMismatch.connect (mem_fun(*this, &ARDOUR_UI::sr_mismatch_dialog));
+
/* lets get this party started */
try {
@@ -984,6 +988,13 @@ ARDOUR_UI::redisplay_recent_sessions ()
fullpath = fullpath.substr (0, fullpath.length()-1);
}
+ /* check whether session still exists */
+ if (!Glib::file_test(fullpath.c_str(), Glib::FILE_TEST_EXISTS)) {
+ /* session doesn't exist */
+ cerr << "skipping non-existent session " << fullpath << endl;
+ continue;
+ }
+
/* now get available states for this session */
if ((states = Session::possible_states (fullpath)) == 0) {
@@ -1665,6 +1676,8 @@ ARDOUR_UI::setup_theme ()
void
ARDOUR_UI::update_clocks ()
{
+ return;
+
if (!editor || !editor->dragging_playhead()) {
Clock (session->audible_frame(), false, editor->get_preferred_edit_position()); /* EMIT_SIGNAL */
}
@@ -2280,6 +2293,7 @@ ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be
session_path = new_session_dialog->session_folder();
}
+ template_name = Glib::ustring();
switch (new_session_dialog->which_page()) {
case NewSessionDialog::OpenPage:
@@ -2329,7 +2343,7 @@ ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be
loadit:
new_session_dialog->hide ();
- if (load_session (session_path, session_name)) {
+ if (load_session (session_path, session_name, template_name)) {
/* force a retry */
response = Gtk::RESPONSE_NONE;
}
@@ -2963,6 +2977,38 @@ what you would like to do.\n"));
return 0;
}
}
+
+int
+ARDOUR_UI::sr_mismatch_dialog (nframes_t desired, nframes_t actual)
+{
+ HBox* hbox = new HBox();
+ Image* image = new Image (Stock::DIALOG_QUESTION, ICON_SIZE_DIALOG);
+ ArdourDialog dialog (_("Sample Rate Mismatch"), true);
+ Label message (string_compose (_("\
+This session was created with a sample rate of %1 Hz\n\
+\n\
+The audioengine is currently running at %2 Hz\n"), desired, actual));
+
+ image->set_alignment(ALIGN_CENTER, ALIGN_TOP);
+ hbox->pack_start (*image, PACK_EXPAND_WIDGET, 12);
+ hbox->pack_end (message, PACK_EXPAND_PADDING, 12);
+ dialog.get_vbox()->pack_start(*hbox, PACK_EXPAND_PADDING, 6);
+ dialog.add_button (_("Load session anyway"), RESPONSE_ACCEPT);
+ dialog.add_button (_("Do not load session"), RESPONSE_REJECT);
+ dialog.set_default_response (RESPONSE_ACCEPT);
+ dialog.set_position (WIN_POS_CENTER);
+ message.show();
+ image->show();
+ hbox->show();
+
+ switch (dialog.run ()) {
+ case RESPONSE_ACCEPT:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
void
ARDOUR_UI::disconnect_from_jack ()
diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h
index 8417c6c4fd..49e85e149c 100644
--- a/gtk2_ardour/ardour_ui.h
+++ b/gtk2_ardour/ardour_ui.h
@@ -669,6 +669,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI
void disk_underrun_handler ();
int pending_state_dialog ();
+ int sr_mismatch_dialog (nframes_t, nframes_t);
void disconnect_from_jack ();
void reconnect_to_jack ();
diff --git a/gtk2_ardour/ardour_ui_dependents.cc b/gtk2_ardour/ardour_ui_dependents.cc
index 387aa88ac2..f3b8f976e4 100644
--- a/gtk2_ardour/ardour_ui_dependents.cc
+++ b/gtk2_ardour/ardour_ui_dependents.cc
@@ -52,6 +52,7 @@ ARDOUR_UI::shutdown ()
session->remove_pending_capture_state ();
session = 0;
}
+
ui_config->save_state();
}
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index 7de571c7a0..c248c20913 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -1742,12 +1742,25 @@ Editor::add_region_context_items (AudioStreamView* sv, boost::shared_ptr<Region>
items.push_back (CheckMenuElem (_("Lock")));
CheckMenuItem* region_lock_item = static_cast<CheckMenuItem*>(&items.back());
- fooc = region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_lock));
if (region->locked()) {
- fooc.block (true);
region_lock_item->set_active();
- fooc.block (false);
}
+ region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_lock));
+
+ items.push_back (CheckMenuElem (_("Glue to Bars&Beats")));
+ CheckMenuItem* bbt_glue_item = static_cast<CheckMenuItem*>(&items.back());
+
+ switch (region->positional_lock_style()) {
+ case Region::MusicTime:
+ bbt_glue_item->set_active (true);
+ break;
+ default:
+ bbt_glue_item->set_active (true);
+ break;
+ }
+
+ bbt_glue_item->signal_activate().connect (bind (mem_fun (*this, &Editor::set_region_lock_style), Region::MusicTime));
+
items.push_back (CheckMenuElem (_("Mute")));
CheckMenuItem* region_mute_item = static_cast<CheckMenuItem*>(&items.back());
fooc = region_mute_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_mute));
@@ -3140,15 +3153,25 @@ Editor::hide_verbose_canvas_cursor ()
verbose_cursor_visible = false;
}
+double
+Editor::clamp_verbose_cursor_x (double x)
+{
+ return min (horizontal_adjustment.get_value() + canvas_width - 75.0, x);
+}
+
+double
+Editor::clamp_verbose_cursor_y (double y)
+{
+ return min (vertical_adjustment.get_value() + canvas_height - 50.0, y);
+}
+
void
Editor::set_verbose_canvas_cursor (const string & txt, double x, double y)
{
- /* XXX get origin of canvas relative to root window,
- add x and y and check compared to gdk_screen_{width,height}
- */
verbose_canvas_cursor->property_text() = txt.c_str();
- verbose_canvas_cursor->property_x() = x;
- verbose_canvas_cursor->property_y() = y;
+ /* don't get too close to the edge */
+ verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (x);
+ verbose_canvas_cursor->property_y() = clamp_verbose_cursor_x (y);
}
void
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index 2b89163687..1a78921dff 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -952,6 +952,7 @@ class Editor : public PublicEditor
void toggle_region_mute ();
void toggle_region_lock ();
void toggle_region_opaque ();
+ void set_region_lock_style (ARDOUR::Region::PositionLockStyle);
void raise_region ();
void raise_region_to_top ();
void lower_region ();
@@ -960,7 +961,7 @@ class Editor : public PublicEditor
void split_region_at (nframes_t);
void split_regions_at (nframes_t, RegionSelection&);
void split_region_at_transients ();
- void split_region_at_points (boost::shared_ptr<ARDOUR::Region>, std::vector<nframes64_t>&);
+ void split_region_at_points (boost::shared_ptr<ARDOUR::Region>, ARDOUR::AnalysisFeatureList&);
void crop_region_to_selection ();
void crop_region_to (nframes_t start, nframes_t end);
void set_sync_point (nframes64_t, const RegionSelection&);
@@ -1258,6 +1259,8 @@ class Editor : public PublicEditor
void show_verbose_time_cursor (nframes_t frame, double offset = 0, double xpos=-1, double ypos=-1);
void show_verbose_duration_cursor (nframes_t start, nframes_t end, double offset = 0, double xpos=-1, double ypos=-1);
+ double clamp_verbose_cursor_x (double);
+ double clamp_verbose_cursor_y (double);
/* Canvas event handlers */
diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc
index 8ec21850c1..fe0ba2943f 100644
--- a/gtk2_ardour/editor_actions.cc
+++ b/gtk2_ardour/editor_actions.cc
@@ -371,10 +371,9 @@ Editor::register_actions ()
act = ActionManager::register_action (editor_actions, "tab-to-transient-forwards", _("Move Forward to Transient"), bind (mem_fun(*this, &Editor::tab_to_transient), true));
ActionManager::session_sensitive_actions.push_back (act);
- act = ActionManager::register_action (editor_actions, "tab-to-transient-backwards", _("Move Forward to Transient"), bind (mem_fun(*this, &Editor::tab_to_transient), false));
+ act = ActionManager::register_action (editor_actions, "tab-to-transient-backwards", _("Move Backwards to Transient"), bind (mem_fun(*this, &Editor::tab_to_transient), false));
ActionManager::session_sensitive_actions.push_back (act);
-
act = ActionManager::register_action (editor_actions, "crop", _("Crop"), mem_fun(*this, &Editor::crop_region_to_selection));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "insert-chunk", _("Insert Chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f));
diff --git a/gtk2_ardour/editor_canvas_events.cc b/gtk2_ardour/editor_canvas_events.cc
index d0085b685b..2d4531f2d1 100644
--- a/gtk2_ardour/editor_canvas_events.cc
+++ b/gtk2_ardour/editor_canvas_events.cc
@@ -19,6 +19,7 @@
#include <cstdlib>
#include <cmath>
+#include <algorithm>
#include <pbd/stacktrace.h>
@@ -43,6 +44,7 @@
#include "i18n.h"
using namespace sigc;
+using namespace std;
using namespace ARDOUR;
using namespace PBD;
using namespace Gtk;
@@ -245,8 +247,8 @@ bool
Editor::track_canvas_motion (GdkEvent *ev)
{
if (verbose_cursor_visible) {
- verbose_canvas_cursor->property_x() = ev->motion.x + 20;
- verbose_canvas_cursor->property_y() = ev->motion.y + 20;
+ verbose_canvas_cursor->property_x() = clamp_verbose_cursor_x (ev->motion.x + 20);
+ verbose_canvas_cursor->property_y() = clamp_verbose_cursor_y (ev->motion.y + 20);
}
#ifdef GTKOSX
diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc
index 4cec847db7..bcc00739ed 100644
--- a/gtk2_ardour/editor_mouse.cc
+++ b/gtk2_ardour/editor_mouse.cc
@@ -3311,7 +3311,7 @@ Editor::region_drag_motion_callback (ArdourCanvas::Item* item, GdkEvent* event)
}
if (sync_frame - sync_offset <= sync_frame) {
- pending_region_position = sync_frame + (sync_dir*sync_offset);
+ pending_region_position = sync_frame - (sync_dir*sync_offset);
} else {
pending_region_position = 0;
}
diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc
index 2755fcc764..3410c8195d 100644
--- a/gtk2_ardour/editor_ops.cc
+++ b/gtk2_ardour/editor_ops.cc
@@ -54,6 +54,7 @@
#include "ardour_ui.h"
#include "editor.h"
#include "time_axis_view.h"
+#include "route_time_axis.h"
#include "audio_time_axis.h"
#include "automation_time_axis.h"
#include "streamview.h"
@@ -1664,7 +1665,6 @@ Editor::temporal_zoom_region ()
ensure_entered_region_selected (true);
if (selection->regions.empty()) {
- info << _("cannot set loop: no region selected") << endmsg;
return;
}
@@ -3515,13 +3515,11 @@ Editor::cut_copy (CutCopyOp op)
switch (current_mouse_mode()) {
case MouseObject:
- cerr << "cutting in object mode\n";
if (!selection->regions.empty() || !selection->points.empty()) {
begin_reversible_command (opname + _(" objects"));
if (!selection->regions.empty()) {
- cerr << "have regions to cut" << endl;
cut_copy_regions (op);
if (op == Cut) {
@@ -3540,7 +3538,6 @@ Editor::cut_copy (CutCopyOp op)
commit_reversible_command ();
break; // terminate case statement here
}
- cerr << "nope, now cutting time range" << endl;
if (!selection->time.empty()) {
/* don't cause suprises */
break;
@@ -3550,9 +3547,7 @@ Editor::cut_copy (CutCopyOp op)
case MouseRange:
if (selection->time.empty()) {
nframes64_t start, end;
- cerr << "no time selection, get edit op range" << endl;
if (!get_edit_op_range (start, end)) {
- cerr << "no edit op range" << endl;
return;
}
selection->set ((TimeAxisView*) 0, start, end);
@@ -4336,6 +4331,15 @@ Editor::toggle_region_lock ()
}
void
+Editor::set_region_lock_style (Region::PositionLockStyle ps)
+{
+ for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
+ (*i)->region()->set_position_lock_style (ps);
+ }
+}
+
+
+void
Editor::toggle_region_mute ()
{
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) {
@@ -4980,31 +4984,70 @@ void
Editor::define_one_bar (nframes64_t start, nframes64_t end)
{
nframes64_t length = end - start;
-
+
const Meter& m (session->tempo_map().meter_at (start));
- /* region length = 1 bar */
+ /* length = 1 bar */
- /* 1 bar = how many beats per bar */
-
- double beats_per_bar = m.beats_per_bar();
-
/* now we want frames per beat.
we have frames per bar, and beats per bar, so ...
*/
- double frames_per_beat = length / beats_per_bar;
+ double frames_per_beat = length / m.beats_per_bar();
/* beats per minute = */
double beats_per_minute = (session->frame_rate() * 60.0) / frames_per_beat;
+ /* now decide whether to:
+
+ (a) set global tempo
+ (b) add a new tempo marker
+
+ */
+
const TempoSection& t (session->tempo_map().tempo_section_at (start));
+ bool do_global = false;
+
+ if ((session->tempo_map().n_tempos() == 1) && (session->tempo_map().n_meters() == 1)) {
+
+ /* only 1 tempo & 1 meter: ask if the user wants to set the tempo
+ at the start, or create a new marker
+ */
+
+ vector<string> options;
+ options.push_back (_("Set global tempo"));
+ options.push_back (_("Add new marker"));
+ options.push_back (_("Cancel"));
+ Choice c (_("Do you want to set the global tempo or add new tempo marker?"),
+ options);
+
+ switch (c.run()) {
+ case 0:
+ do_global = true;
+ break;
+ case 2:
+ return;
+
+ default:
+ do_global = false;
+ }
+
+ } else {
+
+ /* more than 1 tempo and/or meter section already, go ahead do the "usual":
+ if the marker is at the region starter, change it, otherwise add
+ a new tempo marker
+ */
+ }
+
begin_reversible_command (_("set tempo from region"));
XMLNode& before (session->tempo_map().get_state());
- if (t.frame() == start) {
+ if (do_global) {
+ session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
+ } else if (t.frame() == start) {
session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
} else {
session->tempo_map().add_tempo (Tempo (beats_per_minute, t.note_type()), start);
@@ -5019,7 +5062,7 @@ Editor::define_one_bar (nframes64_t start, nframes64_t end)
void
Editor::split_region_at_transients ()
{
- vector<nframes64_t> positions;
+ AnalysisFeatureList positions;
if (!session) {
return;
@@ -5055,7 +5098,7 @@ Editor::split_region_at_transients ()
}
void
-Editor::split_region_at_points (boost::shared_ptr<Region> r, vector<nframes64_t>& positions)
+Editor::split_region_at_points (boost::shared_ptr<Region> r, AnalysisFeatureList& positions)
{
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
@@ -5073,7 +5116,7 @@ Editor::split_region_at_points (boost::shared_ptr<Region> r, vector<nframes64_t>
return;
}
- vector<nframes64_t>::const_iterator x;
+ AnalysisFeatureList::const_iterator x;
nframes64_t pos = ar->position();
@@ -5130,30 +5173,52 @@ Editor::split_region_at_points (boost::shared_ptr<Region> r, vector<nframes64_t>
void
Editor::tab_to_transient (bool forward)
{
-
- vector<nframes64_t> positions;
+ AnalysisFeatureList positions;
if (!session) {
return;
}
- ExclusiveRegionSelection esr (*this, entered_regionview);
+ nframes64_t pos = session->audible_frame ();
- if (selection->regions.empty()) {
- return;
- }
-
- boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (selection->regions.front()->region());
+ if (!selection->tracks.empty()) {
- if (!ar) {
- return;
+ for (TrackSelection::iterator t = selection->tracks.begin(); t != selection->tracks.end(); ++t) {
+
+ RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*t);
+
+ if (rtv) {
+ boost::shared_ptr<Diskstream> ds = rtv->get_diskstream();
+ if (ds) {
+ boost::shared_ptr<Playlist> pl = rtv->get_diskstream()->playlist ();
+ if (pl) {
+ nframes64_t result = pl->find_next_transient (pos, forward ? 1 : -1);
+
+ if (result >= 0) {
+ positions.push_back (result);
+ }
+ }
+ }
+ }
+ }
+
+ } else {
+
+ ExclusiveRegionSelection esr (*this, entered_regionview);
+
+ if (selection->regions.empty()) {
+ return;
+ }
+
+ for (RegionSelection::iterator r = selection->regions.begin(); r != selection->regions.end(); ++r) {
+ (*r)->region()->get_transients (positions);
+ }
}
- ar->get_transients (positions);
- nframes64_t pos = session->audible_frame ();
+ TransientDetector::cleanup_transients (positions, session->frame_rate(), 3.0);
if (forward) {
- vector<nframes64_t>::iterator x;
+ AnalysisFeatureList::iterator x;
for (x = positions.begin(); x != positions.end(); ++x) {
if ((*x) > pos) {
@@ -5166,7 +5231,7 @@ Editor::tab_to_transient (bool forward)
}
} else {
- vector<nframes64_t>::reverse_iterator x;
+ AnalysisFeatureList::reverse_iterator x;
for (x = positions.rbegin(); x != positions.rend(); ++x) {
if ((*x) < pos) {
diff --git a/gtk2_ardour/editor_tempodisplay.cc b/gtk2_ardour/editor_tempodisplay.cc
index b01154d69e..8bb19e358b 100644
--- a/gtk2_ardour/editor_tempodisplay.cc
+++ b/gtk2_ardour/editor_tempodisplay.cc
@@ -438,10 +438,14 @@ Editor::edit_tempo_section (TempoSection* section)
tempo_dialog.get_bbt_time(when);
bpm = max (0.01, bpm);
+ cerr << "Editing tempo section to be at " << when << endl;
+ session->tempo_map().dump (cerr);
begin_reversible_command (_("replace tempo mark"));
XMLNode &before = session->tempo_map().get_state();
session->tempo_map().replace_tempo (*section, Tempo (bpm,nt));
+ session->tempo_map().dump (cerr);
session->tempo_map().move_tempo (*section, when);
+ session->tempo_map().dump (cerr);
XMLNode &after = session->tempo_map().get_state();
session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
commit_reversible_command ();
diff --git a/gtk2_ardour/new_session_dialog.cc b/gtk2_ardour/new_session_dialog.cc
index 9d1805a7c4..41dc429cf8 100644
--- a/gtk2_ardour/new_session_dialog.cc
+++ b/gtk2_ardour/new_session_dialog.cc
@@ -993,6 +993,12 @@ NewSessionDialog::reset_recent()
fullpath = fullpath.substr (0, fullpath.length()-1);
}
+ /* check whether session still exists */
+ if (!Glib::file_test(fullpath, Glib::FILE_TEST_EXISTS)) {
+ /* session doesn't exist */
+ continue;
+ }
+
/* now get available states for this session */
if ((states = ARDOUR::Session::possible_states (fullpath)) == 0) {
diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h
index cf03942e13..4d045dbd62 100644
--- a/gtk2_ardour/public_editor.h
+++ b/gtk2_ardour/public_editor.h
@@ -157,7 +157,7 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway
virtual void restore_editing_space() = 0;
virtual nframes64_t get_preferred_edit_position (bool ignore_playhead = false) = 0;
virtual void toggle_meter_updating() = 0;
- virtual void split_region_at_points (boost::shared_ptr<ARDOUR::Region>, std::vector<nframes64_t>&) = 0;
+ virtual void split_region_at_points (boost::shared_ptr<ARDOUR::Region>, ARDOUR::AnalysisFeatureList&) = 0;
sigc::signal<void> ZoomFocusChanged;
sigc::signal<void> ZoomChanged;
diff --git a/gtk2_ardour/rhythm_ferret.cc b/gtk2_ardour/rhythm_ferret.cc
index 4cdb408076..cb24e97f43 100644
--- a/gtk2_ardour/rhythm_ferret.cc
+++ b/gtk2_ardour/rhythm_ferret.cc
@@ -194,14 +194,14 @@ RhythmFerret::run_analysis ()
}
int
-RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr<Readable> readable, nframes64_t offset, vector<nframes64_t>& results)
+RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr<Readable> readable, nframes64_t offset, AnalysisFeatureList& results)
{
TransientDetector t (session->frame_rate());
bool existing_results = !results.empty();
for (uint32_t i = 0; i < readable->n_channels(); ++i) {
- vector<nframes64_t> these_results;
+ AnalysisFeatureList these_results;
t.reset ();
t.set_threshold (detection_threshold_adjustment.get_value());
@@ -213,38 +213,18 @@ RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr<Readable> readabl
/* translate all transients to give absolute position */
- for (vector<nframes64_t>::iterator i = these_results.begin(); i != these_results.end(); ++i) {
- (*i) += offset;
+ for (AnalysisFeatureList::iterator x = these_results.begin(); x != these_results.end(); ++x) {
+ (*x) += offset;
}
/* merge */
results.insert (results.end(), these_results.begin(), these_results.end());
+ these_results.clear ();
}
-
- if (!results.empty()) {
-
- /* now resort to bring transients from different channels together */
-
- sort (results.begin(), results.end());
-
- /* remove duplicates or other things that are too close */
-
- vector<nframes64_t>::iterator i = results.begin();
- nframes64_t curr = (*i);
- nframes64_t gap_frames = (nframes64_t) floor (trigger_gap_adjustment.get_value() * (session->frame_rate() / 1000.0));
-
- ++i;
-
- while (i != results.end()) {
- if (((*i) == curr) || (((*i) - curr) < gap_frames)) {
- i = results.erase (i);
- } else {
- ++i;
- curr = *i;
- }
- }
+ if (!results.empty()) {
+ TransientDetector::cleanup_transients (results, session->frame_rate(), trigger_gap_adjustment.get_value());
}
return 0;
diff --git a/gtk2_ardour/rhythm_ferret.h b/gtk2_ardour/rhythm_ferret.h
index 36d4450939..891b447a11 100644
--- a/gtk2_ardour/rhythm_ferret.h
+++ b/gtk2_ardour/rhythm_ferret.h
@@ -84,17 +84,17 @@ class RhythmFerret : public ArdourDialog {
std::vector<std::string> analysis_mode_strings;
- std::vector<nframes64_t> current_results;
+ ARDOUR::AnalysisFeatureList current_results;
AnalysisMode get_analysis_mode () const;
Action get_action() const;
void run_analysis ();
- int run_percussion_onset_analysis (boost::shared_ptr<ARDOUR::Readable> region, nframes64_t offset, std::vector<nframes64_t>& results);
+ int run_percussion_onset_analysis (boost::shared_ptr<ARDOUR::Readable> region, nframes64_t offset, ARDOUR::AnalysisFeatureList& results);
void do_action ();
void do_split_action ();
- void do_region_split (RegionView* rv, const std::vector<nframes64_t>&);
+ void do_region_split (RegionView* rv, const ARDOUR::AnalysisFeatureList&);
};
#endif /* __gtk2_ardour_rhythm_ferret_h__ */
diff --git a/gtk2_ardour/splash.cc b/gtk2_ardour/splash.cc
index 4221785765..7f68f85216 100644
--- a/gtk2_ardour/splash.cc
+++ b/gtk2_ardour/splash.cc
@@ -12,6 +12,8 @@ using namespace Glib;
using namespace std;
using namespace ARDOUR;
+Splash* Splash::the_splash = 0;
+
Splash::Splash ()
{
string path = find_data_file ("splash.png");
@@ -45,6 +47,14 @@ Splash::Splash ()
darea.signal_expose_event().connect (mem_fun (*this, &Splash::expose));
add (darea);
+
+ the_splash = this;
+}
+
+void
+Splash::pop_back ()
+{
+ set_keep_above (false);
}
void
diff --git a/gtk2_ardour/splash.h b/gtk2_ardour/splash.h
index 5ba5478941..07532d9344 100644
--- a/gtk2_ardour/splash.h
+++ b/gtk2_ardour/splash.h
@@ -34,13 +34,19 @@ class Splash : public Gtk::Window
Splash ();
~Splash () {}
+ static Splash* instance() { return the_splash; }
+
+ void pop_back ();
+
bool expose (GdkEventExpose*);
bool on_button_release_event (GdkEventButton*);
void on_realize ();
-
+
void message (const std::string& msg);
private:
+ static Splash* the_splash;
+
Glib::RefPtr<Gdk::Pixbuf> pixbuf;
Gtk::DrawingArea darea;
Glib::RefPtr<Pango::Layout> layout;
diff --git a/gtk2_ardour/tempo_dialog.cc b/gtk2_ardour/tempo_dialog.cc
index 0c99a4eeb0..6f46ec1a9d 100644
--- a/gtk2_ardour/tempo_dialog.cc
+++ b/gtk2_ardour/tempo_dialog.cc
@@ -243,7 +243,6 @@ TempoDialog::get_note_type ()
}
}
- cerr << "returning " << note_type << " based on " << text << endl;
return note_type;
}
diff --git a/gtk2_ardour/time_axis_view.cc b/gtk2_ardour/time_axis_view.cc
index 96db5b3ada..5cc44f1712 100644
--- a/gtk2_ardour/time_axis_view.cc
+++ b/gtk2_ardour/time_axis_view.cc
@@ -1102,7 +1102,7 @@ TimeAxisView::covers_y_position (double y)
}
void
-TimeAxisView::show_temporary_lines (const vector<nframes64_t>& pos)
+TimeAxisView::show_temporary_lines (const AnalysisFeatureList& pos)
{
while (temp_lines.size()< pos.size()) {
ArdourCanvas::SimpleLine* l = new ArdourCanvas::SimpleLine (*canvas_display);
@@ -1118,7 +1118,7 @@ TimeAxisView::show_temporary_lines (const vector<nframes64_t>& pos)
delete line;
}
- vector<nframes64_t>::const_iterator i;
+ AnalysisFeatureList::const_iterator i;
list<ArdourCanvas::SimpleLine*>::iterator l;
for (i = pos.begin(), l = temp_lines.begin(); i != pos.end() && l != temp_lines.end(); ++i, ++l) {
diff --git a/gtk2_ardour/time_axis_view.h b/gtk2_ardour/time_axis_view.h
index c98a72c626..27d6ba632a 100644
--- a/gtk2_ardour/time_axis_view.h
+++ b/gtk2_ardour/time_axis_view.h
@@ -176,7 +176,7 @@ class TimeAxisView : public virtual AxisView
virtual ARDOUR::RouteGroup* edit_group() const { return 0; }
virtual boost::shared_ptr<ARDOUR::Playlist> playlist() const { return boost::shared_ptr<ARDOUR::Playlist> (); }
- virtual void show_temporary_lines (const std::vector<nframes64_t>&);
+ virtual void show_temporary_lines (const ARDOUR::AnalysisFeatureList&);
virtual void hide_temporary_lines ();
virtual void set_samples_per_unit (double);
diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc
index 5395fedc2d..d0e85a4fa0 100644
--- a/gtk2_ardour/utils.cc
+++ b/gtk2_ardour/utils.cc
@@ -463,6 +463,16 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
int ret = false;
switch (ev->keyval) {
+ case GDK_Tab:
+ ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_nabla, GdkModifierType(ev->state));
+ break;
+
+ // some X and/or GDK implementations do Shift-Tab -> GDK_ISO_Left_Tab
+
+ case GDK_ISO_Left_Tab:
+ ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_nabla, GdkModifierType(ev->state));
+ break;
+
case GDK_Up:
ret = gtk_accel_groups_activate(G_OBJECT(win), GDK_uparrow, GdkModifierType(ev->state));
break;
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
index 5b4f0cda87..c4f8eece0f 100644
--- a/libs/ardour/SConscript
+++ b/libs/ardour/SConscript
@@ -28,6 +28,7 @@ ardour.Append(CPPPATH = '#libs/surfaces/control_protocol')
ardour_files=Split("""
diskstream.cc
+analyser.cc
audioanalyser.cc
audio_diskstream.cc
audio_library.cc
diff --git a/libs/ardour/analyser.cc b/libs/ardour/analyser.cc
new file mode 100644
index 0000000000..7ddb5428e9
--- /dev/null
+++ b/libs/ardour/analyser.cc
@@ -0,0 +1,119 @@
+/*
+ Copyright (C) 2008 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 <ardour/analyser.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/transient_detector.h>
+
+#include <pbd/pthread_utils.h>
+#include <pbd/convert.h>
+
+using namespace std;
+using namespace sigc;
+using namespace ARDOUR;
+using namespace PBD;
+
+Analyser* Analyser::the_analyser = 0;
+Glib::StaticMutex Analyser::analysis_queue_lock = GLIBMM_STATIC_MUTEX_INIT;
+Glib::Cond* Analyser::SourcesToAnalyse = 0;
+list<boost::weak_ptr<Source> > Analyser::analysis_queue;
+
+Analyser::Analyser ()
+{
+
+}
+
+Analyser::~Analyser ()
+{
+}
+
+static void
+analyser_work ()
+{
+ Analyser::work ();
+}
+
+void
+Analyser::init ()
+{
+ SourcesToAnalyse = new Glib::Cond();
+ Glib::Thread::create (sigc::ptr_fun (analyser_work), false);
+}
+
+void
+Analyser::queue_source_for_analysis (boost::shared_ptr<Source> src, bool force)
+{
+ if (!src->can_be_analysed()) {
+ return;
+ }
+
+ if (!force && src->has_been_analysed()) {
+ return;
+ }
+
+ Glib::Mutex::Lock lm (analysis_queue_lock);
+ analysis_queue.push_back (boost::weak_ptr<Source>(src));
+ SourcesToAnalyse->broadcast ();
+}
+
+void
+Analyser::work ()
+{
+ PBD::ThreadCreated (pthread_self(), string ("analyser-") + to_string (pthread_self(), std::dec));
+
+ while (true) {
+ analysis_queue_lock.lock ();
+
+ wait:
+ if (analysis_queue.empty()) {
+ SourcesToAnalyse->wait (analysis_queue_lock);
+ }
+
+ if (analysis_queue.empty()) {
+ goto wait;
+ }
+
+ boost::shared_ptr<Source> src (analysis_queue.front().lock());
+ analysis_queue.pop_front();
+ analysis_queue_lock.unlock ();
+
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
+
+ if (afs) {
+ analyse_audio_file_source (afs);
+ }
+ }
+}
+
+void
+Analyser::analyse_audio_file_source (boost::shared_ptr<AudioFileSource> src)
+{
+ AnalysisFeatureList results;
+
+ TransientDetector td (src->sample_rate());
+
+ if (td.run (src->get_transients_path(), src.get(), 0, results) == 0) {
+ src->set_been_analysed (true);
+ } else {
+ src->set_been_analysed (false);
+ }
+
+}
+
+
diff --git a/libs/ardour/ardour/analyser.h b/libs/ardour/ardour/analyser.h
new file mode 100644
index 0000000000..8771cab6b0
--- /dev/null
+++ b/libs/ardour/ardour/analyser.h
@@ -0,0 +1,35 @@
+#ifndef __ardour_analyser_h__
+#define __ardour_analyser_h__
+
+#include <glibmm/thread.h>
+#include <boost/shared_ptr.hpp>
+
+namespace ARDOUR {
+
+class AudioFileSource;
+class Source;
+class TransientDetector;
+
+class Analyser {
+
+ public:
+ Analyser();
+ ~Analyser ();
+
+ static void init ();
+ static void queue_source_for_analysis (boost::shared_ptr<Source>, bool force);
+ static void work ();
+
+ private:
+ static Analyser* the_analyser;
+ static Glib::StaticMutex analysis_queue_lock;
+ static Glib::Cond* SourcesToAnalyse;
+ static std::list<boost::weak_ptr<Source> > analysis_queue;
+
+ static void analyse_audio_file_source (boost::shared_ptr<AudioFileSource>);
+};
+
+
+}
+
+#endif /* __ardour_analyser_h__ */
diff --git a/libs/ardour/ardour/audioanalyser.h b/libs/ardour/ardour/audioanalyser.h
index dbd8a52d5a..06b841990a 100644
--- a/libs/ardour/ardour/audioanalyser.h
+++ b/libs/ardour/ardour/audioanalyser.h
@@ -40,7 +40,7 @@ class AudioAnalyser {
AudioAnalyser (float sample_rate, AnalysisPluginKey key);
virtual ~AudioAnalyser();
-
+
/* analysis object should provide a run method
that accepts a path to write the results to (optionally empty)
a Readable* to read data from
diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h
index 22359d9149..c339c1cfc6 100644
--- a/libs/ardour/ardour/audiofilesource.h
+++ b/libs/ardour/ardour/audiofilesource.h
@@ -123,6 +123,8 @@ class AudioFileSource : public AudioSource {
virtual void handle_header_position_change () {}
+ bool can_be_analysed() const { return _length > 0; }
+
protected:
/* constructor to be called for existing external-to-session files */
diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h
index ba14d05afe..c5d8618a1c 100644
--- a/libs/ardour/ardour/audioregion.h
+++ b/libs/ardour/ardour/audioregion.h
@@ -148,7 +148,7 @@ class AudioRegion : public Region
void set_playlist (boost::weak_ptr<Playlist>);
- int get_transients (std::vector<nframes64_t>&, bool force_new = false);
+ int get_transients (AnalysisFeatureList&, bool force_new = false);
private:
friend class RegionFactory;
@@ -188,6 +188,7 @@ class AudioRegion : public Region
void fade_out_changed ();
void source_offset_changed ();
void listen_to_my_curves ();
+ void listen_to_my_sources ();
void source_deleted ();
@@ -207,11 +208,6 @@ class AudioRegion : public Region
protected:
int set_live_state (const XMLNode&, Change&, bool send);
-
- std::vector<nframes64_t> _transients;
- bool valid_transients;
- void invalidate_transients ();
- void cleanup_transients (std::vector<nframes64_t>&);
};
} /* namespace ARDOUR */
diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h
index e8ef3a0fa2..20acd9fcd8 100644
--- a/libs/ardour/ardour/audiosource.h
+++ b/libs/ardour/ardour/audiosource.h
@@ -122,19 +122,23 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
int prepare_for_peakfile_writes ();
void done_with_peakfile_writes (bool done = true);
- std::vector<nframes64_t> transients;
+ AnalysisFeatureList transients;
std::string get_transients_path() const;
+ void set_been_analysed (bool yn);
+ bool check_for_analysis_data_on_disk ();
+
protected:
static bool _build_missing_peakfiles;
static bool _build_peakfiles;
bool _peaks_built;
+ bool _analysed;
mutable Glib::Mutex _lock;
mutable Glib::Mutex _peaks_ready_lock;
nframes_t _length;
- Glib::ustring peakpath;
- Glib::ustring _captured_for;
+ Glib::ustring peakpath;
+ Glib::ustring _captured_for;
mutable uint32_t _read_data_count; // modified in read()
mutable uint32_t _write_data_count; // modified in write()
diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h
index 81d04abfe3..cb045091ef 100644
--- a/libs/ardour/ardour/configuration_vars.h
+++ b/libs/ardour/ardour/configuration_vars.h
@@ -53,6 +53,7 @@ CONFIG_VARIABLE (float, track_buffer_seconds, "track-buffer-seconds", 5.0)
CONFIG_VARIABLE (uint32_t, disk_choice_space_threshold, "disk-choice-space-threshold", 57600000)
CONFIG_VARIABLE (SampleFormat, native_file_data_format, "native-file-data-format", ARDOUR::FormatFloat)
CONFIG_VARIABLE (HeaderFormat, native_file_header_format, "native-file-header-format", ARDOUR::WAVE)
+CONFIG_VARIABLE (bool, auto_analyse_audio, "auto-analyse-audio", true)
/* OSC */
@@ -118,6 +119,7 @@ CONFIG_VARIABLE (bool, quieten_at_speed, "quieten-at-speed", true)
CONFIG_VARIABLE (bool, primary_clock_delta_edit_cursor, "primary-clock-delta-edit-cursor", false)
CONFIG_VARIABLE (bool, secondary_clock_delta_edit_cursor, "secondary-clock-delta-edit-cursor", false)
CONFIG_VARIABLE (bool, show_track_meters, "show-track-meters", true)
+
/* timecode and sync */
CONFIG_VARIABLE (bool, jack_time_master, "jack-time-master", true)
diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h
index 402e02109a..c80330c04a 100644
--- a/libs/ardour/ardour/playlist.h
+++ b/libs/ardour/ardour/playlist.h
@@ -93,6 +93,7 @@ class Playlist : public PBD::StatefulDestructible, public boost::enable_shared_f
void duplicate (boost::shared_ptr<Region>, nframes_t position, float times);
void nudge_after (nframes_t start, nframes_t distance, bool forwards);
void shuffle (boost::shared_ptr<Region>, int dir);
+ void update_after_tempo_map_change ();
boost::shared_ptr<Playlist> cut (list<AudioRange>&, bool result_is_hidden = true);
boost::shared_ptr<Playlist> copy (list<AudioRange>&, bool result_is_hidden = true);
@@ -107,6 +108,8 @@ class Playlist : public PBD::StatefulDestructible, public boost::enable_shared_f
nframes64_t find_next_region_boundary (nframes64_t frame, int dir);
bool region_is_shuffle_constrained (boost::shared_ptr<Region>);
+ nframes64_t find_next_transient (nframes64_t position, int dir);
+
template<class T> void foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>, void *), void *arg);
template<class T> void foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>));
diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h
index e75fb43acc..19600f3db6 100644
--- a/libs/ardour/ardour/region.h
+++ b/libs/ardour/ardour/region.h
@@ -67,6 +67,11 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost::
range_guarantoor = USHRT_MAX
};
+ enum PositionLockStyle {
+ AudioTime,
+ MusicTime
+ };
+
static const Flag DefaultFlags = Flag (Opaque|DefaultFadeIn|DefaultFadeOut|FadeIn|FadeOut);
static Change FadeChanged;
@@ -121,6 +126,9 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost::
bool whole_file() const { return _flags & WholeFile ; }
Flag flags() const { return _flags; }
+ PositionLockStyle positional_lock_style() const { return _positional_lock_style; }
+ void set_position_lock_style (PositionLockStyle ps);
+
virtual bool should_save_state () const { return !(_flags & DoNotSaveState); };
void freeze ();
@@ -149,6 +157,7 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost::
void set_position (nframes_t, void *src);
void set_position_on_top (nframes_t, void *src);
void special_set_position (nframes_t);
+ void update_position_after_tempo_map_change ();
void nudge_position (nframes64_t, void *src);
bool at_natural_position () const;
@@ -189,6 +198,13 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost::
uint64_t last_layer_op() const { return _last_layer_op; }
void set_last_layer_op (uint64_t when);
+ virtual int get_transients (AnalysisFeatureList&, bool force_new = false) {
+ // no transients, but its OK
+ return 0;
+ }
+
+ void invalidate_transients ();
+
protected:
friend class RegionFactory;
@@ -198,13 +214,13 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost::
Region (boost::shared_ptr<const Region>);
Region (const XMLNode&);
-
protected:
XMLNode& get_short_state (); /* used only by Session */
void send_change (Change);
void trim_to_internal (nframes_t position, nframes_t length, void *src);
+ void set_position_internal (nframes_t pos, bool allow_bbt_recompute);
bool copied() const { return _flags & Copied; }
void maybe_uncopy ();
@@ -217,27 +233,30 @@ class Region : public PBD::StatefulDestructible, public Readable, public boost::
virtual void recompute_at_start () = 0;
virtual void recompute_at_end () = 0;
-
- nframes_t _start;
- nframes_t _length;
- nframes_t _last_length;
- nframes_t _position;
- nframes_t _last_position;
- Flag _flags;
- nframes_t _sync_position;
- layer_t _layer;
- string _name;
- mutable RegionEditState _first_edit;
- int _frozen;
- Glib::Mutex lock;
+ nframes_t _start;
+ nframes_t _length;
+ nframes_t _last_length;
+ nframes_t _position;
+ nframes_t _last_position;
+ Flag _flags;
+ PositionLockStyle _positional_lock_style;
+ nframes_t _sync_position;
+ layer_t _layer;
+ string _name;
+ mutable RegionEditState _first_edit;
+ int _frozen;
+ Glib::Mutex lock;
boost::weak_ptr<ARDOUR::Playlist> _playlist;
- mutable uint32_t _read_data_count; // modified in read()
- Change pending_changed;
- uint64_t _last_layer_op; // timestamp
- nframes64_t _ancestral_start;
- nframes64_t _ancestral_length;
- float _stretch;
- float _shift;
+ mutable uint32_t _read_data_count; // modified in read()
+ Change pending_changed;
+ uint64_t _last_layer_op; // timestamp
+ nframes64_t _ancestral_start;
+ nframes64_t _ancestral_length;
+ float _stretch;
+ float _shift;
+ BBT_Time _bbt_time;
+ AnalysisFeatureList _transients;
+ bool valid_transients;
};
} /* namespace ARDOUR */
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
index acb9f5f53f..c61137b4f3 100644
--- a/libs/ardour/ardour/session.h
+++ b/libs/ardour/ardour/session.h
@@ -381,7 +381,10 @@ class Session : public PBD::StatefulDestructible
nframes_t get_maximum_extent () const;
nframes_t current_end_frame() const { return end_location->start(); }
nframes_t current_start_frame() const { return start_location->start(); }
+ // "actual" sample rate of session, set by current audioengine rate, pullup/down etc.
nframes_t frame_rate() const { return _current_frame_rate; }
+ // "native" sample rate of session, regardless of current audioengine rate, pullup/down etc
+ nframes_t nominal_frame_rate() const { return _nominal_frame_rate; }
nframes_t frames_per_hour() const { return _frames_per_hour; }
double frames_per_smpte_frame() const { return _frames_per_smpte_frame; }
@@ -615,6 +618,12 @@ class Session : public PBD::StatefulDestructible
sigc::signal<int,boost::shared_ptr<ARDOUR::Playlist> > AskAboutPlaylistDeletion;
+ /* handlers should return 0 for "ignore the rate mismatch"
+ and !0 for "do not use this session"
+ */
+
+ static sigc::signal<int,nframes_t, nframes_t> AskAboutSampleRateMismatch;
+
/* handlers should return !0 for use pending state, 0 for
ignore it.
*/
@@ -983,6 +992,7 @@ class Session : public PBD::StatefulDestructible
bool waiting_for_sync_offset;
nframes_t _base_frame_rate;
nframes_t _current_frame_rate; //this includes video pullup offset
+ nframes_t _nominal_frame_rate; //ignores audioengine setting, "native" SR
int transport_sub_state;
mutable gint _record_status;
volatile nframes_t _transport_frame;
diff --git a/libs/ardour/ardour/silentfilesource.h b/libs/ardour/ardour/silentfilesource.h
index e0103185c2..87065c1cd0 100644
--- a/libs/ardour/ardour/silentfilesource.h
+++ b/libs/ardour/ardour/silentfilesource.h
@@ -35,6 +35,7 @@ class SilentFileSource : public AudioFileSource {
void set_length (nframes_t len);
bool destructive() const { return false; }
+ bool can_be_analysed() const { return false; }
protected:
diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h
index 6e6561f22f..aa9931eae3 100644
--- a/libs/ardour/ardour/source.h
+++ b/libs/ardour/ardour/source.h
@@ -59,12 +59,21 @@ class Source : public PBD::StatefulDestructible, public ARDOUR::Readable
uint32_t used() const;
- protected:
- Session& _session;
- string _name;
- time_t _timestamp;
+ bool has_been_analysed() const;
+ virtual bool can_be_analysed() const { return false; }
+ virtual void set_been_analysed (bool yn);
+ virtual bool check_for_analysis_data_on_disk () { return false; }
+
+ sigc::signal<void> AnalysisChanged;
- Glib::Mutex playlist_lock;
+ protected:
+ Session& _session;
+ string _name;
+ time_t _timestamp;
+ bool _analysed;
+ mutable Glib::Mutex _analysis_lock;
+ Glib::Mutex playlist_lock;
+
typedef std::map<boost::shared_ptr<ARDOUR::Playlist>, uint32_t > PlaylistMap;
PlaylistMap _playlists;
diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h
index fa1d882ab3..c4915072c5 100644
--- a/libs/ardour/ardour/tempo.h
+++ b/libs/ardour/ardour/tempo.h
@@ -203,7 +203,7 @@ class TempoMap : public PBD::StatefulDestructible
BBTPointList *get_points (nframes_t start, nframes_t end) const;
- void bbt_time (nframes_t when, BBT_Time&) const;
+ void bbt_time (nframes_t when, BBT_Time&) const;
nframes_t frame_time (const BBT_Time&) const;
nframes_t bbt_duration_at (nframes_t, const BBT_Time&, int dir) const;
@@ -279,6 +279,10 @@ class TempoMap : public PBD::StatefulDestructible
void bbt_time_with_metric (nframes_t, BBT_Time&, const Metric&) const;
void change_existing_tempo_at (nframes_t, double bpm, double note_type);
+ void change_initial_tempo (double bpm, double note_type);
+
+ int n_tempos () const;
+ int n_meters () const;
sigc::signal<void,ARDOUR::Change> StateChanged;
@@ -286,12 +290,12 @@ class TempoMap : public PBD::StatefulDestructible
static Tempo _default_tempo;
static Meter _default_meter;
- Metrics *metrics;
- nframes_t _frame_rate;
- nframes_t last_bbt_when;
- bool last_bbt_valid;
- BBT_Time last_bbt;
- mutable Glib::RWLock lock;
+ Metrics* metrics;
+ nframes_t _frame_rate;
+ nframes_t last_bbt_when;
+ bool last_bbt_valid;
+ BBT_Time last_bbt;
+ mutable Glib::RWLock lock;
void timestamp_metrics (bool use_bbt);
diff --git a/libs/ardour/ardour/transient_detector.h b/libs/ardour/ardour/transient_detector.h
index c65bae3ed5..259b79176f 100644
--- a/libs/ardour/ardour/transient_detector.h
+++ b/libs/ardour/ardour/transient_detector.h
@@ -34,17 +34,23 @@ class TransientDetector : public AudioAnalyser
TransientDetector (float sample_rate);
~TransientDetector();
+ static std::string operational_identifier();
+
void set_threshold (float);
void set_sensitivity (float);
float get_threshold () const;
float get_sensitivity () const;
- int run (const std::string& path, Readable*, uint32_t channel, std::vector<nframes64_t>& results);
+ int run (const std::string& path, Readable*, uint32_t channel, AnalysisFeatureList& results);
- protected:
- std::vector<nframes64_t>* current_results;
+ static void cleanup_transients (AnalysisFeatureList&, float sr, float gap_msecs);
+
+ protected:
+ AnalysisFeatureList* current_results;
int use_features (Vamp::Plugin::FeatureSet&, std::ostream*);
+
+ static std::string _op_id;
};
} /* namespace */
diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h
index 933ca44e73..5c88acfcad 100644
--- a/libs/ardour/ardour/types.h
+++ b/libs/ardour/ardour/types.h
@@ -380,6 +380,8 @@ namespace ARDOUR {
int opts; // really RubberBandStretcher::Options
};
+ typedef std::list<nframes64_t> AnalysisFeatureList;
+
} // namespace ARDOUR
std::istream& operator>>(std::istream& o, ARDOUR::SampleFormat& sf);
diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc
index 8b02af2101..b44753ee96 100644
--- a/libs/ardour/audio_diskstream.cc
+++ b/libs/ardour/audio_diskstream.cc
@@ -40,6 +40,7 @@
#include <ardour/ardour.h>
#include <ardour/audioengine.h>
+#include <ardour/analyser.h>
#include <ardour/audio_diskstream.h>
#include <ardour/utils.h>
#include <ardour/configuration.h>
@@ -1597,6 +1598,7 @@ AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_ca
s->update_header (capture_info.front()->start, when, twhen);
s->set_captured_for (_name);
s->mark_immutable ();
+ Analyser::queue_source_for_analysis (s, true);
}
}
diff --git a/libs/ardour/audioanalyser.cc b/libs/ardour/audioanalyser.cc
index 4cc99a5d5e..9da404e5bf 100644
--- a/libs/ardour/audioanalyser.cc
+++ b/libs/ardour/audioanalyser.cc
@@ -18,13 +18,19 @@ using namespace ARDOUR;
AudioAnalyser::AudioAnalyser (float sr, AnalysisPluginKey key)
: sample_rate (sr)
- , plugin (0)
, plugin_key (key)
{
+ /* create VAMP plugin and initialize */
+
+ if (initialize_plugin (plugin_key, sample_rate)) {
+ error << string_compose (_("cannot load VAMP plugin \"%1\""), key) << endmsg;
+ throw failed_constructor();
+ }
}
AudioAnalyser::~AudioAnalyser ()
{
+ delete plugin;
}
int
@@ -73,29 +79,28 @@ int
AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel)
{
ofstream ofile;
- Plugin::FeatureSet onsets;
+ Plugin::FeatureSet features;
int ret = -1;
bool done = false;
Sample* data = 0;
nframes64_t len = src->readable_length();
nframes64_t pos = 0;
float* bufs[1] = { 0 };
+ string tmp_path;
if (!path.empty()) {
- ofile.open (path.c_str());
+
+ /* store data in tmp file, not the real one */
+
+ tmp_path = path;
+ tmp_path += ".tmp";
+
+ ofile.open (tmp_path.c_str());
if (!ofile) {
goto out;
}
}
- /* create VAMP percussion onset plugin and initialize */
-
- if (plugin == 0) {
- if (initialize_plugin (plugin_key, sample_rate)) {
- goto out;
- }
- }
-
data = new Sample[bufsize];
bufs[0] = data;
@@ -108,7 +113,6 @@ AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel)
to_read = min ((len - pos), bufsize);
if (src->read (data, pos, to_read, channel) != to_read) {
- cerr << "bad read\n";
goto out;
}
@@ -118,14 +122,14 @@ AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel)
memset (data + to_read, 0, (bufsize - to_read));
}
- onsets = plugin->process (bufs, RealTime::fromSeconds ((double) pos / sample_rate));
+ features = plugin->process (bufs, RealTime::fromSeconds ((double) pos / sample_rate));
- if (use_features (onsets, (path.empty() ? &ofile : 0))) {
+ if (use_features (features, (path.empty() ? 0 : &ofile))) {
goto out;
}
pos += stepsize;
-
+
if (pos >= len) {
done = true;
}
@@ -133,9 +137,9 @@ AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel)
/* finish up VAMP plugin */
- onsets = plugin->getRemainingFeatures ();
+ features = plugin->getRemainingFeatures ();
- if (use_features (onsets, (path.empty() ? &ofile : 0))) {
+ if (use_features (features, (path.empty() ? &ofile : 0))) {
goto out;
}
@@ -146,10 +150,14 @@ AudioAnalyser::analyse (const string& path, Readable* src, uint32_t channel)
ofile.close ();
if (ret) {
- g_remove (path.c_str());
+ g_remove (tmp_path.c_str());
+ } else if (!path.empty()) {
+ /* move the data file to the requested path */
+ g_rename (tmp_path.c_str(), path.c_str());
}
+
if (data) {
- delete data;
+ delete [] data;
}
return ret;
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
index 2337e51481..4c2a7a2f63 100644
--- a/libs/ardour/audioengine.cc
+++ b/libs/ardour/audioengine.cc
@@ -797,13 +797,16 @@ void
AudioEngine::halted (void *arg)
{
AudioEngine* ae = static_cast<AudioEngine *> (arg);
+ bool was_running = ae->_running;
ae->_running = false;
ae->_buffer_size = 0;
ae->_frame_rate = 0;
ae->_jack = 0;
- ae->Halted(); /* EMIT SIGNAL */
+ if (was_running) {
+ ae->Halted(); /* EMIT SIGNAL */
+ }
}
uint32_t
diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc
index fd39e05636..4f9e70e334 100644
--- a/libs/ardour/audioregion.cc
+++ b/libs/ardour/audioregion.cc
@@ -80,12 +80,12 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
}
_scale_amplitude = 1.0;
- valid_transients = false;
set_default_fades ();
set_default_envelope ();
listen_to_my_curves ();
+ listen_to_my_sources ();
}
AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
@@ -106,12 +106,12 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
}
_scale_amplitude = 1.0;
- valid_transients = false;
set_default_fades ();
set_default_envelope ();
listen_to_my_curves ();
+ listen_to_my_sources ();
}
AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
@@ -134,12 +134,12 @@ AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t len
}
_scale_amplitude = 1.0;
- valid_transients = false;
set_default_fades ();
set_default_envelope ();
listen_to_my_curves ();
+ listen_to_my_sources ();
}
@@ -203,9 +203,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t
}
_scale_amplitude = other->_scale_amplitude;
- valid_transients = false;
listen_to_my_curves ();
+ listen_to_my_sources ();
}
AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
@@ -241,13 +241,13 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
}
_scale_amplitude = other->_scale_amplitude;
- valid_transients = false;
_envelope = other->_envelope;
_fade_in_disabled = 0;
_fade_out_disabled = 0;
listen_to_my_curves ();
+ listen_to_my_sources ();
}
AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
@@ -266,13 +266,13 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod
}
set_default_fades ();
- valid_transients = false;
if (set_state (node)) {
throw failed_constructor();
}
listen_to_my_curves ();
+ listen_to_my_sources ();
}
AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
@@ -307,13 +307,13 @@ AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
set_default_fades ();
_scale_amplitude = 1.0;
- valid_transients = false;
if (set_state (node)) {
throw failed_constructor();
}
listen_to_my_curves ();
+ listen_to_my_sources ();
}
AudioRegion::~AudioRegion ()
@@ -331,10 +331,11 @@ AudioRegion::~AudioRegion ()
}
void
-AudioRegion::invalidate_transients ()
+AudioRegion::listen_to_my_sources ()
{
- valid_transients = false;
- _transients.clear ();
+ for (SourceList::const_iterator i = sources.begin(); i != sources.end(); ++i) {
+ (*i)->AnalysisChanged.connect (mem_fun (*this, &AudioRegion::invalidate_transients));
+ }
}
void
@@ -1517,36 +1518,12 @@ AudioRegion::set_playlist (boost::weak_ptr<Playlist> wpl)
}
}
-void
-AudioRegion::cleanup_transients (vector<nframes64_t>& t)
-{
- sort (t.begin(), t.end());
-
- /* remove duplicates or other things that are too close */
-
- vector<nframes64_t>::iterator i = t.begin();
- nframes64_t curr = (*i);
-
- /* XXX force a 3msec gap - use a config variable */
-
- nframes64_t gap_frames = (nframes64_t) floor (3.0 * (playlist()->session().frame_rate() / 1000.0));
-
- ++i;
-
- while (i != t.end()) {
- if (((*i) == curr) || (((*i) - curr) < gap_frames)) {
- i = t.erase (i);
- } else {
- ++i;
- curr = *i;
- }
- }
-}
-
int
-AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new)
+AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
{
- if (!playlist()) {
+ boost::shared_ptr<Playlist> pl = playlist();
+
+ if (!pl) {
return -1;
}
@@ -1555,7 +1532,51 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new)
return 0;
}
- TransientDetector t (playlist()->session().frame_rate());
+ SourceList::iterator s;
+
+ for (s = sources.begin() ; s != sources.end(); ++s) {
+ if (!(*s)->has_been_analysed()) {
+ cerr << "For " << name() << " source " << (*s)->name() << " has not been analyzed\n";
+ break;
+ }
+ }
+
+ if (s == sources.end()) {
+ /* all sources are analyzed, merge data from each one */
+
+ for (s = sources.begin() ; s != sources.end(); ++s) {
+
+ /* find the set of transients within the bounds of this region */
+
+ AnalysisFeatureList::iterator low = lower_bound ((*s)->transients.begin(),
+ (*s)->transients.end(),
+ _start);
+
+ AnalysisFeatureList::iterator high = upper_bound ((*s)->transients.begin(),
+ (*s)->transients.end(),
+ _start + _length);
+
+ /* and add them */
+
+ results.insert (results.end(), low, high);
+ }
+
+ TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
+
+ /* translate all transients to current position */
+
+ for (AnalysisFeatureList::iterator x = results.begin(); x != results.end(); ++x) {
+ (*x) -= _start;
+ (*x) += _position;
+ }
+
+ _transients = results;
+ valid_transients = true;
+
+ return 0;
+ }
+
+ TransientDetector t (pl->session().frame_rate());
bool existing_results = !results.empty();
_transients.clear ();
@@ -1563,7 +1584,7 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new)
for (uint32_t i = 0; i < n_channels(); ++i) {
- vector<nframes64_t> these_results;
+ AnalysisFeatureList these_results;
t.reset ();
@@ -1573,7 +1594,7 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new)
/* translate all transients to give absolute position */
- for (vector<nframes64_t>::iterator i = these_results.begin(); i != these_results.end(); ++i) {
+ for (AnalysisFeatureList::iterator i = these_results.begin(); i != these_results.end(); ++i) {
(*i) += _position;
}
@@ -1590,12 +1611,12 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new)
*/
results.insert (results.end(), _transients.begin(), _transients.end());
- cleanup_transients (results);
+ TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
}
/* make sure ours are clean too */
- cleanup_transients (_transients);
+ TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0);
}
valid_transients = true;
@@ -1603,7 +1624,6 @@ AudioRegion::get_transients (vector<nframes64_t>& results, bool force_new)
return 0;
}
-
extern "C" {
int region_read_peaks_from_c (void *arg, uint32_t npeaks, uint32_t start, uint32_t cnt, intptr_t data, uint32_t n_chan, double samples_per_unit)
diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc
index 68bfff10ee..51b4763e24 100644
--- a/libs/ardour/audiosource.cc
+++ b/libs/ardour/audiosource.cc
@@ -961,9 +961,37 @@ AudioSource::get_transients_path () const
s = _id.to_s();
s += '.';
- s += X_("transients");
+ s += TransientDetector::operational_identifier();
parts.push_back (s);
return Glib::build_filename (parts);
}
+void
+AudioSource::set_been_analysed (bool yn)
+{
+ Source::set_been_analysed (yn);
+
+ if (yn) {
+ load_transients (get_transients_path());
+ }
+}
+
+bool
+AudioSource::check_for_analysis_data_on_disk ()
+{
+ /* looks to see if the analysis files for this source are on disk.
+ if so, mark us already analysed.
+ */
+
+ string path = get_transients_path ();
+ bool ok = true;
+
+ if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
+ ok = false;
+ }
+
+ // XXX add other tests here as appropriate
+
+ set_been_analysed (ok);
+}
diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc
index 17dff0bda6..78583c8ddf 100644
--- a/libs/ardour/enums.cc
+++ b/libs/ardour/enums.cc
@@ -80,6 +80,7 @@ setup_enum_writer ()
Location::Flags _Location_Flags;
RouteGroup::Flag _RouteGroup_Flag;
Region::Flag _Region_Flag;
+ Region::PositionLockStyle _Region_PositionLockStyle;
Track::FreezeState _Track_FreezeState;
#define REGISTER(e) enum_writer->register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
@@ -352,6 +353,10 @@ setup_enum_writer ()
REGISTER_CLASS_ENUM (Region, DoNotSaveState);
REGISTER_BITS (_Region_Flag);
+ REGISTER_CLASS_ENUM (Region, AudioTime);
+ REGISTER_CLASS_ENUM (Region, MusicTime);
+ REGISTER_BITS (_Region_PositionLockStyle);
+
REGISTER_CLASS_ENUM (Track, NoFreeze);
REGISTER_CLASS_ENUM (Track, Frozen);
REGISTER_CLASS_ENUM (Track, UnFrozen);
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
index 5ada27a787..422ed16786 100644
--- a/libs/ardour/globals.cc
+++ b/libs/ardour/globals.cc
@@ -46,6 +46,7 @@
#include <midi++/mmc.h>
#include <ardour/ardour.h>
+#include <ardour/analyser.h>
#include <ardour/audio_library.h>
#include <ardour/configuration.h>
#include <ardour/profile.h>
@@ -300,6 +301,7 @@ ARDOUR::init (bool use_vst, bool try_optimization)
setup_hardware_optimization (try_optimization);
SourceFactory::init ();
+ Analyser::init ();
/* singleton - first object is "it" */
new PluginManager ();
diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc
index f495e00510..aa79812f98 100644
--- a/libs/ardour/playlist.cc
+++ b/libs/ardour/playlist.cc
@@ -37,6 +37,7 @@
#include <ardour/region.h>
#include <ardour/region_factory.h>
#include <ardour/playlist_factory.h>
+#include <ardour/transient_detector.h>
#include "i18n.h"
@@ -1411,6 +1412,65 @@ Playlist::regions_touched (nframes_t start, nframes_t end)
return rlist;
}
+nframes64_t
+Playlist::find_next_transient (nframes64_t from, int dir)
+{
+ RegionLock rlock (this);
+ AnalysisFeatureList points;
+ AnalysisFeatureList these_points;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if (dir > 0) {
+ if ((*i)->last_frame() < from) {
+ continue;
+ }
+ } else {
+ if ((*i)->first_frame() > from) {
+ continue;
+ }
+ }
+
+ (*i)->get_transients (these_points);
+
+ /* add first frame, just, err, because */
+
+ these_points.push_back ((*i)->first_frame());
+
+ points.insert (points.end(), these_points.begin(), these_points.end());
+ these_points.clear ();
+ }
+
+ if (points.empty()) {
+ return -1;
+ }
+
+ TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
+ bool reached = false;
+
+ if (dir > 0) {
+ for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
+ if ((*x) >= from) {
+ reached = true;
+ }
+
+ if (reached && (*x) > from) {
+ return *x;
+ }
+ }
+ } else {
+ for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
+ if ((*x) <= from) {
+ reached = true;
+ }
+
+ if (reached && (*x) < from) {
+ return *x;
+ }
+ }
+ }
+
+ return -1;
+}
boost::shared_ptr<Region>
Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
@@ -2209,3 +2269,18 @@ Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
return false;
}
+
+void
+Playlist::update_after_tempo_map_change ()
+{
+ RegionLock rlock (const_cast<Playlist*> (this));
+ RegionList copy (regions);
+
+ freeze ();
+
+ for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
+ (*i)->update_position_after_tempo_map_change ();
+ }
+
+ thaw ();
+}
diff --git a/libs/ardour/recent_sessions.cc b/libs/ardour/recent_sessions.cc
index 954162dd18..f14661d3d0 100644
--- a/libs/ardour/recent_sessions.cc
+++ b/libs/ardour/recent_sessions.cc
@@ -64,14 +64,9 @@ ARDOUR::read_recent_sessions (RecentSessions& rs)
break;
}
- if (!access(newpair.second.c_str(), R_OK)) {
- rs.push_back (newpair);
- }
+ rs.push_back (newpair);
}
- // This deletes any missing sessions
- ARDOUR::write_recent_sessions (rs);
-
/* display sorting should be done in the GUI, otherwise the
* natural order will be broken
*/
diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc
index 75682ec592..6bbc4329eb 100644
--- a/libs/ardour/region.cc
+++ b/libs/ardour/region.cc
@@ -21,6 +21,7 @@
#include <cmath>
#include <climits>
#include <algorithm>
+#include <sstream>
#include <sigc++/bind.h>
#include <sigc++/class_slot.h>
@@ -28,10 +29,12 @@
#include <glibmm/thread.h>
#include <pbd/xml++.h>
#include <pbd/stacktrace.h>
+#include <pbd/enumwriter.h>
#include <ardour/region.h>
#include <ardour/playlist.h>
#include <ardour/session.h>
+#include <ardour/tempo.h>
#include <ardour/region_factory.h>
#include "i18n.h"
@@ -56,7 +59,7 @@ Region::Region (nframes_t start, nframes_t length, const string& name, layer_t l
_read_data_count = 0;
_frozen = 0;
pending_changed = Change (0);
-
+ valid_transients = false;
_name = name;
_start = start;
_sync_position = _start;
@@ -72,6 +75,7 @@ Region::Region (nframes_t start, nframes_t length, const string& name, layer_t l
_read_data_count = 0;
_first_edit = EditChangesNothing;
_last_layer_op = 0;
+ _positional_lock_style = AudioTime;
}
Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
@@ -81,6 +85,7 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes
_frozen = 0;
pending_changed = Change (0);
_read_data_count = 0;
+ valid_transients = false;
_start = other->_start + offset;
if (other->_sync_position < offset) {
@@ -101,6 +106,7 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes
_flags = Flag (flags & ~(Locked|WholeFile|Hidden));
_first_edit = EditChangesNothing;
_last_layer_op = 0;
+ _positional_lock_style = AudioTime;
}
Region::Region (boost::shared_ptr<const Region> other)
@@ -110,6 +116,7 @@ Region::Region (boost::shared_ptr<const Region> other)
_frozen = 0;
pending_changed = Change (0);
_read_data_count = 0;
+ valid_transients = false;
_first_edit = EditChangesID;
other->_first_edit = EditChangesName;
@@ -134,12 +141,14 @@ Region::Region (boost::shared_ptr<const Region> other)
_layer = other->_layer;
_flags = Flag (other->_flags & ~Locked);
_last_layer_op = other->_last_layer_op;
+ _positional_lock_style = AudioTime;
}
Region::Region (const XMLNode& node)
{
_frozen = 0;
pending_changed = Change (0);
+ valid_transients = false;
_read_data_count = 0;
_start = 0;
_sync_position = _start;
@@ -151,6 +160,7 @@ Region::Region (const XMLNode& node)
_layer = 0;
_flags = Flag (0);
_first_edit = EditChangesNothing;
+ _positional_lock_style = AudioTime;
if (set_state (node)) {
throw failed_constructor();
@@ -206,6 +216,7 @@ Region::set_length (nframes_t len, void *src)
first_edit ();
maybe_uncopy ();
+ invalidate_transients ();
if (!_frozen) {
recompute_at_end ();
@@ -283,12 +294,49 @@ Region::special_set_position (nframes_t pos)
}
void
+Region::set_position_lock_style (PositionLockStyle ps)
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (!pl) {
+ return;
+ }
+
+ _positional_lock_style = ps;
+
+ if (_positional_lock_style == MusicTime) {
+ pl->session().tempo_map().bbt_time (_position, _bbt_time);
+ }
+
+}
+
+void
+Region::update_position_after_tempo_map_change ()
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (!pl || _positional_lock_style != MusicTime) {
+ return;
+ }
+
+ TempoMap& map (pl->session().tempo_map());
+ nframes_t pos = map.frame_time (_bbt_time);
+ set_position_internal (pos, false);
+}
+
+void
Region::set_position (nframes_t pos, void *src)
{
if (_flags & Locked) {
return;
}
+ set_position_internal (pos, true);
+}
+
+void
+Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
+{
if (_position != pos) {
_last_position = _position;
_position = pos;
@@ -303,6 +351,15 @@ Region::set_position (nframes_t pos, void *src)
_last_length = _length;
_length = max_frames - _position;
}
+
+ if (allow_bbt_recompute && _positional_lock_style == MusicTime) {
+ boost::shared_ptr<Playlist> pl (playlist());
+ if (pl) {
+ pl->session().tempo_map().bbt_time (_position, _bbt_time);
+ }
+ }
+
+ invalidate_transients ();
}
/* do this even if the position is the same. this helps out
@@ -396,6 +453,7 @@ Region::set_start (nframes_t pos, void *src)
_start = pos;
_flags = Region::Flag (_flags & ~WholeFile);
first_edit ();
+ invalidate_transients ();
send_change (StartChanged);
}
@@ -793,9 +851,9 @@ Region::state (bool full_state)
node->add_property ("length", buf);
snprintf (buf, sizeof (buf), "%u", _position);
node->add_property ("position", buf);
- snprintf (buf, sizeof (buf), "%Ld", _ancestral_start);
+ snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
node->add_property ("ancestral-start", buf);
- snprintf (buf, sizeof (buf), "%Ld", _ancestral_length);
+ snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
node->add_property ("ancestral-length", buf);
snprintf (buf, sizeof (buf), "%.12g", _stretch);
node->add_property ("stretch", buf);
@@ -826,6 +884,13 @@ Region::state (bool full_state)
snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
node->add_property ("sync-position", buf);
+ if (_positional_lock_style != AudioTime) {
+ node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
+ stringstream str;
+ str << _bbt_time;
+ node->add_property ("bbt-position", str.str());
+ }
+
return *node;
}
@@ -908,6 +973,27 @@ Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
_sync_position = _start;
}
+ if ((prop = node.property ("positional-lock-style")) != 0) {
+ _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
+
+ if (_positional_lock_style == MusicTime) {
+ if ((prop = node.property ("bbt-position")) == 0) {
+ /* missing BBT info, revert to audio time locking */
+ _positional_lock_style = AudioTime;
+ } else {
+ if (sscanf (prop->value().c_str(), "%d|%d|%d",
+ &_bbt_time.bars,
+ &_bbt_time.beats,
+ &_bbt_time.ticks) != 3) {
+ _positional_lock_style = AudioTime;
+ }
+ }
+ }
+
+ } else {
+ _positional_lock_style = AudioTime;
+ }
+
/* XXX FIRST EDIT !!! */
/* these 3 properties never change as a result of any editing */
@@ -1070,3 +1156,11 @@ Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
{
return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
}
+
+void
+Region::invalidate_transients ()
+{
+ valid_transients = false;
+ _transients.clear ();
+}
+
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
index ad629f5d6e..1f2ba8d61b 100644
--- a/libs/ardour/session.cc
+++ b/libs/ardour/session.cc
@@ -46,6 +46,7 @@
#include <ardour/audioengine.h>
#include <ardour/configuration.h>
#include <ardour/session.h>
+#include <ardour/analyser.h>
#include <ardour/audio_diskstream.h>
#include <ardour/utils.h>
#include <ardour/audioplaylist.h>
@@ -105,6 +106,7 @@ Session::mix_buffers_with_gain_t Session::mix_buffers_with_gain = 0;
Session::mix_buffers_no_gain_t Session::mix_buffers_no_gain = 0;
sigc::signal<int> Session::AskAboutPendingState;
+sigc::signal<int,nframes_t,nframes_t> Session::AskAboutSampleRateMismatch;
sigc::signal<void> Session::SendFeedback;
sigc::signal<void> Session::SMPTEOffsetChanged;
@@ -2819,6 +2821,9 @@ Session::add_source (boost::shared_ptr<Source> source)
set_dirty();
}
+ if (Config->get_auto_analyse_audio()) {
+ Analyser::queue_source_for_analysis (source, false);
+ }
}
}
@@ -3606,18 +3611,36 @@ void
Session::tempo_map_changed (Change ignored)
{
clear_clicks ();
+
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ (*i)->update_after_tempo_map_change ();
+ }
+
+ for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+ (*i)->update_after_tempo_map_change ();
+ }
+
set_dirty ();
}
void
Session::ensure_passthru_buffers (uint32_t howmany)
{
+ if (current_block_size == 0) {
+ return;
+ }
+
while (howmany > _passthru_buffers.size()) {
Sample *p;
#ifdef NO_POSIX_MEMALIGN
p = (Sample *) malloc(current_block_size * sizeof(Sample));
#else
- posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample));
+ if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * sizeof(Sample)) != 0) {
+ fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"),
+ current_block_size, sizeof (Sample), strerror (errno))
+ << endmsg;
+ /*NOTREACHED*/
+ }
#endif
_passthru_buffers.push_back (p);
@@ -3626,7 +3649,12 @@ Session::ensure_passthru_buffers (uint32_t howmany)
#ifdef NO_POSIX_MEMALIGN
p = (Sample *) malloc(current_block_size * sizeof(Sample));
#else
- posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * 4);
+ if (posix_memalign((void **)&p,CPU_CACHE_ALIGN,current_block_size * 4) != 0) {
+ fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"),
+ current_block_size, sizeof (Sample), strerror (errno))
+ << endmsg;
+ /*NOTREACHED*/
+ }
#endif
memset (p, 0, sizeof (Sample) * current_block_size);
_silent_buffers.push_back (p);
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
index 265be63960..d0302ea675 100644
--- a/libs/ardour/session_state.cc
+++ b/libs/ardour/session_state.cc
@@ -127,6 +127,9 @@ Session::first_stage_init (string fullpath, string snapshot_name)
_name = _current_snapshot_name = snapshot_name;
_current_frame_rate = _engine.frame_rate ();
+ _nominal_frame_rate = _current_frame_rate;
+ _base_frame_rate = _current_frame_rate;
+
_tempo_map = new TempoMap (_current_frame_rate);
_tempo_map->StateChanged.connect (mem_fun (*this, &Session::tempo_map_changed));
@@ -222,9 +225,6 @@ Session::first_stage_init (string fullpath, string snapshot_name)
waiting_for_sync_offset = false;
}
- _current_frame_rate = 48000;
- _base_frame_rate = 48000;
-
last_smpte_when = 0;
_smpte_offset = 0;
_smpte_offset_negative = true;
@@ -931,16 +931,16 @@ Session::state(bool full_state)
// store libardour version, just in case
char buf[16];
- snprintf(buf, sizeof(buf)-1, "%d.%d.%d",
- libardour2_major_version, libardour2_minor_version, libardour2_micro_version);
+ snprintf(buf, sizeof(buf), "%d.%d.%d", libardour2_major_version, libardour2_minor_version, libardour2_micro_version);
node->add_property("version", string(buf));
/* store configuration settings */
if (full_state) {
- /* store the name */
node->add_property ("name", _name);
+ snprintf (buf, sizeof (buf), "%" PRId32, _nominal_frame_rate);
+ node->add_property ("sample-rate", buf);
if (session_dirs.size() > 1) {
@@ -1158,7 +1158,6 @@ Session::set_state (const XMLNode& node)
int ret = -1;
_state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave);
-
if (node.name() != X_("Session")){
fatal << _("programming error: Session: incorrect XML node sent to set_state()") << endmsg;
@@ -1169,6 +1168,17 @@ Session::set_state (const XMLNode& node)
_name = prop->value ();
}
+ if ((prop = node.property (X_("sample-rate"))) != 0) {
+
+ _nominal_frame_rate = atoi (prop->value());
+
+ if (_nominal_frame_rate != _current_frame_rate) {
+ if (AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate)) {
+ return -1;
+ }
+ }
+ }
+
setup_raid_path(_path);
if ((prop = node.property (X_("id-counter"))) != 0) {
diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc
index 7f680e7100..16cc603089 100644
--- a/libs/ardour/source.cc
+++ b/libs/ardour/source.cc
@@ -46,6 +46,7 @@ Source::Source (Session& s, string name)
: _session (s)
{
_name = name;
+ _analysed = false;
_timestamp = 0;
_in_use = 0;
}
@@ -54,6 +55,7 @@ Source::Source (Session& s, const XMLNode& node)
: _session (s)
{
_timestamp = 0;
+ _analysed = false;
_in_use = 0;
if (set_state (node)) {
@@ -151,3 +153,24 @@ Source::used () const
{
return _playlists.size();
}
+
+bool
+Source::has_been_analysed() const
+{
+ Glib::Mutex::Lock lm (_analysis_lock);
+ return _analysed;
+}
+
+void
+Source::set_been_analysed (bool yn)
+{
+ {
+ Glib::Mutex::Lock lm (_analysis_lock);
+ _analysed = yn;
+ }
+
+ if (yn) {
+ AnalysisChanged(); // EMIT SIGNAL
+ }
+}
+
diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc
index 9d632b9a84..23a9c29c96 100644
--- a/libs/ardour/source_factory.cc
+++ b/libs/ardour/source_factory.cc
@@ -115,6 +115,7 @@ boost::shared_ptr<Source>
SourceFactory::createSilent (Session& s, const XMLNode& node, nframes_t nframes, float sr)
{
boost::shared_ptr<Source> ret (new SilentFileSource (s, node, nframes, sr));
+ // no analysis data - the file is non-existent
SourceCreated (ret);
return ret;
}
@@ -128,6 +129,7 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks)
if (setup_peakfile (ret, defer_peaks)) {
return boost::shared_ptr<Source>();
}
+ ret->check_for_analysis_data_on_disk ();
SourceCreated (ret);
return ret;
}
@@ -141,6 +143,7 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks)
if (setup_peakfile (ret, defer_peaks)) {
return boost::shared_ptr<Source>();
}
+ ret->check_for_analysis_data_on_disk ();
SourceCreated (ret);
return ret;
}
@@ -160,7 +163,7 @@ SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks)
if (setup_peakfile (ret, defer_peaks)) {
return boost::shared_ptr<Source>();
}
-
+ ret->check_for_analysis_data_on_disk ();
SourceCreated (ret);
return ret;
}
@@ -178,6 +181,7 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource
if (setup_peakfile (ret, defer_peaks)) {
return boost::shared_ptr<Source>();
}
+ ret->check_for_analysis_data_on_disk ();
if (announce) {
SourceCreated (ret);
}
@@ -192,6 +196,7 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource
if (setup_peakfile (ret, defer_peaks)) {
return boost::shared_ptr<Source>();
}
+ ret->check_for_analysis_data_on_disk ();
if (announce) {
SourceCreated (ret);
}
@@ -204,6 +209,7 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource
if (setup_peakfile (ret, defer_peaks)) {
return boost::shared_ptr<Source>();
}
+ ret->check_for_analysis_data_on_disk ();
if (announce) {
SourceCreated (ret);
}
@@ -224,6 +230,8 @@ SourceFactory::createReadable (Session& s, string path, int chn, AudioFileSource
return boost::shared_ptr<Source>();
}
+ ret->check_for_analysis_data_on_disk ();
+
if (announce) {
SourceCreated (ret);
}
@@ -250,6 +258,8 @@ SourceFactory::createWritable (Session& s, std::string path, bool destructive, n
return boost::shared_ptr<Source>();
}
+ // no analysis data - this is a new file
+
if (announce) {
SourceCreated (ret);
}
diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc
index b2865fc399..3170d588a1 100644
--- a/libs/ardour/tempo.cc
+++ b/libs/ardour/tempo.cc
@@ -246,29 +246,49 @@ TempoMap::~TempoMap ()
int
TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
{
- if (when == section.start()) {
+ if (when == section.start() || !section.movable()) {
return -1;
}
- if (!section.movable()) {
- return 1;
- }
-
Glib::RWLock::WriterLock lm (lock);
MetricSectionSorter cmp;
- BBT_Time corrected (when);
-
- if (dynamic_cast<MeterSection*>(&section) != 0) {
- if (corrected.beats > 1) {
- corrected.beats = 1;
- corrected.bars++;
+
+ if (when.beats != 1) {
+
+ /* position by audio frame, then recompute BBT timestamps from the audio ones */
+
+ nframes_t frame = frame_time (when);
+ // cerr << "nominal frame time = " << frame << endl;
+
+ nframes_t prev_frame = round_to_type (frame, -1, Beat);
+ nframes_t next_frame = round_to_type (frame, 1, Beat);
+
+ // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
+
+ /* use the closest beat */
+
+ if ((frame - prev_frame) < (next_frame - frame)) {
+ frame = prev_frame;
+ } else {
+ frame = next_frame;
}
+
+ // cerr << "actual frame time = " << frame << endl;
+ section.set_frame (frame);
+ // cerr << "frame time = " << section.frame() << endl;
+ timestamp_metrics (false);
+ // cerr << "new BBT time = " << section.start() << endl;
+ metrics->sort (cmp);
+
+ } else {
+
+ /* positioned at bar start already, so just put it there */
+
+ section.set_start (when);
+ metrics->sort (cmp);
+ timestamp_metrics (true);
}
- corrected.ticks = 0;
- section.set_start (corrected);
- metrics->sort (cmp);
- timestamp_metrics (true);
return 0;
}
@@ -288,7 +308,6 @@ TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
StateChanged (Change (0));
}
}
-
void
TempoMap::remove_tempo (const TempoSection& tempo)
@@ -412,11 +431,12 @@ TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
TempoSection *ts;
if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
-
- *((Tempo *) ts) = replacement;
+
+ *((Tempo *) ts) = replacement;
replaced = true;
timestamp_metrics (true);
+
break;
}
}
@@ -494,6 +514,21 @@ TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
}
void
+TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
+{
+ Tempo newtempo (beats_per_minute, note_type);
+ TempoSection* t;
+
+ for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ *((Tempo*) t) = newtempo;
+ StateChanged (Change (0));
+ break;
+ }
+ }
+}
+
+void
TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type)
{
Tempo newtempo (beats_per_minute, note_type);
@@ -582,6 +617,8 @@ TempoMap::timestamp_metrics (bool use_bbt)
if (use_bbt) {
+ // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
+
nframes_t current = 0;
nframes_t section_frames;
BBT_Time start;
@@ -611,42 +648,68 @@ TempoMap::timestamp_metrics (bool use_bbt)
} else {
+ // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
+
bool first = true;
+ MetricSection* prev = 0;
for (i = metrics->begin(); i != metrics->end(); ++i) {
BBT_Time bbt;
-
- bbt_time_with_metric ((*i)->frame(), bbt, Metric (*meter, *tempo));
+ Metric metric (*meter, *tempo);
+
+ if (prev) {
+ metric.set_start (prev->start());
+ } else {
+ // metric will be at frames=0 bbt=1|1|0 by default
+ // which is correct for our purpose
+ }
+
+ bbt_time_with_metric ((*i)->frame(), bbt, metric);
// cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
+
if (first) {
first = false;
} else {
- if (bbt.beats != 1 || bbt.ticks != 0) {
+
+ if (bbt.ticks > Meter::ticks_per_beat/2) {
+ /* round up to next beat */
+ bbt.beats += 1;
+ }
+
+ bbt.ticks = 0;
+
+ if (bbt.beats != 1) {
+ /* round up to next bar */
bbt.bars += 1;
bbt.beats = 1;
- bbt.ticks = 0;
}
}
-
- // cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
-
+
+ //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
+
(*i)->set_start (bbt);
if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
tempo = t;
+ // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
} else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
meter = m;
+ // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
} else {
fatal << _("programming error: unhandled MetricSection type") << endmsg;
/*NOTREACHED*/
}
+
+ prev = (*i);
}
}
// dump (cerr);
+ // cerr << "###############################################\n\n\n" << endl;
+
}
TempoMap::Metric
@@ -741,6 +804,8 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me
double xtra_beats = 0;
double beats = 0;
+ // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
+
const double beats_per_bar = metric.meter().beats_per_bar();
const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter());
@@ -748,11 +813,14 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me
/* now compute how far beyond that point we actually are. */
frame_diff = frame - metric.frame();
+
+ // cerr << "----\tdelta = " << frame_diff << endl;
xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
xtra_beats = (double) frame_diff / beat_frames;
+ // cerr << "---\tmeaning " << xtra_bars << " xtra bars and " << xtra_beats << " xtra beats\n";
/* and set the returned value */
@@ -765,21 +833,21 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me
bbt.bars = metric.start().bars + xtra_bars;
beats = (double) metric.start().beats + xtra_beats;
-
+
bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
beats = fmod(beats - 1, beats_per_bar )+ 1.0;
bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
bbt.beats = (uint32_t) floor(beats);
-
+
+ // cerr << "-----\t RETURN " << bbt << endl;
}
-
nframes_t
TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
{
/* for this to work with fractional measure types, start and end have to be "legal" BBT types,
- that means that the beats and ticks should be inside a bar
+ that means that the beats and ticks should be inside a bar
*/
nframes_t frames = 0;
@@ -814,7 +882,7 @@ TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) con
nframes_t
TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
{
- /*this is used in timestamping the metrics by actually counting the beats */
+ /* this is used in timestamping the metrics by actually counting the beats */
nframes_t frames = 0;
uint32_t bar = start.bars;
@@ -834,16 +902,26 @@ TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo,
beat = 1;
++bar;
++beats_counted;
- } else {
- ++beat;
- ++beats_counted;
+
if (beat > beats_per_bar) {
+
/* this is a fractional beat at the end of a fractional bar
- so it should only count for the fraction */
+ so it should only count for the fraction
+ */
+
beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
}
+
+ } else {
+ ++beat;
+ ++beats_counted;
}
}
+
+ // cerr << "Counted " << beats_counted << " from " << start << " to " << end
+ // << " bpb were " << beats_per_bar
+ // << " fpb was " << beat_frames
+ // << endl;
frames = (nframes_t) floor (beats_counted * beat_frames);
@@ -1052,7 +1130,6 @@ TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
}
nframes_t
-
TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
{
Metric metric = metric_at (frame);
@@ -1191,7 +1268,9 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
if (i == metrics->end()) {
limit = upper;
+ // cerr << "== limit set to end of request @ " << limit << endl;
} else {
+ // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
limit = (*i)->frame();
}
@@ -1224,6 +1303,10 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
beat++;
}
+ // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
+ // << (beat > ceil(beats_per_bar))
+ // << endl;
+
if (beat > ceil(beats_per_bar) || i != metrics->end()) {
/* we walked an entire bar. its
@@ -1245,9 +1328,11 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
if (beat > ceil (beats_per_bar)) {
/* next bar goes where the numbers suggest */
current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
+ // cerr << "++ next bar from numbers\n";
} else {
/* next bar goes where the next metric is */
current = limit;
+ // cerr << "++ next bar at next metric\n";
}
bar++;
beat = 1;
@@ -1275,6 +1360,9 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
beat = 1;
}
+ current = (*i)->frame ();
+ // cerr << "loop around with current @ " << current << endl;
+
beats_per_bar = meter->beats_per_bar ();
frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
@@ -1419,3 +1507,32 @@ TempoMap::dump (std::ostream& o) const
}
}
+int
+TempoMap::n_tempos() const
+{
+ Glib::RWLock::ReaderLock lm (lock);
+ int cnt = 0;
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if (dynamic_cast<const TempoSection*>(*i) != 0) {
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+int
+TempoMap::n_meters() const
+{
+ Glib::RWLock::ReaderLock lm (lock);
+ int cnt = 0;
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if (dynamic_cast<const MeterSection*>(*i) != 0) {
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
diff --git a/libs/ardour/transient_detector.cc b/libs/ardour/transient_detector.cc
index b85700dd90..b92bf5fb2d 100644
--- a/libs/ardour/transient_detector.cc
+++ b/libs/ardour/transient_detector.cc
@@ -6,20 +6,36 @@ using namespace Vamp;
using namespace ARDOUR;
using namespace std;
+string TransientDetector::_op_id;
+
TransientDetector::TransientDetector (float sr)
: AudioAnalyser (sr, X_("libardourvampplugins:percussiononsets"))
{
+ if (_op_id.empty()) {
+ _op_id = X_("libardourvampplugins:percussiononsets");
+
+ // XXX this should load the above-named plugin and get the current version
+
+ _op_id += ":2";
+ }
}
TransientDetector::~TransientDetector()
{
}
+string
+TransientDetector::operational_identifier()
+{
+ return _op_id;
+}
+
int
-TransientDetector::run (const std::string& path, Readable* src, uint32_t channel, vector<nframes64_t>& results)
+TransientDetector::run (const std::string& path, Readable* src, uint32_t channel, AnalysisFeatureList& results)
{
current_results = &results;
int ret = analyse (path, src, channel);
+
current_results = 0;
return ret;
}
@@ -59,3 +75,42 @@ TransientDetector::set_sensitivity (float val)
plugin->setParameter ("sensitivity", val);
}
}
+
+void
+TransientDetector::cleanup_transients (AnalysisFeatureList& t, float sr, float gap_msecs)
+{
+ if (t.empty()) {
+ return;
+ }
+
+ t.sort ();
+
+ /* remove duplicates or other things that are too close */
+
+ AnalysisFeatureList::iterator i = t.begin();
+ AnalysisFeatureList::iterator f, b;
+ const nframes64_t gap_frames = (nframes64_t) floor (gap_msecs * (sr / 1000.0));
+
+ while (i != t.end()) {
+
+ // move front iterator to just past i, and back iterator the same place
+
+ f = i;
+ ++f;
+ b = f;
+
+ // move f until we find a new value that is far enough away
+
+ while ((f != t.end()) && (((*f) - (*i)) < gap_frames)) {
+ ++f;
+ }
+
+ i = f;
+
+ // if f moved forward from b, we had duplicates/too-close points: get rid of them
+
+ if (b != f) {
+ t.erase (b, f);
+ }
+ }
+}
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp
index 406d4978c4..cbe179fb6b 100644
--- a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp
@@ -57,6 +57,8 @@ public:
OutputList getOutputDescriptors() const;
+ void reset();
+
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
@@ -225,7 +227,8 @@ protected:
vector<RingBuffer *> m_queue;
float **m_buffers;
float m_inputSampleRate;
- RealTime m_timestamp;
+ RealTime m_timestamp;
+ bool m_unrun;
OutputList m_outputs;
void processBlock(FeatureSet& allFeatureSets, RealTime timestamp);
@@ -253,6 +256,12 @@ PluginBufferingAdapter::getOutputDescriptors() const
{
return m_impl->getOutputDescriptors();
}
+
+void
+PluginBufferingAdapter::reset()
+{
+ m_impl->reset();
+}
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::process(const float *const *inputBuffers,
@@ -277,7 +286,8 @@ PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
m_queue(0),
m_buffers(0),
m_inputSampleRate(inputSampleRate),
- m_timestamp()
+ m_timestamp(RealTime::zeroTime),
+ m_unrun(true)
{
m_outputs = plugin->getOutputDescriptors();
}
@@ -333,8 +343,8 @@ PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_
}
}
- std::cerr << "PluginBufferingAdapter::initialise: stepSize " << m_inputStepSize << " -> " << m_stepSize
- << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;
+ // std::cerr << "PluginBufferingAdapter::initialise: stepSize " << m_inputStepSize << " -> " << m_stepSize
+ // << ", blockSize " << m_inputBlockSize << " -> " << m_blockSize << std::endl;
// current implementation breaks if step is greater than block
if (m_stepSize > m_blockSize) {
@@ -365,11 +375,27 @@ PluginBufferingAdapter::Impl::getOutputDescriptors() const
return outs;
}
+void
+PluginBufferingAdapter::Impl::reset()
+{
+ m_timestamp = RealTime::zeroTime;
+ m_unrun = true;
+
+ for (size_t i = 0; i < m_queue.size(); ++i) {
+ m_queue[i]->reset();
+ }
+}
+
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
RealTime timestamp)
{
FeatureSet allFeatureSets;
+
+ if (m_unrun) {
+ m_timestamp = timestamp;
+ m_unrun = false;
+ }
// queue the new input
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h
index 96a958b9e5..24ca6f8db8 100644
--- a/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h
@@ -81,6 +81,8 @@ public:
OutputList getOutputDescriptors() const;
+ void reset();
+
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.cpp b/libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.cpp
index 3ce51cedd2..fe676bcafd 100644
--- a/libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.cpp
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.cpp
@@ -143,7 +143,7 @@ PluginChannelAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t
m_pluginChannels = minch;
- std::cerr << "PluginChannelAdapter::initialise: expanding " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
+ // std::cerr << "PluginChannelAdapter::initialise: expanding " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
} else if (m_inputChannels > maxch) {
@@ -155,18 +155,18 @@ PluginChannelAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t
m_buffer = new float *[1];
m_buffer[0] = new float[blockSize];
- std::cerr << "PluginChannelAdapter::initialise: mixing " << m_inputChannels << " to mono for plugin" << std::endl;
+ // std::cerr << "PluginChannelAdapter::initialise: mixing " << m_inputChannels << " to mono for plugin" << std::endl;
} else {
- std::cerr << "PluginChannelAdapter::initialise: reducing " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
+ // std::cerr << "PluginChannelAdapter::initialise: reducing " << m_inputChannels << " to " << m_pluginChannels << " for plugin" << std::endl;
}
m_pluginChannels = maxch;
} else {
- std::cerr << "PluginChannelAdapter::initialise: accepting given number of channels (" << m_inputChannels << ")" << std::endl;
+ // std::cerr << "PluginChannelAdapter::initialise: accepting given number of channels (" << m_inputChannels << ")" << std::endl;
m_pluginChannels = m_inputChannels;
}
diff --git a/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp b/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp
index e706414ae2..273925f96d 100644
--- a/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp
+++ b/libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp
@@ -8,6 +8,9 @@
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006-2007 Chris Cannam and QMUL.
+ This file is based in part on Don Cross's public domain FFT
+ implementation.
+
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
@@ -38,28 +41,33 @@
#include <cmath>
-
/**
* If you want to compile using FFTW instead of the built-in FFT
* implementation for the PluginInputDomainAdapter, define HAVE_FFTW3
* in the Makefile.
*
- * Remember that FFTW is licensed under the GPL (unlike this SDK, which
- * is licensed liberally in order to permit closed-source usage), so
- * you should not define this symbol unless your code is also under the
- * GPL. Also, parties redistributing this SDK for use in other
- * programs should be careful _not_ to define this symbol in order not
- * to affect the stated license of this SDK.
+ * Be aware that FFTW is licensed under the GPL -- unlike this SDK,
+ * which is provided under a more liberal BSD license in order to
+ * permit use in closed source applications. The use of FFTW would
+ * mean that your code would need to be licensed under the GPL as
+ * well. Do not define this symbol unless you understand and accept
+ * the implications of this.
+ *
+ * Parties such as Linux distribution packagers who redistribute this
+ * SDK for use in other programs should _not_ define this symbol, as
+ * it would change the effective licensing terms under which the SDK
+ * was available to third party developers.
+ *
+ * The default is not to use FFTW, and to use the built-in FFT instead.
*
- * Note: This code uses FFTW_MEASURE, and will perform badly on its
- * first invocation unless the host has saved and restored FFTW wisdom
- * (see the FFTW documentation).
+ * Note: The FFTW code uses FFTW_MEASURE, and so will perform badly on
+ * its first invocation unless the host has saved and restored FFTW
+ * wisdom (see the FFTW documentation).
*/
#ifdef HAVE_FFTW3
#include <fftw3.h>
#endif
-
namespace Vamp {
namespace HostExt {