Merge with 2.0-ongoing R2988
authorDavid Robillard <d@drobilla.net>
Sat, 2 Feb 2008 17:22:04 +0000 (17:22 +0000)
committerDavid Robillard <d@drobilla.net>
Sat, 2 Feb 2008 17:22:04 +0000 (17:22 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@2991 d708f5d6-7413-0410-9779-e7cbd77b26cf

70 files changed:
SConstruct
gtk2_ardour/ardour.bindings.in
gtk2_ardour/ardour3_ui_default.conf
gtk2_ardour/ardour_dialog.cc
gtk2_ardour/ardour_dialog.h
gtk2_ardour/ardour_ui.cc
gtk2_ardour/ardour_ui.h
gtk2_ardour/ardour_ui_dependents.cc
gtk2_ardour/audio_region_view.cc
gtk2_ardour/canvas_vars.h
gtk2_ardour/editor.cc
gtk2_ardour/editor.h
gtk2_ardour/editor_actions.cc
gtk2_ardour/editor_canvas_events.cc
gtk2_ardour/editor_mouse.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/editor_tempodisplay.cc
gtk2_ardour/engine_dialog.cc
gtk2_ardour/generic_pluginui.cc
gtk2_ardour/icons/ferret_02.png [new file with mode: 0644]
gtk2_ardour/new_session_dialog.cc
gtk2_ardour/public_editor.h
gtk2_ardour/rhythm_ferret.cc
gtk2_ardour/rhythm_ferret.h
gtk2_ardour/splash.cc
gtk2_ardour/splash.h
gtk2_ardour/tempo_dialog.cc
gtk2_ardour/time_axis_view.cc
gtk2_ardour/time_axis_view.h
gtk2_ardour/utils.cc
libs/ardour/SConscript
libs/ardour/analyser.cc [new file with mode: 0644]
libs/ardour/ardour/analyser.h [new file with mode: 0644]
libs/ardour/ardour/audioanalyser.h
libs/ardour/ardour/audiofilesource.h
libs/ardour/ardour/audioregion.h
libs/ardour/ardour/audiosource.h
libs/ardour/ardour/configuration_vars.h
libs/ardour/ardour/playlist.h
libs/ardour/ardour/region.h
libs/ardour/ardour/session.h
libs/ardour/ardour/silentfilesource.h
libs/ardour/ardour/source.h
libs/ardour/ardour/tempo.h
libs/ardour/ardour/transient_detector.h
libs/ardour/ardour/types.h
libs/ardour/audio_buffer.cc
libs/ardour/audio_diskstream.cc
libs/ardour/audioanalyser.cc
libs/ardour/audioengine.cc
libs/ardour/audioregion.cc
libs/ardour/audiosource.cc
libs/ardour/enums.cc
libs/ardour/globals.cc
libs/ardour/playlist.cc
libs/ardour/recent_sessions.cc
libs/ardour/region.cc
libs/ardour/session.cc
libs/ardour/session_state.cc
libs/ardour/source.cc
libs/ardour/source_factory.cc
libs/ardour/tempo.cc
libs/ardour/transient_detector.cc
libs/libgnomecanvasmm/SConscript
libs/midi++2/SConscript
libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.cpp
libs/vamp-sdk/vamp-sdk/hostext/PluginBufferingAdapter.h
libs/vamp-sdk/vamp-sdk/hostext/PluginChannelAdapter.cpp
libs/vamp-sdk/vamp-sdk/hostext/PluginInputDomainAdapter.cpp
svn_revision.h

index f2a2d069b598472d7dc757d2fcb39ee5dabac4ad..33cb8b7feb3bd0410f153e5004ad06ffa967ea1c 100644 (file)
@@ -534,6 +534,7 @@ if env['LV2']:
        if conf.CheckPKGExists ('\"slv2 >= 0.6.0\"'):
                libraries['slv2'] = LibraryInfo()
                libraries['slv2'].ParseConfig('pkg-config --cflags --libs slv2')
+                env.Append (CCFLAGS="-DHAVE_LV2")
        else:
                print 'Building Ardour with LV2 support requires SLV2 >= 0.6.0'
                print 'WARNING: SLV2 not found, or too old.  Ardour will be built without LV2 support.'
index 3d488c1ec4df171dd7fa43e82dad812c3372cac5..0465889f7495db741d91debde3fa5a0ca4da3cf2 100644 (file)
 (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" "7")
+(gtk_accel_path "<Actions>/Editor/tab-to-transient-backwards" "8")
 (gtk_accel_path "<Actions>/Editor/set-tempo-from-region" "9")
 (gtk_accel_path "<Actions>/Editor/set-tempo-from-edit-range" "0")
 
+
 ;;
 ;; unbound actions
 ;;
index b64296ae02cd01fd35facf6336f489dba4caf69c..aed0f07bcaeb3fd9b685d5baea2240c3f2bac001 100644 (file)
@@ -4,11 +4,13 @@
     <Option name="ui-rc-file" value="ardour3_ui_dark.rc"/>
   </UI>
   <Canvas>
-    <Option name="waveform outline" value="0f0f0fcc"/>
-    <Option name="waveform fill" value="3d475378"/>
+    <Option name="waveform outline" value="0f0f0fc8"/>
+    <Option name="waveform fill" value="3d4753dc"/>
+    <Option name="selected waveform outline" value="0f0f0fcc"/>
+    <Option name="selected waveform fill" value="51518ac8"/>
     <Option name="clipped waveform" value="ff0000e5"/>
-    <Option name="region base" value="99a7b5aa"/>
-    <Option name="selected region base" value="b591a8ff"/>
+    <Option name="region base" value="99a7b5a0"/>
+    <Option name="selected region base" value="51518aa0"/>
     <Option name="midi frame base" value="698f9d6d"/>
     <Option name="audio track base" value="c6d3d868"/>
     <Option name="audio bus base" value="dbd1ea68"/>
index 795b924075deb01e511ed7fbc64e8abd72ecf572..bc24389948cfb993118a1fa082ed1cbfb9636f1a 100644 (file)
@@ -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 ();
+}
index 13248e14de1a3a02b8d2982b0504c80b75c4cff7..264dcfcd58bdf46c46eb1c16e9a97d148bc88e15 100644 (file)
@@ -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;
 
index b563fc4186f7f91c9f6454653810602ed12056c5..271cbab85b69d82ea73af6dc47ea9a5b36bc9304 100644 (file)
@@ -242,6 +242,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 {
@@ -403,6 +407,15 @@ ARDOUR_UI::~ARDOUR_UI ()
        }
 }
 
+void
+ARDOUR_UI::pop_back_splash ()
+{
+       if (Splash::instance()) {
+               // Splash::instance()->pop_back();
+               Splash::instance()->hide ();
+       }
+}
+
 gint
 ARDOUR_UI::configure_timeout ()
 {
@@ -609,6 +622,7 @@ Please consider the possibilities, and perhaps (re)start JACK."));
        
        win.show_all ();
        win.set_position (Gtk::WIN_POS_CENTER);
+       pop_back_splash ();
 
        /* we just don't care about the result, but we want to block */
 
@@ -691,6 +705,8 @@ ARDOUR_UI::check_memory_locking ()
                                cb.show();
                                vbox->show();
                                hbox.show ();
+
+                               pop_back_splash ();
                                
                                editor->ensure_float (msg);
                                msg.run ();
@@ -725,6 +741,7 @@ ARDOUR_UI::finish()
 Ardour was unable to save your session.\n\n\
 If you still wish to quit, please use the\n\n\
 \"Just quit\" option."));
+                                       pop_back_splash();
                                        msg.run ();
                                        return;
                                }
@@ -997,8 +1014,27 @@ ARDOUR_UI::redisplay_recent_sessions ()
 
                get_state_files_in_directory (*i, state_file_paths);
 
