Merge with trunk R2978.

git-svn-id: svn://localhost/ardour2/branches/3.0@2988 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2008-02-02 03:57:35 +00:00
parent 85ea9028b5
commit 9f63ab9931
92 changed files with 4426 additions and 668 deletions

View File

@ -33,7 +33,7 @@ opts.AddOptions(
BoolOption('NATIVE_OSX_KEYS', 'Build key bindings file that matches OS X conventions', 0),
BoolOption('DEBUG', 'Set to build with debugging information and no optimizations', 0),
PathOption('DESTDIR', 'Set the intermediate install "prefix"', '/'),
EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'tiger', 'panther', 'none' ), ignorecase=2),
EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'tiger', 'panther', 'leopard', 'none' ), ignorecase=2),
BoolOption('DMALLOC', 'Compile and link using the dmalloc library', 0),
BoolOption('EXTRA_WARN', 'Compile with -Wextra, -ansi, and -pedantic. Might break compilation. For pedants', 0),
BoolOption('FFT_ANALYSIS', 'Include FFT analysis window', 0),
@ -531,15 +531,19 @@ if env['FFT_ANALYSIS']:
if env['LV2']:
conf = env.Configure(custom_tests = { 'CheckPKGExists' : CheckPKGExists })
if conf.CheckPKGExists ('\"slv2 >= 0.4.4\"'):
if conf.CheckPKGExists ('\"slv2 >= 0.6.0\"'):
libraries['slv2'] = LibraryInfo()
libraries['slv2'].ParseConfig('pkg-config --cflags --libs slv2')
else:
print 'Building Ardour with LV2 support requires SLV2 >= 0.4.4'
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.'
print 'Until the 2.3 release, Ardour requires SLV2 out of SVN.'
print 'Testing would be very much appreciated! svn co http://svn.drobilla.net/lad/slv2'
env['LV2'] = 0
conf.Finish()
else:
print 'LV2 support is not enabled. Build with \'scons LV2=1\' to enable.'
libraries['jack'] = LibraryInfo()
libraries['jack'].ParseConfig('pkg-config --cflags --libs jack')
@ -621,8 +625,10 @@ if env['DIST_TARGET'] == 'auto':
# The [.] matches to the dot after the major version, "." would match any character
if re.search ("darwin[0-7][.]", config[config_kernel]) != None:
env['DIST_TARGET'] = 'panther'
else:
if re.search ("darwin8[.]", config[config_kernel]) != None:
env['DIST_TARGET'] = 'tiger'
else:
env['DIST_TARGET'] = 'leopard'
else:
if re.search ("x86_64", config[config_cpu]) != None:
env['DIST_TARGET'] = 'x86_64'
@ -688,9 +694,9 @@ elif ((re.search ("i[0-9]86", config[config_cpu]) != None) or (re.search ("x86_6
# optimization section
if env['FPU_OPTIMIZATION']:
if env['DIST_TARGET'] == 'tiger':
opt_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS")
debug_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS")
if env['DIST_TARGET'] == 'tiger' or env['DIST_TARGET'] == 'leopard':
opt_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS");
debug_flags.append ("-DBUILD_VECLIB_OPTIMIZATIONS");
libraries['core'].Append(LINKFLAGS= '-framework Accelerate')
elif env['DIST_TARGET'] == 'i686' or env['DIST_TARGET'] == 'x86_64':
opt_flags.append ("-DBUILD_SSE_OPTIMIZATIONS")
@ -709,6 +715,18 @@ if env['DIST_TARGET'] == 'x86_64':
else:
env['LIBDIR']='lib'
#
# a single way to test if we're on OS X
#
if env['DIST_TARGET'] in ['panther', 'tiger', 'leopard' ]:
env['IS_OSX'] = 1
# force tiger or later, to avoid issues on PPC which defaults
# back to 10.1 if we don't tell it otherwise.
env.Append (CCFLAGS="-DMAC_OS_X_VERSION_MIN_REQUIRED=1040")
else:
env['IS_OSX'] = 0
#
# save off guessed arch element in an env
#
@ -767,7 +785,7 @@ if env['LIBLO']:
def prep_libcheck(topenv, libinfo):
if topenv['DIST_TARGET'] == 'panther' or topenv['DIST_TARGET'] == 'tiger':
if topenv['IS_OSX']:
#
# rationale: GTK-Quartz uses jhbuild and installs to /opt/gtk by default.
# All libraries needed should be built against this location
@ -780,9 +798,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')
CPPPATH='#libs/vamp-sdk')
libraries['vamphost'] = LibraryInfo (LIBS='vamphostsdk',
LIBPATH='#libs/vamp-sdk',
CPPPATH='#libs/vamp-sdk')
env['RUBBERBAND'] = False
@ -1007,6 +1032,7 @@ if env['SYSLIBS']:
'libs/midi++2',
'libs/ardour',
'libs/vamp-sdk',
'libs/vamp-plugins/',
# these are unconditionally included but have
# tests internally to avoid compilation etc
# if VST is not set
@ -1081,6 +1107,7 @@ else:
'libs/midi++2',
'libs/ardour',
'libs/vamp-sdk',
'libs/vamp-plugins/',
# these are unconditionally included but have
# tests internally to avoid compilation etc
# if VST is not set

View File

@ -24,7 +24,7 @@ gtkardour.Append(CPPPATH="#/") # for top level svn_revision.h
gtkardour.Append(PACKAGE=domain)
gtkardour.Append(POTFILE=domain + '.pot')
if gtkardour['DIST_TARGET'] == 'panther' or gtkardour['DIST_TARGET'] == 'tiger':
if gtkardour['IS_OSX']:
gtkardour.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
gtkardour.Merge ([
@ -50,6 +50,10 @@ gtkardour.Merge ([
libraries['xml'],
libraries['xslt'],
libraries['samplerate'],
libraries['vamp'],
libraries['vamphost'],
libraries['fftw3f'],
libraries['fftw3'],
libraries['jack'],
libraries['cairomm'],
libraries['asound']
@ -77,7 +81,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'] ])
@ -101,50 +105,44 @@ x11.cc
gtkardour_files=Split("""
about.cc
actions.cc
add_route_dialog.cc
add_midi_cc_track_dialog.cc
add_route_dialog.cc
ardour_dialog.cc
ardour_ui.cc
ardour_ui2.cc
ardour_ui.cc
ardour_ui_dependents.cc
ardour_ui_dialogs.cc
ardour_ui_ed.cc
ardour_ui_mixer.cc
ardour_ui_options.cc
audio_clock.cc
audio_time_axis.cc
audio_region_editor.cc
control_point.cc
automation_line.cc
automation_time_axis.cc
automation_streamview.cc
audio_region_view.cc
audio_streamview.cc
audio_time_axis.cc
automation_controller.cc
automation_line.cc
automation_region_view.cc
bundle_manager.cc
midi_port_dialog.cc
midi_time_axis.cc
midi_scroomer.cc
midi_streamview.cc
automation_streamview.cc
automation_time_axis.cc
axis_view.cc
canvas-simpleline.c
simpleline.cc
canvas-simplerect.c
simplerect.cc
lineset.cc
canvas-waveview.c
diamond.cc
bundle_manager.cc
canvas-midi-event.cc
canvas-simpleline.c
canvas-simplerect.c
canvas-waveview.c
control_point.cc
crossfade_edit.cc
crossfade_view.cc
curvetest.cc
enums.cc
diamond.cc
editing.cc
editor.cc
editor_actions.cc
editor_audio_import.cc
editor_audiotrack.cc
editor_canvas.cc
editor_canvas_events.cc
editor.cc
editor_cursors.cc
editor_edit_groups.cc
editor_export_audio.cc
@ -165,66 +163,72 @@ editor_selection_list.cc
editor_tempodisplay.cc
editor_timefx.cc
engine_dialog.cc
enums.cc
export_dialog.cc
export_session_dialog.cc
export_region_dialog.cc
export_range_markers_dialog.cc
export_region_dialog.cc
export_session_dialog.cc
gain_meter.cc
generic_pluginui.cc
ghostregion.cc
gtk-custom-hruler.c
gtk-custom-ruler.c
io_selector.cc
port_matrix.cc
keyboard.cc
keyeditor.cc
latency_gui.cc
level_meter.cc
lineset.cc
location_ui.cc
main.cc
marker.cc
midi_port_dialog.cc
midi_region_view.cc
midi_scroomer.cc
midi_streamview.cc
midi_time_axis.cc
mixer_strip.cc
mixer_ui.cc
new_session_dialog.cc
option_editor.cc
opts.cc
panner.cc
panner2d.cc
panner.cc
panner_ui.cc
piano_roll_header.cc
playlist_selector.cc
plugin_selector.cc
plugin_ui.cc
port_matrix.cc
processor_box.cc
prompter.cc
public_editor.cc
processor_box.cc
region_gain_line.cc
region_selection.cc
region_view.cc
audio_region_view.cc
midi_region_view.cc
tape_region_view.cc
rhythm_ferret.cc
route_params_ui.cc
route_processor_selection.cc
route_time_axis.cc
route_ui.cc
selection.cc
sfdb_ui.cc
send_ui.cc
sfdb_ui.cc
simpleline.cc
simplerect.cc
splash.cc
streamview.cc
audio_streamview.cc
tape_region_view.cc
tempo_dialog.cc
tempo_lines.cc
theme_manager.cc
time_axis_view.cc
time_axis_view_item.cc
route_time_axis.cc
time_selection.cc
ui_config.cc
utils.cc
version.cc
waveview.cc
tempo_lines.cc
""")
fft_analysis_files=Split("""
@ -315,7 +319,7 @@ tt = gtkmmtests.Program(target = 'tt', source = tt_files)
my_font_dict = { }
if gtkardour['DIST_TARGET'] == 'panther' or gtkardour['DIST_TARGET'] == 'tiger':
if gtkardour['IS_OSX']:
#
# OS X font rendering is different even with X11
#

View File

@ -4,7 +4,7 @@ cd `dirname "$0"`/..
export ARDOUR_PATH=gtk2_ardour/icons:gtk2_ardour/pixmaps:gtk2_ardour:.
export GTK_PATH=libs/clearlooks
export VAMP_PATH=libs/vamp-plugins:$VAMP_PATH
export LD_LIBRARY_PATH=libs/vamp-sdk:libs/surfaces/control_protocol:libs/ardour:libs/midi++2:libs/pbd:libs/rubberband:libs/soundtouch:libs/gtkmm2ext:libs/sigc++2:libs/glibmm2:libs/gtkmm2/atk:libs/gtkmm2/pango:libs/gtkmm2/gdk:libs/gtkmm2/gtk:libs/libgnomecanvasmm:libs/libsndfile:libs/appleutility:libs/cairomm:$LD_LIBRARY_PATH

View File

@ -335,6 +335,8 @@
(gtk_accel_path "<Actions>/Editor/goto-mark-9" "KP_9")
(gtk_accel_path "<Actions>/Transport/ToggleClick" "5")
(gtk_accel_path "<Actions>/Transport/ToggleAutoReturn" "4")
(gtk_accel_path "<Actions>/Editor/set-tempo-from-region" "9")
(gtk_accel_path "<Actions>/Editor/set-tempo-from-edit-range" "0")
(gtk_accel_path "<Actions>/Transport/focus-on-clock" "KP_Divide")
(gtk_accel_path "<Actions>/Editor/set-loop-from-edit-range" "bracketright")
(gtk_accel_path "<Actions>/Editor/set-punch-from-edit-range" "bracketleft")

View File

@ -133,6 +133,9 @@
<menuitem action='cycle-edit-point'/>
<menuitem action='cycle-edit-point-with-marker'/>
<menuitem action='toggle-edit-mode'/>
<separator/>
<menuitem action='boost-region-gain'/>
<menuitem action='cut-region-gain'/>
</menu>
</menu>
<menu name='Regions' action='Regions'>
@ -164,6 +167,8 @@
<menuitem action='trim-region-to-punch'/>
<separator/>
<menuitem action='pitch-shift-region'/>
<menuitem action='set-tempo-from-region'/>
<menuitem action='set-tempo-from-edit-range'/>
</menu>
<menu name='View' action = 'View'>
<menuitem action='ToggleMaximalEditor'/>

View File

@ -10,6 +10,7 @@
(gtk_accel_path "<Actions>/Editor/edit-cursor-to-previous-region-sync" "apostrophe")
(gtk_accel_path "<Actions>/Editor/edit-cursor-to-next-region-sync" "semicolon")
(gtk_accel_path "<Actions>/Editor/cycle-edit-point" "grave")
(gtk_accel_path "<Actions>/Editor/cycle-edit-point-with-marker" "<%PRIMARY%>asciicircum")
(gtk_accel_path "<Actions>/Editor/playhead-to-next-region-boundary" "period")
(gtk_accel_path "<Actions>/Editor/playhead-to-next-region-sync" "<%PRIMARY%>period")
@ -38,6 +39,9 @@
(gtk_accel_path "<Actions>/Editor/set-punch-from-edit-range" "bracketleft")
(gtk_accel_path "<Actions>/Editor/set-punch-from-region" "<%SECONDARY%>bracketleft")
(gtk_accel_path "<Actions>/Editor/boost-region-gain" "asciicircum")
(gtk_accel_path "<Actions>/Editor/cut-region-gain" "ampersand")
;; letters
;; TOP ROW
@ -50,6 +54,7 @@
(gtk_accel_path "<Actions>/Editor/select-all-before-edit-cursor" "<%PRIMARY%>e")
(gtk_accel_path "<Actions>/Editor/show-editor-mixer" "<%TERTIARY%>e")
(gtk_accel_path "<Actions>/Common/goto-editor" "<%WINDOW%>e")
(gtk_accel_path "<Actions>/Editor/select-all-after-edit-cursor" "<%TERTIARY%><%PRIMARY%>e")
(gtk_accel_path "<Actions>/MouseMode/set-mouse-mode-range" "r")
(gtk_accel_path "<Actions>/Editor/redo" "<%PRIMARY%>r")
(gtk_accel_path "<Actions>/Transport/Record" "<%TERTIARY%>r")
@ -62,22 +67,19 @@
(gtk_accel_path "<Actions>/Common/ToggleOptionsEditor" "<%WINDOW%>o")
(gtk_accel_path "<Actions>/Editor/set-playhead" "p")
(gtk_accel_path "<Actions>/Editor/select-all-before-playhead" "<%PRIMARY%>p")
(gtk_accel_path "<Actions>/Editor/select-all-after-playhead" "<%TERTIARY%><%PRIMARY%>p")
;; MIDDLE ROW
(gtk_accel_path "<Actions>/Editor/align-regions-sync-relative" "a")
(gtk_accel_path "<Actions>/Editor/align-regions-start-relative" "<%PRIMARY%>a")
(gtk_accel_path "<Actions>/Editor/select-all" "<%PRIMARY%>a")
(gtk_accel_path "<Actions>/Editor/align-regions-end" "<%SECONDARY%>a")
(gtk_accel_path "<Actions>/Editor/align-regions-sync" "<%TERTIARY%>a")
(gtk_accel_path "<Actions>/Editor/align-regions-start-relative" "<%LEVEL4%>a")
(gtk_accel_path "<Actions>/Editor/split-region" "s")
(gtk_accel_path "<Actions>/Common/Save" "<%PRIMARY%>s")
(gtk_accel_path "<Actions>/Editor/duplicate-region" "d")
(gtk_accel_path "<Actions>/Editor/select-all-in-punch-range" "<%PRIMARY%>d")
(gtk_accel_path "<Actions>/Editor/select-all" "<%PRIMARY%>a")
(gtk_accel_path "<Actions>/Editor/select-all-after-playhead" "<%TERTIARY%><%PRIMARY%>p")
(gtk_accel_path "<Actions>/Editor/select-all-after-edit-cursor" "<%TERTIARY%><%PRIMARY%>e")
(gtk_accel_path "<Actions>/Editor/toggle-follow-playhead" "f")
(gtk_accel_path "<Actions>/MouseMode/set-mouse-mode-gain" "g")
(gtk_accel_path "<Actions>/Editor/play-selected-regions" "h")
@ -180,6 +182,8 @@
(gtk_accel_path "<Actions>/Editor/cycle-snap-choice" "3")
(gtk_accel_path "<Actions>/Transport/ToggleAutoReturn" "4")
(gtk_accel_path "<Actions>/Transport/ToggleClick" "5")
(gtk_accel_path "<Actions>/Editor/set-tempo-from-region" "9")
(gtk_accel_path "<Actions>/Editor/set-tempo-from-edit-range" "0")
;;
;; unbound actions

View File

@ -44,6 +44,9 @@
<menuitem action='GotoStart'/>
<menuitem action='GotoEnd'/>
<separator/>
<menuitem action='tab-to-transient-forwards'/>
<menuitem action='tab-to-transient-backwards'/>
<separator/>
<menuitem action='Record'/>
<separator/>
<menuitem action='TransitionToRoll'/>
@ -161,6 +164,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'/>
@ -194,6 +198,8 @@
<menuitem action='trim-region-to-punch'/>
<separator/>
<menuitem action='pitch-shift-region'/>
<menuitem action='set-tempo-from-region'/>
<menuitem action='set-tempo-from-edit-range'/>
</menu>
<menu name='View' action = 'View'>
<menu name='ZoomFocus' action='ZoomFocus'>
@ -302,6 +308,7 @@
<menuitem action='ToggleThemeManager'/>
<menuitem action='ToggleBigClock'/>
<menuitem action='ToggleBundleManager'/>
<menuitem action='toggle-rhythm-ferret'/>
<separator/>
</menu>
<menu name='Options' action='Options'>

View File

@ -4,11 +4,11 @@
<Option name="ui-rc-file" value="ardour3_ui_dark.rc"/>
</UI>
<Canvas>
<Option name="waveform" value="000000d6"/>
<Option name="waveform fill" value="0b225a78"/>
<Option name="waveform outline" value="0f0f0fcc"/>
<Option name="waveform fill" value="3d475378"/>
<Option name="clipped waveform" value="ff0000e5"/>
<Option name="region base" value="b2bcd3aa"/>
<Option name="selected region base" value="565693a6"/>
<Option name="region base" value="99a7b5aa"/>
<Option name="selected region base" value="b591a8ff"/>
<Option name="midi frame base" value="698f9d6d"/>
<Option name="audio track base" value="c6d3d868"/>
<Option name="audio bus base" value="dbd1ea68"/>

View File

@ -610,8 +610,6 @@ Please consider the possibilities, and perhaps (re)start JACK."));
win.show_all ();
win.set_position (Gtk::WIN_POS_CENTER);
hide_splash ();
/* we just don't care about the result, but we want to block */
win.run ();
@ -2208,6 +2206,7 @@ ARDOUR_UI::end_loading_messages ()
void
ARDOUR_UI::loading_message (const std::string& msg)
{
cerr << "say: " << msg << endl;
show_splash ();
splash->message (msg);
flush_pending ();
@ -2254,10 +2253,6 @@ ARDOUR_UI::get_session_parameters (bool backend_audio_is_running, bool should_be
new_session_dialog->set_existing_session (existing_session);
new_session_dialog->reset_recent();
/* get this out of the way */
hide_splash ();
do {
new_session_dialog->set_have_engine (backend_audio_is_running);
new_session_dialog->present ();
@ -2622,6 +2617,7 @@ ARDOUR_UI::show_splash ()
}
splash->show ();
splash->present ();
splash->queue_draw ();
splash->get_window()->process_updates (true);
flush_pending ();

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

@ -796,20 +796,21 @@ AudioRegionView::set_envelope_visible (bool yn)
void
AudioRegionView::create_waves ()
{
//cerr << "AudioRegionView::create_waves() called on " << this << endl;//DEBUG
RouteTimeAxisView& atv (*(dynamic_cast<RouteTimeAxisView*>(&trackview))); // ick
if (!atv.get_diskstream()) {
return;
}
uint32_t nchans = atv.get_diskstream()->n_channels().n_audio();
ChanCount nchans = atv.get_diskstream()->n_channels();
/* in tmp_waves, set up null pointers for each channel so the vector is allocated */
for (uint32_t n = 0; n < nchans; ++n) {
for (uint32_t n = 0; n < nchans.n_audio(); ++n) {
tmp_waves.push_back (0);
}
for (uint32_t n = 0; n < nchans; ++n) {
for (uint32_t n = 0; n < nchans.n_audio(); ++n) {
if (n >= audio_region()->n_channels()) {
break;
@ -818,21 +819,16 @@ AudioRegionView::create_waves ()
wave_caches.push_back (WaveView::create_cache ());
if (wait_for_data) {
if (audio_region()->audio_source(n)->peaks_ready (bind (mem_fun(*this, &AudioRegionView::peaks_ready_handler), n), data_ready_connection)) {
create_one_wave (n, true);
} else {
// we'll get a PeaksReady signal from the source in the future
// and will call create_one_wave(n) then.
}
} else {
create_one_wave (n, true);
}
}
}
void
AudioRegionView::create_one_wave (uint32_t which, bool direct)
{
//cerr << "AudioRegionView::create_one_wave() called which: " << which << " this: " << this << endl;//DEBUG
RouteTimeAxisView& atv (*(dynamic_cast<RouteTimeAxisView*>(&trackview))); // ick
uint32_t nchans = atv.get_diskstream()->n_channels().n_audio();
uint32_t n;
@ -862,6 +858,7 @@ AudioRegionView::create_one_wave (uint32_t which, bool direct)
wave->property_samples_per_unit() = samples_per_unit;
wave->property_amplitude_above_axis() = _amplitude_above_axis;
wave->property_wave_color() = _region->muted() ? UINT_RGBA_CHANGE_A(ARDOUR_UI::config()->canvasvar_WaveForm.get(), MUTED_ALPHA) : ARDOUR_UI::config()->canvasvar_WaveForm.get();
wave->property_fill_color() = ARDOUR_UI::config()->canvasvar_WaveFormFill.get();
wave->property_clip_color() = ARDOUR_UI::config()->canvasvar_WaveFormClip.get();
wave->property_zero_color() = ARDOUR_UI::config()->canvasvar_ZeroLine.get();
wave->property_region_start() = _region->start();
@ -916,7 +913,8 @@ AudioRegionView::create_one_wave (uint32_t which, bool direct)
void
AudioRegionView::peaks_ready_handler (uint32_t which)
{
Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun(*this, &AudioRegionView::create_one_wave), which, false));
//Gtkmm2ext::UI::instance()->call_slot (bind (mem_fun(*this, &AudioRegionView::create_one_wave), which, false));
cerr << "AudioRegionView::peaks_ready_handler() called on " << which << " this: " << this << endl;
}
void
@ -1110,6 +1108,7 @@ AudioRegionView::add_ghost (AutomationTimeAxisView& atv)
wave->property_samples_per_unit() = samples_per_unit;
wave->property_amplitude_above_axis() = _amplitude_above_axis;
wave->property_wave_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWave.get();
wave->property_fill_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWave.get();
wave->property_clip_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWaveClip.get();
wave->property_zero_color() = ARDOUR_UI::config()->canvasvar_GhostTrackZeroLine.get();
wave->property_region_start() = _region->start();

View File

@ -368,10 +368,10 @@ gnome_canvas_waveview_init (GnomeCanvasWaveView *waveview)
waveview->screen_width = gdk_screen_width ();
waveview->reload_cache_in_render = FALSE;
waveview->wave_color = RGBA_TO_UINT(44,35,126,255);
waveview->clip_color = RGBA_TO_UINT(44,0,0,100);
waveview->zero_color = RGBA_TO_UINT(44,0,128,100);
waveview->fill_color = RGBA_TO_UINT(44,35,126,128);
waveview->wave_color = 0;
waveview->clip_color = 0;
waveview->zero_color = 0;
waveview->fill_color = 0;
}
static void

View File

@ -1,4 +1,4 @@
CANVAS_VARIABLE(canvasvar_WaveForm, "waveform")
CANVAS_VARIABLE(canvasvar_WaveForm, "waveform outline")
CANVAS_VARIABLE(canvasvar_WaveFormFill, "waveform fill")
CANVAS_VARIABLE(canvasvar_WaveFormClip, "clipped waveform")
CANVAS_VARIABLE(canvasvar_FrameBase, "region base")

View File

@ -18,6 +18,8 @@
#include <Carbon/Carbon.h>
#undef check // stupid, stupid carbon
#undef YES // stupid, stupid gtkmm and/or NSObjC
#undef NO // ditto
#include "ardour_ui.h"
#include "actions.h"
@ -117,6 +119,6 @@ ARDOUR_UI::platform_setup ()
/* if invoked from the command line, make sure we're visible */
[NSApp activateIgnoringOtherApps:YES];
[NSApp activateIgnoringOtherApps:1];
}
}

View File

@ -982,10 +982,12 @@ CrossfadeEditor::curve_select_clicked (WhichFade wf)
for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
(*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
(*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
}
for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
(*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
(*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
}
fade[In].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorLine.get();
@ -1005,10 +1007,12 @@ CrossfadeEditor::curve_select_clicked (WhichFade wf)
for (vector<ArdourCanvas::WaveView*>::iterator i = fade[In].waves.begin(); i != fade[In].waves.end(); ++i) {
(*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
(*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_CrossfadeEditorWave.get();
}
for (vector<ArdourCanvas::WaveView*>::iterator i = fade[Out].waves.begin(); i != fade[Out].waves.end(); ++i) {
(*i)->property_wave_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
(*i)->property_fill_color() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorWave.get();
}
fade[Out].line->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedCrossfadeEditorLine.get();
@ -1084,6 +1088,7 @@ CrossfadeEditor::make_waves (boost::shared_ptr<AudioRegion> region, WhichFade wh
waveview->property_samples_per_unit() = spu;
waveview->property_amplitude_above_axis() = 2.0;
waveview->property_wave_color() = color;
waveview->property_fill_color() = color;
if (which==In)
waveview->property_region_start() = region->start();

View File

@ -83,6 +83,7 @@
#include "sfdb_ui.h"
#include "gui_thread.h"
#include "simpleline.h"
#include "rhythm_ferret.h"
#ifdef FFT_ANALYSIS
#include "analysis_window.h"
@ -341,6 +342,7 @@ Editor::Editor ()
_dragging_hscrollbar = false;
select_new_marker = false;
zoomed_to_region = false;
rhythm_ferret = 0;
scrubbing_direction = 0;
@ -1190,6 +1192,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);
@ -3297,26 +3303,32 @@ Editor::duplicate_dialog (bool with_dialog)
if (with_dialog) {
ArdourDialog win ("duplicate dialog");
Label label (_("Duplicate how many times?"));
ArdourDialog win ("Duplication Dialog");
Label label (_("Number of Duplications:"));
Adjustment adjustment (1.0, 1.0, 1000000.0, 1.0, 5.0);
SpinButton spinner (adjustment);
SpinButton spinner (adjustment, 0.0, 1);
HBox hbox;
win.get_vbox()->set_spacing (12);
win.get_vbox()->pack_start (label);
win.get_vbox()->pack_start (hbox);
hbox.set_border_width (6);
hbox.pack_start (label, PACK_EXPAND_PADDING, 12);
/* dialogs have ::add_action_widget() but that puts the spinner in the wrong
place, visually. so do this by hand.
*/
win.get_vbox()->pack_start (spinner);
hbox.pack_start (spinner, PACK_EXPAND_PADDING, 12);
spinner.signal_activate().connect (sigc::bind (mem_fun (win, &ArdourDialog::response), RESPONSE_ACCEPT));
spinner.grab_focus();
hbox.show ();
label.show ();
spinner.show ();
win.add_button (Stock::OK, RESPONSE_ACCEPT);
win.add_button (Stock::CANCEL, RESPONSE_CANCEL);
win.add_button (_("Duplicate"), RESPONSE_ACCEPT);
win.set_default_response (RESPONSE_ACCEPT);
win.set_position (WIN_POS_MOUSE);
@ -4587,3 +4599,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

@ -107,6 +107,7 @@ class StreamView;
class AudioStreamView;
class ControlPoint;
class SoundFileOmega;
class RhythmFerret;
#ifdef FFT_ANALYSIS
class AnalysisWindow;
#endif
@ -371,6 +372,8 @@ class Editor : public PublicEditor
void toggle_meter_updating();
void show_rhythm_ferret();
protected:
void map_transport_state ();
void map_position_change (nframes_t);
@ -1024,6 +1027,8 @@ class Editor : public PublicEditor
void split_region ();
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 crop_region_to_selection ();
void crop_region_to (nframes_t start, nframes_t end);
void set_sync_point (nframes64_t, const RegionSelection&);
@ -1051,6 +1056,13 @@ class Editor : public PublicEditor
void adjust_region_scale_amplitude (bool up);
void quantize_region ();
void tab_to_transient (bool forward);
void use_region_as_bar ();
void use_range_as_bar ();
void define_one_bar (nframes64_t start, nframes64_t end);
void audition_region_from_region_list ();
void hide_region_from_region_list ();
void remove_region_from_region_list ();
@ -2164,6 +2176,8 @@ class Editor : public PublicEditor
void select_next_route ();
void select_prev_route ();
RhythmFerret* rhythm_ferret;
};
#endif /* __ardour_editor_h__ */

View File

@ -356,13 +356,30 @@ Editor::register_actions ()
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "normalize-region", _("Normalize Region"), mem_fun(*this, &Editor::normalize_region));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "boost-region-gain", _("Boost Region Gain"), bind (mem_fun(*this, &Editor::adjust_region_scale_amplitude), true));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "cut-region-gain", _("Cut Region Gain"), bind (mem_fun(*this, &Editor::adjust_region_scale_amplitude), false));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "quantize-region", _("Quantize Region"), mem_fun(*this, &Editor::quantize_region));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "set-tempo-from-region", _("Set Tempo from Region=Bar"), mem_fun(*this, &Editor::use_region_as_bar));
ActionManager::session_sensitive_actions.push_back (act);
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", _("Split Regions At Percussion Onsets"), mem_fun(*this, &Editor::split_region_at_transients));
ActionManager::session_sensitive_actions.push_back (act);
act = ActionManager::register_action (editor_actions, "toggle-rhythm-ferret", _("Rhythm Ferret"), mem_fun(*this, &Editor::show_rhythm_ferret));
ActionManager::session_sensitive_actions.push_back (act);
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));
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

@ -453,6 +453,8 @@ Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context
const SelectionData& data,
guint info, guint time)
{
cerr << "drop on canvas, target = " << data.get_target() << endl;
if (data.get_target() == "regions") {
drop_regions (context, x, y, data, info, time);
} else {

View File

@ -45,10 +45,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/quantize.h>
@ -3042,9 +3044,9 @@ Editor::align_selection_relative (RegionPoint point, nframes_t position, const R
return;
}
nframes_t distance;
nframes_t distance = 0;
nframes_t pos = 0;
int dir;
int dir = 0;
list<RegionView*> sorted;
rs.by_position (sorted);
@ -4148,7 +4150,7 @@ Editor::adjust_region_scale_amplitude (bool up)
return;
}
ExclusiveRegionSelection (*this, entered_regionview);
ExclusiveRegionSelection esr (*this, entered_regionview);
if (selection->regions.empty()) {
return;
@ -4164,10 +4166,6 @@ Editor::adjust_region_scale_amplitude (bool up)
double fraction = gain_to_slider_position (arv->audio_region()->scale_amplitude ());
cerr << "slider pos for " << arv->audio_region()->scale_amplitude ()
<< " = " << fraction
<< endl;
if (up) {
fraction += 0.05;
fraction = min (fraction, 1.0);
@ -4180,16 +4178,14 @@ Editor::adjust_region_scale_amplitude (bool up)
continue;
}
if (up && fraction >= 1.0) {
continue;
}
fraction = slider_position_to_gain (fraction);
fraction = coefficient_to_dB (fraction);
fraction = dB_to_coefficient (fraction);
cerr << "set scale amp for " << arv->audio_region()->name() << " to " << fraction << endl;
if (up && fraction >= 2.0) {
continue;
}
arv->audio_region()->set_scale_amplitude (fraction);
session->add_command (new MementoCommand<Region>(*(arv->region().get()), &before, &arv->region()->get_state()));
}
@ -4481,7 +4477,7 @@ Editor::toggle_fade_active (bool in)
const char* cmd = (in ? _("toggle fade in active") : _("toggle fade out active"));
bool have_switch = false;
bool yn;
bool yn = false;
begin_reversible_command (cmd);
@ -4986,3 +4982,233 @@ Editor::pitch_shift_regions ()
pitch_shift (selection->regions, 1.2);
}
void
Editor::use_region_as_bar ()
{
if (!session) {
return;
}
ExclusiveRegionSelection esr (*this, entered_regionview);
if (selection->regions.empty()) {
return;
}
RegionView* rv = selection->regions.front();
define_one_bar (rv->region()->position(), rv->region()->last_frame() + 1);
}
void
Editor::use_range_as_bar ()
{
nframes64_t start, end;
if (get_edit_op_range (start, end)) {
define_one_bar (start, end);
}
}
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 */
/* 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;
/* beats per minute = */
double beats_per_minute = (session->frame_rate() * 60.0) / frames_per_beat;
const TempoSection& t (session->tempo_map().tempo_section_at (start));
begin_reversible_command (_("set tempo from region"));
XMLNode& before (session->tempo_map().get_state());
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);
}
XMLNode& after (session->tempo_map().get_state());
session->add_command (new MementoCommand<TempoMap>(session->tempo_map(), &before, &after));
commit_reversible_command ();
}
void
Editor::split_region_at_transients ()
{
vector<nframes64_t> positions;
if (!session) {
return;
}
ExclusiveRegionSelection esr (*this, entered_regionview);
if (selection->regions.empty()) {
return;
}
session->begin_reversible_command (_("split regions"));
for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ) {
RegionSelection::iterator tmp;
tmp = i;
++tmp;
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> ((*i)->region());
if (ar && (ar->get_transients (positions) == 0)) {
split_region_at_points ((*i)->region(), positions);
positions.clear ();
}
i = tmp;
}
session->commit_reversible_command ();
}
void
Editor::split_region_at_points (boost::shared_ptr<Region> r, vector<nframes64_t>& positions)
{
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
if (!ar) {
return;
}
boost::shared_ptr<Playlist> pl = ar->playlist();
if (!pl) {
return;
}
if (positions.empty()) {
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->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
Editor::tab_to_transient (bool forward)
{
vector<nframes64_t> positions;
if (!session) {
return;
}
ExclusiveRegionSelection esr (*this, entered_regionview);
if (selection->regions.empty()) {
return;
}
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (selection->regions.front()->region());
if (!ar) {
return;
}
ar->get_transients (positions);
nframes64_t pos = session->audible_frame ();
if (forward) {
vector<nframes64_t>::iterator x;
for (x = positions.begin(); x != positions.end(); ++x) {
if ((*x) > pos) {
break;
}
}
if (x != positions.end ()) {
session->request_locate (*x);
}
} else {
vector<nframes64_t>::reverse_iterator x;
for (x = positions.rbegin(); x != positions.rend(); ++x) {
if ((*x) < pos) {
break;
}
}
if (x != positions.rend ()) {
session->request_locate (*x);
}
}
}

View File

@ -220,7 +220,8 @@ Editor::mouse_add_new_tempo_event (nframes_t frame)
TempoDialog tempo_dialog (map, frame, _("add"));
tempo_dialog.set_position (Gtk::WIN_POS_MOUSE);
tempo_dialog.signal_realize().connect (bind (sigc::ptr_fun (set_decoration), &tempo_dialog, Gdk::WMDecoration (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH)));
//this causes compiz to display no border.
//tempo_dialog.signal_realize().connect (bind (sigc::ptr_fun (set_decoration), &tempo_dialog, Gdk::WMDecoration (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH)));
ensure_float (tempo_dialog);
@ -247,7 +248,7 @@ Editor::mouse_add_new_tempo_event (nframes_t frame)
session->add_command(new MementoCommand<TempoMap>(map, &before, &after));
commit_reversible_command ();
map.dump (cerr);
//map.dump (cerr);
}
void
@ -262,7 +263,9 @@ Editor::mouse_add_new_meter_event (nframes_t frame)
MeterDialog meter_dialog (map, frame, _("add"));
meter_dialog.set_position (Gtk::WIN_POS_MOUSE);
meter_dialog.signal_realize().connect (bind (sigc::ptr_fun (set_decoration), &meter_dialog, Gdk::WMDecoration (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH)));
//this causes compiz to display no border..
//meter_dialog.signal_realize().connect (bind (sigc::ptr_fun (set_decoration), &meter_dialog, Gdk::WMDecoration (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH)));
ensure_float (meter_dialog);
@ -278,16 +281,16 @@ Editor::mouse_add_new_meter_event (nframes_t frame)
double note_type = meter_dialog.get_note_type ();
BBT_Time requested;
meter_dialog.get_bbt_time (requested);
begin_reversible_command (_("add meter mark"));
XMLNode &before = map.get_state();
map.add_meter (Meter (bpb, note_type), requested);
session->add_command(new MementoCommand<TempoMap>(map, &before, &map.get_state()));
commit_reversible_command ();
map.dump (cerr);
//map.dump (cerr);
}
void

View File

@ -373,7 +373,7 @@ EngineControl::build_command_line (vector<string>& cmd)
str = timeout_combo.get_active_text ();
if (str != _("Ignore")) {
double secs;
double secs = 0;
uint32_t msecs;
atof (str);
msecs = (uint32_t) floor (secs * 1000.0);
@ -819,7 +819,7 @@ EngineControl::driver_changed ()
vector<string>& strings = devices[driver];
if (strings.empty()) {
if (strings.empty() && driver != "FFADO") {
error << string_compose (_("No devices found for driver \"%1\""), driver) << endmsg;
return;
}
@ -1096,7 +1096,7 @@ EngineControl::set_state (const XMLNode& root)
XMLNodeList clist;
XMLNodeConstIterator citer;
XMLNode* child;
XMLProperty* prop;
XMLProperty* prop = NULL;
bool using_dummy = false;
int val;
@ -1112,7 +1112,8 @@ EngineControl::set_state (const XMLNode& root)
clist = root.children();
for (citer = clist.begin(); citer != clist.end(); ++citer) {
if ( prop && (prop->value() == "FFADO" ))
continue;
child = *citer;
prop = child->property ("val");

View File

@ -38,6 +38,7 @@
#include <ardour/plugin.h>
#include <ardour/plugin_insert.h>
#include <ardour/ladspa_plugin.h>
#include <ardour/lv2_plugin.h>
#include <lrdf.h>
@ -384,6 +385,7 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
if (plugin->parameter_is_input (port_index)) {
boost::shared_ptr<LadspaPlugin> lp;
boost::shared_ptr<LV2Plugin> lv2p;
if ((lp = boost::dynamic_pointer_cast<LadspaPlugin>(plugin)) != 0) {
@ -406,6 +408,26 @@ GenericPluginUI::build_control_ui (guint32 port_index, boost::shared_ptr<Automat
lrdf_free_setting_values(defaults);
return control_ui;
}
} else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin>(plugin)) != 0) {
SLV2Port port = lv2p->slv2_port(port_index);
SLV2ScalePoints points = slv2_port_get_scale_points(lv2p->slv2_plugin(), port);
if (points) {
control_ui->combo = new Gtk::ComboBoxText;
//control_ui->combo->set_value_in_list(true, false);
set_popdown_strings (*control_ui->combo, setup_scale_values(port_index, control_ui));
control_ui->combo->signal_changed().connect (bind (mem_fun(*this, &GenericPluginUI::control_combo_changed), control_ui));
mcontrol->Changed.connect (bind (mem_fun (*this, &GenericPluginUI::parameter_changed), control_ui));
control_ui->pack_start(control_ui->label, true, true);
control_ui->pack_start(*control_ui->combo, false, true);
update_control_display(control_ui);
slv2_scale_points_free(points);
return control_ui;
}
}
if (desc.toggled) {
@ -734,26 +756,49 @@ vector<string>
GenericPluginUI::setup_scale_values(guint32 port_index, ControlUI* cui)
{
vector<string> enums;
boost::shared_ptr<LadspaPlugin> lp = boost::dynamic_pointer_cast<LadspaPlugin> (plugin);
boost::shared_ptr<LadspaPlugin> lp;
boost::shared_ptr<LV2Plugin> lv2p;
cui->combo_map = new std::map<string, float>;
// FIXME: not all plugins have a numeric unique ID
uint32_t id = atol (lp->unique_id().c_str());
lrdf_defaults* defaults = lrdf_get_scale_values(id, port_index);
if ((lp = boost::dynamic_pointer_cast<LadspaPlugin>(plugin)) != 0) {
// all LADPSA plugins have a numeric unique ID
uint32_t id = atol (lp->unique_id().c_str());
if (defaults) {
for (uint32_t i = 0; i < defaults->count; ++i) {
enums.push_back(defaults->items[i].label);
pair<string, float> newpair;
newpair.first = defaults->items[i].label;
newpair.second = defaults->items[i].value;
cui->combo_map->insert(newpair);
cui->combo_map = new std::map<string, float>;
lrdf_defaults* defaults = lrdf_get_scale_values(id, port_index);
if (defaults) {
for (uint32_t i = 0; i < defaults->count; ++i) {
enums.push_back(defaults->items[i].label);
pair<string, float> newpair;
newpair.first = defaults->items[i].label;
newpair.second = defaults->items[i].value;
cui->combo_map->insert(newpair);
}
lrdf_free_setting_values(defaults);
}
lrdf_free_setting_values(defaults);
} else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin>(plugin)) != 0) {
SLV2Port port = lv2p->slv2_port(port_index);
SLV2ScalePoints points = slv2_port_get_scale_points(lv2p->slv2_plugin(), port);
cui->combo_map = new std::map<string, float>;
for (unsigned i=0; i < slv2_scale_points_size(points); ++i) {
SLV2ScalePoint p = slv2_scale_points_get_at(points, i);
SLV2Value label = slv2_scale_point_get_label(p);
SLV2Value value = slv2_scale_point_get_value(p);
if (label && (slv2_value_is_float(value) || slv2_value_is_int(value))) {
enums.push_back(slv2_value_as_string(label));
pair<string, float> newpair;
newpair.first = slv2_value_as_string(label);
newpair.second = slv2_value_as_float(value);
cui->combo_map->insert(newpair);
}
}
slv2_scale_points_free(points);
}
return enums;
}

View File

@ -98,6 +98,7 @@ GhostRegion::set_colors ()
for (uint32_t n=0; n < waves.size(); ++n) {
waves[n]->property_wave_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWave.get();
waves[n]->property_fill_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWave.get();
waves[n]->property_clip_color() = ARDOUR_UI::config()->canvasvar_GhostTrackWaveClip.get();
waves[n]->property_zero_color() = ARDOUR_UI::config()->canvasvar_GhostTrackZeroLine.get();

View File

@ -114,7 +114,7 @@ void
LatencyGUI::change_latency_from_button (int dir)
{
Glib::ustring unitstr = units_combo.get_active_text();
double shift;
double shift = 0.0;
if (unitstr == unit_strings[0]) {
shift = 1;

View File

@ -254,6 +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;
#ifdef WITH_CMT
virtual void add_imageframe_time_axis(const std::string & track_name, void*) = 0;

View File

@ -0,0 +1,305 @@
#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());
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.get(), 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()) {
/* 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 ();
editor.split_region_at_points ((*i)->region(), current_results);
/* i is invalid at this point */
i = tmp;
}
}
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

@ -29,14 +29,19 @@ Splash::Splash ()
catch (...) {
throw failed_constructor();
}
darea.set_size_request (pixbuf->get_width(), pixbuf->get_height());
set_type_hint (Gdk::WINDOW_TYPE_HINT_SPLASHSCREEN);
set_keep_above (true);
set_position (WIN_POS_CENTER);
darea.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
layout = create_pango_layout ("");
string str = "<b>";
string i18n = _("Ardour loading ...");
str += i18n;
str += "</b>";
layout->set_markup (str);
darea.show ();
darea.signal_expose_event().connect (mem_fun (*this, &Splash::expose));
@ -48,6 +53,7 @@ void
Splash::on_realize ()
{
Window::on_realize ();
get_window()->set_decorations (Gdk::WMDecoration(0));
layout->set_font_description (get_style()->get_font());
}
@ -62,21 +68,19 @@ Splash::on_button_release_event (GdkEventButton* ev)
bool
Splash::expose (GdkEventExpose* ev)
{
#if 0
RefPtr<Gdk::Window> window = darea.get_window();
Glib::RefPtr<Gtk::Style> style = darea.get_style();
window->draw_pixbuf (darea.get_style()->get_bg_gc (STATE_NORMAL), pixbuf,
window->draw_pixbuf (get_style()->get_bg_gc (STATE_NORMAL), pixbuf,
ev->area.x, ev->area.y,
ev->area.x, ev->area.y,
ev->area.width, ev->area.height,
Gdk::RGB_DITHER_NONE, 0, 0);
Glib::RefPtr<Gtk::Style> style = darea.get_style();
Glib::RefPtr<Gdk::GC> white = style->get_white_gc();
window->draw_layout (white, 10, pixbuf->get_height() - 30, layout);
#endif
return true;
}

View File

@ -36,12 +36,11 @@ TempoDialog::TempoDialog (TempoMap& map, nframes_t frame, const string & action)
: ArdourDialog (_("edit tempo")),
bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0, 1.0),
bpm_spinner (bpm_adjustment),
bpm_frame (_("Beats per minute")),
note_frame (_("BPM denominator")),
bpm_frame (_("Tempo")),
ok_button (action),
cancel_button (_("Cancel")),
when_bar_label (_("Bar")),
when_beat_label (_("Beat")),
when_bar_label (_("Bar"), ALIGN_LEFT, ALIGN_CENTER),
when_beat_label (_("Beat"), ALIGN_LEFT, ALIGN_CENTER),
when_table (2, 2),
when_frame (_("Location"))
{
@ -56,11 +55,11 @@ TempoDialog::TempoDialog (TempoSection& section, const string & action)
: ArdourDialog ("tempo dialog"),
bpm_adjustment (60.0, 1.0, 999.9, 0.1, 1.0, 1.0),
bpm_spinner (bpm_adjustment),
bpm_frame (_("Beats per minute")),
bpm_frame (_("Tempo")),
ok_button (action),
cancel_button (_("Cancel")),
when_bar_label (_("Bar")),
when_beat_label (_("Beat")),
when_bar_label (_("Bar"), ALIGN_LEFT, ALIGN_CENTER),
when_beat_label (_("Beat"), ALIGN_LEFT, ALIGN_CENTER),
when_table (2, 2),
when_frame (_("Location"))
{
@ -108,18 +107,18 @@ TempoDialog::init (const BBT_Time& when, double bpm, double note_type, bool mova
else
note_types.set_active_text (_("quarter (4)"));
hspacer1.set_border_width (5);
hspacer1.pack_start (bpm_spinner, false, false);
vspacer1.set_border_width (5);
vspacer1.pack_start (hspacer1, false, false);
Label* bpm_label = manage(new Label(_("Beats Per Minute:"), ALIGN_LEFT, ALIGN_CENTER));
hspacer2.set_border_width (5);
hspacer2.pack_start (note_types, false, false);
vspacer2.set_border_width (5);
vspacer2.pack_start (hspacer2, false, false);
hspacer1.set_border_width (6);
hspacer1.pack_end (bpm_spinner, PACK_EXPAND_PADDING);
hspacer1.pack_start (*bpm_label, PACK_EXPAND_PADDING);
vspacer1.set_border_width (6);
vspacer1.pack_start (hspacer1, PACK_EXPAND_PADDING);
hspacer2.set_border_width (6);
hspacer2.pack_start (note_types, PACK_EXPAND_PADDING);
bpm_frame.add (vspacer1);
note_frame.add (vspacer2);
if (movable) {
snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
@ -139,35 +138,40 @@ TempoDialog::init (const BBT_Time& when, double bpm, double note_type, bool mova
when_table.set_homogeneous (true);
when_table.set_row_spacings (2);
when_table.set_col_spacings (2);
when_table.set_border_width (5);
when_table.set_border_width (6);
when_table.attach (when_bar_label, 0, 1, 0, 1, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
when_table.attach (when_bar_entry, 0, 1, 1, 2, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
when_table.attach (when_bar_label, 0, 1, 0, 1, AttachOptions(0), FILL|EXPAND);
when_table.attach (when_bar_entry, 1, 2, 0, 1, AttachOptions(0), FILL|EXPAND);
when_table.attach (when_beat_label, 1, 2, 0, 1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
when_table.attach (when_beat_entry, 1, 2, 1, 2, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
when_table.attach (when_beat_label, 0, 1, 1, 2, AttachOptions(0), AttachOptions(0));
when_table.attach (when_beat_entry, 1, 2, 1, 2, AttachOptions(0), AttachOptions(0));
when_frame.set_name ("MetricDialogFrame");
when_frame.add (when_table);
HBox* when_hbox = manage (new HBox());
Label* when_label = manage(new Label(_("Tempo Begins at:"), ALIGN_LEFT, ALIGN_TOP));
when_hbox->pack_end(when_table, PACK_EXPAND_PADDING, 6);
when_hbox->pack_start(*when_label, PACK_EXPAND_PADDING, 6);
when_frame.set_name ("MetricDialogFrame");
when_frame.add (*when_hbox);
get_vbox()->pack_end (when_frame, false, false);
when_frame.show_all();
get_vbox()->pack_start (when_frame, false, false);
}
bpm_frame.set_name ("MetricDialogFrame");
bpm_spinner.set_name ("MetricEntry");
note_frame.set_name ("MetricDialogFrame");
get_vbox()->set_border_width (12);
get_vbox()->pack_start (bpm_frame, false, false);
get_vbox()->pack_start (note_frame, false, false);
get_vbox()->pack_end (bpm_frame, false, false);
add_button (Stock::CANCEL, RESPONSE_CANCEL);
add_button (Stock::APPLY, RESPONSE_ACCEPT);
set_response_sensitive (Gtk::RESPONSE_ACCEPT, false);
set_response_sensitive (RESPONSE_ACCEPT, false);
set_default_response (RESPONSE_ACCEPT);
get_vbox()->show_all();
bpm_spinner.show();
bpm_frame.show_all ();
bpm_spinner.show ();
set_name ("MetricDialog");
@ -188,7 +192,7 @@ TempoDialog::bpm_button_release (GdkEventButton* ev)
{
/* the value has been modified, accept should work now */
set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
set_response_sensitive (RESPONSE_ACCEPT, true);
return false;
}
@ -209,6 +213,8 @@ TempoDialog::get_bbt_time (BBT_Time& requested)
return false;
}
requested.ticks = 0;
return true;
}
@ -244,18 +250,17 @@ TempoDialog::get_note_type ()
void
TempoDialog::note_types_change ()
{
set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
set_response_sensitive (RESPONSE_ACCEPT, true);
}
MeterDialog::MeterDialog (TempoMap& map, nframes_t frame, const string & action)
: ArdourDialog ("meter dialog"),
note_frame (_("Meter denominator")),
bpb_frame (_("Beats per bar")),
bpb_frame (_("Meter")),
ok_button (action),
cancel_button (_("Cancel")),
when_bar_label (_("Bar")),
when_beat_label (_("Beat")),
when_bar_label (_("Bar"), ALIGN_LEFT, ALIGN_CENTER),
when_beat_label (_("Beat"), ALIGN_LEFT, ALIGN_CENTER),
when_frame (_("Location"))
{
BBT_Time when;
@ -268,12 +273,11 @@ MeterDialog::MeterDialog (TempoMap& map, nframes_t frame, const string & action)
MeterDialog::MeterDialog (MeterSection& section, const string & action)
: ArdourDialog ("meter dialog"),
note_frame (_("Meter denominator")),
bpb_frame (_("Beats per bar")),
bpb_frame (_("Meter")),
ok_button (action),
cancel_button (_("Cancel")),
when_bar_label (_("Bar")),
when_beat_label (_("Beat")),
when_bar_label (_("Bar"), ALIGN_LEFT, ALIGN_CENTER),
when_beat_label (_("Beat"), ALIGN_LEFT, ALIGN_CENTER),
when_frame (_("Location"))
{
init (section.start(), section.beats_per_bar(), section.note_divisor(), section.movable());
@ -320,19 +324,16 @@ MeterDialog::init (const BBT_Time& when, double bpb, double note_type, bool mova
note_types.set_active_text (_("thirty-second (32)"));
else
note_types.set_active_text (_("quarter (4)"));
hspacer1.set_border_width (5);
hspacer1.pack_start (note_types, false, false);
vspacer1.set_border_width (5);
vspacer1.pack_start (hspacer1, false, false);
hspacer2.set_border_width (5);
hspacer2.pack_start (bpb_entry, false, false);
vspacer2.set_border_width (5);
vspacer2.pack_start (hspacer2, false, false);
Label* note_label = manage(new Label(_("Note Value:"), ALIGN_LEFT, ALIGN_CENTER));
Label* bpb_label = manage(new Label(_("Beats Per Bar:"), ALIGN_LEFT, ALIGN_CENTER));
Table* bpb_table = manage (new Table(2, 2));
note_frame.add (vspacer1);
bpb_frame.add (vspacer2);
bpb_table->attach (*bpb_label, 0, 1, 0, 1, FILL|EXPAND, FILL|EXPAND, 6, 6);
bpb_table->attach (bpb_entry, 1, 2, 0, 1, FILL|EXPAND, FILL|EXPAND, 6, 6);
bpb_table->attach (*note_label, 0, 1, 1, 2, FILL|EXPAND, FILL|EXPAND, 6, 6);
bpb_table->attach (note_types, 1, 2, 1, 2, FILL|EXPAND, SHRINK, 6, 6);
bpb_frame.add (*bpb_table);
if (movable) {
snprintf (buf, sizeof (buf), "%" PRIu32, when.bars);
@ -352,26 +353,29 @@ MeterDialog::init (const BBT_Time& when, double bpb, double note_type, bool mova
when_table.set_homogeneous (true);
when_table.set_row_spacings (2);
when_table.set_col_spacings (2);
when_table.set_border_width (5);
when_table.set_border_width (6);
when_table.attach (when_bar_label, 0, 1, 0, 1, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
when_table.attach (when_bar_entry, 0, 1, 1, 2, Gtk::AttachOptions(0), Gtk::FILL|Gtk::EXPAND);
when_table.attach (when_beat_label, 1, 2, 0, 1, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
when_table.attach (when_beat_entry, 1, 2, 1, 2, Gtk::AttachOptions(0), Gtk::AttachOptions(0));
when_table.attach (when_bar_label, 0, 1, 0, 1, AttachOptions(0), FILL|EXPAND);
when_table.attach (when_bar_entry, 1, 2, 0, 1, AttachOptions(0), FILL|EXPAND);
when_table.attach (when_beat_label, 0, 1, 1, 2, AttachOptions(0), AttachOptions(0));
when_table.attach (when_beat_entry, 1, 2, 1, 2, AttachOptions(0), AttachOptions(0));
HBox* when_hbox = manage (new HBox());
Label* when_label = manage(new Label(_("Meter Begins at:"), ALIGN_LEFT, ALIGN_TOP));
when_hbox->pack_end(when_table, PACK_EXPAND_PADDING, 6);
when_hbox->pack_start(*when_label, PACK_EXPAND_PADDING, 6);
when_frame.set_name ("MetricDialogFrame");
when_frame.add (when_table);
when_frame.add (*when_hbox);
get_vbox()->pack_start (when_frame, false, false);
get_vbox()->pack_end (when_frame, false, false);
}
get_vbox()->set_border_width (12);
get_vbox()->pack_start (bpb_frame, false, false);
get_vbox()->pack_start (note_frame, false, false);
bpb_frame.set_name ("MetricDialogFrame");
note_frame.set_name ("MetricDialogFrame");
bpb_entry.set_name ("MetricEntry");
add_button (Stock::CANCEL, RESPONSE_CANCEL);
@ -380,7 +384,6 @@ MeterDialog::init (const BBT_Time& when, double bpb, double note_type, bool mova
set_default_response (RESPONSE_ACCEPT);
get_vbox()->show_all ();
bpb_entry.show ();
set_name ("MetricDialog");
bpb_entry.signal_activate().connect (bind (mem_fun (*this, &MeterDialog::response), RESPONSE_ACCEPT));
@ -393,48 +396,48 @@ bool
MeterDialog::bpb_key_press (GdkEventKey* ev)
{
switch (ev->keyval) {
switch (ev->keyval) {
case GDK_0:
case GDK_1:
case GDK_2:
case GDK_3:
case GDK_4:
case GDK_5:
case GDK_6:
case GDK_7:
case GDK_8:
case GDK_9:
case GDK_KP_0:
case GDK_KP_1:
case GDK_KP_2:
case GDK_KP_3:
case GDK_KP_4:
case GDK_KP_5:
case GDK_KP_6:
case GDK_KP_7:
case GDK_KP_8:
case GDK_KP_9:
case GDK_period:
case GDK_comma:
case GDK_KP_Delete:
case GDK_KP_Enter:
case GDK_Delete:
case GDK_BackSpace:
case GDK_Escape:
case GDK_Return:
case GDK_Home:
case GDK_End:
case GDK_Left:
case GDK_Right:
case GDK_Num_Lock:
case GDK_Tab:
return FALSE;
default:
break;
}
case GDK_0:
case GDK_1:
case GDK_2:
case GDK_3:
case GDK_4:
case GDK_5:
case GDK_6:
case GDK_7:
case GDK_8:
case GDK_9:
case GDK_KP_0:
case GDK_KP_1:
case GDK_KP_2:
case GDK_KP_3:
case GDK_KP_4:
case GDK_KP_5:
case GDK_KP_6:
case GDK_KP_7:
case GDK_KP_8:
case GDK_KP_9:
case GDK_period:
case GDK_comma:
case GDK_KP_Delete:
case GDK_KP_Enter:
case GDK_Delete:
case GDK_BackSpace:
case GDK_Escape:
case GDK_Return:
case GDK_Home:
case GDK_End:
case GDK_Left:
case GDK_Right:
case GDK_Num_Lock:
case GDK_Tab:
return FALSE;
default:
break;
}
return TRUE;
return TRUE;
}
bool
@ -451,7 +454,7 @@ MeterDialog::bpb_key_release (GdkEventKey* ev)
void
MeterDialog::note_types_change ()
{
set_response_sensitive (Gtk::RESPONSE_ACCEPT, true);
set_response_sensitive (RESPONSE_ACCEPT, true);
}
double
@ -497,7 +500,6 @@ MeterDialog::get_note_type ()
bool
MeterDialog::get_bbt_time (BBT_Time& requested)
{
requested.ticks = 0;
if (sscanf (when_bar_entry.get_text().c_str(), "%" PRIu32, &requested.bars) != 1) {
return false;
@ -507,5 +509,7 @@ MeterDialog::get_bbt_time (BBT_Time& requested)
return false;
}
requested.ticks = 0;
return true;
}

View File

@ -37,74 +37,66 @@
struct TempoDialog : public ArdourDialog
{
Gtk::ComboBoxText note_types;
vector<string> strings;
Gtk::Adjustment bpm_adjustment;
Gtk::SpinButton bpm_spinner;
Gtk::Frame bpm_frame;
Gtk::Frame note_frame;
Gtk::VBox vpacker;
Gtk::Button ok_button;
Gtk::Button cancel_button;
Gtk::HBox button_box;
Gtk::HBox hspacer1, hspacer2;
Gtk::VBox vspacer1, vspacer2;
Gtk::Entry when_bar_entry;
Gtk::Entry when_beat_entry;
Gtk::Label when_bar_label;
Gtk::Label when_beat_label;
Gtk::Table when_table;
Gtk::Frame when_frame;
char buf[64];
Gtk::ComboBoxText note_types;
vector<string> strings;
Gtk::Adjustment bpm_adjustment;
Gtk::SpinButton bpm_spinner;
Gtk::Frame bpm_frame;
Gtk::Button ok_button;
Gtk::Button cancel_button;
Gtk::HBox hspacer1, hspacer2;
Gtk::VBox vspacer1;
Gtk::Entry when_bar_entry;
Gtk::Entry when_beat_entry;
Gtk::Label when_bar_label;
Gtk::Label when_beat_label;
Gtk::Table when_table;
Gtk::Frame when_frame;
char buf[64];
TempoDialog (ARDOUR::TempoMap&, nframes_t, const string & action);
TempoDialog (ARDOUR::TempoSection&, const string & action);
TempoDialog (ARDOUR::TempoMap&, nframes_t, const string & action);
TempoDialog (ARDOUR::TempoSection&, const string & action);
double get_bpm ();
double get_note_type ();
bool get_bbt_time (ARDOUR::BBT_Time&);
double get_bpm ();
double get_note_type ();
bool get_bbt_time (ARDOUR::BBT_Time&);
private:
void init (const ARDOUR::BBT_Time& start, double, double, bool);
void bpm_changed ();
bool bpm_button_press (GdkEventButton* );
bool bpm_button_release (GdkEventButton* );
void note_types_change ();
private:
void init (const ARDOUR::BBT_Time& start, double, double, bool);
void bpm_changed ();
bool bpm_button_press (GdkEventButton* );
bool bpm_button_release (GdkEventButton* );
void note_types_change ();
};
struct MeterDialog : public ArdourDialog
{
Gtk::Entry bpb_entry;
Gtk::ComboBoxText note_types;
vector<string> strings;
Gtk::Frame note_frame;
Gtk::Frame bpb_frame;
Gtk::VBox vpacker;
Gtk::Button ok_button;
Gtk::Button cancel_button;
Gtk::HBox button_box;
Gtk::HBox hspacer1, hspacer2;
Gtk::VBox vspacer1, vspacer2;
Gtk::Entry when_bar_entry;
Gtk::Entry when_beat_entry;
Gtk::Label when_bar_label;
Gtk::Label when_beat_label;
Gtk::Table when_table;
Gtk::Frame when_frame;
char buf[64];
Gtk::Entry bpb_entry;
Gtk::ComboBoxText note_types;
vector<string> strings;
Gtk::Frame bpb_frame;
Gtk::Button ok_button;
Gtk::Button cancel_button;
Gtk::Entry when_bar_entry;
Gtk::Entry when_beat_entry;
Gtk::Label when_bar_label;
Gtk::Label when_beat_label;
Gtk::Table when_table;
Gtk::Frame when_frame;
char buf[64];
MeterDialog (ARDOUR::TempoMap&, nframes_t, const string & action);
MeterDialog (ARDOUR::MeterSection&, const string & action);
MeterDialog (ARDOUR::TempoMap&, nframes_t, const string & action);
MeterDialog (ARDOUR::MeterSection&, const string & action);
double get_bpb ();
double get_note_type ();
bool get_bbt_time (ARDOUR::BBT_Time&);
double get_bpb ();
double get_note_type ();
bool get_bbt_time (ARDOUR::BBT_Time&);
private:
void init (const ARDOUR::BBT_Time&, double, double, bool);
bool bpb_key_press (GdkEventKey* );
bool bpb_key_release (GdkEventKey* );
void note_types_change ();
private:
void init (const ARDOUR::BBT_Time&, double, double, bool);
bool bpb_key_press (GdkEventKey* );
bool bpb_key_release (GdkEventKey* );
void note_types_change ();
};
#endif /* __ardour_gtk_tempo_dialog_h__ */

View File

@ -50,7 +50,8 @@ sigc::signal<void,uint32_t> ColorChanged;
ThemeManager::ThemeManager()
: ArdourDialog ("ThemeManager"),
dark_button ("Dark Theme"),
light_button ("Light Theme")
light_button ("Light Theme"),
reset_button ("Restore Defaults")
{
Gtkmm2ext::WindowTitle title (Glib::get_application_name ());
title += _("Theme Manager");
@ -81,6 +82,7 @@ ThemeManager::ThemeManager()
get_vbox()->set_homogeneous(false);
get_vbox()->pack_start (theme_selection_hbox, PACK_SHRINK);
get_vbox()->pack_start (reset_button, PACK_SHRINK);
get_vbox()->pack_start (scroller);
color_display.signal_button_press_event().connect (mem_fun (*this, &ThemeManager::button_press_event), false);
@ -92,6 +94,7 @@ ThemeManager::ThemeManager()
color_dialog.get_cancel_button()->signal_clicked().connect (bind (mem_fun (color_dialog, &Gtk::Dialog::response), RESPONSE_CANCEL));
dark_button.signal_toggled().connect (mem_fun (*this, &ThemeManager::on_dark_theme_button_toggled));
light_button.signal_toggled().connect (mem_fun (*this, &ThemeManager::on_light_theme_button_toggled));
reset_button.signal_clicked().connect (mem_fun (*this, &ThemeManager::reset_canvas_colors));
set_size_request (-1, 400);
setup_theme ();
@ -226,6 +229,8 @@ void
ThemeManager::setup_theme ()
{
int r, g, b, a;
color_list->clear();
for (std::vector<UIConfigVariable<uint32_t> *>::iterator i = ARDOUR_UI::config()->canvas_colors.begin(); i != ARDOUR_UI::config()->canvas_colors.end(); i++) {
TreeModel::Row row = *(color_list->append());
@ -262,3 +267,10 @@ ThemeManager::setup_theme ()
load_rc_file(rcfile, false);
}
void
ThemeManager::reset_canvas_colors()
{
ARDOUR_UI::config()->load_defaults();
setup_theme ();
}

View File

@ -25,6 +25,7 @@
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/colorselection.h>
#include <gtkmm/radiobutton.h>
#include <gtkmm/button.h>
#include <gtkmm/rc.h>
#include "ardour_dialog.h"
#include "ui_config.h"
@ -37,6 +38,7 @@ class ThemeManager : public ArdourDialog
int save (std::string path);
void setup_theme ();
void reset_canvas_colors();
void on_dark_theme_button_toggled ();
void on_light_theme_button_toggled ();
@ -66,6 +68,7 @@ class ThemeManager : public ArdourDialog
Gtk::HBox theme_selection_hbox;
Gtk::RadioButton dark_button;
Gtk::RadioButton light_button;
Gtk::Button reset_button;
bool button_press_event (GdkEventButton*);
};

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"
@ -1101,3 +1102,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 (*i);
(*l)->property_x2() = editor.frame_to_pixel (*i);
}
}
void
TimeAxisView::hide_temporary_lines ()
{
for (list<ArdourCanvas::SimpleLine*>::iterator l = temp_lines.begin(); l != temp_lines.end(); ++l) {
(*l)->hide ();
}
}

View File

@ -172,6 +172,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 ();
@ -309,6 +312,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

@ -56,11 +56,40 @@ UIConfiguration::~UIConfiguration ()
{
}
int
UIConfiguration::load_defaults ()
{
int found = 0;
sys::path default_ui_rc_file;
if ( find_file_in_search_path (ardour_search_path() + system_config_search_path(),
"ardour3_ui_default.conf", default_ui_rc_file) )
{
XMLTree tree;
found = 1;
string rcfile = default_ui_rc_file.to_string();
cerr << string_compose (_("loading default ui configuration file %1"), rcfile) << endl;
if (!tree.read (rcfile.c_str())) {
error << string_compose(_("Ardour: cannot read default ui configuration file \"%1\""), rcfile) << endmsg;
return -1;
}
if (set_state (*tree.root())) {
error << string_compose(_("Ardour: default ui configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
return -1;
}
}
return found;
}
int
UIConfiguration::load_state ()
{
bool found = false;
sys::path default_ui_rc_file;
if ( find_file_in_search_path (ardour_search_path() + system_config_search_path(),

View File

@ -108,6 +108,7 @@ class UIConfiguration : public PBD::Stateful
int load_state ();
int save_state ();
int load_defaults ();
int set_state (const XMLNode&);
XMLNode& get_state (void);

View File

@ -31,6 +31,7 @@ amp.cc
audio_buffer.cc
auto_bundle.cc
user_bundle.cc
audioanalyser.cc
audio_diskstream.cc
audio_library.cc
audio_playlist.cc
@ -134,6 +135,7 @@ tape_file_matcher.cc
template_utils.cc
tempo.cc
track.cc
transient_detector.cc
utils.cc
version.cc
""")
@ -162,6 +164,7 @@ if ardour['LIBLO']:
ardour.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
ardour.Append(CXXFLAGS="-DDATA_DIR=\\\"" + os.path.join (final_prefix, 'share') + "\\\"")
ardour.Append(CXXFLAGS="-DMODULE_DIR=\\\"" + os.path.join (final_prefix, env['LIBDIR']) + "\\\"")
ardour.Append(CXXFLAGS="-DVAMP_DIR=\\\"" + os.path.join (final_prefix, env['LIBDIR'], 'ardour2', 'vamp') + "\\\"")
ardour.Append(CXXFLAGS="-DCONFIG_DIR=\\\"" + final_config_prefix + "\\\"")
ardour.Append(CXXFLAGS="-DLOCALEDIR=\\\"" + os.path.join (final_prefix, 'share', 'locale') + "\\\"")
@ -304,15 +307,19 @@ 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'] ])
# timefx_sources += [ 'rb_effect.cc' ]
#else:
ardour.Merge ([ libraries['soundtouch'] ])
timefx_sources += [ 'st_stretch.cc', 'st_pitch.cc' ]
if ardour['RUBBERBAND']:
ardour.Merge ([ libraries['rubberband']])
timefx_sources += [ 'rb_effect.cc' ]
else:
ardour.Merge ([ libraries['soundtouch'] ])
timefx_sources += [ 'st_stretch.cc', 'st_pitch.cc' ]
if ardour['LV2']:
ardour.Merge ([ libraries['slv2'] ])

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 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, 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>
@ -75,6 +76,11 @@ 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,
@ -128,12 +134,14 @@ class AudioRegion : public Region
void resume_fade_in ();
void resume_fade_out ();
int get_transients (std::vector<nframes64_t>&, bool force_new = false);
private:
friend class RegionFactory;
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<AudioSource>, const XMLNode&);
AudioRegion (SourceList &, const XMLNode&);
@ -148,10 +156,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;
void recompute_at_start ();
void recompute_at_end ();
@ -178,6 +187,11 @@ 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 */

View File

@ -50,10 +50,20 @@ class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR:
AudioSource (Session&, const XMLNode&);
virtual ~AudioSource ();
/* 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 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);
@ -101,6 +111,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;
@ -134,6 +147,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

@ -186,17 +186,7 @@ static inline cycles_t get_cycles (void)
/* begin mach */
#elif defined(__APPLE__)
#ifdef HAVE_WEAK_COREAUDIO
#include <CoreAudio/HostTime.h>
#else // Due to MacTypes.h and libgnomecanvasmm Rect conflict
typedef unsigned long long UInt64;
extern UInt64
AudioGetCurrentHostTime();
extern UInt64
AudioConvertHostTimeToNanos(UInt64 inHostTime);
#endif
typedef UInt64 cycles_t;
static inline cycles_t get_cycles (void)

View File

@ -37,20 +37,21 @@
namespace ARDOUR {
class AudioEngine;
class Session;
struct LV2World;
class LV2Plugin : public ARDOUR::Plugin
{
public:
LV2Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&, SLV2Plugin plugin, nframes_t sample_rate);
LV2Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&, ARDOUR::LV2World&, SLV2Plugin plugin, nframes_t sample_rate);
LV2Plugin (const LV2Plugin &);
~LV2Plugin ();
/* Plugin interface */
std::string unique_id() const;
const char* label() const { return slv2_plugin_get_name(_plugin); }
const char* name() const { return slv2_plugin_get_name(_plugin); }
const char* maker() const { return slv2_plugin_get_author_name(_plugin); }
const char* label() const { return slv2_value_as_string(_name); }
const char* name() const { return slv2_value_as_string(_name); }
const char* maker() const { return _author ? slv2_value_as_string(_author) : "Unknown"; }
uint32_t parameter_count() const { return slv2_plugin_get_num_ports(_plugin); }
float default_value (uint32_t port);
nframes_t signal_latency() const;
@ -58,6 +59,9 @@ class LV2Plugin : public ARDOUR::Plugin
float get_parameter (uint32_t port) const;
int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const;
uint32_t nth_parameter (uint32_t port, bool& ok) const;
SLV2Plugin slv2_plugin() { return _plugin; }
SLV2Port slv2_port(uint32_t i) { return slv2_plugin_get_port_by_index(_plugin, i); }
std::set<Parameter> automatable() const;
@ -105,29 +109,56 @@ class LV2Plugin : public ARDOUR::Plugin
private:
void* _module;
LV2World& _world;
SLV2Plugin _plugin;
SLV2Template _template;
SLV2Value _name;
SLV2Value _author;
SLV2Instance _instance;
nframes_t _sample_rate;
float* _control_data;
float* _shadow_data;
float* _defaults;
float* _latency_control_port;
bool _was_activated;
vector<bool> _port_is_input;
void init (SLV2Plugin plugin, nframes_t rate);
void init (LV2World& world, SLV2Plugin plugin, nframes_t rate);
void run (nframes_t nsamples);
void latency_compute_run ();
};
/** The SLV2World, and various cached (as symbols, fast) URIs.
*
* This object represents everything ardour 'knows' about LV2
* (ie understood extensions/features/etc)
*/
struct LV2World {
LV2World();
~LV2World();
SLV2World world;
SLV2Value input_class;
SLV2Value output_class;
SLV2Value audio_class;
SLV2Value control_class;
SLV2Value event_class;
SLV2Value in_place_broken;
SLV2Value integer;
SLV2Value toggled;
SLV2Value srate;
};
class LV2PluginInfo : public PluginInfo {
public:
LV2PluginInfo (void* slv2_plugin);;
LV2PluginInfo (void* slv2_world, void* slv2_plugin);;
~LV2PluginInfo ();;
static PluginInfoList discover (void* slv2_world);
PluginPtr load (Session& session);
void* _lv2_world;
void* _slv2_plugin;
};

View File

@ -50,6 +50,10 @@ class MidiRegion : public Region
~MidiRegion();
boost::shared_ptr<MidiSource> midi_source (uint32_t n=0) const;
/* Stub Readable interface */
virtual nframes64_t read (Sample*, nframes64_t pos, nframes64_t cnt, int channel) const { return 0; }
virtual nframes64_t readable_length() const { return length(); }
nframes_t read_at (MidiRingBuffer& dst,
nframes_t position,
@ -86,11 +90,11 @@ class MidiRegion : public Region
MidiRegion (boost::shared_ptr<MidiSource>, nframes_t start, nframes_t length);
MidiRegion (boost::shared_ptr<MidiSource>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
MidiRegion (SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
MidiRegion (const SourceList &, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
MidiRegion (boost::shared_ptr<const MidiRegion>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Region::Flag flags = Region::DefaultFlags);
MidiRegion (boost::shared_ptr<const MidiRegion>);
MidiRegion (boost::shared_ptr<MidiSource>, const XMLNode&);
MidiRegion (SourceList &, const XMLNode&);
MidiRegion (const SourceList &, const XMLNode&);
private:
nframes_t _read_at (const SourceList&, MidiRingBuffer& dst,

View File

@ -49,8 +49,15 @@ class MidiSource : public Source
MidiSource (Session& session, const XMLNode&);
virtual ~MidiSource ();
virtual nframes_t read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const;
virtual nframes_t write (MidiRingBuffer& src, nframes_t cnt);
/* Stub Readable interface */
virtual nframes64_t read (Sample*, nframes64_t pos, nframes64_t cnt, int channel) const { return 0; }
virtual nframes64_t readable_length() const { return length(); }
virtual uint32_t n_channels () const { return 1; }
// FIXME: integrate this with the Readable::read interface somehow
virtual nframes_t midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const;
virtual nframes_t midi_write (MidiRingBuffer& src, nframes_t cnt);
virtual void append_event_unlocked(const MidiEvent& ev) = 0;
virtual void mark_for_remove() = 0;

View File

@ -28,7 +28,7 @@
#include <ardour/plugin.h>
#ifdef HAVE_SLV2
#include <slv2/slv2.h>
#include <ardour/lv2_plugin.h>
#endif
namespace ARDOUR {
@ -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; }
@ -59,7 +61,7 @@ class PluginManager {
ARDOUR::PluginInfoList _au_plugin_info;
#ifdef HAVE_SLV2
SLV2World _lv2_world;
LV2World* _lv2_world;
#endif
std::map<uint32_t, std::string> rdf_type;

View File

@ -0,0 +1,20 @@
#ifndef __ardour_readable_h__
#define __ardour_readable_h__
#include <ardour/types.h>
namespace ARDOUR {
class Readable {
public:
Readable () {}
virtual ~Readable() {}
virtual nframes64_t read (Sample*, nframes64_t pos, nframes64_t cnt, int channel) const = 0;
virtual nframes64_t readable_length() const = 0;
virtual uint32_t n_channels () const = 0;
};
}
#endif /* __ardour_readable_h__ */

View File

@ -29,6 +29,7 @@
#include <ardour/ardour.h>
#include <ardour/data_type.h>
#include <ardour/automatable.h>
#include <ardour/readable.h>
class XMLNode;
@ -43,7 +44,7 @@ enum RegionEditState {
EditChangesID = 2
};
class Region : public Automatable, public boost::enable_shared_from_this<Region>
class Region : public Automatable, public boost::enable_shared_from_this<Region>, public Readable
{
public:
typedef std::vector<boost::shared_ptr<Source> > SourceList;
@ -217,13 +218,13 @@ class Region : public Automatable, public boost::enable_shared_from_this<Region>
Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length,
const string& name, DataType type, layer_t = 0, Flag flags = DefaultFlags);
Region (SourceList& srcs, nframes_t start, nframes_t length,
Region (const SourceList& srcs, nframes_t start, nframes_t length,
const string& name, DataType type, layer_t = 0, Flag flags = DefaultFlags);
Region (boost::shared_ptr<const Region>, nframes_t start, nframes_t length, const string& name, layer_t = 0, Flag flags = DefaultFlags);
Region (boost::shared_ptr<const Region>);
Region (boost::shared_ptr<Source> src, const XMLNode&);
Region (SourceList& srcs, const XMLNode&);
Region (const SourceList& srcs, const XMLNode&);
/* this one is for derived types of derived types */

View File

@ -48,7 +48,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

@ -261,6 +261,9 @@ class Session : public PBD::StatefulDestructible
const SessionDirectory& session_directory () const { return *(_session_dir.get()); }
std::string automation_dir () const;
std::string analysis_dir() const;
int ensure_subdirs ();
Glib::ustring peak_path (Glib::ustring) const;

View File

@ -30,13 +30,14 @@
#include <ardour/ardour.h>
#include <ardour/session_object.h>
#include <ardour/data_type.h>
#include <ardour/readable.h>
namespace ARDOUR {
class Session;
class Playlist;
class Source : public SessionObject
class Source : public SessionObject, public ARDOUR::Readable
{
public:
Source (Session&, const std::string& name, DataType type);

View File

@ -109,6 +109,9 @@ class MetricSection {
public:
MetricSection (const BBT_Time& start)
: _start (start), _frame (0), _movable (true) {}
MetricSection (nframes_t start)
: _frame (start), _movable (true) {}
virtual ~MetricSection() {}
const BBT_Time& start() const { return _start; }
@ -142,6 +145,8 @@ class MeterSection : public MetricSection, public Meter {
public:
MeterSection (const BBT_Time& start, double bpb, double note_type)
: MetricSection (start), Meter (bpb, note_type) {}
MeterSection (nframes_t start, double bpb, double note_type)
: MetricSection (start), Meter (bpb, note_type) {}
MeterSection (const XMLNode&);
static const string xml_state_node_name;
@ -153,6 +158,8 @@ class TempoSection : public MetricSection, public Tempo {
public:
TempoSection (const BBT_Time& start, double qpm, double note_type)
: MetricSection (start), Tempo (qpm, note_type) {}
TempoSection (nframes_t start, double qpm, double note_type)
: MetricSection (start), Tempo (qpm, note_type) {}
TempoSection (const XMLNode&);
static const string xml_state_node_name;
@ -165,7 +172,6 @@ typedef list<MetricSection*> Metrics;
class TempoMap : public PBD::StatefulDestructible
{
public:
TempoMap (nframes_t frame_rate);
~TempoMap();
@ -207,9 +213,14 @@ class TempoMap : public PBD::StatefulDestructible
const Tempo& tempo_at (nframes_t);
const Meter& meter_at (nframes_t);
const TempoSection& tempo_section_at (nframes_t);
void add_tempo(const Tempo&, BBT_Time where);
void add_meter(const Meter&, BBT_Time where);
void add_tempo(const Tempo&, nframes_t where);
void add_meter(const Meter&, nframes_t where);
void move_tempo (TempoSection&, const BBT_Time& to);
void move_meter (MeterSection&, const BBT_Time& to);
@ -267,6 +278,8 @@ class TempoMap : public PBD::StatefulDestructible
Metric metric_at (nframes_t) const;
void bbt_time_with_metric (nframes_t, BBT_Time&, const Metric&) const;
void change_existing_tempo_at (nframes_t, double bpm, double note_type);
sigc::signal<void,ARDOUR::Change> StateChanged;
private:
@ -280,8 +293,7 @@ class TempoMap : public PBD::StatefulDestructible
BBT_Time last_bbt;
mutable Glib::RWLock lock;
void timestamp_metrics ();
void timestamp_metrics (bool use_bbt);
nframes_t round_to_type (nframes_t fr, int dir, BBTPointType);
@ -298,7 +310,7 @@ class TempoMap : public PBD::StatefulDestructible
nframes_t count_frames_between_metrics (const Meter&, const Tempo&, const BBT_Time&, const BBT_Time&) const;
int move_metric_section (MetricSection&, const BBT_Time& to);
void do_insert (MetricSection* section);
void do_insert (MetricSection* section, bool with_bbt);
};
}; /* namespace ARDOUR */

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, 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) {
error << string_compose (_("VAMP Plugin \"%1\" could not be loaded"), key) << endmsg;
return -1;
}
/* we asked for the buffering adapter, so set the blocksize to
something that makes for efficient disk i/o
*/
bufsize = 65536;
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, 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>
@ -42,6 +43,7 @@
#include <ardour/audiofilesource.h>
#include <ardour/region_factory.h>
#include <ardour/runtime_functions.h>
#include <ardour/transient_detector.h>
#include "i18n.h"
#include <locale.h>
@ -64,6 +66,7 @@ void
AudioRegion::init ()
{
_scale_amplitude = 1.0;
valid_transients = false;
set_default_fades ();
set_default_envelope ();
@ -112,7 +115,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, n
}
/* Basic AudioRegion constructor (many channels) */
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 (srcs, start, length, name, DataType::AUDIO, layer, flags)
, _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
, _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
@ -121,7 +124,6 @@ AudioRegion::AudioRegion (SourceList& srcs, nframes_t start, nframes_t length, c
init ();
}
/** Create a new AudioRegion, that is part of an existing one */
AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
: Region (other, offset, length, name, layer, flags)
@ -170,6 +172,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other, nframes_t
}
_scale_amplitude = other->_scale_amplitude;
valid_transients = false;
assert(_type == DataType::AUDIO);
}
@ -181,6 +184,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
, _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
{
_scale_amplitude = other->_scale_amplitude;
valid_transients = false;
_envelope = other->_envelope;
set_default_fades ();
@ -202,6 +206,7 @@ AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& nod
}
init ();
valid_transients = false;
if (set_state (node)) {
throw failed_constructor();
@ -229,6 +234,13 @@ AudioRegion::~AudioRegion ()
{
}
void
AudioRegion::invalidate_transients ()
{
valid_transients = false;
_transients.clear ();
}
void
AudioRegion::listen_to_my_curves ()
{
@ -273,12 +285,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
@ -291,13 +311,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 */
}
@ -320,14 +343,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()) {
@ -336,7 +361,9 @@ AudioRegion::_read_at (const SourceList& srcs, Sample *buf, Sample *mixdown_buff
return 0; /* "read nothing" */
}
_read_data_count += src->read_data_count();
if (!raw) {
_read_data_count += src->read_data_count();
}
} else {
@ -348,37 +375,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->curve().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->curve().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
@ -389,65 +420,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->curve().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->curve().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->curve().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->curve().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) {
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;
}
@ -1224,6 +1256,93 @@ 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)
{
if (!playlist()) {
return -1;
}
if (valid_transients && !force_new) {
results = _transients;
return 0;
}
TransientDetector t (playlist()->session().frame_rate());
bool existing_results = !results.empty();
_transients.clear ();
valid_transients = false;
for (uint32_t i = 0; i < n_channels(); ++i) {
vector<nframes64_t> these_results;
t.reset ();
if (t.run ("", this, i, these_results)) {
return -1;
}
/* translate all transients to give absolute position */
for (vector<nframes64_t>::iterator i = these_results.begin(); i != these_results.end(); ++i) {
(*i) += _position;
}
/* merge */
_transients.insert (_transients.end(), these_results.begin(), these_results.end());
}
if (!results.empty()) {
if (existing_results) {
/* merge our transients into the existing ones, then clean up
those.
*/
results.insert (results.end(), _transients.begin(), _transients.end());
cleanup_transients (results);
}
/* make sure ours are clean too */
cleanup_transients (_transients);
}
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)

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"
@ -916,3 +919,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

@ -285,6 +285,17 @@ ARDOUR::init (bool use_vst, bool try_optimization)
return -1;
}
#endif
/* Make VAMP look in our library ahead of anything else */
char *p = getenv ("VAMP_PATH");
string vamppath = VAMP_DIR;
if (p) {
vamppath += ':';
vamppath += p;
}
setenv ("VAMP_PATH", vamppath.c_str(), 1);
setup_hardware_optimization (try_optimization);

View File

@ -467,7 +467,6 @@ LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& des
desc.label = port_names()[which];
return 0;
}

View File

@ -43,16 +43,18 @@ using namespace std;
using namespace ARDOUR;
using namespace PBD;
LV2Plugin::LV2Plugin (AudioEngine& e, Session& session, SLV2Plugin plugin, nframes_t rate)
LV2Plugin::LV2Plugin (AudioEngine& e, Session& session, LV2World& world, SLV2Plugin plugin, nframes_t rate)
: Plugin (e, session)
, _world(world)
{
init (plugin, rate);
init (world, plugin, rate);
}
LV2Plugin::LV2Plugin (const LV2Plugin &other)
: Plugin (other)
, _world(other._world)
{
init (other._plugin, other._sample_rate);
init (other._world, other._plugin, other._sample_rate);
for (uint32_t i = 0; i < parameter_count(); ++i) {
_control_data[i] = other._shadow_data[i];
@ -61,24 +63,30 @@ LV2Plugin::LV2Plugin (const LV2Plugin &other)
}
void
LV2Plugin::init (SLV2Plugin plugin, nframes_t rate)
LV2Plugin::init (LV2World& world, SLV2Plugin plugin, nframes_t rate)
{
_world = world;
_plugin = plugin;
_template = slv2_plugin_get_template(plugin);
_control_data = 0;
_shadow_data = 0;
_latency_control_port = 0;
_was_activated = false;
_instance = slv2_plugin_instantiate(plugin, rate, NULL);
_name = slv2_plugin_get_name(plugin);
assert(_name);
_author = slv2_plugin_get_author_name(plugin);
if (_instance == 0) {
error << _("LV2: Failed to instantiate plugin ") << slv2_plugin_get_uri(plugin) << endl;
throw failed_constructor();
}
if (slv2_plugin_has_feature(plugin, "http://lv2plug.in/ns/lv2core#inPlaceBroken")) {
error << string_compose(_("LV2: \"%1\" cannot be used, since it cannot do inplace processing"), slv2_plugin_get_name(plugin)) << endmsg;
if (slv2_plugin_has_feature(plugin, world.in_place_broken)) {
error << string_compose(_("LV2: \"%1\" cannot be used, since it cannot do inplace processing"),
slv2_value_as_string(_name));
slv2_value_free(_name);
slv2_value_free(_author);
throw failed_constructor();
}
@ -88,14 +96,21 @@ LV2Plugin::init (SLV2Plugin plugin, nframes_t rate)
_control_data = new float[num_ports];
_shadow_data = new float[num_ports];
_defaults = new float[num_ports];
const bool latent = slv2_plugin_has_latency(plugin);
uint32_t latency_port = (latent ? slv2_plugin_get_latency_port(plugin) : 0);
uint32_t latency_port = (latent ? slv2_plugin_get_latency_port_index(plugin) : 0);
for (uint32_t i = 0; i < num_ports; ++i) {
if (parameter_is_control(i)) {
SLV2Port port = slv2_plugin_get_port_by_index(plugin, i);
SLV2Value def;
slv2_port_get_range(plugin, port, &def, NULL, NULL);
_defaults[i] = def ? slv2_value_as_float(def) : 0.0f;
slv2_value_free(def);
slv2_instance_connect_port (_instance, i, &_control_data[i]);
if (latent && i == latency_port) {
_latency_control_port = &_control_data[i];
*_latency_control_port = 0;
@ -104,6 +119,8 @@ LV2Plugin::init (SLV2Plugin plugin, nframes_t rate)
if (parameter_is_input(i)) {
_shadow_data[i] = default_value (i);
}
} else {
_defaults[i] = 0.0f;
}
}
@ -118,6 +135,8 @@ LV2Plugin::~LV2Plugin ()
GoingAway (); /* EMIT SIGNAL */
slv2_instance_free(_instance);
slv2_value_free(_name);
slv2_value_free(_author);
if (_control_data) {
delete [] _control_data;
@ -131,15 +150,14 @@ LV2Plugin::~LV2Plugin ()
string
LV2Plugin::unique_id() const
{
return slv2_plugin_get_uri(_plugin);
return slv2_value_as_uri(slv2_plugin_get_uri(_plugin));
}
float
LV2Plugin::default_value (uint32_t port)
{
return slv2_port_get_default_value(_plugin,
slv2_plugin_get_port_by_index(_plugin, port));
return _defaults[port];
}
void
@ -276,15 +294,16 @@ LV2Plugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc)
{
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, which);
#define LV2_URI "http://lv2plug.in/ns/lv2core#"
SLV2Value def, min, max;
slv2_port_get_range(_plugin, port, &def, &min, &max);
desc.integer_step = slv2_port_has_property(_plugin, port, LV2_URI "integer");
desc.toggled = slv2_port_has_property(_plugin, port, LV2_URI "toggled");
desc.integer_step = slv2_port_has_property(_plugin, port, _world.integer);
desc.toggled = slv2_port_has_property(_plugin, port, _world.toggled);
desc.logarithmic = false; // TODO (LV2 extension)
desc.sr_dependent = slv2_port_has_property(_plugin, port, LV2_URI "sampleRate");
desc.label = slv2_port_get_name(_plugin, port);
desc.lower = slv2_port_get_minimum_value(_plugin, port);
desc.upper = slv2_port_get_maximum_value(_plugin, port);
desc.sr_dependent = slv2_port_has_property(_plugin, port, _world.srate);
desc.label = slv2_value_as_string(slv2_port_get_name(_plugin, port));
desc.lower = min ? slv2_value_as_float(min) : 0.0f;
desc.upper = max ? slv2_value_as_float(max) : 1.0f;
desc.min_unbound = false; // TODO (LV2 extension)
desc.max_unbound = false; // TODO (LV2 extension)
@ -299,6 +318,10 @@ LV2Plugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc)
desc.largestep = delta/10.0f;
}
slv2_value_free(def);
slv2_value_free(min);
slv2_value_free(max);
return 0;
}
@ -307,8 +330,11 @@ string
LV2Plugin::describe_parameter (Parameter which)
{
if (which.type() == PluginAutomation && which.id() < parameter_count()) {
return slv2_port_get_name(_plugin,
slv2_plugin_get_port_by_index(_plugin, which));
SLV2Value name = slv2_port_get_name(_plugin,
slv2_plugin_get_port_by_index(_plugin, which));
string ret(slv2_value_as_string(name));
slv2_value_free(name);
return ret;
} else {
return "??";
}
@ -377,29 +403,29 @@ LV2Plugin::connect_and_run (BufferSet& bufs, uint32_t& in_index, uint32_t& out_i
bool
LV2Plugin::parameter_is_control (uint32_t param) const
{
SLV2PortSignature sig = slv2_template_get_port(_template, param);
return (slv2_port_signature_get_type(sig) == SLV2_PORT_DATA_TYPE_CONTROL);
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
return slv2_port_is_a(_plugin, port, _world.control_class);
}
bool
LV2Plugin::parameter_is_audio (uint32_t param) const
{
SLV2PortSignature sig = slv2_template_get_port(_template, param);
return (slv2_port_signature_get_type(sig) == SLV2_PORT_DATA_TYPE_AUDIO);
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
return slv2_port_is_a(_plugin, port, _world.audio_class);
}
bool
LV2Plugin::parameter_is_output (uint32_t param) const
{
SLV2PortSignature sig = slv2_template_get_port(_template, param);
return (slv2_port_signature_get_direction(sig) == SLV2_PORT_DIRECTION_OUTPUT);
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
return slv2_port_is_a(_plugin, port, _world.output_class);
}
bool
LV2Plugin::parameter_is_input (uint32_t param) const
{
SLV2PortSignature sig = slv2_template_get_port(_template, param);
return (slv2_port_signature_get_direction(sig) == SLV2_PORT_DIRECTION_INPUT);
SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
return slv2_port_is_a(_plugin, port, _world.input_class);
}
void
@ -418,9 +444,7 @@ void
LV2Plugin::run (nframes_t nframes)
{
for (uint32_t i = 0; i < parameter_count(); ++i) {
SLV2PortSignature sig = slv2_template_get_port(_template, i);
if (slv2_port_signature_get_type(sig) == SLV2_PORT_DATA_TYPE_CONTROL
&& slv2_port_signature_get_direction(sig) == SLV2_PORT_DIRECTION_INPUT) {
if (parameter_is_control(i) && parameter_is_input(i)) {
_control_data[i] = _shadow_data[i];
}
}
@ -472,8 +496,34 @@ LV2Plugin::latency_compute_run ()
deactivate ();
}
LV2PluginInfo::LV2PluginInfo (void* slv2_plugin)
: _slv2_plugin(slv2_plugin)
LV2World::LV2World()
: world(slv2_world_new())
{
slv2_world_load_all(world);
input_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_INPUT);
output_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_OUTPUT);
control_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_CONTROL);
audio_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_AUDIO);
event_class = slv2_value_new_uri(world, SLV2_PORT_CLASS_EVENT);
in_place_broken = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "inPlaceBroken");
integer = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "integer");
toggled = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "toggled");
srate = slv2_value_new_uri(world, SLV2_NAMESPACE_LV2 "sampleRate");
}
LV2World::~LV2World()
{
slv2_value_free(input_class);
slv2_value_free(output_class);
slv2_value_free(control_class);
slv2_value_free(audio_class);
slv2_value_free(event_class);
slv2_value_free(in_place_broken);
}
LV2PluginInfo::LV2PluginInfo (void* lv2_world, void* slv2_plugin)
: _lv2_world(lv2_world)
, _slv2_plugin(slv2_plugin)
{
}
@ -484,12 +534,11 @@ LV2PluginInfo::~LV2PluginInfo()
PluginPtr
LV2PluginInfo::load (Session& session)
{
SLV2Plugin p = (SLV2Plugin)_slv2_plugin;
try {
PluginPtr plugin;
plugin.reset (new LV2Plugin (session.engine(), session, p, session.frame_rate()));
plugin.reset (new LV2Plugin (session.engine(), session,
*(LV2World*)_lv2_world, (SLV2Plugin)_slv2_plugin, session.frame_rate()));
plugin->set_info(PluginInfoPtr(new LV2PluginInfo(*this)));
return plugin;
@ -503,40 +552,42 @@ LV2PluginInfo::load (Session& session)
}
PluginInfoList
LV2PluginInfo::discover (void* slv2_world)
LV2PluginInfo::discover (void* lv2_world)
{
PluginInfoList plugs;
SLV2Plugins plugins = slv2_world_get_all_plugins((SLV2World)slv2_world);
LV2World* world = (LV2World*)lv2_world;
SLV2Plugins plugins = slv2_world_get_all_plugins(world->world);
for (unsigned i=0; i < slv2_plugins_size(plugins); ++i) {
SLV2Plugin p = slv2_plugins_get_at(plugins, i);
LV2PluginInfoPtr info (new LV2PluginInfo(p));
LV2PluginInfoPtr info (new LV2PluginInfo(lv2_world, p));
info->name = slv2_plugin_get_name(p);
SLV2Value name = slv2_plugin_get_name(p);
info->name = string(slv2_value_as_string(name));
slv2_value_free(name);
SLV2PluginClass pclass = slv2_plugin_get_class(p);
info->category = slv2_plugin_class_get_label(pclass);
SLV2Value label = slv2_plugin_class_get_label(pclass);
info->category = slv2_value_as_string(label);
char* author_name = slv2_plugin_get_author_name(p);
info->creator = author_name ? string(author_name) : "Unknown";
free(author_name);
SLV2Value author_name = slv2_plugin_get_author_name(p);
info->creator = author_name ? string(slv2_value_as_string(author_name)) : "Unknown";
slv2_value_free(author_name);
info->path = "/NOPATH"; // Meaningless for LV2
SLV2Template io = slv2_plugin_get_template(p);
info->n_inputs.set_audio(slv2_template_get_num_ports_of_type(io,
SLV2_PORT_DIRECTION_INPUT, SLV2_PORT_DATA_TYPE_AUDIO));
info->n_outputs.set_audio(slv2_template_get_num_ports_of_type(io,
SLV2_PORT_DIRECTION_OUTPUT, SLV2_PORT_DATA_TYPE_AUDIO));
info->n_inputs.set_audio(slv2_plugin_get_num_ports_of_class(p,
world->input_class, world->audio_class, NULL));
info->n_inputs.set_midi(slv2_plugin_get_num_ports_of_class(p,
world->input_class, world->event_class, NULL));
info->n_inputs.set_midi(slv2_template_get_num_ports_of_type(io,
SLV2_PORT_DIRECTION_INPUT, SLV2_PORT_DATA_TYPE_MIDI));
info->n_outputs.set_midi(slv2_template_get_num_ports_of_type(io,
SLV2_PORT_DIRECTION_OUTPUT, SLV2_PORT_DATA_TYPE_MIDI));
info->n_outputs.set_audio(slv2_plugin_get_num_ports_of_class(p,
world->output_class, world->audio_class, NULL));
info->n_outputs.set_midi(slv2_plugin_get_num_ports_of_class(p,
world->output_class, world->event_class, NULL));
info->unique_id = slv2_plugin_get_uri(p);
info->unique_id = slv2_value_as_uri(slv2_plugin_get_uri(p));
info->index = 0; // Meaningless for LV2
plugs.push_back (info);

View File

@ -933,7 +933,7 @@ MidiDiskstream::do_flush (Session::RunContext context, bool force_flush)
assert(!destructive());
if (record_enabled() && _session.transport_frame() - _last_flush_frame > disk_io_chunk_frames) {
if ((!_write_source) || _write_source->write (*_capture_buf, to_write) != to_write) {
if ((!_write_source) || _write_source->midi_write (*_capture_buf, to_write) != to_write) {
error << string_compose(_("MidiDiskstream %1: cannot write to disk"), _id) << endmsg;
return -1;
} else {

View File

@ -65,7 +65,7 @@ MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, nframes_t start, nfra
}
/* Basic MidiRegion constructor (many channels) */
MidiRegion::MidiRegion (SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
MidiRegion::MidiRegion (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
: Region (srcs, start, length, name, DataType::MIDI, layer, flags)
{
assert(_name.find("/") == string::npos);
@ -100,7 +100,7 @@ MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, const XMLNode& node)
assert(_type == DataType::MIDI);
}
MidiRegion::MidiRegion (SourceList& srcs, const XMLNode& node)
MidiRegion::MidiRegion (const SourceList& srcs, const XMLNode& node)
: Region (srcs, node)
{
if (set_state (node)) {
@ -171,7 +171,7 @@ MidiRegion::_read_at (const SourceList& srcs, MidiRingBuffer& dst, nframes_t pos
boost::shared_ptr<MidiSource> src = midi_source(chan_n);
src->set_note_mode(mode);
if (src->read (dst, _start + internal_offset, to_read, _position) != to_read) {
if (src->midi_read (dst, _start + internal_offset, to_read, _position) != to_read) {
return 0; /* "read nothing" */
}

View File

@ -101,7 +101,7 @@ MidiSource::set_state (const XMLNode& node)
}
nframes_t
MidiSource::read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const
MidiSource::midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset) const
{
Glib::Mutex::Lock lm (_lock);
if (_model) {
@ -114,7 +114,7 @@ MidiSource::read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t
}
nframes_t
MidiSource::write (MidiRingBuffer& dst, nframes_t cnt)
MidiSource::midi_write (MidiRingBuffer& dst, nframes_t cnt)
{
Glib::Mutex::Lock lm (_lock);
return write_unlocked (dst, cnt);

View File

@ -110,8 +110,7 @@ PluginManager::PluginManager ()
}
#ifdef HAVE_SLV2
_lv2_world = slv2_world_new();
slv2_world_load_all(_lv2_world);
_lv2_world = new LV2World();
#endif
refresh ();

View File

@ -296,8 +296,8 @@ PortFacade::disconnect (Port& other)
int
PortFacade::disconnect_all ()
{
int reta;
int retb;
int reta = 0;
int retb = 0;
if (_ext_port) {
reta = _ext_port->disconnect_all ();

View File

@ -71,9 +71,9 @@ Quantize::run (boost::shared_ptr<Region> r)
for (MidiModel::Notes::iterator i = model->notes().begin(); i != model->notes().end(); ++i) {
const double new_time = lrint((*i)->time() / q_frames) * q_frames;
const double new_dur = (((*i)->time() != 0 && new_dur < (q_frames * 1.5))
? q_frames
: lrint((*i)->duration() / q_frames) * q_frames);
double new_dur = lrint((*i)->duration() / q_frames) * q_frames;
if (new_dur == 0.0)
new_dur = q_frames;
(*i)->set_time(new_time);
(*i)->set_duration(new_dur);

View File

@ -99,7 +99,7 @@ Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length
}
/** Basic Region constructor (many sources) */
Region::Region (SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
Region::Region (const SourceList& srcs, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
: Automatable(srcs.front()->session(), name)
, _type(type)
, _flags(flags)
@ -117,13 +117,13 @@ Region::Region (SourceList& srcs, nframes_t start, nframes_t length, const strin
set<boost::shared_ptr<Source> > unique_srcs;
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);
(*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
unique_srcs.insert (*i);
}
for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) {
_master_sources.push_back (*i);
if (unique_srcs.find (*i) == unique_srcs.end()) {
(*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
@ -222,7 +222,7 @@ Region::Region (boost::shared_ptr<const Region> other)
assert(_sources.size() > 0);
}
Region::Region (SourceList& srcs, const XMLNode& node)
Region::Region (const SourceList& srcs, const XMLNode& node)
: Automatable(srcs.front()->session(), X_("error: XML did not reset this"))
, _type(DataType::NIL) // to be loaded from XML
, _flags(Flag(0))
@ -239,13 +239,13 @@ Region::Region (SourceList& srcs, const XMLNode& node)
{
set<boost::shared_ptr<Source> > unique_srcs;
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);
(*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
unique_srcs.insert (*i);
}
for (SourceList::iterator i = srcs.begin(); i != srcs.end(); ++i) {
for (SourceList::const_iterator i = srcs.begin(); i != srcs.end(); ++i) {
_master_sources.push_back (*i);
if (unique_srcs.find (*i) == unique_srcs.end()) {
(*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
@ -974,9 +974,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), "%lu", _ancestral_start);
snprintf (buf, sizeof (buf), "%Ld", _ancestral_start);
node->add_property ("ancestral-start", buf);
snprintf (buf, sizeof (buf), "%lu", _ancestral_length);
snprintf (buf, sizeof (buf), "%Ld", _ancestral_length);
node->add_property ("ancestral-length", buf);
snprintf (buf, sizeof (buf), "%.12g", _stretch);
node->add_property ("stretch", buf);

View File

@ -106,7 +106,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)
{
if (srcs.empty()) {
return boost::shared_ptr<Region>();

View File

@ -38,7 +38,7 @@ ResampledImportableSource::ResampledImportableSource (const std::string& path,
/* Initialize the sample rate converter. */
int src_type;
int src_type = SRC_LINEAR;
switch (srcq) {
case SrcBest:

View File

@ -33,6 +33,7 @@
#include <glibmm/thread.h>
#include <glibmm/miscutils.h>
#include <glibmm/fileutils.h>
#include <pbd/error.h>
#include <glibmm/thread.h>
@ -148,7 +149,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

@ -432,19 +432,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 = session_directory().peak_path().to_string();
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;
}
@ -465,17 +460,39 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia
dir = session_directory().dead_sound_path().to_string();
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 = session_directory().export_path().to_string();
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 */
@ -524,7 +541,6 @@ Session::create (bool& new_session, const string& mix_template, nframes_t initia
_state_of_the_state = Clean;
save_state ("");
return 0;
@ -1979,6 +1995,14 @@ Session::automation_dir () const
return res;
}
string
Session::analysis_dir () const
{
string res = _path;
res += "analysis/";
return res;
}
int
Session::load_bundles (XMLNode const & node)
{
@ -2544,7 +2568,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

@ -40,7 +40,7 @@ using namespace PBD;
sigc::signal<void,boost::shared_ptr<Source> > SourceFactory::SourceCreated;
Glib::Cond* SourceFactory::PeaksToBuild;
Glib::StaticMutex SourceFactory::peak_building_lock;
Glib::StaticMutex SourceFactory::peak_building_lock = GLIBMM_STATIC_MUTEX_INIT;
std::list<boost::weak_ptr<AudioSource> > SourceFactory::files_with_peaks;
static void

View File

@ -268,7 +268,7 @@ TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
section.set_start (corrected);
metrics->sort (cmp);
timestamp_metrics ();
timestamp_metrics (true);
return 0;
}
@ -345,16 +345,22 @@ TempoMap::remove_meter (const MeterSection& tempo)
}
void
TempoMap::do_insert (MetricSection* section)
TempoMap::do_insert (MetricSection* section, bool with_bbt)
{
Metrics::iterator i;
for (i = metrics->begin(); i != metrics->end(); ++i) {
if ((*i)->start() < section->start()) {
continue;
if (with_bbt) {
if ((*i)->start() < section->start()) {
continue;
}
} else {
if ((*i)->frame() < section->frame()) {
continue;
}
}
metrics->insert (i, section);
break;
}
@ -363,7 +369,7 @@ TempoMap::do_insert (MetricSection* section)
metrics->insert (metrics->end(), section);
}
timestamp_metrics ();
timestamp_metrics (with_bbt);
}
void
@ -376,7 +382,18 @@ TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
where.ticks = 0;
do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()));
do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), true);
}
StateChanged (Change (0));
}
void
TempoMap::add_tempo (const Tempo& tempo, nframes_t where)
{
{
Glib::RWLock::WriterLock lm (lock);
do_insert (new TempoSection (where, tempo.beats_per_minute(), tempo.note_type()), false);
}
StateChanged (Change (0));
@ -399,7 +416,7 @@ TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
*((Tempo *) ts) = replacement;
replaced = true;
timestamp_metrics ();
timestamp_metrics (true);
break;
}
}
@ -432,7 +449,18 @@ TempoMap::add_meter (const Meter& meter, BBT_Time where)
where.ticks = 0;
do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()));
do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), true);
}
StateChanged (Change (0));
}
void
TempoMap::add_meter (const Meter& meter, nframes_t where)
{
{
Glib::RWLock::WriterLock lm (lock);
do_insert (new MeterSection (where, meter.beats_per_bar(), meter.note_divisor()), false);
}
StateChanged (Change (0));
@ -454,7 +482,7 @@ TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
*((Meter*) ms) = replacement;
replaced = true;
timestamp_metrics ();
timestamp_metrics (true);
break;
}
}
@ -465,6 +493,49 @@ TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
}
}
void
TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type)
{
Tempo newtempo (beats_per_minute, note_type);
TempoSection* prev;
TempoSection* first;
Metrics::iterator i;
/* find the TempoSection immediately preceding "where"
*/
for (first = 0, i = metrics->begin(), prev = 0; i != metrics->end(); ++i) {
if ((*i)->frame() > where) {
break;
}
TempoSection* t;
if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
if (!first) {
first = t;
}
prev = t;
}
}
if (!prev) {
if (!first) {
error << string_compose (_("no tempo sections defined in tempo map - cannot change tempo @ %1"), where) << endmsg;
return;
}
prev = first;
}
/* reset */
*((Tempo*)prev) = newtempo;
StateChanged (Change (0));
}
const MeterSection&
TempoMap::first_meter () const
{
@ -498,43 +569,84 @@ TempoMap::first_tempo () const
}
void
TempoMap::timestamp_metrics ()
TempoMap::timestamp_metrics (bool use_bbt)
{
Metrics::iterator i;
const Meter* meter;
const Tempo* tempo;
Meter *m;
Tempo *t;
nframes_t current;
nframes_t section_frames;
BBT_Time start;
BBT_Time end;
meter = &first_meter ();
tempo = &first_tempo ();
current = 0;
for (i = metrics->begin(); i != metrics->end(); ++i) {
end = (*i)->start();
if (use_bbt) {
section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
nframes_t current = 0;
nframes_t section_frames;
BBT_Time start;
BBT_Time end;
current += section_frames;
for (i = metrics->begin(); i != metrics->end(); ++i) {
end = (*i)->start();
section_frames = count_frames_between_metrics (*meter, *tempo, start, end);
current += section_frames;
start = end;
(*i)->set_frame (current);
if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
tempo = t;
} else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
meter = m;
} else {
fatal << _("programming error: unhandled MetricSection type") << endmsg;
/*NOTREACHED*/
}
}
start = end;
} else {
(*i)->set_frame (current);
bool first = true;
if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
tempo = t;
} else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
meter = m;
} else {
fatal << _("programming error: unhandled MetricSection type") << endmsg;
/*NOTREACHED*/
for (i = metrics->begin(); i != metrics->end(); ++i) {
BBT_Time bbt;
bbt_time_with_metric ((*i)->frame(), bbt, Metric (*meter, *tempo));
// cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
if (first) {
first = false;
} else {
if (bbt.beats != 1 || bbt.ticks != 0) {
bbt.bars += 1;
bbt.beats = 1;
bbt.ticks = 0;
}
}
// cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
(*i)->set_start (bbt);
if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
tempo = t;
} else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
meter = m;
} else {
fatal << _("programming error: unhandled MetricSection type") << endmsg;
/*NOTREACHED*/
}
}
}
// dump (cerr);
}
TempoMap::Metric
@ -666,17 +778,15 @@ TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& me
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 "legal" BBT types,
that means that the beats and ticks should be inside a bar
/* 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
*/
nframes_t frames = 0;
nframes_t start_frame = 0;
nframes_t end_frame = 0;
Metric m = metric_at(start);
Metric m = metric_at (start);
uint32_t bar_offset = start.bars - m.start().bars;
@ -939,67 +1049,6 @@ TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
}
return frame_time (the_beat);
/*****************************
XXX just keeping this for reference
TempoMap::BBTPointList::iterator i;
TempoMap::BBTPointList *more_zoomed_bbt_points;
nframes_t frame_one_beats_worth;
nframes_t pos = 0;
nframes_t next_pos = 0 ;
double tempo = 1;
double frames_one_subdivisions_worth;
bool fr_has_changed = false;
int n;
frame_one_beats_worth = (nframes_t) ::floor ((double) _frame_rate * 60 / 20 ); //one beat @ 20 bpm
{
Glib::RWLock::ReaderLock lm (lock);
more_zoomed_bbt_points = get_points((fr >= frame_one_beats_worth) ?
fr - frame_one_beats_worth : 0, fr+frame_one_beats_worth );
}
if (more_zoomed_bbt_points == 0 || more_zoomed_bbt_points->empty()) {
return fr;
}
for (i = more_zoomed_bbt_points->begin(); i != more_zoomed_bbt_points->end(); i++) {
if ((*i).frame <= fr) {
pos = (*i).frame;
tempo = (*i).tempo->beats_per_minute();
} else {
i++;
next_pos = (*i).frame;
break;
}
}
frames_one_subdivisions_worth = ((double) _frame_rate * 60 / (sub_num * tempo));
for (n = sub_num; n > 0; n--) {
if (fr >= (pos + ((n - 0.5) * frames_one_subdivisions_worth))) {
fr = (nframes_t) round(pos + (n * frames_one_subdivisions_worth));
if (fr > next_pos) {
fr = next_pos; //take care of fractional beats that don't match the subdivision asked
}
fr_has_changed = true;
break;
}
}
if (!fr_has_changed) {
fr = pos;
}
delete more_zoomed_bbt_points;
return fr ;
******************************/
}
nframes_t
@ -1051,6 +1100,12 @@ TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
}
/*
cerr << "for " << frame << " round to " << bbt << " using "
<< metric.start()
<< endl;
*/
return metric.frame() + count_frames_between (metric.start(), bbt);
}
@ -1148,6 +1203,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
if (beat == 1) {
if (current >= lower) {
// cerr << "Add Bar at " << bar << "|1" << " @ " << current << endl;
points->push_back (BBTPoint (*meter, *tempo,(nframes_t)rint(current), Bar, bar, 1));
}
@ -1159,6 +1215,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
while (beat <= ceil( beats_per_bar) && beat_frame < limit) {
if (beat_frame >= lower) {
// cerr << "Add Beat at " << bar << '|' << beat << " @ " << beat_frame << endl;
points->push_back (BBTPoint (*meter, *tempo, (nframes_t) rint(beat_frame), Beat, bar, beat));
}
beat_frame += beat_frames;
@ -1167,7 +1224,7 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
beat++;
}
if (beat > ceil(beats_per_bar) ) {
if (beat > ceil(beats_per_bar) || i != metrics->end()) {
/* we walked an entire bar. its
important to move `current' forward
@ -1185,10 +1242,15 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
so we subtract the possible extra fraction from the current
*/
current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
if (beat > ceil (beats_per_bar)) {
/* next bar goes where the numbers suggest */
current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
} else {
/* next bar goes where the next metric is */
current = limit;
}
bar++;
beat = 1;
}
}
@ -1225,6 +1287,33 @@ TempoMap::get_points (nframes_t lower, nframes_t upper) const
return points;
}
const TempoSection&
TempoMap::tempo_section_at (nframes_t frame)
{
Glib::RWLock::ReaderLock lm (lock);
Metrics::iterator i;
TempoSection* prev = 0;
for (i = metrics->begin(); i != metrics->end(); ++i) {
TempoSection* t;
if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
if ((*i)->frame() > frame) {
break;
}
prev = t;
}
}
if (prev == 0) {
fatal << endmsg;
}
return *prev;
}
const Tempo&
TempoMap::tempo_at (nframes_t frame)
{
@ -1303,7 +1392,7 @@ TempoMap::set_state (const XMLNode& node)
MetricSectionSorter cmp;
metrics->sort (cmp);
timestamp_metrics ();
timestamp_metrics (true);
}
}

