meet rhythm ferret: cute, furry and always on time (ardour build now requires fftw3 & fftw3f, no exceptions, ever)

git-svn-id: svn://localhost/ardour2/branches/2.0-ongoing@2959 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Paul Davis 2008-01-25 05:35:46 +00:00
parent 28e6ad0091
commit d3f64c2848
29 changed files with 1231 additions and 116 deletions

View file

@ -793,9 +793,16 @@ def prep_libcheck(topenv, libinfo):
prep_libcheck(env, env)
#
# these are part of the Ardour source tree because they are C++
#
libraries['vamp'] = LibraryInfo (LIBS='vampsdk',
LIBPATH='#libs/vamp-sdk',
CPPPATH='#libs/vamp-sdk/vamp')
libraries['vamphost'] = LibraryInfo (LIBS='vamphostsdk',
LIBPATH='#libs/vamp-sdk',
CPPPATH='#libs/vamp-sdk/vamp')
env['RUBBERBAND'] = False

View file

@ -50,6 +50,10 @@ gtkardour.Merge ([
libraries['xml'],
libraries['xslt'],
libraries['samplerate'],
libraries['vamp'],
libraries['vamphost'],
libraries['fftw3f'],
libraries['fftw3'],
libraries['jack']
])
@ -75,7 +79,7 @@ if gtkardour['FFT_ANALYSIS']:
gtkardour.Append(CCFLAGS='-DFFT_ANALYSIS')
if gtkardour['RUBBERBAND']:
gtkardour.Merge ([ libraries['rubberband'], libraries['vamp'], libraries['fftw3f'], libraries['fftw3'] ])
gtkardour.Merge ([ libraries['rubberband'] ])
else:
gtkardour.Merge ([ libraries['soundtouch'] ])
@ -188,7 +192,6 @@ new_session_dialog.cc
option_editor.cc
opts.cc
pan_automation_time_axis.cc
panner.cc
panner2d.cc
panner_ui.cc
@ -200,6 +203,7 @@ public_editor.cc
redirect_automation_line.cc
redirect_automation_time_axis.cc
redirect_box.cc
rhythm_ferret.cc
audio_region_editor.cc
region_gain_line.cc
region_selection.cc

View file

@ -161,6 +161,7 @@
<menuitem action='select-prev-route'/>
</menu>
<menu name='Regions' action='Regions'>
<menuitem action='split-region-at-transients'/>
<menuitem action='crop'/>
<menuitem action='duplicate-region'/>
<menuitem action='multi-duplicate-region'/>

View file

@ -3,6 +3,7 @@
#include <AppKit/AppKit.h>
#include <Carbon/Carbon.h>
#include <AudioUnit/AudioUnitCarbonView.h>
#include <AudioUnit/AudioUnit.h>
/* fix up stupid apple macros */

View file

@ -79,6 +79,7 @@
#include "actions.h"
#include "gui_thread.h"
#include "sfdb_ui.h"
#include "rhythm_ferret.h"
#ifdef FFT_ANALYSIS
#include "analysis_window.h"
@ -328,6 +329,7 @@ Editor::Editor ()
_dragging_hscrollbar = false;
select_new_marker = false;
zoomed_to_region = false;
rhythm_ferret = 0;
scrubbing_direction = 0;
@ -1161,6 +1163,10 @@ Editor::connect_to_session (Session *t)
_playlist_selector->set_session (session);
nudge_clock.set_session (session);
if (rhythm_ferret) {
rhythm_ferret->set_session (session);
}
#ifdef FFT_ANALYSIS
if (analysis_window != 0)
analysis_window->set_session (session);
@ -4359,3 +4365,15 @@ Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<R
}
}
}
void
Editor::show_rhythm_ferret ()
{
if (rhythm_ferret == 0) {
rhythm_ferret = new RhythmFerret(*this);
}
rhythm_ferret->set_session (session);
rhythm_ferret->show ();
rhythm_ferret->present ();
}

View file

@ -105,6 +105,7 @@ class StreamView;
class AudioStreamView;
class ControlPoint;
class SoundFileOmega;
class RhythmFerret;
#ifdef FFT_ANALYSIS
class AnalysisWindow;
#endif
@ -361,6 +362,8 @@ class Editor : public PublicEditor
void toggle_meter_updating();
void show_rhythm_ferret();
protected:
void map_transport_state ();
void map_position_change (nframes_t);
@ -981,6 +984,7 @@ class Editor : public PublicEditor
void normalize_region ();
void denormalize_region ();
void adjust_region_scale_amplitude (bool up);
void split_region_at_transients ();
void use_region_as_bar ();
void use_range_as_bar ();
@ -2061,6 +2065,8 @@ class Editor : public PublicEditor
void select_next_route ();
void select_prev_route ();
RhythmFerret* rhythm_ferret;
};
#endif /* __ardour_editor_h__ */

View file

@ -364,6 +364,9 @@ Editor::register_actions ()
act = ActionManager::register_action (editor_actions, "set-tempo-from-edit-range", _("Set Tempo from Edit Range=Bar"), mem_fun(*this, &Editor::use_range_as_bar));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "split-region-at-transients", _("Other Temporary Label"), mem_fun(*this, &Editor::split_region_at_transients));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "crop", _("Crop"), mem_fun(*this, &Editor::crop_region_to_selection));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "insert-chunk", _("Insert Chunk"), bind (mem_fun(*this, &Editor::paste_named_selection), 1.0f));

View file