-               if (state_file_paths.empty()) {
-                       // no state file?
+               vector<string*>* states;
+               vector<const gchar*> item;
+               string fullpath = (*i).to_string();
+               
+               /* remove any trailing / */
+
+               if (fullpath[fullpath.length()-1] == '/') {
+                       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) {
+                       /* no state file? */
                        continue;
                }
          
@@ -1006,8 +1042,6 @@ ARDOUR_UI::redisplay_recent_sessions ()
 
                Gtk::TreeModel::Row row = *(recent_session_model->append());
 
-               const string fullpath = (*i).to_string();
-               
                row[recent_session_columns.visible_name] = Glib::path_get_basename (fullpath);
                row[recent_session_columns.fullpath] = fullpath;
                
@@ -1153,6 +1187,7 @@ ARDOUR_UI::check_audioengine ()
                if (!engine->connected()) {
                        MessageDialog msg (_("Ardour is not connected to JACK\n"
                                             "You cannot open or close sessions in this condition"));
+                       pop_back_splash ();
                        msg.run ();
                        return false;
                }
@@ -1309,6 +1344,7 @@ ARDOUR_UI::session_add_audio_route (bool track, int32_t input_channels, int32_t
 to create a new track or bus.\n\
 You should save Ardour, exit and\n\
 restart JACK with more ports."));
+               pop_back_splash ();
                msg.run ();
        }
 }
@@ -1685,6 +1721,7 @@ JACK has either been shutdown or it\n\
 disconnected Ardour because Ardour\n\
 was not fast enough. You can save the\n\
 session and/or try to reconnect to JACK ."));
+       pop_back_splash ();
        msg.run ();
 }
 
@@ -2026,6 +2063,7 @@ ARDOUR_UI::fontconfig_dialog ()
                                   true,
                                   Gtk::MESSAGE_INFO,
                                   Gtk::BUTTONS_OK);
+               pop_back_splash ();
                msg.show_all ();
                msg.present ();
                msg.run ();
@@ -2116,7 +2154,8 @@ ARDOUR_UI::ask_about_loading_existing_session (const Glib::ustring& session_path
        msg.set_name (X_("CleanupDialog"));
        msg.set_wmclass (X_("existing_session"), "Ardour");
        msg.set_position (Gtk::WIN_POS_MOUSE);
-       
+       pop_back_splash ();
+
        switch (msg.run()) {
        case RESPONSE_YES:
                return true;
@@ -2325,6 +2364,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: 
@@ -2374,7 +2414,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;
                        }
@@ -2435,6 +2475,7 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na
        if (Glib::file_test (path.c_str(), Glib::FILE_TEST_EXISTS) && ::access (path.c_str(), W_OK)) {
                MessageDialog msg (*editor, _("You do not have write access to this session.\n"
                                              "This prevents the session from being loaded."));
+               pop_back_splash ();
                msg.run ();
                goto out;
        }
@@ -2458,6 +2499,7 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na
                msg.set_title (_("Loading Error"));
                msg.set_secondary_text (_("Click the OK button to try again."));
                msg.set_position (Gtk::WIN_POS_CENTER);
+               pop_back_splash ();
                msg.present ();
 
                int response = msg.run ();
@@ -2483,6 +2525,7 @@ ARDOUR_UI::load_session (const Glib::ustring& path, const Glib::ustring& snap_na
                msg.set_title (_("Loading Error"));
                msg.set_secondary_text (_("Click the OK button to try again."));
                msg.set_position (Gtk::WIN_POS_CENTER);
+               pop_back_splash ();
                msg.present ();
 
                int response = msg.run ();
@@ -2555,6 +2598,7 @@ ARDOUR_UI::build_session (const Glib::ustring& path, const Glib::ustring& snap_n
        catch (...) {
 
                MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path));
+               pop_back_splash ();
                msg.run ();
                return -1;
        }
@@ -3030,6 +3074,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 ()
index 247d4cf6b384726e0c25e546b5e5bb093db3bebf..01e53c7d54f667357381a4a1eed4c10b200dc3cf 100644 (file)
@@ -660,7 +660,9 @@ class ARDOUR_UI : public Gtkmm2ext::UI
 
        About* about;
        Splash* splash;
+       void pop_back_splash ();
        bool shown_flag;
+
        /* cleanup */
 
        Gtk::MenuItem *cleanup_item;
@@ -682,6 +684,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 ();
index d4dda9c540128e0ca4e8dbc3e2f204eede727ff4..a2ac809203ae20861ad9a635e8b9da205a3e322e 100644 (file)
@@ -52,6 +52,7 @@ ARDOUR_UI::shutdown ()
                session->remove_pending_capture_state ();
                session = 0;
        }
+
        ui_config->save_state();
 }
 
index efd1bdd503e38f493e484aae395f6bb590344349..d19abf2ce5462cc7bb62a88fdfe8bd7da4712000 100644 (file)
@@ -1220,14 +1220,15 @@ AudioRegionView::set_frame_color ()
        uint32_t r,g,b,a;
        
        if (_selected && should_show_selection) {
-               frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_WaveForm.get();
+               UINT_TO_RGBA(ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get(), &r, &g, &b, &a);
+               frame->property_fill_color_rgba() = RGBA_TO_UINT(r, g, b, fill_opacity ? fill_opacity : a);
 
-               UINT_TO_RGBA(ARDOUR_UI::config()->canvasvar_FrameBase.get(), &r, &g, &b, &a);
                for (vector<ArdourCanvas::WaveView*>::iterator w = waves.begin(); w != waves.end(); ++w) {
                        if (_region->muted()) {
-                               (*w)->property_wave_color() = RGBA_TO_UINT(r, g, b, MUTED_ALPHA);
+                               (*w)->property_wave_color() = UINT_RGBA_CHANGE_A(ARDOUR_UI::config()->canvasvar_SelectedWaveForm.get(), MUTED_ALPHA);
                        } else {
-                               (*w)->property_wave_color() = RGBA_TO_UINT(r, g, b, fill_opacity ? fill_opacity : a);// Lets still use the theme's opacity value if Opaque is not set
+                               (*w)->property_wave_color() = ARDOUR_UI::config()->canvasvar_SelectedWaveForm.get();
+                               (*w)->property_fill_color() = ARDOUR_UI::config()->canvasvar_SelectedWaveFormFill.get();
                        }
                }
        } else {
index 354618e1769f3c3bff009376c4932f4eb53648ef..8085ec0227a92eb825b055d272d506b13c1ade2d 100644 (file)
@@ -1,5 +1,7 @@
 CANVAS_VARIABLE(canvasvar_WaveForm, "waveform outline")
 CANVAS_VARIABLE(canvasvar_WaveFormFill, "waveform fill")
+CANVAS_VARIABLE(canvasvar_SelectedWaveForm, "selected waveform outline")
+CANVAS_VARIABLE(canvasvar_SelectedWaveFormFill, "selected waveform fill")
 CANVAS_VARIABLE(canvasvar_WaveFormClip, "clipped waveform")
 CANVAS_VARIABLE(canvasvar_FrameBase, "region base")
 CANVAS_VARIABLE(canvasvar_SelectedFrameBase, "selected region base")
index 5c93877e07c08951d53023fb5bdf770b5dc73b1d..d26f01f76be5b9d031d6ebd3bc426fbc840896e9 100644 (file)
@@ -1803,12 +1803,25 @@ Editor::add_region_context_items (StreamView* sv, boost::shared_ptr<Region> regi
 
        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));
@@ -3366,15 +3379,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
index 56b0077b998533339524ba488618d43980cbcffb..5b4e54f4f66163b32b75e0a6a4f5eb0dcf06b407 100644 (file)
@@ -1019,7 +1019,7 @@ class Editor : public PublicEditor
        void toggle_region_mute ();
        void toggle_region_lock ();
        void toggle_region_opaque ();
-       void toggle_region_position_lock ();
+       void set_region_lock_style (ARDOUR::Region::PositionLockStyle);
        void raise_region ();
        void raise_region_to_top ();
        void lower_region ();