View File

@ -0,0 +1,61 @@
#include <ardour/transient_detector.h>
#include "i18n.h"
using namespace Vamp;
using namespace ARDOUR;
using namespace std;
TransientDetector::TransientDetector (float sr)
: AudioAnalyser (sr, X_("libardourvampplugins:percussiononsets"))
{
}
TransientDetector::~TransientDetector()
{
}
int
TransientDetector::run (const std::string& path, 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, (nframes_t) floor(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);
}
}

View File

@ -11,6 +11,9 @@ gtkmm2 = env.Copy()
gtkmm2.Merge([libraries['glibmm2'], libraries['gtk2'], libraries['sigc2'], libraries['pangomm'], libraries['atkmm'], libraries['gdkmm2'], libraries['cairomm'], libraries['gtk2-unix-print'] ])
gtkmm2.Append(CXXFLAGS = ['-DGLIBMM_DEFAULT_SIGNAL_HANDLERS_ENABLED', '-DGLIBMM_PROPERTIES_ENABLED', '-DGLIBMM_EXCEPTIONS_ENABLED'])
if gtkmm2['IS_OSX']:
gtkmm2.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
libgtkmm2 = gtkmm2.SharedLibrary('gtkmm2', gtkmm2_files)
Default(libgtkmm2)

View File