@ -43,10 +43,12 @@
#include <ardour/location.h>
#include <ardour/named_selection.h>
#include <ardour/audio_track.h>
#include <ardour/audiofilesource.h>
#include <ardour/audioplaylist.h>
#include <ardour/region_factory.h>
#include <ardour/playlist_factory.h>
#include <ardour/reverse.h>
#include <ardour/transient_detector.h>
#include <ardour/dB.h>
#include "ardour_ui.h"
@ -5013,3 +5015,83 @@ Editor::define_one_bar (nframes64_t start, nframes64_t end)
session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
commit_reversible_command ();
}
void
Editor::split_region_at_transients ()
{
list<nframes64_t> transients;
if (!session) {
return;
}
ExclusiveRegionSelection esr (*this, entered_regionview);
if (selection->regions.empty()) {
return;
}
show_rhythm_ferret ();
return;
#if 0
cerr << "selection size is " << selection->regions.size() << endl;
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ) {
RegionSelection::iterator tmp;
tmp = i;
++tmp;
cerr << "working on " << (*i)->get_item_name() << endl;
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
if (!ar) {
continue;
}
boost::shared_ptr<Playlist> pl = ar->playlist();
if (!pl) {
continue;
}
cerr << "getting transients\n";
ar->get_transients (transients);
nframes64_t start = ar->start();
nframes64_t pos = ar->position();
pl->freeze ();
pl->remove_region (ar);
cerr << "creating new regions from " << transients.size() << " transients\n";
for (list<nframes64_t>::iterator x = transients.begin(); x != transients.end(); ++x) {
nframes_t len = (*x) - start;
string new_name;
if (session->region_name (new_name, ar->name())) {
continue;
}
pl->add_region (RegionFactory::create (ar->get_sources(), start, len, new_name), pos);
start = (*x);
pos += len;
}
pl->thaw ();
transients.clear ();
cerr << "done with that one\n";
i = tmp;
}
#endif
}

View file