@@ -1028,7 +1028,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&);
@@ -1334,6 +1334,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 */
 
index 51752fb6bbdc4ab5f4079eee36122c4abadc3521..3627a7fe237591804b81e940a1643d1739847aa6 100644 (file)
@@ -377,7 +377,7 @@ 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));
index a69dcf935a0cf9d961b78fcffcc2fb6bed196254..3de7dc5c21fa7655c30682e72820da4523a24b94 100644 (file)
@@ -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
index 9689cdc64a0261e63dfb0d581819bd9023eccad5..4234368acdc6c67a992097039fd2152f8d3cd379 100644 (file)
@@ -3382,7 +3382,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;
                        }
index 1f833e833791dd93abb35662b709bb820fc12f24..77b0883fc18ed836497f5fe96b7ce51715f6dd2a 100644 (file)
@@ -57,6 +57,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"
@@ -1659,7 +1660,6 @@ Editor::temporal_zoom_region ()
        ensure_entered_region_selected (true);
 
        if (selection->regions.empty()) {
-               info << _("cannot set loop: no region selected") << endmsg;
                return;
        }
 
@@ -3540,13 +3540,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) {
@@ -3565,7 +3563,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;
@@ -3575,9 +3572,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);
@@ -4375,6 +4370,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 ()
 {
@@ -5013,31 +5017,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);
@@ -5052,7 +5095,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;
@@ -5088,7 +5131,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);
        
@@ -5106,7 +5149,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();
        
@@ -5163,30 +5206,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) {
@@ -5199,7 +5264,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) {
index 591c3d12fe36725a2fe65036aacd51ec6e3fce0f..39c74323f37b82ca0e9d7350b7b96b6351cac234 100644 (file)
@@ -365,10 +365,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 ();
index 9835901a248d49d5cc740b2ffd87055a29e5d9d7..4e5e068f65a6632670a21486911e27c6c730d46d 100644 (file)
@@ -553,10 +553,12 @@ EngineControl::setup_engine ()
                error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg;
                return -1;
        }
-
+       cerr << "JACK COMMAND: ";
        for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) {
+               cerr << (*i) << ' ';
                jackdrc << (*i) << ' ';
        }
+       cerr << endl;
        jackdrc << endl;
        jackdrc.close ();
 
@@ -915,7 +917,7 @@ EngineControl::find_jack_servers (vector<string>& strings)
 
        _NSGetExecutablePath (execpath, &pathsz);
        
-       Glib::ustring path (Glib::path_get_dirname (execpath));
+       string path (Glib::path_get_dirname (execpath));
        path += "/jackd";
 
        if (Glib::file_test (path, FILE_TEST_EXISTS)) {
@@ -937,8 +939,36 @@ EngineControl::find_jack_servers (vector<string>& strings)
        PathScanner scanner;
        vector<string *> *jack_servers;
        std::map<string,int> un;
-       
-       path = getenv ("PATH");
+       char *p;
+       bool need_minimal_path = false;
+
+       p = getenv ("PATH");
+
+       if (p && *p) {
+               path = p;
+       } else {
+               need_minimal_path = true;
+       }
+
+#ifdef __APPLE__
+       // many mac users don't have PATH set up to include
+       // likely installed locations of JACK
+       need_minimal_path = true;
+#endif
+
+       if (need_minimal_path) {
+               if (path.empty()) {
+                       path = "/usr/bin:/bin:/usr/local/bin:/opt/local/bin";
+               } else {
+                       path += ":/usr/local/bin:/opt/local/bin";
+               }
+       }
+
+#ifdef __APPLE__
+       // push it back into the environment so that auto-started JACK can find it.
+       // XXX why can't we just expect OS X users to have PATH set correctly? we can't ...
+       setenv ("PATH", path.c_str(), 1);
+#endif
 
        jack_servers = scanner (path, jack_server_filter, 0, false, true);
        
index ed185c33bc0699c3b655dd2f73ad7cc68e0a0215..6e3119b373c1b6a42d16c145d315ef11d4788b25 100644 (file)
@@ -38,7 +38,9 @@
 #include <ardour/plugin.h>
 #include <ardour/plugin_insert.h>
 #include <ardour/ladspa_plugin.h>
+#ifdef HAVE_LV2
 #include <ardour/lv2_plugin.h>
+#endif
 
 #include <lrdf.h>
 
@@ -385,8 +387,9 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
        if (plugin->parameter_is_input (port_index)) {
 
                boost::shared_ptr<LadspaPlugin> lp;
+#ifdef HAVE_LV2
                boost::shared_ptr<LV2Plugin> lv2p;
-
+#endif
                if ((lp = boost::dynamic_pointer_cast<LadspaPlugin>(plugin)) != 0) {
                        
                        // FIXME: not all plugins have a numeric unique ID
@@ -409,6 +412,7 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
                                return control_ui;
                        }
 
+#ifdef HAVE_LV2
                } else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin>(plugin)) != 0) {
 
                        SLV2Port port = lv2p->slv2_port(port_index);
@@ -428,6 +432,7 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
                                slv2_scale_points_free(points);
                                return control_ui;
                        }
+#endif
                }
                        
                if (desc.toggled) {
@@ -757,7 +762,9 @@ GenericPluginUI::setup_scale_values(guint32 port_index, ControlUI* cui)
 {
        vector<string> enums;
        boost::shared_ptr<LadspaPlugin> lp;
+#ifdef HAVE_LV2
        boost::shared_ptr<LV2Plugin> lv2p;
+#endif
 
        if ((lp = boost::dynamic_pointer_cast<LadspaPlugin>(plugin)) != 0) {
                // all LADPSA plugins have a numeric unique ID
@@ -777,6 +784,7 @@ GenericPluginUI::setup_scale_values(guint32 port_index, ControlUI* cui)
                        lrdf_free_setting_values(defaults);
                }
 
+#ifdef HAVE_LV2
        } else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin>(plugin)) != 0) {
 
                SLV2Port port = lv2p->slv2_port(port_index);
@@ -797,6 +805,7 @@ GenericPluginUI::setup_scale_values(guint32 port_index, ControlUI* cui)
                }
 
                slv2_scale_points_free(points);
+#endif
        }
        
 
diff --git a/gtk2_ardour/icons/ferret_02.png b/gtk2_ardour/icons/ferret_02.png
new file mode 100644 (file)
index 0000000..a6d625b
Binary files /dev/null and b/gtk2_ardour/icons/ferret_02.png differ
index 1b9d81df22aecf63d626a5bccc8d7094ae847991..e995ff1a8e7c281cfb330e1d720f8c31f31e8eba 100644 (file)
@@ -971,6 +971,8 @@ NewSessionDialog::reset_recent()
                        i != session_directories.end(); ++i)
        {
                std::vector<sys::path> state_file_paths;
+               std::vector<std::string*>* states;
+               const string fullpath = (*i).to_string();
            
                // now get available states for this session
 
@@ -980,28 +982,37 @@ NewSessionDialog::reset_recent()
                        // no state file?
                        continue;
                }
-         
-               std::vector<string> state_file_names(get_file_names_no_extension (state_file_paths));
-
+           
+               /* 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) {
+                       /* no state file? */
+                       continue;
+               }
+           
                Gtk::TreeModel::Row row = *(recent_model->append());
-
-               const string fullpath = (*i).to_string();
                
                row[recent_columns.visible_name] = Glib::path_get_basename (fullpath);
                row[recent_columns.fullpath] = fullpath;
                