@ -20,6 +20,9 @@ mackie.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
mackie.Append(PACKAGE = domain)
mackie.Append(POTFILE = domain + '.pot')
if mackie['IS_OSX']:
mackie.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
mackie_files=Split("""
interface.cc
midi_byte_array.cc

View File

@ -0,0 +1,247 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
This file copyright 2006 Dan Stowell.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#include "AmplitudeFollower.h"
#include <cmath>
#include <string>
#include <vector>
#include <iostream>
using std::string;
using std::vector;
using std::cerr;
using std::endl;
/**
* An implementation of SuperCollider's amplitude-follower algorithm
* as a simple Vamp plugin.
*/
AmplitudeFollower::AmplitudeFollower(float inputSampleRate) :
Plugin(inputSampleRate),
m_stepSize(0),
m_previn(0.0f),
m_clampcoef(0.01f),
m_relaxcoef(0.01f)
{
}
AmplitudeFollower::~AmplitudeFollower()
{
}
string
AmplitudeFollower::getIdentifier() const
{
return "amplitudefollower";
}
string
AmplitudeFollower::getName() const
{
return "Amplitude Follower";
}
string
AmplitudeFollower::getDescription() const
{
return "Track the amplitude of the audio signal";
}
string
AmplitudeFollower::getMaker() const
{
return "Vamp SDK Example Plugins";
}
int
AmplitudeFollower::getPluginVersion() const
{
return 1;
}
string
AmplitudeFollower::getCopyright() const
{
return "Code copyright 2006 Dan Stowell; method from SuperCollider. Freely redistributable (BSD license)";
}
bool
AmplitudeFollower::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (channels < getMinChannelCount() ||
channels > getMaxChannelCount()) return false;
m_stepSize = std::min(stepSize, blockSize);
// Translate the coefficients
// from their "convenient" 60dB convergence-time values
// to real coefficients
m_clampcoef = m_clampcoef==0.0 ? 0.0 : exp(log(0.1)/(m_clampcoef * m_inputSampleRate));
m_relaxcoef = m_relaxcoef==0.0 ? 0.0 : exp(log(0.1)/(m_relaxcoef * m_inputSampleRate));
return true;
}
void
AmplitudeFollower::reset()
{
m_previn = 0.0f;
}
AmplitudeFollower::OutputList
AmplitudeFollower::getOutputDescriptors() const
{
OutputList list;
OutputDescriptor sca;
sca.identifier = "amplitude";
sca.name = "Amplitude";
sca.description = "";
sca.unit = "V";
sca.hasFixedBinCount = true;
sca.binCount = 1;
sca.hasKnownExtents = false;
sca.isQuantized = false;
sca.sampleType = OutputDescriptor::OneSamplePerStep;
list.push_back(sca);
return list;
}
AmplitudeFollower::ParameterList
AmplitudeFollower::getParameterDescriptors() const
{
ParameterList list;
ParameterDescriptor att;
att.identifier = "attack";
att.name = "Attack time";
att.description = "";
att.unit = "s";
att.minValue = 0.0f;
att.maxValue = 1.f;
att.defaultValue = 0.01f;
att.isQuantized = false;
list.push_back(att);
ParameterDescriptor dec;
dec.identifier = "release";
dec.name = "Release time";
dec.description = "";
dec.unit = "s";
dec.minValue = 0.0f;
dec.maxValue = 1.f;
dec.defaultValue = 0.01f;
dec.isQuantized = false;
list.push_back(dec);
return list;
}
void AmplitudeFollower::setParameter(std::string paramid, float newval)
{
if (paramid == "attack") {
m_clampcoef = newval;
} else if (paramid == "release") {
m_relaxcoef = newval;
}
}
float AmplitudeFollower::getParameter(std::string paramid) const
{
if (paramid == "attack") {
return m_clampcoef;
} else if (paramid == "release") {
return m_relaxcoef;
}
return 0.0f;
}
AmplitudeFollower::FeatureSet
AmplitudeFollower::process(const float *const *inputBuffers,
Vamp::RealTime timestamp)
{
if (m_stepSize == 0) {
cerr << "ERROR: AmplitudeFollower::process: "
<< "AmplitudeFollower has not been initialised"
<< endl;
return FeatureSet();
}
float previn = m_previn;
FeatureSet returnFeatures;
float val;
float peak = 0.0f;
for (size_t i = 0; i < m_stepSize; ++i) {
val = fabs(inputBuffers[0][i]);
if (val < previn) {
val = val + (previn - val) * m_relaxcoef;
} else {
val = val + (previn - val) * m_clampcoef;
}
if (val > peak) peak = val;
previn = val;
}
m_previn = previn;
// Now store the "feature" (peak amp) for this sample
Feature feature;
feature.hasTimestamp = false;
feature.values.push_back(peak);
returnFeatures[0].push_back(feature);
return returnFeatures;
}
AmplitudeFollower::FeatureSet
AmplitudeFollower::getRemainingFeatures()
{
return FeatureSet();
}

View File

@ -0,0 +1,84 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
This file copyright 2006 Dan Stowell.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#ifndef _AMPLITUDE_FOLLOWER_PLUGIN_H_
#define _AMPLITUDE_FOLLOWER_PLUGIN_H_
#include "vamp-sdk/Plugin.h"
/**
* Example plugin implementing the SuperCollider amplitude follower
* function.
*/
class AmplitudeFollower : public Vamp::Plugin
{
public:
AmplitudeFollower(float inputSampleRate);
virtual ~AmplitudeFollower();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
InputDomain getInputDomain() const { return TimeDomain; }
std::string getIdentifier() const;
std::string getName() const;
std::string getDescription() const;
std::string getMaker() const;
int getPluginVersion() const;
std::string getCopyright() const;
OutputList getOutputDescriptors() const;
ParameterList getParameterDescriptors() const;
float getParameter(std::string paramid) const;
void setParameter(std::string paramid, float newval);
FeatureSet process(const float *const *inputBuffers,
Vamp::RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
size_t m_stepSize;
float m_previn;
float m_clampcoef;
float m_relaxcoef;
};
#endif

View File

@ -0,0 +1,285 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#include "PercussionOnsetDetector.h"
using std::string;
using std::vector;
using std::cerr;
using std::endl;
#include <cmath>
PercussionOnsetDetector::PercussionOnsetDetector(float inputSampleRate) :
Plugin(inputSampleRate),
m_stepSize(0),
m_blockSize(0),
m_threshold(3),
m_sensitivity(40),
m_priorMagnitudes(0),
m_dfMinus1(0),
m_dfMinus2(0)
{
}
PercussionOnsetDetector::~PercussionOnsetDetector()
{
delete[] m_priorMagnitudes;
}
string
PercussionOnsetDetector::getIdentifier() const
{
return "percussiononsets";
}
string
PercussionOnsetDetector::getName() const
{
return "Simple Percussion Onset Detector";
}
string
PercussionOnsetDetector::getDescription() const
{
return "Detect percussive note onsets by identifying broadband energy rises";
}
string
PercussionOnsetDetector::getMaker() const
{
return "Vamp SDK Example Plugins";
}
int
PercussionOnsetDetector::getPluginVersion() const
{
return 2;
}
string
PercussionOnsetDetector::getCopyright() const
{
return "Code copyright 2006 Queen Mary, University of London, after Dan Barry et al 2005. Freely redistributable (BSD license)";
}
size_t
PercussionOnsetDetector::getPreferredStepSize() const
{
return 0;
}
size_t
PercussionOnsetDetector::getPreferredBlockSize() const
{
return 1024;
}
bool
PercussionOnsetDetector::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (channels < getMinChannelCount() ||
channels > getMaxChannelCount()) return false;
m_stepSize = stepSize;
m_blockSize = blockSize;
m_priorMagnitudes = new float[m_blockSize/2];
for (size_t i = 0; i < m_blockSize/2; ++i) {
m_priorMagnitudes[i] = 0.f;
}
m_dfMinus1 = 0.f;
m_dfMinus2 = 0.f;
return true;
}
void
PercussionOnsetDetector::reset()
{
for (size_t i = 0; i < m_blockSize/2; ++i) {
m_priorMagnitudes[i] = 0.f;
}
m_dfMinus1 = 0.f;
m_dfMinus2 = 0.f;
}
PercussionOnsetDetector::ParameterList
PercussionOnsetDetector::getParameterDescriptors() const
{
ParameterList list;
ParameterDescriptor d;
d.identifier = "threshold";
d.name = "Energy rise threshold";
d.description = "Energy rise within a frequency bin necessary to count toward broadband total";
d.unit = "dB";
d.minValue = 0;
d.maxValue = 20;
d.defaultValue = 3;
d.isQuantized = false;
list.push_back(d);
d.identifier = "sensitivity";
d.name = "Sensitivity";
d.description = "Sensitivity of peak detector applied to broadband detection function";
d.unit = "%";
d.minValue = 0;
d.maxValue = 100;
d.defaultValue = 40;
d.isQuantized = false;
list.push_back(d);
return list;
}
float
PercussionOnsetDetector::getParameter(std::string id) const
{
if (id == "threshold") return m_threshold;
if (id == "sensitivity") return m_sensitivity;
return 0.f;
}
void
PercussionOnsetDetector::setParameter(std::string id, float value)
{
if (id == "threshold") {
if (value < 0) value = 0;
if (value > 20) value = 20;
m_threshold = value;
} else if (id == "sensitivity") {
if (value < 0) value = 0;
if (value > 100) value = 100;
m_sensitivity = value;
}
}
PercussionOnsetDetector::OutputList
PercussionOnsetDetector::getOutputDescriptors() const
{
OutputList list;
OutputDescriptor d;
d.identifier = "onsets";
d.name = "Onsets";
d.description = "Percussive note onset locations";
d.unit = "";
d.hasFixedBinCount = true;
d.binCount = 0;
d.hasKnownExtents = false;
d.isQuantized = false;
d.sampleType = OutputDescriptor::VariableSampleRate;
d.sampleRate = m_inputSampleRate;
list.push_back(d);
d.identifier = "detectionfunction";
d.name = "Detection Function";
d.description = "Broadband energy rise detection function";
d.binCount = 1;
d.isQuantized = true;
d.quantizeStep = 1.0;
d.sampleType = OutputDescriptor::OneSamplePerStep;
list.push_back(d);
return list;
}
PercussionOnsetDetector::FeatureSet
PercussionOnsetDetector::process(const float *const *inputBuffers,
Vamp::RealTime ts)
{
if (m_stepSize == 0) {
cerr << "ERROR: PercussionOnsetDetector::process: "
<< "PercussionOnsetDetector has not been initialised"
<< endl;
return FeatureSet();
}
int count = 0;
for (size_t i = 1; i < m_blockSize/2; ++i) {
float real = inputBuffers[0][i*2];
float imag = inputBuffers[0][i*2 + 1];
float sqrmag = real * real + imag * imag;
if (m_priorMagnitudes[i] > 0.f) {
float diff = 10.f * log10f(sqrmag / m_priorMagnitudes[i]);
// std::cout << "i=" << i << ", mag=" << mag << ", prior=" << m_priorMagnitudes[i] << ", diff=" << diff << ", threshold=" << m_threshold << std::endl;
if (diff >= m_threshold) ++count;
}
m_priorMagnitudes[i] = sqrmag;
}
FeatureSet returnFeatures;
Feature detectionFunction;
detectionFunction.hasTimestamp = false;
detectionFunction.values.push_back(count);
returnFeatures[1].push_back(detectionFunction);
if (m_dfMinus2 < m_dfMinus1 &&
m_dfMinus1 >= count &&
m_dfMinus1 > ((100 - m_sensitivity) * m_blockSize) / 200) {
Feature onset;
onset.hasTimestamp = true;
onset.timestamp = ts - Vamp::RealTime::frame2RealTime
(m_stepSize, lrintf(m_inputSampleRate));
returnFeatures[0].push_back(onset);
}
m_dfMinus2 = m_dfMinus1;
m_dfMinus1 = count;
return returnFeatures;
}
PercussionOnsetDetector::FeatureSet
PercussionOnsetDetector::getRemainingFeatures()
{
return FeatureSet();
}

View File

@ -0,0 +1,90 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#ifndef _PERCUSSION_ONSET_DETECTOR_PLUGIN_H_
#define _PERCUSSION_ONSET_DETECTOR_PLUGIN_H_
#include "vamp-sdk/Plugin.h"
/**
* Example plugin that detects percussive events.
*/
class PercussionOnsetDetector : public Vamp::Plugin
{
public:
PercussionOnsetDetector(float inputSampleRate);
virtual ~PercussionOnsetDetector();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
InputDomain getInputDomain() const { return FrequencyDomain; }
std::string getIdentifier() const;
std::string getName() const;
std::string getDescription() const;
std::string getMaker() const;
int getPluginVersion() const;
std::string getCopyright() const;
size_t getPreferredStepSize() const;
size_t getPreferredBlockSize() const;
ParameterList getParameterDescriptors() const;
float getParameter(std::string id) const;
void setParameter(std::string id, float value);
OutputList getOutputDescriptors() const;
FeatureSet process(const float *const *inputBuffers,
Vamp::RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
size_t m_stepSize;
size_t m_blockSize;
float m_threshold;
float m_sensitivity;
float *m_priorMagnitudes;
float m_dfMinus1;
float m_dfMinus2;
};
#endif

View File

@ -0,0 +1,26 @@
# -*- python -*-
import os
import os.path
import glob
plugin_files = glob.glob ("*.cpp")
Import('env install_prefix libraries')
vampplugs = env.Copy()
vampplugs.Append (CPPATH='#libs/vamp-sdk/vamp', CXXFLAGS="-Ilibs/vamp-sdk")
vampplugs.Merge ([libraries['vamp'],
libraries['vamphost']
])
libvampplugins = vampplugs.SharedLibrary('ardourvampplugins', plugin_files)
Default(libvampplugins)
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2', 'vamp'), libvampplugins))
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript', 'COPYING', 'README' ] +
plugin_files +
glob.glob('*.h')))

View File

@ -0,0 +1,188 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#include "SpectralCentroid.h"
using std::string;
using std::vector;
using std::cerr;
using std::endl;
#include <cmath>
SpectralCentroid::SpectralCentroid(float inputSampleRate) :
Plugin(inputSampleRate),
m_stepSize(0),
m_blockSize(0)
{
}
SpectralCentroid::~SpectralCentroid()
{
}
string
SpectralCentroid::getIdentifier() const
{
return "spectralcentroid";
}
string
SpectralCentroid::getName() const
{
return "Spectral Centroid";
}
string
SpectralCentroid::getDescription() const
{
return "Calculate the centroid frequency of the spectrum of the input signal";
}
string
SpectralCentroid::getMaker() const
{
return "Vamp SDK Example Plugins";
}
int
SpectralCentroid::getPluginVersion() const
{
return 2;
}
string
SpectralCentroid::getCopyright() const
{
return "Freely redistributable (BSD license)";
}
bool
SpectralCentroid::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (channels < getMinChannelCount() ||
channels > getMaxChannelCount()) return false;
m_stepSize = stepSize;
m_blockSize = blockSize;
return true;
}
void
SpectralCentroid::reset()
{
}
SpectralCentroid::OutputList
SpectralCentroid::getOutputDescriptors() const
{
OutputList list;
OutputDescriptor d;
d.identifier = "logcentroid";
d.name = "Log Frequency Centroid";
d.description = "Centroid of the log weighted frequency spectrum";
d.unit = "Hz";
d.hasFixedBinCount = true;
d.binCount = 1;
d.hasKnownExtents = false;
d.isQuantized = false;
d.sampleType = OutputDescriptor::OneSamplePerStep;
list.push_back(d);
d.identifier = "linearcentroid";
d.name = "Linear Frequency Centroid";
d.description = "Centroid of the linear frequency spectrum";
list.push_back(d);
return list;
}
SpectralCentroid::FeatureSet
SpectralCentroid::process(const float *const *inputBuffers, Vamp::RealTime)
{
if (m_stepSize == 0) {
cerr << "ERROR: SpectralCentroid::process: "
<< "SpectralCentroid has not been initialised"
<< endl;
return FeatureSet();
}
double numLin = 0.0, numLog = 0.0, denom = 0.0;
for (size_t i = 1; i <= m_blockSize/2; ++i) {
double freq = (double(i) * m_inputSampleRate) / m_blockSize;
double real = inputBuffers[0][i*2];
double imag = inputBuffers[0][i*2 + 1];
double power = sqrt(real * real + imag * imag) / (m_blockSize/2);
numLin += freq * power;
numLog += log10f(freq) * power;
denom += power;
}
FeatureSet returnFeatures;
// std::cerr << "power " << denom << ", block size " << m_blockSize << std::endl;
if (denom != 0.0) {
float centroidLin = float(numLin / denom);
float centroidLog = powf(10, float(numLog / denom));
Feature feature;
feature.hasTimestamp = false;
if (!std::isnan(centroidLog) && !std::isinf(centroidLog)) {
feature.values.push_back(centroidLog);
}
returnFeatures[0].push_back(feature);
feature.values.clear();
if (!std::isnan(centroidLin) && !std::isinf(centroidLin)) {
feature.values.push_back(centroidLin);
}
returnFeatures[1].push_back(feature);
}
return returnFeatures;
}
SpectralCentroid::FeatureSet
SpectralCentroid::getRemainingFeatures()
{
return FeatureSet();
}

View File

@ -0,0 +1,78 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#ifndef _SPECTRAL_CENTROID_PLUGIN_H_
#define _SPECTRAL_CENTROID_PLUGIN_H_
#include "vamp-sdk/Plugin.h"
/**
* Example plugin that calculates the centre of gravity of the
* frequency domain representation of each block of audio.
*/
class SpectralCentroid : public Vamp::Plugin
{
public:
SpectralCentroid(float inputSampleRate);
virtual ~SpectralCentroid();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
InputDomain getInputDomain() const { return FrequencyDomain; }
std::string getIdentifier() const;
std::string getName() const;
std::string getDescription() const;
std::string getMaker() const;
int getPluginVersion() const;
std::string getCopyright() const;
OutputList getOutputDescriptors() const;
FeatureSet process(const float *const *inputBuffers,
Vamp::RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
size_t m_stepSize;
size_t m_blockSize;
};
#endif

View File

@ -0,0 +1,194 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#include "ZeroCrossing.h"
using std::string;
using std::vector;
using std::cerr;
using std::endl;
ZeroCrossing::ZeroCrossing(float inputSampleRate) :
Plugin(inputSampleRate),
m_stepSize(0),
m_previousSample(0.0f)
{
}
ZeroCrossing::~ZeroCrossing()
{
}
string
ZeroCrossing::getIdentifier() const
{
return "zerocrossing";
}
string
ZeroCrossing::getName() const
{
return "Zero Crossings";
}
string
ZeroCrossing::getDescription() const
{
return "Detect and count zero crossing points";
}
string
ZeroCrossing::getMaker() const
{
return "Vamp SDK Example Plugins";
}
int
ZeroCrossing::getPluginVersion() const
{
return 2;
}
string
ZeroCrossing::getCopyright() const
{
return "Freely redistributable (BSD license)";
}
bool
ZeroCrossing::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (channels < getMinChannelCount() ||
channels > getMaxChannelCount()) return false;
m_stepSize = std::min(stepSize, blockSize);
return true;
}
void
ZeroCrossing::reset()
{
m_previousSample = 0.0f;
}
ZeroCrossing::OutputList
ZeroCrossing::getOutputDescriptors() const
{
OutputList list;
OutputDescriptor zc;
zc.identifier = "counts";
zc.name = "Zero Crossing Counts";
zc.description = "The number of zero crossing points per processing block";
zc.unit = "crossings";
zc.hasFixedBinCount = true;
zc.binCount = 1;
zc.hasKnownExtents = false;
zc.isQuantized = true;
zc.quantizeStep = 1.0;
zc.sampleType = OutputDescriptor::OneSamplePerStep;
list.push_back(zc);
zc.identifier = "zerocrossings";
zc.name = "Zero Crossings";
zc.description = "The locations of zero crossing points";
zc.unit = "";
zc.hasFixedBinCount = true;
zc.binCount = 0;
zc.sampleType = OutputDescriptor::VariableSampleRate;
zc.sampleRate = m_inputSampleRate;
list.push_back(zc);
return list;
}
ZeroCrossing::FeatureSet
ZeroCrossing::process(const float *const *inputBuffers,
Vamp::RealTime timestamp)
{
if (m_stepSize == 0) {
cerr << "ERROR: ZeroCrossing::process: "
<< "ZeroCrossing has not been initialised"
<< endl;
return FeatureSet();
}
float prev = m_previousSample;
size_t count = 0;
FeatureSet returnFeatures;
for (size_t i = 0; i < m_stepSize; ++i) {
float sample = inputBuffers[0][i];
bool crossing = false;
if (sample <= 0.0) {
if (prev > 0.0) crossing = true;
} else if (sample > 0.0) {
if (prev <= 0.0) crossing = true;
}
if (crossing) {
++count;
Feature feature;
feature.hasTimestamp = true;
feature.timestamp = timestamp +
Vamp::RealTime::frame2RealTime(i, (size_t)m_inputSampleRate);
returnFeatures[1].push_back(feature);
}
prev = sample;
}
m_previousSample = prev;
Feature feature;
feature.hasTimestamp = false;
feature.values.push_back(count);
returnFeatures[0].push_back(feature);
return returnFeatures;
}
ZeroCrossing::FeatureSet
ZeroCrossing::getRemainingFeatures()
{
return FeatureSet();
}

View File

@ -0,0 +1,78 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#ifndef _ZERO_CROSSING_PLUGIN_H_
#define _ZERO_CROSSING_PLUGIN_H_
#include "vamp-sdk/Plugin.h"
/**
* Example plugin that calculates the positions and density of
* zero-crossing points in an audio waveform.
*/
class ZeroCrossing : public Vamp::Plugin
{
public:
ZeroCrossing(float inputSampleRate);
virtual ~ZeroCrossing();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
void reset();
InputDomain getInputDomain() const { return TimeDomain; }
std::string getIdentifier() const;
std::string getName() const;
std::string getDescription() const;
std::string getMaker() const;
int getPluginVersion() const;
std::string getCopyright() const;
OutputList getOutputDescriptors() const;
FeatureSet process(const float *const *inputBuffers,
Vamp::RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
size_t m_stepSize;
float m_previousSample;
};
#endif

View File

@ -0,0 +1,63 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006 Chris Cannam.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#include "vamp/vamp.h"
#include "vamp-sdk/PluginAdapter.h"
#include "ZeroCrossing.h"
#include "SpectralCentroid.h"
#include "PercussionOnsetDetector.h"
#include "AmplitudeFollower.h"
static Vamp::PluginAdapter<ZeroCrossing> zeroCrossingAdapter;
static Vamp::PluginAdapter<SpectralCentroid> spectralCentroidAdapter;
static Vamp::PluginAdapter<PercussionOnsetDetector> percussionOnsetAdapter;
static Vamp::PluginAdapter<AmplitudeFollower> amplitudeAdapter;
const VampPluginDescriptor *vampGetPluginDescriptor(unsigned int version,
unsigned int index)
{
if (version < 1) return 0;
switch (index) {
case 0: return zeroCrossingAdapter.getDescriptor();
case 1: return spectralCentroidAdapter.getDescriptor();
case 2: return percussionOnsetAdapter.getDescriptor();
case 3: return amplitudeAdapter.getDescriptor();
default: return 0;
}
}

View File

@ -11,6 +11,7 @@ vamp-sdk/RealTime.cpp
vamphostsdk_files = Split ("""
vamp-sdk/PluginHostAdapter.cpp
vamp-sdk/hostext/PluginBufferingAdapter.cpp
vamp-sdk/hostext/PluginChannelAdapter.cpp
vamp-sdk/hostext/PluginInputDomainAdapter.cpp
vamp-sdk/hostext/PluginLoader.cpp
@ -21,7 +22,11 @@ vamp-sdk/RealTime.cpp
Import('env install_prefix libraries')
vampsdk = env.Copy()
vampsdk.Append (CPPATH='#libs/vamp-sdk/vamp', CXXFLAGS="-Ilibs/vamp-sdk")
vampsdk.Merge ([libraries['fftw3'], libraries['fftw3f']])
# HAVE_FFTW3 is used to help improve some performance aspects of VAMP's InputDomainAdapter
vampsdk.Append (CPPATH='#libs/vamp-sdk/vamp', CXXFLAGS="-Ilibs/vamp-sdk -DHAVE_FFTW3")
libvampsdk = vampsdk.SharedLibrary('vampsdk', vampsdk_files)
libvamphostsdk = vampsdk.SharedLibrary('vamphostsdk', vamphostsdk_files)
@ -30,6 +35,8 @@ Default(libvampsdk)
Default(libvamphostsdk)
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libvampsdk))
env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour2'), libvamphostsdk))
env.Alias('tarball', env.Distribute (env['DISTTREE'],
[ 'SConscript', 'COPYING', 'README' ] +
vampsdk_files +

View File

@ -125,23 +125,31 @@ struct RealTime
RealTime operator/(int d) const;
// Find the fractional difference between times
//
/**
* Return the ratio of two times.
*/
double operator/(const RealTime &r) const;
// Return a human-readable debug-type string to full precision
// (probably not a format to show to a user directly)
//
/**
* Return a human-readable debug-type string to full precision
* (probably not a format to show to a user directly)
*/
std::string toString() const;
// Return a user-readable string to the nearest millisecond
// in a form like HH:MM:SS.mmm
//
/**
* Return a user-readable string to the nearest millisecond
* in a form like HH:MM:SS.mmm
*/
std::string toText(bool fixedDp = false) const;
// Convenience functions for handling sample frames
//
/**
* Convert a RealTime into a sample frame at the given sample rate.
*/
static long realTime2Frame(const RealTime &r, unsigned int sampleRate);
/**
* Convert a sample frame at the given sample rate into a RealTime.
*/
static RealTime frame2RealTime(long frame, unsigned int sampleRate);
static const RealTime zeroTime;

View File

@ -0,0 +1,490 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006-2007 Chris Cannam and QMUL.
This file by Mark Levy and Chris Cannam.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#include <vector>
#include <map>
#include "PluginBufferingAdapter.h"
using std::vector;
using std::map;
namespace Vamp {
namespace HostExt {
class PluginBufferingAdapter::Impl
{
public:
Impl(Plugin *plugin, float inputSampleRate);
~Impl();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
OutputList getOutputDescriptors() const;
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
class RingBuffer
{
public:
RingBuffer(int n) :
m_buffer(new float[n+1]), m_writer(0), m_reader(0), m_size(n+1) { }
virtual ~RingBuffer() { delete[] m_buffer; }
int getSize() const { return m_size-1; }
void reset() { m_writer = 0; m_reader = 0; }
int getReadSpace() const {
int writer = m_writer, reader = m_reader, space;
if (writer > reader) space = writer - reader;
else if (writer < reader) space = (writer + m_size) - reader;
else space = 0;
return space;
}
int getWriteSpace() const {
int writer = m_writer;
int reader = m_reader;
int space = (reader + m_size - writer - 1);
if (space >= m_size) space -= m_size;
return space;
}
int peek(float *destination, int n) const {
int available = getReadSpace();
if (n > available) {
for (int i = available; i < n; ++i) {
destination[i] = 0.f;
}
n = available;
}
if (n == 0) return n;
int reader = m_reader;
int here = m_size - reader;
const float *const bufbase = m_buffer + reader;
if (here >= n) {
for (int i = 0; i < n; ++i) {
destination[i] = bufbase[i];
}
} else {
for (int i = 0; i < here; ++i) {
destination[i] = bufbase[i];
}
float *const destbase = destination + here;
const int nh = n - here;
for (int i = 0; i < nh; ++i) {
destbase[i] = m_buffer[i];
}
}
return n;
}
int skip(int n) {
int available = getReadSpace();
if (n > available) {
n = available;
}
if (n == 0) return n;
int reader = m_reader;
reader += n;
while (reader >= m_size) reader -= m_size;
m_reader = reader;
return n;
}
int write(const float *source, int n) {
int available = getWriteSpace();
if (n > available) {
n = available;
}
if (n == 0) return n;
int writer = m_writer;
int here = m_size - writer;
float *const bufbase = m_buffer + writer;
if (here >= n) {
for (int i = 0; i < n; ++i) {
bufbase[i] = source[i];
}
} else {
for (int i = 0; i < here; ++i) {
bufbase[i] = source[i];
}
const int nh = n - here;
const float *const srcbase = source + here;
float *const buf = m_buffer;
for (int i = 0; i < nh; ++i) {
buf[i] = srcbase[i];
}
}
writer += n;
while (writer >= m_size) writer -= m_size;
m_writer = writer;
return n;
}
int zero(int n) {
int available = getWriteSpace();
if (n > available) {
n = available;
}
if (n == 0) return n;
int writer = m_writer;
int here = m_size - writer;
float *const bufbase = m_buffer + writer;
if (here >= n) {
for (int i = 0; i < n; ++i) {
bufbase[i] = 0.f;
}
} else {
for (int i = 0; i < here; ++i) {
bufbase[i] = 0.f;
}
const int nh = n - here;
for (int i = 0; i < nh; ++i) {
m_buffer[i] = 0.f;
}
}
writer += n;
while (writer >= m_size) writer -= m_size;
m_writer = writer;
return n;
}
protected:
float *m_buffer;
int m_writer;
int m_reader;
int m_size;
private:
RingBuffer(const RingBuffer &); // not provided
RingBuffer &operator=(const RingBuffer &); // not provided
};
Plugin *m_plugin;
size_t m_inputStepSize;
size_t m_inputBlockSize;
size_t m_stepSize;
size_t m_blockSize;
size_t m_channels;
vector<RingBuffer *> m_queue;
float **m_buffers;
float m_inputSampleRate;
RealTime m_timestamp;
OutputList m_outputs;
void processBlock(FeatureSet& allFeatureSets, RealTime timestamp);
};
PluginBufferingAdapter::PluginBufferingAdapter(Plugin *plugin) :
PluginWrapper(plugin)
{
m_impl = new Impl(plugin, m_inputSampleRate);
}
PluginBufferingAdapter::~PluginBufferingAdapter()
{
delete m_impl;
}
bool
PluginBufferingAdapter::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
return m_impl->initialise(channels, stepSize, blockSize);
}
PluginBufferingAdapter::OutputList
PluginBufferingAdapter::getOutputDescriptors() const
{
return m_impl->getOutputDescriptors();
}
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::process(const float *const *inputBuffers,
RealTime timestamp)
{
return m_impl->process(inputBuffers, timestamp);
}
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::getRemainingFeatures()
{
return m_impl->getRemainingFeatures();
}
PluginBufferingAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
m_plugin(plugin),
m_inputStepSize(0),
m_inputBlockSize(0),
m_stepSize(0),
m_blockSize(0),
m_channels(0),
m_queue(0),
m_buffers(0),
m_inputSampleRate(inputSampleRate),
m_timestamp()
{
m_outputs = plugin->getOutputDescriptors();
}
PluginBufferingAdapter::Impl::~Impl()
{
// the adapter will delete the plugin
for (size_t i = 0; i < m_channels; ++i) {
delete m_queue[i];
delete[] m_buffers[i];
}
delete[] m_buffers;
}
size_t
PluginBufferingAdapter::getPreferredStepSize() const
{
return getPreferredBlockSize();
}
bool
PluginBufferingAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (stepSize != blockSize) {
std::cerr << "PluginBufferingAdapter::initialise: input stepSize must be equal to blockSize for this adapter (stepSize = " << stepSize << ", blockSize = " << blockSize << ")" << std::endl;
return false;
}
m_channels = channels;
m_inputStepSize = stepSize;
m_inputBlockSize = blockSize;
// use the step and block sizes which the plugin prefers
m_stepSize = m_plugin->getPreferredStepSize();
m_blockSize = m_plugin->getPreferredBlockSize();
// or sensible defaults if it has no preference
if (m_blockSize == 0) {
m_blockSize = 1024;
}
if (m_stepSize == 0) {
if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
m_stepSize = m_blockSize/2;
} else {
m_stepSize = m_blockSize;
}
} else if (m_stepSize > m_blockSize) {
if (m_plugin->getInputDomain() == Vamp::Plugin::FrequencyDomain) {
m_blockSize = m_stepSize * 2;
} else {
m_blockSize = m_stepSize;
}
}
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) {
std::cerr << "PluginBufferingAdapter::initialise: plugin's preferred stepSize greater than blockSize, giving up!" << std::endl;
return false;
}
m_buffers = new float *[m_channels];
for (size_t i = 0; i < m_channels; ++i) {
m_queue.push_back(new RingBuffer(m_blockSize + m_inputBlockSize));
m_buffers[i] = new float[m_blockSize];
}
return m_plugin->initialise(m_channels, m_stepSize, m_blockSize);
}
PluginBufferingAdapter::OutputList
PluginBufferingAdapter::Impl::getOutputDescriptors() const
{
OutputList outs = m_plugin->getOutputDescriptors();
for (size_t i = 0; i < outs.size(); ++i) {
if (outs[i].sampleType == OutputDescriptor::OneSamplePerStep) {
outs[i].sampleRate = 1.f / m_stepSize;
}
outs[i].sampleType = OutputDescriptor::VariableSampleRate;
}
return outs;
}
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::Impl::process(const float *const *inputBuffers,
RealTime timestamp)
{
FeatureSet allFeatureSets;
// queue the new input
for (size_t i = 0; i < m_channels; ++i) {
int written = m_queue[i]->write(inputBuffers[i], m_inputBlockSize);
if (written < int(m_inputBlockSize) && i == 0) {
std::cerr << "WARNING: PluginBufferingAdapter::Impl::process: "
<< "Buffer overflow: wrote " << written
<< " of " << m_inputBlockSize
<< " input samples (for plugin step size "
<< m_stepSize << ", block size " << m_blockSize << ")"
<< std::endl;
}
}
// process as much as we can
while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
processBlock(allFeatureSets, timestamp);
}
return allFeatureSets;
}
PluginBufferingAdapter::FeatureSet
PluginBufferingAdapter::Impl::getRemainingFeatures()
{
FeatureSet allFeatureSets;
// process remaining samples in queue
while (m_queue[0]->getReadSpace() >= int(m_blockSize)) {
processBlock(allFeatureSets, m_timestamp);
}
// pad any last samples remaining and process
if (m_queue[0]->getReadSpace() > 0) {
for (size_t i = 0; i < m_channels; ++i) {
m_queue[i]->zero(m_blockSize - m_queue[i]->getReadSpace());
}
processBlock(allFeatureSets, m_timestamp);
}
// get remaining features
FeatureSet featureSet = m_plugin->getRemainingFeatures();
for (map<int, FeatureList>::iterator iter = featureSet.begin();
iter != featureSet.end(); ++iter) {
FeatureList featureList = iter->second;
for (size_t i = 0; i < featureList.size(); ++i) {
allFeatureSets[iter->first].push_back(featureList[i]);
}
}
return allFeatureSets;
}
void
PluginBufferingAdapter::Impl::processBlock(FeatureSet& allFeatureSets,
RealTime timestamp)
{
for (size_t i = 0; i < m_channels; ++i) {
m_queue[i]->peek(m_buffers[i], m_blockSize);
}
FeatureSet featureSet = m_plugin->process(m_buffers, m_timestamp);
for (map<int, FeatureList>::iterator iter = featureSet.begin();
iter != featureSet.end(); ++iter) {
FeatureList featureList = iter->second;
int outputNo = iter->first;
for (size_t i = 0; i < featureList.size(); ++i) {
// make sure the timestamp is set
switch (m_outputs[outputNo].sampleType) {
case OutputDescriptor::OneSamplePerStep:
// use our internal timestamp - OK????
featureList[i].timestamp = m_timestamp;
break;
case OutputDescriptor::FixedSampleRate:
// use our internal timestamp
featureList[i].timestamp = m_timestamp;
break;
case OutputDescriptor::VariableSampleRate:
break; // plugin must set timestamp
default:
break;
}
allFeatureSets[outputNo].push_back(featureList[i]);
}
}
// step forward
for (size_t i = 0; i < m_channels; ++i) {
m_queue[i]->skip(m_stepSize);
}
// fake up the timestamp each time we step forward
long frame = RealTime::realTime2Frame(m_timestamp,
int(m_inputSampleRate + 0.5));
m_timestamp = RealTime::frame2RealTime(frame + m_stepSize,
int(m_inputSampleRate + 0.5));
}
}
}