@ -0,0 +1,376 @@
#include <gtkmm/stock.h>
#include <gtkmm2ext/utils.h>
#include <pbd/memento_command.h>
#include <ardour/transient_detector.h>
#include <ardour/audiosource.h>
#include <ardour/audioregion.h>
#include <ardour/playlist.h>
#include <ardour/region_factory.h>
#include <ardour/session.h>
#include "rhythm_ferret.h"
#include "audio_region_view.h"
#include "public_editor.h"
#include "i18n.h"
using namespace std;
using namespace Gtk;
using namespace Gdk;
using namespace PBD;
using namespace ARDOUR;
/* order of these must match the AnalysisMode enums
in rhythm_ferret.h
*/
static const gchar * _analysis_mode_strings[] = {
N_("Percussive Onset"),
N_("Note Onset"),
0
};
RhythmFerret::RhythmFerret (PublicEditor& e)
: ArdourDialog (_("Rhythm Ferret"))
, editor (e)
, operation_frame (_("Operation"))
, selection_frame (_("Selection"))
, ferret_frame (_("Analysis"))
, logo (0)
, region_split_button (operation_button_group, _("Split Region"))
, tempo_button (operation_button_group, _("Set Tempo Map"))
, region_conform_button (operation_button_group, _("Conform Region"))
, analysis_mode_label (_("Mode"))
, detection_threshold_adjustment (3, 0, 20, 1, 4)
, detection_threshold_scale (detection_threshold_adjustment)
, detection_threshold_label (_("Threshold"))
, sensitivity_adjustment (40, 0, 100, 1, 10)
, sensitivity_scale (sensitivity_adjustment)
, sensitivity_label (_("Sensitivity"))
, analyze_button (_("Analyze"))
, trigger_gap_adjustment (3, 0, 100, 1, 10)
, trigger_gap_spinner (trigger_gap_adjustment)
, trigger_gap_label (_("Trigger gap (msecs)"))
, action_button (Stock::APPLY)
{
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);
op_packer.pack_start (region_split_button, false, false);
op_packer.pack_start (tempo_button, false, false);
op_packer.pack_start (region_conform_button, false, false);
operation_frame.add (op_packer);
HBox* box;
ferret_packer.set_spacing (6);
ferret_packer.set_border_width (6);
vector<string> strings;
analysis_mode_strings = I18N (_analysis_mode_strings);
Gtkmm2ext::set_popdown_strings (analysis_mode_selector, analysis_mode_strings);
analysis_mode_selector.set_active_text (analysis_mode_strings.front());
box = manage (new HBox);
box->set_spacing (6);
box->pack_start (analysis_mode_label, false, false);
box->pack_start (analysis_mode_selector, true, true);
ferret_packer.pack_start (*box, false, false);
box = manage (new HBox);
box->set_spacing (6);
box->pack_start (detection_threshold_label, false, false);
box->pack_start (detection_threshold_scale, true, true);
ferret_packer.pack_start (*box, false, false);
box = manage (new HBox);
box->set_spacing (6);
box->pack_start (sensitivity_label, false, false);
box->pack_start (sensitivity_scale, true, true);
ferret_packer.pack_start (*box, false, false);
box = manage (new HBox);
box->set_spacing (6);
box->pack_start (trigger_gap_label, false, false);
box->pack_start (trigger_gap_spinner, false, false);
ferret_packer.pack_start (*box, false, false);
ferret_packer.pack_start (analyze_button, false, false);
analyze_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::run_analysis));
ferret_frame.add (ferret_packer);
// Glib::RefPtr<Pixbuf> logo_pixbuf ("somefile");
if (logo) {
lower_hpacker.pack_start (*logo, false, false);
}
lower_hpacker.pack_start (operation_clarification_label, false, false);
lower_hpacker.pack_start (action_button, false, false);
action_button.signal_clicked().connect (mem_fun (*this, &RhythmFerret::do_action));
get_vbox()->set_border_width (6);
get_vbox()->set_spacing (6);
get_vbox()->pack_start (upper_hpacker, true, true);
get_vbox()->pack_start (lower_hpacker, false, false);
show_all ();
}
RhythmFerret::~RhythmFerret()
{
if (logo) {
delete logo;
}
}
RhythmFerret::AnalysisMode
RhythmFerret::get_analysis_mode () const
{
string str = analysis_mode_selector.get_active_text ();
if (str == _(_analysis_mode_strings[(int) NoteOnset])) {
return NoteOnset;
}
return PercussionOnset;
}
RhythmFerret::Action
RhythmFerret::get_action () const
{
if (tempo_button.get_active()) {
return DefineTempoMap;
} else if (region_conform_button.get_active()) {
return ConformRegion;
}
return SplitRegion;
}
void
RhythmFerret::run_analysis ()
{
if (!session) {
return;
}
RegionSelection& regions (editor.get_selection().regions);
current_results.clear ();
if (regions.empty()) {
return;
}
for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
boost::shared_ptr<Readable> rd = boost::static_pointer_cast<AudioRegion> ((*i)->region());
switch (get_analysis_mode()) {
case PercussionOnset:
run_percussion_onset_analysis (rd, (*i)->region()->position(), current_results);
break;
default:
break;
}
}
for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ++i) {
(*i)->get_time_axis_view().show_temporary_lines (current_results);
}
}
int
RhythmFerret::run_percussion_onset_analysis (boost::shared_ptr<Readable> readable, nframes64_t offset, vector<nframes64_t>& results)
{
TransientDetector t (session->frame_rate());
bool existing_results = !results.empty();
for (uint32_t i = 0; i < readable->n_channels(); ++i) {
vector<nframes64_t> these_results;
t.reset ();
t.set_threshold (detection_threshold_adjustment.get_value());
t.set_sensitivity (sensitivity_adjustment.get_value());
if (t.run ("", readable, i, these_results)) {
continue;
}
/* translate all transients to give absolute position */
for (vector<nframes64_t>::iterator i = these_results.begin(); i != these_results.end(); ++i) {
(*i) += offset;
}
/* merge */
results.insert (results.end(), these_results.begin(), these_results.end());
}
if (!results.empty() && (existing_results || readable->n_channels() > 1)) {
/* 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;
}
}
}
return 0;
}
void
RhythmFerret::do_action ()
{
if (!session || current_results.empty()) {
return;
}
switch (get_action()) {
case SplitRegion:
do_split_action ();
break;
default:
break;
}
}
void
RhythmFerret::do_split_action ()
{
/* this can/will change the current selection, so work with a copy */
RegionSelection& regions (editor.get_selection().regions);
if (regions.empty()) {
return;
}
session->begin_reversible_command (_("split regions (rhythm ferret)"));
for (RegionSelection::iterator i = regions.begin(); i != regions.end(); ) {
RegionSelection::iterator tmp;
tmp = i;
++tmp;
(*i)->get_time_axis_view().hide_temporary_lines ();
do_region_split ((*i), current_results);
/* i is invalid at this point */
i = tmp;
}
session->commit_reversible_command ();
}
void
RhythmFerret::do_region_split (RegionView* rv, const vector<nframes64_t>& positions)
{
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (rv->region());
if (!ar) {
return;
}
boost::shared_ptr<Playlist> pl = ar->playlist();
if (!pl) {
return;
}
vector<nframes64_t>::const_iterator x;
nframes64_t pos = ar->position();
XMLNode& before (pl->get_state());
x = positions.begin();
while (x != positions.end()) {
if ((*x) > pos) {
break;
}
}
if (x == positions.end()) {
return;
}
pl->freeze ();
pl->remove_region (ar);
do {
/* file start = original start + how far we from the initial position ?
*/
nframes64_t file_start = ar->start() + (pos - ar->position());
/* length = next position - current position
*/
nframes64_t len = (*x) - pos;
string new_name;
if (session->region_name (new_name, ar->name())) {
continue;
}
pl->add_region (RegionFactory::create (ar->get_sources(), file_start, len, new_name), pos);
pos += len;
++x;
} while (x != positions.end() && (*x) < ar->last_frame());
pl->thaw ();
XMLNode& after (pl->get_state());
session->add_command (new MementoCommand<Playlist>(*pl, &before, &after));
}
void
RhythmFerret::set_session (Session* s)
{
ArdourDialog::set_session (s);
current_results.clear ();
}

100
gtk2_ardour/rhythm_ferret.h Normal file
View file

@ -0,0 +1,100 @@
#ifndef __gtk2_ardour_rhythm_ferret_h__
#define __gtk2_ardour_rhythm_ferret_h__
#include <gtkmm/box.h>
#include <gtkmm/scale.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/radiobutton.h>
#include <gtkmm/radiobuttongroup.h>
#include <gtkmm/frame.h>
#include <gtkmm/image.h>
#include <gtkmm/comboboxtext.h>
#include <gtkmm/button.h>
#include <gtkmm/label.h>
#include "ardour_dialog.h"
namespace ARDOUR {
class Readable;
}
class PublicEditor;
class RegionView;
class RhythmFerret : public ArdourDialog {
public:
/* order of these enums must match the _analyse_mode_strings
in rhythm_ferret.cc
*/
enum AnalysisMode {
PercussionOnset,
NoteOnset
};
enum Action {
SplitRegion,
DefineTempoMap,
ConformRegion
};
RhythmFerret (PublicEditor&);
~RhythmFerret ();
void set_session (ARDOUR::Session*);
private:
PublicEditor& editor;
Gtk::HBox upper_hpacker;
Gtk::HBox lower_hpacker;
Gtk::Frame operation_frame;
Gtk::Frame selection_frame;
Gtk::Frame ferret_frame;
Gtk::VBox op_logo_packer;
Gtk::Image* logo;
/* operation frame */
Gtk::VBox op_packer;
Gtk::RadioButtonGroup operation_button_group;
Gtk::RadioButton region_split_button;
Gtk::RadioButton tempo_button;
Gtk::RadioButton region_conform_button;
/* analysis frame */
Gtk::VBox ferret_packer;
Gtk::ComboBoxText analysis_mode_selector;
Gtk::Label analysis_mode_label;
Gtk::Adjustment detection_threshold_adjustment;
Gtk::HScale detection_threshold_scale;
Gtk::Label detection_threshold_label;
Gtk::Adjustment sensitivity_adjustment;
Gtk::HScale sensitivity_scale;
Gtk::Label sensitivity_label;
Gtk::Button analyze_button;
Gtk::Adjustment trigger_gap_adjustment;
Gtk::SpinButton trigger_gap_spinner;
Gtk::Label trigger_gap_label;
Gtk::Label operation_clarification_label;
Gtk::Button action_button;
std::vector<std::string> analysis_mode_strings;
std::vector<nframes64_t> 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);
void do_action ();
void do_split_action ();
void do_region_split (RegionView* rv, const std::vector<nframes64_t>&);
};
#endif /* __gtk2_ardour_rhythm_ferret_h__ */