-               if (state_file_names.size() > 1) {
-
-                       // add the children
-
-                       for (std::vector<std::string>::iterator i2 = state_file_names.begin();
-                                       i2 != state_file_names.end(); ++i2)
-                       {
-
-                               Gtk::TreeModel::Row child_row = *(recent_model->append (row.children()));
-
-                               child_row[recent_columns.visible_name] = *i2;
+               if (states->size()) {
+                   
+                       /* add the children */
+                   
+                       for (std::vector<std::string*>::iterator i2 = states->begin(); i2 != states->end(); ++i2) {
+
+                               Gtk::TreeModel::Row child_row = *(recent_model->append (row.children()));
+                               
+                               child_row[recent_columns.visible_name] = **i2;
                                child_row[recent_columns.fullpath] = fullpath;
+                               
+                               delete *i2;
                        }
                }
        }
index f279f93c39681afcaf32c28c358c4ddc1575d7f1..3f2c90e05a538da877322bc03941e9277074bb8e 100644 (file)
@@ -254,7 +254,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;
 
 #ifdef WITH_CMT
        virtual void add_imageframe_time_axis(const std::string & track_name, void*)  = 0;
index 980b36e1d13274a2b3994e2b10babe754f44a8c5..d8d2f3c28be4e0173a5bf777627e5d6421395b9b 100644 (file)
@@ -13,6 +13,7 @@
 #include "rhythm_ferret.h"
 #include "audio_region_view.h"
 #include "public_editor.h"
+#include "utils.h"
 
 #include "i18n.h"
 
@@ -57,9 +58,9 @@ RhythmFerret::RhythmFerret (PublicEditor& e)
 {
        upper_hpacker.set_spacing (6);
 
-       upper_hpacker.pack_start (operation_frame, true, true);
-       upper_hpacker.pack_start (selection_frame, true, true);
        upper_hpacker.pack_start (ferret_frame, true, true);
+       upper_hpacker.pack_start (selection_frame, true, true);
+       upper_hpacker.pack_start (operation_frame, true, true);
 
        op_packer.pack_start (region_split_button, false, false);
        op_packer.pack_start (tempo_button, false, false);
@@ -107,15 +108,17 @@ RhythmFerret::RhythmFerret (PublicEditor& e)
        analyze_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::run_analysis));
        
        ferret_frame.add (ferret_packer);
-
-       // Glib::RefPtr<Pixbuf> logo_pixbuf ("somefile");
        
+       logo = manage (new Gtk::Image (::get_icon (X_("ferret_02"))));
+
        if (logo) {
                lower_hpacker.pack_start (*logo, false, false);
        }
 
-       lower_hpacker.pack_start (operation_clarification_label, false, false);
+       lower_hpacker.pack_start (operation_clarification_label, true, true);
        lower_hpacker.pack_start (action_button, false, false);
+       lower_hpacker.set_border_width (6);
+       lower_hpacker.set_spacing (6);
 
        action_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::do_action));
        
@@ -194,13 +197,13 @@ 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());
 
        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());
@@ -212,38 +215,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;
index 36d445093932ecba1dc7b95afe83d4ed9e093cf4..891b447a116aca7d83aa0eb82ad7cd27e2b46e28 100644 (file)
@@ -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__ */
index 9317ec1caa48913bfad17f029b50fec7feb3a822..e901521e37cfb77d480516c910a7a88a47f106ef 100644 (file)
@@ -14,6 +14,8 @@ using namespace Glib;
 using namespace std;
 using namespace ARDOUR;
 
+Splash* Splash::the_splash = 0;
+
 Splash::Splash ()
 {
        sys::path splash_file;
@@ -47,6 +49,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
index 5ba5478941daff5ef2e9f5a11345dc7fdf942fe0..07532d93448db07d2780f28f0bb560a7f2a76eb9 100644 (file)
@@ -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;
index 0c99a4eeb08fca0bf0ce87499fb4ff1404c9916f..6f46ec1a9ddd63606c571b1f5ed4b42bd7ab3563 100644 (file)
@@ -243,7 +243,6 @@ TempoDialog::get_note_type ()
                }
        }
 
-       cerr << "returning " << note_type << " based on " << text << endl;
        return note_type;
 }
 
index a3262baec57ac3c76dd3dba1cf201e6b67e5c209..abe4e930431a8322b249399c5d78dac5980c3a9f 100644 (file)
@@ -1104,7 +1104,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);
@@ -1120,7 +1120,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) {
index f3bdbec7069d6ceb7d296383b8f23b823fa740e4..c210d6deb0a830d0b319c6174caad2f80a240aa8 100644 (file)
@@ -172,7 +172,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);
index a5238784461ad5dae2a5de485ecf5557f39c61c9..3a61a8676a25c517fbc4611d7ea8371a8f39be47 100644 (file)
@@ -466,6 +466,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;
index c4079172633db578f0aa937d71a3f90baae726ab..a4ffa4034e1d3185f5776df1852f11a0fcb07dcb 100644 (file)
@@ -31,6 +31,7 @@ amp.cc
 audio_buffer.cc
 auto_bundle.cc
 user_bundle.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 (file)
index 0000000..7ddb542
--- /dev/null
@@ -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 (file)
index 0000000..8771cab
--- /dev/null
@@ -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__ */
index dbd8a52d5a63d91b772a93593f20acbb5e764d70..06b841990a45f2848c12fa1418c0315381caa5dc 100644 (file)
@@ -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
index 4d80c8ddf558fa625ccce6ff35c73f1eb46c86a4..0e8b6e4fdedab825b0dba5dce353d30d00cc70b4 100644 (file)
@@ -125,6 +125,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 */
index 2c5630aec0c3d149fc35cfc82e919f5e594902d0..3fddfc55b4b6b76debb028ea3c408c3b52a17595 100644 (file)
@@ -134,7 +134,7 @@ class AudioRegion : public Region
        void resume_fade_in ();
        void resume_fade_out ();
 
-       int get_transients (std::vector<nframes64_t>&, bool force_new = false);
+       int get_transients (AnalysisFeatureList&, bool force_new = false);
 
   private:
        friend class RegionFactory;
@@ -170,6 +170,7 @@ class AudioRegion : public Region
        void fade_out_changed ();
        void source_offset_changed ();
        void listen_to_my_curves ();
+       void listen_to_my_sources ();
 
        boost::shared_ptr<AutomationList> _fade_in;
        FadeShape                         _fade_in_shape;
@@ -187,11 +188,6 @@ class AudioRegion : public Region
        AudioRegion (boost::shared_ptr<const AudioRegion>);
 
        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 */
index b11174abe8d64beaf400fefc23585a85c10ef634..1589841baad27b9afa654d4ad6d994f02833997d 100644 (file)
@@ -111,14 +111,12 @@ 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;
-       std::string get_transients_path() const;
-
   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;
        Glib::ustring         peakpath;
@@ -145,9 +143,7 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
                                         double samples_per_visual_peak, nframes_t fpp) const;
 
        int compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, 
-                                    bool intermediate_peaks_ready_signal, nframes_t frames_per_peak);
-
-       int load_transients (const std::string&);
+                                    bool intermediate_peaks_ready_signal, nframes_t frames_per_peak);  
 
   private:
        int peakfile;
index 99776929a4f5d460d59bf401a3eda889b52041a9..134557daebed971c1a0440b72438fdfe2304ff5d 100644 (file)
@@ -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 */
 
index 3f328de00518654d63b1f455b47b15007d18b580..d8a38841f80933e3333a548f07ddc839743facc7 100644 (file)
@@ -96,6 +96,7 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this<Pla
        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);
@@ -110,6 +111,8 @@ class Playlist : public SessionObject, public boost::enable_shared_from_this<Pla
        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>));
 
index da07c580b49a05d200f94a64db9d25f9b787682a..d793e8ca23b23a56cd73409b0839fff18f1760d7 100644 (file)
@@ -73,6 +73,11 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
                range_guarantoor = USHRT_MAX
        };
 
+       enum PositionLockStyle {
+               AudioTime,
+               MusicTime
+       };
+
        static const Flag DefaultFlags = Flag (Opaque|DefaultFadeIn|DefaultFadeOut|FadeIn|FadeOut);
 
        static Change FadeChanged;
@@ -130,6 +135,9 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
        bool captured()   const { return !(_flags & (Region::Flag (Region::Import|Region::External))); }
        bool can_move()   const { return !(_flags & (Locked|PositionLocked)); }
 