View File

@ -0,0 +1,97 @@
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
Vamp
An API for audio analysis and feature extraction plugins.
Centre for Digital Music, Queen Mary, University of London.
Copyright 2006-2007 Chris Cannam and QMUL.
This file by Mark Levy, Copyright 2007 QMUL.
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
restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the names of the Centre for
Digital Music; Queen Mary, University of London; and Chris Cannam
shall not be used in advertising or otherwise to promote the sale,
use or other dealings in this Software without prior written
authorization.
*/
#ifndef _VAMP_PLUGIN_BUFFERING_ADAPTER_H_
#define _VAMP_PLUGIN_BUFFERING_ADAPTER_H_
#include "PluginWrapper.h"
namespace Vamp {
namespace HostExt {
/**
* \class PluginBufferingAdapter PluginBufferingAdapter.h <vamp-sdk/hostext/PluginBufferingAdapter.h>
*
* PluginBufferingAdapter is a Vamp plugin adapter that allows plugins
* to be used by a host supplying an audio stream in non-overlapping
* buffers of arbitrary size.
*
* A host using PluginBufferingAdapter may ignore the preferred step
* and block size reported by the plugin, and still expect the plugin
* to run. The value of blockSize and stepSize passed to initialise
* should be the size of the buffer which the host will supply; the
* stepSize should be equal to the blockSize.
*
* If the internal step size used for the plugin differs from that
* supplied by the host, the adapter will modify the sample rate
* specifications for the plugin outputs (setting them all to
* VariableSampleRate) and set timestamps on the output features for
* outputs that formerly used a different sample rate specification.
* This is necessary in order to obtain correct time stamping.
*
* In other respects, the PluginBufferingAdapter behaves identically
* to the plugin that it wraps. The wrapped plugin will be deleted
* when the wrapper is deleted.
*/
class PluginBufferingAdapter : public PluginWrapper
{
public:
PluginBufferingAdapter(Plugin *plugin); // I take ownership of plugin
virtual ~PluginBufferingAdapter();
bool initialise(size_t channels, size_t stepSize, size_t blockSize);
size_t getPreferredStepSize() const;
OutputList getOutputDescriptors() const;
FeatureSet process(const float *const *inputBuffers, RealTime timestamp);
FeatureSet getRemainingFeatures();
protected:
class Impl;
Impl *m_impl;
};
}
}
#endif