View file

@ -43,6 +43,7 @@
#include "public_editor.h"
#include "time_axis_view.h"
#include "simplerect.h"
#include "simpleline.h"
#include "selection.h"
#include "keyboard.h"
#include "rgb_macros.h"
@ -1099,3 +1100,37 @@ TimeAxisView::covers_y_position (double y)
return 0;
}
void
TimeAxisView::show_temporary_lines (const vector<nframes64_t>& pos)
{
while (temp_lines.size()< pos.size()) {
ArdourCanvas::SimpleLine* l = new ArdourCanvas::SimpleLine (*canvas_display);
l->property_color_rgba() = (guint) ARDOUR_UI::config()->canvasvar_ZeroLine.get();
l->property_y1() = 0;
l->property_y2() = height;
temp_lines.push_back (l);
}
while (temp_lines.size() > pos.size()) {
ArdourCanvas::SimpleLine *line = temp_lines.back();
temp_lines.pop_back ();
delete line;
}
vector<nframes64_t>::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) {
(*l)->property_x1() = editor.frame_to_pixel ((double) *i);
(*l)->property_x2() = editor.frame_to_pixel ((double) *i);
}
}
void
TimeAxisView::hide_temporary_lines ()
{
for (list<ArdourCanvas::SimpleLine*>::iterator l = temp_lines.begin(); l != temp_lines.end(); ++l) {
(*l)->hide ();
}
}

View file

@ -176,6 +176,9 @@ 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 hide_temporary_lines ();
virtual void set_samples_per_unit (double);
virtual void show_selection (TimeSelection&);
virtual void hide_selection ();
@ -322,6 +325,8 @@ class TimeAxisView : public virtual AxisView
void set_height_pixels (uint32_t h);
void color_handler ();
list<ArdourCanvas::SimpleLine*> temp_lines;
}; /* class TimeAxisView */
#endif /* __ardour_gtk_time_axis_h__ */

View file

@ -93,6 +93,7 @@ sndfilesource.cc
source.cc
source_factory.cc
tempo.cc
transient_detector.cc
utils.cc
version.cc
mix.cc
@ -267,11 +268,15 @@ ardour.Merge ([
libraries['pbd'],
libraries['midi++2'],
libraries['glib2'],
libraries['glibmm2']
libraries['glibmm2'],
libraries['vamp'],
libraries['vamphost'],
libraries['fftw3f'],
libraries['fftw3'],
])
if ardour['RUBBERBAND']:
ardour.Merge ([ libraries['rubberband'], libraries['vamp'], libraries['fftw3f'] ])
ardour.Merge ([ libraries['rubberband']])
timefx_sources += [ 'rb_effect.cc' ]
else:
ardour.Merge ([ libraries['soundtouch'] ])

View file

@ -0,0 +1,74 @@
/*
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.
*/
#ifndef __ardour_audioanalyser_h__
#define __ardour_audioanalyser_h__
#include <vector>
#include <string>
#include <ostream>
#include <fstream>
#include <vamp-sdk/Plugin.h>
#include <ardour/audioregion.h>
namespace ARDOUR {
class Readable;
class Session;
class AudioAnalyser {
public:
typedef Vamp::Plugin AnalysisPlugin;
typedef std::string AnalysisPluginKey;
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 boost::shared_ptr<Readable> to read data from
and a reference to a type-specific container to return the
results.
*/
void reset ();
protected:
float sample_rate;
AnalysisPlugin* plugin;
AnalysisPluginKey plugin_key;
nframes64_t bufsize;
nframes64_t stepsize;
int initialize_plugin (AnalysisPluginKey name, float sample_rate);
int analyse (const std::string& path, boost::shared_ptr<Readable>, uint32_t channel);
/* instances of an analysis object will have this method called
whenever there are results to process. if out is non-null,
the data should be written to the stream it points to.
*/
virtual int use_features (Vamp::Plugin::FeatureSet&, std::ostream*) = 0;
};
} /* namespace */
#endif /* __ardour_audioanalyser_h__ */