+       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 ();
@@ -156,6 +164,7 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
        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;
@@ -213,6 +222,13 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
        virtual bool is_dependent() const { return false; }
        virtual bool depends_on (boost::shared_ptr<Region> other) const { return false; }
 
+       virtual int get_transients (AnalysisFeatureList&, bool force_new = false) { 
+               // no transients, but its OK
+               return 0;
+       }
+
+       void invalidate_transients ();  
+
   protected:
        friend class RegionFactory;
 
@@ -226,8 +242,6 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
        Region (boost::shared_ptr<Source> src, const XMLNode&);
        Region (const SourceList& srcs, const XMLNode&);
 
-       /* this one is for derived types of derived types */
-
        Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType, layer_t = 0, Flag flags = DefaultFlags);
 
   protected:
@@ -236,6 +250,7 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
        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 ();
@@ -256,6 +271,7 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
        nframes_t               _last_length;
        nframes_t               _position;
        nframes_t               _last_position;
+       PositionLockStyle       _positional_lock_style;
        nframes_t               _sync_position;
        layer_t                 _layer;
        mutable RegionEditState _first_edit;
@@ -264,6 +280,9 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
        nframes64_t             _ancestral_length;
        float                   _stretch;
        float                   _shift;
+       BBT_Time                _bbt_time;
+       AnalysisFeatureList     _transients;
+       bool                    _valid_transients;
        mutable uint32_t        _read_data_count;  ///< modified in read()
        Change                  _pending_changed;
        uint64_t                _last_layer_op;  ///< timestamp
index d27df2f7ea253577e1d44bcc8292402abe33938c..c0c178eb02c70ef9ed677af93b915dc12013afb5 100644 (file)
@@ -389,7 +389,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; }
@@ -432,6 +435,9 @@ class Session : public PBD::StatefulDestructible
        
        sigc::signal<void,string> StateSaved;
        sigc::signal<void> StateReady;
+       
+       vector<string*>* possible_states() const;
+       static vector<string*>* possible_states(string path);
 
        XMLNode& get_state();
        int      set_state(const XMLNode& node); // not idempotent
@@ -624,6 +630,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.
        */
@@ -981,6 +993,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;
index e0103185c2d6e8612e5e6eaf2165d5e7816c4b80..87065c1cd01be5b505c517753e064ab5db1f309c 100644 (file)
@@ -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:
 
index 174e58c61b0d52d7b2e86ab937772b9bfc2eb4d3..3109cb00ff1772d366b172603901552af00c9e2d 100644 (file)
@@ -78,14 +78,27 @@ class Source : public SessionObject, public ARDOUR::Readable
        static sigc::signal<void,Source*>             SourceCreated;
        sigc::signal<void,boost::shared_ptr<Source> > Switched;
 
+       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();
+
+       sigc::signal<void> AnalysisChanged;
+       
+       AnalysisFeatureList transients;
+       std::string get_transients_path() const;
+       int load_transients (const std::string&);
+
   protected:
        void update_length (nframes_t pos, nframes_t cnt);
        
-       DataType  _type;
-       time_t    _timestamp;
-       nframes_t _length;
-
-       Glib::Mutex playlist_lock;
+       DataType            _type;
+       time_t              _timestamp;
+       nframes_t           _length;
+       bool                _analysed;
+       mutable Glib::Mutex _analysis_lock;
+       Glib::Mutex         _playlist_lock;
+       
        typedef std::map<boost::shared_ptr<ARDOUR::Playlist>, uint32_t > PlaylistMap;
        PlaylistMap _playlists;
 
index dc49f5cdefae5d6eb5877dae0ae5ba6ed184db7f..c4915072c57ba7349b6e00fcb10adcc57404b932 100644 (file)
@@ -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);
 
index c65bae3ed5be4055c73f0d068a05da523f00b3df..259b79176fe68eb236a6df09835e15628d39130a 100644 (file)
@@ -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 */
index cc756b0ec6f136b0459ab02cb3e7f977813879db..fdf8d0b4390e5b598febcf333c65a1f1e9b64858 100644 (file)
@@ -392,6 +392,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);
index 8444304832384e2a7df76d2f0532c66d6c78063f..915fdeb948adc75ec1d988ca2638cd59891dc962 100644 (file)
 */
 
 #include <ardour/audio_buffer.h>
+#include <pbd/error.h>
+#include <errno.h>
+
+#include "i18n.h"
 
 #ifdef __x86_64__
 static const int CPU_CACHE_ALIGN = 64;
@@ -24,6 +28,8 @@ static const int CPU_CACHE_ALIGN = 64;
 static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
 #endif
 
+using namespace PBD;
+
 namespace ARDOUR {
 
 
@@ -63,7 +69,10 @@ AudioBuffer::resize (size_t size)
 #ifdef NO_POSIX_MEMALIGN
        _data =  (Sample *) malloc(sizeof(Sample) * _capacity);
 #else
-       posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(Sample) * _capacity);
+       if (posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(Sample) * _capacity)) {
+               fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"),
+                               CPU_CACHE_ALIGN, sizeof (Sample) * _capacity, strerror (errno)) << endmsg;
+       }
 #endif 
        
        _owns_data = true;
index d65183a84fa3c7f18a56da72eb3ca5abe8686197..71ff81490034fe4c39d4d44ead82c8d01ec8ccd3 100644 (file)
@@ -39,6 +39,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>
@@ -1569,6 +1570,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);
                }
        }
 
index 4cc99a5d5e2da2a6cac03bd350ad3f8d1709f912..9da404e5bf5e7c3df83eec5b3be7349fc4418c00 100644 (file)
@@ -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;
index 69502f9922eace5b535861272c3e44fb0b2adfca..da4f8a642ca1aebf8e9d229ff09d31a522398b03 100644 (file)
@@ -857,13 +857,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
index 822fe2cb72976a34416bb6f15e5fb514f8c39b9f..20115ff944049911338da9d5cc89e33b12b15137 100644 (file)
@@ -66,12 +66,12 @@ void
 AudioRegion::init ()
 {
        _scale_amplitude = 1.0;
-       valid_transients = false;
 
        set_default_fades ();
        set_default_envelope ();
 
        listen_to_my_curves ();
+       listen_to_my_sources ();
 }
 
 /* constructor for use by derived types only */
@@ -122,6 +122,7 @@ AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t len
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
 {
        init ();
+       listen_to_my_sources ();
 }
 
 /** Create a new AudioRegion, that is part of an existing one */
@@ -172,9 +173,9 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t
        }
 
        _scale_amplitude = other->_scale_amplitude;
-       valid_transients = false;
 
        assert(_type == DataType::AUDIO);
+       listen_to_my_sources ();
 }
 
 AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
@@ -183,15 +184,14 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
        , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
        , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
 {
+       assert(_type == DataType::AUDIO);
        _scale_amplitude = other->_scale_amplitude;
-       valid_transients = false;
        _envelope = other->_envelope;
 
        set_default_fades ();
        
        listen_to_my_curves ();
-
-       assert(_type == DataType::AUDIO);
+       listen_to_my_sources ();
 }
 
 AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
@@ -206,13 +206,13 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod
        }
 
        init ();
-       valid_transients = false;
 
        if (set_state (node)) {
                throw failed_constructor();
        }
 
        assert(_type == DataType::AUDIO);
+       listen_to_my_sources ();
 }
 
 AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
@@ -228,6 +228,7 @@ AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
        }
 
        assert(_type == DataType::AUDIO);
+       listen_to_my_sources ();
 }
 
 AudioRegion::~AudioRegion ()
@@ -235,10 +236,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
@@ -1256,53 +1258,73 @@ AudioRegion::audio_source (uint32_t n) const
        return boost::dynamic_pointer_cast<AudioSource>(source(n));
 }
 
-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;
        }
 