View File

@ -38,6 +38,28 @@
#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.
*
* 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).
*/
#ifdef HAVE_FFTW3
#include <fftw3.h>
#endif
namespace Vamp {
namespace HostExt {
@ -58,15 +80,22 @@ public:
protected:
Plugin *m_plugin;
float m_inputSampleRate;
size_t m_channels;
size_t m_blockSize;
int m_channels;
int m_blockSize;
float **m_freqbuf;
double *m_ri;
double *m_window;
#ifdef HAVE_FFTW3
fftw_plan m_plan;
fftw_complex *m_cbuf;
#else
double *m_ro;
double *m_io;
void fft(unsigned int n, bool inverse,
double *ri, double *ii, double *ro, double *io);
#endif
size_t makeBlockSizeAcceptable(size_t) const;
};
@ -112,12 +141,21 @@ PluginInputDomainAdapter::process(const float *const *inputBuffers, RealTime tim
return m_impl->process(inputBuffers, timestamp);
}
PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
PluginInputDomainAdapter::Impl::Impl(Plugin *plugin, float inputSampleRate) :
m_plugin(plugin),
m_inputSampleRate(inputSampleRate),
m_channels(0),
m_blockSize(0),
m_freqbuf(0)
m_freqbuf(0),
m_ri(0),
m_window(0),
#ifdef HAVE_FFTW3
m_plan(0),
m_cbuf(0)
#else
m_ro(0),
m_io(0)
#endif
{
}
@ -126,23 +164,38 @@ PluginInputDomainAdapter::Impl::~Impl()
// the adapter will delete the plugin
if (m_channels > 0) {
for (size_t c = 0; c < m_channels; ++c) {
for (int c = 0; c < m_channels; ++c) {
delete[] m_freqbuf[c];
}
delete[] m_freqbuf;
#ifdef HAVE_FFTW3
if (m_plan) {
fftw_destroy_plan(m_plan);
fftw_free(m_ri);
fftw_free(m_cbuf);
m_plan = 0;
}
#else
delete[] m_ri;
delete[] m_ro;
delete[] m_io;
#endif
delete[] m_window;
}
}
// for some visual studii apparently
#ifndef M_PI
#define M_PI 3.14159265358979232846
#endif
bool
PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, size_t blockSize)
{
if (m_plugin->getInputDomain() == TimeDomain) {
m_blockSize = blockSize;
m_channels = channels;
m_blockSize = int(blockSize);
m_channels = int(channels);
return m_plugin->initialise(channels, stepSize, blockSize);
}
@ -158,25 +211,48 @@ PluginInputDomainAdapter::Impl::initialise(size_t channels, size_t stepSize, siz
}
if (m_channels > 0) {
for (size_t c = 0; c < m_channels; ++c) {
for (int c = 0; c < m_channels; ++c) {
delete[] m_freqbuf[c];
}
delete[] m_freqbuf;
#ifdef HAVE_FFTW3
if (m_plan) {
fftw_destroy_plan(m_plan);
fftw_free(m_ri);
fftw_free(m_cbuf);
m_plan = 0;
}
#else
delete[] m_ri;
delete[] m_ro;
delete[] m_io;
#endif
delete[] m_window;
}
m_blockSize = blockSize;
m_channels = channels;
m_blockSize = int(blockSize);
m_channels = int(channels);
m_freqbuf = new float *[m_channels];
for (size_t c = 0; c < m_channels; ++c) {
for (int c = 0; c < m_channels; ++c) {
m_freqbuf[c] = new float[m_blockSize + 2];
}
m_window = new double[m_blockSize];
for (int i = 0; i < m_blockSize; ++i) {
// Hanning window
m_window[i] = (0.50 - 0.50 * cos((2.0 * M_PI * i) / m_blockSize));
}
#ifdef HAVE_FFTW3
m_ri = (double *)fftw_malloc(blockSize * sizeof(double));
m_cbuf = (fftw_complex *)fftw_malloc((blockSize/2 + 1) * sizeof(fftw_complex));
m_plan = fftw_plan_dft_r2c_1d(blockSize, m_ri, m_cbuf, FFTW_MEASURE);
#else
m_ri = new double[m_blockSize];
m_ro = new double[m_blockSize];
m_io = new double[m_blockSize];
#endif
return m_plugin->initialise(channels, stepSize, blockSize);
}
@ -220,7 +296,11 @@ PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
} else if (blockSize & (blockSize-1)) {
// not a power of two, can't handle that with our current fft
#ifdef HAVE_FFTW3
// not an issue with FFTW
#else
// not a power of two, can't handle that with our built-in FFT
// implementation
size_t nearest = blockSize;
@ -241,16 +321,13 @@ PluginInputDomainAdapter::Impl::makeBlockSizeAcceptable(size_t blockSize) const
std::cerr << "WARNING: Vamp::HostExt::PluginInputDomainAdapter::Impl::initialise: non-power-of-two\nblocksize " << blockSize << " not supported, using blocksize " << nearest << " instead" << std::endl;
blockSize = nearest;
#endif
}
return blockSize;
}
// for some visual studii apparently
#ifndef M_PI
#define M_PI 3.14159265358979232846
#endif
Plugin::FeatureSet
PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
RealTime timestamp)
@ -308,33 +385,45 @@ PluginInputDomainAdapter::Impl::process(const float *const *inputBuffers,
// std::cerr << " to " << timestamp << std::endl;
for (size_t c = 0; c < m_channels; ++c) {
for (int c = 0; c < m_channels; ++c) {
for (size_t i = 0; i < m_blockSize; ++i) {
// Hanning window
m_ri[i] = double(inputBuffers[c][i])
* (0.50 - 0.50 * cos((2 * M_PI * i)
/ m_blockSize));
for (int i = 0; i < m_blockSize; ++i) {
m_ri[i] = double(inputBuffers[c][i]) * m_window[i];
}
for (size_t i = 0; i < m_blockSize/2; ++i) {
for (int i = 0; i < m_blockSize/2; ++i) {
// FFT shift
double value = m_ri[i];
m_ri[i] = m_ri[i + m_blockSize/2];
m_ri[i + m_blockSize/2] = value;
}
#ifdef HAVE_FFTW3
fftw_execute(m_plan);
for (int i = 0; i <= m_blockSize/2; ++i) {
m_freqbuf[c][i * 2] = float(m_cbuf[i][0]);
m_freqbuf[c][i * 2 + 1] = float(m_cbuf[i][1]);
}
#else
fft(m_blockSize, false, m_ri, 0, m_ro, m_io);
for (size_t i = 0; i <= m_blockSize/2; ++i) {
m_freqbuf[c][i * 2] = m_ro[i];
m_freqbuf[c][i * 2 + 1] = m_io[i];
for (int i = 0; i <= m_blockSize/2; ++i) {
m_freqbuf[c][i * 2] = float(m_ro[i]);
m_freqbuf[c][i * 2 + 1] = float(m_io[i]);
}
#endif
}
return m_plugin->process(m_freqbuf, timestamp);
}
#ifndef HAVE_FFTW3
void
PluginInputDomainAdapter::Impl::fft(unsigned int n, bool inverse,
double *ri, double *ii, double *ro, double *io)
@ -452,6 +541,8 @@ PluginInputDomainAdapter::Impl::fft(unsigned int n, bool inverse,
}
}
#endif
}
}

View File

@ -38,6 +38,7 @@
#include "PluginLoader.h"
#include "PluginInputDomainAdapter.h"
#include "PluginChannelAdapter.h"
#include "PluginBufferingAdapter.h"
#include <fstream>
#include <cctype> // tolower
@ -85,6 +86,8 @@ public:
string getLibraryPathForPlugin(PluginKey key);
static void setInstanceToClean(PluginLoader *instance);
protected:
class PluginDeletionNotifyAdapter : public PluginWrapper {
public:
@ -94,6 +97,15 @@ protected:
Impl *m_loader;
};
class InstanceCleaner {
public:
InstanceCleaner() : m_instance(0) { }
~InstanceCleaner() { delete m_instance; }
void setInstance(PluginLoader *instance) { m_instance = instance; }
protected:
PluginLoader *m_instance;
};
virtual void pluginDeleted(PluginDeletionNotifyAdapter *adapter);
map<PluginKey, string> m_pluginLibraryNameMap;
@ -114,11 +126,16 @@ protected:
string splicePath(string a, string b);
vector<string> listFiles(string dir, string ext);
static InstanceCleaner m_cleaner;
};
PluginLoader *
PluginLoader::m_instance = 0;
PluginLoader::Impl::InstanceCleaner
PluginLoader::Impl::m_cleaner;
PluginLoader::PluginLoader()
{
m_impl = new Impl();
@ -132,7 +149,13 @@ PluginLoader::~PluginLoader()
PluginLoader *
PluginLoader::getInstance()
{
if (!m_instance) m_instance = new PluginLoader();
if (!m_instance) {
// The cleaner doesn't own the instance, because we leave the
// instance pointer in the base class for binary backwards
// compatibility reasons and to avoid waste
m_instance = new PluginLoader();
Impl::setInstanceToClean(m_instance);
}
return m_instance;
}
@ -177,6 +200,12 @@ PluginLoader::Impl::~Impl()
{
}
void
PluginLoader::Impl::setInstanceToClean(PluginLoader *instance)
{
m_cleaner.setInstance(instance);
}
vector<PluginLoader::PluginKey>
PluginLoader::Impl::listPlugins()
{
@ -366,6 +395,10 @@ PluginLoader::Impl::loadPlugin(PluginKey key,
}
}
if (adapterFlags & ADAPT_BUFFER_SIZE) {
adapter = new PluginBufferingAdapter(adapter);
}
if (adapterFlags & ADAPT_CHANNEL_COUNT) {
adapter = new PluginChannelAdapter(adapter);
}
@ -549,9 +582,11 @@ PluginLoader::Impl::listFiles(string dir, string extension)
struct dirent *e = 0;
while ((e = readdir(d))) {
if (!(e->d_type & DT_REG) && (e->d_type != DT_UNKNOWN)) continue;
if (!(e->d_type & DT_REG) || !e->d_name) continue;
if (!e->d_name) continue;
size_t len = strlen(e->d_name);
if (len < extlen + 2 ||
e->d_name + len - extlen - 1 != "." + extension) {

View File

@ -142,15 +142,35 @@ public:
* to be mixed down to mono, etc., without having to worry about
* doing that itself.
*
* ADAPT_ALL - Perform all available adaptations, where meaningful.
* ADAPT_BUFFER_SIZE - Wrap the plugin in a PluginBufferingAdapter
* permitting the host to provide audio input using any block
* size, with no overlap, regardless of the plugin's preferred
* block size (suitable for hosts that read from non-seekable
* streaming media, for example). This adapter introduces some
* run-time overhead and also changes the semantics of the plugin
* slightly (see the PluginBufferingAdapter header documentation
* for details).
*
* ADAPT_ALL_SAFE - Perform all available adaptations that are
* meaningful for the plugin and "safe". Currently this means to
* ADAPT_INPUT_DOMAIN if the plugin wants FrequencyDomain input;
* ADAPT_CHANNEL_COUNT always; and ADAPT_BUFFER_SIZE never.
*
* See PluginInputDomainAdapter and PluginChannelAdapter for more
* details of the classes that the loader may use if these flags
* are set.
* ADAPT_ALL - Perform all available adaptations that are
* meaningful for the plugin.
*
* See PluginInputDomainAdapter, PluginChannelAdapter and
* PluginBufferingAdapter for more details of the classes that the
* loader may use if these flags are set.
*/
enum AdapterFlags {
ADAPT_INPUT_DOMAIN = 0x01,
ADAPT_CHANNEL_COUNT = 0x02,
ADAPT_BUFFER_SIZE = 0x04,
ADAPT_ALL_SAFE = 0x03,
ADAPT_ALL = 0xff
};