View file

@ -21,6 +21,7 @@
#define __ardour_audio_region_h__
#include <vector>
#include <list>
#include <pbd/fastlog.h>
#include <pbd/undo.h>
@ -59,6 +60,7 @@ class AudioRegion : public Region
bool speed_mismatch (float) const;
boost::shared_ptr<AudioSource> source (uint32_t n=0) const { if (n < sources.size()) return sources[n]; else return sources[0]; }
const SourceList& get_sources() const { return sources; }
void set_scale_amplitude (gain_t);
gain_t scale_amplitude() const { return _scale_amplitude; }
@ -82,11 +84,16 @@ class AudioRegion : public Region
nframes_t offset, nframes_t cnt,
uint32_t chan_n=0, double samples_per_unit= 1.0) const;
/* Readable interface */
virtual nframes64_t read (Sample*, nframes64_t pos, nframes64_t cnt, int channel) const;
virtual nframes64_t readable_length() const { return length(); }
virtual nframes_t read_at (Sample *buf, Sample *mixdown_buf,
float *gain_buf, nframes_t position, nframes_t cnt,
uint32_t chan_n = 0,
nframes_t read_frames = 0,
nframes_t skip_frames = 0) const;
float *gain_buf, nframes_t position, nframes_t cnt,
uint32_t chan_n = 0,
nframes_t read_frames = 0,
nframes_t skip_frames = 0) const;
nframes_t master_read_at (Sample *buf, Sample *mixdown_buf,
float *gain_buf,
@ -146,7 +153,7 @@ class AudioRegion : public Region
AudioRegion (boost::shared_ptr<AudioSource>, nframes_t start, nframes_t length);
AudioRegion (boost::shared_ptr<AudioSource>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
AudioRegion (SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
AudioRegion (const SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
AudioRegion (boost::shared_ptr<const AudioRegion>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
AudioRegion (boost::shared_ptr<const AudioRegion>);
AudioRegion (boost::shared_ptr<AudioSource>, const XMLNode&);
@ -161,10 +168,11 @@ class AudioRegion : public Region
void recompute_gain_at_start ();
nframes_t _read_at (const SourceList&, Sample *buf, Sample *mixdown_buffer,
float *gain_buffer, nframes_t position, nframes_t cnt,
uint32_t chan_n = 0,
nframes_t read_frames = 0,
nframes_t skip_frames = 0) const;
float *gain_buffer, nframes_t position, nframes_t cnt,
uint32_t chan_n = 0,
nframes_t read_frames = 0,
nframes_t skip_frames = 0,
bool raw = false) const;
bool verify_start (nframes_t position);
bool verify_length (nframes_t& length);

View file

@ -57,7 +57,8 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
virtual nframes_t natural_position() const { return 0; }
/* returns the number of items in this `audio_source' */
nframes64_t readable_length() const { return _length; }
uint32_t n_channels() const { return 1; }
virtual nframes_t length() const {
return _length;
@ -65,6 +66,15 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
virtual nframes_t available_peaks (double zoom) const;
/* stopgap until nframes_t becomes nframes64_t. this function is needed by the Readable interface */
virtual nframes64_t read (Sample *dst, nframes64_t start, nframes64_t cnt, int channel) const {
/* XXX currently ignores channel, assuming that source is always mono, which
historically has been true.
*/
return read (dst, (nframes_t) start, (nframes_t) cnt);
}
virtual nframes_t read (Sample *dst, nframes_t start, nframes_t cnt) const;
virtual nframes_t write (Sample *src, nframes_t cnt);
@ -112,6 +122,9 @@ 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;
@ -146,6 +159,8 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
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&);
private:
int peakfile;
nframes_t peak_leftover_cnt;

View file

@ -40,6 +40,8 @@ class PluginManager {
PluginManager ();
~PluginManager ();
/* realtime plugin APIs */
ARDOUR::PluginInfoList &vst_plugin_info () { return _vst_plugin_info; }
ARDOUR::PluginInfoList &ladspa_plugin_info () { return _ladspa_plugin_info; }
ARDOUR::PluginInfoList &lv2_plugin_info () { return _lv2_plugin_info; }

View file

@ -27,6 +27,7 @@
#include <pbd/statefuldestructible.h>
#include <ardour/ardour.h>
#include <ardour/readable.h>
class XMLNode;
@ -40,7 +41,7 @@ enum RegionEditState {
EditChangesID = 2
};
class Region : public PBD::StatefulDestructible, public boost::enable_shared_from_this<Region>
class Region : public PBD::StatefulDestructible, public Readable, public boost::enable_shared_from_this<Region>
{
public:
enum Flag {

View file

@ -49,7 +49,7 @@ class RegionFactory {
nframes_t length, std::string name,
layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
static boost::shared_ptr<Region> create (boost::shared_ptr<Source>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
static boost::shared_ptr<Region> create (SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
static boost::shared_ptr<Region> create (const SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags, bool announce = true);
static boost::shared_ptr<Region> create (boost::shared_ptr<Region>);
static boost::shared_ptr<Region> create (Session&, XMLNode&, bool);
static boost::shared_ptr<Region> create (SourceList &, const XMLNode&);

View file

@ -252,6 +252,9 @@ class Session : public PBD::StatefulDestructible
std::string peak_dir () const;
std::string dead_sound_dir () const;
std::string automation_dir () const;
std::string analysis_dir() const;
int ensure_subdirs ();
Glib::ustring peak_path (Glib::ustring) const;

View file

@ -28,13 +28,14 @@
#include <pbd/statefuldestructible.h>
#include <ardour/ardour.h>
#include <ardour/readable.h>
namespace ARDOUR {
class Session;
class Playlist;
class Source : public PBD::StatefulDestructible
class Source : public PBD::StatefulDestructible, public ARDOUR::Readable
{
public:
Source (Session&, std::string name);

View file

@ -0,0 +1,52 @@
/*
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.
*/
#ifndef __ardour_transient_detector_h__
#define __ardour_transient_detector_h__
#include <ardour/audioanalyser.h>
namespace ARDOUR {
class AudioSource;
class Session;
class TransientDetector : public AudioAnalyser
{
public:
TransientDetector (float sample_rate);
~TransientDetector();
void set_threshold (float);
void set_sensitivity (float);
float get_threshold () const;
float get_sensitivity () const;
int run (const std::string& path, boost::shared_ptr<Readable>, uint32_t channel, std::vector<nframes64_t>& results);
protected:
std::vector<nframes64_t>* current_results;
int use_features (Vamp::Plugin::FeatureSet&, std::ostream*);
};
} /* namespace */
#endif /* __ardour_audioanalyser_h__ */

View file

@ -0,0 +1,157 @@
#include <vamp-sdk/hostext/PluginLoader.h>
#include <glibmm/miscutils.h>
#include <glibmm/fileutils.h>
#include <glib/gstdio.h> // for g_remove()
#include <pbd/error.h>
#include <ardour/audioanalyser.h>
#include <ardour/readable.h>
#include <ardour/readable.h>
#include "i18n.h"
using namespace std;
using namespace Vamp;
using namespace PBD;
using namespace ARDOUR;
AudioAnalyser::AudioAnalyser (float sr, AnalysisPluginKey key)
: sample_rate (sr)
, plugin (0)
, plugin_key (key)
{
}
AudioAnalyser::~AudioAnalyser ()
{
}
int
AudioAnalyser::initialize_plugin (AnalysisPluginKey key, float sr)
{
using namespace Vamp::HostExt;
PluginLoader* loader (PluginLoader::getInstance());
plugin = loader->loadPlugin (key, sr, PluginLoader::ADAPT_ALL);
if (!plugin) {
return -1;
}
if ((bufsize = plugin->getPreferredBlockSize ()) == 0) {
bufsize = 65536;
}
if ((stepsize = plugin->getPreferredStepSize()) == 0) {
stepsize = bufsize;
}
if (plugin->getMinChannelCount() > 1) {
delete plugin;
return -1;
}
if (!plugin->initialise (1, stepsize, bufsize)) {
delete plugin;
return -1;
}
return 0;
}
void
AudioAnalyser::reset ()
{
if (plugin) {
plugin->reset ();
}
}
int
AudioAnalyser::analyse (const string& path, boost::shared_ptr<Readable> src, uint32_t channel)
{
ofstream ofile;
Plugin::FeatureSet onsets;
int ret = -1;
bool done = false;
Sample* data = 0;
nframes64_t len = src->readable_length();
nframes64_t pos = 0;
float* bufs[1] = { 0 };
if (!path.empty()) {
ofile.open (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;
while (!done) {
nframes64_t to_read;
/* read from source */
to_read = min ((len - pos), bufsize);
if (src->read (data, pos, to_read, channel) != to_read) {
cerr << "bad read\n";
goto out;
}
/* zero fill buffer if necessary */
if (to_read != bufsize) {
memset (data + to_read, 0, (bufsize - to_read));
}
onsets = plugin->process (bufs, RealTime::fromSeconds ((double) pos / sample_rate));
if (use_features (onsets, (path.empty() ? &ofile : 0))) {
goto out;
}
pos += stepsize;
if (pos >= len) {
done = true;
}
}
/* finish up VAMP plugin */
onsets = plugin->getRemainingFeatures ();
if (use_features (onsets, (path.empty() ? &ofile : 0))) {
goto out;
}
ret = 0;
out:
/* works even if it has not been opened */
ofile.close ();
if (ret) {
g_remove (path.c_str());
}
if (data) {
delete data;
}
return ret;
}

View file

@ -20,6 +20,7 @@
#include <cmath>
#include <climits>
#include <cfloat>
#include <algorithm>
#include <set>
@ -110,15 +111,15 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
listen_to_my_curves ();
}
AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
AudioRegion::AudioRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
: Region (start, length, name, layer, flags),
_fade_in (0.0, 2.0, 1.0, false),
_fade_out (0.0, 2.0, 1.0, false),
_envelope (0.0, 2.0, 1.0, false)
{
/* basic AudioRegion constructor */
for (SourceList::iterator i=srcs.begin(); i != srcs.end(); ++i) {
for (SourceList::const_iterator i=srcs.begin(); i != srcs.end(); ++i) {
sources.push_back (*i);
master_sources.push_back (*i);
(*i)->GoingAway.connect (mem_fun (*this, &AudioRegion::source_deleted));
@ -437,12 +438,20 @@ AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nfra
}
}
nframes64_t
AudioRegion::read (Sample* buf, nframes64_t position, nframes64_t cnt, int channel) const
{
/* raw read, no fades, no gain, nada */
return _read_at (sources, buf, 0, 0, _position + position, cnt, channel, 0, 0, true);
}
nframes_t
AudioRegion::read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position,
nframes_t cnt,
uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const
{
return _read_at (sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, read_frames, skip_frames);
/* regular diskstream/butler read complete with fades etc */
return _read_at (sources, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, read_frames, skip_frames, false);
}
nframes_t
@ -455,13 +464,16 @@ AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_bu
nframes_t
AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buffer, float *gain_buffer,
nframes_t position, nframes_t cnt,
uint32_t chan_n, nframes_t read_frames, nframes_t skip_frames) const
uint32_t chan_n,
nframes_t read_frames,
nframes_t skip_frames,
bool raw) const
{
nframes_t internal_offset;
nframes_t buf_offset;
nframes_t to_read;
if (muted()) {
if (muted() && !raw) {
return 0; /* read nothing */
}
@ -484,14 +496,16 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
return 0; /* read nothing */
}
if (opaque()) {
if (opaque() || raw) {
/* overwrite whatever is there */
mixdown_buffer = buf + buf_offset;
} else {
mixdown_buffer += buf_offset;
}
_read_data_count = 0;
if (!raw) {
_read_data_count = 0;
}
if (chan_n < n_channels()) {
@ -500,7 +514,9 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
return 0; /* "read nothing" */
}
_read_data_count += srcs[chan_n]->read_data_count();
if (!raw) {
_read_data_count += srcs[chan_n]->read_data_count();
}
} else {
@ -512,37 +528,41 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
/* no fades required */
goto merge;
if (!raw) {
goto merge;
}
}
/* fade in */
if (_flags & FadeIn) {
nframes_t fade_in_length = (nframes_t) _fade_in.back()->when;
/* see if this read is within the fade in */
if (internal_offset < fade_in_length) {
nframes_t limit;
limit = min (to_read, fade_in_length - internal_offset);
_fade_in.get_vector (internal_offset, internal_offset+limit, gain_buffer, limit);
for (nframes_t n = 0; n < limit; ++n) {
mixdown_buffer[n] *= gain_buffer[n];
if (!raw) {
if (_flags & FadeIn) {
nframes_t fade_in_length = (nframes_t) _fade_in.back()->when;
/* see if this read is within the fade in */
if (internal_offset < fade_in_length) {
nframes_t limit;
limit = min (to_read, fade_in_length - internal_offset);
_fade_in.get_vector (internal_offset, internal_offset+limit, gain_buffer, limit);
for (nframes_t n = 0; n < limit; ++n) {
mixdown_buffer[n] *= gain_buffer[n];
}
}
}
}
/* fade out */
if (_flags & FadeOut) {
/* see if some part of this read is within the fade out */
/* fade out */
if (_flags & FadeOut) {
/* see if some part of this read is within the fade out */
/* ................. >| REGION
_length
@ -553,65 +573,66 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
|--------------|
^internal_offset
^internal_offset + to_read
we need the intersection of [internal_offset,internal_offset+to_read] with
[_length - fade_out_length, _length]
we need the intersection of [internal_offset,internal_offset+to_read] with
[_length - fade_out_length, _length]
*/
nframes_t fade_out_length = (nframes_t) _fade_out.back()->when;
nframes_t fade_interval_start = max(internal_offset, _length-fade_out_length);
nframes_t fade_interval_end = min(internal_offset + to_read, _length);
if (fade_interval_end > fade_interval_start) {
/* (part of the) the fade out is in this buffer */
nframes_t fade_out_length = (nframes_t) _fade_out.back()->when;
nframes_t fade_interval_start = max(internal_offset, _length-fade_out_length);
nframes_t fade_interval_end = min(internal_offset + to_read, _length);
nframes_t limit = fade_interval_end - fade_interval_start;
nframes_t curve_offset = fade_interval_start - (_length-fade_out_length);
nframes_t fade_offset = fade_interval_start - internal_offset;
_fade_out.get_vector (curve_offset,curve_offset+limit, gain_buffer, limit);
for (nframes_t n = 0, m = fade_offset; n < limit; ++n, ++m) {
mixdown_buffer[m] *= gain_buffer[n];
if (fade_interval_end > fade_interval_start) {
/* (part of the) the fade out is in this buffer */
nframes_t limit = fade_interval_end - fade_interval_start;
nframes_t curve_offset = fade_interval_start - (_length-fade_out_length);
nframes_t fade_offset = fade_interval_start - internal_offset;
_fade_out.get_vector (curve_offset,curve_offset+limit, gain_buffer, limit);
for (nframes_t n = 0, m = fade_offset; n < limit; ++n, ++m) {
mixdown_buffer[m] *= gain_buffer[n];
}
}
}
/* Regular gain curves */
if (envelope_active()) {
_envelope.get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
if (_scale_amplitude != 1.0f) {
for (nframes_t n = 0; n < to_read; ++n) {
mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
}
} else {
for (nframes_t n = 0; n < to_read; ++n) {
mixdown_buffer[n] *= gain_buffer[n];
}
}
} else if (_scale_amplitude != 1.0f) {
Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
}
merge:
if (!opaque()) {
/* gack. the things we do for users.
*/
buf += buf_offset;
for (nframes_t n = 0; n < to_read; ++n) {
buf[n] += mixdown_buffer[n];
}
}
}
/* Regular gain curves */
if (envelope_active()) {
_envelope.get_vector (internal_offset, internal_offset + to_read, gain_buffer, to_read);
if (_scale_amplitude != 1.0f) {
for (nframes_t n = 0; n < to_read; ++n) {
mixdown_buffer[n] *= gain_buffer[n] * _scale_amplitude;
}
} else {
for (nframes_t n = 0; n < to_read; ++n) {
mixdown_buffer[n] *= gain_buffer[n];
}
}
} else if (_scale_amplitude != 1.0f) {
Session::apply_gain_to_buffer (mixdown_buffer, to_read, _scale_amplitude);
}
merge:
if (!opaque()) {
/* gack. the things we do for users.
*/
buf += buf_offset;
for (nframes_t n = 0; n < to_read; ++n) {
buf[n] += mixdown_buffer[n];
}
}
return to_read;
}

View file

@ -27,10 +27,12 @@
#include <ctime>
#include <cmath>
#include <iomanip>
#include <fstream>
#include <algorithm>
#include <vector>
#include <glibmm/fileutils.h>
#include <glibmm/miscutils.h>
#include <pbd/xml++.h>
#include <pbd/pthread_utils.h>
@ -38,6 +40,7 @@
#include <ardour/audiosource.h>
#include <ardour/cycle_timer.h>
#include <ardour/session.h>
#include <ardour/transient_detector.h>
#include "i18n.h"
@ -917,3 +920,50 @@ 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);
}

View file

@ -90,7 +90,7 @@ RegionFactory::create (Session& session, XMLNode& node, bool yn)
}
boost::shared_ptr<Region>
RegionFactory::create (SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags, bool announce)
RegionFactory::create (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags, bool announce)
{
boost::shared_ptr<AudioRegion> arp (new AudioRegion (srcs, start, length, name, layer, flags));
boost::shared_ptr<Region> ret (boost::static_pointer_cast<Region> (arp));

View file

@ -34,6 +34,7 @@
#include <glibmm/thread.h>
#include <glibmm/miscutils.h>
#include <glibmm/fileutils.h>
#include <pbd/error.h>
#include <glibmm/thread.h>
@ -290,7 +291,8 @@ Session::Session (AudioEngine &eng,
first_stage_init (fullpath, snapshot_name);
new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
new_session = !Glib::file_test (_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
if (new_session) {
if (create (new_session, mix_template, compute_initial_length())) {
destroy ();

View file

@ -469,19 +469,14 @@ Session::setup_raid_path (string path)
}
int
Session::create (bool& new_session, const string& mix_template, nframes_t initial_length)
Session::ensure_subdirs ()
{
string dir;
if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session dir \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
return -1;
}
dir = peak_dir ();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
@ -492,7 +487,7 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia
dir = sound_dir ();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
error << string_compose(_("Session: cannot create session sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
}
@ -500,17 +495,39 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia
dir = dead_sound_dir ();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session dead sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
error << string_compose(_("Session: cannot create session dead sounds folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
dir = export_dir ();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session export dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
error << string_compose(_("Session: cannot create session export folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
dir = analysis_dir ();
if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session analysis folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
return -1;
}
return 0;
}
int
Session::create (bool& new_session, const string& mix_template, nframes_t initial_length)
{
if (g_mkdir_with_parents (_path.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session folder \"%1\" (%2)"), _path, strerror (errno)) << endmsg;
return -1;
}
if (ensure_subdirs ()) {
return -1;
}
/* check new_session so we don't overwrite an existing one */
@ -559,7 +576,6 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia
_state_of_the_state = Clean;
save_state ("");
return 0;
@ -2044,6 +2060,14 @@ Session::automation_dir () const
return res;
}
string
Session::analysis_dir () const
{
string res = _path;
res += "analysis/";
return res;
}
string
Session::template_dir ()
{
@ -2808,7 +2832,7 @@ Session::cleanup_sources (Session::cleanup_report& rep)
newpath += dead_sound_dir_name;
if (g_mkdir_with_parents (newpath.c_str(), 0755) < 0) {
error << string_compose(_("Session: cannot create session peakfile dir \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
return -1;
}

View file

@ -0,0 +1,62 @@
#include <ardour/transient_detector.h>
#include "i18n.h"
using namespace Vamp;
using namespace ARDOUR;
using namespace std;
TransientDetector::TransientDetector (float sr)
: AudioAnalyser (sr, X_("vamp-example-plugins:percussiononsets"))
{
cerr << "plugin in constructor = " << plugin << endl;
}
TransientDetector::~TransientDetector()
{
}
int
TransientDetector::run (const std::string& path, boost::shared_ptr<Readable> src, uint32_t channel, vector<nframes64_t>& results)
{
current_results = &results;
int ret = analyse (path, src, channel);
current_results = 0;
return ret;
}
int
TransientDetector::use_features (Plugin::FeatureSet& features, ostream* out)
{
const Plugin::FeatureList& fl (features[0]);
for (Plugin::FeatureList::const_iterator f = fl.begin(); f != fl.end(); ++f) {
if ((*f).hasTimestamp) {
if (out) {
(*out) << (*f).timestamp.toString() << endl;
}
current_results->push_back (RealTime::realTime2Frame ((*f).timestamp, sample_rate));
}
}
return 0;
}
void
TransientDetector::set_threshold (float val)
{
if (plugin) {
plugin->setParameter ("threshold", val);
}
}
void
TransientDetector::set_sensitivity (float val)
{
if (plugin) {
plugin->setParameter ("sensitivity", val);
}
}