-       if (valid_transients && !force_new) {
+       if (_valid_transients && !force_new) {
                results = _transients;
                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 ();
-       valid_transients = false;
+       _valid_transients = false;
 
        for (uint32_t i = 0; i < n_channels(); ++i) {
 
-               vector<nframes64_t> these_results;
+               AnalysisFeatureList these_results;
 
                t.reset ();
 
@@ -1312,7 +1334,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;
                }
 
@@ -1329,20 +1351,19 @@ 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;
+       _valid_transients = true;
 
        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) 
index 80116988d5a5f0c1ff0e5c3883b317a0b10225c9..cd71dd21e405b78fa585e4b1482af629a5ddcd0b 100644 (file)
@@ -919,50 +919,3 @@ AudioSource::update_length (nframes_t pos, nframes_t cnt)
        }
 }
 
-int
-AudioSource::load_transients (const string& path)
-{
-       ifstream file (path.c_str());
-
-       if (!file) {
-               return -1;
-       }
-       
-       transients.clear ();
-
-       stringstream strstr;
-       double val;
-
-       while (file.good()) {
-               file >> val;
-
-               if (!file.fail()) {
-                       nframes64_t frame = (nframes64_t) floor (val * _session.frame_rate());
-                       transients.push_back (frame);
-               }
-       }
-
-       return 0;
-}
-
-string 
-AudioSource::get_transients_path () const
-{
-       vector<string> parts;
-       string s;
-
-       /* old sessions may not have the analysis directory */
-       
-       _session.ensure_subdirs ();
-
-       s = _session.analysis_dir ();
-       parts.push_back (s);
-
-       s = _id.to_s();
-       s += '.';
-       s += X_("transients");
-       parts.push_back (s);
-       
-       return Glib::build_filename (parts);
-}
-
index 9f2ce4fab9e6275e1b78aa38e47e614d29c4b9fc..d64d3a2f9966136f26bf0f0e3abca2818431157f 100644 (file)
@@ -82,6 +82,7 @@ setup_enum_writer ()
        Location::Flags _Location_Flags;
        RouteGroup::Flag _RouteGroup_Flag;
        Region::Flag _Region_Flag;
+       Region::PositionLockStyle _Region_PositionLockStyle;
        Track::FreezeState _Track_FreezeState;
        AutomationList::InterpolationStyle _AutomationList_InterpolationStyle;
 
@@ -364,6 +365,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);
index 6bb21a419cf94083a919d7ab2a0e5c3f4c7dec8f..6aba688cae9e2da38c61064b0c45d521d8bfd824 100644 (file)
@@ -43,6 +43,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 ();
index 04649a56fe5fb0409d2bc519a6ac647571fbd9ad..da3a812123b0744cb1b1526b5eac7a4e5ef2965b 100644 (file)
@@ -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"
 
@@ -1428,6 +1429,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)
@@ -2259,3 +2319,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 ();
+}
index cfb8d5bf2e73e6243e4f6d088ae4de260791dda1..ebc5499f89134e4d28dce90cb88742505cecd13c 100644 (file)
@@ -75,14 +75,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
         */
index 054e85cd2fb63d62a7b4330e851e56204bded27e..2193a69908e6f185361af6a1c38540c6cdce619e 100644 (file)
@@ -21,6 +21,7 @@
 #include <cmath>
 #include <climits>
 #include <algorithm>
+#include <sstream>
 
 #include <sigc++/bind.h>
 #include <sigc++/class_slot.h>
 #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/source.h>
+#include <ardour/tempo.h>
 #include <ardour/region_factory.h>
 #include <ardour/filter.h>
 
@@ -59,6 +62,8 @@ Region::Region (Session& s, nframes_t start, nframes_t length, const string& nam
        , _start(start) 
        , _length(length) 
        , _position(0) 
+       , _last_position(0) 
+       , _positional_lock_style(AudioTime)
        , _sync_position(_start)
        , _layer(layer)
        , _first_edit(EditChangesNothing)
@@ -70,7 +75,6 @@ Region::Region (Session& s, nframes_t start, nframes_t length, const string& nam
        /* no sources at this point */
 }
 
-
 /** Basic Region constructor (single source) */
 Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
        : Automatable(src->session(), name)
@@ -79,6 +83,8 @@ Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length
        , _start(start) 
        , _length(length) 
        , _position(0) 
+       , _last_position(0) 
+       , _positional_lock_style(AudioTime)
        , _sync_position(_start)
        , _layer(layer)
        , _first_edit(EditChangesNothing)
@@ -86,6 +92,8 @@ Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length
        , _ancestral_start (start)
        , _ancestral_length (length)
        , _stretch (1.0)
+       , _shift (0.0)
+       , _valid_transients(false)
        , _read_data_count(0)
        , _pending_changed(Change (0))
        , _last_layer_op(0)
@@ -96,6 +104,7 @@ Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length
        src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
 
        assert(_sources.size() > 0);
+       _positional_lock_style = AudioTime;
 }
 
 /** Basic Region constructor (many sources) */
@@ -106,6 +115,8 @@ Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const
        , _start(start) 
        , _length(length) 
        , _position(0) 
+       , _last_position(0) 
+       , _positional_lock_style(AudioTime)
        , _sync_position(_start)
        , _layer(layer)
        , _first_edit(EditChangesNothing)
@@ -141,6 +152,8 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes
        , _start(other->_start + offset) 
        , _length(length) 
        , _position(0) 
+       , _last_position(0) 
+       , _positional_lock_style(other->_positional_lock_style)
        , _sync_position(_start)
        , _layer(layer)
        , _first_edit(EditChangesNothing)
@@ -148,6 +161,8 @@ Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes
        , _ancestral_start (other->_ancestral_start + offset)
        , _ancestral_length (length)
        , _stretch (1.0)
+       , _shift (0.0)
+       , _valid_transients(false)
        , _read_data_count(0)
        , _pending_changed(Change (0))
        , _last_layer_op(0)
@@ -185,6 +200,8 @@ Region::Region (boost::shared_ptr<const Region> other)
        , _start(other->_start) 
        , _length(other->_length) 
        , _position(other->_position) 
+       , _last_position(other->_last_position) 
+       , _positional_lock_style(other->_positional_lock_style)
        , _sync_position(other->_sync_position)
        , _layer(other->_layer)
        , _first_edit(EditChangesID)
@@ -192,6 +209,8 @@ Region::Region (boost::shared_ptr<const Region> other)
        , _ancestral_start (_start)
        , _ancestral_length (_length)
        , _stretch (1.0)
+       , _shift (0.0)
+       , _valid_transients(false)
        , _read_data_count(0)
        , _pending_changed(Change(0))
        , _last_layer_op(other->_last_layer_op)
@@ -229,6 +248,8 @@ Region::Region (const SourceList& srcs, const XMLNode& node)
        , _start(0) 
        , _length(0) 
        , _position(0) 
+       , _last_position(0) 
+       , _positional_lock_style(AudioTime)
        , _sync_position(_start)
        , _layer(0)
        , _first_edit(EditChangesNothing)
@@ -267,6 +288,8 @@ Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
        , _start(0) 
        , _length(0) 
        , _position(0) 
+       , _last_position(0) 
+       , _positional_lock_style(AudioTime)
        , _sync_position(_start)
        , _layer(0)
        , _first_edit(EditChangesNothing)
@@ -373,6 +396,7 @@ Region::set_length (nframes_t len, void *src)
 
                first_edit ();
                maybe_uncopy ();
+               invalidate_transients ();
 
                if (!_frozen) {
                        recompute_at_end ();
@@ -449,6 +473,37 @@ Region::special_set_position (nframes_t pos)
        _position = 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)
 {
@@ -456,6 +511,12 @@ Region::set_position (nframes_t pos, void *src)
                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;
@@ -470,6 +531,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
@@ -563,6 +633,7 @@ Region::set_start (nframes_t pos, void *src)
                _start = pos;
                _flags = Region::Flag (_flags & ~WholeFile);
                first_edit ();
+               invalidate_transients ();
 
                send_change (StartChanged);
        }
@@ -974,9 +1045,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);
@@ -1007,6 +1078,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;
 }
 
@@ -1095,6 +1173,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 */
@@ -1261,7 +1360,8 @@ Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
 void
 Region::source_deleted (boost::shared_ptr<Source>)
 {
-       delete this;
+       _sources.clear ();
+       drop_references ();
 }
 
 vector<string>
@@ -1397,3 +1497,10 @@ Region::apply (Filter& filter)
 }
 
 
+void
+Region::invalidate_transients ()
+{
+       _valid_transients = false;
+       _transients.clear ();
+}
+
index c6ace87b731a6af067e5d54a1bb4d907ba7cc45c..5a3144b828a29c52bfa1fc289c5dd813fa53f4a1 100644 (file)
@@ -80,6 +80,7 @@
 #include <ardour/filename_extensions.h>
 #include <ardour/session_directory.h>
 #include <ardour/tape_file_matcher.h>
+#include <ardour/analyser.h>
 
 #ifdef HAVE_LIBLO
 #include <ardour/osc.h>
@@ -107,6 +108,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;
@@ -2771,6 +2773,14 @@ Session::add_source (boost::shared_ptr<Source> source)
                source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr<Source> (source)));
                set_dirty();
        }
+       
+       boost::shared_ptr<AudioFileSource> afs;
+
+       if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
+               if (Config->get_auto_analyse_audio()) {
+                       Analyser::queue_source_for_analysis (source, false);
+               }
+       } 
 }
 
 void
@@ -3726,6 +3736,15 @@ 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 ();
 }
 
@@ -3737,7 +3756,7 @@ Session::ensure_buffers (ChanCount howmany)
 {
        if (current_block_size == 0)
                return; // too early? (is this ok?)
-
+       
        // We need at least 2 MIDI scratch buffers to mix/merge
        if (howmany.n_midi() < 2)
                howmany.set_midi(2);
index d18b9cedd7acf5fcfd20e95e01f90ce62835d082..af4be07dc466ad14cfe506c326eea3f0aee68ecc 100644 (file)
@@ -141,6 +141,9 @@ Session::first_stage_init (string fullpath, string snapshot_name)
        set_history_depth (Config->get_history_depth());
 
        _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));
 
@@ -236,9 +239,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;
@@ -920,16 +920,16 @@ Session::state(bool full_state)
 
        // store libardour version, just in case
        char buf[16];
-       snprintf(buf, sizeof(buf)-1, "%d.%d.%d", 
-                libardour_major_version, libardour_minor_version, libardour_micro_version);
+       snprintf(buf, sizeof(buf), "%d.%d.%d", libardour3_major_version, libardour3_minor_version, libardour3_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) {
 
@@ -1148,7 +1148,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;
@@ -1159,6 +1158,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(_session_dir->root_path().to_string());
 
        if ((prop = node.property (X_("id-counter"))) != 0) {
@@ -2067,6 +2077,56 @@ Session::auto_save()
        save_state (_current_snapshot_name);
 }
 
+static bool
+state_file_filter (const string &str, void *arg)
+{
+       return (str.length() > strlen(statefile_suffix) &&
+               str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
+}
+
+struct string_cmp {
+       bool operator()(const string* a, const string* b) {
+               return *a < *b;
+       }
+};
+
+static string*
+remove_end(string* state)
+{
+       string statename(*state);
+       
+       string::size_type start,end;
+       if ((start = statename.find_last_of ('/')) != string::npos) {
+               statename = statename.substr (start+1);
+       }
+               
+       if ((end = statename.rfind(".ardour")) == string::npos) {
+               end = statename.length();
+       }
+
+       return new string(statename.substr (0, end));
+}
+
+vector<string *> *
+Session::possible_states (string path) 
+{
+       PathScanner scanner;
+       vector<string*>* states = scanner (path, state_file_filter, 0, false, false);
+       
+       transform(states->begin(), states->end(), states->begin(), remove_end);
+       
+       string_cmp cmp;
+       sort (states->begin(), states->end(), cmp);
+       
+       return states;
+}
+
+vector<string *> *
+Session::possible_states () const
+{
+       return possible_states(_path);
+}
+
 RouteGroup *
 Session::add_edit_group (string name)
 {
index a11e82f1e81b245e912ca9f3e2a1439ee2fbd71c..7941eb693b23aedf0f825b9bc0cea18a8faa3fec 100644 (file)
 #include <cmath>
 #include <iomanip>
 #include <algorithm>
+#include <fstream>
 
 #include <glibmm/thread.h>
+#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
 #include <pbd/xml++.h>
 #include <pbd/pthread_utils.h>
 
 #include <ardour/source.h>
 #include <ardour/playlist.h>
+#include <ardour/session.h>
+#include <ardour/transient_detector.h>
 
 #include "i18n.h"
 
-using std::min;
-using std::max;
-
+using namespace std;
 using namespace ARDOUR;
 
 Source::Source (Session& s, const string& name, DataType type)
@@ -49,6 +52,7 @@ Source::Source (Session& s, const string& name, DataType type)
        // not true.. is this supposed to be an assertion?
        //assert(_name.find("/") == string::npos);
 
+       _analysed = false;
        _timestamp = 0;
        _length = 0;
        _in_use = 0;
@@ -60,6 +64,7 @@ Source::Source (Session& s, const XMLNode& node)
 {
        _timestamp = 0;
        _length = 0;
+       _analysed = false;
        _in_use = 0;
 
        if (set_state (node) || _type == DataType::NIL) {
@@ -135,7 +140,7 @@ Source::add_playlist (boost::shared_ptr<Playlist> pl)
 {
        std::pair<PlaylistMap::iterator,bool> res;
        std::pair<boost::shared_ptr<Playlist>, uint32_t> newpair (pl, 1);
-       Glib::Mutex::Lock lm (playlist_lock);
+       Glib::Mutex::Lock lm (_playlist_lock);
 
        res = _playlists.insert (newpair);
 
@@ -157,7 +162,7 @@ Source::remove_playlist (boost::weak_ptr<Playlist> wpl)
        }
 
        PlaylistMap::iterator x;
-       Glib::Mutex::Lock lm (playlist_lock);
+       Glib::Mutex::Lock lm (_playlist_lock);
 
        if ((x = _playlists.find (pl)) != _playlists.end()) {
                if (x->second > 1) {
@@ -173,3 +178,90 @@ 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
+       }
+}
+
+int
+Source::load_transients (const string& path)
+{
+       ifstream file (path.c_str());
+
+       if (!file) {
+               return -1;
+       }
+       
+       transients.clear ();
+
+       stringstream strstr;
+       double val;
+
+       while (file.good()) {
+               file >> val;
+
+               if (!file.fail()) {
+                       nframes64_t frame = (nframes64_t) floor (val * _session.frame_rate());
+                       transients.push_back (frame);
+               }
+       }
+
+       return 0;
+}
+
+string 
+Source::get_transients_path () const
+{
+       vector<string> parts;
+       string s;
+
+       /* old sessions may not have the analysis directory */
+       
+       _session.ensure_subdirs ();
+
+       s = _session.analysis_dir ();
+       parts.push_back (s);
+
+       s = _id.to_s();
+       s += '.';
+       s += TransientDetector::operational_identifier();
+       parts.push_back (s);
+       
+       return Glib::build_filename (parts);
+}
+
+bool
+Source::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);
+       return ok;
+}
index 02c35d2188fa7c12b601033e16ee3a55abeed51d..804d4a4d474c0e2c414ec393c7f1a108b5a6a032 100644 (file)
@@ -111,6 +111,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;
 }
@@ -118,61 +119,64 @@ SourceFactory::createSilent (Session& s, const XMLNode& node, nframes_t nframes,
 boost::shared_ptr<Source>
 SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks)
 {
-       DataType type = DataType::AUDIO;
-       const XMLProperty* prop = node.property("type");
+       DataType type = DataType::AUDIO;
+       const XMLProperty* prop = node.property("type");
 
-       if (prop) {
-               type = DataType(prop->value());
-       }
+       if (prop) {
+               type = DataType(prop->value());
+       }
 
-       if (type == DataType::AUDIO) {
+       if (type == DataType::AUDIO) {
 
 #ifdef HAVE_COREAUDIO
 
-              try {
-                      boost::shared_ptr<Source> ret (new CoreAudioSource (s, node));
-                      
-                      if (setup_peakfile (ret, defer_peaks)) {
-                              return boost::shared_ptr<Source>();
-                      }
-                      
-                      SourceCreated (ret);
-                      return ret;
-              } 
-              
-              
-              catch (failed_constructor& err) {        
-                      
-                      /* this is allowed to throw */
-                      
-                      boost::shared_ptr<Source> ret (new SndFileSource (s, node));
-                      
-                      if (setup_peakfile (ret, defer_peaks)) {
-                              return boost::shared_ptr<Source>();
-                      }
-                      
-                      SourceCreated (ret);
-                      return ret;
-              }
+               try {
+                       boost::shared_ptr<Source> ret (new CoreAudioSource (s, node));
+
+                       if (setup_peakfile (ret, defer_peaks)) {
+                               return boost::shared_ptr<Source>();
+                       }
+
+                       ret->check_for_analysis_data_on_disk ();
+                       SourceCreated (ret);
+                       return ret;
+               } 
+
+
+               catch (failed_constructor& err) {
+
+                       /* this is allowed to throw */
+
+                       boost::shared_ptr<Source> ret (new SndFileSource (s, node));
+
+                       if (setup_peakfile (ret, defer_peaks)) {
+                               return boost::shared_ptr<Source>();
+                       }
+
+                       ret->check_for_analysis_data_on_disk ();
+                       SourceCreated (ret);
+                       return ret;
+               }
 #else
-              /* this is allowed to throw */
-
-              boost::shared_ptr<Source> ret (new SndFileSource (s, node));
-              
-              if (setup_peakfile (ret, defer_peaks)) {
-                      return boost::shared_ptr<Source>();
-              }
-              
-              SourceCreated (ret);
-              return ret;
+               /* this is allowed to throw */
+
+               boost::shared_ptr<Source> ret (new SndFileSource (s, node));
+
+               if (setup_peakfile (ret, defer_peaks)) {
+                       return boost::shared_ptr<Source>();
+               }
+
+               ret->check_for_analysis_data_on_disk ();
+               SourceCreated (ret);
+               return ret;
 #endif
-       
-       } else if (type == DataType::MIDI) {
-                  boost::shared_ptr<Source> ret (new SMFSource (s, node));
-                      
-                  SourceCreated (ret);
-                  return ret;
-       }
+
+       } else if (type == DataType::MIDI) {
+               boost::shared_ptr<Source> ret (new SMFSource (s, node));
+               ret->check_for_analysis_data_on_disk ();
+               SourceCreated (ret);
+               return ret;
+       }
 
        return boost::shared_ptr<Source>();
 }
@@ -190,6 +194,7 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn,
                                return boost::shared_ptr<Source>();
                        }
 
+                       ret->check_for_analysis_data_on_disk ();
                        if (announce) {
                                SourceCreated (ret);
                        }
@@ -201,6 +206,7 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn,
                        if (setup_peakfile (ret, defer_peaks)) {
                                return boost::shared_ptr<Source>();
                        }
+                       ret->check_for_analysis_data_on_disk ();
                        if (announce) {
                                SourceCreated (ret);
                        }
@@ -213,6 +219,7 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn,
                        return boost::shared_ptr<Source>();
                }
 
+               ret->check_for_analysis_data_on_disk ();
                if (announce) {
                        SourceCreated (ret);
                }
@@ -225,6 +232,7 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn,
                // FIXME: flags?
                boost::shared_ptr<Source> ret (new SMFSource (s, path, SMFSource::Flag(0)));
 
+               ret->check_for_analysis_data_on_disk ();
                if (announce) {
                        SourceCreated (ret);
                }
@@ -252,6 +260,8 @@ SourceFactory::createWritable (DataType type, Session& s, std::string path, bool
                if (setup_peakfile (ret, defer_peaks)) {
                        return boost::shared_ptr<Source>();
                }
+               
+               // no analysis data - this is a new file
 
                if (announce) {
                        SourceCreated (ret);
@@ -261,6 +271,8 @@ SourceFactory::createWritable (DataType type, Session& s, std::string path, bool
        } else if (type == DataType::MIDI) {
 
                boost::shared_ptr<Source> ret (new SMFSource (s, path));
+       
+               // no analysis data - this is a new file
                
                if (announce) {
                        SourceCreated (ret);
index b2865fc399128f11356e47cb87460fe3cc851611..3170d588a1c1c80ee7a3ba09cad4cfe4cda21362 100644 (file)
@@ -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;
                        }
                }
@@ -493,6 +513,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)
 {
@@ -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;
+}
index b85700dd90c1adaabedd79fe2563e6dd24945ae8..b92bf5fb2de61a2034154c901f79881efca0b4db 100644 (file)
@@ -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);
+               }
+       }
+}
index cc0aff0bd829482451b5ad3181bb297459d26d67..40967cc8d2ed75d1a8927db41cbf042218bd9d04 100644 (file)
@@ -18,7 +18,8 @@ gnomecanvasmm.Merge([libraries['glibmm2'],
                      libraries['libgnomecanvas2'],
                      libraries['cairomm']])
 
-gnomecanvasmm.Append(CXXFLAGS = ["-DGLIBMM_PROPERTIES_ENABLED", "-DGLIBMM_EXCEPTIONS_ENABLED"])
+if gnomecanvasmm['IS_OSX']:
+       gnomecanvasmm.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
 
 libgnomecanvasmm = gnomecanvasmm.SharedLibrary('libgnomecanvasmm', gnomecanvasmm_files)
 Default(libgnomecanvasmm)
index 769d8889032cbe759dd61c66ed76f14fe7e9e904..9d2ce5c95e7d123b821234d7f1366cde17ae27b1 100644 (file)
@@ -9,6 +9,9 @@ Import('env libraries install_prefix')
 midi2 = env.Copy()
 midi2.Merge([ libraries['sigc2'], libraries['xml'], libraries['glibmm2'], libraries['glib2'], libraries['pbd'], libraries['jack'] ])
 
+if midi2['IS_OSX']:
+       midi2.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
+
 domain = 'midipp'
 
 midi2.Append(DOMAIN=domain,MAJOR=2,MINOR=1,MICRO=1)
index 406d4978c468894407fa2b393ae721b4e14b19c3..cbe179fb6b1b8fca504bebb8134746622a9dc0e2 100644 (file)
@@ -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
     
index 96a958b9e5c85135636e2321f98766f7f4dd9f04..24ca6f8db8e957304e5b23353f6f749475a80b84 100644 (file)
@@ -81,6 +81,8 @@ public:
     
     OutputList getOutputDescriptors() const;
 
+    void reset();
+
     FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
     
     FeatureSet getRemainingFeatures();
index 3ce51cedd2d95d46284198115404fe437a60d5d0..fe676bcafd794647d5b652656d57f8dba1a47027 100644 (file)
@@ -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;
     }
 
index e706414ae2416ab6a3ca644815040601c66a0d28..273925f96d21d3bd5607d8e1130b24117ca3c374 100644 (file)
@@ -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
 
 #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 {
index 14515fb63bfdb24a45b8cd4cf7bfac779fe08617..5000e5a2a4434aa565962f64e83ddc93f07cda59 100644 (file)
@@ -1,4 +1,4 @@
 #ifndef __ardour_svn_revision_h__
 #define __ardour_svn_revision_h__
-static const char* ardour_svn_revision = "2918";
+static const char* ardour_svn_revision = "2985";
 #endif