summaryrefslogtreecommitdiff
path: root/libs/ardour
diff options
context:
space:
mode:
authorPaul Davis <paul@linuxaudiosystems.com>2008-06-02 21:41:35 +0000
committerPaul Davis <paul@linuxaudiosystems.com>2008-06-02 21:41:35 +0000
commit449aab3c465bbbf66d221fac3d7ea559f1720357 (patch)
tree6843cc40c88250a132acac701271f1504cd2df04 /libs/ardour
parent9c0d7d72d70082a54f823cd44c0ccda5da64bb6f (diff)
rollback to 3428, before the mysterious removal of libs/* at 3431/3432
git-svn-id: svn://localhost/ardour2/branches/3.0@3435 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/ardour')
-rw-r--r--libs/ardour/.cvsignore8
-rw-r--r--libs/ardour/ChangeLog118
-rw-r--r--libs/ardour/SConscript395
-rw-r--r--libs/ardour/amp.cc102
-rw-r--r--libs/ardour/analyser.cc119
-rw-r--r--libs/ardour/ardour/.cvsignore1
-rw-r--r--libs/ardour/ardour/amp.h44
-rw-r--r--libs/ardour/ardour/analyser.h35
-rw-r--r--libs/ardour/ardour/ardour.h87
-rw-r--r--libs/ardour/ardour/audio_buffer.h121
-rw-r--r--libs/ardour/ardour/audio_diskstream.h277
-rw-r--r--libs/ardour/ardour/audio_library.h54
-rw-r--r--libs/ardour/ardour/audio_port.h46
-rw-r--r--libs/ardour/ardour/audio_track.h80
-rw-r--r--libs/ardour/ardour/audio_unit.h172
-rw-r--r--libs/ardour/ardour/audioanalyser.h74
-rw-r--r--libs/ardour/ardour/audioengine.h276
-rw-r--r--libs/ardour/ardour/audiofilesource.h183
-rw-r--r--libs/ardour/ardour/audioplaylist.h93
-rw-r--r--libs/ardour/ardour/audioregion.h204
-rw-r--r--libs/ardour/ardour/audiosource.h159
-rw-r--r--libs/ardour/ardour/auditioner.h70
-rw-r--r--libs/ardour/ardour/auto_bundle.h50
-rw-r--r--libs/ardour/ardour/automatable.h121
-rw-r--r--libs/ardour/ardour/automation_control.h64
-rw-r--r--libs/ardour/ardour/automation_event.h309
-rw-r--r--libs/ardour/ardour/base_audio_port.h106
-rw-r--r--libs/ardour/ardour/base_midi_port.h67
-rw-r--r--libs/ardour/ardour/buffer.h92
-rw-r--r--libs/ardour/ardour/buffer_set.h159
-rw-r--r--libs/ardour/ardour/bundle.h73
-rw-r--r--libs/ardour/ardour/caimportable.h48
-rw-r--r--libs/ardour/ardour/chan_count.h126
-rw-r--r--libs/ardour/ardour/click.h45
-rw-r--r--libs/ardour/ardour/configuration.h106
-rw-r--r--libs/ardour/ardour/configuration_variable.h184
-rw-r--r--libs/ardour/ardour/configuration_vars.h176
-rw-r--r--libs/ardour/ardour/control_protocol_manager.h93
-rw-r--r--libs/ardour/ardour/control_protocol_search_path.h42
-rw-r--r--libs/ardour/ardour/coreaudiosource.h57
-rw-r--r--libs/ardour/ardour/crossfade.h179
-rw-r--r--libs/ardour/ardour/crossfade_compare.h42
-rw-r--r--libs/ardour/ardour/curve.h62
-rw-r--r--libs/ardour/ardour/cycle_timer.h50
-rw-r--r--libs/ardour/ardour/cycles.h221
-rw-r--r--libs/ardour/ardour/dB.h33
-rw-r--r--libs/ardour/ardour/data_type.h130
-rw-r--r--libs/ardour/ardour/directory_names.h23
-rw-r--r--libs/ardour/ardour/diskstream.h308
-rw-r--r--libs/ardour/ardour/export.h107
-rw-r--r--libs/ardour/ardour/filename_extensions.h17
-rw-r--r--libs/ardour/ardour/filesystem_paths.h50
-rw-r--r--libs/ardour/ardour/filter.h51
-rw-r--r--libs/ardour/ardour/gain.h43
-rw-r--r--libs/ardour/ardour/gdither.h92
-rw-r--r--libs/ardour/ardour/gdither_types.h48
-rw-r--r--libs/ardour/ardour/gdither_types_internal.h74
-rw-r--r--libs/ardour/ardour/importable_source.h43
-rw-r--r--libs/ardour/ardour/internal_audio_port.h55
-rw-r--r--libs/ardour/ardour/internal_port.h82
-rw-r--r--libs/ardour/ardour/io.h391
-rw-r--r--libs/ardour/ardour/io_processor.h86
-rw-r--r--libs/ardour/ardour/jack_audio_port.h51
-rw-r--r--libs/ardour/ardour/jack_midi_port.h52
-rw-r--r--libs/ardour/ardour/jack_port.h112
-rw-r--r--libs/ardour/ardour/ladspa.h606
-rw-r--r--libs/ardour/ardour/ladspa_plugin.h147
-rw-r--r--libs/ardour/ardour/latent.h26
-rw-r--r--libs/ardour/ardour/location.h206
-rw-r--r--libs/ardour/ardour/logcurve.h132
-rw-r--r--libs/ardour/ardour/lv2_plugin.h171
-rw-r--r--libs/ardour/ardour/meter.h77
-rw-r--r--libs/ardour/ardour/midi_buffer.h102
-rw-r--r--libs/ardour/ardour/midi_diskstream.h184
-rw-r--r--libs/ardour/ardour/midi_model.h252
-rw-r--r--libs/ardour/ardour/midi_playlist.h84
-rw-r--r--libs/ardour/ardour/midi_port.h47
-rw-r--r--libs/ardour/ardour/midi_region.h121
-rw-r--r--libs/ardour/ardour/midi_ring_buffer.h466
-rw-r--r--libs/ardour/ardour/midi_source.h119
-rw-r--r--libs/ardour/ardour/midi_stretch.h40
-rw-r--r--libs/ardour/ardour/midi_track.h111
-rw-r--r--libs/ardour/ardour/midi_util.h73
-rw-r--r--libs/ardour/ardour/mix.h65
-rw-r--r--libs/ardour/ardour/named_selection.h55
-rw-r--r--libs/ardour/ardour/noise.h38
-rw-r--r--libs/ardour/ardour/note.h82
-rw-r--r--libs/ardour/ardour/osc.h120
-rw-r--r--libs/ardour/ardour/panner.h299
-rw-r--r--libs/ardour/ardour/parameter.h168
-rw-r--r--libs/ardour/ardour/pcm_utils.h41
-rw-r--r--libs/ardour/ardour/peak.h36
-rw-r--r--libs/ardour/ardour/pitch.h62
-rw-r--r--libs/ardour/ardour/playlist.h287
-rw-r--r--libs/ardour/ardour/playlist_factory.h44
-rw-r--r--libs/ardour/ardour/playlist_templates.h48
-rw-r--r--libs/ardour/ardour/plugin.h174
-rw-r--r--libs/ardour/ardour/plugin_insert.h131
-rw-r--r--libs/ardour/ardour/plugin_manager.h101
-rw-r--r--libs/ardour/ardour/port.h180
-rw-r--r--libs/ardour/ardour/port_insert.h73
-rw-r--r--libs/ardour/ardour/port_set.h161
-rw-r--r--libs/ardour/ardour/processor.h123
-rw-r--r--libs/ardour/ardour/profile.h58
-rw-r--r--libs/ardour/ardour/quantize.h41
-rw-r--r--libs/ardour/ardour/rb_effect.h42
-rw-r--r--libs/ardour/ardour/readable.h20
-rw-r--r--libs/ardour/ardour/recent_sessions.h39
-rw-r--r--libs/ardour/ardour/region.h313
-rw-r--r--libs/ardour/ardour/region_factory.h64
-rw-r--r--libs/ardour/ardour/resampled_source.h55
-rw-r--r--libs/ardour/ardour/reverse.h37
-rw-r--r--libs/ardour/ardour/route.h384
-rw-r--r--libs/ardour/ardour/route_group.h124
-rw-r--r--libs/ardour/ardour/route_group_specialized.h41
-rw-r--r--libs/ardour/ardour/runtime_functions.h40
-rw-r--r--libs/ardour/ardour/send.h74
-rw-r--r--libs/ardour/ardour/session.h1714
-rw-r--r--libs/ardour/ardour/session_directory.h136
-rw-r--r--libs/ardour/ardour/session_object.h65
-rw-r--r--libs/ardour/ardour/session_playlist.h46
-rw-r--r--libs/ardour/ardour/session_region.h38
-rw-r--r--libs/ardour/ardour/session_route.h76
-rw-r--r--libs/ardour/ardour/session_selection.h39
-rw-r--r--libs/ardour/ardour/session_state_utils.h65
-rw-r--r--libs/ardour/ardour/session_utils.h26
-rw-r--r--libs/ardour/ardour/silentfilesource.h68
-rw-r--r--libs/ardour/ardour/slave.h153
-rw-r--r--libs/ardour/ardour/smf_reader.h87
-rw-r--r--libs/ardour/ardour/smf_source.h151
-rw-r--r--libs/ardour/ardour/smpte.h79
-rw-r--r--libs/ardour/ardour/sndfile_helpers.h58
-rw-r--r--libs/ardour/ardour/sndfileimportable.h50
-rw-r--r--libs/ardour/ardour/sndfilesource.h109
-rw-r--r--libs/ardour/ardour/soundseq.h53
-rw-r--r--libs/ardour/ardour/source.h112
-rw-r--r--libs/ardour/ardour/source_factory.h60
-rw-r--r--libs/ardour/ardour/spline.h90
-rw-r--r--libs/ardour/ardour/stretch.h62
-rw-r--r--libs/ardour/ardour/tape_file_matcher.h45
-rw-r--r--libs/ardour/ardour/template_utils.h20
-rw-r--r--libs/ardour/ardour/tempo.h322
-rw-r--r--libs/ardour/ardour/timestamps.h32
-rw-r--r--libs/ardour/ardour/track.h151
-rw-r--r--libs/ardour/ardour/transient_detector.h58
-rw-r--r--libs/ardour/ardour/types.h443
-rw-r--r--libs/ardour/ardour/user_bundle.h72
-rw-r--r--libs/ardour/ardour/utils.h76
-rw-r--r--libs/ardour/ardour/vst_plugin.h116
-rw-r--r--libs/ardour/audio_buffer.cc82
-rw-r--r--libs/ardour/audio_diskstream.cc2490
-rw-r--r--libs/ardour/audio_library.cc154
-rw-r--r--libs/ardour/audio_playlist.cc788
-rw-r--r--libs/ardour/audio_port.cc126
-rw-r--r--libs/ardour/audio_track.cc884
-rw-r--r--libs/ardour/audio_unit.cc924
-rw-r--r--libs/ardour/audioanalyser.cc166
-rw-r--r--libs/ardour/audioengine.cc1379
-rw-r--r--libs/ardour/audiofilesource.cc759
-rw-r--r--libs/ardour/audioregion.cc1402
-rw-r--r--libs/ardour/audiosource.cc932
-rw-r--r--libs/ardour/auditioner.cc233
-rw-r--r--libs/ardour/auto_bundle.cc47
-rw-r--r--libs/ardour/automatable.cc477
-rw-r--r--libs/ardour/automation.cc32
-rw-r--r--libs/ardour/automation_control.cc91
-rw-r--r--libs/ardour/automation_event.cc1666
-rw-r--r--libs/ardour/base_audio_port.cc82
-rw-r--r--libs/ardour/base_midi_port.cc67
-rw-r--r--libs/ardour/buffer.cc45
-rw-r--r--libs/ardour/buffer_set.cc177
-rw-r--r--libs/ardour/bundle.cc282
-rw-r--r--libs/ardour/caimportable.cc118
-rw-r--r--libs/ardour/chan_count.cc48
-rw-r--r--libs/ardour/configuration.cc371
-rw-r--r--libs/ardour/control_protocol_manager.cc390
-rw-r--r--libs/ardour/control_protocol_search_path.cc52
-rw-r--r--libs/ardour/coreaudiosource.cc270
-rw-r--r--libs/ardour/crossfade.cc904
-rw-r--r--libs/ardour/curve.cc406
-rw-r--r--libs/ardour/cycle_timer.cc74
-rw-r--r--libs/ardour/default_click.cc1174
-rw-r--r--libs/ardour/directory_names.cc19
-rw-r--r--libs/ardour/diskstream.cc411
-rw-r--r--libs/ardour/enums.cc388
-rw-r--r--libs/ardour/filename_extensions.cc15
-rw-r--r--libs/ardour/filesystem_paths.cc106
-rw-r--r--libs/ardour/filter.cc123
-rw-r--r--libs/ardour/find_session.cc167
-rw-r--r--libs/ardour/gain.cc61
-rw-r--r--libs/ardour/gdither.cc474
-rw-r--r--libs/ardour/gettext.h82
-rw-r--r--libs/ardour/globals.cc619
-rw-r--r--libs/ardour/i18n.h16
-rw-r--r--libs/ardour/import.cc491
-rw-r--r--libs/ardour/internal_audio_port.cc71
-rw-r--r--libs/ardour/internal_port.cc163
-rw-r--r--libs/ardour/io.cc2612
-rw-r--r--libs/ardour/io_processor.cc107
-rw-r--r--libs/ardour/jack_audio_port.cc55
-rw-r--r--libs/ardour/jack_midi_port.cc91
-rw-r--r--libs/ardour/jack_port.cc179
-rw-r--r--libs/ardour/jack_slave.cc95
-rw-r--r--libs/ardour/ladspa_plugin.cc663
-rw-r--r--libs/ardour/location.cc915
-rw-r--r--libs/ardour/lv2_plugin.cc625
-rw-r--r--libs/ardour/macosx/English.lproj/InfoPlist.stringsbin0 -> 142 bytes
-rw-r--r--libs/ardour/macosx/Info.plist26
-rw-r--r--libs/ardour/macosx/ardour.xcodeproj/project.pbxproj1214
-rw-r--r--libs/ardour/macosx/ardour_Prefix.pch4
-rw-r--r--libs/ardour/macosx/version.cc3
-rw-r--r--libs/ardour/macosx/version.h17
-rw-r--r--libs/ardour/meter.cc165
-rw-r--r--libs/ardour/midi_buffer.cc282
-rw-r--r--libs/ardour/midi_diskstream.cc1461
-rw-r--r--libs/ardour/midi_model.cc953
-rw-r--r--libs/ardour/midi_playlist.cc318
-rw-r--r--libs/ardour/midi_port.cc112
-rw-r--r--libs/ardour/midi_region.cc363
-rw-r--r--libs/ardour/midi_source.cc206
-rw-r--r--libs/ardour/midi_stretch.cc108
-rw-r--r--libs/ardour/midi_track.cc769
-rw-r--r--libs/ardour/mix.cc176
-rw-r--r--libs/ardour/mtc_slave.cc357
-rw-r--r--libs/ardour/named_selection.cc130
-rw-r--r--libs/ardour/note.cc110
-rw-r--r--libs/ardour/osc.cc480
-rw-r--r--libs/ardour/panner.cc1489
-rw-r--r--libs/ardour/parameter.cc121
-rw-r--r--libs/ardour/pcm_utils.cc177
-rw-r--r--libs/ardour/playlist.cc2367
-rw-r--r--libs/ardour/playlist_factory.cc111
-rw-r--r--libs/ardour/plugin.cc243
-rw-r--r--libs/ardour/plugin_insert.cc917
-rw-r--r--libs/ardour/plugin_manager.cc489
-rw-r--r--libs/ardour/po/el_GR.po2195
-rw-r--r--libs/ardour/po/it_IT.po2236
-rw-r--r--libs/ardour/po/pl_PL.po2063
-rw-r--r--libs/ardour/po/ru_RU.po2000
-rw-r--r--libs/ardour/po/sv_SE.po2025
-rw-r--r--libs/ardour/port.cc379
-rw-r--r--libs/ardour/port_insert.cc246
-rw-r--r--libs/ardour/port_set.cc126
-rw-r--r--libs/ardour/processor.cc255
-rw-r--r--libs/ardour/quantize.cc85
-rw-r--r--libs/ardour/rb_effect.cc298
-rw-r--r--libs/ardour/recent_sessions.cc135
-rw-r--r--libs/ardour/region.cc1532
-rw-r--r--libs/ardour/region_factory.cc181
-rw-r--r--libs/ardour/resampled_source.cc128
-rw-r--r--libs/ardour/reverse.cc130
-rw-r--r--libs/ardour/route.cc2676
-rw-r--r--libs/ardour/route_group.cc213
-rw-r--r--libs/ardour/send.cc228
-rw-r--r--libs/ardour/session.cc4273
-rw-r--r--libs/ardour/session_butler.cc471
-rw-r--r--libs/ardour/session_click.cc223
-rw-r--r--libs/ardour/session_command.cc544
-rw-r--r--libs/ardour/session_directory.cc148
-rw-r--r--libs/ardour/session_events.cc453
-rw-r--r--libs/ardour/session_export.cc652
-rw-r--r--libs/ardour/session_feedback.cc49
-rw-r--r--libs/ardour/session_midi.cc1211
-rw-r--r--libs/ardour/session_process.cc886
-rw-r--r--libs/ardour/session_state.cc3219
-rw-r--r--libs/ardour/session_state_utils.cc76
-rw-r--r--libs/ardour/session_time.cc606
-rw-r--r--libs/ardour/session_transport.cc1355
-rw-r--r--libs/ardour/session_utils.cc39
-rw-r--r--libs/ardour/session_vst.cc315
-rw-r--r--libs/ardour/silentfilesource.cc39
-rw-r--r--libs/ardour/smf_reader.cc285
-rw-r--r--libs/ardour/smf_source.cc971
-rw-r--r--libs/ardour/sndfile_helpers.cc209
-rw-r--r--libs/ardour/sndfileimportable.cc47
-rw-r--r--libs/ardour/sndfilesource.cc900
-rw-r--r--libs/ardour/source.cc269
-rw-r--r--libs/ardour/source_factory.cc278
-rw-r--r--libs/ardour/sse_functions.s531
-rw-r--r--libs/ardour/sse_functions_64bit.s609
-rw-r--r--libs/ardour/sse_functions_xmm.cc116
-rw-r--r--libs/ardour/st_pitch.cc52
-rw-r--r--libs/ardour/st_stretch.cc215
-rw-r--r--libs/ardour/tape_file_matcher.cc62
-rw-r--r--libs/ardour/template_utils.cc34
-rw-r--r--libs/ardour/tempo.cc1539
-rw-r--r--libs/ardour/track.cc224
-rw-r--r--libs/ardour/transient_detector.cc118
-rw-r--r--libs/ardour/user_bundle.cc198
-rw-r--r--libs/ardour/utils.cc529
-rw-r--r--libs/ardour/vst_plugin.cc512
291 files changed, 96490 insertions, 0 deletions
diff --git a/libs/ardour/.cvsignore b/libs/ardour/.cvsignore
new file mode 100644
index 0000000000..15d7926473
--- /dev/null
+++ b/libs/ardour/.cvsignore
@@ -0,0 +1,8 @@
+libardour.la
+libardour.pc
+version.cc
+*.lo
+*.os
+*.mo
+*.pot
+*.dylib
diff --git a/libs/ardour/ChangeLog b/libs/ardour/ChangeLog
new file mode 100644
index 0000000000..a0d3b193b6
--- /dev/null
+++ b/libs/ardour/ChangeLog
@@ -0,0 +1,118 @@
+2002-11-24 gettextize <bug-gnu-gettext@gnu.org>
+
+ * configure.ac (AC_OUTPUT): Add intl/Makefile,
+
+2002-11-24 gettextize <bug-gnu-gettext@gnu.org>
+
+ * Makefile.am (ACLOCAL_AMFLAGS): New variable.
+
+2001-10-26 Paul Davis <pbd>
+
+ * playlist.cc (recover_backup): restored the backup recovery code
+ for playlists.
+
+ * diskstream.cc (do_refill): added state_lock to diskstream, just
+ to be safe.
+
+ * session.cc (butler_thread_work): changed Session ISA thread to
+ HASA thread.
+
+2001-10-23 Paul Davis <pbd>
+
+ merged in marcus' patch for edit/mix group save/restore, and
+ rationalized both it and the existing code for Route::set_state()
+
+2001-10-20 Paul Davis <pbd>
+
+ * session.cc (get_state): in get_state, use the public order for routes.
+
+2001-10-18 Paul Davis <pbd>
+
+ * playlist.cc (read): stop a muted region from causing a playlist
+ read error.
+
+2001-10-17 Paul Davis <pbd>
+
+ * region.cc (read_at): remove staccato noise caused by not
+ shifting target buffer when !opaque.
+
+2001-10-15 Paul Davis <pbd>
+
+ * region.cc (set_fade_out_active): made region fade in/out optional.
+
+ * configure.in: patches from Ben related to libxml++
+
+2001-10-12 Paul Davis <pbd>
+
+ * session.cc (XMLRegionFactory): move most XML-based Region
+ constructor into region.
+
+
+2001-10-10 Paul Davis <pbd>
+
+ * session.cc (load_sources): add whole-file regions when loading
+ sources.
+
+2001-10-09 Paul Davis <pbd>
+
+ * ardour/session.h: fix an ugly bug with a non-reference return type.
+
+2001-10-04 Paul Davis <pbd>
+
+ * playlist.cc (split_region): ensure that left region after split
+ is in the right place.
+
+ * auditioner.cc (play_audition): stop existing audition before
+ starting a new one.
+
+2001-10-03 Paul Davis <pbd>
+
+ * session.cc (process): stop regular process() call from operating
+ on hidden diskstreams and routes. the butler thread still works on
+ all diskstreams, every time, which might be a mistake.
+
+2001-10-02 Paul Davis <pbd>
+
+ * session.cc (set_auto_play_range): added provisional support
+ for play ranges using session events. added code to use
+ auditioner.
+
+ * auditioner.cc: new file/object to support auditioning.
+
+ * route.cc: remove seek() function (didn't exist).
+
+ * session.cc (process): use list<DiskStream *> instead of GList
+ for diskstreams. add auditioner object.
+
+2001-09-30 Paul Davis <pbd>
+
+ * playlist.cc (split_region): fix problem with region splitting
+ not defining two *smaller* regions of the original.
+
+ * region.cc (set_position): remove RegionTemplate object.
+
+ * playlist.cc (struct RegionSorter ): fix sorting to use position,
+ not start - whatever was i thinking ?
+
+2001-09-28 Paul Davis <pbd>
+
+ * source.cc: emit source creation signal.
+
+ * session.cc (first_stage_init): catch all source creation events.
+
+ * sndfilesource.cc (init): fix up an off-by-one substr-length
+ error when creating a sndfilesource.
+
+2001-09-27 Paul Davis <pbd>
+
+ * route.cc (operator): correct loop increment bug that caused a
+ hang when an Insert is added to a Route as a Redirect.
+
+2001-09-25 Paul Davis <pbd>
+
+ * session.cc: make new file sources be partially named for their
+ initial host diskstream.
+
+ peak file construction now carried out en-masse at the
+ end of capture run.
+
diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript
new file mode 100644
index 0000000000..b50aecbcb3
--- /dev/null
+++ b/libs/ardour/SConscript
@@ -0,0 +1,395 @@
+# -*- python -*-
+
+import os
+import os.path
+import glob
+
+Import('env final_prefix install_prefix final_config_prefix libraries i18n')
+
+ardour = env.Copy()
+
+#
+# this defines the version number of libardour
+#
+
+domain = 'libardour3'
+
+ardour.Append(DOMAIN = domain, MAJOR = 2, MINOR = 0, MICRO = 0)
+ardour.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"")
+ardour.Append(CXXFLAGS=["-DLIBSIGC_DISABLE_DEPRECATED", "-DGLIBMM_EXCEPTIONS_ENABLED"])
+ardour.Append(PACKAGE = domain)
+ardour.Append(POTFILE = domain + '.pot')
+
+if ardour['IS_OSX']:
+ ardour.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048")
+
+#
+# explicitly reference the control protocol LGPL library for includes
+#
+
+ardour.Append(CPPPATH = '#libs/surfaces/control_protocol')
+
+ardour_files=Split("""
+amp.cc
+analyser.cc
+audioanalyser.cc
+audio_buffer.cc
+audio_diskstream.cc
+audioengine.cc
+audiofilesource.cc
+audio_library.cc
+audio_playlist.cc
+audio_port.cc
+audioregion.cc
+audiosource.cc
+audio_track.cc
+auditioner.cc
+auto_bundle.cc
+automatable.cc
+automation.cc
+automation_control.cc
+automation_event.cc
+base_audio_port.cc
+base_midi_port.cc
+buffer.cc
+buffer_set.cc
+chan_count.cc
+configuration.cc
+control_protocol_manager.cc
+control_protocol_search_path.cc
+crossfade.cc
+curve.cc
+cycle_timer.cc
+default_click.cc
+directory_names.cc
+diskstream.cc
+enums.cc
+filename_extensions.cc
+filesystem_paths.cc
+filter.cc
+find_session.cc
+gain.cc
+gdither.cc
+globals.cc
+import.cc
+io.cc
+io_processor.cc
+jack_audio_port.cc
+jack_midi_port.cc
+jack_port.cc
+jack_slave.cc
+ladspa_plugin.cc
+location.cc
+meter.cc
+midi_buffer.cc
+midi_diskstream.cc
+midi_model.cc
+midi_playlist.cc
+midi_port.cc
+midi_region.cc
+midi_source.cc
+midi_stretch.cc
+midi_track.cc
+mix.cc
+mtc_slave.cc
+named_selection.cc
+note.cc
+panner.cc
+parameter.cc
+pcm_utils.cc
+playlist.cc
+playlist_factory.cc
+plugin.cc
+plugin_insert.cc
+plugin_manager.cc
+port.cc
+port_insert.cc
+port_set.cc
+processor.cc
+quantize.cc
+recent_sessions.cc
+region.cc
+region_factory.cc
+resampled_source.cc
+reverse.cc
+route.cc
+route_group.cc
+send.cc
+session_butler.cc
+session.cc
+session_click.cc
+session_command.cc
+session_directory.cc
+session_events.cc
+session_export.cc
+session_midi.cc
+session_process.cc
+session_state.cc
+session_state_utils.cc
+session_time.cc
+session_transport.cc
+session_utils.cc
+silentfilesource.cc
+smf_reader.cc
+smf_source.cc
+sndfile_helpers.cc
+sndfilesource.cc
+sndfileimportable.cc
+source.cc
+source_factory.cc
+tape_file_matcher.cc
+template_utils.cc
+tempo.cc
+track.cc
+transient_detector.cc
+user_bundle.cc
+utils.cc
+version.cc
+""")
+
+arch_specific_objects = [ ]
+
+osc_files = [ 'osc.cc' ]
+vst_files = [ 'vst_plugin.cc', 'session_vst.cc' ]
+lv2_files = [ 'lv2_plugin.cc' ]
+audiounit_files = [ 'audio_unit.cc' ]
+coreaudio_files = [ 'coreaudiosource.cc', 'caimportable.cc' ]
+extra_sources = [ ]
+timefx_sources = [ ]
+
+if ardour['VST']:
+ extra_sources += vst_files
+ ardour.Append(CCFLAGS="-DVST_SUPPORT", CPPPATH="#libs/fst")
+
+if ardour['LV2']:
+ extra_sources += lv2_files
+ ardour.Append(CCFLAGS="-DHAVE_SLV2")
+
+if ardour['LIBLO']:
+ extra_sources += osc_files
+
+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'], 'ardour3', 'vamp') + "\\\"")
+ardour.Append(CXXFLAGS="-DCONFIG_DIR=\\\"" + final_config_prefix + "\\\"")
+ardour.Append(CXXFLAGS="-DLOCALEDIR=\\\"" + os.path.join (final_prefix, 'share', 'locale') + "\\\"")
+
+ardour.Merge ([ libraries['jack'] ])
+
+#
+# See if JACK supports jack_client_open()
+#
+
+jack_test_source_file = """
+#include <jack/jack.h>
+int main(int argc, char **argv)
+{
+ jack_client_open ("foo", 0, 0);
+ return 0;
+}
+"""
+def CheckJackClientOpen(context):
+ context.Message('Checking for jack_client_open()...')
+ result = context.TryLink(jack_test_source_file, '.c')
+ context.Result(result)
+ return result
+
+#
+# See if JACK supports jack_recompute_total_latencies()
+#
+
+jack_test_source_file = """
+#include <jack/jack.h>
+int main(int argc, char **argv)
+{
+ jack_recompute_total_latencies ((jack_client_t*) 0);
+ return 0;
+}
+"""
+def CheckJackRecomputeLatencies(context):
+ context.Message('Checking for jack_recompute_total_latencies()...')
+ result = context.TryLink(jack_test_source_file, '.c')
+ context.Result(result)
+ return result
+
+jack_video_frame_offset_test = """
+#include <jack/transport.h>
+int main(int argc, char** argv)
+{
+ jack_position_t pos;
+
+ pos.valid & JackVideoFrameOffset;
+ return 0;
+}
+"""
+def CheckJackVideoFrameOffset(context):
+ context.Message('Checking for JackVideoFrameOffset in jack_position_bits_t enum...')
+ result = context.TryLink(jack_video_frame_offset_test, '.c')
+ context.Result(result)
+ return result
+
+
+#
+# See if JACK supports jack_recompute_total_latency() (single port version)
+#
+
+jack_port_latency_test = """
+#include <jack/jack.h>
+int main(int argc, char **argv)
+{
+ jack_recompute_total_latency ((jack_client_t*) 0, (jack_port_t*) 0);
+ return 0;
+}
+"""
+def CheckJackRecomputeLatency(context):
+ context.Message('Checking for jack_recompute_total_latency()...')
+ result = context.TryLink(jack_port_latency_test, '.c')
+ context.Result(result)
+ return result
+
+conf = Configure(ardour, custom_tests = {
+ 'CheckJackClientOpen' : CheckJackClientOpen,
+ 'CheckJackRecomputeLatencies' : CheckJackRecomputeLatencies,
+ 'CheckJackRecomputeLatency' : CheckJackRecomputeLatency,
+ 'CheckJackVideoFrameOffset' : CheckJackVideoFrameOffset
+})
+
+if conf.CheckJackClientOpen():
+ ardour.Append(CXXFLAGS="-DHAVE_JACK_CLIENT_OPEN")
+
+if conf.CheckJackRecomputeLatencies():
+ ardour.Append(CXXFLAGS="-DHAVE_JACK_RECOMPUTE_LATENCIES")
+
+if conf.CheckJackRecomputeLatency():
+ ardour.Append(CXXFLAGS="-DHAVE_JACK_RECOMPUTE_LATENCY")
+
+if conf.CheckJackVideoFrameOffset():
+ ardour.Append(CXXFLAGS="-DHAVE_JACK_VIDEO_SUPPORT")
+
+#
+# Optional header files
+#
+
+if conf.CheckCHeader('wordexp.h'):
+ ardour.Append(CXXFLAGS="-DHAVE_WORDEXP")
+
+if conf.CheckCHeader('sys/vfs.h'):
+ ardour.Append(CXXFLAGS="-DHAVE_SYS_VFS_H")
+
+if conf.CheckCHeader('/System/Library/Frameworks/CoreMIDI.framework/Headers/CoreMIDI.h'):
+ ardour.Append(LINKFLAGS="-framework CoreMIDI")
+
+if conf.CheckCHeader('/System/Library/Frameworks/AudioToolbox.framework/Headers/ExtendedAudioFile.h'):
+ ardour.Append(LINKFLAGS="-framework AudioToolbox")
+
+if conf.CheckCHeader('/System/Library/Frameworks/CoreAudio.framework/Headers/CoreAudio.h'):
+ ardour.Append(CXXFLAGS="-DHAVE_WEAK_COREAUDIO")
+
+if conf.CheckCHeader('/System/Library/Frameworks/AudioUnit.framework/Headers/AudioUnit.h') and ardour['AUDIOUNITS']:
+ ardour.Append(CXXFLAGS="-DHAVE_AUDIOUNITS")
+ ardour.Append(LINKFLAGS="-framework AudioUnit")
+ extra_sources += audiounit_files
+
+if ardour['COREAUDIO']:
+ ardour.Append(CXXFLAGS="-DHAVE_COREAUDIO")
+ extra_sources += coreaudio_files
+
+if env['CONFIG_ARCH'] == 'apple':
+ # this next line avoids issues with circular dependencies between libardour and libardour_cp.
+ # it is based on the (entirely reasonable) assumption that a system with CoreAudio is OS X
+ #
+ ardour.Append(LINKFLAGS='-undefined suppress -flat_namespace')
+
+ardour = conf.Finish ()
+
+ardour.Merge ([
+ libraries['core'],
+ libraries['fftw3'],
+ libraries['fftw3f'],
+ libraries['glib2'],
+ libraries['glibmm2'],
+ libraries['lrdf'],
+ libraries['midi++2'],
+ libraries['pbd'],
+ libraries['raptor'],
+ libraries['samplerate'],
+ libraries['sigc2'],
+ libraries['sndfile-ardour'],
+ libraries['vamp'],
+ libraries['vamphost'],
+ libraries['xml']
+ ])
+
+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'] ])
+
+if ardour['LIBLO']:
+ ardour.Merge ([ libraries['lo'] ])
+
+if ardour['COREAUDIO'] or ardour['AUDIOUNITS']:
+ ardour.Merge ([ libraries['appleutility'] ])
+
+def SharedAsmObjectEmitter(target, source, env):
+ for tgt in target:
+ tgt.attributes.shared = 1
+ return (target, source)
+
+
+env['BUILDERS']['SharedAsmObject'] = Builder (action = '$CXX -c -fPIC $SOURCE -o $TARGET',
+ emitter = SharedAsmObjectEmitter,
+ suffix = '$SHOBJSUFFIX',
+ src_suffix = '.s',
+ single_source = 1)
+#
+# handle objects that should always be compiled with -msse in their own
+# special environment, which is exactly like "ardour" but unconditionally
+# includes -msse
+#
+
+
+always_sse_objects = []
+sse_env = ardour.Copy()
+sse_env.Append (CXXFLAGS="-msse")
+
+if env['FPU_OPTIMIZATION']:
+ if env['DIST_TARGET'] == "i386":
+ arch_specific_objects = env.SharedAsmObject('sse_functions.os', 'sse_functions.s')
+ always_sse_objects += [ sse_env.SharedObject (source = 'sse_functions_xmm.cc') ]
+ if env['DIST_TARGET'] == "i686":
+ arch_specific_objects = env.SharedAsmObject('sse_functions.os', 'sse_functions.s')
+ always_sse_objects += [ sse_env.SharedObject (source = 'sse_functions_xmm.cc') ]
+ if env['DIST_TARGET'] == "x86_64":
+ arch_specific_objects = env.SharedAsmObject('sse_functions_64bit.os', 'sse_functions_64bit.s')
+ always_sse_objects += [ sse_env.SharedObject (source = 'sse_functions_xmm.cc') ]
+
+libardour = ardour.SharedLibrary('ardour', ardour_files + always_sse_objects + timefx_sources + extra_sources + arch_specific_objects)
+
+Default(libardour)
+
+if env['NLS']:
+ i18n (ardour, ardour_files + vst_files + coreaudio_files + timefx_sources + audiounit_files, env)
+
+
+env.Alias('install', env.Install(os.path.join(install_prefix, env['LIBDIR'], 'ardour3'), libardour))
+
+env.Alias('version', ardour.VersionBuild(['version.cc', 'ardour/version.h'], []))
+
+env.Alias('tarball', env.Distribute (env['DISTTREE'],
+ [ 'SConscript', 'i18n.h', 'gettext.h' ] +
+ [ 'sse_functions_xmm.cc', 'sse_functions.s', 'sse_functions_64bit.s' ] +
+ [ 'rb_effect.cc', 'st_stretch.cc', 'st_pitch.cc' ] +
+ ardour_files +
+ osc_files +
+ vst_files +
+ coreaudio_files +
+ audiounit_files +
+ lv2_files +
+ glob.glob('po/*.po') + glob.glob('ardour/*.h')))
diff --git a/libs/ardour/amp.cc b/libs/ardour/amp.cc
new file mode 100644
index 0000000000..a2ad70e8b1
--- /dev/null
+++ b/libs/ardour/amp.cc
@@ -0,0 +1,102 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cstring>
+#include <cmath>
+#include <algorithm>
+#include <ardour/amp.h>
+#include <ardour/buffer_set.h>
+#include <ardour/audio_buffer.h>
+
+namespace ARDOUR {
+
+
+/** Apply a declicked gain to the audio buffers of @a bufs */
+void
+Amp::run_in_place (BufferSet& bufs, nframes_t nframes, gain_t initial, gain_t target, bool invert_polarity)
+{
+ if (nframes == 0)
+ return;
+
+ if (bufs.count().n_audio() == 0)
+ return;
+
+ // assert(bufs.buffer_capacity(DataType::AUDIO) >= nframes);
+
+ // if we don't need to declick, defer to apply_simple_gain
+ if (initial == target) {
+ if (target == 0.0) {
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+ memset (i->data(), 0, sizeof (Sample) * nframes);
+ }
+ } else if (target != 1.0) {
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+ apply_gain_to_buffer (i->data(), nframes, target);
+ }
+ }
+ return;
+ }
+
+ const nframes_t declick = std::min ((nframes_t)128, nframes);
+ gain_t delta;
+ double fractional_shift = -1.0/declick;
+ double fractional_pos;
+ gain_t polscale = invert_polarity ? -1.0f : 1.0f;
+
+ if (target < initial) {
+ /* fade out: remove more and more of delta from initial */
+ delta = -(initial - target);
+ } else {
+ /* fade in: add more and more of delta from initial */
+ delta = target - initial;
+ }
+
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+ Sample* const buffer = i->data();
+
+ fractional_pos = 1.0;
+
+ for (nframes_t nx = 0; nx < declick; ++nx) {
+ buffer[nx] *= polscale * (initial + (delta * (0.5 + 0.5 * cos (M_PI * fractional_pos))));
+ fractional_pos += fractional_shift;
+ }
+
+ /* now ensure the rest of the buffer has the target value applied, if necessary. */
+
+ if (declick != nframes) {
+
+ if (invert_polarity) {
+ target = -target;
+ }
+
+ if (target == 0.0) {
+ memset (&buffer[declick], 0, sizeof (Sample) * (nframes - declick));
+ } else if (target != 1.0) {
+ apply_gain_to_buffer (&buffer[declick], nframes - declick, target);
+ }
+ }
+ }
+}
+
+void
+Amp::apply_simple_gain (BufferSet& bufs, nframes_t nframes, gain_t target)
+{
+}
+
+
+} // namespace ARDOUR
diff --git a/libs/ardour/analyser.cc b/libs/ardour/analyser.cc
new file mode 100644
index 0000000000..2e14c74b86
--- /dev/null
+++ b/libs/ardour/analyser.cc
@@ -0,0 +1,119 @@
+/*
+ Copyright (C) 2008 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/analyser.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/transient_detector.h>
+
+#include <pbd/pthread_utils.h>
+#include <pbd/convert.h>
+
+using namespace std;
+using namespace sigc;
+using namespace ARDOUR;
+using namespace PBD;
+
+Analyser* Analyser::the_analyser = 0;
+Glib::StaticMutex Analyser::analysis_queue_lock = GLIBMM_STATIC_MUTEX_INIT;
+Glib::Cond* Analyser::SourcesToAnalyse = 0;
+list<boost::weak_ptr<Source> > Analyser::analysis_queue;
+
+Analyser::Analyser ()
+{
+
+}
+
+Analyser::~Analyser ()
+{
+}
+
+static void
+analyser_work ()
+{
+ Analyser::work ();
+}
+
+void
+Analyser::init ()
+{
+ SourcesToAnalyse = new Glib::Cond();
+ Glib::Thread::create (sigc::ptr_fun (analyser_work), false);
+}
+
+void
+Analyser::queue_source_for_analysis (boost::shared_ptr<Source> src, bool force)
+{
+ if (!src->can_be_analysed()) {
+ return;
+ }
+
+ if (!force && src->has_been_analysed()) {
+ return;
+ }
+
+ Glib::Mutex::Lock lm (analysis_queue_lock);
+ analysis_queue.push_back (boost::weak_ptr<Source>(src));
+ SourcesToAnalyse->broadcast ();
+}
+
+void
+Analyser::work ()
+{
+ PBD::ThreadCreated (pthread_self(), string ("analyser-") + to_string (pthread_self(), std::dec));
+
+ while (true) {
+ analysis_queue_lock.lock ();
+
+ wait:
+ if (analysis_queue.empty()) {
+ SourcesToAnalyse->wait (analysis_queue_lock);
+ }
+
+ if (analysis_queue.empty()) {
+ goto wait;
+ }
+
+ boost::shared_ptr<Source> src (analysis_queue.front().lock());
+ analysis_queue.pop_front();
+ analysis_queue_lock.unlock ();
+
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
+
+ if (afs && afs->length()) {
+ analyse_audio_file_source (afs);
+ }
+ }
+}
+
+void
+Analyser::analyse_audio_file_source (boost::shared_ptr<AudioFileSource> src)
+{
+ AnalysisFeatureList results;
+
+ TransientDetector td (src->sample_rate());
+
+ if (td.run (src->get_transients_path(), src.get(), 0, results) == 0) {
+ src->set_been_analysed (true);
+ } else {
+ src->set_been_analysed (false);
+ }
+
+}
+
+
diff --git a/libs/ardour/ardour/.cvsignore b/libs/ardour/ardour/.cvsignore
new file mode 100644
index 0000000000..67020331ba
--- /dev/null
+++ b/libs/ardour/ardour/.cvsignore
@@ -0,0 +1 @@
+version.h
diff --git a/libs/ardour/ardour/amp.h b/libs/ardour/ardour/amp.h
new file mode 100644
index 0000000000..cdbcacbd91
--- /dev/null
+++ b/libs/ardour/ardour/amp.h
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 2006 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_amp_h__
+#define __ardour_amp_h__
+
+#include <ardour/types.h>
+
+namespace ARDOUR {
+
+class BufferSet;
+
+
+/** Applies a declick operation to all audio inputs, passing the same number of
+ * audio outputs, and passing through any other types unchanged.
+ *
+ * FIXME: make this a Processor.
+ */
+class Amp {
+public:
+ static void run_in_place (BufferSet& bufs, nframes_t nframes, gain_t initial, gain_t target, bool invert_polarity);
+
+ static void apply_simple_gain(BufferSet& bufs, nframes_t nframes, gain_t target);
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_amp_h__
diff --git a/libs/ardour/ardour/analyser.h b/libs/ardour/ardour/analyser.h
new file mode 100644
index 0000000000..8771cab6b0
--- /dev/null
+++ b/libs/ardour/ardour/analyser.h
@@ -0,0 +1,35 @@
+#ifndef __ardour_analyser_h__
+#define __ardour_analyser_h__
+
+#include <glibmm/thread.h>
+#include <boost/shared_ptr.hpp>
+
+namespace ARDOUR {
+
+class AudioFileSource;
+class Source;
+class TransientDetector;
+
+class Analyser {
+
+ public:
+ Analyser();
+ ~Analyser ();
+
+ static void init ();
+ static void queue_source_for_analysis (boost::shared_ptr<Source>, bool force);
+ static void work ();
+
+ private:
+ static Analyser* the_analyser;
+ static Glib::StaticMutex analysis_queue_lock;
+ static Glib::Cond* SourcesToAnalyse;
+ static std::list<boost::weak_ptr<Source> > analysis_queue;
+
+ static void analyse_audio_file_source (boost::shared_ptr<AudioFileSource>);
+};
+
+
+}
+
+#endif /* __ardour_analyser_h__ */
diff --git a/libs/ardour/ardour/ardour.h b/libs/ardour/ardour/ardour.h
new file mode 100644
index 0000000000..6f653f10bf
--- /dev/null
+++ b/libs/ardour/ardour/ardour.h
@@ -0,0 +1,87 @@
+/*
+ Copyright (C) 1999 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_ardour_h__
+#define __ardour_ardour_h__
+
+#include <map>
+#include <string>
+
+#include <limits.h>
+#include <signal.h>
+
+#include <pbd/error.h>
+#include <pbd/failed_constructor.h>
+
+#include <ardour/configuration.h>
+#include <ardour/types.h>
+
+namespace MIDI {
+ class MachineControl;
+ class Port;
+}
+
+namespace ARDOUR {
+
+ class AudioEngine;
+ class OSC;
+
+ extern OSC* osc;
+
+ static const nframes_t max_frames = JACK_MAX_FRAMES;
+ extern sigc::signal<void,std::string> BootMessage;
+
+ int init (bool with_vst, bool try_optimization);
+ int cleanup ();
+
+ std::string get_ardour_revision ();
+
+ void find_bindings_files (std::map<std::string,std::string>&);
+
+ const layer_t max_layer = UCHAR_MAX;
+
+ microseconds_t get_microseconds ();
+
+ Change new_change ();
+
+ extern Change StartChanged;
+ extern Change LengthChanged;
+ extern Change PositionChanged;
+ extern Change NameChanged;
+ extern Change BoundsChanged;
+
+ struct LocaleGuard {
+ LocaleGuard (const char*);
+ ~LocaleGuard ();
+ const char* old;
+ };
+
+ static const double SHUTTLE_FRACT_SPEED1=0.48412291827; /* derived from A1,A2 */
+
+ void setup_fpu ();
+}
+
+/* how do we make these be within the Ardour namespace? */
+
+extern MIDI::Port* default_mmc_port;
+extern MIDI::Port* default_mtc_port;
+extern MIDI::Port* default_midi_port;
+
+#endif /* __ardour_ardour_h__ */
+
diff --git a/libs/ardour/ardour/audio_buffer.h b/libs/ardour/ardour/audio_buffer.h
new file mode 100644
index 0000000000..71eaf60ade
--- /dev/null
+++ b/libs/ardour/ardour/audio_buffer.h
@@ -0,0 +1,121 @@
+/*
+ Copyright (C) 2006 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_audio_buffer_h__
+#define __ardour_audio_buffer_h__
+
+#include <cstring>
+#include <ardour/buffer.h>
+
+namespace ARDOUR {
+
+class AudioBuffer : public Buffer
+{
+public:
+ AudioBuffer(size_t capacity);
+
+ ~AudioBuffer();
+
+ void silence(nframes_t len, nframes_t offset = 0) {
+ if (!_silent) {
+ assert(_capacity > 0);
+ assert(offset + len <= _capacity);
+ memset(_data + offset, 0, sizeof (Sample) * len);
+ if (offset == 0 && len == _capacity) {
+ _silent = true;
+ }
+ }
+ }
+
+ /** Read @a len frames FROM THE START OF @a src into self at @a offset */
+ void read_from(const Buffer& src, nframes_t len, nframes_t offset) {
+ assert(&src != this);
+ assert(_capacity > 0);
+ assert(src.type() == DataType::AUDIO);
+ assert(offset + len <= _capacity);
+ memcpy(_data + offset, ((AudioBuffer&)src).data(), sizeof(Sample) * len);
+ _silent = src.silent();
+ }
+
+ /** Accumulate (add)@a len frames FROM THE START OF @a src into self at @a offset */
+ void accumulate_from(const AudioBuffer& src, nframes_t len, nframes_t offset) {
+ assert(_capacity > 0);
+ assert(offset + len <= _capacity);
+
+ Sample* const dst_raw = _data + offset;
+ const Sample* const src_raw = src.data();
+
+ mix_buffers_no_gain(dst_raw, src_raw, len);
+
+ _silent = (src.silent() && _silent);
+ }
+
+ /** Accumulate (add) @a len frames FROM THE START OF @a src into self at @a offset
+ * scaling by @a gain_coeff */
+ void accumulate_with_gain_from(const AudioBuffer& src, nframes_t len, nframes_t offset, gain_t gain_coeff) {
+ assert(_capacity > 0);
+ assert(offset + len <= _capacity);
+
+ Sample* const dst_raw = _data + offset;
+ const Sample* const src_raw = src.data();
+
+ mix_buffers_with_gain (dst_raw, src_raw, len, gain_coeff);
+
+ _silent = ( (src.silent() && _silent) || (_silent && gain_coeff == 0) );
+ }
+
+ void apply_gain(gain_t gain, nframes_t len, nframes_t offset=0) {
+ apply_gain_to_buffer (_data + offset, len, gain);
+ }
+
+ /** Set the data contained by this buffer manually (for setting directly to jack buffer).
+ *
+ * Constructor MUST have been passed capacity=0 or this will die (to prevent mem leaks).
+ */
+ void set_data (Sample* data, size_t size) {
+ assert(!_owns_data); // prevent leaks
+ _capacity = size;
+ _size = size;
+ _data = data;
+ _silent = false;
+ }
+
+ /** Reallocate the buffer used internally to handle at least @nframes of data
+ *
+ * Constructor MUST have been passed capacity!=0 or this will die (to prevent mem leaks).
+ */
+ void resize (size_t nframes);
+
+ const Sample* data () const { return _data; }
+ Sample* data () { return _data; }
+
+ const Sample* data(nframes_t nframes, nframes_t offset) const
+ { assert(offset + nframes <= _capacity); return _data + offset; }
+
+ Sample* data (nframes_t nframes, nframes_t offset)
+ { assert(offset + nframes <= _capacity); return _data + offset; }
+
+private:
+ bool _owns_data;
+ Sample* _data; ///< Actual buffer contents
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_audio_audio_buffer_h__
diff --git a/libs/ardour/ardour/audio_diskstream.h b/libs/ardour/ardour/audio_diskstream.h
new file mode 100644
index 0000000000..aaf5461361
--- /dev/null
+++ b/libs/ardour/ardour/audio_diskstream.h
@@ -0,0 +1,277 @@
+/*
+ Copyright (C) 2000-2006 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_audio_diskstream_h__
+#define __ardour_audio_diskstream_h__
+
+#include <sigc++/signal.h>
+
+#include <cmath>
+#include <string>
+#include <queue>
+#include <map>
+#include <vector>
+
+#include <time.h>
+
+#include <pbd/fastlog.h>
+#include <pbd/ringbufferNPT.h>
+#include <pbd/stateful.h>
+#include <pbd/rcu.h>
+
+#include <ardour/ardour.h>
+#include <ardour/configuration.h>
+#include <ardour/session.h>
+#include <ardour/route_group.h>
+#include <ardour/route.h>
+#include <ardour/port.h>
+#include <ardour/utils.h>
+#include <ardour/diskstream.h>
+#include <ardour/audioplaylist.h>
+
+struct tm;
+
+namespace ARDOUR {
+
+class AudioEngine;
+class Send;
+class Session;
+class AudioPlaylist;
+class AudioFileSource;
+class IO;
+
+class AudioDiskstream : public Diskstream
+{
+ public:
+ AudioDiskstream (Session &, const string& name, Diskstream::Flag f = Recordable);
+ AudioDiskstream (Session &, const XMLNode&);
+ ~AudioDiskstream();
+
+ float playback_buffer_load() const;
+ float capture_buffer_load() const;
+
+ string input_source (uint32_t n=0) const {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ if (n < c->size()) {
+ return (*c)[n]->source ? (*c)[n]->source->name() : "";
+ } else {
+ return "";
+ }
+ }
+
+ Port *input_source_port (uint32_t n=0) const {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ if (n < c->size()) return (*c)[n]->source; return 0;
+ }
+
+ void set_record_enabled (bool yn);
+ int set_destructive (bool yn);
+ bool can_become_destructive (bool& requires_bounce) const;
+
+ float peak_power(uint32_t n = 0) {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ ChannelInfo* chaninfo = (*c)[n];
+ float x = chaninfo->peak_power;
+ chaninfo->peak_power = 0.0f;
+ if (x > 0.0f) {
+ return 20.0f * fast_log10(x);
+ } else {
+ return minus_infinity();
+ }
+ }
+
+ boost::shared_ptr<AudioPlaylist> audio_playlist () { return boost::dynamic_pointer_cast<AudioPlaylist>(_playlist); }
+
+ int use_playlist (boost::shared_ptr<Playlist>);
+ int use_new_playlist ();
+ int use_copy_playlist ();
+
+ Sample *playback_buffer (uint32_t n = 0) {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ if (n < c->size())
+ return (*c)[n]->current_playback_buffer;
+ return 0;
+ }
+
+ Sample *capture_buffer (uint32_t n = 0) {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ if (n < c->size())
+ return (*c)[n]->current_capture_buffer;
+ return 0;
+ }
+
+ boost::shared_ptr<AudioFileSource> write_source (uint32_t n=0) {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ if (n < c->size())
+ return (*c)[n]->write_source;
+ return boost::shared_ptr<AudioFileSource>();
+ }
+
+ int add_channel (uint32_t how_many);
+ int remove_channel (uint32_t how_many);
+
+ /* stateful */
+
+ XMLNode& get_state(void);
+ int set_state(const XMLNode& node);
+
+ void monitor_input (bool);
+
+ static void swap_by_ptr (Sample *first, Sample *last) {
+ while (first < last) {
+ Sample tmp = *first;
+ *first++ = *last;
+ *last-- = tmp;
+ }
+ }
+
+ static void swap_by_ptr (Sample *first, Sample *last, nframes_t n) {
+ while (n--) {
+ Sample tmp = *first;
+ *first++ = *last;
+ *last-- = tmp;
+ }
+ }
+
+ XMLNode* deprecated_io_node;
+
+ protected:
+ friend class Session;
+
+ /* the Session is the only point of access for these
+ because they require that the Session is "inactive"
+ while they are called.
+ */
+
+ void set_pending_overwrite(bool);
+ int overwrite_existing_buffers ();
+ void set_block_size (nframes_t);
+ int internal_playback_seek (nframes_t distance);
+ int can_internal_playback_seek (nframes_t distance);
+ int rename_write_sources ();
+ void reset_write_sources (bool, bool force = false);
+ void non_realtime_input_change ();
+
+ protected:
+ friend class Auditioner;
+ int seek (nframes_t which_sample, bool complete_refill = false);
+
+ protected:
+ friend class AudioTrack;
+
+ int process (nframes_t transport_frame, nframes_t nframes, nframes_t offset, bool can_record, bool rec_monitors_input);
+ bool commit (nframes_t nframes);
+
+ private:
+
+ struct ChannelInfo {
+
+ ChannelInfo (nframes_t buffer_size, nframes_t speed_buffer_size, nframes_t wrap_buffer_size);
+ ~ChannelInfo ();
+
+ Sample *playback_wrap_buffer;
+ Sample *capture_wrap_buffer;
+ Sample *speed_buffer;
+
+ float peak_power;
+
+ boost::shared_ptr<AudioFileSource> fades_source;
+ boost::shared_ptr<AudioFileSource> write_source;
+
+ /// the Port that our audio data comes from
+ Port *source;
+ Sample *current_capture_buffer;
+ Sample *current_playback_buffer;
+
+ RingBufferNPT<Sample> *playback_buf;
+ RingBufferNPT<Sample> *capture_buf;
+
+ Sample* scrub_buffer;
+ Sample* scrub_forward_buffer;
+ Sample* scrub_reverse_buffer;
+
+ RingBufferNPT<Sample>::rw_vector playback_vector;
+ RingBufferNPT<Sample>::rw_vector capture_vector;
+
+ RingBufferNPT<CaptureTransition> * capture_transition_buf;
+ // the following are used in the butler thread only
+ nframes_t curr_capture_cnt;
+ };
+
+ typedef std::vector<ChannelInfo*> ChannelList;
+
+ /* The two central butler operations */
+ int do_flush (Session::RunContext context, bool force = false);
+ int do_refill () { return _do_refill(_mixdown_buffer, _gain_buffer); }
+
+ int do_refill_with_alloc ();
+
+ int read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer,
+ nframes_t& start, nframes_t cnt,
+ ChannelInfo* channel_info, int channel, bool reversed);
+
+ void finish_capture (bool rec_monitors_input, boost::shared_ptr<ChannelList>);
+ void transport_stopped (struct tm&, time_t, bool abort);
+ void transport_looped (nframes_t transport_frame);
+
+ void init (Diskstream::Flag);
+
+ void init_channel (ChannelInfo &chan);
+ void destroy_channel (ChannelInfo &chan);
+
+ int use_new_write_source (uint32_t n=0);
+
+ int find_and_use_playlist (const string&);
+
+ void allocate_temporary_buffers ();
+
+ int use_pending_capture_data (XMLNode& node);
+
+ void get_input_sources ();
+ void check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record);
+ void set_align_style_from_io();
+ void setup_destructive_playlist ();
+ void use_destructive_playlist ();
+
+ void engage_record_enable ();
+ void disengage_record_enable ();
+
+ // Working buffers for do_refill (butler thread)
+ static void allocate_working_buffers();
+ static void free_working_buffers();
+
+ static size_t _working_buffers_size;
+ static Sample* _mixdown_buffer;
+ static gain_t* _gain_buffer;
+
+ std::vector<boost::shared_ptr<AudioFileSource> > capturing_sources;
+
+ SerializedRCUManager<ChannelList> channels;
+
+ /* really */
+ private:
+ int _do_refill (Sample *mixdown_buffer, float *gain_buffer);
+
+ int add_channel_to (boost::shared_ptr<ChannelList>, uint32_t how_many);
+ int remove_channel_from (boost::shared_ptr<ChannelList>, uint32_t how_many);
+
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_audio_diskstream_h__ */
diff --git a/libs/ardour/ardour/audio_library.h b/libs/ardour/ardour/audio_library.h
new file mode 100644
index 0000000000..86b5fb3aa2
--- /dev/null
+++ b/libs/ardour/ardour/audio_library.h
@@ -0,0 +1,54 @@
+/*
+ Copyright (C) 2003-2006 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_audio_library_h__
+#define __ardour_audio_library_h__
+
+#include <string>
+#include <map>
+#include <vector>
+
+using std::vector;
+using std::string;
+using std::map;
+
+namespace ARDOUR {
+
+class AudioLibrary
+{
+ public:
+ AudioLibrary ();
+ ~AudioLibrary ();
+
+ void set_tags (string member, vector<string> tags);
+ vector<string> get_tags (string member);
+
+ void search_members_and (vector<string>& results, const vector<string> tags);
+
+ void save_changes();
+
+ private:
+ string src;
+};
+
+extern AudioLibrary* Library;
+
+} // ARDOUR namespace
+
+#endif // __ardour_audio_library_h__
diff --git a/libs/ardour/ardour/audio_port.h b/libs/ardour/ardour/audio_port.h
new file mode 100644
index 0000000000..874f842d83
--- /dev/null
+++ b/libs/ardour/ardour/audio_port.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2002 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.
+
+ $Id: port.h 712 2006-07-28 01:08:57Z drobilla $
+*/
+
+#ifndef __ardour_audio_port_h__
+#define __ardour_audio_port_h__
+
+#include <ardour/base_audio_port.h>
+
+namespace ARDOUR {
+
+class AudioPort : public BaseAudioPort, public PortFacade {
+
+ public:
+ ~AudioPort();
+
+ void reset ();
+
+ void cycle_start (nframes_t nframes, nframes_t offset);
+ void cycle_end (nframes_t nframes, nframes_t offset);
+
+ protected:
+ friend class AudioEngine;
+
+ AudioPort (const std::string&, Flags, bool external, nframes_t);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_audio_port_h__ */
diff --git a/libs/ardour/ardour/audio_track.h b/libs/ardour/ardour/audio_track.h
new file mode 100644
index 0000000000..3546545329
--- /dev/null
+++ b/libs/ardour/ardour/audio_track.h
@@ -0,0 +1,80 @@
+/*
+ Copyright (C) 2002-2006 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_audio_track_h__
+#define __ardour_audio_track_h__
+
+#include <ardour/track.h>
+
+namespace ARDOUR {
+
+class Session;
+class AudioDiskstream;
+class AudioPlaylist;
+class RouteGroup;
+
+class AudioTrack : public Track
+{
+ public:
+ AudioTrack (Session&, string name, Route::Flag f = Route::Flag (0), TrackMode m = Normal);
+ AudioTrack (Session&, const XMLNode&);
+ ~AudioTrack ();
+
+ int set_mode (TrackMode m);
+ bool can_use_mode (TrackMode m, bool& bounce_required);
+
+ int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, int declick, bool can_record, bool rec_monitors_input);
+
+ int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, bool state_changing, bool can_record, bool rec_monitors_input);
+
+ int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, bool can_record, bool rec_monitors_input);
+
+ boost::shared_ptr<AudioDiskstream> audio_diskstream() const;
+
+ int use_diskstream (string name);
+ int use_diskstream (const PBD::ID& id);
+
+ int export_stuff (BufferSet& bufs, nframes_t nframes, nframes_t end_frame);
+
+ void freeze (InterThreadInfo&);
+ void unfreeze ();
+
+ void bounce (InterThreadInfo&);
+ void bounce_range (nframes_t start, nframes_t end, InterThreadInfo&);
+
+ int set_state(const XMLNode& node);
+
+ protected:
+ XMLNode& state (bool full);
+
+ int _set_state (const XMLNode&, bool call_base);
+
+ private:
+ int set_diskstream (boost::shared_ptr<AudioDiskstream>, void *);
+ int deprecated_use_diskstream_connections ();
+ void set_state_part_two ();
+ void set_state_part_three ();
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_audio_track_h__ */
diff --git a/libs/ardour/ardour/audio_unit.h b/libs/ardour/ardour/audio_unit.h
new file mode 100644
index 0000000000..dc9a52d5d3
--- /dev/null
+++ b/libs/ardour/ardour/audio_unit.h
@@ -0,0 +1,172 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Taybin Rutkin
+
+ 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_audio_unit_h__
+#define __ardour_audio_unit_h__
+
+#include <stdint.h>
+#include <boost/shared_ptr.hpp>
+
+#include <list>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <ardour/plugin.h>
+
+#include <AudioUnit/AudioUnit.h>
+#include <appleutility/AUParamInfo.h>
+
+#include <boost/shared_ptr.hpp>
+
+class CAComponent;
+class CAAudioUnit;
+class CAComponentDescription;
+struct AudioBufferList;
+
+namespace ARDOUR {
+
+class AudioEngine;
+class Session;
+
+struct AUParameterDescriptor : public Plugin::ParameterDescriptor {
+ // additional fields to make operations more efficient
+ AudioUnitParameterID id;
+ AudioUnitScope scope;
+ AudioUnitElement element;
+ float default_value;
+ bool automatable;
+ AudioUnitParameterUnit unit;
+};
+
+class AUPlugin : public ARDOUR::Plugin
+{
+ public:
+ AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAComponent> comp);
+ AUPlugin (const AUPlugin& other);
+ virtual ~AUPlugin ();
+
+ std::string unique_id () const;
+ const char * label () const;
+ const char * name () const { return _info->name.c_str(); }
+ const char * maker () const { return _info->creator.c_str(); }
+ uint32_t parameter_count () const;
+ float default_value (uint32_t port);
+ nframes_t signal_latency () const;
+ void set_parameter (uint32_t which, float val);
+ float get_parameter (uint32_t which) const;
+
+ int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const;
+ uint32_t nth_parameter (uint32_t which, bool& ok) const;
+ void activate ();
+ void deactivate ();
+ void set_block_size (nframes_t nframes);
+
+ int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset);
+
+ std::set<uint32_t> automatable() const;
+ string describe_parameter (uint32_t);
+ string state_node_name () const { return "audiounit"; }
+ void print_parameter (uint32_t, char*, uint32_t len) const;
+
+ bool parameter_is_audio (uint32_t) const;
+ bool parameter_is_control (uint32_t) const;
+ bool parameter_is_input (uint32_t) const;
+ bool parameter_is_output (uint32_t) const;
+
+ XMLNode& get_state();
+ int set_state(const XMLNode& node);
+
+ bool save_preset (string name);
+ bool load_preset (const string preset_label);
+ std::vector<std::string> get_presets ();
+
+ bool has_editor () const;
+
+ bool fixed_io() const { return false; }
+ int32_t can_support_input_configuration (int32_t in);
+ int32_t compute_output_streams (int32_t nplugins);
+ uint32_t output_streams() const;
+ uint32_t input_streams() const;
+
+ boost::shared_ptr<CAAudioUnit> get_au () { return unit; }
+ boost::shared_ptr<CAComponent> get_comp () const { return comp; }
+
+ OSStatus render_callback(AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList* ioData);
+ private:
+ boost::shared_ptr<CAComponent> comp;
+ boost::shared_ptr<CAAudioUnit> unit;
+
+ AudioStreamBasicDescription streamFormat;
+ bool initialized;
+ int format_set;
+ AudioBufferList* buffers;
+
+ UInt32 global_elements;
+ UInt32 output_elements;
+ UInt32 input_elements;
+
+ int set_output_format ();
+ int set_input_format ();
+ int set_stream_format (int scope, uint32_t cnt);
+ int _set_block_size (nframes_t nframes);
+ void discover_parameters ();
+
+ std::vector<std::pair<uint32_t, uint32_t> > parameter_map;
+ uint32_t current_maxbuf;
+ nframes_t current_offset;
+ nframes_t cb_offset;
+ vector<Sample*>* current_buffers;
+ nframes_t frames_processed;
+
+ std::vector<AUParameterDescriptor> descriptors;
+ void init ();
+};
+
+typedef boost::shared_ptr<AUPlugin> AUPluginPtr;
+
+class AUPluginInfo : public PluginInfo {
+ public:
+ AUPluginInfo (boost::shared_ptr<CAComponentDescription>);
+ ~AUPluginInfo ();
+
+ PluginPtr load (Session& session);
+
+ static PluginInfoList discover ();
+ static void get_names (CAComponentDescription&, std::string& name, Glib::ustring& maker);
+ static std::string stringify_descriptor (const CAComponentDescription&);
+
+ private:
+ boost::shared_ptr<CAComponentDescription> descriptor;
+
+ static void discover_music (PluginInfoList&);
+ static void discover_fx (PluginInfoList&);
+ static void discover_by_description (PluginInfoList&, CAComponentDescription&);
+};
+
+typedef boost::shared_ptr<AUPluginInfo> AUPluginInfoPtr;
+
+} // namespace ARDOUR
+
+#endif // __ardour_audio_unit_h__
diff --git a/libs/ardour/ardour/audioanalyser.h b/libs/ardour/ardour/audioanalyser.h
new file mode 100644
index 0000000000..06b841990a
--- /dev/null
+++ b/libs/ardour/ardour/audioanalyser.h
@@ -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__ */
diff --git a/libs/ardour/ardour/audioengine.h b/libs/ardour/ardour/audioengine.h
new file mode 100644
index 0000000000..e1d5e50cc2
--- /dev/null
+++ b/libs/ardour/ardour/audioengine.h
@@ -0,0 +1,276 @@
+/*
+ Copyright (C) 2002-2004 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_audioengine_h__
+#define __ardour_audioengine_h__
+
+#include <list>
+#include <set>
+#include <cmath>
+#include <exception>
+#include <string>
+
+#include <sigc++/signal.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/rcu.h>
+
+#include <ardour/ardour.h>
+#include <jack/jack.h>
+#include <jack/transport.h>
+#include <ardour/types.h>
+#include <ardour/data_type.h>
+
+namespace ARDOUR {
+
+class Session;
+class Port;
+class InternalPort;
+
+class AudioEngine : public sigc::trackable
+{
+ public:
+ typedef std::set<Port*> Ports;
+
+ AudioEngine (std::string client_name);
+ virtual ~AudioEngine ();
+
+ jack_client_t* jack() const;
+ bool connected() const { return _jack != 0; }
+
+ bool is_realtime () const;
+
+ std::string client_name() const { return jack_client_name; }
+
+ int reconnect_to_jack ();
+ int disconnect_from_jack();
+
+ bool will_reconnect_at_halt ();
+ void set_reconnect_at_halt (bool);
+
+ int stop (bool forever = false);
+ int start ();
+ bool running() const { return _running; }
+
+ Glib::Mutex& process_lock() { return _process_lock; }
+
+ nframes_t frame_rate();
+ nframes_t frames_per_cycle();
+
+ int usecs_per_cycle () const { return _usecs_per_cycle; }
+
+ bool get_sync_offset (nframes_t& offset) const;
+
+ nframes_t frames_since_cycle_start () {
+ if (!_running || !_jack) return 0;
+ return jack_frames_since_cycle_start (_jack);
+ }
+ nframes_t frame_time () {
+ if (!_running || !_jack) return 0;
+ return jack_frame_time (_jack);
+ }
+
+ nframes_t transport_frame () const {
+ if (!_running || !_jack) return 0;
+ return jack_get_current_transport_frame (_jack);
+ }
+
+ int request_buffer_size (nframes_t);
+
+ nframes_t set_monitor_check_interval (nframes_t);
+
+ float get_cpu_load() {
+ if (!_running || !_jack) return 0;
+ return jack_cpu_load (_jack);
+ }
+
+ void set_session (Session *);
+ void remove_session ();
+
+ class PortRegistrationFailure : public std::exception {
+ public:
+ PortRegistrationFailure (const char* why = "") {
+ reason = why;
+ }
+ virtual const char *what() const throw() { return reason; }
+
+ private:
+ const char* reason;
+ };
+
+ class NoBackendAvailable : public std::exception {
+ public:
+ virtual const char *what() const throw() { return "could not connect to engine backend"; }
+ };
+
+ Port *register_input_port (DataType, const std::string& portname, bool publish);
+ Port *register_output_port (DataType, const std::string& portname, bool publish);
+ int unregister_port (Port &);
+
+ int connect (const std::string& source, const std::string& destination);
+ int disconnect (const std::string& source, const std::string& destination);
+ int disconnect (Port &);
+
+ const char ** get_ports (const std::string& port_name_pattern, const std::string& type_name_pattern, uint32_t flags);
+
+ uint32_t n_physical_outputs () const;
+ uint32_t n_physical_inputs () const;
+
+ bool can_request_hardware_monitoring ();
+
+ void get_physical_outputs (std::vector<std::string>&);
+ void get_physical_inputs (std::vector<std::string>&);
+
+ std::string get_nth_physical_output (DataType type, uint32_t n) {
+ return get_nth_physical (type, n, JackPortIsInput);
+ }
+
+ std::string get_nth_physical_input (DataType type, uint32_t n) {
+ return get_nth_physical (type, n, JackPortIsOutput);
+ }
+
+ nframes_t get_port_total_latency (const Port&);
+ void update_total_latencies ();
+ void update_total_latency (const Port&);
+
+ /** Caller may not delete the object pointed to by the return value
+ */
+ Port *get_port_by_name (const std::string& name, bool keep = true) const;
+
+ enum TransportState {
+ TransportStopped = JackTransportStopped,
+ TransportRolling = JackTransportRolling,
+ TransportLooping = JackTransportLooping,
+ TransportStarting = JackTransportStarting
+ };
+
+ void transport_start ();
+ void transport_stop ();
+ void transport_locate (nframes_t);
+ TransportState transport_state ();
+
+ int reset_timebase ();
+
+ /* start/stop freewheeling */
+
+ int freewheel (bool onoff);
+ bool freewheeling() const { return _freewheeling; }
+
+ /* this signal is sent for every process() cycle while freewheeling.
+ the regular process() call to session->process() is not made.
+ */
+
+ sigc::signal<int,nframes_t> Freewheel;
+
+ sigc::signal<void> Xrun;
+
+ /* this signal is if JACK notifies us of a graph order event */
+
+ sigc::signal<void> GraphReordered;
+
+ /* this signal is emitted if the sample rate changes */
+
+ sigc::signal<void,nframes_t> SampleRateChanged;
+
+ /* this signal is sent if JACK ever disconnects us */
+
+ sigc::signal<void> Halted;
+
+ /* these two are emitted when the engine itself is
+ started and stopped
+ */
+
+ sigc::signal<void> Running;
+ sigc::signal<void> Stopped;
+
+ std::string make_port_name_relative (std::string);
+ std::string make_port_name_non_relative (std::string);
+
+ private:
+ ARDOUR::Session *session;
+ jack_client_t *_jack;
+ std::string jack_client_name;
+ mutable Glib::Mutex _process_lock;
+ Glib::Cond session_removed;
+ bool session_remove_pending;
+ bool _running;
+ bool _has_run;
+ nframes_t _buffer_size;
+ nframes_t _frame_rate;
+ /// number of frames between each check for changes in monitor input
+ nframes_t monitor_check_interval;
+ /// time of the last monitor check in frames
+ nframes_t last_monitor_check;
+ /// the number of frames processed since start() was called
+ nframes_t _processed_frames;
+ bool _freewheeling;
+ bool _freewheel_thread_registered;
+ sigc::slot<int,nframes_t> freewheel_action;
+ bool reconnect_on_halt;
+ int _usecs_per_cycle;
+
+ SerializedRCUManager<Ports> ports;
+
+ Port *register_port (DataType type, const std::string& portname, bool input, bool publish);
+
+ int process_callback (nframes_t nframes);
+ void remove_all_ports ();
+
+ Port* get_port (const std::string& short_name);
+
+ typedef std::pair<std::string,std::string> PortConnection;
+ typedef std::list<PortConnection> PortConnections;
+
+ PortConnections port_connections;
+ void remove_connections_for (Port&);
+
+ std::string get_nth_physical (DataType type, uint32_t n, int flags);
+
+ void port_registration_failure (const std::string& portname);
+
+ static int _xrun_callback (void *arg);
+ static int _graph_order_callback (void *arg);
+ static int _process_callback (nframes_t nframes, void *arg);
+ static int _sample_rate_callback (nframes_t nframes, void *arg);
+ static int _bufsize_callback (nframes_t nframes, void *arg);
+ static void _jack_timebase_callback (jack_transport_state_t, nframes_t, jack_position_t*, int, void*);
+ static int _jack_sync_callback (jack_transport_state_t, jack_position_t*, void *arg);
+ static void _freewheel_callback (int , void *arg);
+
+ void jack_timebase_callback (jack_transport_state_t, nframes_t, jack_position_t*, int);
+ int jack_sync_callback (jack_transport_state_t, jack_position_t*);
+ int jack_bufsize_callback (nframes_t);
+ int jack_sample_rate_callback (nframes_t);
+
+ static void halted (void *);
+
+ int connect_to_jack (std::string client_name);
+
+ void meter_thread ();
+ void start_metering_thread ();
+ void stop_metering_thread ();
+
+ Glib::Thread* m_meter_thread;
+ static gint m_meter_exit;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_audioengine_h__ */
diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h
new file mode 100644
index 0000000000..de388a06fc
--- /dev/null
+++ b/libs/ardour/ardour/audiofilesource.h
@@ -0,0 +1,183 @@
+/*
+ Copyright (C) 2006 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_audiofilesource_h__
+#define __ardour_audiofilesource_h__
+
+#include <exception>
+
+#include <time.h>
+
+#include <ardour/audiosource.h>
+
+namespace ARDOUR {
+
+class non_existent_source : public std::exception {
+ public:
+ virtual const char *what() const throw() { return "audio file does not exist"; }
+};
+
+struct SoundFileInfo {
+ float samplerate;
+ uint16_t channels;
+ int64_t length;
+ std::string format_name;
+ int64_t timecode;
+};
+
+class AudioFileSource : public AudioSource {
+ public:
+ enum Flag {
+ Writable = 0x1,
+ CanRename = 0x2,
+ Broadcast = 0x4,
+ Removable = 0x8,
+ RemovableIfEmpty = 0x10,
+ RemoveAtDestroy = 0x20,
+ NoPeakFile = 0x40,
+ Destructive = 0x80
+ };
+
+ virtual ~AudioFileSource ();
+
+ bool set_name (const std::string& newname) { return (set_source_name(newname, destructive()) == 0); }
+ int set_source_name (Glib::ustring newname, bool destructive);
+
+ Glib::ustring path() const { return _path; }
+ Glib::ustring peak_path (Glib::ustring audio_path);
+ Glib::ustring find_broken_peakfile (Glib::ustring missing_peak_path,
+ Glib::ustring audio_path);
+
+ uint16_t channel() const { return _channel; }
+
+ static void set_peak_dir (Glib::ustring dir) { peak_dir = dir; }
+
+ static bool get_soundfile_info (Glib::ustring path, SoundFileInfo& _info, std::string& error);
+
+ static bool safe_file_extension (Glib::ustring path);
+
+ void set_allow_remove_if_empty (bool yn);
+ void mark_for_remove();
+
+ /* this block of methods do nothing for regular file sources, but are significant
+ for files used in destructive recording.
+ */
+
+ virtual nframes_t last_capture_start_frame() const { return 0; }
+ virtual void mark_capture_start (nframes_t) {}
+ virtual void mark_capture_end () {}
+ virtual void clear_capture_marks() {}
+ virtual bool one_of_several_channels () const { return false; }
+
+ virtual int update_header (nframes_t when, struct tm&, time_t) = 0;
+ virtual int flush_header () = 0;
+
+ int move_to_trash (const Glib::ustring& trash_dir_name);
+
+ static bool is_empty (Session&, Glib::ustring path);
+ void mark_streaming_write_completed ();
+
+ void mark_take (Glib::ustring);
+ Glib::ustring take_id() const { return _take_id; }
+
+ bool is_embedded() const { return _is_embedded; }
+
+ static void set_bwf_serial_number (int);
+
+ static void set_search_path (Glib::ustring string);
+ static void set_header_position_offset (nframes_t offset );
+
+ int setup_peakfile ();
+
+ static sigc::signal<void> HeaderPositionOffsetChanged;
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
+ bool destructive() const { return (_flags & Destructive); }
+ virtual bool set_destructive (bool yn) { return false; }
+ bool can_truncate_peaks() const { return !destructive(); }
+
+ Flag flags() const { return _flags; }
+
+ void mark_immutable ();
+
+ /* this should really be protected, but C++ is getting stricter
+ and creating slots from protected member functions is starting
+ to cause issues.
+ */
+
+ virtual void handle_header_position_change () {}
+
+ bool can_be_analysed() const { return _length > 0; }
+
+ protected:
+
+ /* constructor to be called for existing external-to-session files */
+
+ AudioFileSource (Session&, Glib::ustring path, Flag flags);
+
+ /* constructor to be called for new in-session files */
+
+ AudioFileSource (Session&, Glib::ustring path, Flag flags,
+ SampleFormat samp_format, HeaderFormat hdr_format);
+
+ /* constructor to be called for existing in-session files */
+
+ AudioFileSource (Session&, const XMLNode&, bool must_exit = true);
+
+ int init (Glib::ustring idstr, bool must_exist);
+
+ Glib::ustring _path;
+ Flag _flags;
+ Glib::ustring _take_id;
+ int64_t timeline_position;
+ bool file_is_new;
+ uint16_t _channel;
+
+ bool _is_embedded;
+ static bool determine_embeddedness(Glib::ustring path);
+
+ static Glib::ustring peak_dir;
+ static Glib::ustring search_path;
+
+ static char bwf_country_code[3];
+ static char bwf_organization_code[4];
+ static char bwf_serial_number[13];
+
+ static uint64_t header_position_offset;
+
+ virtual void set_timeline_position (int64_t pos);
+ virtual void set_header_timeline_position () = 0;
+
+ bool find (Glib::ustring& path, bool must_exist, bool& is_new, uint16_t& chan);
+ bool removable() const;
+ bool writable() const { return _flags & Writable; }
+
+ static Sample* get_interleave_buffer (nframes_t size);
+
+ private:
+ Glib::ustring old_peak_path (Glib::ustring audio_path);
+ Glib::ustring broken_peak_path (Glib::ustring audio_path);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_audiofilesource_h__ */
+
diff --git a/libs/ardour/ardour/audioplaylist.h b/libs/ardour/ardour/audioplaylist.h
new file mode 100644
index 0000000000..4acbc9ad51
--- /dev/null
+++ b/libs/ardour/ardour/audioplaylist.h
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2003 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_audio_playlist_h__
+#define __ardour_audio_playlist_h__
+
+#include <vector>
+#include <list>
+
+#include <ardour/ardour.h>
+#include <ardour/playlist.h>
+
+namespace ARDOUR {
+
+class Session;
+class Region;
+class AudioRegion;
+class Source;
+
+class AudioPlaylist : public ARDOUR::Playlist
+{
+ public:
+ typedef std::list<boost::shared_ptr<Crossfade> > Crossfades;
+
+ public:
+ AudioPlaylist (Session&, const XMLNode&, bool hidden = false);
+ AudioPlaylist (Session&, string name, bool hidden = false);
+ AudioPlaylist (boost::shared_ptr<const AudioPlaylist>, string name, bool hidden = false);
+ AudioPlaylist (boost::shared_ptr<const AudioPlaylist>, nframes_t start, nframes_t cnt, string name, bool hidden = false);
+
+ ~AudioPlaylist ();
+
+ void clear (bool with_signals=true);
+
+ nframes_t read (Sample *dst, Sample *mixdown, float *gain_buffer, nframes_t start, nframes_t cnt, uint32_t chan_n=0);
+
+ int set_state (const XMLNode&);
+
+ sigc::signal<void,boost::shared_ptr<Crossfade> > NewCrossfade;
+
+ template<class T> void foreach_crossfade (T *t, void (T::*func)(boost::shared_ptr<Crossfade>));
+ void crossfades_at (nframes_t frame, Crossfades&);
+
+ bool destroy_region (boost::shared_ptr<Region>);
+
+ protected:
+
+ /* playlist "callbacks" */
+ void notify_crossfade_added (boost::shared_ptr<Crossfade>);
+ void flush_notifications ();
+
+ void finalize_split_region (boost::shared_ptr<Region> orig, boost::shared_ptr<Region> left, boost::shared_ptr<Region> right);
+
+ void refresh_dependents (boost::shared_ptr<Region> region);
+ void check_dependents (boost::shared_ptr<Region> region, bool norefresh);
+ void remove_dependents (boost::shared_ptr<Region> region);
+
+ private:
+ Crossfades _crossfades;
+ Crossfades _pending_xfade_adds;
+
+ void crossfade_invalidated (boost::shared_ptr<Region>);
+ XMLNode& state (bool full_state);
+ void dump () const;
+
+ bool region_changed (Change, boost::shared_ptr<Region>);
+ void crossfade_changed (Change);
+ void add_crossfade (boost::shared_ptr<Crossfade>);
+
+ void source_offset_changed (boost::shared_ptr<AudioRegion> region);
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_audio_playlist_h__ */
+
+
diff --git a/libs/ardour/ardour/audioregion.h b/libs/ardour/ardour/audioregion.h
new file mode 100644
index 0000000000..81b7ef7c57
--- /dev/null
+++ b/libs/ardour/ardour/audioregion.h
@@ -0,0 +1,204 @@
+/*
+ Copyright (C) 2000-2006 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_audio_region_h__
+#define __ardour_audio_region_h__
+
+#include <vector>
+#include <list>
+
+#include <pbd/fastlog.h>
+#include <pbd/undo.h>
+
+#include <ardour/ardour.h>
+#include <ardour/region.h>
+#include <ardour/gain.h>
+#include <ardour/logcurve.h>
+#include <ardour/export.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Route;
+class Playlist;
+class Session;
+class Filter;
+class AudioSource;
+
+class AudioRegion : public Region
+{
+ public:
+ static Change FadeInChanged;
+ static Change FadeOutChanged;
+ static Change FadeInActiveChanged;
+ static Change FadeOutActiveChanged;
+ static Change EnvelopeActiveChanged;
+ static Change ScaleAmplitudeChanged;
+ static Change EnvelopeChanged;
+
+ ~AudioRegion();
+
+ bool speed_mismatch (float) const;
+
+ boost::shared_ptr<AudioSource> audio_source (uint32_t n=0) const;
+
+ void set_scale_amplitude (gain_t);
+ gain_t scale_amplitude() const { return _scale_amplitude; }
+
+ void normalize_to (float target_in_dB = 0.0f);
+
+ bool envelope_active () const { return _flags & Region::EnvelopeActive; }
+ bool fade_in_active () const { return _flags & Region::FadeIn; }
+ bool fade_out_active () const { return _flags & Region::FadeOut; }
+
+ boost::shared_ptr<AutomationList> fade_in() { return _fade_in; }
+ boost::shared_ptr<AutomationList> fade_out() { return _fade_out; }
+ boost::shared_ptr<AutomationList> envelope() { return _envelope; }
+
+ virtual nframes_t read_peaks (PeakData *buf, nframes_t npeaks,
+ nframes_t offset, nframes_t cnt,
+ uint32_t chan_n=0, double samples_per_unit= 1.0) const;
+
+ /* Readable interface */
+
+ virtual nframes64_t read (Sample*, nframes64_t pos, nframes64_t cnt, int channel) const;
+ virtual nframes64_t readable_length() const { return length(); }
+
+ virtual nframes_t read_at (Sample *buf, Sample *mixdown_buf,
+ float *gain_buf, nframes_t position, nframes_t cnt,
+ uint32_t chan_n = 0,
+ nframes_t read_frames = 0,
+ nframes_t skip_frames = 0) const;
+
+ virtual nframes_t master_read_at (Sample *buf, Sample *mixdown_buf,
+ float *gain_buf,
+ nframes_t position, nframes_t cnt, uint32_t chan_n=0) const;
+
+ virtual nframes_t read_raw_internal (Sample*, nframes_t, nframes_t) const;
+
+ XMLNode& state (bool);
+ int set_state (const XMLNode&);
+
+ static void set_default_fade (float steepness, nframes_t len);
+ bool fade_in_is_default () const;
+ bool fade_out_is_default () const;
+
+ enum FadeShape {
+ Linear,
+ Fast,
+ Slow,
+ LogA,
+ LogB
+ };
+
+ void set_fade_in_active (bool yn);
+ void set_fade_in_shape (FadeShape);
+ void set_fade_in_length (nframes_t);
+ void set_fade_in (FadeShape, nframes_t);
+
+ void set_fade_out_active (bool yn);
+ void set_fade_out_shape (FadeShape);
+ void set_fade_out_length (nframes_t);
+ void set_fade_out (FadeShape, nframes_t);
+
+ void set_envelope_active (bool yn);
+ void set_default_envelope ();
+
+ int separate_by_channel (ARDOUR::Session&, vector<boost::shared_ptr<AudioRegion> >&) const;
+
+ /* export */
+
+ int exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&);
+
+ /* xfade/fade interactions */
+
+ void suspend_fade_in ();
+ void suspend_fade_out ();
+ void resume_fade_in ();
+ void resume_fade_out ();
+
+ int get_transients (AnalysisFeatureList&, 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 (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&);
+
+ private:
+ void init ();
+ void set_default_fades ();
+ void set_default_fade_in ();
+ void set_default_fade_out ();
+
+ void recompute_gain_at_end ();
+ void recompute_gain_at_start ();
+
+ nframes_t _read_at (const SourceList&, nframes_t limit,
+ 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,
+ bool raw = false) const;
+
+ void recompute_at_start ();
+ void recompute_at_end ();
+
+ void envelope_changed ();
+ void fade_in_changed ();
+ void fade_out_changed ();
+ void source_offset_changed ();
+ void listen_to_my_curves ();
+ void listen_to_my_sources ();
+
+ boost::shared_ptr<AutomationList> _fade_in;
+ FadeShape _fade_in_shape;
+ boost::shared_ptr<AutomationList> _fade_out;
+ FadeShape _fade_out_shape;
+ boost::shared_ptr<AutomationList> _envelope;
+ gain_t _scale_amplitude;
+ uint32_t _fade_in_disabled;
+ uint32_t _fade_out_disabled;
+
+ protected:
+ /* default constructor for derived (compound) types */
+
+ AudioRegion (Session& s, nframes_t, nframes_t, std::string name);
+ AudioRegion (boost::shared_ptr<const AudioRegion>);
+
+ int set_live_state (const XMLNode&, Change&, bool send);
+};
+
+} /* namespace ARDOUR */
+
+/* access from C objects */
+
+extern "C" {
+ int region_read_peaks_from_c (void *arg, uint32_t npeaks, uint32_t start, uint32_t length, intptr_t data, uint32_t n_chan, double samples_per_unit);
+ uint32_t region_length_from_c (void *arg);
+ uint32_t sourcefile_length_from_c (void *arg, double);
+}
+
+#endif /* __ardour_audio_region_h__ */
diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h
new file mode 100644
index 0000000000..d11b829694
--- /dev/null
+++ b/libs/ardour/ardour/audiosource.h
@@ -0,0 +1,159 @@
+/*
+ Copyright (C) 2000 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_audio_source_h__
+#define __ardour_audio_source_h__
+
+#include <list>
+#include <vector>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <time.h>
+
+#include <glibmm/thread.h>
+#include <glibmm/ustring.h>
+
+#include <sigc++/signal.h>
+
+#include <ardour/source.h>
+#include <ardour/ardour.h>
+#include <pbd/stateful.h>
+#include <pbd/xml++.h>
+
+using std::list;
+using std::vector;
+
+namespace ARDOUR {
+
+class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR::AudioSource>
+{
+ public:
+ AudioSource (Session&, Glib::ustring name);
+ AudioSource (Session&, const XMLNode&);
+ virtual ~AudioSource ();
+
+ 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);
+
+ virtual float sample_rate () const = 0;
+
+ virtual void mark_for_remove() = 0;
+ virtual void mark_streaming_write_completed () {}
+
+ virtual bool can_truncate_peaks() const { return true; }
+
+ void set_captured_for (Glib::ustring str) { _captured_for = str; }
+ Glib::ustring captured_for() const { return _captured_for; }
+
+ uint32_t read_data_count() const { return _read_data_count; }
+ uint32_t write_data_count() const { return _write_data_count; }
+
+ int read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_visual_peak) const;
+
+ int build_peaks ();
+ bool peaks_ready (sigc::slot<void>, sigc::connection&) const;
+
+ mutable sigc::signal<void> PeaksReady;
+ mutable sigc::signal<void,nframes_t,nframes_t> PeakRangeReady;
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
+ int rename_peakfile (Glib::ustring newpath);
+ void touch_peakfile ();
+
+ static void set_build_missing_peakfiles (bool yn) {
+ _build_missing_peakfiles = yn;
+ }
+
+ static void set_build_peakfiles (bool yn) {
+ _build_peakfiles = yn;
+ }
+
+ static bool get_build_peakfiles () {
+ return _build_peakfiles;
+ }
+
+ virtual int setup_peakfile () { return 0; }
+
+ int prepare_for_peakfile_writes ();
+ void done_with_peakfile_writes (bool done = true);
+
+ protected:
+ static bool _build_missing_peakfiles;
+ static bool _build_peakfiles;
+
+ bool _peaks_built;
+ mutable Glib::Mutex _lock;
+ mutable Glib::Mutex _peaks_ready_lock;
+ Glib::ustring peakpath;
+ Glib::ustring _captured_for;
+
+ mutable uint32_t _read_data_count; // modified in read()
+ mutable uint32_t _write_data_count; // modified in write()
+
+ int initialize_peakfile (bool newfile, Glib::ustring path);
+ int build_peaks_from_scratch ();
+ int compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, bool intermediate_peaks_ready_signal);
+ void truncate_peakfile();
+
+ mutable off_t _peak_byte_max; // modified in compute_and_write_peak()
+
+ virtual nframes_t read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const = 0;
+ virtual nframes_t write_unlocked (Sample *dst, nframes_t cnt) = 0;
+ virtual Glib::ustring peak_path(Glib::ustring audio_path) = 0;
+ virtual Glib::ustring find_broken_peakfile (Glib::ustring missing_peak_path, Glib::ustring audio_path) = 0;
+
+ void update_length (nframes_t pos, nframes_t cnt);
+
+ virtual int read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt,
+ double samples_per_visual_peak, nframes_t fpp) const;
+
+ int compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force,
+ bool intermediate_peaks_ready_signal, nframes_t frames_per_peak);
+
+ private:
+ int peakfile;
+ nframes_t peak_leftover_cnt;
+ nframes_t peak_leftover_size;
+ Sample* peak_leftovers;
+ nframes_t peak_leftover_frame;
+
+ bool file_changed (Glib::ustring path);
+};
+
+}
+
+#endif /* __ardour_audio_source_h__ */
diff --git a/libs/ardour/ardour/auditioner.h b/libs/ardour/ardour/auditioner.h
new file mode 100644
index 0000000000..06d521ea21
--- /dev/null
+++ b/libs/ardour/ardour/auditioner.h
@@ -0,0 +1,70 @@
+/*
+ Copyright (C) 2001 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_auditioner_h__
+#define __ardour_auditioner_h__
+
+#include <string>
+
+#include <glibmm/thread.h>
+
+#include <ardour/ardour.h>
+#include <ardour/audio_track.h>
+
+namespace ARDOUR {
+
+class Session;
+class AudioRegion;
+class AudioPlaylist;
+
+class Auditioner : public AudioTrack
+{
+ public:
+ Auditioner (Session&);
+ ~Auditioner ();
+
+ void audition_region (boost::shared_ptr<Region>);
+
+ ARDOUR::AudioPlaylist& prepare_playlist ();
+ void audition_current_playlist ();
+
+ int play_audition (nframes_t nframes);
+
+ void cancel_audition () {
+ g_atomic_int_set (&_active, 0);
+ }
+
+ bool active() const { return g_atomic_int_get (&_active); }
+
+ private:
+ boost::shared_ptr<AudioRegion> the_region;
+ nframes_t current_frame;
+ mutable gint _active;
+ Glib::Mutex lock;
+ nframes_t length;
+
+ void drop_ports ();
+ static void *_drop_ports (void *);
+ void actually_drop_ports ();
+ void output_changed (IOChange, void*);
+};
+
+}; /* namespace ARDOUR */
+
+#endif /* __ardour_auditioner_h__ */
diff --git a/libs/ardour/ardour/auto_bundle.h b/libs/ardour/ardour/auto_bundle.h
new file mode 100644
index 0000000000..685a083e8d
--- /dev/null
+++ b/libs/ardour/ardour/auto_bundle.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2007 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_auto_bundle_h__
+#define __ardour_auto_bundle_h__
+
+#include <vector>
+#include <glibmm/thread.h>
+#include "ardour/bundle.h"
+
+namespace ARDOUR {
+
+class AutoBundle : public Bundle {
+
+ public:
+ AutoBundle (bool i = true);
+ AutoBundle (std::string const &, bool i = true);
+
+ uint32_t nchannels () const;
+ const PortList& channel_ports (uint32_t) const;
+
+ void set_channels (uint32_t);
+ void set_port (uint32_t, std::string const &);
+
+ private:
+ /// mutex for _ports;
+ /// XXX: is this necessary?
+ mutable Glib::Mutex _ports_mutex;
+ std::vector<PortList> _ports;
+};
+
+}
+
+#endif /* __ardour_auto_bundle_h__ */
diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h
new file mode 100644
index 0000000000..a2c1d98ae7
--- /dev/null
+++ b/libs/ardour/ardour/automatable.h
@@ -0,0 +1,121 @@
+/*
+ Copyright (C) 2000-2007 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_automatable_h__
+#define __ardour_automatable_h__
+
+#include <set>
+#include <map>
+#include <boost/shared_ptr.hpp>
+#include <ardour/session_object.h>
+#include <ardour/automation_event.h>
+#include <ardour/automation_control.h>
+#include <ardour/parameter.h>
+
+namespace ARDOUR {
+
+class Session;
+class AutomationControl;
+
+class Automatable : public SessionObject
+{
+public:
+ Automatable(Session&, const std::string& name);
+
+ virtual ~Automatable() {}
+
+ // shorthand for gain, pan, etc
+ inline boost::shared_ptr<AutomationControl>
+ control(AutomationType type, bool create_if_missing=false) {
+ return control(Parameter(type), create_if_missing);
+ }
+
+ virtual boost::shared_ptr<AutomationControl> control(Parameter id, bool create_if_missing=false);
+ virtual boost::shared_ptr<const AutomationControl> control(Parameter id) const;
+
+ boost::shared_ptr<AutomationControl> control_factory(boost::shared_ptr<AutomationList> list);
+
+ typedef std::map<Parameter,boost::shared_ptr<AutomationControl> > Controls;
+ Controls& controls() { return _controls; }
+ const Controls& controls() const { return _controls; }
+
+ virtual void add_control(boost::shared_ptr<AutomationControl>);
+
+ virtual void automation_snapshot(nframes_t now, bool force);
+ bool should_snapshot (nframes_t now) {
+ return (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval);
+ }
+ virtual void transport_stopped(nframes_t now);
+
+ virtual bool find_next_event(nframes_t start, nframes_t end, ControlEvent& ev) const;
+
+ virtual string describe_parameter(Parameter param);
+ virtual float default_parameter_value(Parameter param) { return 1.0f; }
+
+ virtual void clear_automation();
+
+ AutoState get_parameter_automation_state (Parameter param, bool lock = true);
+ virtual void set_parameter_automation_state (Parameter param, AutoState);
+
+ AutoStyle get_parameter_automation_style (Parameter param);
+ void set_parameter_automation_style (Parameter param, AutoStyle);
+
+ void protect_automation ();
+
+ void what_has_automation(std::set<Parameter>&) const;
+ void what_has_visible_automation(std::set<Parameter>&) const;
+ const std::set<Parameter>& what_can_be_automated() const { return _can_automate_list; }
+
+ void mark_automation_visible(Parameter, bool);
+
+ Glib::Mutex& automation_lock() const { return _automation_lock; }
+
+ static void set_automation_interval (jack_nframes_t frames) {
+ _automation_interval = frames;
+ }
+
+ static jack_nframes_t automation_interval() {
+ return _automation_interval;
+ }
+
+protected:
+
+ void can_automate(Parameter);
+
+ virtual void auto_state_changed (Parameter which) {}
+
+ int set_automation_state(const XMLNode&, Parameter default_param);
+ XMLNode& get_automation_state();
+
+ int load_automation (const std::string& path);
+ int old_set_automation_state(const XMLNode&);
+
+ mutable Glib::Mutex _automation_lock;
+
+ Controls _controls;
+ std::set<Parameter> _visible_controls;
+ std::set<Parameter> _can_automate_list;
+
+ nframes_t _last_automation_snapshot;
+ static nframes_t _automation_interval;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_automatable_h__ */
diff --git a/libs/ardour/ardour/automation_control.h b/libs/ardour/ardour/automation_control.h
new file mode 100644
index 0000000000..68ac5797dc
--- /dev/null
+++ b/libs/ardour/ardour/automation_control.h
@@ -0,0 +1,64 @@
+/*
+ Copyright (C) 2007 Paul Davis
+ Author: Dave Robillard
+
+ 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_automation_control_h__
+#define __ardour_automation_control_h__
+
+#include <boost/shared_ptr.hpp>
+#include <pbd/controllable.h>
+#include <ardour/parameter.h>
+
+namespace ARDOUR {
+
+class AutomationList;
+class Session;
+class Automatable;
+
+
+/** A PBD:Controllable with associated automation data (AutomationList)
+ */
+class AutomationControl : public PBD::Controllable
+{
+public:
+ AutomationControl(ARDOUR::Session&,
+ boost::shared_ptr<ARDOUR::AutomationList>,
+ std::string name="unnamed controllable");
+
+ void set_value(float val);
+ float get_value() const;
+ float user_value() const;
+
+ void set_list(boost::shared_ptr<ARDOUR::AutomationList>);
+
+ boost::shared_ptr<ARDOUR::AutomationList> list() { return _list; }
+ boost::shared_ptr<const ARDOUR::AutomationList> list() const { return _list; }
+
+ Parameter parameter() const;
+
+protected:
+ ARDOUR::Session& _session;
+ boost::shared_ptr<ARDOUR::AutomationList> _list;
+ float _user_value;
+};
+
+
+} // namespace ARDOUR
+
+#endif /* __ardour_automation_control_h__ */
diff --git a/libs/ardour/ardour/automation_event.h b/libs/ardour/ardour/automation_event.h
new file mode 100644
index 0000000000..18190aa9b6
--- /dev/null
+++ b/libs/ardour/ardour/automation_event.h
@@ -0,0 +1,309 @@
+/*
+ Copyright (C) 2002 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_automation_event_h__
+#define __ardour_automation_event_h__
+
+#include <stdint.h>
+#include <list>
+#include <cmath>
+
+#include <sigc++/signal.h>
+#include <glibmm/thread.h>
+
+#include <boost/pool/pool.hpp>
+#include <boost/pool/pool_alloc.hpp>
+
+#include <pbd/undo.h>
+#include <pbd/xml++.h>
+#include <pbd/statefuldestructible.h>
+
+#include <ardour/ardour.h>
+#include <ardour/parameter.h>
+
+namespace ARDOUR {
+
+class Curve;
+
+struct ControlEvent {
+
+ ControlEvent (double w, double v)
+ : when (w), value (v), coeff (0) {
+ }
+
+ ControlEvent (const ControlEvent& other)
+ : when (other.when), value (other.value), coeff (0) {
+ if (other.coeff) {
+ create_coeffs();
+ for (size_t i=0; i < 4; ++i)
+ coeff[i] = other.coeff[i];
+ }
+ }
+
+ ~ControlEvent() { if (coeff) delete[] coeff; }
+
+ void create_coeffs() {
+ if (!coeff)
+ coeff = new double[4];
+
+ coeff[0] = coeff[1] = coeff[2] = coeff[3] = 0.0;
+ }
+
+ double when;
+ double value;
+ double* coeff; ///< double[4] allocated by Curve as needed
+};
+
+/* automation lists use a pool allocator that does not use a lock and
+ allocates 8k of new pointers at a time
+*/
+
+typedef boost::fast_pool_allocator<ControlEvent*,
+ boost::default_user_allocator_new_delete,
+ boost::details::pool::null_mutex,
+ 8192> ControlEventAllocator;
+
+class AutomationList : public PBD::StatefulDestructible
+{
+ public:
+ typedef std::list<ControlEvent*,ControlEventAllocator> EventList;
+ typedef EventList::iterator iterator;
+ typedef EventList::reverse_iterator reverse_iterator;
+ typedef EventList::const_iterator const_iterator;
+
+ AutomationList (Parameter id, double min_val, double max_val, double default_val);
+ AutomationList (const XMLNode&, Parameter id);
+ ~AutomationList();
+
+ AutomationList (const AutomationList&);
+ AutomationList (const AutomationList&, double start, double end);
+ AutomationList& operator= (const AutomationList&);
+ bool operator== (const AutomationList&);
+
+ const Parameter& parameter() const { return _parameter; }
+ void set_parameter(Parameter p) { _parameter = p; }
+
+ void freeze();
+ void thaw ();
+
+ EventList::size_type size() const { return _events.size(); }
+ bool empty() const { return _events.empty(); }
+
+ void reset_default (double val) {
+ _default_value = val;
+ }
+
+ void clear ();
+ void x_scale (double factor);
+ bool extend_to (double);
+ void slide (iterator before, double distance);
+
+ void reposition_for_rt_add (double when);
+ void rt_add (double when, double value);
+ void add (double when, double value);
+ /* this should be private but old-school automation loading needs it in IO/IOProcessor */
+ void fast_simple_add (double when, double value);
+
+ void reset_range (double start, double end);
+ void erase_range (double start, double end);
+ void erase (iterator);
+ void erase (iterator, iterator);
+ void move_range (iterator start, iterator end, double, double);
+ void modify (iterator, double, double);
+
+ AutomationList* cut (double, double);
+ AutomationList* copy (double, double);
+ void clear (double, double);
+
+ AutomationList* cut (iterator, iterator);
+ AutomationList* copy (iterator, iterator);
+ void clear (iterator, iterator);
+
+ bool paste (AutomationList&, double position, float times);
+
+ void set_automation_state (AutoState);
+ AutoState automation_state() const { return _state; }
+ sigc::signal<void> automation_style_changed;
+
+ void set_automation_style (AutoStyle m);
+ AutoStyle automation_style() const { return _style; }
+ sigc::signal<void> automation_state_changed;
+
+ bool automation_playback() const {
+ return (_state & Play) || ((_state & Touch) && !_touching);
+ }
+ bool automation_write () const {
+ return (_state & Write) || ((_state & Touch) && _touching);
+ }
+
+ void start_touch ();
+ void stop_touch ();
+ bool touching() const { return _touching; }
+
+ void set_yrange (double min, double max) {
+ _min_yval = min;
+ _max_yval = max;
+ }
+
+ double get_max_y() const { return _max_yval; }
+ double get_min_y() const { return _min_yval; }
+
+ void truncate_end (double length);
+ void truncate_start (double length);
+
+ iterator begin() { return _events.begin(); }
+ iterator end() { return _events.end(); }
+
+ ControlEvent* back() { return _events.back(); }
+ ControlEvent* front() { return _events.front(); }
+
+ const_iterator const_begin() const { return _events.begin(); }
+ const_iterator const_end() const { return _events.end(); }
+
+ std::pair<AutomationList::iterator,AutomationList::iterator> control_points_adjacent (double when);
+
+ template<class T> void apply_to_points (T& obj, void (T::*method)(const AutomationList&)) {
+ Glib::Mutex::Lock lm (_lock);
+ (obj.*method)(*this);
+ }
+
+ sigc::signal<void> StateChanged;
+
+ XMLNode& get_state(void);
+ int set_state (const XMLNode &s);
+ XMLNode& state (bool full);
+ XMLNode& serialize_events ();
+
+ void set_max_xval (double);
+ double get_max_xval() const { return _max_xval; }
+
+ double eval (double where) {
+ Glib::Mutex::Lock lm (_lock);
+ return unlocked_eval (where);
+ }
+
+ double rt_safe_eval (double where, bool& ok) {
+
+ Glib::Mutex::Lock lm (_lock, Glib::TRY_LOCK);
+
+ if ((ok = lm.locked())) {
+ return unlocked_eval (where);
+ } else {
+ return 0.0;
+ }
+ }
+
+ static inline bool time_comparator (const ControlEvent* a, const ControlEvent* b) {
+ return a->when < b->when;
+ }
+
+ /** Lookup cache for eval functions, range contains equivalent values */
+ struct LookupCache {
+ LookupCache() : left(-1) {}
+ double left; /* leftmost x coordinate used when finding "range" */
+ std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
+ };
+
+ /** Lookup cache for point finding, range contains points between left and right */
+ struct SearchCache {
+ SearchCache() : left(-1), right(-1) {}
+ double left; /* leftmost x coordinate used when finding "range" */
+ double right; /* rightmost x coordinate used when finding "range" */
+ std::pair<AutomationList::const_iterator,AutomationList::const_iterator> range;
+ };
+
+ static sigc::signal<void, AutomationList*> AutomationListCreated;
+
+ const EventList& events() const { return _events; }
+ double default_value() const { return _default_value; }
+
+ // teeny const violations for Curve
+ mutable sigc::signal<void> Dirty;
+ Glib::Mutex& lock() const { return _lock; }
+ LookupCache& lookup_cache() const { return _lookup_cache; }
+ SearchCache& search_cache() const { return _search_cache; }
+
+ /** Called by locked entry point and various private
+ * locations where we already hold the lock.
+ *
+ * FIXME: Should this be private? Curve needs it..
+ */
+ double unlocked_eval (double x) const;
+
+ bool rt_safe_earliest_event (double start, double end, double& x, double& y, bool start_inclusive=false) const;
+ bool rt_safe_earliest_event_unlocked (double start, double end, double& x, double& y, bool start_inclusive=false) const;
+
+ Curve& curve() { return *_curve; }
+ const Curve& curve() const { return *_curve; }
+
+ enum InterpolationStyle {
+ Discrete,
+ Linear,
+ Curved
+ };
+
+ InterpolationStyle interpolation() const { return _interpolation; }
+ void set_interpolation(InterpolationStyle style) { _interpolation = style; }
+
+ private:
+
+ /** Called by unlocked_eval() to handle cases of 3 or more control points.
+ */
+ double multipoint_eval (double x) const;
+
+ void build_search_cache_if_necessary(double start, double end) const;
+
+ bool rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
+ bool rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const;
+
+ AutomationList* cut_copy_clear (double, double, int op);
+
+ int deserialize_events (const XMLNode&);
+
+ void maybe_signal_changed ();
+ void mark_dirty ();
+ void _x_scale (double factor);
+
+ mutable LookupCache _lookup_cache;
+ mutable SearchCache _search_cache;
+
+ Parameter _parameter;
+ InterpolationStyle _interpolation;
+ EventList _events;
+ mutable Glib::Mutex _lock;
+ int8_t _frozen;
+ bool _changed_when_thawed;
+ AutoState _state;
+ AutoStyle _style;
+ bool _touching;
+ bool _new_touch;
+ double _max_xval;
+ double _min_yval;
+ double _max_yval;
+ double _default_value;
+ bool _sort_pending;
+ iterator _rt_insertion_point;
+ double _rt_pos;
+
+ Curve* _curve;
+};
+
+} // namespace
+
+#endif /* __ardour_automation_event_h__ */
diff --git a/libs/ardour/ardour/base_audio_port.h b/libs/ardour/ardour/base_audio_port.h
new file mode 100644
index 0000000000..791928fa03
--- /dev/null
+++ b/libs/ardour/ardour/base_audio_port.h
@@ -0,0 +1,106 @@
+/*
+ Copyright (C) 2002 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.
+
+ $Id: port.h 712 2006-07-28 01:08:57Z drobilla $
+*/
+
+#ifndef __ardour_base_audio_port_h__
+#define __ardour_base_audio_port_h__
+
+#include <string>
+
+#include <sigc++/signal.h>
+
+#include <pbd/failed_constructor.h>
+
+#include <ardour/ardour.h>
+#include <ardour/port.h>
+#include <ardour/audio_buffer.h>
+
+namespace ARDOUR {
+
+class AudioEngine;
+
+class BaseAudioPort : public virtual Port {
+ public:
+ virtual ~BaseAudioPort();
+
+ DataType type() const { return DataType::AUDIO; }
+
+ virtual Buffer& get_buffer () {
+ assert (_buffer);
+ return *_buffer;
+ }
+
+ virtual AudioBuffer& get_audio_buffer() {
+ assert (_buffer);
+ return *_buffer;
+ }
+
+ void reset ();
+
+ void reset_overs () {
+ /* XXX NOT THREAD SAFE */
+ _short_overs = 0;
+ _long_overs = 0;
+ _overlen = 0;
+ }
+
+ void reset_peak_meter () {
+ /* XXX NOT THREAD SAFE */
+ _peak = 0;
+ }
+
+ void reset_meters () {
+ /* XXX NOT THREAD SAFE */
+ reset_peak_meter ();
+ reset_overs ();
+ }
+
+ float peak_db() const { return _peak_db; }
+ Sample peak() const { return _peak; }
+
+ uint32_t short_overs () const { return _short_overs; }
+ uint32_t long_overs () const { return _long_overs; }
+
+ static void set_short_over_length (nframes_t);
+ static void set_long_over_length (nframes_t);
+
+ void set_mixdown_function (void (*func)(const std::set<Port*>&, AudioBuffer*, nframes_t, nframes_t, bool));
+
+ protected:
+ BaseAudioPort (const std::string& name, Flags flags);
+
+ AudioBuffer* _buffer;
+ nframes_t _overlen;
+ Sample _peak;
+ float _peak_db;
+ uint32_t _short_overs;
+ uint32_t _long_overs;
+ bool _own_buffer;
+
+ void (*_mixdown)(const std::set<Port*>&, AudioBuffer*, nframes_t, nframes_t, bool);
+
+ static void default_mixdown (const std::set<Port*>&, AudioBuffer*, nframes_t, nframes_t, bool);
+
+ static nframes_t _long_over_length;
+ static nframes_t _short_over_length;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_base_audio_port_h__ */
diff --git a/libs/ardour/ardour/base_midi_port.h b/libs/ardour/ardour/base_midi_port.h
new file mode 100644
index 0000000000..1c9a69d48d
--- /dev/null
+++ b/libs/ardour/ardour/base_midi_port.h
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2002 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.
+
+ $Id: port.h 712 2006-07-28 01:08:57Z drobilla $
+*/
+
+#ifndef __ardour_base_midi_port_h__
+#define __ardour_base_midi_port_h__
+
+#include <sigc++/signal.h>
+#include <pbd/failed_constructor.h>
+#include <ardour/ardour.h>
+#include <ardour/port.h>
+#include <ardour/midi_buffer.h>
+
+namespace ARDOUR {
+
+class MidiEngine;
+
+class BaseMidiPort : public virtual Port {
+ public:
+ virtual ~BaseMidiPort();
+
+ DataType type() const { return DataType::MIDI; }
+
+ Buffer& get_buffer() {
+ assert (_buffer);
+ return *_buffer;
+ }
+
+ MidiBuffer& get_midi_buffer() {
+ assert (_buffer);
+ return *_buffer;
+ }
+
+ size_t capacity() { return _buffer->capacity(); }
+ size_t size() { return _buffer->size(); }
+
+ void set_mixdown_function (void (*func)(const std::set<Port*>&, MidiBuffer*, nframes_t, nframes_t, bool));
+
+ protected:
+ BaseMidiPort (const std::string& name, Flags);
+
+ MidiBuffer* _buffer;
+ bool _own_buffer;
+
+ void (*_mixdown)(const std::set<Port*>&, MidiBuffer*, nframes_t, nframes_t, bool);
+ static void default_mixdown (const std::set<Port*>&, MidiBuffer*, nframes_t, nframes_t, bool);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_base_midi_port_h__ */
diff --git a/libs/ardour/ardour/buffer.h b/libs/ardour/ardour/buffer.h
new file mode 100644
index 0000000000..fd94360226
--- /dev/null
+++ b/libs/ardour/ardour/buffer.h
@@ -0,0 +1,92 @@
+/*
+ Copyright (C) 2006 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_buffer_h__
+#define __ardour_buffer_h__
+
+#include <cstdlib>
+#include <cassert>
+#include <iostream>
+#include <boost/utility.hpp>
+#include <ardour/types.h>
+#include <ardour/data_type.h>
+#include <ardour/runtime_functions.h>
+
+namespace ARDOUR {
+
+
+/** A buffer of recordable/playable data.
+ *
+ * This is a datatype-agnostic base class for all buffers (there are no
+ * methods to actually access the data). This provides a way for code that
+ * doesn't care about the data type to still deal with buffers (which is
+ * why the base class can't be a template).
+ *
+ * To actually read/write buffer contents, use the appropriate derived class.
+ */
+class Buffer : public boost::noncopyable
+{
+public:
+ virtual ~Buffer() {}
+
+ /** Factory function */
+ static Buffer* create(DataType type, size_t capacity);
+
+ /** Maximum capacity of buffer.
+ * Note in some cases the entire buffer may not contain valid data, use size. */
+ size_t capacity() const { return _capacity; }
+
+ /** Amount of valid data in buffer. Use this over capacity almost always. */
+ size_t size() const { return _size; }
+
+ /** Type of this buffer.
+ * Based on this you can static cast a Buffer* to the desired type. */
+ DataType type() const { return _type; }
+
+ bool silent() const { return _silent; }
+
+ /** Reallocate the buffer used internally to handle at least @a size_t units of data.
+ *
+ * The buffer is not silent after this operation. the @a capacity argument
+ * passed to the constructor must have been non-zero.
+ */
+ virtual void resize(size_t) = 0;
+
+ /** Clear (eg zero, or empty) buffer starting at TIME @a offset */
+ virtual void silence(nframes_t len, nframes_t offset=0) = 0;
+
+ /** Clear the entire buffer */
+ virtual void clear() { silence(_capacity, 0); }
+
+ virtual void read_from(const Buffer& src, nframes_t offset, nframes_t len) = 0;
+
+protected:
+ Buffer(DataType type, size_t capacity)
+ : _type(type), _capacity(capacity), _size(0), _silent(true)
+ {}
+
+ DataType _type;
+ size_t _capacity;
+ size_t _size;
+ bool _silent;
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_buffer_h__
diff --git a/libs/ardour/ardour/buffer_set.h b/libs/ardour/ardour/buffer_set.h
new file mode 100644
index 0000000000..c750615798
--- /dev/null
+++ b/libs/ardour/ardour/buffer_set.h
@@ -0,0 +1,159 @@
+/*
+ Copyright (C) 2006 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_buffer_set_h__
+#define __ardour_buffer_set_h__
+
+#include <cassert>
+#include <vector>
+#include <ardour/chan_count.h>
+#include <ardour/data_type.h>
+
+namespace ARDOUR {
+
+class Buffer;
+class AudioBuffer;
+class MidiBuffer;
+class PortSet;
+
+
+/** A set of buffers of various types.
+ *
+ * These are mainly accessed from Session and passed around as scratch buffers
+ * (eg as parameters to run() methods) to do in-place signal processing.
+ *
+ * There are two types of counts associated with a BufferSet - available,
+ * and the 'use count'. Available is the actual number of allocated buffers
+ * (and so is the maximum acceptable value for the use counts).
+ *
+ * The use counts are how things determine the form of their input and inform
+ * others the form of their output (eg what they did to the BufferSet).
+ * Setting the use counts is realtime safe.
+ */
+class BufferSet
+{
+public:
+ BufferSet();
+ ~BufferSet();
+
+ void clear();
+
+ void attach_buffers(PortSet& ports);
+
+ void ensure_buffers(const ChanCount& count, size_t buffer_capacity);
+ void ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capacity);
+
+ const ChanCount& available() const { return _available; }
+ ChanCount& available() { return _available; }
+
+ const ChanCount& count() const { return _count; }
+ ChanCount& count() { return _count; }
+
+ void set_count(const ChanCount& count) { _count = count; }
+
+ size_t buffer_capacity(DataType type) const;
+
+ Buffer& get(DataType type, size_t i)
+ {
+ assert(i <= _count.get(type));
+ return *_buffers[type][i];
+ }
+
+ AudioBuffer& get_audio(size_t i)
+ {
+ return (AudioBuffer&)get(DataType::AUDIO, i);
+ }
+
+ MidiBuffer& get_midi(size_t i)
+ {
+ return (MidiBuffer&)get(DataType::MIDI, i);
+ }
+
+ void read_from(BufferSet& in, jack_nframes_t nframes, jack_nframes_t offset=0);
+
+ // ITERATORS
+
+ // FIXME: this is a filthy copy-and-paste mess
+ // FIXME: litter these with assertions
+
+ class audio_iterator {
+ public:
+
+ AudioBuffer& operator*() { return _set.get_audio(_index); }
+ AudioBuffer* operator->() { return &_set.get_audio(_index); }
+ audio_iterator& operator++() { ++_index; return *this; } // yes, prefix only
+ bool operator==(const audio_iterator& other) { return (_index == other._index); }
+ bool operator!=(const audio_iterator& other) { return (_index != other._index); }
+
+ private:
+ friend class BufferSet;
+
+ audio_iterator(BufferSet& list, size_t index) : _set(list), _index(index) {}
+
+ BufferSet& _set;
+ size_t _index;
+ };
+
+ audio_iterator audio_begin() { return audio_iterator(*this, 0); }
+ audio_iterator audio_end() { return audio_iterator(*this, _count.n_audio()); }
+
+ class iterator {
+ public:
+
+ Buffer& operator*() { return _set.get(_type, _index); }
+ Buffer* operator->() { return &_set.get(_type, _index); }
+ iterator& operator++() { ++_index; return *this; } // yes, prefix only
+ bool operator==(const iterator& other) { return (_index == other._index); }
+ bool operator!=(const iterator& other) { return (_index != other._index); }
+ iterator operator=(const iterator& other) { _set = other._set; _type = other._type; _index = other._index; return *this; }
+
+ private:
+ friend class BufferSet;
+
+ iterator(BufferSet& list, DataType type, size_t index)
+ : _set(list), _type(type), _index(index) {}
+
+ BufferSet& _set;
+ DataType _type;
+ size_t _index;
+ };
+
+ iterator begin(DataType type) { return iterator(*this, type, 0); }
+ iterator end(DataType type) { return iterator(*this, type, _count.get(type)); }
+
+
+private:
+ typedef std::vector<Buffer*> BufferVec;
+
+ /// Vector of vectors, indexed by DataType
+ std::vector<BufferVec> _buffers;
+
+ /// Use counts (there may be more actual buffers than this)
+ ChanCount _count;
+
+ /// Available counts (number of buffers actually allocated)
+ ChanCount _available;
+
+ /// Whether we (don't) 'own' the contained buffers (otherwise we mirror a PortSet)
+ bool _is_mirror;
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_buffer_set_h__
diff --git a/libs/ardour/ardour/bundle.h b/libs/ardour/ardour/bundle.h
new file mode 100644
index 0000000000..ba92063b30
--- /dev/null
+++ b/libs/ardour/ardour/bundle.h
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 2002-2007 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_bundle_h__
+#define __ardour_bundle_h__
+
+#include <string>
+#include <sigc++/signal.h>
+#include "ardour/data_type.h"
+
+namespace ARDOUR {
+
+typedef std::vector<std::string> PortList;
+
+/**
+ * A set of `channels', each of which is associated with 0 or more JACK ports.
+ */
+
+class Bundle {
+ public:
+ Bundle () : _type (DataType::AUDIO) {}
+ Bundle (bool i) : _type (DataType::AUDIO), _ports_are_inputs (i) {}
+ Bundle (std::string const & n, bool i = true) : _name (n), _type (DataType::AUDIO), _ports_are_inputs (i) {}
+ virtual ~Bundle() {}
+
+ /**
+ * @return Number of channels that this Bundle has.
+ */
+ virtual uint32_t nchannels () const = 0;
+ virtual const PortList& channel_ports (uint32_t) const = 0;
+
+ void set_name (std::string const & n) {
+ _name = n;
+ NameChanged ();
+ }
+
+ std::string name () const { return _name; }
+
+ sigc::signal<void> NameChanged;
+
+ void set_type (DataType t) { _type = t; }
+ DataType type () const { return _type; }
+
+ void set_ports_are_inputs () { _ports_are_inputs = true; }
+ void set_ports_are_outputs () { _ports_are_inputs = false; }
+ bool ports_are_inputs () const { return _ports_are_inputs; }
+ bool ports_are_outputs () const { return !_ports_are_inputs; }
+
+ private:
+ std::string _name;
+ ARDOUR::DataType _type;
+ bool _ports_are_inputs;
+};
+
+}
+
+#endif /* __ardour_bundle_h__ */
diff --git a/libs/ardour/ardour/caimportable.h b/libs/ardour/ardour/caimportable.h
new file mode 100644
index 0000000000..dc7f5769ae
--- /dev/null
+++ b/libs/ardour/ardour/caimportable.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2007 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_ca_importable_source_h__
+#define __ardour_ca_importable_source_h__
+
+#include <pbd/failed_constructor.h>
+#include <ardour/types.h>
+#include <ardour/importable_source.h>
+
+#include <appleutility/CAAudioFile.h>
+
+namespace ARDOUR {
+
+class CAImportableSource : public ImportableSource {
+ public:
+ CAImportableSource (const std::string& path);
+ virtual ~CAImportableSource();
+
+ nframes_t read (Sample* buffer, nframes_t nframes);
+ uint32_t channels() const;
+ nframes_t length() const;
+ nframes_t samplerate() const;
+ void seek (nframes_t pos);
+
+ protected:
+ mutable CAAudioFile af;
+};
+
+}
+
+#endif /* __ardour_ca_importable_source_h__ */
diff --git a/libs/ardour/ardour/chan_count.h b/libs/ardour/ardour/chan_count.h
new file mode 100644
index 0000000000..986ef76e25
--- /dev/null
+++ b/libs/ardour/ardour/chan_count.h
@@ -0,0 +1,126 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Author: Dave Robillard
+
+ 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_chan_count_h__
+#define __ardour_chan_count_h__
+
+#include <ardour/data_type.h>
+#include <cassert>
+
+namespace ARDOUR {
+
+
+/** A count of channels, possibly with many types.
+ *
+ * Operators are defined so this may safely be used as if it were a simple
+ * (single-typed) integer count of channels.
+ */
+class ChanCount {
+public:
+ ChanCount() { reset(); }
+
+ // Convenience constructor for making single-typed streams (stereo, mono, etc)
+ ChanCount(DataType type, uint32_t channels)
+ {
+ reset();
+ set(type, channels);
+ }
+
+ void reset()
+ {
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ _counts[*t] = 0;
+ }
+ }
+
+ void set(DataType t, uint32_t count) { assert(t != DataType::NIL); _counts[t] = count; }
+ uint32_t get(DataType t) const { assert(t != DataType::NIL); return _counts[t]; }
+
+ inline uint32_t n_audio() const { return _counts[DataType::AUDIO]; }
+ inline void set_audio(uint32_t a) { _counts[DataType::AUDIO] = a; }
+
+ inline uint32_t n_midi() const { return _counts[DataType::MIDI]; }
+ inline void set_midi(uint32_t m) { _counts[DataType::MIDI] = m; }
+
+ uint32_t n_total() const
+ {
+ uint32_t ret = 0;
+ for (uint32_t i=0; i < DataType::num_types; ++i)
+ ret += _counts[i];
+
+ return ret;
+ }
+
+ bool operator==(const ChanCount& other) const
+ {
+ for (uint32_t i=0; i < DataType::num_types; ++i)
+ if (_counts[i] != other._counts[i])
+ return false;
+
+ return true;
+ }
+
+ bool operator!=(const ChanCount& other) const
+ {
+ return ! (*this == other);
+ }
+
+ bool operator<(const ChanCount& other) const
+ {
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ if (_counts[*t] > other._counts[*t]) {
+ return false;
+ }
+ }
+ return (*this != other);
+ }
+
+ bool operator<=(const ChanCount& other) const
+ {
+ return ( (*this < other) || (*this == other) );
+ }
+
+ bool operator>(const ChanCount& other) const
+ {
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ if (_counts[*t] < other._counts[*t]) {
+ return false;
+ }
+ }
+ return (*this != other);
+ }
+
+ bool operator>=(const ChanCount& other) const
+ {
+ return ( (*this > other) || (*this == other) );
+ }
+
+ static const ChanCount INFINITE;
+ static const ChanCount ZERO;
+
+private:
+
+ uint32_t _counts[DataType::num_types];
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_chan_count_h__
+
diff --git a/libs/ardour/ardour/click.h b/libs/ardour/ardour/click.h
new file mode 100644
index 0000000000..60499b98da
--- /dev/null
+++ b/libs/ardour/ardour/click.h
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2004 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_click_h__
+#define __ardour_click_h__
+
+#include <ardour/io.h>
+
+namespace ARDOUR {
+
+class ClickIO : public IO
+{
+ public:
+ ClickIO (Session& s, const string& name,
+
+ int input_min = -1, int input_max = -1,
+
+ int output_min = -1, int output_max = -1)
+ : IO (s, name, input_min, input_max, output_min, output_max) {}
+
+ ~ClickIO() {}
+
+ protected:
+ uint32_t pans_required () const { return 1; }
+};
+
+}; /* namespace ARDOUR */
+
+#endif /*__ardour_click_h__ */
diff --git a/libs/ardour/ardour/configuration.h b/libs/ardour/ardour/configuration.h
new file mode 100644
index 0000000000..7b890500d8
--- /dev/null
+++ b/libs/ardour/ardour/configuration.h
@@ -0,0 +1,106 @@
+/*
+ Copyright (C) 1999 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_configuration_h__
+#define __ardour_configuration_h__
+
+#include <map>
+#include <vector>
+
+#include <sys/types.h>
+#include <string>
+
+#include <pbd/stateful.h>
+
+#include <ardour/types.h>
+#include <ardour/utils.h>
+#include <ardour/configuration_variable.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Configuration : public PBD::Stateful
+{
+ public:
+ Configuration();
+ virtual ~Configuration();
+
+ std::map<std::string,XMLNode> midi_ports;
+
+ void map_parameters (sigc::slot<void,const char*> theSlot);
+
+ int load_state ();
+ int save_state ();
+
+ /// calls Stateful::*instant_xml methods using
+ /// ARDOUR::user_config_directory for the directory argument
+ void add_instant_xml (XMLNode&);
+ XMLNode * instant_xml (const std::string& str);
+
+ int set_state (const XMLNode&);
+ XMLNode& get_state (void);
+ XMLNode& get_variables (sigc::slot<bool,ConfigVariableBase::Owner>, std::string which_node = "Config");
+ void set_variables (const XMLNode&, ConfigVariableBase::Owner owner);
+
+ void set_current_owner (ConfigVariableBase::Owner);
+
+ XMLNode* control_protocol_state () { return _control_protocol_state; }
+
+ sigc::signal<void,const char*> ParameterChanged;
+
+ /* define accessor methods */
+
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+#define CONFIG_VARIABLE(Type,var,name,value) \
+ Type get_##var () const { return var.get(); } \
+ bool set_##var (Type val) { bool ret = var.set (val, current_owner); if (ret) { ParameterChanged (name); } return ret; }
+#define CONFIG_VARIABLE_SPECIAL(Type,var,name,value,mutator) \
+ Type get_##var () const { return var.get(); } \
+ bool set_##var (Type val) { bool ret = var.set (val, current_owner); if (ret) { ParameterChanged (name); } return ret; }
+#include "ardour/configuration_vars.h"
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+
+ private:
+
+ /* declare variables */
+
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+#define CONFIG_VARIABLE(Type,var,name,value) ConfigVariable<Type> var;
+#define CONFIG_VARIABLE_SPECIAL(Type,var,name,value,mutator) ConfigVariableWithMutation<Type> var;
+#include "ardour/configuration_vars.h"
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+
+ ConfigVariableBase::Owner current_owner;
+ XMLNode* _control_protocol_state;
+
+ XMLNode& state (sigc::slot<bool,ConfigVariableBase::Owner>);
+ bool save_config_options_predicate (ConfigVariableBase::Owner owner);
+};
+
+extern Configuration *Config;
+extern gain_t speed_quietning; /* see comment in configuration.cc */
+
+} // namespace ARDOUR
+
+#endif /* __ardour_configuration_h__ */
diff --git a/libs/ardour/ardour/configuration_variable.h b/libs/ardour/ardour/configuration_variable.h
new file mode 100644
index 0000000000..a61283ecd0
--- /dev/null
+++ b/libs/ardour/ardour/configuration_variable.h
@@ -0,0 +1,184 @@
+/*
+ Copyright (C) 2000-2007 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_configuration_variable_h__
+#define __ardour_configuration_variable_h__
+
+#include <sstream>
+#include <ostream>
+#include <iostream>
+
+#include <pbd/xml++.h>
+
+namespace ARDOUR {
+
+class ConfigVariableBase {
+ public:
+ enum Owner {
+ Default = 0x1,
+ System = 0x2,
+ Config = 0x4,
+ Session = 0x8,
+ Interface = 0x10
+ };
+
+ ConfigVariableBase (std::string str) : _name (str), _owner (Default) {}
+ virtual ~ConfigVariableBase() {}
+
+ std::string name() const { return _name; }
+ Owner owner() const { return _owner; }
+
+ virtual void add_to_node (XMLNode& node) = 0;
+ virtual bool set_from_node (const XMLNode& node, Owner owner) = 0;
+
+ void show_stored_value (const std::string&);
+ static void set_show_stored_values (bool yn);
+
+ protected:
+ std::string _name;
+ Owner _owner;
+ static bool show_stores;
+
+ void notify ();
+ void miss ();
+};
+
+template<class T>
+class ConfigVariable : public ConfigVariableBase
+{
+ public:
+ ConfigVariable (std::string str) : ConfigVariableBase (str) {}
+ ConfigVariable (std::string str, T val) : ConfigVariableBase (str), value (val) {}
+
+ virtual bool set (T val, Owner owner = ARDOUR::ConfigVariableBase::Config) {
+ if (val == value) {
+ miss ();
+ return false;
+ }
+ value = val;
+ _owner = (ConfigVariableBase::Owner)(_owner |owner);
+ notify ();
+ return true;
+ }
+
+ T get() const {
+ return value;
+ }
+
+ void add_to_node (XMLNode& node) {
+ std::stringstream ss;
+ ss << value;
+ show_stored_value (ss.str());
+ XMLNode* child = new XMLNode ("Option");
+ child->add_property ("name", _name);
+ child->add_property ("value", ss.str());
+ node.add_child_nocopy (*child);
+ }
+
+ bool set_from_node (const XMLNode& node, Owner owner) {
+
+ if (node.name() == "Config") {
+
+ /* ardour.rc */
+
+ const XMLProperty* prop;
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLNode* child;
+
+ nlist = node.children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ child = *niter;
+
+ if (child->name() == "Option") {
+ if ((prop = child->property ("name")) != 0) {
+ if (prop->value() == _name) {
+ if ((prop = child->property ("value")) != 0) {
+ std::stringstream ss;
+ ss << prop->value();
+ ss >> value;
+ _owner = (ConfigVariableBase::Owner)(_owner |owner);
+ return true;
+ }
+ }
+ }
+ }
+ }
+
+ } else if (node.name() == "Options") {
+
+ /* session file */
+
+ XMLNodeList olist;
+ XMLNodeConstIterator oiter;
+ XMLNode* option;
+ const XMLProperty* opt_prop;
+
+ olist = node.children();
+
+ for (oiter = olist.begin(); oiter != olist.end(); ++oiter) {
+
+ option = *oiter;
+
+ if (option->name() == _name) {
+ if ((opt_prop = option->property ("val")) != 0) {
+ std::stringstream ss;
+ ss << opt_prop->value();
+ ss >> value;
+ _owner = (ConfigVariableBase::Owner)(_owner |owner);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ protected:
+ virtual T get_for_save() { return value; }
+ T value;
+};
+
+template<class T>
+class ConfigVariableWithMutation : public ConfigVariable<T>
+{
+ public:
+ ConfigVariableWithMutation (std::string name, T val, T (*m)(T))
+ : ConfigVariable<T> (name, val), mutator (m) {}
+
+ bool set (T val, ConfigVariableBase::Owner owner) {
+ if (unmutated_value != val) {
+ unmutated_value = val;
+ return ConfigVariable<T>::set (mutator (val), owner);
+ }
+ return false;
+ }
+
+ protected:
+ virtual T get_for_save() { return unmutated_value; }
+ T unmutated_value;
+ T (*mutator)(T);
+};
+
+}
+
+#endif /* __ardour_configuration_variable_h__ */
diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h
new file mode 100644
index 0000000000..4e2a14c07b
--- /dev/null
+++ b/libs/ardour/ardour/configuration_vars.h
@@ -0,0 +1,176 @@
+/*
+ Copyright (C) 2000-2007 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.
+
+*/
+
+/* IO connection */
+
+CONFIG_VARIABLE (AutoConnectOption, output_auto_connect, "output-auto-connect", AutoConnectOption (0))
+CONFIG_VARIABLE (AutoConnectOption, input_auto_connect, "input-auto-connect", AutoConnectOption (0))
+
+CONFIG_VARIABLE (std::string, auditioner_output_left, "auditioner-output-left", "default")
+CONFIG_VARIABLE (std::string, auditioner_output_right, "auditioner-output-right", "default")
+
+/* MIDI and MIDI related */
+
+CONFIG_VARIABLE (std::string, mtc_port_name, "mtc-port-name", "default")
+CONFIG_VARIABLE (std::string, mmc_port_name, "mmc-port-name", "default")
+CONFIG_VARIABLE (std::string, midi_port_name, "midi-port-name", "default")
+CONFIG_VARIABLE (bool, trace_midi_input, "trace-midi-input", false)
+CONFIG_VARIABLE (bool, trace_midi_output, "trace-midi-output", false)
+CONFIG_VARIABLE (bool, send_mtc, "send-mtc", false)
+CONFIG_VARIABLE (bool, send_mmc, "send-mmc", true)
+CONFIG_VARIABLE (bool, mmc_control, "mmc-control", true)
+CONFIG_VARIABLE (bool, midi_feedback, "midi-feedback", false)
+CONFIG_VARIABLE (uint8_t, mmc_receive_device_id, "mmc-receive-device-id", 0)
+CONFIG_VARIABLE (uint8_t, mmc_send_device_id, "mmc-send-device-id", 0)
+
+/* control surfaces */
+
+CONFIG_VARIABLE (uint32_t, feedback_interval_ms, "feedback-interval-ms", 100)
+CONFIG_VARIABLE (bool, use_tranzport, "use-tranzport", false)
+CONFIG_VARIABLE (std::string, mackie_emulation, "mackie-emulation", "mcu")
+CONFIG_VARIABLE (RemoteModel, remote_model, "remote-model", MixerOrdered)
+
+/* disk operations */
+
+CONFIG_VARIABLE (uint32_t, minimum_disk_io_bytes, "minimum-disk-io-bytes", 1024 * 256)
+CONFIG_VARIABLE (float, audio_track_buffer_seconds, "track-buffer-seconds", 5.0)
+CONFIG_VARIABLE (float, midi_track_buffer_seconds, "midi-track-buffer-seconds", 1.0)
+CONFIG_VARIABLE (uint32_t, disk_choice_space_threshold, "disk-choice-space-threshold", 57600000)
+CONFIG_VARIABLE (SampleFormat, native_file_data_format, "native-file-data-format", ARDOUR::FormatFloat)
+CONFIG_VARIABLE (HeaderFormat, native_file_header_format, "native-file-header-format", ARDOUR::WAVE)
+CONFIG_VARIABLE (bool, auto_analyse_audio, "auto-analyse-audio", false)
+
+/* OSC */
+
+CONFIG_VARIABLE (uint32_t, osc_port, "osc-port", 3819)
+CONFIG_VARIABLE (bool, use_osc, "use-osc", false)
+
+/* crossfades */
+
+CONFIG_VARIABLE (CrossfadeModel, xfade_model, "xfade-model", FullCrossfade)
+CONFIG_VARIABLE (bool, auto_xfade, "auto-xfade", true)
+CONFIG_VARIABLE (float, short_xfade_seconds, "short-xfade-seconds", 0.015)
+CONFIG_VARIABLE (bool, xfades_active, "xfades-active", true)
+CONFIG_VARIABLE (bool, xfades_visible, "xfades-visible", true)
+CONFIG_VARIABLE (uint32_t, destructive_xfade_msecs, "destructive-xfade-msecs", 2)
+
+/* editing related */
+
+CONFIG_VARIABLE (EditMode, edit_mode, "edit-mode", Slide)
+CONFIG_VARIABLE (LayerModel, layer_model, "layer-model", MoveAddHigher)
+CONFIG_VARIABLE (bool, link_region_and_track_selection, "link-region-and-track-selection", false)
+CONFIG_VARIABLE (std::string, keyboard_layout_name, "keyboard-layout-name", "ansi")
+
+/* monitoring, mute, solo etc */
+
+CONFIG_VARIABLE (bool, mute_affects_pre_fader, "mute-affects-pre-fader", true)
+CONFIG_VARIABLE (bool, mute_affects_post_fader, "mute-affects-post-fader", true)
+CONFIG_VARIABLE (bool, mute_affects_control_outs, "mute-affects-control-outs", true)
+CONFIG_VARIABLE (bool, mute_affects_main_outs, "mute-affects-main-outs", true)
+CONFIG_VARIABLE (MonitorModel, monitoring_model, "monitoring-model", ExternalMonitoring)
+CONFIG_VARIABLE (SoloModel, solo_model, "solo-model", InverseMute)
+CONFIG_VARIABLE (bool, solo_latched, "solo-latched", true)
+CONFIG_VARIABLE (bool, latched_record_enable, "latched-record-enable", false)
+CONFIG_VARIABLE (bool, all_safe, "all-safe", false)
+CONFIG_VARIABLE (bool, show_solo_mutes, "show-solo-mutes", false)
+CONFIG_VARIABLE (bool, tape_machine_mode, "tape-machine-mode", false)
+
+/* click */
+
+CONFIG_VARIABLE (bool, clicking, "clicking", false)
+CONFIG_VARIABLE (std::string, click_sound, "click-sound", "")
+CONFIG_VARIABLE (std::string, click_emphasis_sound, "click-emphasis-sound", "")
+
+/* transport control and related */
+
+CONFIG_VARIABLE (bool, auto_play, "auto-play", false)
+CONFIG_VARIABLE (bool, auto_return, "auto-return", false)
+CONFIG_VARIABLE (bool, auto_input, "auto-input", true)
+CONFIG_VARIABLE (bool, punch_in, "punch-in", false)
+CONFIG_VARIABLE (bool, punch_out, "punch-out", false)
+CONFIG_VARIABLE (bool, plugins_stop_with_transport, "plugins-stop-with-transport", false)
+CONFIG_VARIABLE (bool, do_not_record_plugins, "do-not-record-plugins", false)
+CONFIG_VARIABLE (bool, stop_recording_on_xrun, "stop-recording-on-xrun", false)
+CONFIG_VARIABLE (bool, create_xrun_marker, "create-xrun-marker", true)
+CONFIG_VARIABLE (bool, stop_at_session_end, "stop-at-session-end", true)
+CONFIG_VARIABLE (bool, seamless_loop, "seamless-loop", false)
+CONFIG_VARIABLE (nframes_t, preroll, "preroll", 0)
+CONFIG_VARIABLE (nframes_t, postroll, "postroll", 0)
+CONFIG_VARIABLE (float, rf_speed, "rf-speed", 2.0f)
+CONFIG_VARIABLE (float, shuttle_speed_factor, "shuttle-speed-factor", 1.0f)
+CONFIG_VARIABLE (float, shuttle_speed_threshold, "shuttle-speed-threshold", 5.0f)
+CONFIG_VARIABLE (SlaveSource, slave_source, "slave-source", None)
+CONFIG_VARIABLE (ShuttleBehaviour, shuttle_behaviour, "shuttle-behaviour", Sprung)
+CONFIG_VARIABLE (ShuttleUnits, shuttle_units, "shuttle-units", Percentage)
+CONFIG_VARIABLE (bool, quieten_at_speed, "quieten-at-speed", true)
+CONFIG_VARIABLE (bool, primary_clock_delta_edit_cursor, "primary-clock-delta-edit-cursor", false)
+CONFIG_VARIABLE (bool, secondary_clock_delta_edit_cursor, "secondary-clock-delta-edit-cursor", false)
+CONFIG_VARIABLE (bool, show_track_meters, "show-track-meters", true)
+
+/* timecode and sync */
+
+CONFIG_VARIABLE (bool, jack_time_master, "jack-time-master", true)
+CONFIG_VARIABLE (SmpteFormat, smpte_format, "smpte-format", smpte_30)
+CONFIG_VARIABLE (bool, use_video_sync, "use-video-sync", false)
+CONFIG_VARIABLE (bool, timecode_source_is_synced, "timecode-source-is-synced", true)
+CONFIG_VARIABLE (float, video_pullup, "video-pullup", 0.0f)
+
+/* metering */
+
+CONFIG_VARIABLE (float, meter_hold, "meter-hold", 100.0f)
+CONFIG_VARIABLE (float, meter_falloff, "meter-falloff", 27.0f)
+CONFIG_VARIABLE (nframes_t, over_length_short, "over-length-short", 2)
+CONFIG_VARIABLE (nframes_t, over_length_long, "over-length-long", 10)
+
+/* miscellany */
+
+CONFIG_VARIABLE (bool, hiding_groups_deactivates_groups, "hiding-groups-deactivates-groups", true)
+CONFIG_VARIABLE (bool, verify_remove_last_capture, "verify-remove-last-capture", true)
+CONFIG_VARIABLE (bool, no_new_session_dialog, "no-new-session-dialog", false)
+CONFIG_VARIABLE (bool, use_vst, "use-vst", true)
+CONFIG_VARIABLE (uint32_t, subframes_per_frame, "subframes-per-frame", 100)
+CONFIG_VARIABLE (bool, save_history, "save-history", true)
+CONFIG_VARIABLE (int32_t, saved_history_depth, "save-history-depth", 20)
+CONFIG_VARIABLE (int32_t, history_depth, "history-depth", 20)
+CONFIG_VARIABLE (bool, use_overlap_equivalency, "use-overlap-equivalency", false)
+CONFIG_VARIABLE (bool, periodic_safety_backups, "periodic-safety-backups", true)
+CONFIG_VARIABLE (uint32_t, periodic_safety_backup_interval, "periodic-safety-backup-interval", 120)
+CONFIG_VARIABLE (float, automation_interval, "automation-interval", 50)
+CONFIG_VARIABLE (bool, sync_all_route_ordering, "sync-all-route-ordering", true)
+CONFIG_VARIABLE (bool, only_copy_imported_files, "only-copy-imported-files", true)
+CONFIG_VARIABLE (std::string, keyboard_layout, "keyboard-layout", "ansi")
+CONFIG_VARIABLE (std::string, default_bindings, "default-bindings", "ardour")
+CONFIG_VARIABLE (bool, default_narrow_ms, "default-narrow_ms", false)
+CONFIG_VARIABLE (bool, rubberbanding_snaps_to_grid, "rubberbanding-snaps-to-grid", false)
+CONFIG_VARIABLE (long, font_scale, "font-scale", 102400)
+
+/* denormal management */
+
+CONFIG_VARIABLE (bool, denormal_protection, "denormal-protection", false)
+CONFIG_VARIABLE (DenormalModel, denormal_model, "denormal-model", DenormalNone)
+
+/* BWAV */
+
+CONFIG_VARIABLE (string, bwf_country_code, "bwf-country-code", "US")
+CONFIG_VARIABLE (string, bwf_organization_code, "bwf-organization-code", "US")
+
+/* these variables have custom set() methods (e.g. path globbing) */
+
+CONFIG_VARIABLE_SPECIAL(Glib::ustring, raid_path, "raid-path", "", path_expand)
+
diff --git a/libs/ardour/ardour/control_protocol_manager.h b/libs/ardour/ardour/control_protocol_manager.h
new file mode 100644
index 0000000000..c61513e117
--- /dev/null
+++ b/libs/ardour/ardour/control_protocol_manager.h
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2000-2007 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_control_protocol_manager_h
+#define ardour_control_protocol_manager_h
+
+#include <string>
+#include <list>
+
+#include <sigc++/sigc++.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/stateful.h>
+
+namespace ARDOUR {
+
+class ControlProtocol;
+class ControlProtocolDescriptor;
+class Session;
+
+struct ControlProtocolInfo {
+ ControlProtocolDescriptor* descriptor;
+ ControlProtocol* protocol;
+ std::string name;
+ std::string path;
+ bool requested;
+ bool mandatory;
+ bool supports_feedback;
+ XMLNode* state;
+
+ ControlProtocolInfo() : descriptor (0), protocol (0), state (0) {}
+ ~ControlProtocolInfo() { if (state) { delete state; } }
+};
+
+ class ControlProtocolManager : public sigc::trackable, public PBD::Stateful
+{
+ public:
+ ControlProtocolManager ();
+ ~ControlProtocolManager ();
+
+ static ControlProtocolManager& instance() { return *_instance; }
+
+ void set_session (Session&);
+ void discover_control_protocols ();
+ void foreach_known_protocol (sigc::slot<void,const ControlProtocolInfo*>);
+ void load_mandatory_protocols ();
+
+ ControlProtocol* instantiate (ControlProtocolInfo&);
+ int teardown (ControlProtocolInfo&);
+
+ std::list<ControlProtocolInfo*> control_protocol_info;
+
+ static const std::string state_node_name;
+
+ void set_protocol_states (const XMLNode&);
+
+ int set_state (const XMLNode&);
+ XMLNode& get_state (void);
+
+ private:
+ static ControlProtocolManager* _instance;
+
+ Session* _session;
+ Glib::Mutex protocols_lock;
+ std::list<ControlProtocol*> control_protocols;
+
+ void drop_session ();
+
+ int control_protocol_discover (std::string path);
+ ControlProtocolDescriptor* get_descriptor (std::string path);
+ ControlProtocolInfo* cpi_by_name (std::string);
+};
+
+} // namespace
+
+#endif // ardour_control_protocol_manager_h
diff --git a/libs/ardour/ardour/control_protocol_search_path.h b/libs/ardour/ardour/control_protocol_search_path.h
new file mode 100644
index 0000000000..f9a8103c0c
--- /dev/null
+++ b/libs/ardour/ardour/control_protocol_search_path.h
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ 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_CONTROL_PROTOCOL_SEARCH_PATH_INCLUDED
+#define ARDOUR_CONTROL_PROTOCOL_SEARCH_PATH_INCLUDED
+
+#include <pbd/search_path.h>
+
+namespace ARDOUR {
+
+ using PBD::SearchPath;
+
+ /**
+ * return a SearchPath containing directories in which to look for
+ * control surface plugins.
+ *
+ * If ARDOUR_SURFACES_PATH is defined then the SearchPath returned
+ * will contain only those directories specified in it, otherwise it will
+ * contain the user and system directories which may contain control
+ * surface plugins.
+ */
+ SearchPath control_protocol_search_path ();
+
+} // namespace ARDOUR
+
+#endif
diff --git a/libs/ardour/ardour/coreaudiosource.h b/libs/ardour/ardour/coreaudiosource.h
new file mode 100644
index 0000000000..d7282b35bd
--- /dev/null
+++ b/libs/ardour/ardour/coreaudiosource.h
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2000 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 __coreaudio_source_h__
+#define __coreaudio_source_h__
+
+#include <appleutility/CAAudioFile.h>
+#include <ardour/audiofilesource.h>
+
+namespace ARDOUR {
+
+class CoreAudioSource : public AudioFileSource {
+ public:
+ CoreAudioSource (ARDOUR::Session&, const XMLNode&);
+ CoreAudioSource (ARDOUR::Session&, const string& path, int chn, Flag);
+ ~CoreAudioSource ();
+
+ float sample_rate() const;
+ int update_header (nframes_t when, struct tm&, time_t);
+
+ int flush_header () {return 0;};
+ void set_header_timeline_position () {};
+
+ static int get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg);
+
+ protected:
+ nframes_t read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const;
+ nframes_t write_unlocked (Sample *dst, nframes_t cnt) { return 0; }
+
+ private:
+ mutable CAAudioFile af;
+ uint16_t n_channels;
+
+ void init ();
+ int safe_read (Sample*, nframes_t start, nframes_t cnt, AudioBufferList&) const;
+};
+
+}; /* namespace ARDOUR */
+
+#endif /* __coreaudio_source_h__ */
+
diff --git a/libs/ardour/ardour/crossfade.h b/libs/ardour/ardour/crossfade.h
new file mode 100644
index 0000000000..9ba3689e82
--- /dev/null
+++ b/libs/ardour/ardour/crossfade.h
@@ -0,0 +1,179 @@
+/*
+ Copyright (C) 2000 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_overlap_h__
+#define __ardour_overlap_h__
+
+#include <vector>
+#include <algorithm>
+#include <boost/shared_ptr.hpp>
+
+#include <sigc++/signal.h>
+
+#include <pbd/undo.h>
+#include <pbd/statefuldestructible.h>
+
+#include <ardour/ardour.h>
+#include <ardour/curve.h>
+#include <ardour/audioregion.h>
+#include <ardour/crossfade_compare.h>
+
+namespace ARDOUR {
+
+class AudioRegion;
+class Playlist;
+
+class Crossfade : public ARDOUR::AudioRegion
+{
+ public:
+
+ class NoCrossfadeHere: std::exception {
+ public:
+ virtual const char *what() const throw() { return "no crossfade should be constructed here"; }
+ };
+
+ /* constructor for "fixed" xfades at each end of an internal overlap */
+
+ Crossfade (boost::shared_ptr<ARDOUR::AudioRegion> in, boost::shared_ptr<ARDOUR::AudioRegion> out,
+ nframes_t position,
+ nframes_t initial_length,
+ AnchorPoint);
+
+ /* constructor for xfade between two regions that are overlapped in any way
+ except the "internal" case.
+ */
+
+ Crossfade (boost::shared_ptr<ARDOUR::AudioRegion> in, boost::shared_ptr<ARDOUR::AudioRegion> out, CrossfadeModel, bool active);
+
+
+ /* copy constructor to copy a crossfade with new regions. used (for example)
+ when a playlist copy is made
+ */
+ Crossfade (boost::shared_ptr<Crossfade>, boost::shared_ptr<ARDOUR::AudioRegion>, boost::shared_ptr<ARDOUR::AudioRegion>);
+
+ /* the usual XML constructor */
+
+ Crossfade (const Playlist&, XMLNode&);
+ virtual ~Crossfade();
+
+ bool operator== (const ARDOUR::Crossfade&);
+
+ XMLNode& get_state (void);
+ int set_state (const XMLNode&);
+
+ boost::shared_ptr<ARDOUR::AudioRegion> in() const { return _in; }
+ boost::shared_ptr<ARDOUR::AudioRegion> out() const { return _out; }
+
+ nframes_t read_at (Sample *buf, Sample *mixdown_buffer,
+ float *gain_buffer, nframes_t position, nframes_t cnt,
+ uint32_t chan_n,
+ nframes_t read_frames = 0,
+ nframes_t skip_frames = 0) const;
+
+ bool refresh ();
+
+ uint32_t upper_layer () const {
+ return std::max (_in->layer(), _out->layer());
+ }
+
+ uint32_t lower_layer () const {
+ return std::min (_in->layer(), _out->layer());
+ }
+
+ bool involves (boost::shared_ptr<ARDOUR::AudioRegion> region) const {
+ return _in == region || _out == region;
+ }
+
+ bool involves (boost::shared_ptr<ARDOUR::AudioRegion> a, boost::shared_ptr<ARDOUR::AudioRegion> b) const {
+ return (_in == a && _out == b) || (_in == b && _out == a);
+ }
+
+ nframes_t overlap_length() const;
+
+ void invalidate();
+
+ sigc::signal<void,boost::shared_ptr<Region> > Invalidated;
+ sigc::signal<void,Change> StateChanged;
+
+ bool covers (nframes_t frame) const {
+ return _position <= frame && frame < _position + _length;
+ }
+
+ OverlapType coverage (nframes_t start, nframes_t end) const;
+
+ static void set_buffer_size (nframes_t);
+
+ bool active () const { return _active; }
+ void set_active (bool yn);
+
+ bool following_overlap() const { return _follow_overlap; }
+ bool can_follow_overlap() const;
+ void set_follow_overlap (bool yn);
+
+ AutomationList& fade_in() { return _fade_in; }
+ AutomationList& fade_out() { return _fade_out; }
+
+ nframes_t set_length (nframes_t);
+
+ bool is_dependent() const { return true; }
+ bool depends_on (boost::shared_ptr<Region> other) const {
+ return other == _in || other == _out;
+ }
+
+ static nframes_t short_xfade_length() { return _short_xfade_length; }
+ static void set_short_xfade_length (nframes_t n);
+
+ static Change ActiveChanged;
+ static Change FollowOverlapChanged;
+
+ private:
+ friend struct CrossfadeComparePtr;
+ friend class AudioPlaylist;
+
+ static nframes_t _short_xfade_length;
+
+ boost::shared_ptr<ARDOUR::AudioRegion> _in;
+ boost::shared_ptr<ARDOUR::AudioRegion> _out;
+ bool _active;
+ bool _in_update;
+ OverlapType overlap_type;
+ AnchorPoint _anchor_point;
+ bool _follow_overlap;
+ bool _fixed;
+ int32_t layer_relation;
+
+
+ mutable AutomationList _fade_in;
+ mutable AutomationList _fade_out;
+
+ static Sample* crossfade_buffer_out;
+ static Sample* crossfade_buffer_in;
+
+ void initialize ();
+ int compute (boost::shared_ptr<ARDOUR::AudioRegion>, boost::shared_ptr<ARDOUR::AudioRegion>, CrossfadeModel);
+ bool update ();
+
+ protected:
+ nframes_t read_raw_internal (Sample*, nframes_t, nframes_t) const;
+};
+
+
+} // namespace ARDOUR
+
+#endif /* __ardour_overlap_h__ */
diff --git a/libs/ardour/ardour/crossfade_compare.h b/libs/ardour/ardour/crossfade_compare.h
new file mode 100644
index 0000000000..b92806a6bb
--- /dev/null
+++ b/libs/ardour/ardour/crossfade_compare.h
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2003 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_crossfade_compare_h__
+#define __ardour_crossfade_compare_h__
+
+/* this exists so that playlist.h doesn't have to include crossfade.h
+ */
+
+namespace ARDOUR {
+
+class Crossfade;
+
+struct CrossfadeComparePtr {
+ bool operator() (const Crossfade *a, const Crossfade *b) const;
+};
+
+enum AnchorPoint {
+ StartOfIn,
+ EndOfIn,
+ EndOfOut
+};
+
+}
+
+#endif /* __ardour_crossfade_compare_h__ */
diff --git a/libs/ardour/ardour/curve.h b/libs/ardour/ardour/curve.h
new file mode 100644
index 0000000000..433b00a270
--- /dev/null
+++ b/libs/ardour/ardour/curve.h
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2001-2007 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_curve_h__
+#define __ardour_curve_h__
+
+#include <sys/types.h>
+#include <boost/utility.hpp>
+#include <sigc++/signal.h>
+#include <glibmm/thread.h>
+#include <pbd/undo.h>
+#include <list>
+#include <algorithm>
+#include <ardour/automation_event.h>
+
+namespace ARDOUR {
+
+class Curve : public boost::noncopyable
+{
+ public:
+ Curve (const AutomationList& al);
+
+ bool rt_safe_get_vector (double x0, double x1, float *arg, int32_t veclen);
+ void get_vector (double x0, double x1, float *arg, int32_t veclen);
+
+ void solve ();
+
+ private:
+ double unlocked_eval (double where);
+ double multipoint_eval (double x);
+
+ void _get_vector (double x0, double x1, float *arg, int32_t veclen);
+
+ void on_list_dirty() { _dirty = true; }
+
+ bool _dirty;
+ const AutomationList& _list;
+};
+
+} // namespace ARDOUR
+
+extern "C" {
+ void curve_get_vector_from_c (void *arg, double, double, float*, int32_t);
+}
+
+#endif /* __ardour_curve_h__ */
diff --git a/libs/ardour/ardour/cycle_timer.h b/libs/ardour/ardour/cycle_timer.h
new file mode 100644
index 0000000000..4e1a50e602
--- /dev/null
+++ b/libs/ardour/ardour/cycle_timer.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2002 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_cycle_timer_h__
+#define __ardour_cycle_timer_h__
+
+#include <string>
+#include <iostream>
+
+#include <ardour/cycles.h>
+
+class CycleTimer {
+ private:
+ static float cycles_per_usec;
+ cycles_t _entry;
+ cycles_t _exit;
+ std::string _name;
+
+ public:
+ CycleTimer(std::string name) : _name (name){
+ if (cycles_per_usec == 0) {
+ cycles_per_usec = get_mhz ();
+ }
+ _entry = get_cycles();
+ }
+ ~CycleTimer() {
+ _exit = get_cycles();
+ std::cerr << _name << ": " << (float) (_exit - _entry) / cycles_per_usec << " (" << _entry << ", " << _exit << ')' << endl;
+ }
+
+ static float get_mhz ();
+};
+
+#endif /* __ardour_cycle_timer_h__ */
diff --git a/libs/ardour/ardour/cycles.h b/libs/ardour/ardour/cycles.h
new file mode 100644
index 0000000000..0d1ac154dd
--- /dev/null
+++ b/libs/ardour/ardour/cycles.h
@@ -0,0 +1,221 @@
+/*
+ Copyright (C) 2001 Paul Davis
+ Code derived from various headers from the Linux kernel
+
+ 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_cycles_h__
+#define __ardour_cycles_h__
+
+#include <stdint.h>
+
+#if defined(__i386__) || defined(__x86_64__)
+
+/*
+ * Standard way to access the cycle counter on i586+ CPUs.
+ * Currently only used on SMP.
+ *
+ * If you really have a SMP machine with i486 chips or older,
+ * compile for that, and this will just always return zero.
+ * That's ok, it just means that the nicer scheduling heuristics
+ * won't work for you.
+ *
+ * We only use the low 32 bits, and we'd simply better make sure
+ * that we reschedule before that wraps. Scheduling at least every
+ * four billion cycles just basically sounds like a good idea,
+ * regardless of how fast the machine is.
+ */
+typedef uint64_t cycles_t;
+
+extern cycles_t cacheflush_time;
+
+#define rdtscll(val) \
+ __asm__ __volatile__("rdtsc" : "=A" (val))
+
+static inline cycles_t get_cycles (void)
+{
+ uint32_t long ret;
+
+ rdtscll(ret);
+ return ret;
+}
+
+#elif defined(__powerpc__)
+
+#define CPU_FTR_601 0x00000100
+
+typedef uint32_t cycles_t;
+
+/*
+ * For the "cycle" counter we use the timebase lower half.
+ * Currently only used on SMP.
+ */
+
+extern cycles_t cacheflush_time;
+
+static inline cycles_t get_cycles(void)
+{
+ cycles_t ret = 0;
+
+ __asm__ __volatile__(
+ "98: mftb %0\n"
+ "99:\n"
+ ".section __ftr_fixup,\"a\"\n"
+ " .long %1\n"
+ " .long 0\n"
+ " .long 98b\n"
+ " .long 99b\n"
+ ".previous"
+ : "=r" (ret) : "i" (CPU_FTR_601));
+ return ret;
+}
+
+#elif defined(__ia64__)
+/* ia64 */
+
+typedef uint32_t cycles_t;
+static inline cycles_t
+get_cycles (void)
+{
+ cycles_t ret;
+ __asm__ __volatile__ ("mov %0=ar.itc" : "=r"(ret));
+ return ret;
+}
+
+#elif defined(__alpha__)
+/* alpha */
+
+/*
+ * Standard way to access the cycle counter.
+ * Currently only used on SMP for scheduling.
+ *
+ * Only the low 32 bits are available as a continuously counting entity.
+ * But this only means we'll force a reschedule every 8 seconds or so,
+ * which isn't an evil thing.
+ */
+
+typedef uint32_t cycles_t;
+static inline cycles_t get_cycles (void)
+{
+ cycles_t ret;
+ __asm__ __volatile__ ("rpcc %0" : "=r"(ret));
+ return ret;
+}
+
+#elif defined(__s390__)
+/* s390 */
+
+typedef uint32_t long cycles_t;
+static inline cycles_t get_cycles(void)
+{
+ cycles_t cycles;
+ __asm__("stck 0(%0)" : : "a" (&(cycles)) : "memory", "cc");
+ return cycles >> 2;
+}
+
+#elif defined(__hppa__)
+/* hppa/parisc */
+
+#define mfctl(reg) ({ \
+ uint32_t cr; \
+ __asm__ __volatile__( \
+ "mfctl " #reg ",%0" : \
+ "=r" (cr) \
+ ); \
+ cr; \
+})
+
+typedef uint32_t cycles_t;
+static inline cycles_t get_cycles (void)
+{
+ return mfctl(16);
+}
+
+#elif defined(__mips__)
+/* mips/mipsel */
+
+/*
+ * Standard way to access the cycle counter.
+ * Currently only used on SMP for scheduling.
+ *
+ * Only the low 32 bits are available as a continuously counting entity.
+ * But this only means we'll force a reschedule every 8 seconds or so,
+ * which isn't an evil thing.
+ *
+ * We know that all SMP capable CPUs have cycle counters.
+ */
+
+#define __read_32bit_c0_register(source, sel) \
+({ int __res; \
+ if (sel == 0) \
+ __asm__ __volatile__( \
+ "mfc0\t%0, " #source "\n\t" \
+ : "=r" (__res)); \
+ else \
+ __asm__ __volatile__( \
+ ".set\tmips32\n\t" \
+ "mfc0\t%0, " #source ", " #sel "\n\t" \
+ ".set\tmips0\n\t" \
+ : "=r" (__res)); \
+ __res; \
+})
+
+/* #define CP0_COUNT $9 */
+#define read_c0_count() __read_32bit_c0_register($9, 0)
+
+typedef uint32_t cycles_t;
+static inline cycles_t get_cycles (void)
+{
+ return read_c0_count();
+}
+
+/* begin mach */
+#elif defined(__APPLE__)
+
+#include <CoreAudio/HostTime.h>
+
+typedef UInt64 cycles_t;
+static inline cycles_t get_cycles (void)
+{
+ UInt64 time = AudioGetCurrentHostTime();
+ return AudioConvertHostTimeToNanos(time);
+}
+/* end mach */
+
+#else
+
+/* debian: sparc, arm, m68k */
+
+#warning You are compiling libardour on a platform for which ardour/cycles.h needs work
+
+#include <sys/time.h>
+
+typedef long cycles_t;
+
+extern cycles_t cacheflush_time;
+
+static inline cycles_t get_cycles(void)
+{
+ struct timeval tv;
+ gettimeofday (&tv, NULL);
+
+ return tv.tv_usec;
+}
+
+#endif
+
+#endif /* __ardour_cycles_h__ */
diff --git a/libs/ardour/ardour/dB.h b/libs/ardour/ardour/dB.h
new file mode 100644
index 0000000000..b67e581067
--- /dev/null
+++ b/libs/ardour/ardour/dB.h
@@ -0,0 +1,33 @@
+/*
+ Copyright (C) 2001 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_dB_h__
+#define __ardour_dB_h__
+
+#include <pbd/fastlog.h>
+
+static inline float dB_to_coefficient (float dB) {
+ return dB > -318.8f ? pow (10.0f, dB * 0.05f) : 0.0f;
+}
+
+static inline float coefficient_to_dB (float coeff) {
+ return 20.0f * fast_log10 (coeff);
+}
+
+#endif /* __ardour_dB_h__ */
diff --git a/libs/ardour/ardour/data_type.h b/libs/ardour/ardour/data_type.h
new file mode 100644
index 0000000000..854f52acba
--- /dev/null
+++ b/libs/ardour/ardour/data_type.h
@@ -0,0 +1,130 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Author: Dave Robillard
+
+ 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_data_type_h__
+#define __ardour_data_type_h__
+
+#include <string>
+#include <jack/jack.h>
+
+namespace ARDOUR {
+
+
+/** A type of Data Ardour is capable of processing.
+ *
+ * The majority of this class is dedicated to conversion to and from various
+ * other type representations, simple comparison between then, etc. This code
+ * is deliberately 'ugly' so other code doesn't have to be.
+ */
+class DataType
+{
+public:
+ /** Numeric symbol for this DataType.
+ *
+ * Castable to int for use as an array index (e.g. by ChanCount).
+ * Note this means NIL is (ntypes-1) and guaranteed to change when
+ * types are added, so this number is NOT suitable for serialization,
+ * network, or binary anything.
+ *
+ * WARNING: The number of non-NIL entries here must match num_types.
+ */
+ enum Symbol {
+ AUDIO = 0,
+ MIDI = 1,
+ NIL = 2,
+ };
+
+ /** Number of types (not including NIL).
+ * WARNING: make sure this matches Symbol!
+ */
+ static const uint32_t num_types = 2;
+
+ DataType(const Symbol& symbol)
+ : _symbol(symbol)
+ {}
+
+ /** Construct from a string (Used for loading from XML and Ports)
+ * The string can be as in an XML file (eg "audio" or "midi"), or a
+ * Jack type string (from jack_port_type) */
+ DataType(const std::string& str)
+ : _symbol(NIL) {
+ if (str == "audio" || str == JACK_DEFAULT_AUDIO_TYPE)
+ _symbol = AUDIO;
+ else if (str == "midi" || str == JACK_DEFAULT_MIDI_TYPE)
+ _symbol = MIDI;
+ }
+
+ /** Get the Jack type this DataType corresponds to */
+ const char* to_jack_type() const {
+ switch (_symbol) {
+ case AUDIO: return JACK_DEFAULT_AUDIO_TYPE;
+ case MIDI: return JACK_DEFAULT_MIDI_TYPE;
+ default: return "";
+ }
+ }
+
+ /** Inverse of the from-string constructor */
+ const char* to_string() const {
+ switch (_symbol) {
+ case AUDIO: return "audio";
+ case MIDI: return "midi";
+ default: return "unknown"; // reeeally shouldn't ever happen
+ }
+ }
+
+ inline operator uint32_t() const { return (uint32_t)_symbol; }
+
+ /** DataType iterator, for writing generic loops that iterate over all
+ * available types.
+ */
+ class iterator {
+ public:
+
+ iterator(uint32_t index) : _index(index) {}
+
+ DataType operator*() { return DataType((Symbol)_index); }
+ iterator& operator++() { ++_index; return *this; } // yes, prefix only
+ bool operator==(const iterator& other) { return (_index == other._index); }
+ bool operator!=(const iterator& other) { return (_index != other._index); }
+
+ private:
+ friend class DataType;
+
+ uint32_t _index;
+ };
+
+ static iterator begin() { return iterator(0); }
+ static iterator end() { return iterator(num_types); }
+
+ bool operator==(const Symbol symbol) { return (_symbol == symbol); }
+ bool operator!=(const Symbol symbol) { return (_symbol != symbol); }
+
+ bool operator==(const DataType other) { return (_symbol == other._symbol); }
+ bool operator!=(const DataType other) { return (_symbol != other._symbol); }
+
+private:
+ Symbol _symbol; // could be const if not for the string constructor
+};
+
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_data_type_h__
+
diff --git a/libs/ardour/ardour/directory_names.h b/libs/ardour/ardour/directory_names.h
new file mode 100644
index 0000000000..8fabe025d1
--- /dev/null
+++ b/libs/ardour/ardour/directory_names.h
@@ -0,0 +1,23 @@
+
+#ifndef __ardour_directory_names_h__
+#define __ardour_directory_names_h__
+
+#include <string>
+
+namespace ARDOUR {
+
+extern const char* const old_sound_dir_name;
+extern const char* const sound_dir_name;
+extern const char* const midi_dir_name;
+extern const char* const dead_sound_dir_name;
+extern const char* const dead_midi_dir_name;
+extern const char* const interchange_dir_name;
+extern const char* const peak_dir_name;
+extern const char* const export_dir_name;
+extern const char* const templates_dir_name;
+extern const char* const surfaces_dir_name;
+extern const char* const user_config_dir_name;
+
+};
+
+#endif
diff --git a/libs/ardour/ardour/diskstream.h b/libs/ardour/ardour/diskstream.h
new file mode 100644
index 0000000000..824d864663
--- /dev/null
+++ b/libs/ardour/ardour/diskstream.h
@@ -0,0 +1,308 @@
+/*
+ Copyright (C) 2000-2006 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_diskstream_h__
+#define __ardour_diskstream_h__
+
+#include <sigc++/signal.h>
+
+#include <cmath>
+#include <string>
+#include <queue>
+#include <map>
+#include <vector>
+
+#include <time.h>
+
+#include <pbd/fastlog.h>
+#include <pbd/ringbufferNPT.h>
+#include <pbd/stateful.h>
+#include <pbd/statefuldestructible.h>
+
+#include <ardour/ardour.h>
+#include <ardour/configuration.h>
+#include <ardour/session.h>
+#include <ardour/route_group.h>
+#include <ardour/route.h>
+#include <ardour/port.h>
+#include <ardour/utils.h>
+
+struct tm;
+
+namespace ARDOUR {
+
+class AudioEngine;
+class Send;
+class Session;
+class Playlist;
+class IO;
+
+class Diskstream : public SessionObject
+{
+ public:
+ enum Flag {
+ Recordable = 0x1,
+ Hidden = 0x2,
+ Destructive = 0x4
+ };
+
+ Diskstream (Session &, const string& name, Flag f = Recordable);
+ Diskstream (Session &, const XMLNode&);
+ virtual ~Diskstream();
+
+ bool set_name (const string& str);
+
+ ARDOUR::IO* io() const { return _io; }
+ void set_io (ARDOUR::IO& io);
+
+ virtual float playback_buffer_load() const = 0;
+ virtual float capture_buffer_load() const = 0;
+
+ void set_flag (Flag f) { _flags = Flag (_flags | f); }
+ void unset_flag (Flag f) { _flags = Flag (_flags & ~f); }
+
+ AlignStyle alignment_style() const { return _alignment_style; }
+ void set_align_style (AlignStyle);
+ void set_persistent_align_style (AlignStyle a) { _persistent_alignment_style = a; }
+
+ nframes_t roll_delay() const { return _roll_delay; }
+ void set_roll_delay (nframes_t);
+
+ bool record_enabled() const { return g_atomic_int_get (&_record_enabled); }
+ virtual void set_record_enabled (bool yn) = 0;
+
+ bool destructive() const { return _flags & Destructive; }
+ virtual int set_destructive (bool yn) { return -1; }
+ virtual bool can_become_destructive (bool& requires_bounce) const { return false; }
+
+ bool hidden() const { return _flags & Hidden; }
+ bool recordable() const { return _flags & Recordable; }
+ bool reversed() const { return _actual_speed < 0.0f; }
+ double speed() const { return _visible_speed; }
+
+ virtual void punch_in() {}
+ virtual void punch_out() {}
+
+ void set_speed (double);
+ void non_realtime_set_speed ();
+ virtual void non_realtime_locate (nframes_t location) {};
+ virtual void playlist_modified ();
+
+ boost::shared_ptr<Playlist> playlist () { return _playlist; }
+
+ virtual int use_playlist (boost::shared_ptr<Playlist>);
+ virtual int use_new_playlist () = 0;
+ virtual int use_copy_playlist () = 0;
+
+ nframes_t current_capture_start() const { return capture_start_frame; }
+ nframes_t current_capture_end() const { return capture_start_frame + capture_captured; }
+ nframes_t get_capture_start_frame (uint32_t n=0);
+ nframes_t get_captured_frames (uint32_t n=0);
+
+ ChanCount n_channels() { return _n_channels; }
+
+ static nframes_t disk_io_frames() { return disk_io_chunk_frames; }
+ static void set_disk_io_chunk_frames (uint32_t n) { disk_io_chunk_frames = n; }
+
+ /* Stateful */
+ virtual XMLNode& get_state(void) = 0;
+ virtual int set_state(const XMLNode& node) = 0;
+
+ virtual void monitor_input (bool) {}
+
+ nframes_t capture_offset() const { return _capture_offset; }
+ virtual void set_capture_offset ();
+
+ bool slaved() const { return _slaved; }
+ void set_slaved(bool yn) { _slaved = yn; }
+
+ int set_loop (Location *loc);
+
+ std::list<boost::shared_ptr<Region> >& last_capture_regions () { return _last_capture_regions; }
+
+ void handle_input_change (IOChange, void *src);
+
+ void remove_region_from_last_capture (boost::weak_ptr<Region> wregion);
+
+ sigc::signal<void> RecordEnableChanged;
+ sigc::signal<void> SpeedChanged;
+ sigc::signal<void> ReverseChanged;
+ sigc::signal<void> PlaylistChanged;
+ sigc::signal<void> AlignmentStyleChanged;
+ sigc::signal<void,Location *> LoopSet;
+
+ static sigc::signal<void> DiskOverrun;
+ static sigc::signal<void> DiskUnderrun;
+
+ protected:
+ friend class Session;
+
+ /* the Session is the only point of access for these because they require
+ * that the Session is "inactive" while they are called.
+ */
+
+ virtual void set_pending_overwrite (bool) = 0;
+ virtual int overwrite_existing_buffers () = 0;
+ virtual void set_block_size (nframes_t) = 0;
+ virtual int internal_playback_seek (nframes_t distance) = 0;
+ virtual int can_internal_playback_seek (nframes_t distance) = 0;
+ virtual int rename_write_sources () = 0;
+ virtual void reset_write_sources (bool, bool force = false) = 0;
+ virtual void non_realtime_input_change () = 0;
+
+ uint32_t read_data_count() const { return _read_data_count; }
+ uint32_t write_data_count() const { return _write_data_count; }
+
+ protected:
+ friend class Auditioner;
+ virtual int seek (nframes_t which_sample, bool complete_refill = false) = 0;
+
+ protected:
+ friend class Track;
+
+ virtual void prepare ();
+ virtual int process (nframes_t transport_frame, nframes_t nframes, nframes_t offset, bool can_record, bool rec_monitors_input) = 0;
+ virtual bool commit (nframes_t nframes) = 0;
+ virtual void recover (); /* called if commit will not be called, but process was */
+
+ //private:
+
+ enum TransitionType {
+ CaptureStart = 0,
+ CaptureEnd
+ };
+
+ struct CaptureTransition {
+ TransitionType type;
+ nframes_t capture_val; ///< The start or end file frame position
+ };
+
+ /* The two central butler operations */
+ virtual int do_flush (Session::RunContext context, bool force = false) = 0;
+ virtual int do_refill () = 0;
+
+ /** For non-butler contexts (allocates temporary working buffers) */
+ virtual int do_refill_with_alloc() = 0;
+
+ /* XXX fix this redundancy ... */
+
+ virtual void playlist_changed (Change);
+ virtual void playlist_deleted (boost::weak_ptr<Playlist>);
+
+ virtual void transport_stopped (struct tm&, time_t, bool abort) = 0;
+ virtual void transport_looped (nframes_t transport_frame) = 0;
+
+ struct CaptureInfo {
+ uint32_t start;
+ uint32_t frames;
+ };
+
+ virtual void init (Flag);
+
+ virtual int use_new_write_source (uint32_t n=0) = 0;
+
+ virtual int find_and_use_playlist (const string&) = 0;
+
+ virtual void allocate_temporary_buffers () = 0;
+
+ virtual bool realtime_set_speed (double, bool global_change);
+
+ std::list<boost::shared_ptr<Region> > _last_capture_regions;
+
+ virtual int use_pending_capture_data (XMLNode& node) = 0;
+
+ virtual void get_input_sources () = 0;
+ virtual void check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record) = 0;
+ virtual void set_align_style_from_io() {}
+ virtual void setup_destructive_playlist () {}
+ virtual void use_destructive_playlist () {}
+
+ static nframes_t disk_io_chunk_frames;
+ std::vector<CaptureInfo*> capture_info;
+ Glib::Mutex capture_info_lock;
+
+ uint32_t i_am_the_modifier;
+
+ ARDOUR::IO* _io;
+ ChanCount _n_channels;
+
+ boost::shared_ptr<Playlist> _playlist;
+
+ mutable gint _record_enabled;
+ double _visible_speed;
+ double _actual_speed;
+ /* items needed for speed change logic */
+ bool _buffer_reallocation_required;
+ bool _seek_required;
+
+ bool force_refill;
+ nframes_t capture_start_frame;
+ nframes_t capture_captured;
+ bool was_recording;
+ nframes_t adjust_capture_position;
+ nframes_t _capture_offset;
+ nframes_t _roll_delay;
+ nframes_t first_recordable_frame;
+ nframes_t last_recordable_frame;
+ int last_possibly_recording;
+ AlignStyle _alignment_style;
+ bool _scrubbing;
+ bool _slaved;
+ bool _processed;
+ Location* loop_location;
+ nframes_t overwrite_frame;
+ off_t overwrite_offset;
+ bool pending_overwrite;
+ bool overwrite_queued;
+ IOChange input_change_pending;
+ nframes_t wrap_buffer_size;
+ nframes_t speed_buffer_size;
+
+ uint64_t last_phase;
+ uint64_t phi;
+ uint64_t target_phi;
+
+ nframes_t file_frame;
+ nframes_t playback_sample;
+ nframes_t playback_distance;
+ bool commit_should_unlock;
+
+ uint32_t _read_data_count;
+ uint32_t _write_data_count;
+
+ bool in_set_state;
+ AlignStyle _persistent_alignment_style;
+ bool first_input_change;
+
+ Glib::Mutex state_lock;
+
+ nframes_t scrub_start;
+ nframes_t scrub_buffer_size;
+ nframes_t scrub_offset;
+
+ sigc::connection ports_created_c;
+ sigc::connection plmod_connection;
+ sigc::connection plgone_connection;
+
+ Flag _flags;
+};
+
+}; /* namespace ARDOUR */
+
+#endif /* __ardour_diskstream_h__ */
diff --git a/libs/ardour/ardour/export.h b/libs/ardour/ardour/export.h
new file mode 100644
index 0000000000..141786873d
--- /dev/null
+++ b/libs/ardour/ardour/export.h
@@ -0,0 +1,107 @@
+/*
+ Copyright (C) 2000-2007 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_export_h__
+#define __ardour_export_h__
+
+#include <map>
+#include <vector>
+#include <string>
+
+#include <sigc++/signal.h>
+
+#include <sndfile.h>
+#include <samplerate.h>
+
+#include <ardour/ardour.h>
+#include <ardour/gdither.h>
+
+using std::map;
+using std::vector;
+using std::string;
+using std::pair;
+
+namespace ARDOUR
+{
+ class Port;
+
+ typedef pair<Port *, uint32_t> PortChannelPair;
+ typedef map<uint32_t, vector<PortChannelPair> > ExportPortMap;
+
+ struct ExportSpecification : public SF_INFO, public sigc::trackable {
+
+ ExportSpecification();
+ ~ExportSpecification ();
+
+ void init ();
+ void clear ();
+
+
+ int prepare (nframes_t blocksize, nframes_t frame_rate);
+
+ int process (nframes_t nframes);
+
+ /* set by the user */
+
+ string path;
+ nframes_t sample_rate;
+
+ int src_quality;
+ SNDFILE* out;
+ uint32_t channels;
+ ExportPortMap port_map;
+ nframes_t start_frame;
+ nframes_t end_frame;
+ GDitherType dither_type;
+ bool do_freewheel;
+
+ /* used exclusively during export */
+
+ nframes_t frame_rate;
+ GDither dither;
+ float* dataF;
+ float* dataF2;
+ float* leftoverF;
+ nframes_t leftover_frames;
+ nframes_t max_leftover_frames;
+ void* output_data;
+ nframes_t out_samples_max;
+ uint32_t sample_bytes;
+ uint32_t data_width;
+
+ nframes_t total_frames;
+ SF_INFO sfinfo;
+ SRC_DATA src_data;
+ SRC_STATE* src_state;
+ nframes_t pos;
+
+ sigc::connection freewheel_connection;
+
+ /* shared between UI thread and audio thread */
+
+ volatile float progress; /* audio thread sets this */
+ volatile bool stop; /* UI sets this */
+ volatile bool running; /* audio thread sets to false when export is done */
+
+ int status;
+ };
+
+} // namespace ARDOUR
+
+#endif /* __ardour_export_h__ */
diff --git a/libs/ardour/ardour/filename_extensions.h b/libs/ardour/ardour/filename_extensions.h
new file mode 100644
index 0000000000..3e280d6326
--- /dev/null
+++ b/libs/ardour/ardour/filename_extensions.h
@@ -0,0 +1,17 @@
+
+#ifndef __ardour_filename_extensions_h__
+#define __ardour_filename_extensions_h__
+
+namespace ARDOUR {
+
+extern const char* const template_suffix;
+extern const char* const statefile_suffix;
+extern const char* const pending_suffix;
+extern const char* const peakfile_suffix;
+extern const char* const backup_suffix;
+extern const char* const temp_suffix;
+extern const char* const history_suffix;
+
+}
+
+#endif
diff --git a/libs/ardour/ardour/filesystem_paths.h b/libs/ardour/ardour/filesystem_paths.h
new file mode 100644
index 0000000000..12995bd818
--- /dev/null
+++ b/libs/ardour/ardour/filesystem_paths.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ 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_FILESYSTEM_PATHS_INCLUDED
+#define ARDOUR_FILESYSTEM_PATHS_INCLUDED
+
+#include <pbd/filesystem.h>
+#include <pbd/search_path.h>
+
+namespace ARDOUR {
+
+ using namespace PBD;
+
+ /**
+ * @return the path to the directory used to store user specific ardour
+ * configuration files.
+ */
+ sys::path user_config_directory ();
+
+ /**
+ * @return the path to the directory that contains the system wide ardour
+ * modules.
+ */
+ sys::path ardour_module_directory ();
+
+ SearchPath ardour_search_path ();
+
+ SearchPath system_config_search_path ();
+
+ SearchPath system_data_search_path ();
+
+} // namespace ARDOUR
+
+#endif
diff --git a/libs/ardour/ardour/filter.h b/libs/ardour/ardour/filter.h
new file mode 100644
index 0000000000..b659873bdb
--- /dev/null
+++ b/libs/ardour/ardour/filter.h
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 2007 Paul Davis
+ Author: Dave Robillard
+
+ 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_filter_h__
+#define __ardour_filter_h__
+
+#include <vector>
+#include <ardour/region.h>
+
+namespace ARDOUR {
+
+class Region;
+class Session;
+
+class Filter {
+
+ public:
+ virtual ~Filter() {}
+
+ virtual int run (boost::shared_ptr<ARDOUR::Region>) = 0;
+ std::vector<boost::shared_ptr<ARDOUR::Region> > results;
+
+ protected:
+ Filter (ARDOUR::Session& s) : session(s) {}
+
+ int make_new_sources (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&, std::string suffix = "");
+ int finish (boost::shared_ptr<ARDOUR::Region>, ARDOUR::SourceList&, std::string region_name = "");
+
+ ARDOUR::Session& session;
+};
+
+} /* namespace */
+
+#endif /* __ardour_filter_h__ */
diff --git a/libs/ardour/ardour/gain.h b/libs/ardour/ardour/gain.h
new file mode 100644
index 0000000000..e57cfdc0d7
--- /dev/null
+++ b/libs/ardour/ardour/gain.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2000 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_gain_h__
+#define __ardour_gain_h__
+
+#include "ardour.h"
+#include "curve.h"
+
+namespace ARDOUR {
+
+struct Gain : public AutomationList {
+
+ Gain();
+ Gain (const Gain&);
+ Gain& operator= (const Gain&);
+
+ static void fill_linear_fade_in (Gain& curve, nframes_t frames);
+ static void fill_linear_volume_fade_in (Gain& curve, nframes_t frames);
+ static void fill_linear_fade_out (Gain& curve, nframes_t frames);
+ static void fill_linear_volume_fade_out (Gain& curve, nframes_t frames);
+
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_gain_h__ */
diff --git a/libs/ardour/ardour/gdither.h b/libs/ardour/ardour/gdither.h
new file mode 100644
index 0000000000..67efcc3583
--- /dev/null
+++ b/libs/ardour/ardour/gdither.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * 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 GDITHER_H
+#define GDITHER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "gdither_types.h"
+
+/* Create and initialise a state structure, takes a dither type, a number of
+ * channels and a bit depth as input
+ *
+ * The Dither type is one of
+ *
+ * GDitherNone - straight nearest neighbour rounding. Theres no pressing
+ * reason to do this at 8 or 16 bit, but you might want to at 24, for some
+ * reason. At the lest it will save you writing int->float conversion code,
+ * which is arder than it sounds.
+ *
+ * GDitherRect - mathematically most accurate, lowest noise floor, but not
+ * that good for audio. It is the fastest though.
+ *
+ * GDitherTri - a happy medium between Rectangular and Shaped, reasonable
+ * noise floor, not too obvious, quite fast.
+ *
+ * GDitherShaped - should have the least audible impact, but has the highest
+ * noise floor, fairly CPU intensive. Not advisible if your going to apply
+ * any frequency manipulation afterwards.
+ *
+ * channels, sets the number of channels in the output data, output data will
+ * be written interleaved into the area given to gdither_run(). Set to 1
+ * if you are not working with interleaved buffers.
+ *
+ * bit depth, sets the bit width of the output sample data, it can be one of:
+ *
+ * GDither8bit - 8 bit unsiged
+ * GDither16bit - 16 bit signed
+ * GDither32bit - 24+bits in upper bits of a 32 bit word
+ * GDitherFloat - IEEE floating point (32bits)
+ * GDitherDouble - Double precision IEEE floating point (64bits)
+ *
+ * dither_depth, set the number of bits before the signal will be truncated to,
+ * eg. 16 will produce an output stream with 16bits-worth of signal. Setting to
+ * zero or greater than the width of the output format will dither to the
+ * maximum precision allowed by the output format.
+ */
+GDither gdither_new(GDitherType type, uint32_t channels,
+
+ GDitherSize bit_depth, int dither_depth);
+
+/* Frees memory used by gdither_new.
+ */
+void gdither_free(GDither s);
+
+/* Applies dithering to the supplied signal.
+ *
+ * channel is the channel number you are processing (0 - channles-1), length is
+ * the length of the input, in samples, x is the input samples (float), y is
+ * where the output samples will be written, it should have the approaprate
+ * type for the chosen bit depth
+ */
+void gdither_runf(GDither s, uint32_t channel, uint32_t length,
+ float *x, void *y);
+
+/* see gdither_runf, vut input argument is double format */
+void gdither_run(GDither s, uint32_t channel, uint32_t length,
+ double *x, void *y);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libs/ardour/ardour/gdither_types.h b/libs/ardour/ardour/gdither_types.h
new file mode 100644
index 0000000000..bcc0097d7f
--- /dev/null
+++ b/libs/ardour/ardour/gdither_types.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * 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 GDITHER_TYPES_H
+#define GDITHER_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ GDitherNone = 0,
+ GDitherRect,
+ GDitherTri,
+ GDitherShaped
+} GDitherType;
+
+typedef enum {
+ GDither8bit = 8,
+ GDither16bit = 16,
+ GDither32bit = 32,
+ GDitherFloat = 25,
+ GDitherDouble = 54
+} GDitherSize;
+
+typedef void *GDither;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libs/ardour/ardour/gdither_types_internal.h b/libs/ardour/ardour/gdither_types_internal.h
new file mode 100644
index 0000000000..e73a256310
--- /dev/null
+++ b/libs/ardour/ardour/gdither_types_internal.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * 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 GDITHER_TYPES_H
+#define GDITHER_TYPES_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define GDITHER_SH_BUF_SIZE 8
+#define GDITHER_SH_BUF_MASK 7
+
+/* this must agree with whats in gdither_types.h */
+typedef enum {
+ GDitherNone = 0,
+ GDitherRect,
+ GDitherTri,
+ GDitherShaped
+} GDitherType;
+
+typedef enum {
+ GDither8bit = 8,
+ GDither16bit = 16,
+ GDither32bit = 32,
+ GDitherFloat = 25,
+ GDitherDouble = 54
+} GDitherSize;
+
+typedef struct {
+ uint32_t phase;
+ float buffer[GDITHER_SH_BUF_SIZE];
+} GDitherShapedState;
+
+typedef struct GDither_s {
+ GDitherType type;
+ uint32_t channels;
+ uint32_t bit_depth;
+ uint32_t dither_depth;
+ float scale;
+ uint32_t post_scale;
+ float post_scale_fp;
+ float bias;
+
+ int clamp_u;
+
+ int clamp_l;
+ float *tri_state;
+ GDitherShapedState *shaped_state;
+} *GDither;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libs/ardour/ardour/importable_source.h b/libs/ardour/ardour/importable_source.h
new file mode 100644
index 0000000000..a33cf567e7
--- /dev/null
+++ b/libs/ardour/ardour/importable_source.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2007 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_importable_source_h__
+#define __ardour_importable_source_h__
+
+#include <pbd/failed_constructor.h>
+#include <ardour/types.h>
+
+namespace ARDOUR {
+
+class ImportableSource {
+public:
+ ImportableSource () {}
+ virtual ~ImportableSource() {}
+
+ virtual nframes_t read (Sample* buffer, nframes_t nframes) = 0;
+ virtual float ratio() const { return 1.0f; }
+ virtual uint32_t channels() const = 0;
+ virtual nframes_t length() const = 0;
+ virtual nframes_t samplerate() const = 0;
+ virtual void seek (nframes_t pos) = 0;
+};
+
+}
+
+#endif /* __ardour_importable_source_h__ */
diff --git a/libs/ardour/ardour/internal_audio_port.h b/libs/ardour/ardour/internal_audio_port.h
new file mode 100644
index 0000000000..7b70989d4b
--- /dev/null
+++ b/libs/ardour/ardour/internal_audio_port.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2007 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.
+
+ $Id: port.h 712 2006-07-28 01:08:57Z drobilla $
+*/
+
+#ifndef __ardour_internal_audio_port_h__
+#define __ardour_internal_audio_port_h__
+
+#include <sigc++/signal.h>
+#include <pbd/failed_constructor.h>
+#include <ardour/ardour.h>
+#include <ardour/internal_port.h>
+#include <ardour/audio_port.h>
+
+namespace ARDOUR {
+
+class AudioEngine;
+class InternalAudioPort : public AudioPort, public InternalPort {
+ public:
+ void cycle_start(nframes_t nframes) {
+ _buffer.silence (nframes);
+ }
+
+ AudioBuffer& get_audio_buffer();
+
+ void set_mixdown_function (void (*func)(const std::list<InternalPort*>&, AudioBuffer&, nframes_t, nframes_t));
+ void reset ();
+
+ protected:
+ friend class AudioEngine;
+
+ InternalAudioPort (const std::string& name, Flags flags);
+ void (*_mixdown)(const std::list<InternalPort*>&, AudioBuffer&, nframes_t, nframes_t);
+
+ static void default_mixdown (const std::list<InternalPort*>&, AudioBuffer&, nframes_t, nframes_t);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_internal_audio_port_h__ */
diff --git a/libs/ardour/ardour/internal_port.h b/libs/ardour/ardour/internal_port.h
new file mode 100644
index 0000000000..e6053c0e58
--- /dev/null
+++ b/libs/ardour/ardour/internal_port.h
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 2007 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_internal_port_h__
+#define __ardour_internal_port_h__
+
+#include <list>
+
+#include <sigc++/signal.h>
+#include <pbd/failed_constructor.h>
+#include <ardour/port.h>
+
+namespace ARDOUR {
+
+class AudioEngine;
+class Buffer;
+
+/** Abstract class representing internal (ardour<->ardour only) ports
+ */
+class InternalPort : public virtual Port {
+ public:
+
+ ~InternalPort();
+
+ std::string short_name();
+
+ int set_name (std::string str);
+
+ int connected () const;
+
+ int reestablish ();
+
+ bool connected_to (const std::string& portname) const;
+
+ const char ** get_connections () const;
+ bool monitoring_input () const { return false; }
+
+ void ensure_monitor_input (bool yn) {}
+ void request_monitor_input (bool yn) {}
+
+ nframes_t latency () const { return _latency; }
+ nframes_t total_latency() const { return _latency; }
+
+ void set_latency (nframes_t nframes);
+
+ static void connect (InternalPort& src, InternalPort& dst);
+ static void disconnect (InternalPort& a, InternalPort& b);
+
+ protected:
+ friend class AudioEngine;
+
+ InternalPort (const std::string&, DataType type, Flags flags);
+
+ int disconnect ();
+ void recompute_total_latency() const;
+
+ std::list<InternalPort*> _connections;
+ nframes_t _latency;
+
+ static AudioEngine* engine;
+ static void set_engine (AudioEngine* e);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_internal_port_h__ */
diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h
new file mode 100644
index 0000000000..83b6378dae
--- /dev/null
+++ b/libs/ardour/ardour/io.h
@@ -0,0 +1,391 @@
+/*
+ Copyright (C) 2000 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_io_h__
+#define __ardour_io_h__
+
+#include <string>
+#include <vector>
+#include <cmath>
+#include <sigc++/signal.h>
+#include <jack/jack.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/fastlog.h>
+#include <pbd/undo.h>
+#include <pbd/statefuldestructible.h>
+#include <pbd/controllable.h>
+
+#include <ardour/ardour.h>
+#include <ardour/automatable.h>
+#include <ardour/utils.h>
+#include <ardour/curve.h>
+#include <ardour/types.h>
+#include <ardour/data_type.h>
+#include <ardour/port_set.h>
+#include <ardour/chan_count.h>
+#include <ardour/latent.h>
+#include <ardour/automation_control.h>
+#include <ardour/user_bundle.h>
+
+using std::string;
+using std::vector;
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Session;
+class AudioEngine;
+class Bundle;
+class AutoBundle;
+class Panner;
+class PeakMeter;
+class Port;
+class AudioPort;
+class MidiPort;
+class BufferSet;
+
+
+/** A collection of input and output ports with connections.
+ *
+ * An IO can contain ports of varying types, making routes/inserts/etc with
+ * varied combinations of types (eg MIDI and audio) possible.
+ */
+
+class IO : public Automatable, public Latent
+{
+ public:
+ static const string state_node_name;
+
+ IO (Session&, const string& name,
+ int input_min = -1, int input_max = -1,
+ int output_min = -1, int output_max = -1,
+ DataType default_type = DataType::AUDIO,
+ bool public_ports = true);
+
+ IO (Session&, const XMLNode&, DataType default_type = DataType::AUDIO);
+
+ virtual ~IO();
+
+ ChanCount input_minimum() const { return _input_minimum; }
+ ChanCount input_maximum() const { return _input_maximum; }
+ ChanCount output_minimum() const { return _output_minimum; }
+ ChanCount output_maximum() const { return _output_maximum; }
+
+ void set_input_minimum (ChanCount n);
+ void set_input_maximum (ChanCount n);
+ void set_output_minimum (ChanCount n);
+ void set_output_maximum (ChanCount n);
+
+ bool active() const { return _active; }
+ void set_active (bool yn);
+
+ DataType default_type() const { return _default_type; }
+ void set_default_type(DataType t) { _default_type = t; }
+
+ bool set_name (const string& str);
+
+ virtual void silence (nframes_t, nframes_t offset);
+
+ void collect_input (BufferSet& bufs, nframes_t nframes, nframes_t offset);
+ void deliver_output (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset);
+ void just_meter_input (nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset);
+
+ gain_t gain () const { return _desired_gain; }
+ virtual gain_t effective_gain () const;
+
+ void set_denormal_protection (bool yn, void *src);
+ bool denormal_protection() const { return _denormal_protection; }
+
+ void set_phase_invert (bool yn, void *src);
+ bool phase_invert() const { return _phase_invert; }
+
+ Panner& panner() { return *_panner; }
+ PeakMeter& peak_meter() { return *_meter; }
+ const Panner& panner() const { return *_panner; }
+
+ int ensure_io (ChanCount in, ChanCount out, bool clear, void *src);
+
+ int connect_input_ports_to_bundle (boost::shared_ptr<Bundle>, void *src);
+ int connect_output_ports_to_bundle (boost::shared_ptr<Bundle>, void *src);
+
+ std::vector<boost::shared_ptr<Bundle> > bundles_connected_to_inputs ();
+ std::vector<boost::shared_ptr<Bundle> > bundles_connected_to_outputs ();
+
+ boost::shared_ptr<AutoBundle> bundle_for_inputs () { return _bundle_for_inputs; }
+ boost::shared_ptr<AutoBundle> bundle_for_outputs () { return _bundle_for_outputs; }
+
+ int add_input_port (string source, void *src, DataType type = DataType::NIL);
+ int add_output_port (string destination, void *src, DataType type = DataType::NIL);
+
+ int remove_input_port (Port *, void *src);
+ int remove_output_port (Port *, void *src);
+
+ int set_input (Port *, void *src);
+
+ int connect_input (Port *our_port, string other_port, void *src);
+ int connect_output (Port *our_port, string other_port, void *src);
+
+ int disconnect_input (Port *our_port, string other_port, void *src);
+ int disconnect_output (Port *our_port, string other_port, void *src);
+
+ int disconnect_inputs (void *src);
+ int disconnect_outputs (void *src);
+
+ nframes_t signal_latency() const { return _own_latency; }
+ nframes_t output_latency() const;
+ nframes_t input_latency() const;
+ void set_port_latency (nframes_t);
+
+ void update_port_total_latencies ();
+
+ const PortSet& inputs() const { return _inputs; }
+ const PortSet& outputs() const { return _outputs; }
+
+ Port *output (uint32_t n) const {
+ if (n < _outputs.num_ports()) {
+ return _outputs.port(n);
+ } else {
+ return 0;
+ }
+ }
+
+ Port *input (uint32_t n) const {
+ if (n < _inputs.num_ports()) {
+ return _inputs.port(n);
+ } else {
+ return 0;
+ }
+ }
+
+ AudioPort* audio_input(uint32_t n) const;
+ AudioPort* audio_output(uint32_t n) const;
+ MidiPort* midi_input(uint32_t n) const;
+ MidiPort* midi_output(uint32_t n) const;
+
+ const ChanCount& n_inputs () const { return _inputs.count(); }
+ const ChanCount& n_outputs () const { return _outputs.count(); }
+
+ void attach_buffers(ChanCount ignored);
+
+ sigc::signal<void> active_changed;
+
+ sigc::signal<void,IOChange,void*> input_changed;
+ sigc::signal<void,IOChange,void*> output_changed;
+
+ virtual XMLNode& state (bool full);
+ XMLNode& get_state (void);
+ int set_state (const XMLNode&);
+
+ static int disable_connecting (void);
+
+ static int enable_connecting (void);
+
+ static int disable_ports (void);
+
+ static int enable_ports (void);
+
+ static int disable_panners (void);
+
+ static int reset_panners (void);
+
+ static sigc::signal<int> PortsLegal;
+ static sigc::signal<int> PannersLegal;
+ static sigc::signal<int> ConnectingLegal;
+ /// raised when the number of input or output ports changes
+ static sigc::signal<void,ChanCount> PortCountChanged;
+ static sigc::signal<int> PortsCreated;
+
+ static void update_meters();
+
+ private:
+
+ static sigc::signal<void> Meter;
+ static Glib::StaticMutex m_meter_signal_lock;
+ sigc::connection m_meter_connection;
+
+ public:
+
+ /* automation */
+
+ struct GainControl : public AutomationControl {
+ GainControl (std::string name, IO& i, boost::shared_ptr<AutomationList> al)
+ : AutomationControl (i._session, al, name)
+ , _io (i)
+ {}
+
+ void set_value (float val);
+ float get_value (void) const;
+
+ IO& _io;
+ };
+
+ boost::shared_ptr<GainControl> gain_control() {
+ return _gain_control;
+ }
+ boost::shared_ptr<const GainControl> gain_control() const {
+ return _gain_control;
+ }
+
+ void clear_automation ();
+
+ void set_parameter_automation_state (Parameter, AutoState);
+
+ virtual void transport_stopped (nframes_t now);
+ virtual void automation_snapshot (nframes_t now, bool force);
+
+ void start_pan_touch (uint32_t which);
+ void end_pan_touch (uint32_t which);
+
+ void defer_pan_reset ();
+ void allow_pan_reset ();
+
+ /* the session calls this for master outs before
+ anyone else. controls outs too, at some point.
+ */
+
+ XMLNode *pending_state_node;
+ int ports_became_legal ();
+
+ private:
+ mutable Glib::Mutex io_lock;
+
+ protected:
+ Panner* _panner;
+ BufferSet* _output_buffers; //< Set directly to output port buffers
+ bool _active;
+ gain_t _gain;
+ gain_t _effective_gain;
+ gain_t _desired_gain;
+ Glib::Mutex declick_lock;
+ PortSet _outputs;
+ PortSet _inputs;
+ PeakMeter* _meter;
+ bool no_panner_reset;
+ bool _phase_invert;
+ bool _denormal_protection;
+ XMLNode* deferred_state;
+ DataType _default_type;
+ bool _public_ports;
+
+ virtual void set_deferred_state() {}
+
+ void reset_panner ();
+
+ virtual uint32_t pans_required() const
+ { return _inputs.count().n_audio(); }
+
+ boost::shared_ptr<GainControl> _gain_control;
+
+ virtual void set_gain (gain_t g, void *src);
+ void inc_gain (gain_t delta, void *src);
+
+ bool apply_gain_automation;
+
+ virtual int load_automation (std::string path);
+
+ /* AudioTrack::deprecated_use_diskstream_connections() needs these */
+
+ int set_inputs (const string& str);
+ int set_outputs (const string& str);
+
+ static bool connecting_legal;
+ static bool ports_legal;
+
+ BufferSet& output_buffers() { return *_output_buffers; }
+
+ private:
+
+ friend class Send;
+
+ static bool panners_legal;
+
+ int connecting_became_legal ();
+ int panners_became_legal ();
+ sigc::connection connection_legal_c;
+ sigc::connection port_legal_c;
+ sigc::connection panner_legal_c;
+
+ ChanCount _input_minimum; ///< minimum number of input channels (0 for no minimum)
+ ChanCount _input_maximum; ///< maximum number of input channels (ChanCount::INFINITE for no maximum)
+ ChanCount _output_minimum; ///< minimum number of output channels (0 for no minimum)
+ ChanCount _output_maximum; ///< maximum number of output channels (ChanCount::INFINITE for no maximum)
+
+ boost::shared_ptr<AutoBundle> _bundle_for_inputs; ///< a bundle representing our inputs
+ boost::shared_ptr<AutoBundle> _bundle_for_outputs; ///< a bundle representing our outputs
+
+ struct UserBundleInfo {
+ UserBundleInfo (IO*, boost::shared_ptr<UserBundle> b);
+
+ boost::shared_ptr<UserBundle> bundle;
+ sigc::connection configuration_will_change;
+ sigc::connection configuration_has_changed;
+ sigc::connection ports_will_change;
+ sigc::connection ports_have_changed;
+ };
+
+ std::vector<UserBundleInfo> _bundles_connected_to_outputs; ///< user bundles connected to our outputs
+ std::vector<UserBundleInfo> _bundles_connected_to_inputs; ///< user bundles connected to our inputs
+
+ static int parse_io_string (const string&, vector<string>& chns);
+
+ static int parse_gain_string (const string&, vector<string>& chns);
+
+ int set_sources (vector<string>&, void *src, bool add);
+ int set_destinations (vector<string>&, void *src, bool add);
+
+ int ensure_inputs (ChanCount, bool clear, bool lockit, void *src);
+ int ensure_outputs (ChanCount, bool clear, bool lockit, void *src);
+
+ void check_bundles_connected_to_inputs ();
+ void check_bundles_connected_to_outputs ();
+ void check_bundles (std::vector<UserBundleInfo>&, const PortSet&);
+
+ void bundle_configuration_will_change ();
+ void bundle_configuration_has_changed ();
+ void bundle_ports_will_change (int);
+ void bundle_ports_have_changed (int);
+
+ int create_ports (const XMLNode&);
+ int make_connections (const XMLNode&);
+
+ void setup_peak_meters ();
+ void meter ();
+
+ bool ensure_inputs_locked (ChanCount, bool clear, void *src);
+ bool ensure_outputs_locked (ChanCount, bool clear, void *src);
+
+ std::string build_legal_port_name (DataType type, bool for_input);
+ int32_t find_input_port_hole (const char* base);
+ int32_t find_output_port_hole (const char* base);
+
+ void create_bundles_for_inputs_and_outputs ();
+ void setup_bundles_for_inputs_and_outputs ();
+
+ void maybe_add_input_bundle_to_list (boost::shared_ptr<Bundle>, std::vector<boost::shared_ptr<Bundle> >*);
+ void maybe_add_output_bundle_to_list (boost::shared_ptr<Bundle>, std::vector<boost::shared_ptr<Bundle> >*);
+};
+
+} // namespace ARDOUR
+
+#endif /*__ardour_io_h__ */
diff --git a/libs/ardour/ardour/io_processor.h b/libs/ardour/ardour/io_processor.h
new file mode 100644
index 0000000000..a535ce3bb4
--- /dev/null
+++ b/libs/ardour/ardour/io_processor.h
@@ -0,0 +1,86 @@
+/*
+ Copyright (C) 2001 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_redirect_h__
+#define __ardour_redirect_h__
+
+#include <string>
+#include <vector>
+#include <set>
+#include <map>
+#include <boost/shared_ptr.hpp>
+#include <sigc++/signal.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/undo.h>
+
+#include <ardour/ardour.h>
+#include <ardour/processor.h>
+#include <ardour/io.h>
+#include <ardour/automation_event.h>
+
+using std::map;
+using std::set;
+using std::string;
+using std::vector;
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Session;
+
+/** A mixer strip element (Processor) with Jack ports (IO).
+ */
+class IOProcessor : public Processor
+{
+ public:
+ IOProcessor (Session&, const string& name, Placement,
+ int input_min = -1, int input_max = -1, int output_min = -1, int output_max = -1);
+ IOProcessor (const IOProcessor&);
+ virtual ~IOProcessor ();
+
+ virtual ChanCount output_streams() const { return _io->n_outputs(); }
+ virtual ChanCount input_streams () const { return _io->n_inputs(); }
+ virtual ChanCount natural_output_streams() const { return _io->n_outputs(); }
+ virtual ChanCount natural_input_streams () const { return _io->n_inputs(); }
+
+ boost::shared_ptr<IO> io() { return _io; }
+ boost::shared_ptr<const IO> io() const { return _io; }
+
+ virtual void automation_snapshot (nframes_t now, bool force) { _io->automation_snapshot(now, force); }
+
+ virtual void run_in_place (BufferSet& in, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) = 0;
+
+ void silence (nframes_t nframes, nframes_t offset);
+
+ sigc::signal<void,IOProcessor*,bool> AutomationPlaybackChanged;
+ sigc::signal<void,IOProcessor*,uint32_t> AutomationChanged;
+
+ XMLNode& state (bool full_state);
+ int set_state (const XMLNode&);
+
+ protected:
+ boost::shared_ptr<IO> _io;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_redirect_h__ */
diff --git a/libs/ardour/ardour/jack_audio_port.h b/libs/ardour/ardour/jack_audio_port.h
new file mode 100644
index 0000000000..703fb81fa3
--- /dev/null
+++ b/libs/ardour/ardour/jack_audio_port.h
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 2002 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.
+
+ $Id: port.h 712 2006-07-28 01:08:57Z drobilla $
+*/
+
+#ifndef __ardour_jack_audio_port_h__
+#define __ardour_jack_audio_port_h__
+
+#include <sigc++/signal.h>
+#include <pbd/failed_constructor.h>
+#include <ardour/ardour.h>
+#include <ardour/jack_port.h>
+#include <ardour/audio_port.h>
+
+namespace ARDOUR {
+
+class AudioEngine;
+class JackAudioPort : public JackPort, public BaseAudioPort {
+ public:
+ void cycle_start (nframes_t nframes, nframes_t offset) {
+ _buffer->set_data ((Sample*) jack_port_get_buffer (_port, nframes) + offset, nframes);
+ }
+
+ int reestablish ();
+
+ protected:
+ friend class AudioPort;
+
+ JackAudioPort (const std::string& name, Flags flags, AudioBuffer* buf);
+
+ AudioBuffer* _source_buffer;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_jack_audio_port_h__ */
diff --git a/libs/ardour/ardour/jack_midi_port.h b/libs/ardour/ardour/jack_midi_port.h
new file mode 100644
index 0000000000..d1fb5cc4fb
--- /dev/null
+++ b/libs/ardour/ardour/jack_midi_port.h
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2002 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.
+
+ $Id: port.h 712 2006-07-28 01:08:57Z drobilla $
+*/
+
+#ifndef __ardour_jack_midi_port_h__
+#define __ardour_jack_midi_port_h__
+
+#include <sigc++/signal.h>
+#include <pbd/failed_constructor.h>
+#include <ardour/ardour.h>
+#include <jack/jack.h>
+#include <jack/midiport.h>
+#include <ardour/port.h>
+#include <ardour/jack_port.h>
+#include <ardour/base_midi_port.h>
+#include <ardour/midi_buffer.h>
+
+namespace ARDOUR {
+
+class MidiEngine;
+
+class JackMidiPort : public JackPort, public BaseMidiPort {
+ public:
+ void cycle_start (nframes_t nframes, nframes_t offset);
+ void cycle_end (nframes_t nframes, nframes_t offset);
+ void set_buffer (MidiBuffer& buf);
+
+ protected:
+ friend class MidiPort;
+
+ JackMidiPort (const std::string&, Flags, MidiBuffer*);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_jack_midi_port_h__ */
diff --git a/libs/ardour/ardour/jack_port.h b/libs/ardour/ardour/jack_port.h
new file mode 100644
index 0000000000..3fa0008e17
--- /dev/null
+++ b/libs/ardour/ardour/jack_port.h
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 2002 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_jack_port_h__
+#define __ardour_jack_port_h__
+
+#include <sigc++/signal.h>
+#include <pbd/failed_constructor.h>
+#include <ardour/port.h>
+#include <jack/jack.h>
+
+namespace ARDOUR {
+
+class AudioEngine;
+class Buffer;
+
+/** Abstract class representing JACK ports
+ */
+class JackPort : public virtual Port, public PortConnectableByName {
+ public:
+
+ ~JackPort();
+
+ std::string short_name() const {
+ return jack_port_short_name (_port);
+ }
+
+ int set_name (const std::string& str);
+
+ bool connected () const {
+ return jack_port_connected (_port);
+ }
+
+ int reestablish ();
+ int reconnect ();
+
+ int connect (Port& other) {
+ return connect (other.name());
+ }
+
+ int disconnect (Port& other) {
+ return disconnect (other.name());
+ }
+
+ int disconnect_all ();
+
+ // connect-by-name API
+
+ int connect (const std::string& other_name);
+ int disconnect (const std::string& other_name);
+
+ bool connected_to (const std::string& portname) const {
+ return jack_port_connected_to (_port, portname.c_str());
+ }
+
+ int get_connections (std::vector<std::string>& names) const;
+
+ bool monitoring_input () const {
+ return jack_port_monitoring_input (_port);
+ }
+
+ void ensure_monitor_input (bool yn) {
+ jack_port_ensure_monitor (_port, yn);
+ }
+
+ /*XXX completely bloody useless imho*/
+ void request_monitor_input (bool yn) {
+ jack_port_request_monitor (_port, yn);
+ }
+
+ nframes_t latency () const {
+ return jack_port_get_latency (_port);
+ }
+
+ nframes_t total_latency() const;
+
+ void set_latency (nframes_t nframes) {
+ jack_port_set_latency (_port, nframes);
+ }
+
+
+ protected:
+ friend class AudioEngine;
+
+ JackPort (const std::string&, DataType type, Flags flags);
+ jack_port_t* _port;
+
+ int disconnect ();
+ void recompute_total_latency() const;
+
+ std::set<std::string> _named_connections;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_jack_port_h__ */
diff --git a/libs/ardour/ardour/ladspa.h b/libs/ardour/ardour/ladspa.h
new file mode 100644
index 0000000000..e552f35bb5
--- /dev/null
+++ b/libs/ardour/ardour/ladspa.h
@@ -0,0 +1,606 @@
+/* ladspa.h
+
+ Linux Audio Developer's Simple Plugin API Version 1.1[LGPL].
+ Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis,
+ Stefan Westerfeld.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License
+ as published by the Free Software Foundation; either version 2.1 of
+ the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA. */
+
+#ifndef LADSPA_INCLUDED
+#define LADSPA_INCLUDED
+
+#define LADSPA_VERSION "1.1"
+#define LADSPA_VERSION_MAJOR 1
+#define LADSPA_VERSION_MINOR 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************/
+
+/* Overview:
+
+ There is a large number of synthesis packages in use or development
+ on the Linux platform at this time. This API (`The Linux Audio
+ Developer's Simple Plugin API') attempts to give programmers the
+ ability to write simple `plugin' audio processors in C/C++ and link
+ them dynamically (`plug') into a range of these packages (`hosts').
+ It should be possible for any host and any plugin to communicate
+ completely through this interface.
+
+ This API is deliberately short and simple. To achieve compatibility
+ with a range of promising Linux sound synthesis packages it
+ attempts to find the `greatest common divisor' in their logical
+ behaviour. Having said this, certain limiting decisions are
+ implicit, notably the use of a fixed type (LADSPA_Data) for all
+ data transfer and absence of a parameterised `initialisation'
+ phase. See below for the LADSPA_Data typedef.
+
+ Plugins are expected to distinguish between control and audio
+ data. Plugins have `ports' that are inputs or outputs for audio or
+ control data and each plugin is `run' for a `block' corresponding
+ to a short time interval measured in samples. Audio data is
+ communicated using arrays of LADSPA_Data, allowing a block of audio
+ to be processed by the plugin in a single pass. Control data is
+ communicated using single LADSPA_Data values. Control data has a
+ single value at the start of a call to the `run()' or `run_adding()'
+ function, and may be considered to remain this value for its
+ duration. The plugin may assume that all its input and output ports
+ have been connected to the relevant data location (see the
+ `connect_port()' function below) before it is asked to run.
+
+ Plugins will reside in shared object files suitable for dynamic
+ linking by dlopen() and family. The file will provide a number of
+ `plugin types' that can be used to instantiate actual plugins
+ (sometimes known as `plugin instances') that can be connected
+ together to perform tasks.
+
+ This API contains very limited error-handling. */
+
+/*****************************************************************************/
+
+/* Fundamental data type passed in and out of plugin. This data type
+ is used to communicate audio samples and control values. It is
+ assumed that the plugin will work sensibly given any numeric input
+ value although it may have a preferred range (see hints below).
+
+ For audio it is generally assumed that 1.0f is the `0dB' reference
+ amplitude and is a `normal' signal level. */
+
+typedef float LADSPA_Data;
+
+/*****************************************************************************/
+
+/* Special Plugin Properties:
+
+ Optional features of the plugin type are encapsulated in the
+ LADSPA_Properties type. This is assembled by ORing individual
+ properties together. */
+
+
+typedef int LADSPA_Properties;
+
+/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a
+ real-time dependency (e.g. listens to a MIDI device) and so its
+ output must not be cached or subject to significant latency. */
+#define LADSPA_PROPERTY_REALTIME 0x1
+
+/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin
+ may cease to work correctly if the host elects to use the same data
+ location for both input and output (see connect_port()). This
+ should be avoided as enabling this flag makes it impossible for
+ hosts to use the plugin to process audio `in-place.' */
+#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2
+
+/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin
+ is capable of running not only in a conventional host but also in a
+ `hard real-time' environment. To qualify for this the plugin must
+ satisfy all of the following:
+
+ (1) The plugin must not use malloc(), free() or other heap memory
+ management within its run() or run_adding() functions. All new
+ memory used in run() must be managed via the stack. These
+ restrictions only apply to the run() function.
+
+ (2) The plugin will not attempt to make use of any library
+ functions with the exceptions of functions in the ANSI standard C
+ and C maths libraries, which the host is expected to provide.
+
+ (3) The plugin will not access files, devices, pipes, sockets, IPC
+ or any other mechanism that might result in process or thread
+ blocking.
+
+ (4) The plugin will take an amount of time to execute a run() or
+ run_adding() call approximately of form (A+B*SampleCount) where A
+ and B depend on the machine and host in use. This amount of time
+ may not depend on input signals or plugin state. The host is left
+ the responsibility to perform timings to estimate upper bounds for
+ A and B. */
+#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4
+
+#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME)
+#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN)
+#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE)
+
+/*****************************************************************************/
+
+/* Plugin Ports:
+
+ Plugins have `ports' that are inputs or outputs for audio or
+ data. Ports can communicate arrays of LADSPA_Data (for audio
+ inputs/outputs) or single LADSPA_Data values (for control
+ input/outputs). This information is encapsulated in the
+ LADSPA_PortDescriptor type which is assembled by ORing individual
+ properties together.
+
+ Note that a port must be an input or an output port but not both
+ and that a port must be a control or audio port but not both. */
+
+
+typedef int LADSPA_PortDescriptor;
+
+/* Property LADSPA_PORT_INPUT indicates that the port is an input. */
+#define LADSPA_PORT_INPUT 0x1
+
+/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */
+#define LADSPA_PORT_OUTPUT 0x2
+
+/* Property LADSPA_PORT_CONTROL indicates that the port is a control
+ port. */
+#define LADSPA_PORT_CONTROL 0x4
+
+/* Property LADSPA_PORT_AUDIO indicates that the port is a audio
+ port. */
+#define LADSPA_PORT_AUDIO 0x8
+
+#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT)
+#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT)
+#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL)
+#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO)
+
+/*****************************************************************************/
+
+/* Plugin Port Range Hints:
+
+ The host may wish to provide a representation of data entering or
+ leaving a plugin (e.g. to generate a GUI automatically). To make
+ this more meaningful, the plugin should provide `hints' to the host
+ describing the usual values taken by the data.
+
+ Note that these are only hints. The host may ignore them and the
+ plugin must not assume that data supplied to it is meaningful. If
+ the plugin receives invalid input data it is expected to continue
+ to run without failure and, where possible, produce a sensible
+ output (e.g. a high-pass filter given a negative cutoff frequency
+ might switch to an all-pass mode).
+
+ Hints are meaningful for all input and output ports but hints for
+ input control ports are expected to be particularly useful.
+
+ More hint information is encapsulated in the
+ LADSPA_PortRangeHintDescriptor type which is assembled by ORing
+ individual hint types together. Hints may require further
+ LowerBound and UpperBound information.
+
+ All the hint information for a particular port is aggregated in the
+ LADSPA_PortRangeHint structure. */
+
+
+typedef int LADSPA_PortRangeHintDescriptor;
+
+/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field
+ of the LADSPA_PortRangeHint should be considered meaningful. The
+ value in this field should be considered the (inclusive) lower
+ bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+ specified then the value of LowerBound should be multiplied by the
+ sample rate. */
+#define LADSPA_HINT_BOUNDED_BELOW 0x1
+
+/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field
+ of the LADSPA_PortRangeHint should be considered meaningful. The
+ value in this field should be considered the (inclusive) upper
+ bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also
+ specified then the value of UpperBound should be multiplied by the
+ sample rate. */
+#define LADSPA_HINT_BOUNDED_ABOVE 0x2
+
+/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be
+ considered a Boolean toggle. Data less than or equal to zero should
+ be considered `off' or `false,' and data above zero should be
+ considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in
+ conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or
+ LADSPA_HINT_DEFAULT_1. */
+#define LADSPA_HINT_TOGGLED 0x4
+
+/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified
+ should be interpreted as multiples of the sample rate. For
+ instance, a frequency range from 0Hz to the Nyquist frequency (half
+ the sample rate) could be requested by this hint in conjunction
+ with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds
+ at all must support this hint to retain meaning. */
+#define LADSPA_HINT_SAMPLE_RATE 0x8
+
+/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the
+ user will find it more intuitive to view values using a logarithmic
+ scale. This is particularly useful for frequencies and gains. */
+#define LADSPA_HINT_LOGARITHMIC 0x10
+
+/* Hint LADSPA_HINT_INTEGER indicates that a user interface would
+ probably wish to provide a stepped control taking only integer
+ values. Any bounds set should be slightly wider than the actual
+ integer range required to avoid floating point rounding errors. For
+ instance, the integer set {0,1,2,3} might be described as [-0.1,
+ 3.1]. */
+#define LADSPA_HINT_INTEGER 0x20
+
+/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal'
+ value for the port that is sensible as a default. For instance,
+ this value is suitable for use as an initial value in a user
+ interface or as a value the host might assign to a control port
+ when the user has not provided one. Defaults are encoded using a
+ mask so only one default may be specified for a port. Some of the
+ hints make use of lower and upper bounds, in which case the
+ relevant bound or bounds must be available and
+ LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting
+ default must be rounded if LADSPA_HINT_INTEGER is present. Default
+ values were introduced in LADSPA v1.1. */
+#define LADSPA_HINT_DEFAULT_MASK 0x3C0
+
+/* This default values indicates that no default is provided. */
+#define LADSPA_HINT_DEFAULT_NONE 0x0
+
+/* This default hint indicates that the suggested lower bound for the
+ port should be used. */
+#define LADSPA_HINT_DEFAULT_MINIMUM 0x40
+
+/* This default hint indicates that a low value between the suggested
+ lower and upper bounds should be chosen. For ports with
+ LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 +
+ log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper
+ * 0.25). */
+#define LADSPA_HINT_DEFAULT_LOW 0x80
+
+/* This default hint indicates that a middle value between the
+ suggested lower and upper bounds should be chosen. For ports with
+ LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 +
+ log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper *
+ 0.5). */
+#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0
+
+/* This default hint indicates that a high value between the suggested
+ lower and upper bounds should be chosen. For ports with
+ LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 +
+ log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper
+ * 0.75). */
+#define LADSPA_HINT_DEFAULT_HIGH 0x100
+
+/* This default hint indicates that the suggested upper bound for the
+ port should be used. */
+#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140
+
+/* This default hint indicates that the number 0 should be used. Note
+ that this default may be used in conjunction with
+ LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_0 0x200
+
+/* This default hint indicates that the number 1 should be used. Note
+ that this default may be used in conjunction with
+ LADSPA_HINT_TOGGLED. */
+#define LADSPA_HINT_DEFAULT_1 0x240
+
+/* This default hint indicates that the number 100 should be used. */
+#define LADSPA_HINT_DEFAULT_100 0x280
+
+/* This default hint indicates that the Hz frequency of `concert A'
+ should be used. This will be 440 unless the host uses an unusual
+ tuning convention, in which case it may be within a few Hz. */
+#define LADSPA_HINT_DEFAULT_440 0x2C0
+
+#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW)
+#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE)
+#define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED)
+#define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE)
+#define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC)
+#define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER)
+
+#define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK)
+#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_MINIMUM)
+#define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_LOW)
+#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_MIDDLE)
+#define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_HIGH)
+#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_MAXIMUM)
+#define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_0)
+#define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_1)
+#define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_100)
+#define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \
+ == LADSPA_HINT_DEFAULT_440)
+
+typedef struct _LADSPA_PortRangeHint {
+
+ /* Hints about the port. */
+ LADSPA_PortRangeHintDescriptor HintDescriptor;
+
+ /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When
+ LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+ multiplied by the relevant sample rate. */
+ LADSPA_Data LowerBound;
+
+ /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When
+ LADSPA_HINT_SAMPLE_RATE is also active then this value should be
+ multiplied by the relevant sample rate. */
+ LADSPA_Data UpperBound;
+
+} LADSPA_PortRangeHint;
+
+/*****************************************************************************/
+
+/* Plugin Handles:
+
+ This plugin handle indicates a particular instance of the plugin
+ concerned. It is valid to compare this to NULL (0 for C++) but
+ otherwise the host should not attempt to interpret it. The plugin
+ may use it to reference internal instance data. */
+
+typedef void * LADSPA_Handle;
+
+/*****************************************************************************/
+
+/* Descriptor for a Type of Plugin:
+
+ This structure is used to describe a plugin type. It provides a
+ number of functions to examine the type, instantiate it, link it to
+ buffers and workspaces and to run it. */
+
+typedef struct _LADSPA_Descriptor {
+
+ /* This numeric identifier indicates the plugin type
+ uniquely. Plugin programmers may reserve ranges of IDs from a
+ central body to avoid clashes. Hosts may assume that IDs are
+ below 0x1000000. */
+ unsigned long UniqueID;
+
+ /* This identifier can be used as a unique, case-sensitive
+ identifier for the plugin type within the plugin file. Plugin
+ types should be identified by file and label rather than by index
+ or plugin name, which may be changed in new plugin
+ versions. Labels must not contain white-space characters. */
+ const char * Label;
+
+ /* This indicates a number of properties of the plugin. */
+ LADSPA_Properties Properties;
+
+ /* This member points to the null-terminated name of the plugin
+ (e.g. "Sine Oscillator"). */
+ const char * Name;
+
+ /* This member points to the null-terminated string indicating the
+ maker of the plugin. This can be an empty string but not NULL. */
+ const char * Maker;
+
+ /* This member points to the null-terminated string indicating any
+ copyright applying to the plugin. If no Copyright applies the
+ string "None" should be used. */
+ const char * Copyright;
+
+ /* This indicates the number of ports (input AND output) present on
+ the plugin. */
+ unsigned long PortCount;
+
+ /* This member indicates an array of port descriptors. Valid indices
+ vary from 0 to PortCount-1. */
+ const LADSPA_PortDescriptor * PortDescriptors;
+
+ /* This member indicates an array of null-terminated strings
+ describing ports (e.g. "Frequency (Hz)"). Valid indices vary from
+ 0 to PortCount-1. */
+ const char * const * PortNames;
+
+ /* This member indicates an array of range hints for each port (see
+ above). Valid indices vary from 0 to PortCount-1. */
+ const LADSPA_PortRangeHint * PortRangeHints;
+
+ /* This may be used by the plugin developer to pass any custom
+ implementation data into an instantiate call. It must not be used
+ or interpreted by the host. It is expected that most plugin
+ writers will not use this facility as LADSPA_Handle should be
+ used to hold instance data. */
+ void * ImplementationData;
+
+ /* This member is a function pointer that instantiates a plugin. A
+ handle is returned indicating the new plugin instance. The
+ instantiation function accepts a sample rate as a parameter. The
+ plugin descriptor from which this instantiate function was found
+ must also be passed. This function must return NULL if
+ instantiation fails.
+
+ Note that instance initialisation should generally occur in
+ activate() rather than here. */
+ LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor,
+ unsigned long SampleRate);
+
+ /* This member is a function pointer that connects a port on an
+ instantiated plugin to a memory location at which a block of data
+ for the port will be read/written. The data location is expected
+ to be an array of LADSPA_Data for audio ports or a single
+ LADSPA_Data value for control ports. Memory issues will be
+ managed by the host. The plugin must read/write the data at these
+ locations every time run() or run_adding() is called and the data
+ present at the time of this connection call should not be
+ considered meaningful.
+
+ connect_port() may be called more than once for a plugin instance
+ to allow the host to change the buffers that the plugin is
+ reading or writing. These calls may be made before or after
+ activate() or deactivate() calls.
+
+ connect_port() must be called at least once for each port before
+ run() or run_adding() is called. When working with blocks of
+ LADSPA_Data the plugin should pay careful attention to the block
+ size passed to the run function as the block allocated may only
+ just be large enough to contain the block of samples.
+
+ Plugin writers should be aware that the host may elect to use the
+ same buffer for more than one port and even use the same buffer
+ for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN).
+ However, overlapped buffers or use of a single buffer for both
+ audio and control data may result in unexpected behaviour. */
+ void (*connect_port)(LADSPA_Handle Instance,
+ unsigned long Port,
+ LADSPA_Data * DataLocation);
+
+ /* This member is a function pointer that initialises a plugin
+ instance and activates it for use. This is separated from
+ instantiate() to aid real-time support and so that hosts can
+ reinitialise a plugin instance by calling deactivate() and then
+ activate(). In this case the plugin instance must reset all state
+ information dependent on the history of the plugin instance
+ except for any data locations provided by connect_port() and any
+ gain set by set_run_adding_gain(). If there is nothing for
+ activate() to do then the plugin writer may provide a NULL rather
+ than an empty function.
+
+ When present, hosts must call this function once before run() (or
+ run_adding()) is called for the first time. This call should be
+ made as close to the run() call as possible and indicates to
+ real-time plugins that they are now live. Plugins should not rely
+ on a prompt call to run() after activate(). activate() may not be
+ called again unless deactivate() is called first. Note that
+ connect_port() may be called before or after a call to
+ activate(). */
+ void (*activate)(LADSPA_Handle Instance);
+
+ /* This method is a function pointer that runs an instance of a
+ plugin for a block. Two parameters are required: the first is a
+ handle to the particular instance to be run and the second
+ indicates the block size (in samples) for which the plugin
+ instance may run.
+
+ Note that if an activate() function exists then it must be called
+ before run() or run_adding(). If deactivate() is called for a
+ plugin instance then the plugin instance may not be reused until
+ activate() has been called again.
+
+ If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE
+ then there are various things that the plugin should not do
+ within the run() or run_adding() functions (see above). */
+ void (*run)(LADSPA_Handle Instance,
+ unsigned long SampleCount);
+
+ /* This method is a function pointer that runs an instance of a
+ plugin for a block. This has identical behaviour to run() except
+ in the way data is output from the plugin. When run() is used,
+ values are written directly to the memory areas associated with
+ the output ports. However when run_adding() is called, values
+ must be added to the values already present in the memory
+ areas. Furthermore, output values written must be scaled by the
+ current gain set by set_run_adding_gain() (see below) before
+ addition.
+
+ run_adding() is optional. When it is not provided by a plugin,
+ this function pointer must be set to NULL. When it is provided,
+ the function set_run_adding_gain() must be provided also. */
+ void (*run_adding)(LADSPA_Handle Instance,
+ unsigned long SampleCount);
+
+ /* This method is a function pointer that sets the output gain for
+ use when run_adding() is called (see above). If this function is
+ never called the gain is assumed to default to 1. Gain
+ information should be retained when activate() or deactivate()
+ are called.
+
+ This function should be provided by the plugin if and only if the
+ run_adding() function is provided. When it is absent this
+ function pointer must be set to NULL. */
+ void (*set_run_adding_gain)(LADSPA_Handle Instance,
+ LADSPA_Data Gain);
+
+ /* This is the counterpart to activate() (see above). If there is
+ nothing for deactivate() to do then the plugin writer may provide
+ a NULL rather than an empty function.
+
+ Hosts must deactivate all activated units after they have been
+ run() (or run_adding()) for the last time. This call should be
+ made as close to the last run() call as possible and indicates to
+ real-time plugins that they are no longer live. Plugins should
+ not rely on prompt deactivation. Note that connect_port() may be
+ called before or after a call to deactivate().
+
+ Deactivation is not similar to pausing as the plugin instance
+ will be reinitialised when activate() is called to reuse it. */
+ void (*deactivate)(LADSPA_Handle Instance);
+
+ /* Once an instance of a plugin has been finished with it can be
+ deleted using the following function. The instance handle passed
+ ceases to be valid after this call.
+
+ If activate() was called for a plugin instance then a
+ corresponding call to deactivate() must be made before cleanup()
+ is called. */
+ void (*cleanup)(LADSPA_Handle Instance);
+
+} LADSPA_Descriptor;
+
+/**********************************************************************/
+
+/* Accessing a Plugin: */
+
+/* The exact mechanism by which plugins are loaded is host-dependent,
+ however all most hosts will need to know is the name of shared
+ object file containing the plugin types. To allow multiple hosts to
+ share plugin types, hosts may wish to check for environment
+ variable LADSPA_PATH. If present, this should contain a
+ colon-separated path indicating directories that should be searched
+ (in order) when loading plugin types.
+
+ A plugin programmer must include a function called
+ "ladspa_descriptor" with the following function prototype within
+ the shared object file. This function will have C-style linkage (if
+ you are using C++ this is taken care of by the `extern "C"' clause
+ at the top of the file).
+
+ A host will find the plugin shared object file by one means or
+ another, find the ladspa_descriptor() function, call it, and
+ proceed from there.
+
+ Plugin types are accessed by index (not ID) using values from 0
+ upwards. Out of range indexes must result in this function
+ returning NULL, so the plugin count can be determined by checking
+ for the least index that results in NULL being returned. */
+
+const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index);
+
+/* Datatype corresponding to the ladspa_descriptor() function. */
+typedef const LADSPA_Descriptor *
+(*LADSPA_Descriptor_Function)(unsigned long Index);
+
+/**********************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LADSPA_INCLUDED */
+
+/* EOF */
diff --git a/libs/ardour/ardour/ladspa_plugin.h b/libs/ardour/ardour/ladspa_plugin.h
new file mode 100644
index 0000000000..b26e4120b1
--- /dev/null
+++ b/libs/ardour/ardour/ladspa_plugin.h
@@ -0,0 +1,147 @@
+/*
+ Copyright (C) 2000-2006 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_ladspa_plugin_h__
+#define __ardour_ladspa_plugin_h__
+
+#include <set>
+#include <vector>
+#include <string>
+#include <dlfcn.h>
+
+#include <sigc++/signal.h>
+
+#include <pbd/stateful.h>
+
+#include <jack/types.h>
+#include <ardour/ladspa.h>
+#include <ardour/plugin.h>
+
+namespace ARDOUR {
+class AudioEngine;
+class Session;
+
+class LadspaPlugin : public ARDOUR::Plugin
+{
+ public:
+ LadspaPlugin (void *module, ARDOUR::AudioEngine&, ARDOUR::Session&, uint32_t index, nframes_t sample_rate);
+ LadspaPlugin (const LadspaPlugin &);
+ ~LadspaPlugin ();
+
+ /* Plugin interface */
+
+ std::string unique_id() const;
+ const char* label() const { return _descriptor->Label; }
+ const char* name() const { return _descriptor->Name; }
+ const char* maker() const { return _descriptor->Maker; }
+ uint32_t parameter_count() const { return _descriptor->PortCount; }
+ float default_value (uint32_t port);
+ nframes_t signal_latency() const;
+ void set_parameter (uint32_t port, float val);
+ 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;
+
+ std::set<Parameter> automatable() const;
+
+ void activate () {
+ if (!_was_activated && _descriptor->activate)
+ _descriptor->activate (_handle);
+
+ _was_activated = true;
+ }
+
+ void deactivate () {
+ if (_was_activated && _descriptor->deactivate)
+ _descriptor->deactivate (_handle);
+
+ _was_activated = false;
+ }
+
+ void cleanup () {
+ activate();
+ deactivate();
+
+ if (_descriptor->cleanup)
+ _descriptor->cleanup (_handle);
+ }
+
+ void set_block_size (nframes_t nframes) {}
+
+ int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset);
+ std::string describe_parameter (Parameter);
+ std::string state_node_name() const { return "ladspa"; }
+ void print_parameter (uint32_t, char*, uint32_t len) const;
+
+ bool parameter_is_audio(uint32_t) const;
+ bool parameter_is_control(uint32_t) const;
+ bool parameter_is_input(uint32_t) const;
+ bool parameter_is_output(uint32_t) const;
+ bool parameter_is_toggled(uint32_t) const;
+
+ XMLNode& get_state();
+ int set_state(const XMLNode& node);
+ bool save_preset(std::string name);
+
+ bool has_editor() const { return false; }
+
+ int require_output_streams (uint32_t);
+
+ /* LADSPA extras */
+
+ LADSPA_Properties properties() const { return _descriptor->Properties; }
+ uint32_t index() const { return _index; }
+ const char * copyright() const { return _descriptor->Copyright; }
+ LADSPA_PortDescriptor port_descriptor(uint32_t i) const { return _descriptor->PortDescriptors[i]; }
+ const LADSPA_PortRangeHint* port_range_hints() const { return _descriptor->PortRangeHints; }
+ const char * const * port_names() const { return _descriptor->PortNames; }
+
+ void set_gain (float gain) { _descriptor->set_run_adding_gain (_handle, gain); }
+ void run_adding (uint32_t nsamples) { _descriptor->run_adding (_handle, nsamples); }
+ void connect_port (uint32_t port, float *ptr) { _descriptor->connect_port (_handle, port, ptr); }
+
+ private:
+ void* _module;
+ const LADSPA_Descriptor* _descriptor;
+ LADSPA_Handle _handle;
+ nframes_t _sample_rate;
+ LADSPA_Data* _control_data;
+ LADSPA_Data* _shadow_data;
+ LADSPA_Data* _latency_control_port;
+ uint32_t _index;
+ bool _was_activated;
+
+ void init (void *mod, uint32_t index, nframes_t rate);
+ void run_in_place (nframes_t nsamples);
+ void latency_compute_run ();
+};
+
+class LadspaPluginInfo : public PluginInfo {
+ public:
+ LadspaPluginInfo () { };
+ ~LadspaPluginInfo () { };
+
+ PluginPtr load (Session& session);
+};
+
+typedef boost::shared_ptr<LadspaPluginInfo> LadspaPluginInfoPtr;
+
+} // namespace ARDOUR
+
+#endif /* __ardour_ladspa_plugin_h__ */
diff --git a/libs/ardour/ardour/latent.h b/libs/ardour/ardour/latent.h
new file mode 100644
index 0000000000..11bdf11370
--- /dev/null
+++ b/libs/ardour/ardour/latent.h
@@ -0,0 +1,26 @@
+#ifndef __ardour_latent_h__
+#define __ardour_latent_h__
+
+#include <ardour/types.h>
+
+namespace ARDOUR {
+
+class Latent {
+ public:
+ Latent() : _own_latency (0), _user_latency (0) {}
+ virtual ~Latent() {}
+
+ virtual nframes_t signal_latency() const = 0;
+ nframes_t user_latency () const { return _user_latency; }
+
+ virtual void set_latency_delay (nframes_t val) { _own_latency = val; }
+ virtual void set_user_latency (nframes_t val) { _user_latency = val; }
+
+ protected:
+ nframes_t _own_latency;
+ nframes_t _user_latency;
+};
+
+}
+
+#endif /* __ardour_latent_h__*/
diff --git a/libs/ardour/ardour/location.h b/libs/ardour/ardour/location.h
new file mode 100644
index 0000000000..53d9489823
--- /dev/null
+++ b/libs/ardour/ardour/location.h
@@ -0,0 +1,206 @@
+/*
+ Copyright (C) 2000 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_location_h__
+#define __ardour_location_h__
+
+#include <string>
+#include <list>
+#include <iostream>
+#include <map>
+
+#include <sys/types.h>
+#include <sigc++/signal.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/undo.h>
+#include <pbd/stateful.h>
+#include <pbd/statefuldestructible.h>
+
+#include <ardour/ardour.h>
+
+using std::string;
+
+namespace ARDOUR {
+
+class Location : public PBD::StatefulDestructible
+{
+ public:
+ enum Flags {
+ IsMark = 0x1,
+ IsAutoPunch = 0x2,
+ IsAutoLoop = 0x4,
+ IsHidden = 0x8,
+ IsCDMarker = 0x10,
+ IsEnd = 0x20,
+ IsRangeMarker = 0x40,
+ IsStart = 0x80
+ };
+
+ Location (nframes_t sample_start,
+ nframes_t sample_end,
+ const string &name,
+ Flags bits = Flags(0))
+
+ : _name (name),
+ _start (sample_start),
+ _end (sample_end),
+ _flags (bits),
+ _locked (false) { }
+
+ Location () {
+ _start = 0;
+ _end = 0;
+ _flags = Flags (0);
+ _locked = false;
+ }
+
+ Location (const Location& other);
+ Location (const XMLNode&);
+ Location* operator= (const Location& other);
+
+ bool locked() const { return _locked; }
+ void lock() { _locked = true; changed (this); }
+ void unlock() { _locked = false; changed (this); }
+
+ nframes_t start() const { return _start; }
+ nframes_t end() const { return _end; }
+ nframes_t length() const { return _end - _start; }
+
+ int set_start (nframes_t s);
+ int set_end (nframes_t e);
+ int set (nframes_t start, nframes_t end);
+
+ int move_to (nframes_t pos);
+
+ const string& name() { return _name; }
+ void set_name (const string &str) { _name = str; name_changed(this); }
+
+ void set_auto_punch (bool yn, void *src);
+ void set_auto_loop (bool yn, void *src);
+ void set_hidden (bool yn, void *src);
+ void set_cd (bool yn, void *src);
+ void set_is_end (bool yn, void* src);
+ void set_is_start (bool yn, void* src);
+
+ bool is_auto_punch () { return _flags & IsAutoPunch; }
+ bool is_auto_loop () { return _flags & IsAutoLoop; }
+ bool is_mark () { return _flags & IsMark; }
+ bool is_hidden () { return _flags & IsHidden; }
+ bool is_cd_marker () { return _flags & IsCDMarker; }
+ bool is_end() { return _flags & IsEnd; }
+ bool is_start() { return _flags & IsStart; }
+ bool is_range_marker() { return _flags & IsRangeMarker; }
+
+ sigc::signal<void,Location*> name_changed;
+ sigc::signal<void,Location*> end_changed;
+ sigc::signal<void,Location*> start_changed;
+
+ sigc::signal<void,Location*,void*> FlagsChanged;
+
+ /* this is sent only when both start&end change at the same time */
+
+ sigc::signal<void,Location*> changed;
+
+ /* CD Track / CD-Text info */
+
+ std::map<string, string> cd_info;
+ XMLNode& cd_info_node (const string &, const string &);
+
+ XMLNode& get_state (void);
+ int set_state (const XMLNode&);
+
+ private:
+ string _name;
+ nframes_t _start;
+ nframes_t _end;
+ Flags _flags;
+ bool _locked;
+
+ void set_mark (bool yn);
+ bool set_flag_internal (bool yn, Flags flag);
+};
+
+class Locations : public PBD::StatefulDestructible
+{
+ public:
+ typedef std::list<Location *> LocationList;
+
+ Locations ();
+ ~Locations ();
+
+ const LocationList& list() { return locations; }
+
+ void add (Location *, bool make_current = false);
+ void remove (Location *);
+ void clear ();
+ void clear_markers ();
+ void clear_ranges ();
+
+ XMLNode& get_state (void);
+ int set_state (const XMLNode&);
+ Location *get_location_by_id(PBD::ID);
+
+ Location* auto_loop_location () const;
+ Location* auto_punch_location () const;
+ Location* end_location() const;
+ Location* start_location() const;
+
+ int next_available_name(string& result,string base);
+ uint32_t num_range_markers() const;
+
+ int set_current (Location *, bool want_lock = true);
+ Location *current () const { return current_location; }
+
+ Location *first_location_before (nframes_t, bool include_special_ranges = false);
+ Location *first_location_after (nframes_t, bool include_special_ranges = false);
+
+ nframes_t first_mark_before (nframes_t, bool include_special_ranges = false);
+ nframes_t first_mark_after (nframes_t, bool include_special_ranges = false);
+
+ sigc::signal<void,Location*> current_changed;
+ sigc::signal<void> changed;
+ sigc::signal<void,Location*> added;
+ sigc::signal<void,Location*> removed;
+ sigc::signal<void,Change> StateChanged;
+
+ template<class T> void apply (T& obj, void (T::*method)(LocationList&)) {
+ Glib::Mutex::Lock lm (lock);
+ (obj.*method)(locations);
+ }
+
+ template<class T1, class T2> void apply (T1& obj, void (T1::*method)(LocationList&, T2& arg), T2& arg) {
+ Glib::Mutex::Lock lm (lock);
+ (obj.*method)(locations, arg);
+ }
+
+ private:
+
+ LocationList locations;
+ Location *current_location;
+ mutable Glib::Mutex lock;
+
+ int set_current_unlocked (Location *);
+ void location_changed (Location*);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_location_h__ */
diff --git a/libs/ardour/ardour/logcurve.h b/libs/ardour/ardour/logcurve.h
new file mode 100644
index 0000000000..dd58263313
--- /dev/null
+++ b/libs/ardour/ardour/logcurve.h
@@ -0,0 +1,132 @@
+/*
+ Copyright (C) 2001 Steve Harris & 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_logcurve_h__
+#define __ardour_logcurve_h__
+
+#include <pbd/fastlog.h>
+#include <glibmm/thread.h>
+
+namespace ARDOUR {
+
+class LogCurve {
+ public:
+ LogCurve (float steepness = 0.2, uint32_t len = 0) {
+ l = len;
+ S = steepness;
+ a = log(S);
+ b = 1.0f / log(1.0f + (1.0f / S));
+ }
+
+ bool operator== (const LogCurve& other) const {
+ return S == other.S && l == other.l;
+ }
+
+ bool operator!= (const LogCurve& other) const {
+ return S != other.S || l != other.l;
+ }
+
+ float value (float frac) const {
+ return (fast_log(frac + S) - a) * b;
+ }
+
+ float value (uint32_t pos) const {
+ return (fast_log(((float) pos/l) + S) - a) * b;
+ }
+
+ float invert_value (float frac) const {
+ return (a - fast_log(frac + S)) * b;
+ }
+
+ float invert_value (uint32_t pos) const {
+ return (a - fast_log(((float) pos/l) + S)) * b;
+ }
+
+ void fill (float *vec, uint32_t veclen, bool invert) const {
+ float dx = 1.0f/veclen;
+ float x;
+ uint32_t i;
+
+ if (!invert) {
+
+ vec[0] = 0.0;
+ vec[veclen-1] = 1.0;
+
+ for (i = 1, x = 0; i < veclen - 1; x += dx, i++) {
+ vec[i] = value (x);
+ }
+
+ } else {
+
+ vec[0] = 1.0;
+ vec[veclen-1] = 0.0;
+
+ for (i = veclen-2, x = 0.0f; i > 0; x += dx, i--) {
+ vec[i] = value (x);
+ }
+ }
+ }
+
+ float steepness() const { return S; }
+ uint32_t length() const { return l; }
+
+ void set_steepness (float steepness) {
+ S = steepness;
+ a = log(S);
+ b = 1.0f / log(1.0f + (1.0f / S));
+ }
+ void set_length (uint32_t len) { l = len; }
+
+ mutable Glib::Mutex lock;
+
+ protected:
+ float a;
+ float b;
+ float S;
+ uint32_t l;
+};
+
+class LogCurveIn : public LogCurve
+{
+ public:
+ LogCurveIn (float steepness = 0.2, uint32_t len = 0)
+ : LogCurve (steepness, len) {}
+
+ float value (float frac) const {
+ return (fast_log(frac + S) - a) * b;
+ }
+
+ float value (uint32_t pos) const {
+ return (fast_log(((float) pos/l) + S) - a) * b;
+ }
+};
+
+class LogCurveOut : public LogCurve
+{
+ public:
+ LogCurveOut (float steepness = 0.2, uint32_t len = 0)
+ : LogCurve (steepness, len) {}
+
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_logcurve_h__ */
+
+
diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h
new file mode 100644
index 0000000000..d20ece65bd
--- /dev/null
+++ b/libs/ardour/ardour/lv2_plugin.h
@@ -0,0 +1,171 @@
+/*
+ Copyright (C) 2008 Paul Davis
+ Author: Dave Robillard
+
+ 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_lv2_plugin_h__
+#define __ardour_lv2_plugin_h__
+
+#include <set>
+#include <vector>
+#include <string>
+#include <dlfcn.h>
+
+#include <sigc++/signal.h>
+
+#include <pbd/stateful.h>
+
+#include <jack/types.h>
+#include <slv2/slv2.h>
+#include <ardour/plugin.h>
+
+namespace ARDOUR {
+class AudioEngine;
+class Session;
+struct LV2World;
+
+class LV2Plugin : public ARDOUR::Plugin
+{
+ public:
+ 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_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;
+ void set_parameter (uint32_t port, float val);
+ 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;
+
+ void activate () {
+ if (!_was_activated) {
+ slv2_instance_activate(_instance);
+ _was_activated = true;
+ }
+ }
+
+ void deactivate () {
+ if (_was_activated) {
+ slv2_instance_deactivate(_instance);
+ _was_activated = false;
+ }
+ }
+
+ void cleanup () {
+ activate();
+ deactivate();
+ slv2_instance_free(_instance);
+ _instance = NULL;
+ }
+
+ void set_block_size (nframes_t nframes) {}
+
+ int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset);
+ std::string describe_parameter (Parameter);
+ std::string state_node_name() const { return "lv2"; }
+ void print_parameter (uint32_t, char*, uint32_t len) const;
+
+ bool parameter_is_audio(uint32_t) const;
+ bool parameter_is_control(uint32_t) const;
+ bool parameter_is_midi(uint32_t) const;
+ bool parameter_is_input(uint32_t) const;
+ bool parameter_is_output(uint32_t) const;
+ bool parameter_is_toggled(uint32_t) const;
+
+ XMLNode& get_state();
+ int set_state(const XMLNode& node);
+ bool save_preset(std::string name);
+
+ bool has_editor() const { return false; }
+
+ int require_output_streams (uint32_t);
+
+ private:
+ void* _module;
+ LV2World& _world;
+ SLV2Plugin _plugin;
+ 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 (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; ///< Input port
+ SLV2Value output_class; ///< Output port
+ SLV2Value audio_class; ///< Audio port
+ SLV2Value control_class; ///< Control port
+ SLV2Value event_class; ///< Event port
+ SLV2Value midi_class; ///< MIDI event
+ SLV2Value in_place_broken;
+ SLV2Value integer;
+ SLV2Value toggled;
+ SLV2Value srate;
+};
+
+
+class LV2PluginInfo : public PluginInfo {
+public:
+ LV2PluginInfo (void* slv2_world, void* slv2_plugin);;
+ ~LV2PluginInfo ();;
+ static PluginInfoList discover (void* slv2_world);
+
+ PluginPtr load (Session& session);
+
+ void* _lv2_world;
+ void* _slv2_plugin;
+};
+
+typedef boost::shared_ptr<LV2PluginInfo> LV2PluginInfoPtr;
+
+} // namespace ARDOUR
+
+#endif /* __ardour_lv2_plugin_h__ */
diff --git a/libs/ardour/ardour/meter.h b/libs/ardour/ardour/meter.h
new file mode 100644
index 0000000000..e19c0a51ca
--- /dev/null
+++ b/libs/ardour/ardour/meter.h
@@ -0,0 +1,77 @@
+/*
+ Copyright (C) 2006 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_meter_h__
+#define __ardour_meter_h__
+
+#include <vector>
+#include <ardour/types.h>
+#include <ardour/processor.h>
+#include <pbd/fastlog.h>
+
+namespace ARDOUR {
+
+class BufferSet;
+class ChanCount;
+class Session;
+
+
+/** Meters peaks on the input and stores them for access.
+ */
+class PeakMeter : public Processor {
+public:
+ PeakMeter(Session& s) : Processor(s, "meter", PreFader) {}
+
+ void reset ();
+ void reset_max ();
+
+ bool configure_io (ChanCount in, ChanCount out);
+
+ /** Compute peaks */
+ void run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset);
+
+ float peak_power (uint32_t n) {
+ if (n < _visible_peak_power.size()) {
+ return _visible_peak_power[n];
+ } else {
+ return minus_infinity();
+ }
+ }
+
+ float max_peak_power (uint32_t n) {
+ if (n < _max_peak_power.size()) {
+ return _max_peak_power[n];
+ } else {
+ return minus_infinity();
+ }
+ }
+
+private:
+
+ friend class IO;
+ void meter();
+
+ std::vector<float> _peak_power;
+ std::vector<float> _visible_peak_power;
+ std::vector<float> _max_peak_power;
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_meter_h__
diff --git a/libs/ardour/ardour/midi_buffer.h b/libs/ardour/ardour/midi_buffer.h
new file mode 100644
index 0000000000..699f461b17
--- /dev/null
+++ b/libs/ardour/ardour/midi_buffer.h
@@ -0,0 +1,102 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Author: Dave Robillard
+
+ 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_midi_buffer_h__
+#define __ardour_midi_buffer_h__
+
+#include <midi++/event.h>
+#include <ardour/buffer.h>
+
+namespace ARDOUR {
+
+
+/** Buffer containing 8-bit unsigned char (MIDI) data. */
+class MidiBuffer : public Buffer
+{
+public:
+ MidiBuffer(size_t capacity);
+ ~MidiBuffer();
+
+ void silence(nframes_t dur, nframes_t offset=0);
+
+ void read_from(const Buffer& src, nframes_t nframes, nframes_t offset);
+
+ void copy(const MidiBuffer& copy);
+
+ bool push_back(const MIDI::Event& event);
+ bool push_back(const jack_midi_event_t& event);
+ uint8_t* reserve(double time, size_t size);
+
+ void resize(size_t);
+
+ bool merge(const MidiBuffer& a, const MidiBuffer& b);
+
+ struct iterator {
+ iterator(MidiBuffer& b, size_t i) : buffer(b), index(i) {}
+
+ inline MIDI::Event& operator*() const { return buffer[index]; }
+ inline iterator& operator++() { ++index; return *this; } // prefix
+ inline bool operator!=(const iterator& other) const { return index != other.index; }
+
+ MidiBuffer& buffer;
+ size_t index;
+ };
+
+ struct const_iterator {
+ const_iterator(const MidiBuffer& b, size_t i) : buffer(b), index(i) {}
+
+ inline const MIDI::Event& operator*() const { return buffer[index]; }
+ inline const_iterator& operator++() { ++index; return *this; } // prefix
+ inline bool operator!=(const const_iterator& other) const { return index != other.index; }
+
+ const MidiBuffer& buffer;
+ size_t index;
+ };
+
+ iterator begin() { return iterator(*this, 0); }
+ iterator end() { return iterator(*this, _size); }
+
+ const_iterator begin() const { return const_iterator(*this, 0); }
+ const_iterator end() const { return const_iterator(*this, _size); }
+
+private:
+
+ friend class iterator;
+ friend class const_iterator;
+
+ const MIDI::Event& operator[](size_t i) const { assert(i < _size); return _events[i]; }
+ MIDI::Event& operator[](size_t i) { assert(i < _size); return _events[i]; }
+
+ // FIXME: Eliminate this
+ static const size_t MAX_EVENT_SIZE = 4; // bytes
+
+ /* We use _size as "number of events", so the size of _data is
+ * (_size * MAX_EVENT_SIZE)
+ */
+
+ /* FIXME: this is utter crap. rewrite as a flat/packed buffer like MidiRingBuffer */
+
+ MIDI::Event* _events; ///< Event structs that point to offsets in _data
+ uint8_t* _data; ///< MIDI, straight up. No time stamps.
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_midi_buffer_h__
diff --git a/libs/ardour/ardour/midi_diskstream.h b/libs/ardour/ardour/midi_diskstream.h
new file mode 100644
index 0000000000..28e80816a8
--- /dev/null
+++ b/libs/ardour/ardour/midi_diskstream.h
@@ -0,0 +1,184 @@
+/*
+ Copyright (C) 2000 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.
+
+ $Id: diskstream.h 579 2006-06-12 19:56:37Z essej $
+*/
+
+#ifndef __ardour_midi_diskstream_h__
+#define __ardour_midi_diskstream_h__
+
+#include <sigc++/signal.h>
+
+#include <cmath>
+#include <cassert>
+#include <string>
+#include <queue>
+#include <map>
+#include <vector>
+
+#include <time.h>
+
+#include <pbd/fastlog.h>
+#include <pbd/ringbufferNPT.h>
+
+
+#include <ardour/ardour.h>
+#include <ardour/configuration.h>
+#include <ardour/session.h>
+#include <ardour/route_group.h>
+#include <ardour/route.h>
+#include <ardour/port.h>
+#include <ardour/utils.h>
+#include <ardour/diskstream.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/midi_ring_buffer.h>
+
+struct tm;
+
+namespace ARDOUR {
+
+class MidiEngine;
+class Send;
+class Session;
+class MidiPlaylist;
+class SMFSource;
+class IO;
+
+class MidiDiskstream : public Diskstream
+{
+ public:
+ MidiDiskstream (Session &, const string& name, Diskstream::Flag f = Recordable);
+ MidiDiskstream (Session &, const XMLNode&);
+ ~MidiDiskstream();
+
+ float playback_buffer_load() const;
+ float capture_buffer_load() const;
+
+ void get_playback(MidiBuffer& dst, nframes_t start, nframes_t end);
+
+ void set_record_enabled (bool yn);
+
+ boost::shared_ptr<MidiPlaylist> midi_playlist () { return boost::dynamic_pointer_cast<MidiPlaylist>(_playlist); }
+
+ int use_playlist (boost::shared_ptr<Playlist>);
+ int use_new_playlist ();
+ int use_copy_playlist ();
+
+ /* stateful */
+ XMLNode& get_state(void);
+ int set_state(const XMLNode& node);
+
+ void monitor_input (bool);
+
+ boost::shared_ptr<SMFSource> write_source () { return _write_source; }
+
+ int set_destructive (bool yn); // doom!
+
+ void set_note_mode (NoteMode m);
+
+ uint16_t get_channel_mask() {
+ uint16_t playback_mask = _playback_buf->get_channel_mask();
+#ifndef NDEBUG
+ uint16_t capture_mask = _capture_buf->get_channel_mask();
+ assert(playback_mask == capture_mask);
+#endif
+ return playback_mask;
+ }
+
+ void set_channel_mode(ChannelMode mode, uint16_t mask) {
+ _playback_buf->set_channel_mode(mode, mask);
+ _capture_buf->set_channel_mode(mode, mask);
+ }
+
+ ChannelMode get_channel_mode() {
+ ChannelMode playback_mode = _playback_buf->get_channel_mode();
+#ifndef NDEBUG
+ ChannelMode capture_mode = _capture_buf->get_channel_mode();
+ assert(playback_mode == capture_mode);
+#endif
+ return playback_mode;
+ }
+
+ protected:
+ friend class Session;
+
+ /* the Session is the only point of access for these
+ because they require that the Session is "inactive"
+ while they are called.
+ */
+
+ void set_pending_overwrite(bool);
+ int overwrite_existing_buffers ();
+ void set_block_size (nframes_t);
+ int internal_playback_seek (nframes_t distance);
+ int can_internal_playback_seek (nframes_t distance);
+ int rename_write_sources ();
+ void reset_write_sources (bool, bool force = false);
+ void non_realtime_input_change ();
+ void non_realtime_locate (nframes_t location);
+
+ protected:
+ int seek (nframes_t which_sample, bool complete_refill = false);
+
+ protected:
+ friend class MidiTrack;
+
+ int process (nframes_t transport_frame, nframes_t nframes, nframes_t offset, bool can_record, bool rec_monitors_input);
+ bool commit (nframes_t nframes);
+
+ private:
+
+ /* The two central butler operations */
+ int do_flush (Session::RunContext context, bool force = false);
+ int do_refill ();
+
+ int do_refill_with_alloc();
+
+ int read (nframes_t& start, nframes_t cnt, bool reversed);
+
+ void finish_capture (bool rec_monitors_input);
+ void transport_stopped (struct tm&, time_t, bool abort);
+ void transport_looped (nframes_t transport_frame);
+
+ void init (Diskstream::Flag);
+
+ int use_new_write_source (uint32_t n=0);
+
+ int find_and_use_playlist (const string&);
+
+ void allocate_temporary_buffers ();
+
+ int use_pending_capture_data (XMLNode& node);
+
+ void get_input_sources ();
+ void check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record);
+ void set_align_style_from_io();
+
+ void engage_record_enable ();
+ void disengage_record_enable ();
+
+ MidiRingBuffer* _playback_buf;
+ MidiRingBuffer* _capture_buf;
+ MidiPort* _source_port;
+ boost::shared_ptr<SMFSource> _write_source;
+ nframes_t _last_flush_frame;
+ NoteMode _note_mode;
+};
+
+}; /* namespace ARDOUR */
+
+#endif /* __ardour_midi_diskstream_h__ */
diff --git a/libs/ardour/ardour/midi_model.h b/libs/ardour/ardour/midi_model.h
new file mode 100644
index 0000000000..2481aa8d34
--- /dev/null
+++ b/libs/ardour/ardour/midi_model.h
@@ -0,0 +1,252 @@
+/*
+ Copyright (C) 2007 Paul Davis
+ Author: Dave Robillard
+
+ 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_midi_model_h__
+#define __ardour_midi_model_h__
+
+#include <queue>
+#include <deque>
+#include <utility>
+#include <boost/utility.hpp>
+#include <glibmm/thread.h>
+#include <pbd/command.h>
+#include <ardour/types.h>
+#include <ardour/midi_buffer.h>
+#include <ardour/midi_ring_buffer.h>
+#include <ardour/automatable.h>
+#include <ardour/note.h>
+#include <ardour/types.h>
+
+namespace ARDOUR {
+
+class Session;
+class MidiSource;
+
+/**
+ * This class keeps track of the current x and y for a control
+ */
+class MidiControlIterator {
+public:
+ boost::shared_ptr<const AutomationList> automation_list;
+ double x;
+ double y;
+
+ MidiControlIterator(boost::shared_ptr<const AutomationList> a_list,
+ double a_x,
+ double a_y)
+ : automation_list(a_list)
+ , x(a_x)
+ , y(a_y)
+ {}
+};
+
+
+/** This is a higher level (than MidiBuffer) model of MIDI data, with separate
+ * representations for notes (instead of just unassociated note on/off events)
+ * and controller data. Controller data is represented as part of the
+ * Automatable base (i.e. in a map of AutomationList, keyed by Parameter).
+ */
+class MidiModel : public boost::noncopyable, public Automatable {
+public:
+ MidiModel(MidiSource* s, size_t size=0);
+
+ void write_lock();
+ void write_unlock();
+
+ void read_lock() const;
+ void read_unlock() const;
+
+ void clear();
+
+ NoteMode note_mode() const { return _note_mode; }
+ void set_note_mode(NoteMode mode) { _note_mode = mode; }
+
+ void start_write();
+ bool writing() const { return _writing; }
+ void end_write(bool delete_stuck=false);
+
+ size_t read (MidiRingBuffer& dst, nframes_t start, nframes_t nframes, nframes_t stamp_offset, nframes_t negative_stamp_offset) const;
+
+ /** Resizes vector if necessary (NOT realtime safe) */
+ void append(const MIDI::Event& ev);
+
+ inline const boost::shared_ptr<const Note> note_at(unsigned i) const { return _notes[i]; }
+ inline const boost::shared_ptr<Note> note_at(unsigned i) { return _notes[i]; }
+
+ inline size_t n_notes() const { return _notes.size(); }
+ inline bool empty() const { return _notes.size() == 0 && _controls.size() == 0; }
+
+ inline static bool note_time_comparator (const boost::shared_ptr<const Note> a,
+ const boost::shared_ptr<const Note> b) {
+ return a->time() < b->time();
+ }
+
+ struct LaterNoteEndComparator {
+ typedef const Note* value_type;
+ inline bool operator()(const boost::shared_ptr<const Note> a,
+ const boost::shared_ptr<const Note> b) const {
+ return a->end_time() > b->end_time();
+ }
+ };
+
+ typedef std::vector< boost::shared_ptr<Note> > Notes;
+ inline Notes& notes() { return _notes; }
+ inline const Notes& notes() const { return _notes; }
+
+ /** Add/Remove notes.
+ * Technically all operations can be implemented as one of these.
+ */
+ class DeltaCommand : public Command
+ {
+ public:
+ DeltaCommand (boost::shared_ptr<MidiModel> m, const std::string& name);
+ DeltaCommand (boost::shared_ptr<MidiModel>, const XMLNode& node);
+
+ const std::string& name() const { return _name; }
+
+ void operator()();
+ void undo();
+
+ int set_state (const XMLNode&);
+ XMLNode& get_state ();
+
+ void add(const boost::shared_ptr<Note> note);
+ void remove(const boost::shared_ptr<Note> note);
+
+ private:
+ XMLNode &marshal_note(const boost::shared_ptr<Note> note);
+ boost::shared_ptr<Note> unmarshal_note(XMLNode *xml_note);
+
+ boost::shared_ptr<MidiModel> _model;
+ const std::string _name;
+
+ typedef std::list< boost::shared_ptr<Note> > NoteList;
+
+ NoteList _added_notes;
+ NoteList _removed_notes;
+ };
+
+ MidiModel::DeltaCommand* new_delta_command(const std::string name="midi edit");
+ void apply_command(Command* cmd);
+
+ bool edited() const { return _edited; }
+ void set_edited(bool yn) { _edited = yn; }
+ bool write_to(boost::shared_ptr<MidiSource> source);
+
+ // MidiModel doesn't use the normal AutomationList serialisation code
+ // since controller data is stored in the .mid
+ XMLNode& get_state();
+ int set_state(const XMLNode&) { return 0; }
+
+ sigc::signal<void> ContentsChanged;
+
+ /** Read iterator */
+ class const_iterator {
+ public:
+ const_iterator(const MidiModel& model, double t);
+ ~const_iterator();
+
+ inline bool locked() const { return _locked; }
+
+ const MIDI::Event& operator*() const { return *_event; }
+ const boost::shared_ptr<MIDI::Event> operator->() const { return _event; }
+ const boost::shared_ptr<MIDI::Event> get_event_pointer() { return _event; }
+
+ const const_iterator& operator++(); // prefix only
+ bool operator==(const const_iterator& other) const;
+ bool operator!=(const const_iterator& other) const { return ! operator==(other); }
+
+ const_iterator& operator=(const const_iterator& other);
+
+ private:
+ friend class MidiModel;
+
+ const MidiModel* _model;
+ boost::shared_ptr<MIDI::Event> _event;
+
+ typedef std::priority_queue<
+ boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
+ LaterNoteEndComparator>
+ ActiveNotes;
+
+ mutable ActiveNotes _active_notes;
+
+ bool _is_end;
+ bool _locked;
+ Notes::const_iterator _note_iter;
+ std::vector<MidiControlIterator> _control_iters;
+ std::vector<MidiControlIterator>::iterator _control_iter;
+ };
+
+ const_iterator begin() const { return const_iterator(*this, 0); }
+ const const_iterator& end() const { return _end_iter; }
+
+ const MidiSource* midi_source() const { return _midi_source; }
+ void set_midi_source(MidiSource* source) { _midi_source = source; }
+ bool control_to_midi_event(boost::shared_ptr<MIDI::Event>& ev, const MidiControlIterator& iter) const;
+
+private:
+ friend class DeltaCommand;
+ void add_note_unlocked(const boost::shared_ptr<Note> note);
+ void remove_note_unlocked(const boost::shared_ptr<const Note> note);
+
+ friend class const_iterator;
+
+#ifndef NDEBUG
+ bool is_sorted() const;
+#endif
+
+ void append_note_on_unlocked(uint8_t chan, double time, uint8_t note, uint8_t velocity);
+ void append_note_off_unlocked(uint8_t chan, double time, uint8_t note);
+ void append_automation_event_unlocked(AutomationType type, uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte);
+ void append_pgm_change_unlocked(uint8_t chan, double time, uint8_t number);
+
+ mutable Glib::RWLock _lock;
+
+ Notes _notes;
+
+ NoteMode _note_mode;
+
+ typedef std::vector<size_t> WriteNotes;
+ WriteNotes _write_notes[16];
+ bool _writing;
+ bool _edited;
+
+ typedef std::vector< boost::shared_ptr<const ARDOUR::AutomationList> > AutomationLists;
+ AutomationLists _dirty_automations;
+
+ const const_iterator _end_iter;
+
+ mutable nframes_t _next_read;
+ mutable const_iterator _read_iter;
+
+ typedef std::priority_queue<
+ boost::shared_ptr<Note>, std::deque< boost::shared_ptr<Note> >,
+ LaterNoteEndComparator>
+ ActiveNotes;
+
+ // We cannot use a boost::shared_ptr here to avoid a retain cycle
+ MidiSource* _midi_source;
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_midi_model_h__ */
+
diff --git a/libs/ardour/ardour/midi_playlist.h b/libs/ardour/ardour/midi_playlist.h
new file mode 100644
index 0000000000..dcc202bbf4
--- /dev/null
+++ b/libs/ardour/ardour/midi_playlist.h
@@ -0,0 +1,84 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Dave Robillard, 2006
+
+ 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_midi_playlist_h__
+#define __ardour_midi_playlist_h__
+
+#include <vector>
+#include <list>
+
+#include <ardour/ardour.h>
+#include <ardour/playlist.h>
+#include <ardour/parameter.h>
+
+namespace ARDOUR
+{
+
+class Session;
+class Region;
+class MidiRegion;
+class Source;
+class MidiRingBuffer;
+
+class MidiPlaylist : public ARDOUR::Playlist
+{
+public:
+ MidiPlaylist (Session&, const XMLNode&, bool hidden = false);
+ MidiPlaylist (Session&, string name, bool hidden = false);
+ MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden = false);
+ MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, nframes_t start, nframes_t cnt,
+ string name, bool hidden = false);
+
+ ~MidiPlaylist ();
+
+ nframes_t read (MidiRingBuffer& buf,
+ nframes_t start, nframes_t cnt, uint32_t chan_n=0);
+
+ int set_state (const XMLNode&);
+ UndoAction get_memento() const;
+
+ bool destroy_region (boost::shared_ptr<Region>);
+
+ void set_note_mode (NoteMode m) { _note_mode = m; }
+
+ std::set<Parameter> contained_automation();
+
+protected:
+
+ /* playlist "callbacks" */
+
+ void finalize_split_region (boost::shared_ptr<Region> original, boost::shared_ptr<Region> left, boost::shared_ptr<Region> right);
+
+ void check_dependents (boost::shared_ptr<Region> region, bool norefresh);
+ void refresh_dependents (boost::shared_ptr<Region> region);
+ void remove_dependents (boost::shared_ptr<Region> region);
+
+private:
+ void dump () const;
+
+ bool region_changed (Change, boost::shared_ptr<Region>);
+
+ NoteMode _note_mode;
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_midi_playlist_h__ */
+
+
diff --git a/libs/ardour/ardour/midi_port.h b/libs/ardour/ardour/midi_port.h
new file mode 100644
index 0000000000..485834aaff
--- /dev/null
+++ b/libs/ardour/ardour/midi_port.h
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 2002 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.
+
+ $Id: port.h 712 2006-07-28 01:08:57Z drobilla $
+*/
+
+#ifndef __ardour_midi_port_h__
+#define __ardour_midi_port_h__
+
+#include <ardour/base_midi_port.h>
+
+namespace ARDOUR {
+
+class MidiEngine;
+
+class MidiPort : public BaseMidiPort, public PortFacade {
+ public:
+ ~MidiPort();
+
+ void reset ();
+
+ void cycle_start (nframes_t nframes, nframes_t offset);
+ void cycle_end (nframes_t nframes, nframes_t offset);
+
+ protected:
+ friend class AudioEngine;
+
+ MidiPort (const std::string& name, Flags, bool external, nframes_t bufsize);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_midi_port_h__ */
diff --git a/libs/ardour/ardour/midi_region.h b/libs/ardour/ardour/midi_region.h
new file mode 100644
index 0000000000..cd2b0d6132
--- /dev/null
+++ b/libs/ardour/ardour/midi_region.h
@@ -0,0 +1,121 @@
+/*
+ Copyright (C) 2000-2006 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.
+
+ $Id: midiregion.h 733 2006-08-01 17:19:38Z drobilla $
+*/
+
+#ifndef __ardour_midi_region_h__
+#define __ardour_midi_region_h__
+
+#include <vector>
+
+#include <pbd/fastlog.h>
+#include <pbd/undo.h>
+
+#include <ardour/ardour.h>
+#include <ardour/region.h>
+#include <ardour/gain.h>
+#include <ardour/logcurve.h>
+#include <ardour/export.h>
+#include <ardour/midi_source.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Route;
+class Playlist;
+class Session;
+class MidiFilter;
+class MidiSource;
+class MidiRingBuffer;
+
+class MidiRegion : public Region
+{
+ public:
+ ~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,
+ nframes_t dur,
+ uint32_t chan_n = 0,
+ NoteMode mode = Sustained) const;
+
+ nframes_t master_read_at (MidiRingBuffer& dst,
+ nframes_t position,
+ nframes_t dur,
+ uint32_t chan_n = 0,
+ NoteMode mode = Sustained) const;
+
+ XMLNode& state (bool);
+ int set_state (const XMLNode&);
+
+ int separate_by_channel (ARDOUR::Session&, vector<MidiRegion*>&) const;
+
+ UndoAction get_memento() const;
+
+ // Act as a proxy for MidiModel automation stuff (for CC)
+ // Yep, this is pretty ugly...
+ Controls& controls() { return midi_source()->model()->controls(); }
+ const Controls& controls() const { return midi_source()->model()->controls(); }
+
+ boost::shared_ptr<AutomationControl> control(Parameter id, bool create_if_missing=false)
+ { return midi_source()->model()->control(id, create_if_missing); }
+
+ boost::shared_ptr<const AutomationControl> control(Parameter id) const
+ { return midi_source()->model()->control(id); }
+
+ int exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&);
+
+ private:
+ friend class RegionFactory;
+
+ 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 (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 (const SourceList &, const XMLNode&);
+
+ private:
+ nframes_t _read_at (const SourceList&, MidiRingBuffer& dst,
+ nframes_t position,
+ nframes_t dur,
+ uint32_t chan_n = 0,
+ NoteMode mode = Sustained) const;
+
+ void recompute_at_start ();
+ void recompute_at_end ();
+
+ void switch_source(boost::shared_ptr<Source> source);
+
+ protected:
+
+ int set_live_state (const XMLNode&, Change&, bool send);
+};
+
+} /* namespace ARDOUR */
+
+
+#endif /* __ardour_midi_region_h__ */
diff --git a/libs/ardour/ardour/midi_ring_buffer.h b/libs/ardour/ardour/midi_ring_buffer.h
new file mode 100644
index 0000000000..ff0be5c997
--- /dev/null
+++ b/libs/ardour/ardour/midi_ring_buffer.h
@@ -0,0 +1,466 @@
+/*
+ Copyright (C) 2006 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_midi_ring_buffer_h__
+#define __ardour_midi_ring_buffer_h__
+
+#include <iostream>
+#include <algorithm>
+#include <ardour/types.h>
+#include <ardour/buffer.h>
+
+namespace ARDOUR {
+
+
+/* FIXME: this is probably too much inlined code */
+
+
+/** A RingBuffer.
+ * Read/Write realtime safe.
+ * Single-reader Single-writer thread safe.
+ *
+ * This is Raul::RingBuffer, lifted for MIDIRingBuffer to inherit from as it works
+ * a bit differently than PBD::Ringbuffer. This could/should be replaced with
+ * the PBD ringbuffer to decrease code size, but this code is tested and known to
+ * work, so here it sits for now...
+ *
+ * Ignore this class, use MidiRingBuffer.
+ */
+template <typename T>
+class MidiRingBufferBase {
+public:
+
+ /** @param size Size in bytes.
+ */
+ MidiRingBufferBase(size_t size)
+ : _size(size)
+ , _buf(new T[size])
+ {
+ reset();
+ assert(read_space() == 0);
+ assert(write_space() == size - 1);
+ }
+
+ virtual ~MidiRingBufferBase() {
+ delete[] _buf;
+ }
+
+ /** Reset(empty) the ringbuffer.
+ * NOT thread safe.
+ */
+ void reset() {
+ g_atomic_int_set(&_write_ptr, 0);
+ g_atomic_int_set(&_read_ptr, 0);
+ }
+
+ size_t write_space() const {
+
+ const size_t w = g_atomic_int_get(&_write_ptr);
+ const size_t r = g_atomic_int_get(&_read_ptr);
+
+ if (w > r) {
+ return ((r - w + _size) % _size) - 1;
+ } else if (w < r) {
+ return (r - w) - 1;
+ } else {
+ return _size - 1;
+ }
+ }
+
+ size_t read_space() const {
+
+ const size_t w = g_atomic_int_get(&_write_ptr);
+ const size_t r = g_atomic_int_get(&_read_ptr);
+
+ if (w > r) {
+ return w - r;
+ } else {
+ return (w - r + _size) % _size;
+ }
+ }
+
+ size_t capacity() const { return _size; }
+
+ size_t peek(size_t size, T* dst);
+ bool full_peek(size_t size, T* dst);
+
+ size_t read(size_t size, T* dst);
+ bool full_read(size_t size, T* dst);
+
+ bool skip(size_t size);
+
+ void write(size_t size, const T* src);
+
+protected:
+ mutable int _write_ptr;
+ mutable int _read_ptr;
+
+ size_t _size; ///< Size (capacity) in bytes
+ T* _buf; ///< size, event, size, event...
+};
+
+
+/** Peek at the ringbuffer (read w/o advancing read pointer).
+ *
+ * Note that a full read may not be done if the data wraps around.
+ * Caller must check return value and call again if necessary, or use the
+ * full_peek method which does this automatically.
+ */
+template<typename T>
+size_t
+MidiRingBufferBase<T>::peek(size_t size, T* dst)
+{
+ const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
+
+ const size_t read_size = (priv_read_ptr + size < _size)
+ ? size
+ : _size - priv_read_ptr;
+
+ memcpy(dst, &_buf[priv_read_ptr], read_size);
+
+ return read_size;
+}
+
+
+template<typename T>
+bool
+MidiRingBufferBase<T>::full_peek(size_t size, T* dst)
+{
+ if (read_space() < size) {
+ return false;
+ }
+
+ const size_t read_size = peek(size, dst);
+
+ if (read_size < size) {
+ peek(size - read_size, dst + read_size);
+ }
+
+ return true;
+}
+
+
+/** Read from the ringbuffer.
+ *
+ * Note that a full read may not be done if the data wraps around.
+ * Caller must check return value and call again if necessary, or use the
+ * full_read method which does this automatically.
+ */
+template<typename T>
+size_t
+MidiRingBufferBase<T>::read(size_t size, T* dst)
+{
+ const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
+
+ const size_t read_size = (priv_read_ptr + size < _size)
+ ? size
+ : _size - priv_read_ptr;
+
+ memcpy(dst, &_buf[priv_read_ptr], read_size);
+
+ g_atomic_int_set(&_read_ptr, (priv_read_ptr + read_size) % _size);
+
+ return read_size;
+}
+
+
+template<typename T>
+bool
+MidiRingBufferBase<T>::full_read(size_t size, T* dst)
+{
+ if (read_space() < size) {
+ return false;
+ }
+
+ const size_t read_size = read(size, dst);
+
+ if (read_size < size) {
+ read(size - read_size, dst + read_size);
+ }
+
+ return true;
+}
+
+
+template<typename T>
+bool
+MidiRingBufferBase<T>::skip(size_t size)
+{
+ if (read_space() < size) {
+ std::cerr << "WARNING: Attempt to skip past end of MIDI ring buffer" << std::endl;
+ return false;
+ }
+
+ const size_t priv_read_ptr = g_atomic_int_get(&_read_ptr);
+ g_atomic_int_set(&_read_ptr, (priv_read_ptr + size) % _size);
+
+ return true;
+}
+
+
+template<typename T>
+inline void
+MidiRingBufferBase<T>::write(size_t size, const T* src)
+{
+ const size_t priv_write_ptr = g_atomic_int_get(&_write_ptr);
+
+ if (priv_write_ptr + size <= _size) {
+ memcpy(&_buf[priv_write_ptr], src, size);
+ g_atomic_int_set(&_write_ptr, (priv_write_ptr + size) % _size);
+ } else {
+ const size_t this_size = _size - priv_write_ptr;
+ assert(this_size < size);
+ assert(priv_write_ptr + this_size <= _size);
+ memcpy(&_buf[priv_write_ptr], src, this_size);
+ memcpy(&_buf[0], src+this_size, size - this_size);
+ g_atomic_int_set(&_write_ptr, size - this_size);
+ }
+}
+
+
+/* ******************************************************************** */
+
+
+/** A MIDI RingBuffer.
+ *
+ * This is timestamps and MIDI packed sequentially into a single buffer, similarly
+ * to LV2 MIDI. The buffer looks like this:
+ *
+ * [timestamp][size][size bytes of raw MIDI][timestamp][size][etc..]
+ */
+class MidiRingBuffer : public MidiRingBufferBase<uint8_t> {
+public:
+ /** @param size Size in bytes.
+ */
+ MidiRingBuffer(size_t size)
+ : MidiRingBufferBase<uint8_t>(size), _channel_mask(0x0000FFFF)
+ {}
+
+ size_t write(double time, size_t size, const uint8_t* buf);
+ bool read(double* time, size_t* size, uint8_t* buf);
+
+ bool read_prefix(double* time, size_t* size);
+ bool read_contents(size_t size, uint8_t* buf);
+
+ size_t read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset=0);
+
+ /** Set the channel filtering mode.
+ * @param mask If mode is FilterChannels, each bit represents a midi channel:
+ * bit 0 = channel 0, bit 1 = channel 1 etc. the read and write methods will only
+ * process events whose channel bit is 1.
+ * If mode is ForceChannel, mask is simply a channel number which all events will
+ * be forced to while reading.
+ */
+ void set_channel_mode(ChannelMode mode, uint16_t mask) {
+ g_atomic_int_set(&_channel_mask, ((uint16_t)mode << 16) | mask);
+ }
+
+ ChannelMode get_channel_mode() const {
+ return static_cast<ChannelMode>((g_atomic_int_get(&_channel_mask) & 0xFFFF0000) >> 16);
+ }
+
+ uint16_t get_channel_mask() const {
+ return static_cast<ChannelMode>((g_atomic_int_get(&_channel_mask) & 0x0000FFFF));
+ }
+
+protected:
+ inline bool is_channel_event(uint8_t event_type_byte) {
+ // mask out channel information
+ event_type_byte &= 0xF0;
+ // midi channel events range from 0x80 to 0xE0
+ return (0x80 <= event_type_byte) && (event_type_byte <= 0xE0);
+ }
+
+private:
+ volatile uint32_t _channel_mask; // 16 bits mode, 16 bits mask
+};
+
+
+inline bool
+MidiRingBuffer::read(double* time, size_t* size, uint8_t* buf)
+{
+ bool success = MidiRingBufferBase<uint8_t>::full_read(sizeof(double), (uint8_t*)time);
+
+ if (success) {
+ success = MidiRingBufferBase<uint8_t>::full_read(sizeof(size_t), (uint8_t*)size);
+ }
+ if (success) {
+ success = MidiRingBufferBase<uint8_t>::full_read(*size, buf);
+ }
+
+ return success;
+}
+
+
+/** Read the time and size of an event. This call MUST be immediately proceeded
+ * by a call to read_contents (or the read pointer will be garabage).
+ */
+inline bool
+MidiRingBuffer::read_prefix(double* time, size_t* size)
+{
+ bool success = MidiRingBufferBase<uint8_t>::full_read(sizeof(double), (uint8_t*)time);
+ if (success) {
+ success = MidiRingBufferBase<uint8_t>::full_read(sizeof(size_t), (uint8_t*)size);
+ }
+
+ return success;
+}
+
+
+/** Read the contenst of an event. This call MUST be immediately preceeded
+ * by a call to read_prefix (or the returned even will be garabage).
+ */
+inline bool
+MidiRingBuffer::read_contents(size_t size, uint8_t* buf)
+{
+ return MidiRingBufferBase<uint8_t>::full_read(size, buf);
+}
+
+
+inline size_t
+MidiRingBuffer::write(double time, size_t size, const uint8_t* buf)
+{
+ /*fprintf(stderr, "MRB %p write (t = %f) ", this, time);
+ for (size_t i = 0; i < size; ++i)
+ fprintf(stderr, "%X", (char)buf[i]);
+ fprintf(stderr, "\n");*/
+
+ assert(size > 0);
+
+ // Don't write event if it doesn't match channel filter
+ if (is_channel_event(buf[0]) && get_channel_mode() == FilterChannels) {
+ uint8_t channel = buf[0] & 0x0F;
+ if ( !(get_channel_mask() & (1L << channel)) ) {
+ return 0;
+ }
+ }
+
+ if (write_space() < (sizeof(double) + sizeof(size_t) + size)) {
+ return 0;
+ } else {
+ MidiRingBufferBase<uint8_t>::write(sizeof(double), (uint8_t*)&time);
+ MidiRingBufferBase<uint8_t>::write(sizeof(size_t), (uint8_t*)&size);
+ if (is_channel_event(buf[0]) && get_channel_mode() == ForceChannel) {
+ assert(size == 2 || size == 3);
+ uint8_t tmp_buf[3];
+ // Force event to channel
+ tmp_buf[0] = (buf[0] & 0xF0) | (get_channel_mask() & 0x0F);
+ tmp_buf[1] = buf[1];
+ if (size == 3) {
+ tmp_buf[2] = buf[2];
+ }
+ MidiRingBufferBase<uint8_t>::write(size, tmp_buf);
+ } else {
+ MidiRingBufferBase<uint8_t>::write(size, buf);
+ }
+ return size;
+ }
+
+}
+
+
+/** Read a block of MIDI events from buffer.
+ *
+ * Timestamps of events returned are relative to start (ie event with stamp 0
+ * occurred at start), with offset added.
+ */
+inline size_t
+MidiRingBuffer::read(MidiBuffer& dst, nframes_t start, nframes_t end, nframes_t offset)
+{
+ if (read_space() == 0)
+ return 0;
+
+ double ev_time;
+ uint32_t ev_size;
+
+ size_t count = 0;
+
+ //printf("---- MRB read %u .. %u + %u\n", start, end, offset);
+
+ while (read_space() > sizeof(double) + sizeof(size_t)) {
+
+ full_peek(sizeof(double), (uint8_t*)&ev_time);
+
+ if (ev_time > end) {
+ break;
+ }
+
+ bool success = MidiRingBufferBase<uint8_t>::full_read(sizeof(double), (uint8_t*)&ev_time);
+ if (success) {
+ success = MidiRingBufferBase<uint8_t>::full_read(sizeof(size_t), (uint8_t*)&ev_size);
+ }
+
+ if (!success) {
+ std::cerr << "MRB: READ ERROR (time/size)" << std::endl;
+ continue;
+ }
+
+ uint8_t status;
+ success = full_peek(sizeof(uint8_t), &status);
+ assert(success); // If this failed, buffer is corrupt, all hope is lost
+
+ // Ignore event if it doesn't match channel filter
+ if (is_channel_event(status) && get_channel_mode() == FilterChannels) {
+ const uint8_t channel = status & 0x0F;
+ if ( !(get_channel_mask() & (1L << channel)) ) {
+ skip(ev_size); // Advance read pointer to next event
+ continue;
+ }
+ }
+
+ if (ev_time >= start) {
+
+ /*std::cerr << "MRB " << this << " - Reading event, time = "
+ << ev_time << " - " << start << " => " << ev_time - start
+ << ", size = " << ev_size << std::endl;*/
+
+ ev_time -= start;
+
+ uint8_t* write_loc = dst.reserve(ev_time, ev_size);
+ if (write_loc == NULL) {
+ std::cerr << "MRB: Unable to reserve space in buffer, event skipped";
+ continue;
+ }
+
+ success = MidiRingBufferBase<uint8_t>::full_read(ev_size, write_loc);
+
+ if (success) {
+ if (is_channel_event(status) && get_channel_mode() == ForceChannel) {
+ write_loc[0] = (write_loc[0] & 0xF0) | (get_channel_mask() & 0x0F);
+ }
+ ++count;
+ //printf("MRB - read event at time %lf\n", ev_time);
+ } else {
+ std::cerr << "MRB: READ ERROR (data)" << std::endl;
+ }
+
+ } else {
+ printf("MRB (start %u) - Skipping event at (too early) time %f\n", start, ev_time);
+ }
+ }
+
+ //printf("(R) read space: %zu\n", read_space());
+
+ return count;
+}
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_midi_ring_buffer_h__
+
diff --git a/libs/ardour/ardour/midi_source.h b/libs/ardour/ardour/midi_source.h
new file mode 100644
index 0000000000..997f3f9d69
--- /dev/null
+++ b/libs/ardour/ardour/midi_source.h
@@ -0,0 +1,119 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Dave Robillard, 2006
+
+ 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_midi_source_h__
+#define __ardour_midi_source_h__
+
+#include <string>
+
+#include <time.h>
+
+#include <glibmm/thread.h>
+
+#include <sigc++/signal.h>
+
+#include <ardour/source.h>
+#include <ardour/ardour.h>
+#include <ardour/buffer.h>
+#include <ardour/midi_model.h>
+#include <pbd/stateful.h>
+#include <pbd/xml++.h>
+
+using std::string;
+
+namespace ARDOUR {
+
+class MidiRingBuffer;
+
+/** Source for MIDI data */
+class MidiSource : public Source
+{
+ public:
+ MidiSource (Session& session, string name);
+ MidiSource (Session& session, const XMLNode&);
+ virtual ~MidiSource ();
+
+ /* 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, nframes_t negative_stamp_offset) const;
+ virtual nframes_t midi_write (MidiRingBuffer& src, nframes_t cnt);
+
+ virtual void append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev) = 0;
+
+ virtual void mark_for_remove() = 0;
+ virtual void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time);
+ virtual void mark_streaming_write_started ();
+ virtual void mark_streaming_write_completed ();
+
+ uint64_t timeline_position () { return _timeline_position; }
+ void set_timeline_position (nframes_t when) { _timeline_position = when; }
+
+ virtual void session_saved();
+
+ string captured_for() const { return _captured_for; }
+ void set_captured_for (string str) { _captured_for = str; }
+
+ uint32_t read_data_count() const { return _read_data_count; }
+ uint32_t write_data_count() const { return _write_data_count; }
+
+ static sigc::signal<void,MidiSource*> MidiSourceCreated;
+
+ // Signal a range of recorded data is available for reading from model()
+ mutable sigc::signal<void,nframes_t,nframes_t> ViewDataRangeReady;
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
+ bool length_mutable() const { return true; }
+
+ virtual void load_model(bool lock=true, bool force_reload=false) = 0;
+ virtual void destroy_model() = 0;
+
+ void set_note_mode(NoteMode mode) { if (_model) _model->set_note_mode(mode); }
+
+ boost::shared_ptr<MidiModel> model() { return _model; }
+ void set_model(boost::shared_ptr<MidiModel> m) { _model = m; }
+
+ protected:
+ virtual int flush_header() = 0;
+ virtual int flush_footer() = 0;
+
+ virtual nframes_t read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const = 0;
+ virtual nframes_t write_unlocked (MidiRingBuffer& dst, nframes_t cnt) = 0;
+
+ mutable Glib::Mutex _lock;
+ string _captured_for;
+ uint64_t _timeline_position;
+ mutable uint32_t _read_data_count; ///< modified in read()
+ mutable uint32_t _write_data_count; ///< modified in write()
+
+ boost::shared_ptr<MidiModel> _model;
+ bool _writing;
+
+ private:
+ bool file_changed (string path);
+};
+
+}
+
+#endif /* __ardour_midi_source_h__ */
diff --git a/libs/ardour/ardour/midi_stretch.h b/libs/ardour/ardour/midi_stretch.h
new file mode 100644
index 0000000000..f4b8f1c618
--- /dev/null
+++ b/libs/ardour/ardour/midi_stretch.h
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 2007 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_midi_stretch_h__
+#define __ardour_midi_stretch_h__
+
+#include <ardour/filter.h>
+
+namespace ARDOUR {
+
+class MidiStretch : public Filter {
+ public:
+ MidiStretch (ARDOUR::Session&, TimeFXRequest&);
+ ~MidiStretch ();
+
+ int run (boost::shared_ptr<ARDOUR::Region>);
+
+ private:
+ TimeFXRequest& _request;
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_midi_stretch_h__ */
diff --git a/libs/ardour/ardour/midi_track.h b/libs/ardour/ardour/midi_track.h
new file mode 100644
index 0000000000..500502ac4b
--- /dev/null
+++ b/libs/ardour/ardour/midi_track.h
@@ -0,0 +1,111 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Dave Robillard
+
+ 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_midi_track_h__
+#define __ardour_midi_track_h__
+
+#include <ardour/track.h>
+#include <ardour/midi_ring_buffer.h>
+
+namespace ARDOUR
+{
+
+class Session;
+class MidiDiskstream;
+class MidiPlaylist;
+class RouteGroup;
+
+class MidiTrack : public Track
+{
+public:
+ MidiTrack (Session&, string name, Route::Flag f = Route::Flag (0), TrackMode m = Normal);
+ MidiTrack (Session&, const XMLNode&);
+ ~MidiTrack ();
+
+ int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, int declick, bool can_record, bool rec_monitors_input);
+
+ int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, bool state_changing, bool can_record, bool rec_monitors_input);
+
+ int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, bool can_record, bool rec_monitors_input);
+
+ void process_output_buffers (BufferSet& bufs,
+ nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset, bool with_redirects, int declick,
+ bool meter);
+
+ boost::shared_ptr<MidiDiskstream> midi_diskstream() const;
+
+ int use_diskstream (string name);
+ int use_diskstream (const PBD::ID& id);
+
+ void set_latency_delay (nframes_t);
+
+ int export_stuff (BufferSet& bufs,
+ nframes_t nframes, nframes_t end_frame);
+
+ void freeze (InterThreadInfo&);
+ void unfreeze ();
+
+ void bounce (InterThreadInfo&);
+ void bounce_range (nframes_t start, nframes_t end, InterThreadInfo&);
+
+ int set_state(const XMLNode& node);
+
+ void midi_panic(void);
+ bool write_immediate_event(size_t size, const uint8_t* buf);
+
+ struct MidiControl : public AutomationControl {
+ MidiControl(MidiTrack* route, boost::shared_ptr<AutomationList> al)
+ : AutomationControl (route->session(), al, al->parameter().to_string())
+ , _route (route)
+ {}
+
+ void set_value (float val);
+
+ MidiTrack* _route;
+ };
+
+ NoteMode note_mode() const { return _note_mode; }
+ void set_note_mode (NoteMode m);
+
+protected:
+
+ XMLNode& state (bool full);
+
+ int _set_state (const XMLNode&, bool call_base);
+
+private:
+
+ void write_controller_messages(MidiBuffer& buf,
+ nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset);
+
+ int set_diskstream (boost::shared_ptr<MidiDiskstream> ds);
+ void set_state_part_two ();
+ void set_state_part_three ();
+
+ MidiRingBuffer _immediate_events;
+ NoteMode _note_mode;
+};
+
+} /* namespace ARDOUR*/
+
+#endif /* __ardour_midi_track_h__ */
diff --git a/libs/ardour/ardour/midi_util.h b/libs/ardour/ardour/midi_util.h
new file mode 100644
index 0000000000..6bcc6278e1
--- /dev/null
+++ b/libs/ardour/ardour/midi_util.h
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Dave Robillard, 2006
+
+ 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_midi_util_h__
+#define __ardour_midi_util_h__
+
+#include <midi++/events.h>
+
+namespace ARDOUR {
+
+/** Return the size of the given event NOT including the status byte,
+ * or -1 if unknown (eg sysex)
+ */
+static inline int
+midi_event_size(unsigned char status)
+{
+ // if we have a channel event
+ if (status >= 0x80 && status < 0xF0) {
+ status &= 0xF0; // mask off the channel
+ }
+
+ switch (status) {
+ case MIDI_CMD_NOTE_OFF:
+ case MIDI_CMD_NOTE_ON:
+ case MIDI_CMD_NOTE_PRESSURE:
+ case MIDI_CMD_CONTROL:
+ case MIDI_CMD_BENDER:
+ case MIDI_CMD_COMMON_SONG_POS:
+ return 2;
+
+ case MIDI_CMD_PGM_CHANGE:
+ case MIDI_CMD_CHANNEL_PRESSURE:
+ case MIDI_CMD_COMMON_MTC_QUARTER:
+ case MIDI_CMD_COMMON_SONG_SELECT:
+ return 1;
+
+ case MIDI_CMD_COMMON_TUNE_REQUEST:
+ case MIDI_CMD_COMMON_SYSEX_END:
+ case MIDI_CMD_COMMON_CLOCK:
+ case MIDI_CMD_COMMON_START:
+ case MIDI_CMD_COMMON_CONTINUE:
+ case MIDI_CMD_COMMON_STOP:
+ case MIDI_CMD_COMMON_SENSING:
+ case MIDI_CMD_COMMON_RESET:
+ return 0;
+
+ case MIDI_CMD_COMMON_SYSEX:
+ return -1;
+ }
+
+ return -1;
+}
+
+} // namespace ARDOUR
+
+#endif /* __ardour_midi_util_h__ */
diff --git a/libs/ardour/ardour/mix.h b/libs/ardour/ardour/mix.h
new file mode 100644
index 0000000000..67779f0e07
--- /dev/null
+++ b/libs/ardour/ardour/mix.h
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 2005 Sampo Savolainen
+
+ 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_mix_h__
+#define __ardour_mix_h__
+
+#include <ardour/types.h>
+#include <ardour/utils.h>
+#include <ardour/io.h>
+
+#if defined (ARCH_X86) && defined (BUILD_SSE_OPTIMIZATIONS)
+
+extern "C" {
+/* SSE functions */
+ float x86_sse_compute_peak (const ARDOUR::Sample * buf, nframes_t nsamples, float current);
+ void x86_sse_apply_gain_to_buffer (ARDOUR::Sample * buf, nframes_t nframes, float gain);
+ void x86_sse_mix_buffers_with_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes, float gain);
+ void x86_sse_mix_buffers_no_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes);
+}
+
+void x86_sse_find_peaks (const ARDOUR::Sample * buf, nframes_t nsamples, float *min, float *max);
+
+/* debug wrappers for SSE functions */
+
+float debug_compute_peak (const ARDOUR::Sample * buf, nframes_t nsamples, float current);
+void debug_apply_gain_to_buffer (ARDOUR::Sample * buf, nframes_t nframes, float gain);
+void debug_mix_buffers_with_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes, float gain);
+void debug_mix_buffers_no_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes);
+
+#endif
+
+#if defined (__APPLE__)
+
+float veclib_compute_peak (const ARDOUR::Sample * buf, nframes_t nsamples, float current);
+void veclib_find_peaks (const ARDOUR::Sample * buf, nframes_t nsamples, float *min, float *max);
+void veclib_apply_gain_to_buffer (ARDOUR::Sample * buf, nframes_t nframes, float gain);
+void veclib_mix_buffers_with_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes, float gain);
+void veclib_mix_buffers_no_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes);
+
+#endif
+
+/* non-optimized functions */
+
+float default_compute_peak (const ARDOUR::Sample * buf, nframes_t nsamples, float current);
+void default_find_peaks (const ARDOUR::Sample * buf, nframes_t nsamples, float *min, float *max);
+void default_apply_gain_to_buffer (ARDOUR::Sample * buf, nframes_t nframes, float gain);
+void default_mix_buffers_with_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes, float gain);
+void default_mix_buffers_no_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes);
+
+#endif /* __ardour_mix_h__ */
diff --git a/libs/ardour/ardour/named_selection.h b/libs/ardour/ardour/named_selection.h
new file mode 100644
index 0000000000..39ab524d4f
--- /dev/null
+++ b/libs/ardour/ardour/named_selection.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2003 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_named_selection_h__
+#define __ardour_named_selection_h__
+
+#include <string>
+#include <list>
+#include <boost/shared_ptr.hpp>
+
+#include <pbd/stateful.h>
+
+class XMLNode;
+
+namespace ARDOUR
+{
+class Session;
+class Playlist;
+
+struct NamedSelection : public PBD::Stateful
+{
+ NamedSelection (std::string, std::list<boost::shared_ptr<Playlist> >&);
+ NamedSelection (Session&, const XMLNode&);
+ virtual ~NamedSelection ();
+
+ std::string name;
+ std::list<boost::shared_ptr<Playlist> > playlists;
+
+ XMLNode& get_state (void);
+
+ int set_state (const XMLNode&);
+
+ static sigc::signal<void,NamedSelection*> NamedSelectionCreated;
+};
+
+}/* namespace ARDOUR */
+
+#endif /* __ardour_named_selection_h__ */
+
diff --git a/libs/ardour/ardour/noise.h b/libs/ardour/ardour/noise.h
new file mode 100644
index 0000000000..f775fcce36
--- /dev/null
+++ b/libs/ardour/ardour/noise.h
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2000-2007 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 NOISE_H
+#define NOISE_H
+
+/* Can be overrriden with any code that produces whitenoise between 0.0f and
+ * 1.0f, eg (random() / (float)RAND_MAX) should be a good source of noise, but
+ * its expensive */
+#ifndef GDITHER_NOISE
+#define GDITHER_NOISE gdither_noise()
+#endif
+
+inline static float gdither_noise()
+{
+ static uint32_t rnd = 23232323;
+ rnd = (rnd * 196314165) + 907633515;
+
+ return rnd * 2.3283064365387e-10f;
+}
+
+#endif
diff --git a/libs/ardour/ardour/note.h b/libs/ardour/ardour/note.h
new file mode 100644
index 0000000000..0f649b3370
--- /dev/null
+++ b/libs/ardour/ardour/note.h
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 2007 Paul Davis
+ Author: Dave Robillard
+
+ 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_note_h__
+#define __ardour_note_h__
+
+#include <stdint.h>
+#include <midi++/event.h>
+
+namespace ARDOUR {
+
+
+/** A MIDI Note.
+ *
+ * A note is (unfortunately) special and not just another MIDI::Event as it
+ * has a duration and two separate MIDI events (on and off).
+ */
+class Note {
+public:
+ Note(uint8_t chan=0, double time=0, double dur=0, uint8_t note=0, uint8_t vel=0x40);
+ Note(const Note& copy);
+ ~Note();
+
+ const Note& operator=(const Note& copy);
+
+ inline bool operator==(const Note& other)
+ { return time() == other.time() &&
+ note() == other.note() &&
+ duration() == other.duration() &&
+ velocity() == other.velocity() &&
+ channel() == other.channel();
+ }
+
+ inline double time() const { return _on_event.time(); }
+ inline double end_time() const { return _off_event.time(); }
+ inline uint8_t note() const { return _on_event.note(); }
+ inline uint8_t velocity() const { return _on_event.velocity(); }
+ inline double duration() const { return _off_event.time() - _on_event.time(); }
+ inline uint8_t channel() const {
+ assert(_on_event.channel() == _off_event.channel());
+ return _on_event.channel();
+ }
+
+ inline void set_time(double t) { _off_event.time() = t + duration(); _on_event.time() = t; }
+ inline void set_note(uint8_t n) { _on_event.buffer()[1] = n; _off_event.buffer()[1] = n; }
+ inline void set_velocity(uint8_t n) { _on_event.buffer()[2] = n; }
+ inline void set_duration(double d) { _off_event.time() = _on_event.time() + d; }
+ inline void set_channel(uint8_t c) { _on_event.set_channel(c); _off_event.set_channel(c); }
+
+ inline MIDI::Event& on_event() { return _on_event; }
+ inline MIDI::Event& off_event() { return _off_event; }
+
+ inline const MIDI::Event& on_event() const { return _on_event; }
+ inline const MIDI::Event& off_event() const { return _off_event; }
+
+private:
+ // Event buffers are self-contained
+ MIDI::Event _on_event;
+ MIDI::Event _off_event;
+};
+
+
+} // namespace ARDOUR
+
+#endif /* __ardour_note_h__ */
diff --git a/libs/ardour/ardour/osc.h b/libs/ardour/ardour/osc.h
new file mode 100644
index 0000000000..3f1ce03445
--- /dev/null
+++ b/libs/ardour/ardour/osc.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef ardour_osc_h
+#define ardour_osc_h
+
+#include <string>
+
+#include <sys/time.h>
+#include <pthread.h>
+
+#include <lo/lo.h>
+
+#include <sigc++/sigc++.h>
+
+#include <ardour/types.h>
+
+#include <control_protocol/basic_ui.h>
+
+namespace ARDOUR {
+ class Session;
+ class Route;
+
+class OSC : public BasicUI, public sigc::trackable
+{
+ public:
+ OSC (uint32_t port);
+ virtual ~OSC();
+
+ void set_session (ARDOUR::Session&);
+ int start ();
+ int stop ();
+
+ private:
+ uint32_t _port;
+ volatile bool _ok;
+ volatile bool _shutdown;
+ lo_server _osc_server;
+ lo_server _osc_unix_server;
+ std::string _osc_unix_socket_path;
+ std::string _osc_url_file;
+ pthread_t _osc_thread;
+ int _request_pipe[2];
+
+ static void * _osc_receiver(void * arg);
+ void osc_receiver();
+
+ bool init_osc_thread ();
+ void terminate_osc_thread ();
+ void poke_osc_thread ();
+
+ void register_callbacks ();
+
+ void session_going_away ();
+
+ std::string get_server_url ();
+ std::string get_unix_server_url ();
+
+ int current_value (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data);
+
+#define PATH_CALLBACK(name) \
+ static int _ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data) { \
+ return static_cast<OSC*>(user_data)->cb_ ## name (path, types, argv, argc, data); \
+ } \
+ int cb_ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data) { \
+ name (); \
+ return 0; \
+ }
+
+ PATH_CALLBACK(add_marker);
+ PATH_CALLBACK(loop_toggle);
+ PATH_CALLBACK(goto_start);
+ PATH_CALLBACK(goto_end);
+ PATH_CALLBACK(rewind);
+ PATH_CALLBACK(ffwd);
+ PATH_CALLBACK(transport_stop);
+ PATH_CALLBACK(transport_play);
+ PATH_CALLBACK(save_state);
+ PATH_CALLBACK(prev_marker);
+ PATH_CALLBACK(next_marker);
+ PATH_CALLBACK(undo);
+ PATH_CALLBACK(redo);
+ PATH_CALLBACK(toggle_punch_in);
+ PATH_CALLBACK(toggle_punch_out);
+ PATH_CALLBACK(rec_enable_toggle);
+ PATH_CALLBACK(toggle_all_rec_enables);
+
+#define PATH_CALLBACK1(name,type) \
+ static int _ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data, void *user_data) { \
+ return static_cast<OSC*>(user_data)->cb_ ## name (path, types, argv, argc, data); \
+ } \
+ int cb_ ## name (const char *path, const char *types, lo_arg **argv, int argc, void *data) { \
+ if (argc > 0) { \
+ name (argv[0]->type); \
+ }\
+ return 0; \
+ }
+
+ PATH_CALLBACK1(set_transport_speed,f);
+};
+
+}
+
+#endif // ardour_osc_h
diff --git a/libs/ardour/ardour/panner.h b/libs/ardour/ardour/panner.h
new file mode 100644
index 0000000000..2559eed003
--- /dev/null
+++ b/libs/ardour/ardour/panner.h
@@ -0,0 +1,299 @@
+/*
+ Copyright (C) 2004 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_panner_h__
+#define __ardour_panner_h__
+
+#include <cmath>
+#include <cassert>
+#include <vector>
+#include <string>
+#include <iostream>
+#include <sigc++/signal.h>
+
+#include <pbd/stateful.h>
+#include <pbd/controllable.h>
+
+#include <ardour/types.h>
+#include <ardour/curve.h>
+#include <ardour/automation_control.h>
+
+using std::istream;
+using std::ostream;
+
+namespace ARDOUR {
+
+class Session;
+class Panner;
+class BufferSet;
+class AudioBuffer;
+
+class StreamPanner : public sigc::trackable, public PBD::Stateful
+{
+ public:
+ StreamPanner (Panner& p, Parameter param);
+ ~StreamPanner ();
+
+ void set_muted (bool yn);
+ bool muted() const { return _muted; }
+
+ void set_position (float x, bool link_call = false);
+ void set_position (float x, float y, bool link_call = false);
+ void set_position (float x, float y, float z, bool link_call = false);
+
+ void get_position (float& xpos) const { xpos = x; }
+ void get_position (float& xpos, float& ypos) const { xpos = x; ypos = y; }
+ void get_position (float& xpos, float& ypos, float& zpos) const { xpos = x; ypos = y; zpos = z; }
+
+ void get_effective_position (float& xpos) const { xpos = effective_x; }
+ void get_effective_position (float& xpos, float& ypos) const { xpos = effective_x; ypos = effective_y; }
+ void get_effective_position (float& xpos, float& ypos, float& zpos) const { xpos = effective_x; ypos = effective_y; zpos = effective_z; }
+
+ /* the basic StreamPanner API */
+
+ virtual void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes) = 0;
+ virtual void distribute_automated (AudioBuffer& src, BufferSet& obufs,
+ nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers) = 0;
+
+ boost::shared_ptr<AutomationControl> pan_control() { return _control; }
+
+ sigc::signal<void> Changed; /* for position */
+ sigc::signal<void> StateChanged; /* for mute */
+
+ int set_state (const XMLNode&);
+ virtual XMLNode& state (bool full_state) = 0;
+
+ Panner & get_parent() { return parent; }
+
+ /* old school automation loading */
+
+ virtual int load (istream&, string path, uint32_t&) = 0;
+
+ protected:
+ friend class Panner;
+ Panner& parent;
+
+ float x;
+ float y;
+ float z;
+
+ /* these are for automation. they store the last value
+ used by the most recent process() cycle.
+ */
+
+ float effective_x;
+ float effective_y;
+ float effective_z;
+
+ bool _muted;
+
+ struct PanControllable : public AutomationControl {
+ PanControllable (Session& s, std::string name, StreamPanner& p, Parameter param)
+ : AutomationControl (s, boost::shared_ptr<AutomationList>(new AutomationList(
+ param, 0.0, 1.0, 0.5)), name)
+ , panner (p) { assert(param.type() != NullAutomation); }
+
+ StreamPanner& panner;
+
+ void set_value (float);
+ float get_value (void) const;
+ bool can_send_feedback() const;
+ };
+
+ boost::shared_ptr<PanControllable> _control;
+
+ void add_state (XMLNode&);
+ virtual void update () = 0;
+};
+
+class BaseStereoPanner : public StreamPanner
+{
+ public:
+ BaseStereoPanner (Panner&, Parameter param);
+ ~BaseStereoPanner ();
+
+ /* this class just leaves the pan law itself to be defined
+ by the update(), distribute_automated()
+ methods. derived classes also need a factory method
+ and a type name. See EqualPowerStereoPanner as an example.
+ */
+
+ void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
+
+ /* old school automation loading */
+
+ int load (istream&, string path, uint32_t&);
+
+ protected:
+ float left;
+ float right;
+ float desired_left;
+ float desired_right;
+ float left_interp;
+ float right_interp;
+};
+
+class EqualPowerStereoPanner : public BaseStereoPanner
+{
+ public:
+ EqualPowerStereoPanner (Panner&, Parameter param);
+ ~EqualPowerStereoPanner ();
+
+ void distribute_automated (AudioBuffer& src, BufferSet& obufs,
+ nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
+
+ void get_current_coefficients (pan_t*) const;
+ void get_desired_coefficients (pan_t*) const;
+
+ static StreamPanner* factory (Panner&, Parameter param);
+ static string name;
+
+ XMLNode& state (bool full_state);
+ XMLNode& get_state (void);
+ int set_state (const XMLNode&);
+
+ private:
+ void update ();
+};
+
+class Multi2dPanner : public StreamPanner
+{
+ public:
+ Multi2dPanner (Panner& parent, Parameter);
+ ~Multi2dPanner ();
+
+ void distribute (AudioBuffer& src, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes);
+ void distribute_automated (AudioBuffer& src, BufferSet& obufs,
+ nframes_t start, nframes_t end, nframes_t nframes, pan_t** buffers);
+
+ static StreamPanner* factory (Panner&, Parameter);
+ static string name;
+
+ XMLNode& state (bool full_state);
+ XMLNode& get_state (void);
+ int set_state (const XMLNode&);
+
+ /* old school automation loading */
+
+ int load (istream&, string path, uint32_t&);
+
+ private:
+ void update ();
+};
+
+class Panner : public std::vector<StreamPanner*>, public PBD::Stateful, public sigc::trackable
+{
+ public:
+ struct Output {
+ float x;
+ float y;
+ pan_t current_pan;
+ pan_t desired_pan;
+
+ Output (float xp, float yp)
+ : x (xp), y (yp), current_pan (0.0f), desired_pan (0.f) {}
+
+ };
+
+ Panner (string name, Session&);
+ virtual ~Panner ();
+
+ /// The fundamental Panner function
+ void distribute(BufferSet& src, BufferSet& dest, nframes_t start_frame, nframes_t end_frames, nframes_t nframes, nframes_t offset);
+
+ bool bypassed() const { return _bypassed; }
+ void set_bypassed (bool yn);
+
+ StreamPanner* add ();
+ void remove (uint32_t which);
+ void clear ();
+ void reset (uint32_t noutputs, uint32_t npans);
+
+ void snapshot (nframes_t now);
+ void transport_stopped (nframes_t frame);
+
+ void clear_automation ();
+
+ void set_automation_state (AutoState);
+ AutoState automation_state() const;
+ void set_automation_style (AutoStyle);
+ AutoStyle automation_style() const;
+ bool touching() const;
+
+ XMLNode& get_state (void);
+ XMLNode& state (bool full);
+ int set_state (const XMLNode&);
+
+ sigc::signal<void> Changed;
+
+ static bool equivalent (pan_t a, pan_t b) {
+ return fabsf (a - b) < 0.002; // about 1 degree of arc for a stereo panner
+ }
+
+ void move_output (uint32_t, float x, float y);
+ uint32_t nouts() const { return outputs.size(); }
+ Output& output (uint32_t n) { return outputs[n]; }
+
+ std::vector<Output> outputs;
+ Session& session() const { return _session; }
+
+ enum LinkDirection {
+ SameDirection,
+ OppositeDirection
+ };
+
+ LinkDirection link_direction() const { return _link_direction; }
+ void set_link_direction (LinkDirection);
+
+ bool linked() const { return _linked; }
+ void set_linked (bool yn);
+
+ sigc::signal<void> LinkStateChanged;
+ sigc::signal<void> StateChanged; /* for bypass */
+
+ /* only StreamPanner should call these */
+
+ void set_position (float x, StreamPanner& orig);
+ void set_position (float x, float y, StreamPanner& orig);
+ void set_position (float x, float y, float z, StreamPanner& orig);
+
+ /* old school automation */
+
+ int load ();
+
+ private:
+ void distribute_no_automation(BufferSet& src, BufferSet& dest, nframes_t nframes, nframes_t offset, gain_t gain_coeff);
+
+ Session& _session;
+ uint32_t current_outs;
+ bool _linked;
+ bool _bypassed;
+ LinkDirection _link_direction;
+
+ static float current_automation_version_number;
+
+ /* old school automation handling */
+
+ std::string automation_path;
+ void set_name (std::string);
+};
+
+} // namespace ARDOUR
+
+#endif /*__ardour_panner_h__ */
diff --git a/libs/ardour/ardour/parameter.h b/libs/ardour/ardour/parameter.h
new file mode 100644
index 0000000000..b86174aa0a
--- /dev/null
+++ b/libs/ardour/ardour/parameter.h
@@ -0,0 +1,168 @@
+/*
+ Copyright (C) 2007 Paul Davis
+ Author: Dave Robillard
+
+ 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_parameter_h__
+#define __ardour_parameter_h__
+
+#include <string>
+#include <pbd/compose.h>
+#include <pbd/error.h>
+#include <ardour/types.h>
+
+namespace ARDOUR {
+
+
+/** ID of an automatable parameter.
+ *
+ * A given automatable object has a number of automatable parameters. This is
+ * the unique ID for those parameters. Anything automatable (AutomationList,
+ * Curve) must have an ID unique with respect to it's Automatable parent.
+ *
+ * A parameter ID has two parts, a type and an int (only used by some types).
+ *
+ * This is a bit more ugly than it could be, due to using the existing/legacy
+ * ARDOUR::AutomationType: GainAutomation, PanAutomation, SoloAutomation,
+ * and MuteAutomation use only the type(), but PluginAutomation and
+ * MidiCCAutomation use the id() as port number and CC number, respectively.
+ *
+ * Future types may use a string or URI or whatever, as long as these are
+ * comparable anything may be added. ints are best as these should be fast to
+ * copy and compare with one another.
+ */
+class Parameter
+{
+public:
+ Parameter(AutomationType type = NullAutomation, uint32_t id=0, uint8_t channel=0)
+ : _type(type), _id(id), _channel(channel)
+ {}
+
+ Parameter(const std::string& str);
+
+ inline AutomationType type() const { return _type; }
+ inline uint32_t id() const { return _id; }
+ inline uint8_t channel() const { return _channel; }
+
+ /**
+ * Equivalence operator
+ * It is obvious from the definition that this operator
+ * is transitive, as required by stict weak ordering
+ * (see: http://www.sgi.com/tech/stl/StrictWeakOrdering.html)
+ */
+ inline bool operator==(const Parameter& id) const {
+ return (_type == id._type && _id == id._id && _channel == id._channel);
+ }
+
+ /** Strict weak ordering
+ * (see: http://www.sgi.com/tech/stl/StrictWeakOrdering.html)
+ * This is necessary so that std::set works):
+ * Sort Parameters first according to type then to id and lastly to channel.
+ *
+ * Proof:
+ * <ol>
+ * <li>Irreflexivity: f(x, x) is false because of the irreflexivity of \c < in each branch.</li>
+ *
+ * <li>Antisymmetry: given x != y, f(x, y) implies !f(y, x) because of the same
+ * property of \c < in each branch and the symmetry of operator==. </li>
+ *
+ * <li>Transitivity: let f(x, y) and f(y, z) be true. We prove by assuming the contrary,
+ * that f(x, z) does not hold.
+ * That would imply exactly one of the following:
+ * <ol>
+ * <li> x == z which contradicts the assumption f(x, y) and f(y, x)
+ * because of antisymmetry.
+ * </li>
+ * <li> f(z, x) is true. That would imply that one of the ivars (we call it i)
+ * of x is greater than the same ivar in z while all "previous" ivars
+ * are equal. That would imply that also in y all those "previous"
+ * ivars are equal and because if x.i > z.i it is impossible
+ * that there is an y that satisfies x.i < y.i < z.i at the same
+ * time which contradicts the assumption.
+ * </li>
+ * </ol>
+ * </li>
+ * </ol>
+ */
+
+ inline bool operator<(const Parameter& id) const {
+#ifndef NDEBUG
+ if (_type == NullAutomation)
+ PBD::warning << "Uninitialized Parameter compared." << endmsg;
+#endif
+ if (_type < id._type) {
+ return true;
+ } else if (_type == id._type && _id < id._id) {
+ return true;
+ } else if (_id == id._id && _channel < id._channel) {
+ return true;
+ }
+
+ return false;
+ }
+
+ inline operator bool() const { return (_type != 0); }
+
+ std::string to_string() const;
+
+ /* The below properties are only used for CC right now, but unchanging properties
+ * of parameters (rather than changing parameters of automation lists themselves)
+ * should be moved here */
+
+ inline double min() const {
+ switch(_type) {
+ case MidiCCAutomation:
+ case MidiPgmChangeAutomation:
+ case MidiPitchBenderAutomation:
+ case MidiChannelAftertouchAutomation:
+ return 0.0;
+
+ default:
+ return DBL_MIN;
+ }
+ }
+
+ inline double max() const {
+ switch(_type) {
+ case MidiCCAutomation:
+ case MidiPgmChangeAutomation:
+ case MidiChannelAftertouchAutomation:
+ return 127.0;
+ case MidiPitchBenderAutomation:
+ return 16383.0;
+
+ default:
+ return DBL_MAX;
+ }
+ }
+
+ inline bool is_integer() const {
+ return (_type >= MidiCCAutomation && _type <= MidiChannelAftertouchAutomation);
+ }
+
+private:
+ // default copy constructor is ok
+ AutomationType _type;
+ uint32_t _id;
+ uint8_t _channel;
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_parameter_h__
+
diff --git a/libs/ardour/ardour/pcm_utils.h b/libs/ardour/ardour/pcm_utils.h
new file mode 100644
index 0000000000..5e6436cc94
--- /dev/null
+++ b/libs/ardour/ardour/pcm_utils.h
@@ -0,0 +1,41 @@
+/*
+ Copyright (C) 2006 Paul Davis , portions Erik de Castro Lopo
+
+ 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_pcm_utils_h__
+#define __ardour_pcm_utils_h__
+
+typedef void tribyte ;
+
+#define SIZEOF_TRIBYTE 3
+
+#define BET2H_INT_PTR(x) (((x) [0] << 24) + ((x) [1] << 16) + ((x) [2] << 8))
+#define LET2H_INT_PTR(x) (((x) [0] << 8) + ((x) [1] << 16) + ((x) [2] << 24))
+
+
+
+void pcm_let2f_array (tribyte *src, int count, float *dest);
+void pcm_bet2f_array (tribyte *src, int count, float *dest);
+void pcm_f2let_array (float *src, tribyte *dest, int count);
+void pcm_f2let_clip_array (float *src, tribyte *dest, int count);
+void pcm_f2bet_array (const float *src, tribyte *dest, int count);
+void pcm_f2bet_clip_array (const float *src, tribyte *dest, int count);
+
+
+
+#endif
diff --git a/libs/ardour/ardour/peak.h b/libs/ardour/ardour/peak.h
new file mode 100644
index 0000000000..bbec40eea7
--- /dev/null
+++ b/libs/ardour/ardour/peak.h
@@ -0,0 +1,36 @@
+/*
+ Copyright (C) 2000-2007 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_peak_h__
+#define __ardour_peak_h__
+
+#include <cmath>
+#include <ardour/types.h>
+#include <ardour/utils.h>
+
+static inline float
+default_compute_peak (const ARDOUR::Sample * const buf, nframes_t nsamples, float current)
+{
+ for (nframes_t i = 0; i < nsamples; ++i) {
+ current = f_max (current, fabsf (buf[i]));
+ }
+ return current;
+}
+
+#endif /* __ardour_peak_h__ */
diff --git a/libs/ardour/ardour/pitch.h b/libs/ardour/ardour/pitch.h
new file mode 100644
index 0000000000..38d8380f5d
--- /dev/null
+++ b/libs/ardour/ardour/pitch.h
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2007 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_pitch_h__
+#define __ardour_pitch_h__
+
+#include <ardour/filter.h>
+
+namespace ARDOUR {
+ class AudioRegion;
+}
+
+#ifdef USE_RUBBERBAND
+
+#include <ardour/rb_effect.h>
+
+namespace ARDOUR {
+
+class Pitch : public RBEffect {
+ public:
+ Pitch (ARDOUR::Session&, TimeFXRequest&);
+ ~Pitch () {}
+};
+
+} /* namespace */
+
+# else
+
+namespace ARDOUR {
+
+class Pitch : public Filter {
+ public:
+ Pitch (ARDOUR::Session&, TimeFXRequest&);
+ ~Pitch () {}
+
+ int run (boost::shared_ptr<ARDOUR::Region>);
+
+ private:
+ TimeFXRequest& tsr;
+};
+
+} /* namespace */
+
+#endif
+
+#endif /* __ardour_pitch_h__ */
diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h
new file mode 100644
index 0000000000..ad7210f48f
--- /dev/null
+++ b/libs/ardour/ardour/playlist.h
@@ -0,0 +1,287 @@
+/*
+ Copyright (C) 2000 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_playlist_h__
+#define __ardour_playlist_h__
+
+#include <string>
+#include <set>
+#include <map>
+#include <list>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include <sigc++/signal.h>
+
+#include <pbd/undo.h>
+#include <pbd/stateful.h>
+#include <pbd/statefuldestructible.h>
+
+#include <ardour/ardour.h>
+#include <ardour/session_object.h>
+#include <ardour/crossfade_compare.h>
+#include <ardour/location.h>
+#include <ardour/data_type.h>
+
+namespace ARDOUR {
+
+class Session;
+class Region;
+
+class Playlist : public SessionObject, public boost::enable_shared_from_this<Playlist> {
+ public:
+ typedef list<boost::shared_ptr<Region> > RegionList;
+
+ Playlist (Session&, const XMLNode&, DataType type, bool hidden = false);
+ Playlist (Session&, string name, DataType type, bool hidden = false);
+ Playlist (boost::shared_ptr<const Playlist>, string name, bool hidden = false);
+ Playlist (boost::shared_ptr<const Playlist>, nframes_t start, nframes_t cnt, string name, bool hidden = false);
+
+ virtual ~Playlist ();
+
+ void set_region_ownership ();
+
+ virtual void clear (bool with_signals=true);
+ virtual void dump () const;
+
+ void use();
+ void release();
+ bool used () const { return _refcnt != 0; }
+
+ bool set_name (const string& str);
+
+ const DataType& data_type() const { return _type; }
+
+ bool frozen() const { return _frozen; }
+ void set_frozen (bool yn);
+
+ bool hidden() const { return _hidden; }
+ bool empty() const;
+ uint32_t n_regions() const;
+ nframes_t get_maximum_extent () const;
+ layer_t top_layer() const;
+
+ EditMode get_edit_mode() const { return _edit_mode; }
+ void set_edit_mode (EditMode);
+
+ /* Editing operations */
+
+ void add_region (boost::shared_ptr<Region>, nframes_t position, float times = 1);
+ void remove_region (boost::shared_ptr<Region>);
+ void get_equivalent_regions (boost::shared_ptr<Region>, std::vector<boost::shared_ptr<Region> >&);
+ void get_region_list_equivalent_regions (boost::shared_ptr<Region>, std::vector<boost::shared_ptr<Region> >&);
+ void replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos);
+ void split_region (boost::shared_ptr<Region>, nframes_t position);
+ void split (nframes64_t at);
+ void shift (nframes64_t at, nframes64_t distance, bool move_intersected, bool ignore_music_glue);
+ void partition (nframes_t start, nframes_t end, bool just_top_level);
+ void duplicate (boost::shared_ptr<Region>, nframes_t position, float times);
+ void nudge_after (nframes_t start, nframes_t distance, bool forwards);
+ void shuffle (boost::shared_ptr<Region>, int dir);
+ void update_after_tempo_map_change ();
+
+ boost::shared_ptr<Playlist> cut (list<AudioRange>&, bool result_is_hidden = true);
+ boost::shared_ptr<Playlist> copy (list<AudioRange>&, bool result_is_hidden = true);
+ int paste (boost::shared_ptr<Playlist>, nframes_t position, float times);
+
+ RegionList* regions_at (nframes_t frame);
+ RegionList* regions_touched (nframes_t start, nframes_t end);
+ RegionList* regions_to_read (nframes_t start, nframes_t end);
+ boost::shared_ptr<Region> find_region (const PBD::ID&) const;
+ boost::shared_ptr<Region> top_region_at (nframes_t frame);
+ boost::shared_ptr<Region> find_next_region (nframes_t frame, RegionPoint point, int dir);
+ nframes64_t find_next_region_boundary (nframes64_t frame, int dir);
+ bool region_is_shuffle_constrained (boost::shared_ptr<Region>);
+
+ nframes64_t find_next_transient (nframes64_t position, int dir);
+
+ template<class T> void foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>, void *), void *arg);
+ template<class T> void foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>));
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+ XMLNode& get_template ();
+
+ sigc::signal<void,bool> InUse;
+ sigc::signal<void> Modified;
+ sigc::signal<void> NameChanged;
+ sigc::signal<void> LengthChanged;
+
+ static string bump_name (string old_name, Session&);
+
+ void freeze ();
+ void thaw ();
+
+ void raise_region (boost::shared_ptr<Region>);
+ void lower_region (boost::shared_ptr<Region>);
+ void raise_region_to_top (boost::shared_ptr<Region>);
+ void lower_region_to_bottom (boost::shared_ptr<Region>);
+
+ uint32_t read_data_count() const { return _read_data_count; }
+
+ const PBD::ID& get_orig_diskstream_id () const { return _orig_diskstream_id; }
+ void set_orig_diskstream_id (const PBD::ID& did) { _orig_diskstream_id = did; }
+
+ /* destructive editing */
+
+ virtual bool destroy_region (boost::shared_ptr<Region>) = 0;
+
+ /* special case function used by UI selection objects, which have playlists that actually own the regions
+ within them.
+ */
+
+ void drop_regions ();
+
+ protected:
+ friend class Session;
+
+ protected:
+ struct RegionLock {
+ RegionLock (Playlist *pl, bool do_block_notify = true) : playlist (pl), block_notify (do_block_notify) {
+ playlist->region_lock.lock();
+ if (block_notify) {
+ playlist->delay_notifications();
+ }
+ }
+ ~RegionLock() {
+ playlist->region_lock.unlock();
+ if (block_notify) {
+ playlist->release_notifications ();
+ }
+ }
+ Playlist *playlist;
+ bool block_notify;
+ };
+
+ friend class RegionLock;
+
+ RegionList regions; /* the current list of regions in the playlist */
+ std::set<boost::shared_ptr<Region> > all_regions; /* all regions ever added to this playlist */
+ DataType _type;
+ mutable gint block_notifications;
+ mutable gint ignore_state_changes;
+ mutable Glib::Mutex region_lock;
+ std::set<boost::shared_ptr<Region> > pending_adds;
+ std::set<boost::shared_ptr<Region> > pending_removes;
+ RegionList pending_bounds;
+ bool pending_modified;
+ bool pending_length;
+ bool save_on_thaw;
+ string last_save_reason;
+ uint32_t in_set_state;
+ bool first_set_state;
+ bool _hidden;
+ bool _splicing;
+ bool _shuffling;
+ bool _nudging;
+ uint32_t _refcnt;
+ EditMode _edit_mode;
+ bool in_flush;
+ bool in_partition;
+ bool _frozen;
+ uint32_t subcnt;
+ uint32_t _read_data_count;
+ PBD::ID _orig_diskstream_id;
+ uint64_t layer_op_counter;
+ nframes_t freeze_length;
+
+ void init (bool hide);
+
+ bool holding_state () const {
+ return g_atomic_int_get (&block_notifications) != 0 ||
+ g_atomic_int_get (&ignore_state_changes) != 0;
+ }
+
+ /* prevent the compiler from ever generating these */
+
+ Playlist (const Playlist&);
+ Playlist (Playlist&);
+
+ void delay_notifications ();
+ void release_notifications ();
+ virtual void flush_notifications ();
+
+ void notify_region_removed (boost::shared_ptr<Region>);
+ void notify_region_added (boost::shared_ptr<Region>);
+ void notify_length_changed ();
+ void notify_layering_changed ();
+ void notify_modified ();
+ void notify_state_changed (Change);
+
+ void mark_session_dirty();
+
+ void region_changed_proxy (Change, boost::weak_ptr<Region>);
+ virtual bool region_changed (Change, boost::shared_ptr<Region>);
+
+ void region_bounds_changed (Change, boost::shared_ptr<Region>);
+ void region_deleted (boost::shared_ptr<Region>);
+
+ void sort_regions ();
+
+ void possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude = boost::shared_ptr<Region>());
+ void possibly_splice_unlocked(nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude = boost::shared_ptr<Region>());
+
+ void core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude);
+ void splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude);
+ void splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude);
+
+ virtual void finalize_split_region (boost::shared_ptr<Region> original, boost::shared_ptr<Region> left, boost::shared_ptr<Region> right) {}
+
+ virtual void check_dependents (boost::shared_ptr<Region> region, bool norefresh) {}
+ virtual void refresh_dependents (boost::shared_ptr<Region> region) {}
+ virtual void remove_dependents (boost::shared_ptr<Region> region) {}
+
+ virtual XMLNode& state (bool);
+
+ boost::shared_ptr<Region> region_by_id (PBD::ID);
+
+ void add_region_internal (boost::shared_ptr<Region>, nframes_t position);
+
+ int remove_region_internal (boost::shared_ptr<Region>);
+ RegionList *find_regions_at (nframes_t frame);
+ void copy_regions (RegionList&) const;
+ void partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist);
+
+ nframes_t _get_maximum_extent() const;
+
+ boost::shared_ptr<Playlist> cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t, bool),
+ list<AudioRange>& ranges, bool result_is_hidden);
+ boost::shared_ptr<Playlist> cut (nframes_t start, nframes_t cnt, bool result_is_hidden);
+ boost::shared_ptr<Playlist> copy (nframes_t start, nframes_t cnt, bool result_is_hidden);
+
+ int move_region_to_layer (layer_t, boost::shared_ptr<Region> r, int dir);
+ void relayer ();
+
+ void unset_freeze_parent (Playlist*);
+ void unset_freeze_child (Playlist*);
+
+ void timestamp_layer_op (boost::shared_ptr<Region>);
+
+ void _split_region (boost::shared_ptr<Region>, nframes_t position);
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_playlist_h__ */
+
+
diff --git a/libs/ardour/ardour/playlist_factory.h b/libs/ardour/ardour/playlist_factory.h
new file mode 100644
index 0000000000..a28e2611d9
--- /dev/null
+++ b/libs/ardour/ardour/playlist_factory.h
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 2000-2007 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_playlist_factory_h__
+#define __ardour_playlist_factory_h__
+
+#include <ardour/playlist.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Session;
+
+class PlaylistFactory {
+
+ public:
+ static sigc::signal<void,boost::shared_ptr<Playlist> > PlaylistCreated;
+
+ static boost::shared_ptr<Playlist> create (Session&, const XMLNode&, bool hidden = false);
+ static boost::shared_ptr<Playlist> create (DataType type, Session&, string name, bool hidden = false);
+ static boost::shared_ptr<Playlist> create (boost::shared_ptr<const Playlist>, string name, bool hidden = false);
+ static boost::shared_ptr<Playlist> create (boost::shared_ptr<const Playlist>, nframes_t start, nframes_t cnt, string name, bool hidden = false);
+};
+
+}
+
+#endif /* __ardour_playlist_factory_h__ */
diff --git a/libs/ardour/ardour/playlist_templates.h b/libs/ardour/ardour/playlist_templates.h
new file mode 100644
index 0000000000..bf072a71c1
--- /dev/null
+++ b/libs/ardour/ardour/playlist_templates.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2003 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_playlist_templates_h__
+#define __ardour_playlist_templates_h__
+
+namespace ARDOUR {
+
+template<class T> void AudioPlaylist::foreach_crossfade (T *t, void (T::*func)(boost::shared_ptr<Crossfade>)) {
+ RegionLock rlock (this, false);
+ for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); i++) {
+ (t->*func) (*i);
+ }
+}
+
+template<class T> void Playlist::foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>, void *), void *arg) {
+ RegionLock rlock (this, false);
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); i++) {
+ (t->*func) ((*i), arg);
+ }
+ }
+
+template<class T> void Playlist::foreach_region (T *t, void (T::*func)(boost::shared_ptr<Region>)) {
+ RegionLock rlock (this, false);
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); i++) {
+ (t->*func) (*i);
+ }
+}
+
+}
+
+#endif /* __ardour_playlist_templates_h__ */
diff --git a/libs/ardour/ardour/plugin.h b/libs/ardour/ardour/plugin.h
new file mode 100644
index 0000000000..73f89f1a91
--- /dev/null
+++ b/libs/ardour/ardour/plugin.h
@@ -0,0 +1,174 @@
+/*
+ Copyright (C) 2000-2006 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_plugin_h__
+#define __ardour_plugin_h__
+
+#include <boost/shared_ptr.hpp>
+#include <sigc++/signal.h>
+#include <glibmm/ustring.h>
+
+#include <pbd/statefuldestructible.h>
+#include <pbd/controllable.h>
+
+#include <jack/types.h>
+#include <ardour/types.h>
+#include <ardour/chan_count.h>
+#include <ardour/cycles.h>
+#include <ardour/latent.h>
+#include <ardour/parameter.h>
+#include <ardour/plugin_insert.h>
+
+#include <vector>
+#include <set>
+#include <map>
+
+using std::string;
+using std::vector;
+using std::set;
+using std::map;
+
+namespace ARDOUR {
+
+class AudioEngine;
+class Session;
+class BufferSet;
+
+class Plugin;
+
+typedef boost::shared_ptr<Plugin> PluginPtr;
+
+class PluginInfo {
+ public:
+ PluginInfo () { }
+ PluginInfo (const PluginInfo &o)
+ : name(o.name),
+ category (o.category),
+ creator (o.creator),
+ path (o.path),
+ n_inputs(o.n_inputs),
+ n_outputs(o.n_outputs),
+ unique_id(o.unique_id),
+ index(o.index) {}
+ virtual ~PluginInfo () { }
+
+ string name;
+ string category;
+ Glib::ustring creator;
+ Glib::ustring path;
+ ChanCount n_inputs;
+ ChanCount n_outputs;
+ ARDOUR::PluginType type;
+
+ std::string unique_id;
+
+ virtual PluginPtr load (Session& session) = 0;
+
+ protected:
+ friend class PluginManager;
+ uint32_t index;
+};
+
+typedef boost::shared_ptr<PluginInfo> PluginInfoPtr;
+typedef std::list<PluginInfoPtr> PluginInfoList;
+
+class Plugin : public PBD::StatefulDestructible, public Latent
+{
+ public:
+ Plugin (ARDOUR::AudioEngine&, ARDOUR::Session&);
+ Plugin (const Plugin&);
+ virtual ~Plugin ();
+
+ struct ParameterDescriptor {
+
+ /* essentially a union of LADSPA and VST info */
+
+ bool integer_step;
+ bool toggled;
+ bool logarithmic;
+ bool sr_dependent;
+ string label;
+ float lower;
+ float upper;
+ float step;
+ float smallstep;
+ float largestep;
+ bool min_unbound;
+ bool max_unbound;
+ };
+
+ virtual std::string unique_id() const = 0;
+ virtual const char * label() const = 0;
+ virtual const char * name() const = 0;
+ virtual const char * maker() const = 0;
+ virtual uint32_t parameter_count () const = 0;
+ virtual float default_value (uint32_t port) = 0;
+ virtual float get_parameter(uint32_t which) const = 0;
+
+ virtual int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const = 0;
+ virtual uint32_t nth_parameter (uint32_t which, bool& ok) const = 0;
+ virtual void activate () = 0;
+ virtual void deactivate () = 0;
+ virtual void set_block_size (nframes_t nframes) = 0;
+
+ virtual int connect_and_run (BufferSet& bufs, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset) = 0;
+
+ virtual std::set<Parameter> automatable() const = 0;
+ virtual string describe_parameter (Parameter) = 0;
+ virtual string state_node_name() const = 0;
+ virtual void print_parameter (uint32_t, char*, uint32_t len) const = 0;
+
+ virtual bool parameter_is_audio(uint32_t) const = 0;
+ virtual bool parameter_is_control(uint32_t) const = 0;
+ virtual bool parameter_is_input(uint32_t) const = 0;
+ virtual bool parameter_is_output(uint32_t) const = 0;
+
+ virtual bool save_preset(string name) = 0;
+ virtual bool load_preset (const string preset_label);
+ virtual std::vector<std::string> get_presets();
+
+ virtual bool has_editor() const = 0;
+
+ PluginInfoPtr get_info() { return _info; }
+ void set_info (const PluginInfoPtr inf) { _info = inf; }
+
+ ARDOUR::AudioEngine& engine() const { return _engine; }
+ ARDOUR::Session& session() const { return _session; }
+
+ void set_cycles (uint32_t c) { _cycles = c; }
+ cycles_t cycles() const { return _cycles; }
+
+ protected:
+ friend class PluginInsert;
+ friend struct PluginInsert::PluginControl;
+ virtual void set_parameter (uint32_t which, float val) = 0;
+
+ ARDOUR::AudioEngine& _engine;
+ ARDOUR::Session& _session;
+ PluginInfoPtr _info;
+ uint32_t _cycles;
+ map<string,string> presets;
+ bool save_preset(string name, string domain /* vst, ladspa etc. */);
+};
+
+PluginPtr find_plugin(ARDOUR::Session&, string unique_id, ARDOUR::PluginType);
+
+} // namespace ARDOUR
+
+#endif /* __ardour_plugin_h__ */
diff --git a/libs/ardour/ardour/plugin_insert.h b/libs/ardour/ardour/plugin_insert.h
new file mode 100644
index 0000000000..42c53c487c
--- /dev/null
+++ b/libs/ardour/ardour/plugin_insert.h
@@ -0,0 +1,131 @@
+/*
+ Copyright (C) 2000,2007 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_plugin_insert_h__
+#define __ardour_plugin_insert_h__
+
+#include <vector>
+#include <string>
+
+#include <sigc++/signal.h>
+#include <ardour/ardour.h>
+#include <ardour/types.h>
+#include <ardour/processor.h>
+#include <ardour/automation_event.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Session;
+class Route;
+class Plugin;
+
+/** Plugin inserts: send data through a plugin
+ */
+class PluginInsert : public Processor
+{
+ public:
+ PluginInsert (Session&, boost::shared_ptr<Plugin>, Placement);
+ PluginInsert (Session&, const XMLNode&);
+ PluginInsert (const PluginInsert&);
+ ~PluginInsert ();
+
+ static const string port_automation_node_name;
+
+ XMLNode& state(bool);
+ XMLNode& get_state(void);
+ int set_state(const XMLNode&);
+
+ void run_in_place (BufferSet& in, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset);
+ void silence (nframes_t nframes, nframes_t offset);
+
+ void activate ();
+ void deactivate ();
+
+ void set_block_size (nframes_t nframes);
+
+ ChanCount output_streams() const;
+ ChanCount input_streams() const;
+ ChanCount natural_output_streams() const;
+ ChanCount natural_input_streams() const;
+
+ bool set_count (uint32_t num);
+ uint32_t get_count () const { return _plugins.size(); }
+
+ virtual bool can_support_input_configuration (ChanCount in) const;
+ virtual ChanCount output_for_input_configuration (ChanCount in) const;
+ virtual bool configure_io (ChanCount in, ChanCount out);
+
+ bool is_generator() const;
+
+ void set_parameter (Parameter param, float val);
+ float get_parameter (Parameter param);
+
+ float default_parameter_value (Parameter param);
+
+ struct PluginControl : public AutomationControl
+ {
+ PluginControl (PluginInsert& p, boost::shared_ptr<AutomationList> list);
+
+ void set_value (float val);
+ float get_value (void) const;
+
+ private:
+ PluginInsert& _plugin;
+ boost::shared_ptr<AutomationList> _list;
+ bool _logarithmic;
+ bool _toggled;
+ };
+
+ boost::shared_ptr<Plugin> plugin(uint32_t num=0) const {
+ if (num < _plugins.size()) {
+ return _plugins[num];
+ } else {
+ return _plugins[0]; // we always have one
+ }
+ }
+
+ PluginType type ();
+
+ string describe_parameter (Parameter param);
+
+ nframes_t signal_latency() const;
+
+ private:
+
+ void parameter_changed (Parameter, float);
+
+ std::vector<boost::shared_ptr<Plugin> > _plugins;
+
+ void automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offset);
+ void connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now = 0);
+
+ void init ();
+ void set_automatable ();
+ void auto_state_changed (Parameter which);
+
+ int32_t count_for_configuration (ChanCount in, ChanCount out) const;
+
+ boost::shared_ptr<Plugin> plugin_factory (boost::shared_ptr<Plugin>);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_plugin_insert_h__ */
diff --git a/libs/ardour/ardour/plugin_manager.h b/libs/ardour/ardour/plugin_manager.h
new file mode 100644
index 0000000000..892c8bd75a
--- /dev/null
+++ b/libs/ardour/ardour/plugin_manager.h
@@ -0,0 +1,101 @@
+/*
+ Copyright (C) 2000-2007 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_plugin_manager_h__
+#define __ardour_plugin_manager_h__
+
+#include <list>
+#include <map>
+#include <string>
+
+#include <ardour/types.h>
+#include <ardour/plugin.h>
+
+#ifdef HAVE_SLV2
+#include <ardour/lv2_plugin.h>
+#endif
+
+namespace ARDOUR {
+
+class Plugin;
+
+class PluginManager {
+ public:
+ 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; }
+ ARDOUR::PluginInfoList &au_plugin_info () { return _au_plugin_info; }
+
+ void refresh ();
+
+ int add_ladspa_directory (std::string dirpath);
+ int add_vst_directory (std::string dirpath);
+
+ static PluginManager* the_manager() { return _manager; }
+
+ private:
+ ARDOUR::PluginInfoList _vst_plugin_info;
+ ARDOUR::PluginInfoList _ladspa_plugin_info;
+ ARDOUR::PluginInfoList _lv2_plugin_info;
+ ARDOUR::PluginInfoList _au_plugin_info;
+
+#ifdef HAVE_SLV2
+ LV2World* _lv2_world;
+#endif
+
+ std::map<uint32_t, std::string> rdf_type;
+
+ std::string ladspa_path;
+ std::string vst_path;
+
+ void ladspa_refresh ();
+ void vst_refresh ();
+
+ void add_lrdf_data (const std::string &path);
+ void add_ladspa_presets ();
+ void add_vst_presets ();
+ void add_presets (std::string domain);
+
+ int au_discover ();
+ void au_refresh ();
+
+ int lv2_discover ();
+ void lv2_refresh ();
+
+ int vst_discover_from_path (std::string path);
+ int vst_discover (std::string path);
+
+ int ladspa_discover_from_path (std::string path);
+ int ladspa_discover (std::string path);
+
+ std::string get_ladspa_category (uint32_t id);
+ std::vector<uint32_t> ladspa_plugin_whitelist;
+
+ static PluginManager* _manager; // singleton
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_plugin_manager_h__ */
+
diff --git a/libs/ardour/ardour/port.h b/libs/ardour/ardour/port.h
new file mode 100644
index 0000000000..93c34da16d
--- /dev/null
+++ b/libs/ardour/ardour/port.h
@@ -0,0 +1,180 @@
+/*
+ Copyright (C) 2002 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_port_h__
+#define __ardour_port_h__
+
+#include <set>
+#include <vector>
+#include <string>
+
+#include <sigc++/signal.h>
+#include <pbd/failed_constructor.h>
+#include <ardour/ardour.h>
+#include <ardour/data_type.h>
+#include <jack/jack.h>
+
+namespace ARDOUR {
+
+class AudioEngine;
+class Buffer;
+
+/** Abstract base for ports
+ */
+class Port : public virtual sigc::trackable {
+ public:
+ enum Flags {
+ IsInput = JackPortIsInput,
+ IsOutput = JackPortIsOutput,
+ IsPhysical = JackPortIsPhysical,
+ IsTerminal = JackPortIsTerminal,
+ CanMonitor = JackPortCanMonitor
+ };
+
+ virtual ~Port();
+
+ std::string name() const {
+ return _name;
+ }
+
+ Flags flags() const {
+ return _flags;
+ }
+
+ bool receives_input() const {
+ return _flags & IsInput;
+ }
+
+ bool sends_output () const {
+ return _flags & IsOutput;
+ }
+
+ bool can_monitor () const {
+ return _flags & CanMonitor;
+ }
+
+ void enable_metering() {
+ _metering++;
+ }
+
+ void disable_metering () {
+ if (_metering) { _metering--; }
+ }
+
+ virtual void cycle_start (nframes_t nframes, nframes_t offset) {}
+ virtual void cycle_end (nframes_t nframes, nframes_t offset) {}
+ virtual DataType type() const = 0;
+ virtual Buffer& get_buffer() = 0;
+
+ virtual bool connected () const;
+ virtual bool connected_to (const std::string& portname) const;
+ virtual int get_connections (std::vector<std::string>&) const;
+
+ virtual int connect (Port& other);
+ virtual int disconnect (Port& other);
+ virtual int disconnect_all ();
+
+ virtual void reset ();
+ virtual int reestablish () {return 0; }
+ virtual int reconnect () { return 0; }
+
+ virtual int set_name (const std::string& str) {
+ _name = str;
+ return 0;
+ }
+
+ virtual std::string short_name() const = 0;
+ virtual bool monitoring_input () const = 0;
+ virtual void ensure_monitor_input (bool yn) = 0;
+ virtual void request_monitor_input (bool yn) = 0;
+ virtual nframes_t latency () const = 0;
+ virtual nframes_t total_latency () const = 0;
+ virtual void set_latency (nframes_t nframes) = 0;
+
+ sigc::signal<void,bool> MonitorInputChanged;
+ sigc::signal<void,bool> ClockSyncChanged;
+
+ static void set_engine (AudioEngine*);
+
+ protected:
+ friend class AudioEngine;
+
+ Port (const std::string& name, Flags flgs);
+
+ virtual void recompute_total_latency() const {}
+
+ /* engine isn't supposed to access below here */
+
+ Flags _flags;
+ std::string _type;
+ std::string _name;
+ unsigned short _metering;
+ bool _last_monitor;
+ nframes_t _latency;
+
+ std::set<Port*> _connections;
+
+ static AudioEngine* engine;
+};
+
+class PortConnectableByName {
+ public:
+ PortConnectableByName() {}
+ virtual ~PortConnectableByName() {}
+
+ virtual int connect (const std::string& other_name) = 0;
+ virtual int disconnect (const std::string& other_name) = 0;
+};
+
+class PortFacade : public virtual Port, public PortConnectableByName {
+ public:
+ PortFacade (const std::string& name, Flags flgs) : Port (name, flgs), _ext_port (0) {}
+ ~PortFacade() {}
+
+ void reset ();
+ int reestablish ();
+ int reconnect ();
+
+ int connect (Port& other);
+ int disconnect (Port& other);
+ int disconnect_all ();
+
+ int connect (const std::string& other_name);
+ int disconnect (const std::string& other_name);
+
+ bool connected () const;
+ bool connected_to (const std::string& portname) const;
+ int get_connections (std::vector<std::string>&) const;
+
+ std::string short_name() const;
+ int set_name (const std::string& str);
+ bool monitoring_input () const;
+ void ensure_monitor_input (bool yn);
+ void request_monitor_input (bool yn);
+ nframes_t latency () const;
+ nframes_t total_latency () const;
+ void set_latency (nframes_t nframes);
+
+ protected:
+ Port* _ext_port;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_port_h__ */
diff --git a/libs/ardour/ardour/port_insert.h b/libs/ardour/ardour/port_insert.h
new file mode 100644
index 0000000000..1743040bf5
--- /dev/null
+++ b/libs/ardour/ardour/port_insert.h
@@ -0,0 +1,73 @@
+/*
+ Copyright (C) 2000,2007 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_port_insert_h__
+#define __ardour_port_insert_h__
+
+#include <vector>
+#include <string>
+#include <exception>
+
+#include <sigc++/signal.h>
+#include <ardour/ardour.h>
+#include <ardour/io_processor.h>
+#include <ardour/types.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Session;
+
+/** Port inserts: send output to a Jack port, pick up input at a Jack port
+ */
+class PortInsert : public IOProcessor
+{
+ public:
+ PortInsert (Session&, Placement);
+ PortInsert (Session&, const XMLNode&);
+ PortInsert (const PortInsert&);
+ ~PortInsert ();
+
+ XMLNode& state(bool full);
+ XMLNode& get_state(void);
+ int set_state(const XMLNode&);
+
+ void init ();
+
+ void run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset);
+
+ nframes_t signal_latency() const;
+
+ ChanCount output_streams() const;
+ ChanCount input_streams() const;
+
+ virtual bool can_support_input_configuration (ChanCount in) const;
+ virtual ChanCount output_for_input_configuration (ChanCount in) const;
+ virtual bool configure_io (ChanCount in, ChanCount out);
+
+ uint32_t bit_slot() const { return bitslot; }
+
+ private:
+ uint32_t bitslot;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_port_insert_h__ */
diff --git a/libs/ardour/ardour/port_set.h b/libs/ardour/ardour/port_set.h
new file mode 100644
index 0000000000..51673472c3
--- /dev/null
+++ b/libs/ardour/ardour/port_set.h
@@ -0,0 +1,161 @@
+/*
+ Copyright (C) 2006 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_port_set_h__
+#define __ardour_port_set_h__
+
+#include <vector>
+#include <ardour/port.h>
+#include <ardour/audio_port.h>
+#include <ardour/midi_port.h>
+#include <ardour/chan_count.h>
+
+namespace ARDOUR {
+
+
+/** An ordered list of Ports, possibly of various types.
+ *
+ * This allows access to all the ports as a list, ignoring type, or accessing
+ * the nth port of a given type. Note that port(n) and nth_audio_port(n) may
+ * NOT return the same port.
+ */
+class PortSet {
+public:
+ PortSet();
+
+ size_t num_ports() const;
+ size_t num_ports(DataType type) const { return _ports[type].size(); }
+
+ void add(Port* port);
+ bool remove(Port* port);
+
+ /** nth port */
+ Port* port(size_t index) const;
+
+ /** nth port of type @a t, or nth port if t = NIL */
+ Port* port(DataType t, size_t index) const;
+
+ AudioPort* nth_audio_port(size_t n) const;
+
+ MidiPort* nth_midi_port(size_t n) const;
+
+ bool contains(const Port* port) const;
+
+ /** Remove all ports from the PortSet. Ports are not deregistered with
+ * the engine, it's the caller's responsibility to not leak here!
+ */
+ void clear() { _ports.clear(); }
+
+ const ChanCount& count() const { return _count; }
+
+ bool empty() const { return (_count.n_total() == 0); }
+
+ // ITERATORS
+
+ // FIXME: this is a filthy copy-and-paste mess
+
+ class iterator {
+ public:
+
+ Port& operator*() { return *_set.port(_type, _index); }
+ Port* operator->() { return _set.port(_type, _index); }
+ iterator& operator++() { ++_index; return *this; } // yes, prefix only
+ bool operator==(const iterator& other) { return (_index == other._index); }
+ bool operator!=(const iterator& other) { return (_index != other._index); }
+
+ private:
+ friend class PortSet;
+
+ iterator(PortSet& list, DataType type, size_t index)
+ : _set(list), _type(type), _index(index) {}
+
+ PortSet& _set;
+ DataType _type; ///< Ignored if NIL (to iterator over entire set)
+ size_t _index;
+ };
+
+ iterator begin(DataType type = DataType::NIL)
+ { return iterator(*this, type, 0); }
+
+ iterator end(DataType type = DataType::NIL)
+ {
+ return iterator(*this, type,
+ (type == DataType::NIL) ? _count.n_total() : _count.get(type));
+ }
+
+ // FIXME: typeify
+ class const_iterator {
+ public:
+
+ const Port& operator*() { return *_set.port(_index); }
+ const Port* operator->() { return _set.port(_index); }
+ const_iterator& operator++() { ++_index; return *this; } // yes, prefix only
+ bool operator==(const const_iterator& other) { return (_index == other._index); }
+ bool operator!=(const const_iterator& other) { return (_index != other._index); }
+
+ private:
+ friend class PortSet;
+
+ const_iterator(const PortSet& list, size_t index) : _set(list), _index(index) {}
+
+ const PortSet& _set;
+ size_t _index;
+ };
+
+ const_iterator begin() const { return const_iterator(*this, 0); }
+ const_iterator end() const { return const_iterator(*this, _count.n_total()); }
+
+
+ class audio_iterator {
+ public:
+
+ AudioPort& operator*() { return *_set.nth_audio_port(_index); }
+ AudioPort* operator->() { return _set.nth_audio_port(_index); }
+ audio_iterator& operator++() { ++_index; return *this; } // yes, prefix only
+ bool operator==(const audio_iterator& other) { return (_index == other._index); }
+ bool operator!=(const audio_iterator& other) { return (_index != other._index); }
+
+ private:
+ friend class PortSet;
+
+ audio_iterator(PortSet& list, size_t index) : _set(list), _index(index) {}
+
+ PortSet& _set;
+ size_t _index;
+ };
+
+ audio_iterator audio_begin() { return audio_iterator(*this, 0); }
+ audio_iterator audio_end() { return audio_iterator(*this, _count.n_audio()); }
+
+private:
+ // Prevent copies (undefined)
+ PortSet(const PortSet& copy);
+ void operator=(const PortSet& other);
+
+ typedef std::vector<Port*> PortVec;
+
+ // Vector of vectors, indexed by DataType::to_index()
+ std::vector<PortVec> _ports;
+
+ ChanCount _count;
+};
+
+
+} // namespace ARDOUR
+
+#endif // __ardour_port_set_h__
diff --git a/libs/ardour/ardour/processor.h b/libs/ardour/ardour/processor.h
new file mode 100644
index 0000000000..d3e95e8ebf
--- /dev/null
+++ b/libs/ardour/ardour/processor.h
@@ -0,0 +1,123 @@
+/*
+ Copyright (C) 2000 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_processor_h__
+#define __ardour_processor_h__
+
+#include <vector>
+#include <string>
+#include <exception>
+
+#include <pbd/statefuldestructible.h>
+
+#include <sigc++/signal.h>
+
+#include <ardour/types.h>
+#include <ardour/ardour.h>
+#include <ardour/buffer_set.h>
+#include <ardour/automatable.h>
+#include <ardour/latent.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Session;
+
+/* A mixer strip element - plugin, send, meter, etc.
+ */
+class Processor : public Automatable, public Latent
+{
+ public:
+ static const string state_node_name;
+
+ Processor(Session&, const string& name, Placement p); // TODO: remove placement in favour of sort key
+
+ virtual ~Processor() { }
+
+ static boost::shared_ptr<Processor> clone (boost::shared_ptr<const Processor>);
+
+ uint32_t sort_key() const { return _sort_key; }
+ void set_sort_key (uint32_t key);
+
+ Placement placement() const { return _placement; }
+ void set_placement (Placement);
+
+ bool active () const { return _active; }
+ void set_active (bool yn);
+
+ bool get_next_ab_is_active () const { return _next_ab_is_active; }
+ void set_next_ab_is_active (bool yn) { _next_ab_is_active = yn; }
+
+ virtual nframes_t signal_latency() const { return 0; }
+
+ virtual void transport_stopped (nframes_t frame) {}
+
+ virtual void set_block_size (nframes_t nframes) {}
+
+ virtual void run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) { assert(is_in_place()); }
+
+ virtual void run_out_of_place (BufferSet& input, BufferSet& output, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset) { assert(is_out_of_place()); }
+
+ virtual void silence (nframes_t nframes, nframes_t offset) {}
+
+ virtual void activate () { _active = true; ActiveChanged.emit(); }
+ virtual void deactivate () { _active = false; ActiveChanged.emit(); }
+
+ virtual bool configure_io (ChanCount in, ChanCount out) { _configured_input = in; return (_configured = true); }
+
+ /* Derived classes should override these, or processor appears as an in-place pass-through */
+
+ /** In-place processors implement run_in_place and modify thee input buffer parameter */
+ virtual bool is_in_place () const { return true; }
+
+ /* Out-Of-Place processors implement run_out_of_place, don't modify the input parameter
+ * and write to their output parameter */
+ virtual bool is_out_of_place () const { return false; }
+
+ virtual bool can_support_input_configuration (ChanCount in) const { return true; }
+ virtual ChanCount output_for_input_configuration (ChanCount in) const { return in; }
+ virtual ChanCount output_streams() const { return _configured_input; }
+ virtual ChanCount input_streams () const { return _configured_input; }
+
+ virtual XMLNode& state (bool full);
+ virtual XMLNode& get_state (void);
+ virtual int set_state (const XMLNode&);
+
+ void *get_gui () const { return _gui; }
+ void set_gui (void *p) { _gui = p; }
+
+ static sigc::signal<void,Processor*> ProcessorCreated;
+
+ sigc::signal<void> ActiveChanged;
+ sigc::signal<void> PlacementChanged;
+
+protected:
+ bool _active;
+ bool _next_ab_is_active;
+ bool _configured;
+ ChanCount _configured_input;
+ Placement _placement;
+ uint32_t _sort_key;
+ void* _gui; /* generic, we don't know or care what this is */
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_processor_h__ */
diff --git a/libs/ardour/ardour/profile.h b/libs/ardour/ardour/profile.h
new file mode 100644
index 0000000000..b016063c4d
--- /dev/null
+++ b/libs/ardour/ardour/profile.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 2000-2007 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_profile_h__
+#define __ardour_profile_h__
+
+#include <boost/dynamic_bitset.hpp>
+#include <stdint.h>
+
+namespace ARDOUR {
+
+class RuntimeProfile {
+ public:
+ enum Element {
+ SmallScreen,
+ SAE,
+ SinglePackage,
+ LastElement
+ };
+
+ RuntimeProfile() { bits.resize (LastElement); }
+ ~RuntimeProfile() {}
+
+ void set_small_screen() { bits[SmallScreen] = true; }
+ bool get_small_screen() const { return bits[SmallScreen]; }
+
+ void set_sae () { bits[SAE] = true; }
+ bool get_sae () const { return bits[SAE]; }
+
+ void set_single_package () { bits[SinglePackage] = true; }
+ bool get_single_package () const { return bits[SinglePackage]; }
+
+ private:
+ boost::dynamic_bitset<uint64_t> bits;
+
+};
+
+extern RuntimeProfile* Profile;
+
+}; // namespace ARDOUR
+
+#endif /* __ardour_profile_h__ */
diff --git a/libs/ardour/ardour/quantize.h b/libs/ardour/ardour/quantize.h
new file mode 100644
index 0000000000..143652dc63
--- /dev/null
+++ b/libs/ardour/ardour/quantize.h
@@ -0,0 +1,41 @@
+/*
+ Copyright (C) 2007 Paul Davis
+ Author: Dave Robillard
+
+ 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_quantize_h__
+#define __ardour_quantize_h__
+
+#include <ardour/filter.h>
+
+namespace ARDOUR {
+
+class Quantize : public Filter {
+public:
+ Quantize (ARDOUR::Session&, double q);
+ ~Quantize ();
+
+ int run (boost::shared_ptr<ARDOUR::Region>);
+
+private:
+ double _q;
+};
+
+} /* namespace */
+
+#endif /* __ardour_quantize_h__ */
diff --git a/libs/ardour/ardour/rb_effect.h b/libs/ardour/ardour/rb_effect.h
new file mode 100644
index 0000000000..a536a309b3
--- /dev/null
+++ b/libs/ardour/ardour/rb_effect.h
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2007 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_rbeffect_h__
+#define __ardour_rbeffect_h__
+
+#include <ardour/filter.h>
+
+namespace ARDOUR {
+
+class AudioRegion;
+
+class RBEffect : public Filter {
+ public:
+ RBEffect (ARDOUR::Session&, TimeFXRequest&);
+ ~RBEffect ();
+
+ int run (boost::shared_ptr<ARDOUR::Region>);
+
+ private:
+ TimeFXRequest& tsr;
+};
+
+} /* namespace */
+
+#endif /* __ardour_rbeffect_h__ */
diff --git a/libs/ardour/ardour/readable.h b/libs/ardour/ardour/readable.h
new file mode 100644
index 0000000000..e072a1c95e
--- /dev/null
+++ b/libs/ardour/ardour/readable.h
@@ -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__ */
diff --git a/libs/ardour/ardour/recent_sessions.h b/libs/ardour/ardour/recent_sessions.h
new file mode 100644
index 0000000000..e32b67bcd8
--- /dev/null
+++ b/libs/ardour/ardour/recent_sessions.h
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 2004 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_recent_sessions_h__
+#define __ardour_recent_sessions_h__
+
+#include <deque>
+#include <utility>
+#include <string>
+
+using std::deque;
+using std::pair;
+using std::string;
+
+namespace ARDOUR {
+ typedef deque<pair<string,string> > RecentSessions;
+
+ int read_recent_sessions (RecentSessions& rs);
+ int store_recent_sessions (string name, string path);
+ int write_recent_sessions (RecentSessions& rs);
+}; // namespace ARDOUR
+
+#endif // __ardour_recent_sessions_h__
diff --git a/libs/ardour/ardour/region.h b/libs/ardour/ardour/region.h
new file mode 100644
index 0000000000..00315846b2
--- /dev/null
+++ b/libs/ardour/ardour/region.h
@@ -0,0 +1,313 @@
+/*
+ Copyright (C) 2000-2001 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_region_h__
+#define __ardour_region_h__
+
+#include <vector>
+#include <boost/shared_ptr.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include <pbd/undo.h>
+
+#include <ardour/ardour.h>
+#include <ardour/data_type.h>
+#include <ardour/automatable.h>
+#include <ardour/readable.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Playlist;
+class Filter;
+class ExportSpecification;
+
+enum RegionEditState {
+ EditChangesNothing = 0,
+ EditChangesName = 1,
+ EditChangesID = 2
+};
+
+class Region : public Automatable, public boost::enable_shared_from_this<Region>, public Readable
+{
+ public:
+ typedef std::vector<boost::shared_ptr<Source> > SourceList;
+
+ enum Flag {
+ Muted = 0x1,
+ Opaque = 0x2,
+ EnvelopeActive = 0x4,
+ DefaultFadeIn = 0x8,
+ DefaultFadeOut = 0x10,
+ Locked = 0x20,
+ Automatic = 0x40,
+ WholeFile = 0x80,
+ FadeIn = 0x100,
+ FadeOut = 0x200,
+ Copied = 0x400,
+ Import = 0x800,
+ External = 0x1000,
+ SyncMarked = 0x2000,
+ LeftOfSplit = 0x4000,
+ RightOfSplit = 0x8000,
+ Hidden = 0x10000,
+ DoNotSaveState = 0x20000,
+ PositionLocked = 0x40000,
+ //
+ range_guarantoor = USHRT_MAX
+ };
+
+ enum PositionLockStyle {
+ AudioTime,
+ MusicTime
+ };
+
+ static const Flag DefaultFlags = Flag (Opaque|DefaultFadeIn|DefaultFadeOut|FadeIn|FadeOut);
+
+ static Change FadeChanged;
+ static Change SyncOffsetChanged;
+ static Change MuteChanged;
+ static Change OpacityChanged;
+ static Change LockChanged;
+ static Change LayerChanged;
+ static Change HiddenChanged;
+
+ sigc::signal<void,Change> StateChanged;
+
+ virtual ~Region();
+
+ /** Note: changing the name of a Region does not constitute an edit */
+ bool set_name (const std::string& str);
+
+ const DataType& data_type() const { return _type; }
+
+ /**
+ * Thats how the region parameters play together:
+ * <PRE>
+ * |------------------------------------------------------------------- track
+ * |..........[------------------].....| region
+ * |-----------------------------| _position
+ * |------------------| _length
+ * |----------| _start
+ * </PRE>
+ */
+ nframes_t position () const { return _position; }
+ nframes_t start () const { return _start; }
+ nframes_t length() const { return _length; }
+ layer_t layer () const { return _layer; }
+
+ /* these two are valid ONLY during a StateChanged signal handler */
+
+ nframes_t last_position() const { return _last_position; }
+ nframes_t last_length() const { return _last_length; }
+
+ nframes64_t ancestral_start () const { return _ancestral_start; }
+ nframes64_t ancestral_length () const { return _ancestral_length; }
+ float stretch() const { return _stretch; }
+ float shift() const { return _shift; }
+
+ void set_ancestral_data (nframes64_t start, nframes64_t length, float stretch, float shift);
+
+ nframes_t sync_offset(int& dir) const;
+ nframes_t sync_position() const;
+
+ nframes_t adjust_to_sync (nframes_t);
+
+ /* first_frame() is an alias; last_frame() just hides some math */
+
+ nframes_t first_frame() const { return _position; }
+ nframes_t last_frame() const { return _position + _length - 1; }
+
+ Flag flags() const { return _flags; }
+ bool hidden() const { return _flags & Hidden; }
+ bool muted() const { return _flags & Muted; }
+ bool opaque () const { return _flags & Opaque; }
+ bool locked() const { return _flags & Locked; }
+ bool position_locked() const { return _flags & PositionLocked; }
+ bool automatic() const { return _flags & Automatic; }
+ bool whole_file() const { return _flags & WholeFile ; }
+ bool captured() const { return !(_flags & (Region::Flag (Region::Import|Region::External))); }
+ bool can_move() const { return !(_flags & (Locked|PositionLocked)); }
+
+ PositionLockStyle positional_lock_style() const { return _positional_lock_style; }
+ void set_position_lock_style (PositionLockStyle ps);
+ void recompute_position_from_lock_style ();
+
+ virtual bool should_save_state () const { return !(_flags & DoNotSaveState); };
+
+ void freeze ();
+ void thaw (const string& why);
+
+ bool covers (nframes_t frame) const {
+ return first_frame() <= frame && frame <= last_frame();
+ }
+
+ OverlapType coverage (nframes_t start, nframes_t end) const {
+ return ARDOUR::coverage (first_frame(), last_frame(), start, end);
+ }
+
+ bool equivalent (boost::shared_ptr<const Region>) const;
+ bool size_equivalent (boost::shared_ptr<const Region>) const;
+ bool overlap_equivalent (boost::shared_ptr<const Region>) const;
+ bool region_list_equivalent (boost::shared_ptr<const Region>) const;
+ bool source_equivalent (boost::shared_ptr<const Region>) const;
+
+ /* EDITING OPERATIONS */
+
+ void set_length (nframes_t, void *src);
+ void set_start (nframes_t, void *src);
+ void set_position (nframes_t, void *src);
+ void set_position_on_top (nframes_t, void *src);
+ void special_set_position (nframes_t);
+ void update_position_after_tempo_map_change ();
+ void nudge_position (nframes64_t, void *src);
+
+ bool at_natural_position () const;
+ void move_to_natural_position (void *src);
+
+ void trim_start (nframes_t new_position, void *src);
+ void trim_front (nframes_t new_position, void *src);
+ void trim_end (nframes_t new_position, void *src);
+ void trim_to (nframes_t position, nframes_t length, void *src);
+
+ void set_layer (layer_t l); /* ONLY Playlist can call this */
+ void raise ();
+ void lower ();
+ void raise_to_top ();
+ void lower_to_bottom ();
+
+ void set_sync_position (nframes_t n);
+ void clear_sync_position ();
+ void set_hidden (bool yn);
+ void set_muted (bool yn);
+ void set_opaque (bool yn);
+ void set_locked (bool yn);
+ void set_position_locked (bool yn);
+
+ int apply (Filter&);
+
+ virtual uint32_t read_data_count() const { return _read_data_count; }
+
+ boost::shared_ptr<ARDOUR::Playlist> playlist() const { return _playlist.lock(); }
+ virtual void set_playlist (boost::weak_ptr<ARDOUR::Playlist>);
+
+ void source_deleted (boost::shared_ptr<Source>);
+
+ boost::shared_ptr<Source> source (uint32_t n=0) const { return _sources[ (n < _sources.size()) ? n : 0 ]; }
+ uint32_t n_channels() const { return _sources.size(); }
+
+ const SourceList& sources() const { return _sources; }
+ const SourceList& master_sources() const { return _master_sources; }
+
+ std::vector<string> master_source_names();
+ void set_master_sources (SourceList&);
+
+ /* serialization */
+
+ XMLNode& get_state ();
+ virtual XMLNode& state (bool);
+ virtual int set_state (const XMLNode&);
+ virtual int set_live_state (const XMLNode&, Change&, bool send);
+
+ virtual boost::shared_ptr<Region> get_parent() const;
+
+ uint64_t last_layer_op() const { return _last_layer_op; }
+ void set_last_layer_op (uint64_t when);
+
+ virtual bool is_dependent() const { return false; }
+ virtual bool depends_on (boost::shared_ptr<Region> other) const { return false; }
+
+ virtual int exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&) = 0;
+
+ virtual int get_transients (AnalysisFeatureList&, bool force_new = false) {
+ // no transients, but its OK
+ return 0;
+ }
+
+ void invalidate_transients ();
+
+ protected:
+ friend class RegionFactory;
+
+ Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length,
+ const string& name, DataType type, layer_t = 0, Flag flags = DefaultFlags);
+ 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 (const SourceList& srcs, const XMLNode&);
+
+ Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType, layer_t = 0, Flag flags = DefaultFlags);
+
+ protected:
+ XMLNode& get_short_state (); /* used only by Session */
+
+ void send_change (Change);
+
+ void trim_to_internal (nframes_t position, nframes_t length, void *src);
+ void set_position_internal (nframes_t pos, bool allow_bbt_recompute);
+
+ bool copied() const { return _flags & Copied; }
+ void maybe_uncopy ();
+ void first_edit ();
+
+ bool verify_start (nframes_t);
+ bool verify_start_and_length (nframes_t, nframes_t&);
+ bool verify_start_mutable (nframes_t&_start);
+ bool verify_length (nframes_t);
+
+ virtual void recompute_at_start () = 0;
+ virtual void recompute_at_end () = 0;
+
+ DataType _type;
+ Flag _flags;
+ nframes_t _start;
+ nframes_t _length;
+ nframes_t _last_length;
+ nframes_t _position;
+ nframes_t _last_position;
+ PositionLockStyle _positional_lock_style;
+ nframes_t _sync_position;
+ layer_t _layer;
+ mutable RegionEditState _first_edit;
+ int _frozen;
+ nframes64_t _ancestral_start;
+ nframes64_t _ancestral_length;
+ float _stretch;
+ float _shift;
+ BBT_Time _bbt_time;
+ AnalysisFeatureList _transients;
+ bool _valid_transients;
+ mutable uint32_t _read_data_count; ///< modified in read()
+ Change _pending_changed;
+ uint64_t _last_layer_op; ///< timestamp
+ Glib::Mutex _lock;
+ SourceList _sources;
+ /** Used when timefx are applied, so we can always use the original source */
+ SourceList _master_sources;
+
+ boost::weak_ptr<ARDOUR::Playlist> _playlist;
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_region_h__ */
diff --git a/libs/ardour/ardour/region_factory.h b/libs/ardour/ardour/region_factory.h
new file mode 100644
index 0000000000..12437ba998
--- /dev/null
+++ b/libs/ardour/ardour/region_factory.h
@@ -0,0 +1,64 @@
+/*
+ Copyright (C) 2000-2007 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_region_factory_h__
+#define __ardour_region_factory_h__
+
+#include <ardour/types.h>
+#include <ardour/region.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Session;
+
+class RegionFactory {
+
+ public:
+ /** This is emitted only when a new id is assigned. Therefore,
+ in a pure Region copy, it will not be emitted.
+
+ It must be emitted by derived classes, not Region
+ itself, to permit dynamic_cast<> to be used to
+ infer the type of Region.
+ */
+ static sigc::signal<void,boost::shared_ptr<Region> > CheckNewRegion;
+
+ static boost::shared_ptr<Region> create (boost::shared_ptr<const Region>);
+
+ /* note: both of the first two should use const shared_ptr as well, but
+ gcc 4.1 doesn't seem to be able to disambiguate them if they do.
+ */
+
+ static boost::shared_ptr<Region> create (boost::shared_ptr<Region>, nframes_t start,
+ 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<AudioRegion>, nframes_t start,
+ 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 (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 (Session&, XMLNode&, bool);
+ static boost::shared_ptr<Region> create (SourceList &, const XMLNode&);
+};
+
+}
+
+#endif /* __ardour_region_factory_h__ */
diff --git a/libs/ardour/ardour/resampled_source.h b/libs/ardour/ardour/resampled_source.h
new file mode 100644
index 0000000000..6eca4cda98
--- /dev/null
+++ b/libs/ardour/ardour/resampled_source.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2007 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_resampled_source_h__
+#define __ardour_resampled_source_h__
+
+#include <samplerate.h>
+
+#include <ardour/types.h>
+#include <ardour/importable_source.h>
+
+namespace ARDOUR {
+
+class ResampledImportableSource : public ImportableSource
+{
+ public:
+ ResampledImportableSource (boost::shared_ptr<ImportableSource>, nframes_t rate, SrcQuality);
+
+ ~ResampledImportableSource ();
+
+ nframes_t read (Sample* buffer, nframes_t nframes);
+ float ratio() const { return src_data.src_ratio; }
+ uint32_t channels() const { return source->channels(); }
+ nframes_t length() const { return source->length(); }
+ nframes_t samplerate() const { return source->samplerate(); }
+ void seek (nframes_t pos) { source->seek (pos); }
+
+ static const uint32_t blocksize;
+
+ private:
+ boost::shared_ptr<ImportableSource> source;
+ float* input;
+ SRC_STATE* src_state;
+ SRC_DATA src_data;
+};
+
+}
+
+#endif /* __ardour_resampled_source_h__ */
diff --git a/libs/ardour/ardour/reverse.h b/libs/ardour/ardour/reverse.h
new file mode 100644
index 0000000000..7870b5aa2e
--- /dev/null
+++ b/libs/ardour/ardour/reverse.h
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 2004 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_reverse_h__
+#define __ardour_reverse_h__
+
+#include <ardour/filter.h>
+
+namespace ARDOUR {
+
+class Reverse : public Filter {
+ public:
+ Reverse (ARDOUR::Session&);
+ ~Reverse ();
+
+ int run (boost::shared_ptr<ARDOUR::Region>);
+};
+
+} /* namespace */
+
+#endif /* __ardour_reverse_h__ */
diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h
new file mode 100644
index 0000000000..c7c0b77102
--- /dev/null
+++ b/libs/ardour/ardour/route.h
@@ -0,0 +1,384 @@
+/*
+ Copyright (C) 2000-2002 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_route_h__
+#define __ardour_route_h__
+
+#include <cmath>
+#include <list>
+#include <set>
+#include <map>
+#include <string>
+
+#include <boost/shared_ptr.hpp>
+
+#include <pbd/fastlog.h>
+#include <glibmm/thread.h>
+#include <pbd/xml++.h>
+#include <pbd/undo.h>
+#include <pbd/stateful.h>
+#include <pbd/controllable.h>
+#include <pbd/destructible.h>
+
+#include <ardour/ardour.h>
+#include <ardour/io.h>
+#include <ardour/session.h>
+#include <ardour/io_processor.h>
+#include <ardour/types.h>
+
+namespace ARDOUR {
+
+class Processor;
+class Send;
+class RouteGroup;
+
+enum mute_type {
+ PRE_FADER = 0x1,
+ POST_FADER = 0x2,
+ CONTROL_OUTS = 0x4,
+ MAIN_OUTS = 0x8
+};
+
+class Route : public IO
+{
+ protected:
+
+ typedef list<boost::shared_ptr<Processor> > ProcessorList;
+
+ public:
+
+ enum Flag {
+ Hidden = 0x1,
+ MasterOut = 0x2,
+ ControlOut = 0x4
+ };
+
+
+ Route (Session&, std::string name, int input_min, int input_max, int output_min, int output_max,
+ Flag flags = Flag(0), DataType default_type = DataType::AUDIO);
+ Route (Session&, const XMLNode&, DataType default_type = DataType::AUDIO);
+ virtual ~Route();
+
+ static std::string ensure_track_or_route_name(std::string, Session &);
+
+ std::string comment() { return _comment; }
+ void set_comment (std::string str, void *src);
+
+ long order_key (const char* name) const;
+ void set_order_key (const char* name, long n);
+
+ bool is_hidden() const { return _flags & Hidden; }
+ bool is_master() const { return _flags & MasterOut; }
+ bool is_control() const { return _flags & ControlOut; }
+
+ /* these are the core of the API of a Route. see the protected sections as well */
+
+
+ virtual int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, int declick, bool can_record, bool rec_monitors_input);
+
+ virtual int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, bool state_changing, bool can_record, bool rec_monitors_input);
+
+ virtual int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, bool can_record, bool rec_monitors_input);
+ virtual void toggle_monitor_input ();
+ virtual bool can_record() { return false; }
+ virtual void set_record_enable (bool yn, void *src) {}
+ virtual bool record_enabled() const { return false; }
+ virtual void handle_transport_stopped (bool abort, bool did_locate, bool flush_processors);
+ virtual void set_pending_declick (int);
+
+ /* end of vfunc-based API */
+
+ /* override IO::set_gain() to provide group control */
+
+ void set_gain (gain_t val, void *src);
+ void inc_gain (gain_t delta, void *src);
+
+ void set_solo (bool yn, void *src);
+ bool soloed() const { return _soloed; }
+
+ void set_solo_safe (bool yn, void *src);
+ bool solo_safe() const { return _solo_safe; }
+
+ void set_mute (bool yn, void *src);
+ bool muted() const { return _muted; }
+ bool solo_muted() const { return desired_solo_gain == 0.0; }
+
+ void set_mute_config (mute_type, bool, void *src);
+ bool get_mute_config (mute_type);
+
+ void set_edit_group (RouteGroup *, void *);
+ void drop_edit_group (void *);
+ RouteGroup *edit_group () { return _edit_group; }
+
+ void set_mix_group (RouteGroup *, void *);
+ void drop_mix_group (void *);
+ RouteGroup *mix_group () { return _mix_group; }
+
+ virtual void set_meter_point (MeterPoint, void *src);
+ MeterPoint meter_point() const { return _meter_point; }
+
+ /* Processors */
+
+ void flush_processors ();
+
+ template<class T> void foreach_processor (T *obj, void (T::*func)(boost::shared_ptr<Processor>)) {
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ (obj->*func) (*i);
+ }
+ }
+
+ boost::shared_ptr<Processor> nth_processor (uint32_t n) {
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+ ProcessorList::iterator i;
+ for (i = _processors.begin(); i != _processors.end() && n; ++i, --n);
+ if (i == _processors.end()) {
+ return boost::shared_ptr<IOProcessor> ();
+ } else {
+ return *i;
+ }
+ }
+
+ ChanCount max_processor_outs () const { return processor_max_outs; }
+ ChanCount pre_fader_streams() const;
+
+ /** A record of the stream configuration at some point in the processor list.
+ * Used to return where and why an processor list configuration request failed.
+ */
+ struct ProcessorStreams {
+ ProcessorStreams(size_t i=0, ChanCount c=ChanCount()) : index(i), count(c) {}
+
+ size_t index; ///< Index of processor where configuration failed
+ ChanCount count; ///< Input requested of processor
+ };
+
+ int add_processor (boost::shared_ptr<Processor>, ProcessorStreams* err = 0);
+ int add_processors (const ProcessorList&, ProcessorStreams* err = 0);
+ int remove_processor (boost::shared_ptr<Processor>, ProcessorStreams* err = 0);
+ int copy_processors (const Route&, Placement, ProcessorStreams* err = 0);
+ int sort_processors (ProcessorStreams* err = 0);
+ void disable_processors (Placement);
+ void disable_processors ();
+ void disable_plugins (Placement);
+ void disable_plugins ();
+ void ab_plugins (bool forward);
+ void clear_processors (Placement);
+ void all_processors_flip();
+ void all_processors_active (Placement, bool state);
+
+ virtual nframes_t update_total_latency();
+ void set_latency_delay (nframes_t);
+ void set_user_latency (nframes_t);
+ nframes_t initial_delay() const { return _initial_delay; }
+
+ sigc::signal<void,void*> solo_changed;
+ sigc::signal<void,void*> solo_safe_changed;
+ sigc::signal<void,void*> comment_changed;
+ sigc::signal<void,void*> mute_changed;
+ sigc::signal<void,void*> pre_fader_changed;
+ sigc::signal<void,void*> post_fader_changed;
+ sigc::signal<void,void*> control_outs_changed;
+ sigc::signal<void,void*> main_outs_changed;
+ sigc::signal<void> processors_changed;
+ sigc::signal<void,void*> record_enable_changed;
+ sigc::signal<void,void*> edit_group_changed;
+ sigc::signal<void,void*> mix_group_changed;
+ sigc::signal<void,void*> meter_change;
+ sigc::signal<void> signal_latency_changed;
+ sigc::signal<void> initial_delay_changed;
+
+ /* gui's call this for their own purposes. */
+
+ sigc::signal<void,std::string,void*> gui_changed;
+
+ /* stateful */
+
+ XMLNode& get_state();
+ int set_state(const XMLNode& node);
+ virtual XMLNode& get_template();
+
+ XMLNode& get_processor_state ();
+ int set_processor_state (const XMLNode&);
+
+ sigc::signal<void,void*> SelectedChanged;
+
+ int set_control_outs (const vector<std::string>& ports);
+ IO* control_outs() { return _control_outs; }
+
+ bool feeds (boost::shared_ptr<Route>);
+ set<boost::shared_ptr<Route> > fed_by;
+
+ struct ToggleControllable : public PBD::Controllable {
+ enum ToggleType {
+ MuteControl = 0,
+ SoloControl
+ };
+
+ ToggleControllable (std::string name, Route&, ToggleType);
+ void set_value (float);
+ float get_value (void) const;
+
+ Route& route;
+ ToggleType type;
+ };
+
+ boost::shared_ptr<PBD::Controllable> solo_control() {
+ return _solo_control;
+ }
+
+ boost::shared_ptr<PBD::Controllable> mute_control() {
+ return _mute_control;
+ }
+
+ void automation_snapshot (nframes_t now, bool force=false);
+ void protect_automation ();
+
+ void set_remote_control_id (uint32_t id);
+ uint32_t remote_control_id () const;
+ sigc::signal<void> RemoteControlIDChanged;
+
+ void sync_order_keys ();
+ static sigc::signal<void> SyncOrderKeys;
+
+ protected:
+ friend class Session;
+
+ void set_solo_mute (bool yn);
+ void set_block_size (nframes_t nframes);
+ bool has_external_redirects() const;
+ void curve_reallocate ();
+
+ protected:
+ Flag _flags;
+
+ /* tight cache-line access here is more important than sheer speed of
+ access.
+ */
+
+ bool _muted : 1;
+ bool _soloed : 1;
+ bool _solo_safe : 1;
+ bool _recordable : 1;
+ bool _mute_affects_pre_fader : 1;
+ bool _mute_affects_post_fader : 1;
+ bool _mute_affects_control_outs : 1;
+ bool _mute_affects_main_outs : 1;
+ bool _silent : 1;
+ bool _declickable : 1;
+ int _pending_declick;
+
+ MeterPoint _meter_point;
+
+ gain_t solo_gain;
+ gain_t mute_gain;
+ gain_t desired_solo_gain;
+ gain_t desired_mute_gain;
+
+
+
+ nframes_t _initial_delay;
+ nframes_t _roll_delay;
+ ProcessorList _processors;
+ Glib::RWLock _processor_lock;
+ IO *_control_outs;
+ Glib::Mutex _control_outs_lock;
+ RouteGroup *_edit_group;
+ RouteGroup *_mix_group;
+ std::string _comment;
+ bool _have_internal_generator;
+
+ boost::shared_ptr<ToggleControllable> _solo_control;
+ boost::shared_ptr<ToggleControllable> _mute_control;
+
+ nframes_t check_initial_delay (nframes_t, nframes_t&, nframes_t&);
+
+ void passthru (nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset, int declick, bool meter_inputs);
+
+ virtual void process_output_buffers (BufferSet& bufs,
+ nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset, bool with_processors, int declick,
+ bool meter);
+
+ protected:
+
+ virtual XMLNode& state(bool);
+
+ void passthru_silence (nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset, int declick,
+ bool meter);
+
+ void silence (nframes_t nframes, nframes_t offset);
+
+ sigc::connection input_signal_connection;
+
+ ChanCount processor_max_outs;
+ uint32_t _remote_control_id;
+
+ uint32_t pans_required() const;
+ ChanCount n_process_buffers ();
+
+ virtual int _set_state (const XMLNode&, bool call_base);
+ virtual void _set_processor_states (const XMLNodeList&);
+
+ private:
+ void init ();
+
+ static uint32_t order_key_cnt;
+
+ struct ltstr
+ {
+ bool operator()(const char* s1, const char* s2) const
+ {
+ return strcmp(s1, s2) < 0;
+ }
+ };
+
+ typedef std::map<const char*,long,ltstr> OrderKeys;
+ OrderKeys order_keys;
+
+ void input_change_handler (IOChange, void *src);
+ void output_change_handler (IOChange, void *src);
+
+ int reset_plugin_counts (ProcessorStreams*); /* locked */
+ int _reset_plugin_counts (ProcessorStreams*); /* unlocked */
+
+ /* processor I/O channels and plugin count handling */
+
+ struct ProcessorCount {
+ boost::shared_ptr<ARDOUR::Processor> processor;
+ ChanCount in;
+ ChanCount out;
+
+ ProcessorCount (boost::shared_ptr<ARDOUR::Processor> ins) : processor(ins) {}
+ };
+
+ int32_t apply_some_plugin_counts (std::list<ProcessorCount>& iclist);
+ bool check_some_plugin_counts (std::list<ProcessorCount>& iclist, ChanCount required_inputs, ProcessorStreams* err_streams);
+
+ void set_deferred_state ();
+ void add_processor_from_xml (const XMLNode&);
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_route_h__ */
diff --git a/libs/ardour/ardour/route_group.h b/libs/ardour/ardour/route_group.h
new file mode 100644
index 0000000000..ae16394289
--- /dev/null
+++ b/libs/ardour/ardour/route_group.h
@@ -0,0 +1,124 @@
+/*
+ Copyright (C) 2000 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_route_group_h__
+#define __ardour_route_group_h__
+
+#include <list>
+#include <set>
+#include <string>
+#include <stdint.h>
+#include <sigc++/signal.h>
+#include <pbd/stateful.h>
+#include <ardour/types.h>
+
+using std::string;
+using std::list;
+
+namespace ARDOUR {
+
+class Route;
+class Track;
+class AudioTrack;
+class Session;
+
+class RouteGroup : public PBD::Stateful, public sigc::trackable {
+ public:
+ enum Flag {
+ Relative = 0x1,
+ Active = 0x2,
+ Hidden = 0x4
+ };
+
+ RouteGroup (Session& s, const string &n, Flag f = Flag(0));
+
+ const string& name() { return _name; }
+ void set_name (std::string str);
+
+ bool is_active () const { return _flags & Active; }
+ bool is_relative () const { return _flags & Relative; }
+ bool is_hidden () const { return _flags & Hidden; }
+ bool empty() const {return routes.empty();}
+
+ gain_t get_max_factor(gain_t factor);
+ gain_t get_min_factor(gain_t factor);
+
+ int size() { return routes.size();}
+ ARDOUR::Route * first () const { return *routes.begin();}
+
+ void set_active (bool yn, void *src);
+ void set_relative (bool yn, void *src);
+ void set_hidden (bool yn, void *src);
+
+ int add (Route *);
+
+ int remove (Route *);
+
+ void apply (void (Route::*func)(void *), void *src) {
+ for (list<Route *>::iterator i = routes.begin(); i != routes.end(); i++) {
+ ((*i)->*func)(src);
+ }
+ }
+
+ template<class T> void apply (void (Route::*func)(T, void *), T val, void *src) {
+ for (list<Route *>::iterator i = routes.begin(); i != routes.end(); i++) {
+ ((*i)->*func)(val, src);
+ }
+ }
+
+ template<class T> void foreach_route (T *obj, void (T::*func)(Route&)) {
+ for (list<Route *>::iterator i = routes.begin(); i != routes.end(); i++) {
+ (obj->*func)(**i);
+ }
+ }
+
+ /* to use these, #include <ardour/route_group_specialized.h> */
+
+ template<class T> void apply (void (Track::*func)(T, void *), T val, void *src);
+
+ /* fills at_set with all members of the group that are AudioTracks */
+
+ void audio_track_group (std::set<AudioTrack*>& at_set);
+
+ void clear () {
+ routes.clear ();
+ changed();
+ }
+
+ const list<Route*>& route_list() { return routes; }
+
+ sigc::signal<void> changed;
+ sigc::signal<void,void*> FlagsChanged;
+
+ XMLNode& get_state (void);
+
+ int set_state (const XMLNode&);
+
+ private:
+ Session& _session;
+ list<Route *> routes;
+ string _name;
+ Flag _flags;
+
+ void remove_when_going_away (Route*);
+};
+
+} /* namespace */
+
+#endif /* __ardour_route_group_h__ */
diff --git a/libs/ardour/ardour/route_group_specialized.h b/libs/ardour/ardour/route_group_specialized.h
new file mode 100644
index 0000000000..9e04c46d0e
--- /dev/null
+++ b/libs/ardour/ardour/route_group_specialized.h
@@ -0,0 +1,41 @@
+/*
+ Copyright (C) 2000-2007 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_route_group_specialized_h__
+#define __ardour_route_group_specialized_h__
+
+#include <ardour/route_group.h>
+#include <ardour/audio_track.h>
+
+namespace ARDOUR {
+
+template<class T> void
+RouteGroup::apply (void (Track::*func)(T, void *), T val, void *src)
+{
+ for (list<Route *>::iterator i = routes.begin(); i != routes.end(); i++) {
+ Track *at;
+ if ((at = dynamic_cast<Track*>(*i)) != 0) {
+ (at->*func)(val, this);
+ }
+ }
+}
+
+} /* namespace ARDOUR */
+
+#endif /* __ardour_route_group_specialized_h__ */
diff --git a/libs/ardour/ardour/runtime_functions.h b/libs/ardour/ardour/runtime_functions.h
new file mode 100644
index 0000000000..c1dab4ebc7
--- /dev/null
+++ b/libs/ardour/ardour/runtime_functions.h
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 2007 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_runtime_functions_h__
+#define __ardour_runtime_functions_h__
+
+#include <ardour/types.h>
+
+namespace ARDOUR {
+
+ typedef float (*compute_peak_t) (const ARDOUR::Sample *, nframes_t, float);
+ typedef void (*find_peaks_t) (const ARDOUR::Sample *, nframes_t, float *, float*);
+ typedef void (*apply_gain_to_buffer_t) (ARDOUR::Sample *, nframes_t, float);
+ typedef void (*mix_buffers_with_gain_t) (ARDOUR::Sample *, const ARDOUR::Sample *, nframes_t, float);
+ typedef void (*mix_buffers_no_gain_t) (ARDOUR::Sample *, const ARDOUR::Sample *, nframes_t);
+
+ extern compute_peak_t compute_peak;
+ extern find_peaks_t find_peaks;
+ extern apply_gain_to_buffer_t apply_gain_to_buffer;
+ extern mix_buffers_with_gain_t mix_buffers_with_gain;
+ extern mix_buffers_no_gain_t mix_buffers_no_gain;
+}
+
+#endif /* __ardour_runtime_functions_h__ */
diff --git a/libs/ardour/ardour/send.h b/libs/ardour/ardour/send.h
new file mode 100644
index 0000000000..1ee8bbceca
--- /dev/null
+++ b/libs/ardour/ardour/send.h
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2000 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_send_h__
+#define __ardour_send_h__
+
+#include <sigc++/signal.h>
+#include <string>
+
+
+#include <pbd/stateful.h>
+#include <ardour/ardour.h>
+#include <ardour/audioengine.h>
+#include <ardour/io.h>
+#include <ardour/io_processor.h>
+
+namespace ARDOUR {
+
+class Send : public IOProcessor
+{
+ public:
+ Send (Session&, Placement);
+ Send (Session&, const XMLNode&);
+ Send (const Send&);
+ virtual ~Send ();
+
+ uint32_t bit_slot() const { return bitslot; }
+
+ ChanCount output_streams() const;
+ ChanCount input_streams () const;
+
+ void run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset);
+
+ void activate() {}
+ void deactivate () {}
+
+ void set_metering (bool yn);
+
+ XMLNode& state(bool full);
+ XMLNode& get_state(void);
+ int set_state(const XMLNode& node);
+
+ uint32_t pans_required() const { return _configured_input.n_audio(); }
+
+ virtual bool can_support_input_configuration (ChanCount in) const;
+ virtual ChanCount output_for_input_configuration (ChanCount in) const;
+ virtual bool configure_io (ChanCount in, ChanCount out);
+
+ static uint32_t how_many_sends();
+
+ private:
+ bool _metering;
+ uint32_t bitslot;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_send_h__ */
diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h
new file mode 100644
index 0000000000..b718d751b4
--- /dev/null
+++ b/libs/ardour/ardour/session.h
@@ -0,0 +1,1714 @@
+/*
+ Copyright (C) 2000 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_session_h__
+#define __ardour_session_h__
+
+#include <string>
+#include <list>
+#include <map>
+#include <vector>
+#include <set>
+#include <stack>
+
+#include <boost/scoped_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/dynamic_bitset.hpp>
+
+#include <stdint.h>
+
+#include <sndfile.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/error.h>
+#include <pbd/undo.h>
+#include <pbd/pool.h>
+#include <pbd/rcu.h>
+#include <pbd/statefuldestructible.h>
+
+#include <midi++/types.h>
+#include <midi++/mmc.h>
+
+#include <pbd/stateful.h>
+#include <pbd/destructible.h>
+
+#include <ardour/ardour.h>
+#include <ardour/configuration.h>
+#include <ardour/location.h>
+#include <ardour/gain.h>
+#include <ardour/io.h>
+
+#include <ardour/smpte.h>
+
+class XMLTree;
+class XMLNode;
+class AEffect;
+
+namespace MIDI {
+ class Port;
+}
+
+namespace PBD {
+ class Controllable;
+}
+
+namespace ARDOUR {
+
+class Port;
+class AudioEngine;
+class Slave;
+class Diskstream;
+class Route;
+class AuxInput;
+class Source;
+class AudioSource;
+class BufferSet;
+
+class Diskstream;
+class AudioDiskstream;
+class MidiDiskstream;
+class AudioFileSource;
+class MidiSource;
+class Auditioner;
+class Processor;
+class Send;
+class IOProcessor;
+class PortInsert;
+class PluginInsert;
+class Bundle;
+class TempoMap;
+class AudioTrack;
+class NamedSelection;
+class AudioRegion;
+
+class Region;
+class Playlist;
+class VSTPlugin;
+class ControlProtocolInfo;
+
+class MidiTrack;
+class MidiRegion;
+class SMFSource;
+
+class SessionDirectory;
+
+struct ExportSpecification;
+struct RouteGroup;
+
+using std::vector;
+using std::string;
+using std::map;
+using std::set;
+
+class Session : public PBD::StatefulDestructible
+{
+ private:
+ typedef std::pair<boost::weak_ptr<Route>,bool> RouteBooleanState;
+ typedef vector<RouteBooleanState> GlobalRouteBooleanState;
+ typedef std::pair<boost::weak_ptr<Route>,MeterPoint> RouteMeterState;
+ typedef vector<RouteMeterState> GlobalRouteMeterState;
+
+ public:
+ enum RecordState {
+ Disabled = 0,
+ Enabled = 1,
+ Recording = 2
+ };
+
+ struct Event {
+ enum Type {
+ SetTransportSpeed,
+ SetDiskstreamSpeed,
+ Locate,
+ LocateRoll,
+ LocateRollLocate,
+ SetLoop,
+ PunchIn,
+ PunchOut,
+ RangeStop,
+ RangeLocate,
+ Overwrite,
+ SetSlaveSource,
+ Audition,
+ InputConfigurationChange,
+ SetAudioRange,
+ SetPlayRange,
+
+ /* only one of each of these events
+ can be queued at any one time
+ */
+
+ StopOnce,
+ AutoLoop
+ };
+
+ enum Action {
+ Add,
+ Remove,
+ Replace,
+ Clear
+ };
+
+ Type type;
+ Action action;
+ nframes_t action_frame;
+ nframes_t target_frame;
+ float speed;
+
+ union {
+ void* ptr;
+ bool yes_or_no;
+ nframes_t target2_frame;
+ SlaveSource slave;
+ Route* route;
+ };
+
+ boost::shared_ptr<Region> region;
+
+ list<AudioRange> audio_range;
+ list<MusicRange> music_range;
+
+ Event(Type t, Action a, nframes_t when, nframes_t where, float spd, bool yn = false)
+ : type (t),
+ action (a),
+ action_frame (when),
+ target_frame (where),
+ speed (spd),
+ yes_or_no (yn) {}
+
+ void set_ptr (void* p) {
+ ptr = p;
+ }
+
+ bool before (const Event& other) const {
+ return action_frame < other.action_frame;
+ }
+
+ bool after (const Event& other) const {
+ return action_frame > other.action_frame;
+ }
+
+ static bool compare (const Event *e1, const Event *e2) {
+ return e1->before (*e2);
+ }
+
+ void *operator new (size_t ignored) {
+ return pool.alloc ();
+ }
+
+ void operator delete(void *ptr, size_t size) {
+ pool.release (ptr);
+ }
+
+ static const nframes_t Immediate = 0;
+
+ private:
+ static MultiAllocSingleReleasePool pool;
+ };
+
+ /* creating from an XML file */
+
+ Session (AudioEngine&,
+ const string& fullpath,
+ const string& snapshot_name,
+ string mix_template = "");
+
+ /* creating a new Session */
+
+ Session (AudioEngine&,
+ string fullpath,
+ string snapshot_name,
+ AutoConnectOption input_auto_connect,
+ AutoConnectOption output_auto_connect,
+ uint32_t control_out_channels,
+ uint32_t master_out_channels,
+ uint32_t n_physical_in,
+ uint32_t n_physical_out,
+ nframes_t initial_length);
+
+ virtual ~Session ();
+
+ string path() const { return _path; }
+ string name() const { return _name; }
+ string snap_name() const { return _current_snapshot_name; }
+ string raid_path () const;
+
+ void set_snap_name ();
+
+ void set_dirty ();
+ void set_clean ();
+ bool dirty() const { return _state_of_the_state & Dirty; }
+ void set_deletion_in_progress ();
+ bool deletion_in_progress() const { return _state_of_the_state & Deletion; }
+ sigc::signal<void> DirtyChanged;
+
+ const SessionDirectory& session_directory () const { return *(_session_dir.get()); }
+
+ static sigc::signal<void> AutoBindingOn;
+ static sigc::signal<void> AutoBindingOff;
+
+ static sigc::signal<void,std::string> Dialog;
+
+ std::string sound_dir (bool with_path = true) const;
+ std::string peak_dir () const;
+ std::string dead_sound_dir () const;
+ std::string automation_dir () const;
+ std::string analysis_dir() const;
+
+ int ensure_subdirs ();
+
+ Glib::ustring peak_path (Glib::ustring) const;
+
+ static string change_audio_path_by_name (string oldpath, string oldname, string newname, bool destructive);
+ static string change_midi_path_by_name (string oldpath, string oldname, string newname, bool destructive);
+
+ string peak_path_from_audio_path (string) const;
+ string audio_path_from_name (string, uint32_t nchans, uint32_t chan, bool destructive);
+ string midi_path_from_name (string);
+
+ void process (nframes_t nframes);
+
+ BufferSet& get_silent_buffers (ChanCount count = ChanCount::ZERO);
+ BufferSet& get_scratch_buffers (ChanCount count = ChanCount::ZERO);
+ BufferSet& get_mix_buffers (ChanCount count = ChanCount::ZERO);
+
+ void add_diskstream (boost::shared_ptr<Diskstream>);
+ boost::shared_ptr<Diskstream> diskstream_by_id (const PBD::ID& id);
+ boost::shared_ptr<Diskstream> diskstream_by_name (string name);
+
+ bool have_captured() const { return _have_captured; }
+
+ void refill_all_diskstream_buffers ();
+ uint32_t audio_diskstream_buffer_size() const { return audio_dstream_buffer_size; }
+ uint32_t midi_diskstream_buffer_size() const { return midi_dstream_buffer_size; }
+
+ uint32_t get_next_diskstream_id() const { return n_diskstreams(); }
+ uint32_t n_diskstreams() const;
+
+ typedef std::list<boost::shared_ptr<Diskstream> > DiskstreamList;
+ typedef std::list<boost::shared_ptr<Route> > RouteList;
+
+ boost::shared_ptr<RouteList> get_routes() const {
+ return routes.reader ();
+ }
+
+ uint32_t nroutes() const { return routes.reader()->size(); }
+ uint32_t ntracks () const;
+ uint32_t nbusses () const;
+
+ struct RoutePublicOrderSorter {
+ bool operator() (boost::shared_ptr<Route>, boost::shared_ptr<Route> b);
+ };
+
+ template<class T> void foreach_route (T *obj, void (T::*func)(Route&));
+ template<class T> void foreach_route (T *obj, void (T::*func)(boost::shared_ptr<Route>));
+ template<class T, class A> void foreach_route (T *obj, void (T::*func)(Route&, A), A arg);
+
+ boost::shared_ptr<Route> route_by_name (string);
+ boost::shared_ptr<Route> route_by_id (PBD::ID);
+ boost::shared_ptr<Route> route_by_remote_id (uint32_t id);
+
+ bool route_name_unique (string) const;
+
+ bool get_record_enabled() const {
+ return (record_status () >= Enabled);
+ }
+
+ RecordState record_status() const {
+ return (RecordState) g_atomic_int_get (&_record_status);
+ }
+
+ bool actively_recording () {
+ return record_status() == Recording;
+ }
+
+ bool record_enabling_legal () const;
+ void maybe_enable_record ();
+ void disable_record (bool rt_context, bool force = false);
+ void step_back_from_record ();
+
+ void maybe_write_autosave ();
+
+ /* Proxy signal for region hidden changes */
+
+ sigc::signal<void,boost::shared_ptr<Region> > RegionHiddenChange;
+
+ /* Emitted when all i/o connections are complete */
+
+ sigc::signal<void> IOConnectionsComplete;
+
+ /* Record status signals */
+
+ sigc::signal<void> RecordStateChanged;
+
+ /* Transport mechanism signals */
+
+ sigc::signal<void> TransportStateChange; /* generic */
+ sigc::signal<void,nframes_t> PositionChanged; /* sent after any non-sequential motion */
+ sigc::signal<void> DurationChanged;
+ sigc::signal<void,nframes_t> Xrun;
+ sigc::signal<void> TransportLooped;
+
+ sigc::signal<void,RouteList&> RouteAdded;
+
+ void request_roll_at_and_return (nframes_t start, nframes_t return_to);
+ void request_bounded_roll (nframes_t start, nframes_t end);
+ void request_stop (bool abort = false);
+ void request_locate (nframes_t frame, bool with_roll = false);
+
+ void request_play_loop (bool yn);
+ bool get_play_loop () const { return play_loop; }
+
+ nframes_t last_transport_start() const { return _last_roll_location; }
+ void goto_end () { request_locate (end_location->start(), false);}
+ void goto_start () { request_locate (start_location->start(), false); }
+ void set_session_start (nframes_t start) { start_location->set_start(start); }
+ void set_session_end (nframes_t end) { end_location->set_start(end); _end_location_is_free = false; }
+ void use_rf_shuttle_speed ();
+ void allow_auto_play (bool yn);
+ void request_transport_speed (float speed);
+ void request_overwrite_buffer (Diskstream*);
+ void request_diskstream_speed (Diskstream&, float speed);
+ void request_input_change_handling ();
+
+ bool locate_pending() const { return static_cast<bool>(post_transport_work&PostTransportLocate); }
+ bool transport_locked () const;
+
+ int wipe ();
+ //int wipe_diskstream (AudioDiskstream *);
+
+ int remove_region_from_region_list (boost::shared_ptr<Region>);
+
+ nframes_t get_maximum_extent () const;
+ nframes_t current_end_frame() const { return end_location->start(); }
+ nframes_t current_start_frame() const { return start_location->start(); }
+ // "actual" sample rate of session, set by current audioengine rate, pullup/down etc.
+ nframes_t frame_rate() const { return _current_frame_rate; }
+ // "native" sample rate of session, regardless of current audioengine rate, pullup/down etc
+ nframes_t nominal_frame_rate() const { return _nominal_frame_rate; }
+ nframes_t frames_per_hour() const { return _frames_per_hour; }
+
+ double frames_per_smpte_frame() const { return _frames_per_smpte_frame; }
+ nframes_t smpte_frames_per_hour() const { return _smpte_frames_per_hour; }
+
+ float smpte_frames_per_second() const;
+ bool smpte_drop_frames() const;
+
+ /* Locations */
+
+ Locations *locations() { return &_locations; }
+
+ sigc::signal<void,Location*> auto_loop_location_changed;
+ sigc::signal<void,Location*> auto_punch_location_changed;
+ sigc::signal<void> locations_modified;
+
+ void set_auto_punch_location (Location *);
+ void set_auto_loop_location (Location *);
+ int location_name(string& result, string base = string(""));
+
+ void reset_input_monitor_state ();
+
+ void add_event (nframes_t action_frame, Event::Type type, nframes_t target_frame = 0);
+ void remove_event (nframes_t frame, Event::Type type);
+ void clear_events (Event::Type type);
+
+ nframes_t get_block_size() const { return current_block_size; }
+ nframes_t worst_output_latency () const { return _worst_output_latency; }
+ nframes_t worst_input_latency () const { return _worst_input_latency; }
+ nframes_t worst_track_latency () const { return _worst_track_latency; }
+
+ int save_state (string snapshot_name, bool pending = false);
+ int restore_state (string snapshot_name);
+ int save_template (string template_name);
+ int save_history (string snapshot_name = "");
+ int restore_history (string snapshot_name);
+ void remove_state (string snapshot_name);
+ void rename_state (string old_name, string new_name);
+ void remove_pending_capture_state ();
+
+ sigc::signal<void,string> StateSaved;
+ sigc::signal<void> StateReady;
+
+ vector<string*>* possible_states() const;
+ static vector<string*>* possible_states(string path);
+
+ XMLNode& get_state();
+ int set_state(const XMLNode& node); // not idempotent
+ XMLNode& get_template();
+
+ /// The instant xml file is written to the session directory
+ void add_instant_xml (XMLNode&);
+ XMLNode * instant_xml (const std::string& str);
+
+ enum StateOfTheState {
+ Clean = 0x0,
+ Dirty = 0x1,
+ CannotSave = 0x2,
+ Deletion = 0x4,
+ InitialConnecting = 0x8,
+ Loading = 0x10,
+ InCleanup = 0x20
+ };
+
+ StateOfTheState state_of_the_state() const { return _state_of_the_state; }
+
+ RouteGroup* add_edit_group (string);
+ RouteGroup* add_mix_group (string);
+
+ void remove_edit_group (RouteGroup&);
+ void remove_mix_group (RouteGroup&);
+
+ RouteGroup *mix_group_by_name (string);
+ RouteGroup *edit_group_by_name (string);
+
+ sigc::signal<void,RouteGroup*> edit_group_added;
+ sigc::signal<void,RouteGroup*> mix_group_added;
+ sigc::signal<void> edit_group_removed;
+ sigc::signal<void> mix_group_removed;
+
+ void foreach_edit_group (sigc::slot<void,RouteGroup*> sl) {
+ for (list<RouteGroup *>::iterator i = edit_groups.begin(); i != edit_groups.end(); i++) {
+ sl (*i);
+ }
+ }
+
+ void foreach_mix_group (sigc::slot<void,RouteGroup*> sl) {
+ for (list<RouteGroup *>::iterator i = mix_groups.begin(); i != mix_groups.end(); i++) {
+ sl (*i);
+ }
+ }
+
+ /* fundamental operations. duh. */
+
+ std::list<boost::shared_ptr<AudioTrack> > new_audio_track (int input_channels, int output_channels, TrackMode mode = Normal, uint32_t how_many = 1);
+ RouteList new_audio_route (int input_channels, int output_channels, uint32_t how_many);
+
+ std::list<boost::shared_ptr<MidiTrack> > new_midi_track (TrackMode mode = Normal, uint32_t how_many = 1);
+ //boost::shared_ptr<Route> new_midi_route (uint32_t how_many = 1);
+
+ void remove_route (boost::shared_ptr<Route>);
+ void resort_routes ();
+ void resort_routes_using (boost::shared_ptr<RouteList>);
+
+ void set_remote_control_ids();
+
+ AudioEngine & engine() { return _engine; }
+ AudioEngine const & engine () const { return _engine; }
+
+ int32_t max_level;
+ int32_t min_level;
+
+ /* Time */
+
+ nframes_t transport_frame () const {return _transport_frame; }
+ nframes_t audible_frame () const;
+ nframes64_t requested_return_frame() const { return _requested_return_frame; }
+
+ enum PullupFormat {
+ pullup_Plus4Plus1,
+ pullup_Plus4,
+ pullup_Plus4Minus1,
+ pullup_Plus1,
+ pullup_None,
+ pullup_Minus1,
+ pullup_Minus4Plus1,
+ pullup_Minus4,
+ pullup_Minus4Minus1
+ };
+
+ int set_smpte_format (SmpteFormat);
+ void sync_time_vars();
+
+ void bbt_time (nframes_t when, BBT_Time&);
+ void smpte_to_sample( SMPTE::Time& smpte, nframes_t& sample, bool use_offset, bool use_subframes ) const;
+ void sample_to_smpte( nframes_t sample, SMPTE::Time& smpte, bool use_offset, bool use_subframes ) const;
+ void smpte_time (SMPTE::Time &);
+ void smpte_time (nframes_t when, SMPTE::Time&);
+ void smpte_time_subframes (nframes_t when, SMPTE::Time&);
+
+ void smpte_duration (nframes_t, SMPTE::Time&) const;
+ void smpte_duration_string (char *, nframes_t) const;
+
+ void set_smpte_offset (nframes_t);
+ nframes_t smpte_offset () const { return _smpte_offset; }
+ void set_smpte_offset_negative (bool);
+ bool smpte_offset_negative () const { return _smpte_offset_negative; }
+
+ nframes_t convert_to_frames_at (nframes_t position, AnyTime&);
+
+ static sigc::signal<void> StartTimeChanged;
+ static sigc::signal<void> EndTimeChanged;
+ static sigc::signal<void> SMPTEOffsetChanged;
+
+ void request_slave_source (SlaveSource);
+ bool synced_to_jack() const { return Config->get_slave_source() == JACK; }
+
+ float transport_speed() const { return _transport_speed; }
+ bool transport_stopped() const { return _transport_speed == 0.0f; }
+ bool transport_rolling() const { return _transport_speed != 0.0f; }
+
+ void set_silent (bool yn);
+ bool silent () { return _silent; }
+
+ int jack_slave_sync (nframes_t);
+
+ TempoMap& tempo_map() { return *_tempo_map; }
+
+ /* region info */
+
+ void add_regions (std::vector<boost::shared_ptr<Region> >&);
+
+ sigc::signal<void,boost::weak_ptr<Region> > RegionAdded;
+ sigc::signal<void,std::vector<boost::weak_ptr<Region> >& > RegionsAdded;
+ sigc::signal<void,boost::weak_ptr<Region> > RegionRemoved;
+
+ int region_name (string& result, string base = string(""), bool newlevel = false) const;
+ string new_region_name (string);
+ string path_from_region_name (DataType type, string name, string identifier);
+
+ boost::shared_ptr<Region> find_whole_file_parent (boost::shared_ptr<Region const>);
+
+ void find_equivalent_playlist_regions (boost::shared_ptr<Region>, std::vector<boost::shared_ptr<Region> >& result);
+
+ boost::shared_ptr<Region> XMLRegionFactory (const XMLNode&, bool full);
+ boost::shared_ptr<AudioRegion> XMLAudioRegionFactory (const XMLNode&, bool full);
+ boost::shared_ptr<MidiRegion> XMLMidiRegionFactory (const XMLNode&, bool full);
+
+ template<class T> void foreach_region (T *obj, void (T::*func)(boost::shared_ptr<Region>));
+
+ /* source management */
+
+ struct import_status : public InterThreadInfo {
+ string doing_what;
+
+ /* control info */
+ SrcQuality quality;
+ volatile bool freeze;
+ std::vector<Glib::ustring> paths;
+ bool replace_existing_source;
+
+ /* result */
+ SourceList sources;
+
+ };
+
+ void import_audiofiles (import_status&);
+ bool sample_rate_convert (import_status&, string infile, string& outfile);
+ string build_tmp_convert_name (string file);
+
+ SlaveSource post_export_slave;
+ nframes_t post_export_position;
+
+ int start_export (ARDOUR::ExportSpecification&);
+ int stop_export (ARDOUR::ExportSpecification&);
+ void finalize_audio_export ();
+
+ void add_source (boost::shared_ptr<Source>);
+ void remove_source (boost::weak_ptr<Source>);
+
+ struct cleanup_report {
+ vector<string> paths;
+ int64_t space;
+ };
+
+ int cleanup_sources (cleanup_report&);
+ int cleanup_trash_sources (cleanup_report&);
+
+ int destroy_region (boost::shared_ptr<Region>);
+ int destroy_regions (std::list<boost::shared_ptr<Region> >);
+
+ int remove_last_capture ();
+
+ /* handlers should return -1 for "stop cleanup", 0 for
+ "yes, delete this playlist" and 1 for "no, don't delete
+ this playlist.
+ */
+
+ sigc::signal<int,boost::shared_ptr<ARDOUR::Playlist> > AskAboutPlaylistDeletion;
+
+ /* handlers should return 0 for "ignore the rate mismatch"
+ and !0 for "do not use this session"
+ */
+
+ static sigc::signal<int,nframes_t, nframes_t> AskAboutSampleRateMismatch;
+
+ /* handlers should return !0 for use pending state, 0 for
+ ignore it.
+ */
+
+ static sigc::signal<int> AskAboutPendingState;
+
+ boost::shared_ptr<AudioFileSource> create_audio_source_for_session (ARDOUR::AudioDiskstream&, uint32_t which_channel, bool destructive);
+
+ boost::shared_ptr<MidiSource> create_midi_source_for_session (ARDOUR::MidiDiskstream&);
+
+ boost::shared_ptr<Source> source_by_id (const PBD::ID&);
+ boost::shared_ptr<Source> source_by_path_and_channel (const Glib::ustring&, uint16_t);
+
+ /* playlist management */
+
+ boost::shared_ptr<Playlist> playlist_by_name (string name);
+ void add_playlist (boost::shared_ptr<Playlist>);
+ sigc::signal<void,boost::shared_ptr<Playlist> > PlaylistAdded;
+ sigc::signal<void,boost::shared_ptr<Playlist> > PlaylistRemoved;
+
+ uint32_t n_playlists() const;
+
+ template<class T> void foreach_playlist (T *obj, void (T::*func)(boost::shared_ptr<Playlist>));
+ void get_playlists (std::vector<boost::shared_ptr<Playlist> >&);
+
+ /* named selections */
+
+ NamedSelection* named_selection_by_name (string name);
+ void add_named_selection (NamedSelection *);
+ void remove_named_selection (NamedSelection *);
+
+ template<class T> void foreach_named_selection (T& obj, void (T::*func)(NamedSelection&));
+ sigc::signal<void> NamedSelectionAdded;
+ sigc::signal<void> NamedSelectionRemoved;
+
+ /* Curves and AutomationLists (TODO when they go away) */
+ void add_curve(Curve*);
+ void add_automation_list(AutomationList*);
+
+ /* fade curves */
+
+ float get_default_fade_length () const { return default_fade_msecs; }
+ float get_default_fade_steepness () const { return default_fade_steepness; }
+ void set_default_fade (float steepness, float msecs);
+
+ /* auditioning */
+
+ boost::shared_ptr<Auditioner> the_auditioner() { return auditioner; }
+ void audition_playlist ();
+ void audition_region (boost::shared_ptr<Region>);
+ void cancel_audition ();
+ bool is_auditioning () const;
+
+ sigc::signal<void,bool> AuditionActive;
+
+ /* flattening stuff */
+
+ int write_one_audio_track (AudioTrack&, nframes_t start, nframes_t cnt, bool overwrite,
+ vector<boost::shared_ptr<Source> >&, InterThreadInfo& wot);
+
+ int freeze (InterThreadInfo&);
+
+ /* session-wide solo/mute/rec-enable */
+
+ bool soloing() const { return currently_soloing; }
+
+ void set_all_solo (bool);
+ void set_all_mute (bool);
+
+ sigc::signal<void,bool> SoloActive;
+ sigc::signal<void> SoloChanged;
+
+ void record_disenable_all ();
+ void record_enable_all ();
+
+ /* control/master out */
+
+ boost::shared_ptr<IO> control_out() const { return _control_out; }
+ boost::shared_ptr<IO> master_out() const { return _master_out; }
+
+ /* insert/send management */
+
+ uint32_t n_port_inserts() const { return _port_inserts.size(); }
+ uint32_t n_plugin_inserts() const { return _plugin_inserts.size(); }
+ uint32_t n_sends() const { return _sends.size(); }
+
+ static void set_disable_all_loaded_plugins (bool yn) {
+ _disable_all_loaded_plugins = yn;
+ }
+ static bool get_disable_all_loaded_plugins() {
+ return _disable_all_loaded_plugins;
+ }
+
+ uint32_t next_send_id();
+ uint32_t next_insert_id();
+ void mark_send_id (uint32_t);
+ void mark_insert_id (uint32_t);
+
+ /* s/w "RAID" management */
+
+ nframes_t available_capture_duration();
+
+ /* I/O bundles */
+
+ void foreach_bundle (sigc::slot<void, boost::shared_ptr<Bundle> >);
+ void add_bundle (boost::shared_ptr<Bundle>);
+ void remove_bundle (boost::shared_ptr<Bundle>);
+ boost::shared_ptr<Bundle> bundle_by_name (string) const;
+
+ sigc::signal<void,boost::shared_ptr<Bundle> > BundleAdded;
+ sigc::signal<void,boost::shared_ptr<Bundle> > BundleRemoved;
+
+ /* MIDI */
+
+ void midi_panic(void);
+ int set_mtc_port (string port_tag);
+ int set_mmc_port (string port_tag);
+ int set_midi_port (string port_tag);
+ MIDI::Port *mtc_port() const { return _mtc_port; }
+ MIDI::Port *mmc_port() const { return _mmc_port; }
+ MIDI::Port *midi_port() const { return _midi_port; }
+
+ sigc::signal<void> MTC_PortChanged;
+ sigc::signal<void> MMC_PortChanged;
+ sigc::signal<void> MIDI_PortChanged;
+
+ void set_trace_midi_input (bool, MIDI::Port* port = 0);
+ void set_trace_midi_output (bool, MIDI::Port* port = 0);
+
+ bool get_trace_midi_input(MIDI::Port *port = 0);
+ bool get_trace_midi_output(MIDI::Port *port = 0);
+
+ void set_mmc_receive_device_id (uint32_t id);
+ void set_mmc_send_device_id (uint32_t id);
+
+ /* Scrubbing */
+
+ void start_scrub (nframes_t where);
+ void stop_scrub ();
+ void set_scrub_speed (float);
+ nframes_t scrub_buffer_size() const;
+ sigc::signal<void> ScrubReady;
+
+ /* History (for editors, mixers, UIs etc.) */
+
+ /** Undo some transactions.
+ * @param n Number of transactions to undo.
+ */
+ void undo (uint32_t n) {
+ _history.undo (n);
+ }
+
+ void redo (uint32_t n) {
+ _history.redo (n);
+ }
+
+ UndoHistory& history() { return _history; }
+
+ uint32_t undo_depth() const { return _history.undo_depth(); }
+ uint32_t redo_depth() const { return _history.redo_depth(); }
+ string next_undo() const { return _history.next_undo(); }
+ string next_redo() const { return _history.next_redo(); }
+
+ void begin_reversible_command (const string& cmd_name);
+ void commit_reversible_command (Command* cmd = 0);
+
+ void add_command (Command *const cmd) {
+ current_trans->add_command (cmd);
+ }
+
+ std::map<PBD::ID, PBD::StatefulThingWithGoingAway*> registry;
+
+ // these commands are implemented in libs/ardour/session_command.cc
+ Command* memento_command_factory(XMLNode* n);
+ void register_with_memento_command_factory(PBD::ID, PBD::StatefulThingWithGoingAway*);
+
+ Command* global_state_command_factory (const XMLNode& n);
+
+ class GlobalRouteStateCommand : public Command
+ {
+ public:
+ GlobalRouteStateCommand (Session&, void*);
+ GlobalRouteStateCommand (Session&, const XMLNode& node);
+ int set_state (const XMLNode&);
+ XMLNode& get_state ();
+
+ protected:
+ GlobalRouteBooleanState before, after;
+ Session& sess;
+ void* src;
+
+ };
+
+ class GlobalSoloStateCommand : public GlobalRouteStateCommand
+ {
+ public:
+ GlobalSoloStateCommand (Session &, void *src);
+ GlobalSoloStateCommand (Session&, const XMLNode&);
+ void operator()(); //redo
+ void undo();
+ XMLNode &get_state();
+ void mark();
+ };
+
+ class GlobalMuteStateCommand : public GlobalRouteStateCommand
+ {
+ public:
+ GlobalMuteStateCommand(Session &, void *src);
+ GlobalMuteStateCommand (Session&, const XMLNode&);
+ void operator()(); // redo
+ void undo();
+ XMLNode &get_state();
+ void mark();
+ };
+
+ class GlobalRecordEnableStateCommand : public GlobalRouteStateCommand
+ {
+ public:
+ GlobalRecordEnableStateCommand(Session &, void *src);
+ GlobalRecordEnableStateCommand (Session&, const XMLNode&);
+ void operator()(); // redo
+ void undo();
+ XMLNode &get_state();
+ void mark();
+ };
+
+ class GlobalMeteringStateCommand : public Command
+ {
+ public:
+ GlobalMeteringStateCommand(Session &, void *src);
+ GlobalMeteringStateCommand (Session&, const XMLNode&);
+ void operator()();
+ void undo();
+ XMLNode &get_state();
+ int set_state (const XMLNode&);
+ void mark();
+
+ protected:
+ Session& sess;
+ void* src;
+ GlobalRouteMeterState before;
+ GlobalRouteMeterState after;
+ };
+
+ /* clicking */
+
+ boost::shared_ptr<IO> click_io() { return _click_io; }
+
+ /* disk, buffer loads */
+
+ uint32_t playback_load ();
+ uint32_t capture_load ();
+ uint32_t playback_load_min ();
+ uint32_t capture_load_min ();
+
+ void reset_playback_load_min ();
+ void reset_capture_load_min ();
+
+ float read_data_rate () const;
+ float write_data_rate () const;
+
+ /* ranges */
+
+ void set_audio_range (list<AudioRange>&);
+ void set_music_range (list<MusicRange>&);
+
+ void request_play_range (bool yn);
+ bool get_play_range () const { return _play_range; }
+
+ /* buffers for gain and pan */
+
+ gain_t* gain_automation_buffer () const { return _gain_automation_buffer; }
+ pan_t** pan_automation_buffer () const { return _pan_automation_buffer; }
+
+ /* buffers for conversion */
+ enum RunContext {
+ ButlerContext = 0,
+ TransportContext,
+ ExportContext
+ };
+
+ /* VST support */
+
+ static long vst_callback (AEffect* effect,
+ long opcode,
+ long index,
+ long value,
+ void* ptr,
+ float opt);
+
+ typedef float (*compute_peak_t) (Sample *, nframes_t, float);
+ typedef void (*find_peaks_t) (Sample *, nframes_t, float *, float*);
+ typedef void (*apply_gain_to_buffer_t) (Sample *, nframes_t, float);
+ typedef void (*mix_buffers_with_gain_t) (Sample *, Sample *, nframes_t, float);
+ typedef void (*mix_buffers_no_gain_t) (Sample *, Sample *, nframes_t);
+
+ static compute_peak_t compute_peak;
+ static find_peaks_t find_peaks;
+ static apply_gain_to_buffer_t apply_gain_to_buffer;
+ static mix_buffers_with_gain_t mix_buffers_with_gain;
+ static mix_buffers_no_gain_t mix_buffers_no_gain;
+
+ static sigc::signal<void> SendFeedback;
+
+ /* Controllables */
+
+ boost::shared_ptr<PBD::Controllable> controllable_by_id (const PBD::ID&);
+
+ void add_controllable (boost::shared_ptr<PBD::Controllable>);
+ void remove_controllable (PBD::Controllable*);
+
+ protected:
+ friend class AudioEngine;
+ void set_block_size (nframes_t nframes);
+ void set_frame_rate (nframes_t nframes);
+
+ protected:
+ friend class Diskstream;
+ void stop_butler ();
+ void wait_till_butler_finished();
+
+ protected:
+ friend class Route;
+ void schedule_curve_reallocation ();
+ void update_latency_compensation (bool, bool);
+
+ private:
+ int create (bool& new_session, const string& mix_template, nframes_t initial_length);
+ void destroy ();
+
+ nframes_t compute_initial_length ();
+
+ enum SubState {
+ PendingDeclickIn = 0x1,
+ PendingDeclickOut = 0x2,
+ StopPendingCapture = 0x4,
+ AutoReturning = 0x10,
+ PendingLocate = 0x20,
+ PendingSetLoop = 0x40
+ };
+
+ /* stuff used in process() should be close together to
+ maximise cache hits
+ */
+
+ typedef void (Session::*process_function_type)(nframes_t);
+
+ AudioEngine& _engine;
+ mutable gint processing_prohibited;
+ process_function_type process_function;
+ process_function_type last_process_function;
+ bool waiting_for_sync_offset;
+ nframes_t _base_frame_rate;
+ nframes_t _current_frame_rate; //this includes video pullup offset
+ nframes_t _nominal_frame_rate; //ignores audioengine setting, "native" SR
+ int transport_sub_state;
+ mutable gint _record_status;
+ volatile nframes_t _transport_frame;
+ Location* end_location;
+ Location* start_location;
+ Slave* _slave;
+ bool _silent;
+ volatile float _transport_speed;
+ volatile float _desired_transport_speed;
+ float _last_transport_speed;
+ bool auto_play_legal;
+ nframes_t _last_slave_transport_frame;
+ nframes_t maximum_output_latency;
+ nframes_t last_stop_frame;
+ volatile nframes64_t _requested_return_frame;
+ BufferSet* _scratch_buffers;
+ BufferSet* _silent_buffers;
+ BufferSet* _mix_buffers;
+ nframes_t current_block_size;
+ nframes_t _worst_output_latency;
+ nframes_t _worst_input_latency;
+ nframes_t _worst_track_latency;
+ bool _have_captured;
+ float _meter_hold;
+ float _meter_falloff;
+ bool _end_location_is_free;
+
+ void set_worst_io_latencies ();
+ void set_worst_io_latencies_x (IOChange asifwecare, void *ignored) {
+ set_worst_io_latencies ();
+ }
+
+ void update_latency_compensation_proxy (void* ignored);
+
+ void ensure_buffers (ChanCount howmany);
+
+ void process_scrub (nframes_t);
+ void process_without_events (nframes_t);
+ void process_with_events (nframes_t);
+ void process_audition (nframes_t);
+ int process_export (nframes_t, ARDOUR::ExportSpecification*);
+
+ /* slave tracking */
+
+ static const int delta_accumulator_size = 25;
+ int delta_accumulator_cnt;
+ long delta_accumulator[delta_accumulator_size];
+ long average_slave_delta;
+ int average_dir;
+ bool have_first_delta_accumulator;
+
+ enum SlaveState {
+ Stopped,
+ Waiting,
+ Running
+ };
+
+ SlaveState slave_state;
+ nframes_t slave_wait_end;
+
+ void reset_slave_state ();
+ bool follow_slave (nframes_t, nframes_t);
+ void set_slave_source (SlaveSource);
+
+ bool _exporting;
+ int prepare_to_export (ARDOUR::ExportSpecification&);
+
+ void prepare_diskstreams ();
+ void commit_diskstreams (nframes_t, bool& session_requires_butler);
+ int process_routes (nframes_t, nframes_t);
+ int silent_process_routes (nframes_t, nframes_t);
+
+ bool get_rec_monitors_input () {
+ if (actively_recording()) {
+ return true;
+ } else {
+ if (Config->get_auto_input()) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ }
+
+ int get_transport_declick_required () {
+
+ if (transport_sub_state & PendingDeclickIn) {
+ transport_sub_state &= ~PendingDeclickIn;
+ return 1;
+ } else if (transport_sub_state & PendingDeclickOut) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+ bool maybe_stop (nframes_t limit) {
+ if ((_transport_speed > 0.0f && _transport_frame >= limit) || (_transport_speed < 0.0f && _transport_frame == 0)) {
+ stop_transport ();
+ return true;
+ }
+ return false;
+ }
+
+ bool maybe_sync_start (nframes_t&, nframes_t&);
+
+ void check_declick_out ();
+
+ MIDI::MachineControl* mmc;
+ MIDI::Port* _mmc_port;
+ MIDI::Port* _mtc_port;
+ MIDI::Port* _midi_port;
+ string _path;
+ string _name;
+ bool session_send_mmc;
+ bool session_send_mtc;
+ bool session_midi_feedback;
+ bool play_loop;
+ bool loop_changing;
+ nframes_t last_loopend;
+
+ boost::scoped_ptr<SessionDirectory> _session_dir;
+
+ RingBuffer<Event*> pending_events;
+
+ void hookup_io ();
+ void when_engine_running ();
+ void graph_reordered ();
+
+ string _current_snapshot_name;
+
+ XMLTree* state_tree;
+ bool state_was_pending;
+ StateOfTheState _state_of_the_state;
+
+ void auto_save();
+ int load_options (const XMLNode&);
+ XMLNode& get_options () const;
+ int load_state (string snapshot_name);
+ bool save_config_options_predicate (ConfigVariableBase::Owner owner) const;
+
+ nframes_t _last_roll_location;
+ nframes_t _last_record_location;
+
+ bool pending_locate_roll;
+ nframes_t pending_locate_frame;
+ bool pending_locate_flush;
+ bool pending_abort;
+ bool pending_auto_loop;
+
+ Sample* butler_mixdown_buffer;
+ float* butler_gain_buffer;
+ pthread_t butler_thread;
+ Glib::Mutex butler_request_lock;
+ Glib::Cond butler_paused;
+ bool butler_should_run;
+ mutable gint butler_should_do_transport_work;
+ int butler_request_pipe[2];
+
+ inline bool transport_work_requested() const { return g_atomic_int_get(&butler_should_do_transport_work); }
+
+ struct ButlerRequest {
+ enum Type {
+ Wake,
+ Run,
+ Pause,
+ Quit
+ };
+ };
+
+ enum PostTransportWork {
+ PostTransportStop = 0x1,
+ PostTransportDisableRecord = 0x2,
+ PostTransportPosition = 0x8,
+ PostTransportDidRecord = 0x20,
+ PostTransportDuration = 0x40,
+ PostTransportLocate = 0x80,
+ PostTransportRoll = 0x200,
+ PostTransportAbort = 0x800,
+ PostTransportOverWrite = 0x1000,
+ PostTransportSpeed = 0x2000,
+ PostTransportAudition = 0x4000,
+ PostTransportScrub = 0x8000,
+ PostTransportReverse = 0x10000,
+ PostTransportInputChange = 0x20000,
+ PostTransportCurveRealloc = 0x40000
+ };
+
+ static const PostTransportWork ProcessCannotProceedMask =
+ PostTransportWork (PostTransportInputChange|
+ PostTransportSpeed|
+ PostTransportReverse|
+ PostTransportCurveRealloc|
+ PostTransportScrub|
+ PostTransportAudition|
+ PostTransportLocate|
+ PostTransportStop);
+
+ PostTransportWork post_transport_work;
+
+ void summon_butler ();
+ void schedule_butler_transport_work ();
+ int start_butler_thread ();
+ void terminate_butler_thread ();
+ static void *_butler_thread_work (void *arg);
+ void* butler_thread_work ();
+
+ uint32_t cumulative_rf_motion;
+ uint32_t rf_scale;
+
+ void set_rf_speed (float speed);
+ void reset_rf_scale (nframes_t frames_moved);
+
+ Locations _locations;
+ void locations_changed ();
+ void locations_added (Location*);
+ void handle_locations_changed (Locations::LocationList&);
+
+ sigc::connection auto_punch_start_changed_connection;
+ sigc::connection auto_punch_end_changed_connection;
+ sigc::connection auto_punch_changed_connection;
+ void auto_punch_start_changed (Location *);
+ void auto_punch_end_changed (Location *);
+ void auto_punch_changed (Location *);
+
+ sigc::connection auto_loop_start_changed_connection;
+ sigc::connection auto_loop_end_changed_connection;
+ sigc::connection auto_loop_changed_connection;
+ void auto_loop_changed (Location *);
+
+ typedef list<Event *> Events;
+ Events events;
+ Events immediate_events;
+ Events::iterator next_event;
+
+ /* there can only ever be one of each of these */
+
+ Event *auto_loop_event;
+ Event *punch_out_event;
+ Event *punch_in_event;
+
+ /* events */
+
+ void dump_events () const;
+ void queue_event (Event *ev);
+ void merge_event (Event*);
+ void replace_event (Event::Type, nframes_t action_frame, nframes_t target = 0);
+ bool _replace_event (Event*);
+ bool _remove_event (Event *);
+ void _clear_event_type (Event::Type);
+
+ void first_stage_init (string path, string snapshot_name);
+ int second_stage_init (bool new_tracks);
+ void find_current_end ();
+ void remove_empty_sounds ();
+
+ void setup_midi_control ();
+ int midi_read (MIDI::Port *);
+
+ void enable_record ();
+
+ void increment_transport_position (uint32_t val) {
+ if (max_frames - val < _transport_frame) {
+ _transport_frame = max_frames;
+ } else {
+ _transport_frame += val;
+ }
+ }
+
+ void decrement_transport_position (uint32_t val) {
+ if (val < _transport_frame) {
+ _transport_frame -= val;
+ } else {
+ _transport_frame = 0;
+ }
+ }
+
+ void post_transport_motion ();
+ static void *session_loader_thread (void *arg);
+
+ void *do_work();
+
+ void set_next_event ();
+ void process_event (Event *ev);
+
+ /* MIDI Machine Control */
+
+ void deliver_mmc (MIDI::MachineControl::Command, nframes_t);
+
+ void spp_start (MIDI::Parser&);
+ void spp_continue (MIDI::Parser&);
+ void spp_stop (MIDI::Parser&);
+
+ void mmc_deferred_play (MIDI::MachineControl &);
+ void mmc_stop (MIDI::MachineControl &);
+ void mmc_step (MIDI::MachineControl &, int);
+ void mmc_pause (MIDI::MachineControl &);
+ void mmc_record_pause (MIDI::MachineControl &);
+ void mmc_record_strobe (MIDI::MachineControl &);
+ void mmc_record_exit (MIDI::MachineControl &);
+ void mmc_track_record_status (MIDI::MachineControl &, uint32_t track, bool enabled);
+ void mmc_fast_forward (MIDI::MachineControl &);
+ void mmc_rewind (MIDI::MachineControl &);
+ void mmc_locate (MIDI::MachineControl &, const MIDI::byte *);
+ void mmc_shuttle (MIDI::MachineControl &mmc, float speed, bool forw);
+ void mmc_record_enable (MIDI::MachineControl &mmc, size_t track, bool enabled);
+
+ struct timeval last_mmc_step;
+ double step_speed;
+
+ typedef sigc::slot<bool> MidiTimeoutCallback;
+ typedef list<MidiTimeoutCallback> MidiTimeoutList;
+
+ MidiTimeoutList midi_timeouts;
+ bool mmc_step_timeout ();
+
+ MIDI::byte mmc_buffer[32];
+ MIDI::byte mtc_msg[16];
+ MIDI::byte mtc_smpte_bits; /* encoding of SMTPE type for MTC */
+ MIDI::byte midi_msg[16];
+ nframes_t outbound_mtc_smpte_frame;
+ SMPTE::Time transmitting_smpte_time;
+ int next_quarter_frame_to_send;
+
+ double _frames_per_smpte_frame; /* has to be floating point because of drop frame */
+ nframes_t _frames_per_hour;
+ nframes_t _smpte_frames_per_hour;
+ nframes_t _smpte_offset;
+ bool _smpte_offset_negative;
+
+ /* cache the most-recently requested time conversions. This helps when we
+ * have multiple clocks showing the same time (e.g. the transport frame) */
+ bool last_smpte_valid;
+ nframes_t last_smpte_when;
+ SMPTE::Time last_smpte;
+
+ bool _send_smpte_update; ///< Flag to send a full frame (SMPTE) MTC message this cycle
+
+ int send_full_time_code(nframes_t nframes);
+ int send_midi_time_code_for_cycle(nframes_t nframes);
+
+ nframes_t adjust_apparent_position (nframes_t frames);
+
+ void reset_record_status ();
+
+ int no_roll (nframes_t nframes, nframes_t offset);
+
+ bool non_realtime_work_pending() const { return static_cast<bool>(post_transport_work); }
+ bool process_can_proceed() const { return !(post_transport_work & ProcessCannotProceedMask); }
+
+ struct MIDIRequest {
+
+ enum Type {
+ PortChange,
+ Quit
+ };
+
+ Type type;
+
+ MIDIRequest () {}
+ };
+
+ Glib::Mutex midi_lock;
+ pthread_t midi_thread;
+ int midi_request_pipe[2];
+ RingBuffer<MIDIRequest*> midi_requests;
+
+ int start_midi_thread ();
+ void terminate_midi_thread ();
+ void poke_midi_thread ();
+ static void *_midi_thread_work (void *arg);
+ void midi_thread_work ();
+ void change_midi_ports ();
+ int use_config_midi_ports ();
+
+ mutable gint butler_active;
+ bool waiting_to_start;
+
+ void set_play_loop (bool yn);
+ void overwrite_some_buffers (Diskstream*);
+ void flush_all_inserts ();
+ void locate (nframes_t, bool with_roll, bool with_flush, bool with_loop=false);
+ void start_locate (nframes_t, bool with_roll, bool with_flush, bool with_loop=false);
+ void force_locate (nframes_t frame, bool with_roll = false);
+ void set_diskstream_speed (Diskstream*, float speed);
+ void set_transport_speed (float speed, bool abort = false);
+ void stop_transport (bool abort = false);
+ void start_transport ();
+ void actually_start_transport ();
+ void realtime_stop (bool abort);
+ void non_realtime_start_scrub ();
+ void non_realtime_set_speed ();
+ void non_realtime_locate ();
+ void non_realtime_stop (bool abort, int entry_request_count, bool& finished);
+ void non_realtime_overwrite (int entry_request_count, bool& finished);
+ void butler_transport_work ();
+ void post_transport ();
+ void engine_halted ();
+ void xrun_recovery ();
+
+ TempoMap *_tempo_map;
+ void tempo_map_changed (Change);
+
+ /* edit/mix groups */
+
+ int load_route_groups (const XMLNode&, bool is_edit);
+ int load_edit_groups (const XMLNode&);
+ int load_mix_groups (const XMLNode&);
+
+
+ list<RouteGroup *> edit_groups;
+ list<RouteGroup *> mix_groups;
+
+ /* disk-streams */
+
+ SerializedRCUManager<DiskstreamList> diskstreams;
+
+ uint32_t audio_dstream_buffer_size;
+ uint32_t midi_dstream_buffer_size;
+ int load_diskstreams (const XMLNode&);
+
+ /* routes stuff */
+
+ SerializedRCUManager<RouteList> routes;
+
+ void add_routes (RouteList&, bool save);
+ uint32_t destructive_index;
+
+ int load_routes (const XMLNode&);
+ boost::shared_ptr<Route> XMLRouteFactory (const XMLNode&);
+
+ /* mixer stuff */
+
+ bool solo_update_disabled;
+ bool currently_soloing;
+
+ void route_mute_changed (void *src);
+ void route_solo_changed (void *src, boost::weak_ptr<Route>);
+ void catch_up_on_solo ();
+ void update_route_solo_state ();
+ void modify_solo_mute (bool, bool);
+ void strip_portname_for_solo (string& portname);
+
+ /* REGION MANAGEMENT */
+
+ mutable Glib::Mutex region_lock;
+ typedef map<PBD::ID,boost::shared_ptr<Region> > RegionList;
+ RegionList regions;
+
+ void add_region (boost::shared_ptr<Region>);
+ void region_changed (Change, boost::weak_ptr<Region>);
+ void remove_region (boost::weak_ptr<Region>);
+
+ int load_regions (const XMLNode& node);
+
+ /* SOURCES */
+
+ mutable Glib::Mutex source_lock;
+ typedef std::map<PBD::ID,boost::shared_ptr<Source> > SourceMap;
+
+ SourceMap sources;
+
+ public:
+ SourceMap get_sources() { return sources; }
+
+ private:
+
+
+ int load_sources (const XMLNode& node);
+ XMLNode& get_sources_as_xml ();
+
+ boost::shared_ptr<Source> XMLSourceFactory (const XMLNode&);
+
+ /* PLAYLISTS */
+
+ mutable Glib::Mutex playlist_lock;
+ typedef set<boost::shared_ptr<Playlist> > PlaylistList;
+ PlaylistList playlists;
+ PlaylistList unused_playlists;
+
+ int load_playlists (const XMLNode&);
+ int load_unused_playlists (const XMLNode&);
+ void remove_playlist (boost::weak_ptr<Playlist>);
+ void track_playlist (bool, boost::weak_ptr<Playlist>);
+
+ boost::shared_ptr<Playlist> playlist_factory (string name);
+ boost::shared_ptr<Playlist> XMLPlaylistFactory (const XMLNode&);
+
+ void playlist_length_changed ();
+ void diskstream_playlist_changed (boost::shared_ptr<Diskstream>);
+
+ /* NAMED SELECTIONS */
+
+ mutable Glib::Mutex named_selection_lock;
+ typedef set<NamedSelection *> NamedSelectionList;
+ NamedSelectionList named_selections;
+
+ int load_named_selections (const XMLNode&);
+
+ NamedSelection *named_selection_factory (string name);
+ NamedSelection *XMLNamedSelectionFactory (const XMLNode&);
+
+ /* CURVES and AUTOMATION LISTS */
+ std::map<PBD::ID, Curve*> curves;
+ std::map<PBD::ID, AutomationList*> automation_lists;
+
+ /* DEFAULT FADE CURVES */
+
+ float default_fade_steepness;
+ float default_fade_msecs;
+
+ /* AUDITIONING */
+
+ boost::shared_ptr<Auditioner> auditioner;
+ void set_audition (boost::shared_ptr<Region>);
+ void non_realtime_set_audition ();
+ boost::shared_ptr<Region> pending_audition_region;
+
+ /* EXPORT */
+
+ /* FLATTEN */
+
+ int flatten_one_track (AudioTrack&, nframes_t start, nframes_t cnt);
+
+ /* INSERT AND SEND MANAGEMENT */
+
+ list<PortInsert *> _port_inserts;
+ list<PluginInsert *> _plugin_inserts;
+ list<Send *> _sends;
+ boost::dynamic_bitset<uint32_t> send_bitset;
+ boost::dynamic_bitset<uint32_t> insert_bitset;
+ uint32_t send_cnt;
+ uint32_t insert_cnt;
+
+
+ void add_processor (Processor *);
+ void remove_processor (Processor *);
+
+ /* S/W RAID */
+
+ struct space_and_path {
+ uint32_t blocks; /* 4kB blocks */
+ string path;
+
+ space_and_path() {
+ blocks = 0;
+ }
+ };
+
+ struct space_and_path_ascending_cmp {
+ bool operator() (space_and_path a, space_and_path b) {
+ return a.blocks > b.blocks;
+ }
+ };
+
+ void setup_raid_path (string path);
+
+ vector<space_and_path> session_dirs;
+ vector<space_and_path>::iterator last_rr_session_dir;
+ uint32_t _total_free_4k_blocks;
+ Glib::Mutex space_lock;
+
+ string get_best_session_directory_for_new_source ();
+ void refresh_disk_space ();
+
+ mutable gint _playback_load;
+ mutable gint _capture_load;
+ mutable gint _playback_load_min;
+ mutable gint _capture_load_min;
+
+ /* I/O bundles */
+
+ typedef list<boost::shared_ptr<Bundle> > BundleList;
+ mutable Glib::Mutex bundle_lock;
+ BundleList _bundles;
+ XMLNode* _bundle_xml_node;
+ int load_bundles (XMLNode const &);
+
+ void reverse_diskstream_buffers ();
+
+ UndoHistory _history;
+ UndoTransaction* current_trans;
+
+ GlobalRouteBooleanState get_global_route_boolean (bool (Route::*method)(void) const);
+ GlobalRouteMeterState get_global_route_metering ();
+
+ void set_global_route_boolean (GlobalRouteBooleanState s, void (Route::*method)(bool, void*), void *arg);
+ void set_global_route_metering (GlobalRouteMeterState s, void *arg);
+
+ void set_global_mute (GlobalRouteBooleanState s, void *src);
+ void set_global_solo (GlobalRouteBooleanState s, void *src);
+ void set_global_record_enable (GlobalRouteBooleanState s, void *src);
+
+ void jack_timebase_callback (jack_transport_state_t, nframes_t, jack_position_t*, int);
+ int jack_sync_callback (jack_transport_state_t, jack_position_t*);
+ void reset_jack_connection (jack_client_t* jack);
+ void record_enable_change_all (bool yn);
+
+ XMLNode& state(bool);
+
+ /* click track */
+
+ struct Click {
+ nframes_t start;
+ nframes_t duration;
+ nframes_t offset;
+ const Sample *data;
+
+ Click (nframes_t s, nframes_t d, const Sample *b)
+ : start (s), duration (d), data (b) { offset = 0; }
+
+ void *operator new(size_t ignored) {
+ return pool.alloc ();
+ };
+
+ void operator delete(void *ptr, size_t size) {
+ pool.release (ptr);
+ }
+
+ private:
+ static Pool pool;
+ };
+
+ typedef list<Click*> Clicks;
+
+ Clicks clicks;
+ bool _clicking;
+ boost::shared_ptr<IO> _click_io;
+ Sample* click_data;
+ Sample* click_emphasis_data;
+ nframes_t click_length;
+ nframes_t click_emphasis_length;
+ mutable Glib::RWLock click_lock;
+
+ static const Sample default_click[];
+ static const nframes_t default_click_length;
+ static const Sample default_click_emphasis[];
+ static const nframes_t default_click_emphasis_length;
+
+ Click *get_click();
+ void setup_click_sounds (int which);
+ void clear_clicks ();
+ void click (nframes_t start, nframes_t nframes, nframes_t offset);
+
+ vector<Route*> master_outs;
+
+ /* range playback */
+
+ list<AudioRange> current_audio_range;
+ bool _play_range;
+ void set_play_range (bool yn);
+ void setup_auto_play ();
+
+ /* main outs */
+ uint32_t main_outs;
+
+ boost::shared_ptr<IO> _master_out;
+ boost::shared_ptr<IO> _control_out;
+
+ gain_t* _gain_automation_buffer;
+ pan_t** _pan_automation_buffer;
+ void allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, bool force);
+ uint32_t _npan_buffers;
+
+ /* VST support */
+
+ long _vst_callback (VSTPlugin*,
+ long opcode,
+ long index,
+ long value,
+ void* ptr,
+ float opt);
+
+ /* number of hardware ports we're using,
+ based on max (requested,available)
+ */
+
+ uint32_t n_physical_outputs;
+ uint32_t n_physical_inputs;
+
+
+ int find_all_sources (std::string path, std::set<std::string>& result);
+ int find_all_sources_across_snapshots (std::set<std::string>& result, bool exclude_this_snapshot);
+
+ LayerModel layer_model;
+ CrossfadeModel xfade_model;
+
+ typedef std::set<boost::shared_ptr<PBD::Controllable> > Controllables;
+ Glib::Mutex controllables_lock;
+ Controllables controllables;
+
+ void reset_native_file_format();
+ bool first_file_data_format_reset;
+ bool first_file_header_format_reset;
+
+ void config_changed (const char*);
+
+ XMLNode& get_control_protocol_state ();
+
+ void set_history_depth (uint32_t depth);
+ void sync_order_keys ();
+
+ static bool _disable_all_loaded_plugins;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_session_h__ */
diff --git a/libs/ardour/ardour/session_directory.h b/libs/ardour/ardour/session_directory.h
new file mode 100644
index 0000000000..a4fb7d07c5
--- /dev/null
+++ b/libs/ardour/ardour/session_directory.h
@@ -0,0 +1,136 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ 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_session_directory_h__
+#define __ardour_session_directory_h__
+
+#include <string>
+#include <vector>
+
+#include <pbd/filesystem.h>
+
+namespace ARDOUR {
+
+using std::string;
+using std::vector;
+using PBD::sys::path;
+
+class SessionDirectory
+{
+public:
+
+ /**
+ * @param session_path An absolute path to a session directory.
+ */
+ SessionDirectory (const path& session_path);
+
+ /**
+ * @return the absolute path to the root directory of the session
+ */
+ const path root_path() const { return m_root_path; }
+
+ /**
+ * @return the absolute path to the directory in which
+ * the session stores audio files.
+ *
+ * If the session is an older session with an existing
+ * "sounds" directory then it will return a path to that
+ * directory otherwise it will return the new location
+ * of root_path()/interchange/session_name/audiofiles
+ */
+ const path sound_path () const;
+
+ /**
+ * @return the absolute path to the directory in which
+ * the session stores MIDI files, ie
+ * root_path()/interchange/session_name/midifiles
+ */
+ const path midi_path () const;
+
+ /**
+ * @return The absolute path to the directory in which all
+ * peak files are stored for a session.
+ */
+ const path peak_path () const;
+
+ /**
+ * @return The absolute path to the directory that audio
+ * files are moved to when they are no longer part of the
+ * session.
+ */
+ const path dead_sound_path () const;
+
+ /**
+ * @return The absolute path to the directory that midi
+ * files are moved to when they are no longer part of the
+ * session.
+ */
+ const path dead_midi_path () const;
+
+ /**
+ * @return The absolute path to the directory that audio
+ * files are created in by default when exporting.
+ */
+ const path export_path () const;
+
+ /**
+ * @return true if session directory and all the required
+ * subdirectories exist.
+ */
+ bool is_valid () const;
+
+ /**
+ * Create the session directory and all the subdirectories.
+ *
+ * @throw PBD::sys::filesystem_error if the directories were
+ * not able to be created.
+ *
+ * @return true If a new session directory was created, otherwise
+ * (if it already existed) false.
+ *
+ * @post is_valid ()
+ */
+ bool create ();
+
+protected:
+
+ /**
+ * @return The path to the old style sound directory.
+ * It isn't created by create().
+ */
+ const path old_sound_path () const;
+
+ /**
+ * @return The path to the directory under which source directories
+ * are created for different source types.
+ * i.e root_path()/interchange/session_name
+ */
+ const path sources_root() const;
+
+ /**
+ * @return a vector containing the fullpath of all subdirectories.
+ */
+ const vector<PBD::sys::path> sub_directories () const;
+
+ /// The path to the root of the session directory.
+ const path m_root_path;
+};
+
+} // namespace ARDOUR
+
+#endif
diff --git a/libs/ardour/ardour/session_object.h b/libs/ardour/ardour/session_object.h
new file mode 100644
index 0000000000..bb726cb0d0
--- /dev/null
+++ b/libs/ardour/ardour/session_object.h
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 2000-2007 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_session_object_h__
+#define __ardour_session_object_h__
+
+#include <string>
+#include <pbd/statefuldestructible.h>
+
+namespace ARDOUR {
+
+class Session;
+
+
+/** An object associated with a Session.
+ *
+ * This is a few common things factored out of IO which weren't IO specific
+ * (to fix the problem with e.g. PluginInsert being an IO which it shouldn't be).
+ * collection of input and output ports with connections.
+ */
+class SessionObject : public PBD::StatefulDestructible
+{
+public:
+ SessionObject(Session& session, const std::string& name)
+ : _session(session)
+ , _name(name)
+ {}
+
+ Session& session() const { return _session; }
+ const std::string& name() const { return _name; }
+
+ virtual bool set_name (const std::string& str) {
+ if (_name != str) {
+ _name = str;
+ NameChanged();
+ }
+ return true;
+ }
+
+ sigc::signal<void> NameChanged;
+
+protected:
+ Session& _session;
+ std::string _name;
+};
+
+} // namespace ARDOUR
+
+#endif /*__ardour_io_h__ */
diff --git a/libs/ardour/ardour/session_playlist.h b/libs/ardour/ardour/session_playlist.h
new file mode 100644
index 0000000000..baeb74916d
--- /dev/null
+++ b/libs/ardour/ardour/session_playlist.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2002 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_session_playlist_h__
+#define __ardour_session_playlist_h__
+
+#include <ardour/session.h>
+#include <ardour/playlist.h>
+
+namespace ARDOUR {
+
+template<class T> void
+Session::foreach_playlist (T *obj, void (T::*func)(boost::shared_ptr<Playlist>))
+{
+ Glib::Mutex::Lock lm (playlist_lock);
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); i++) {
+ if (!(*i)->hidden()) {
+ (obj->*func) (*i);
+ }
+ }
+ for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); i++) {
+ if (!(*i)->hidden()) {
+ (obj->*func) (*i);
+ }
+ }
+}
+
+} /* namespace */
+
+#endif /* __ardour_session_playlist_h__ */
diff --git a/libs/ardour/ardour/session_region.h b/libs/ardour/ardour/session_region.h
new file mode 100644
index 0000000000..254bbfe1a3
--- /dev/null
+++ b/libs/ardour/ardour/session_region.h
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2000-2007 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_session_region_h__
+#define __ardour_session_region_h__
+
+#include <ardour/session.h>
+#include <ardour/audioregion.h>
+
+namespace ARDOUR {
+
+template<class T> void Session::foreach_region (T *obj, void (T::*func)(boost::shared_ptr<Region>))
+{
+ Glib::Mutex::Lock lm (region_lock);
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); i++) {
+ (obj->*func) (i->second);
+ }
+}
+
+} // namespace ARDOUR
+
+#endif /* __ardour_session_region_h__ */
diff --git a/libs/ardour/ardour/session_route.h b/libs/ardour/ardour/session_route.h
new file mode 100644
index 0000000000..0c70bf407d
--- /dev/null
+++ b/libs/ardour/ardour/session_route.h
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2000 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_session_route_h__
+#define __ardour_session_route_h__
+
+#include <iostream>
+
+#include <glibmm/thread.h>
+
+#include <ardour/session.h>
+#include <ardour/route.h>
+
+namespace ARDOUR {
+
+template<class T> void
+Session::foreach_route (T *obj, void (T::*func)(Route&))
+{
+ boost::shared_ptr<RouteList> r = routes.reader();
+ RouteList public_order (*r);
+ RoutePublicOrderSorter cmp;
+
+ public_order.sort (cmp);
+
+ for (RouteList::iterator i = public_order.begin(); i != public_order.end(); i++) {
+ (obj->*func) (**i);
+ }
+}
+
+template<class T> void
+Session::foreach_route (T *obj, void (T::*func)(boost::shared_ptr<Route>))
+{
+ boost::shared_ptr<RouteList> r = routes.reader();
+ RouteList public_order (*r);
+ RoutePublicOrderSorter cmp;
+
+ public_order.sort (cmp);
+
+ for (RouteList::iterator i = public_order.begin(); i != public_order.end(); i++) {
+ (obj->*func) (*i);
+ }
+}
+
+template<class T, class A> void
+Session::foreach_route (T *obj, void (T::*func)(Route&, A), A arg1)
+{
+ boost::shared_ptr<RouteList> r = routes.reader();
+ RouteList public_order (*r);
+ RoutePublicOrderSorter cmp;
+
+ public_order.sort (cmp);
+
+ for (RouteList::iterator i = public_order.begin(); i != public_order.end(); i++) {
+ (obj->*func) (**i, arg1);
+ }
+}
+
+} /* namespace */
+
+#endif /* __ardour_session_route_h__ */
diff --git a/libs/ardour/ardour/session_selection.h b/libs/ardour/ardour/session_selection.h
new file mode 100644
index 0000000000..4169a3a511
--- /dev/null
+++ b/libs/ardour/ardour/session_selection.h
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 2002 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_session_named_selection_h__
+#define __ardour_session_named_selection_h__
+
+#include <ardour/session.h>
+#include <ardour/named_selection.h>
+
+namespace ARDOUR {
+
+template<class T> void
+Session::foreach_named_selection (T& obj, void (T::*func)(NamedSelection&))
+{
+ Glib::Mutex::Lock lm (named_selection_lock);
+ for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); i++) {
+ (obj.*func) (**i);
+ }
+}
+
+} /* namespace */
+
+#endif /* __ardour_session_named_selection_h__ */
diff --git a/libs/ardour/ardour/session_state_utils.h b/libs/ardour/ardour/session_state_utils.h
new file mode 100644
index 0000000000..8825b041f3
--- /dev/null
+++ b/libs/ardour/ardour/session_state_utils.h
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ 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_SESSION_STATE_UTILS_INCLUDED
+#define ARDOUR_SESSION_STATE_UTILS_INCLUDED
+
+#include <vector>
+#include <string>
+
+#include <pbd/filesystem.h>
+
+namespace ARDOUR {
+
+using std::string;
+using std::vector;
+using namespace PBD;
+
+/**
+ * Attempt to create a backup copy of a file.
+ *
+ * A copy of the file is created in the same directory using
+ * the same filename with the backup suffix appended.
+ *
+ * @return true if successful, false otherwise.
+ */
+bool create_backup_file (const sys::path & file_path);
+
+/**
+ * Get the absolute paths to all state files in the directory
+ * at path directory_path.
+ *
+ * @param directory_path The absolute path to a directory.
+ * @param result vector to contain resulting state files.
+ */
+void get_state_files_in_directory (const sys::path & directory_path,
+ vector<sys::path>& result);
+
+/**
+ * Given a vector of paths to files, return a vector containing
+ * the filenames without any extension.
+ *
+ * @param file_paths a vector containing the file paths
+ * @return a vector containing a list of file names without any
+ * filename extension.
+ */
+vector<string> get_file_names_no_extension (const vector<sys::path> & file_paths);
+
+} // namespace ARDOUR
+
+#endif
diff --git a/libs/ardour/ardour/session_utils.h b/libs/ardour/ardour/session_utils.h
new file mode 100644
index 0000000000..8a9f6f584c
--- /dev/null
+++ b/libs/ardour/ardour/session_utils.h
@@ -0,0 +1,26 @@
+
+#ifndef __ardour_session_utils_h__
+#define __ardour_session_utils_h__
+
+#include <string>
+
+namespace ARDOUR {
+
+using std::string;
+
+int find_session (string str, string& path, string& snapshot, bool& isnew);
+
+/**
+ * Create a SessionDirectory at the path specified by
+ * session_directory_path, this includes all subdirectories.
+ *
+ * @return true if the session directory was able to be created
+ * or if it already existed, false otherwise.
+ *
+ * @see SessionDirectory
+ */
+bool create_session_directory (const string& session_directory_path);
+
+};
+
+#endif
diff --git a/libs/ardour/ardour/silentfilesource.h b/libs/ardour/ardour/silentfilesource.h
new file mode 100644
index 0000000000..cbb123139a
--- /dev/null
+++ b/libs/ardour/ardour/silentfilesource.h
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2007 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_silentfilesource_h__
+#define __ardour_silentfilesource_h__
+
+#include <cstring>
+#include <ardour/audiofilesource.h>
+
+namespace ARDOUR {
+
+class SilentFileSource : public AudioFileSource {
+ public:
+ virtual ~SilentFileSource ();
+
+ int update_header (nframes_t when, struct tm&, time_t) { return 0; }
+ int flush_header () { return 0; }
+ float sample_rate () const { return _sample_rate; }
+
+ void set_length (nframes_t len);
+
+ bool destructive() const { return false; }
+ bool can_be_analysed() const { return false; }
+
+ protected:
+
+ float _sample_rate;
+
+ SilentFileSource (Session&, const XMLNode&, nframes_t nframes, float sample_rate);
+
+ friend class SourceFactory;
+
+ nframes_t read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const {
+ memset (dst, 0, sizeof (Sample) * cnt);
+ return cnt;
+ }
+
+ nframes_t write_unlocked (Sample *dst, nframes_t cnt) { return 0; }
+
+ void set_header_timeline_position () {}
+
+ int read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_unit, nframes_t fpp) const {
+ memset (peaks, 0, sizeof (PeakData) * npeaks);
+ return 0;
+ }
+
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_audiofilesource_h__ */
+
diff --git a/libs/ardour/ardour/slave.h b/libs/ardour/ardour/slave.h
new file mode 100644
index 0000000000..509f8fa9d2
--- /dev/null
+++ b/libs/ardour/ardour/slave.h
@@ -0,0 +1,153 @@
+/*
+ Copyright (C) 2002 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_slave_h__
+#define __ardour_slave_h__
+
+#include <vector>
+
+#include <jack/jack.h>
+
+#include <sigc++/signal.h>
+#include <ardour/ardour.h>
+#include <midi++/parser.h>
+#include <midi++/types.h>
+
+namespace MIDI {
+ class Port;
+}
+
+namespace ARDOUR {
+class Session;
+
+class Slave {
+ public:
+ Slave() { }
+ virtual ~Slave() {}
+
+ virtual bool speed_and_position (float&, nframes_t&) = 0;
+ virtual bool locked() const = 0;
+ virtual bool ok() const = 0;
+ virtual bool starting() const { return false; }
+ virtual nframes_t resolution() const = 0;
+ virtual bool requires_seekahead () const = 0;
+ virtual bool is_always_synced() const { return false; }
+};
+
+
+class MTC_Slave : public Slave, public sigc::trackable {
+ public:
+ MTC_Slave (Session&, MIDI::Port&);
+ ~MTC_Slave ();
+
+ void rebind (MIDI::Port&);
+ bool speed_and_position (float&, nframes_t&);
+
+ bool locked() const;
+ bool ok() const;
+ void handle_locate (const MIDI::byte*);
+
+ nframes_t resolution() const;
+ bool requires_seekahead () const { return true; }
+
+ private:
+ Session& session;
+ MIDI::Port* port;
+ std::vector<sigc::connection> connections;
+ bool can_notify_on_unknown_rate;
+
+ struct SafeTime {
+
+
+ int guard1;
+ //SMPTE_Time mtc;
+ nframes_t position;
+ nframes_t timestamp;
+ int guard2;
+
+ SafeTime() {
+ guard1 = 0;
+ guard2 = 0;
+ timestamp = 0;
+ }
+ };
+
+ SafeTime current;
+ nframes_t mtc_frame; /* current time */
+ nframes_t last_inbound_frame; /* when we got it; audio clocked */
+
+ float mtc_speed;
+ nframes_t first_mtc_frame;
+ nframes_t first_mtc_time;
+
+ static const int32_t accumulator_size = 128;
+ float accumulator[accumulator_size];
+ int32_t accumulator_index;
+ bool have_first_accumulated_speed;
+
+ void reset ();
+ void update_mtc_qtr (MIDI::Parser&);
+ void update_mtc_time (const MIDI::byte *, bool);
+ void update_mtc_status (MIDI::Parser::MTC_Status);
+ void read_current (SafeTime *) const;
+};
+
+class ADAT_Slave : public Slave
+{
+ public:
+ ADAT_Slave () {}
+ ~ADAT_Slave () {}
+
+ bool speed_and_position (float& speed, nframes_t& pos) {
+ speed = 0;
+ pos = 0;
+ return false;
+ }
+
+ bool locked() const { return false; }
+ bool ok() const { return false; }
+ nframes_t resolution() const { return 1; }
+ bool requires_seekahead () const { return true; }
+};
+
+class JACK_Slave : public Slave
+{
+ public:
+ JACK_Slave (jack_client_t*);
+ ~JACK_Slave ();
+
+ bool speed_and_position (float& speed, nframes_t& pos);
+
+ bool starting() const { return _starting; }
+ bool locked() const;
+ bool ok() const;
+ nframes_t resolution() const { return 1; }
+ bool requires_seekahead () const { return false; }
+ void reset_client (jack_client_t* jack);
+ bool is_always_synced() const { return true; }
+
+ private:
+ jack_client_t* jack;
+ float speed;
+ bool _starting;
+};
+
+} /* namespace */
+
+#endif /* __ardour_slave_h__ */
diff --git a/libs/ardour/ardour/smf_reader.h b/libs/ardour/ardour/smf_reader.h
new file mode 100644
index 0000000000..e41dcc3bc4
--- /dev/null
+++ b/libs/ardour/ardour/smf_reader.h
@@ -0,0 +1,87 @@
+/*
+ Copyright (C) 2008 Paul Davis
+ Written by Dave Robillard
+
+ 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_smf_reader_h__
+#define __ardour_smf_reader_h__
+
+#include <exception>
+#include <stdexcept>
+#include <string>
+#include <inttypes.h>
+
+namespace ARDOUR {
+
+
+/** Standard MIDI File (Type 0) Reader
+ *
+ * Currently this only reads SMF files with tempo-based timing.
+ */
+class SMFReader {
+public:
+ class PrematureEOF : public std::exception {
+ const char* what() const throw() { return "Unexpected end of file"; }
+ };
+ class CorruptFile : public std::exception {
+ const char* what() const throw() { return "Corrupted file"; }
+ };
+ class UnsupportedTime : public std::exception {
+ const char* what() const throw() { return "Unsupported time stamp type (SMPTE)"; }
+ };
+
+ SMFReader(const std::string filename="");
+ ~SMFReader();
+
+ bool open(const std::string& filename) throw (std::logic_error, UnsupportedTime);
+
+ bool seek_to_track(unsigned track) throw (std::logic_error);
+
+ const std::string& filename() const { return _filename; };
+
+ uint16_t type() const { return _type; }
+ uint16_t ppqn() const { return _ppqn; }
+ uint16_t num_tracks() const { return _num_tracks; }
+
+ int read_event(size_t buf_len,
+ uint8_t* buf,
+ uint32_t* ev_size,
+ uint32_t* ev_delta_time)
+ throw (std::logic_error, PrematureEOF, CorruptFile);
+
+ void close();
+
+ static uint32_t read_var_len(FILE* fd) throw (PrematureEOF);
+
+protected:
+ /** size of SMF header, including MTrk chunk header */
+ static const uint32_t HEADER_SIZE = 22;
+
+ std::string _filename;
+ FILE* _fd;
+ uint16_t _type;
+ uint16_t _ppqn;
+ uint16_t _num_tracks;
+ uint32_t _track;
+ uint32_t _track_size;
+};
+
+
+} // namespace ARDOUR
+
+#endif /* __ardour_smf_reader_h__ */
+
diff --git a/libs/ardour/ardour/smf_source.h b/libs/ardour/ardour/smf_source.h
new file mode 100644
index 0000000000..88bf1e5d13
--- /dev/null
+++ b/libs/ardour/ardour/smf_source.h
@@ -0,0 +1,151 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Dave Robillard, 2006
+
+ 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_smf_filesource_h__
+#define __ardour_smf_filesource_h__
+
+#include <cstdio>
+#include <time.h>
+
+#include <ardour/midi_source.h>
+
+namespace ARDOUR {
+
+class MidiRingBuffer;
+
+/** Standard Midi File (Type 0) Source */
+class SMFSource : public MidiSource {
+ public:
+ enum Flag {
+ Writable = 0x1,
+ CanRename = 0x2,
+ Broadcast = 0x4,
+ Removable = 0x8,
+ RemovableIfEmpty = 0x10,
+ RemoveAtDestroy = 0x20,
+ BuildPeaks = 0x40
+ };
+
+ /** Constructor for existing external-to-session files */
+ SMFSource (Session& session, std::string path, Flag flags = Flag(0));
+
+ /* Constructor for existing in-session files */
+ SMFSource (Session& session, const XMLNode&);
+
+ virtual ~SMFSource ();
+
+ /* this block of methods do nothing for regular file sources, but are significant
+ for files used in destructive recording.
+ */
+ // FIXME and thus are useless for MIDI.. but make MidiDiskstream compile easier! :)
+
+ virtual nframes_t last_capture_start_frame() const { return 0; }
+ virtual void mark_capture_start (nframes_t) {}
+ virtual void mark_capture_end () {}
+ virtual void clear_capture_marks() {}
+
+ bool set_name (const std::string& newname) { return (set_source_name(newname, false) == 0); }
+ int set_source_name (string newname, bool destructive);
+
+ static bool safe_file_extension (const Glib::ustring& path);
+
+ Glib::ustring path() const { return _path; }
+
+ void set_allow_remove_if_empty (bool yn);
+ void mark_for_remove();
+
+ void append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev);
+
+ int flush_header ();
+ int flush_footer ();
+
+ int move_to_trash (const string trash_dir_name);
+
+ bool is_empty () const;
+ void mark_streaming_midi_write_started (NoteMode mode, nframes_t start_time);
+ void mark_streaming_write_completed ();
+
+ void mark_take (string);
+ string take_id() const { return _take_id; }
+
+ static void set_search_path (string);
+ static void set_header_position_offset (nframes_t offset, bool negative);
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
+ void seek_to(nframes_t time);
+
+ void load_model(bool lock=true, bool force_reload=false);
+ void destroy_model();
+
+ uint16_t ppqn() const { return _ppqn; }
+
+ private:
+
+ int init (string idstr, bool must_exist);
+
+ nframes_t read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cn, nframes_t stamp_offset, nframes_t negative_stamp_offset) const;
+ nframes_t write_unlocked (MidiRingBuffer& dst, nframes_t cnt);
+
+ bool find (std::string path, bool must_exist, bool& is_new);
+ bool removable() const;
+ bool writable() const { return _flags & Writable; }
+
+ int open();
+ void close();
+
+ /**
+ * This method is only used by flush_footer() to find the right seek position
+ * for the footer (at the end after recording or -4 offset ro SEEK_END
+ * if a footer is already present)
+ */
+ void seek_to_footer_position();
+
+ /**
+ * write the track footer at the current seek position
+ */
+ void write_footer();
+
+ void write_chunk_header(const char id[4], uint32_t length);
+ void write_chunk(const char id[4], uint32_t length, void* data);
+ size_t write_var_len(uint32_t val);
+ uint32_t read_var_len() const;
+ int read_event(uint32_t* delta_t, uint32_t* size, uint8_t** buf) const;
+
+ static const uint16_t _ppqn = 19200;
+
+ Glib::ustring _path;
+ Flag _flags;
+ string _take_id;
+ bool _allow_remove_if_empty;
+ FILE* _fd;
+ double _last_ev_time; ///< last frame time written, relative to source start
+ uint32_t _track_size;
+ uint32_t _header_size; ///< size of SMF header, including MTrk chunk header
+ bool _empty; ///< true iff file contains (non-empty) events
+
+ static string _search_path;
+};
+
+}; /* namespace ARDOUR */
+
+#endif /* __ardour_smf_filesource_h__ */
+
diff --git a/libs/ardour/ardour/smpte.h b/libs/ardour/ardour/smpte.h
new file mode 100644
index 0000000000..d0901c372e
--- /dev/null
+++ b/libs/ardour/ardour/smpte.h
@@ -0,0 +1,79 @@
+/* Copyright (C) 2006 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.,
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#ifndef __ardour_smpte_h__
+#define __ardour_smpte_h__
+
+#include <inttypes.h>
+
+namespace SMPTE {
+
+enum Wrap {
+ NONE = 0,
+ FRAMES,
+ SECONDS,
+ MINUTES,
+ HOURS
+};
+
+/** SMPTE frame rate (in frames per second).
+ *
+ * This should be eliminated in favour of a float to support arbitrary rates.
+ */
+enum FPS {
+ MTC_24_FPS = 0,
+ MTC_25_FPS = 1,
+ MTC_30_FPS_DROP = 2,
+ MTC_30_FPS = 3
+};
+
+struct Time {
+ bool negative;
+ uint32_t hours;
+ uint32_t minutes;
+ uint32_t seconds;
+ uint32_t frames; ///< SMPTE frames (not audio samples)
+ uint32_t subframes; ///< Typically unused
+ FPS rate; ///< Frame rate of this Time
+ static FPS default_rate; ///< Rate to use for default constructor
+
+ Time(FPS a_rate = default_rate) {
+ negative = false;
+ hours = 0;
+ minutes = 0;
+ seconds = 0;
+ frames = 0;
+ subframes = 0;
+ rate = a_rate;
+ }
+};
+
+Wrap increment( Time& smpte );
+Wrap decrement( Time& smpte );
+Wrap increment_subframes( Time& smpte );
+Wrap decrement_subframes( Time& smpte );
+Wrap increment_seconds( Time& smpte );
+Wrap increment_minutes( Time& smpte );
+Wrap increment_hours( Time& smpte );
+void frames_floor( Time& smpte );
+void seconds_floor( Time& smpte );
+void minutes_floor( Time& smpte );
+void hours_floor( Time& smpte );
+
+} // namespace SMPTE
+
+#endif // __ardour_smpte_h__
diff --git a/libs/ardour/ardour/sndfile_helpers.h b/libs/ardour/ardour/sndfile_helpers.h
new file mode 100644
index 0000000000..cf6b15f3a4
--- /dev/null
+++ b/libs/ardour/ardour/sndfile_helpers.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 2000-2007 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 __sndfile_helpers_h__
+#define __sndfile_helpers_h__
+
+#include <string>
+#include <stdint.h>
+
+using std::string;
+
+// Use this define when initializing arrarys for use in sndfile_*_format()
+#define SNDFILE_STR_LENGTH 32
+
+#define SNDFILE_HEADER_FORMATS 5
+extern const char * const sndfile_header_formats_strings[SNDFILE_HEADER_FORMATS+1];
+extern const char * const sndfile_file_endings_strings[SNDFILE_HEADER_FORMATS+1];
+
+extern int sndfile_header_formats[SNDFILE_HEADER_FORMATS];
+
+#define SNDFILE_BITDEPTH_FORMATS 5
+extern const char * const sndfile_bitdepth_formats_strings[SNDFILE_BITDEPTH_FORMATS+1];
+
+extern int sndfile_bitdepth_formats[SNDFILE_BITDEPTH_FORMATS];
+
+#define SNDFILE_ENDIAN_FORMATS 2
+extern const char * const sndfile_endian_formats_strings[SNDFILE_ENDIAN_FORMATS+1];
+
+extern int sndfile_endian_formats[SNDFILE_ENDIAN_FORMATS];
+
+int sndfile_bitdepth_format_from_string(string);
+int sndfile_header_format_from_string(string);
+int sndfile_endian_format_from_string(string);
+string sndfile_file_ending_from_string(string);
+
+int sndfile_data_width (int format);
+
+// It'd be nice if libsndfile did this for us
+string sndfile_major_format(int);
+string sndfile_minor_format(int);
+
+#endif /* __sndfile_helpers_h__ */
diff --git a/libs/ardour/ardour/sndfileimportable.h b/libs/ardour/ardour/sndfileimportable.h
new file mode 100644
index 0000000000..5cd84f4f5f
--- /dev/null
+++ b/libs/ardour/ardour/sndfileimportable.h
@@ -0,0 +1,50 @@
+/*
+ Copyright (C) 2007 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_sndfile_importable_source_h__
+#define __ardour_sndfile_importable_source_h__
+
+#include <boost/shared_ptr.hpp>
+#include <sndfile.h>
+#include <pbd/failed_constructor.h>
+#include <ardour/types.h>
+#include <ardour/importable_source.h>
+
+namespace ARDOUR {
+
+class SndFileImportableSource : public ImportableSource {
+ public:
+ SndFileImportableSource (const std::string& path);
+ virtual ~SndFileImportableSource();
+
+ nframes_t read (Sample* buffer, nframes_t nframes);
+ uint32_t channels() const;
+ nframes_t length() const;
+ nframes_t samplerate() const;
+ void seek (nframes_t pos);
+
+ protected:
+ SF_INFO sf_info;
+ boost::shared_ptr<SNDFILE> in;
+
+};
+
+}
+
+#endif /* __ardour_sndfile_importable_source_h__ */
diff --git a/libs/ardour/ardour/sndfilesource.h b/libs/ardour/ardour/sndfilesource.h
new file mode 100644
index 0000000000..4ad967c132
--- /dev/null
+++ b/libs/ardour/ardour/sndfilesource.h
@@ -0,0 +1,109 @@
+/*
+ Copyright (C) 2006 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 __sndfile_source_h__
+#define __sndfile_source_h__
+
+#include <sndfile.h>
+
+#include <ardour/audiofilesource.h>
+
+namespace ARDOUR {
+
+class SndFileSource : public AudioFileSource {
+ public:
+ /* constructor to be called for existing external-to-session files */
+
+ SndFileSource (Session&, Glib::ustring path, int chn, Flag flags);
+
+ /* constructor to be called for new in-session files */
+
+ SndFileSource (Session&, Glib::ustring path, SampleFormat samp_format, HeaderFormat hdr_format, nframes_t rate,
+ Flag flags = SndFileSource::default_writable_flags);
+
+ /* constructor to be called for existing in-session files */
+
+ SndFileSource (Session&, const XMLNode&);
+
+ ~SndFileSource ();
+
+ float sample_rate () const;
+ int update_header (nframes_t when, struct tm&, time_t);
+ int flush_header ();
+
+ nframes_t natural_position () const;
+
+ nframes_t last_capture_start_frame() const;
+ void mark_capture_start (nframes_t);
+ void mark_capture_end ();
+ void clear_capture_marks();
+
+ bool set_destructive (bool yn);
+
+ bool one_of_several_channels () const;
+
+ static void setup_standard_crossfades (nframes_t sample_rate);
+ static const AudioFileSource::Flag default_writable_flags;
+
+ static int get_soundfile_info (const Glib::ustring& path, SoundFileInfo& _info, string& error_msg);
+
+ protected:
+ void set_header_timeline_position ();
+
+ nframes_t read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const;
+ nframes_t write_unlocked (Sample *dst, nframes_t cnt);
+
+ nframes_t write_float (Sample* data, nframes_t pos, nframes_t cnt);
+
+ private:
+ SNDFILE *sf;
+ SF_INFO _info;
+ SF_BROADCAST_INFO *_broadcast_info;
+
+ void init ();
+ int open();
+ int setup_broadcast_info (nframes_t when, struct tm&, time_t);
+
+ /* destructive */
+
+ static nframes_t xfade_frames;
+ static gain_t* out_coefficient;
+ static gain_t* in_coefficient;
+
+ bool _capture_start;
+ bool _capture_end;
+ nframes_t capture_start_frame;
+ nframes_t file_pos; // unit is frames
+ nframes_t xfade_out_count;
+ nframes_t xfade_in_count;
+ Sample* xfade_buf;
+
+ nframes_t crossfade (Sample* data, nframes_t cnt, int dir);
+ void set_timeline_position (int64_t);
+ nframes_t destructive_write_unlocked (Sample *dst, nframes_t cnt);
+ nframes_t nondestructive_write_unlocked (Sample *dst, nframes_t cnt);
+ void handle_header_position_change ();
+
+ static int64_t get_timecode_info (SNDFILE* sf, SF_BROADCAST_INFO* binfo, bool& exists);
+};
+
+} // namespace ARDOUR
+
+#endif /* __sndfile_source_h__ */
+
diff --git a/libs/ardour/ardour/soundseq.h b/libs/ardour/ardour/soundseq.h
new file mode 100644
index 0000000000..c7157428ee
--- /dev/null
+++ b/libs/ardour/ardour/soundseq.h
@@ -0,0 +1,53 @@
+/*
+ Copyright (C) 2001 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 __soundseq_h__
+#define __soundseq_h__
+
+#include "edl.h"
+
+namespace ARDOUR {
+
+typedef gint16 peak_datum;
+
+struct peak_data_t {
+ peak_datum min;
+ peak_datum max;
+};
+
+const uint32_t frames_per_peak = 2048;
+
+class Sound : public EDL::Piece {
+ public:
+ int peak (peak_data_t& pk, uint32_t start, uint32_t cnt);
+ int read_peaks (peak_data_t *, uint32_t npeaks, uint32_t start, uint32_t cnt);
+ int build_peak (uint32_t first_frame, uint32_t cnt);
+};
+
+class SoundPlaylist : public EDL::Playlist {
+ public:
+ int read_peaks (peak_data_t *, uint32_t npeaks, uint32_t start, uint32_t cnt);
+};
+
+} /* namespace ARDOUR */
+
+#endif /* __soundseq_h__ */
+
+
+
diff --git a/libs/ardour/ardour/source.h b/libs/ardour/ardour/source.h
new file mode 100644
index 0000000000..a2ee96bf63
--- /dev/null
+++ b/libs/ardour/ardour/source.h
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 2000 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_source_h__
+#define __ardour_source_h__
+
+#include <string>
+#include <set>
+
+#include <sigc++/signal.h>
+
+#include <pbd/statefuldestructible.h>
+
+#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, public ARDOUR::Readable
+{
+ public:
+ Source (Session&, const std::string& name, DataType type);
+ Source (Session&, const XMLNode&);
+
+ virtual ~Source ();
+
+ DataType type() { return _type; }
+
+ time_t timestamp() const { return _timestamp; }
+ void stamp (time_t when) { _timestamp = when; }
+
+ nframes_t length() const { return _length; }
+
+ virtual Glib::ustring path() const = 0;
+
+ virtual nframes_t natural_position() const { return 0; }
+
+ virtual void mark_for_remove() = 0;
+ virtual void mark_streaming_write_started () {}
+ virtual void mark_streaming_write_completed () = 0;
+
+ virtual void session_saved() {}
+
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
+ virtual bool destructive() const { return false; }
+ virtual bool length_mutable() const { return false; }
+
+ void use () { _in_use++; }
+ void disuse () { if (_in_use) { _in_use--; } }
+
+ void add_playlist (boost::shared_ptr<ARDOUR::Playlist>);
+ void remove_playlist (boost::weak_ptr<ARDOUR::Playlist>);
+
+ uint32_t used() const;
+
+ static sigc::signal<void,Source*> SourceCreated;
+ sigc::signal<void,boost::shared_ptr<Source> > Switched;
+
+ bool has_been_analysed() const;
+ virtual bool can_be_analysed() const { return false; }
+ virtual void set_been_analysed (bool yn);
+ virtual bool check_for_analysis_data_on_disk();
+
+ sigc::signal<void> AnalysisChanged;
+
+ AnalysisFeatureList transients;
+ std::string get_transients_path() const;
+ int load_transients (const std::string&);
+
+ void update_length (nframes_t pos, nframes_t cnt);
+
+ protected:
+ DataType _type;
+ time_t _timestamp;
+ nframes_t _length;
+ bool _analysed;
+ mutable Glib::Mutex _analysis_lock;
+ Glib::Mutex _playlist_lock;
+
+ typedef std::map<boost::shared_ptr<ARDOUR::Playlist>, uint32_t > PlaylistMap;
+ PlaylistMap _playlists;
+
+ private:
+ uint32_t _in_use;
+};
+
+}
+
+#endif /* __ardour_source_h__ */
diff --git a/libs/ardour/ardour/source_factory.h b/libs/ardour/ardour/source_factory.h
new file mode 100644
index 0000000000..7e9be451e8
--- /dev/null
+++ b/libs/ardour/ardour/source_factory.h
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 2000-2007 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_source_factory_h__
+#define __ardour_source_factory_h__
+
+#include <string>
+#include <stdint.h>
+#include <sigc++/sigc++.h>
+#include <boost/shared_ptr.hpp>
+
+#include <ardour/source.h>
+#include <ardour/audiofilesource.h>
+
+class XMLNode;
+
+namespace ARDOUR {
+
+class Session;
+
+class SourceFactory {
+ public:
+ static void init ();
+
+ static sigc::signal<void,boost::shared_ptr<Source> > SourceCreated;
+
+ static boost::shared_ptr<Source> create (Session&, const XMLNode& node, bool async = false);
+ static boost::shared_ptr<Source> createSilent (Session&, const XMLNode& node, nframes_t nframes, float sample_rate);
+
+ static boost::shared_ptr<Source> createReadable (DataType type, Session&, std::string path, int chn, AudioFileSource::Flag flags,
+ bool announce = true, bool async = false);
+ static boost::shared_ptr<Source> createWritable (DataType type, Session&, std::string name, bool destructive, nframes_t rate,
+ bool announce = true, bool async = false);
+
+ static Glib::Cond* PeaksToBuild;
+ static Glib::StaticMutex peak_building_lock;
+ static std::list<boost::weak_ptr<AudioSource> > files_with_peaks;
+
+ static int setup_peakfile (boost::shared_ptr<Source>, bool async);
+};
+
+}
+
+#endif /* __ardour_source_factory_h__ */
diff --git a/libs/ardour/ardour/spline.h b/libs/ardour/ardour/spline.h
new file mode 100644
index 0000000000..de1ece6edb
--- /dev/null
+++ b/libs/ardour/ardour/spline.h
@@ -0,0 +1,90 @@
+/* This code is based upon work that bore the legend:
+ *
+ * Copyright (C) 1997 David Mosberger
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __ardour_spline_h__
+#define __ardour_spline_h__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _spline Spline;
+typedef struct _spline_point SplinePoint;
+
+struct _spline_point
+{
+ float x;
+ float y;
+};
+
+Spline *spline_new (void);
+void spline_free (Spline *);
+
+void spline_set (Spline *, uint32_t n, SplinePoint *);
+void spline_add (Spline *, uint32_t n, SplinePoint *);
+void spline_solve (Spline *);
+float spline_eval (Spline *, float val);
+void spline_fill (Spline *, float x0, float x1, float *vec, uint32_t veclen);
+float spline_get_max_x (Spline *);
+float spline_get_min_x (Spline *);
+
+struct _spline
+{
+ float *deriv2;
+ float *x;
+ float *y;
+ float max_x;
+ float min_x;
+ SplinePoint *points;
+ uint32_t npoints;
+ uint32_t space;
+
+#ifdef __cplusplus
+
+ void set (uint32_t n, SplinePoint *points) {
+ spline_set (this, n, points);
+ }
+
+ void add (uint32_t n, SplinePoint *points) {
+ spline_add (this, n, points);
+ }
+
+ void solve () {
+ spline_solve (this);
+ }
+
+ float eval (float val) {
+ return spline_eval (this, val);
+ }
+
+ void fill (float x0, float x1, float *vec, uint32_t veclen) {
+ spline_fill (this, x0, x1, vec, veclen);
+ }
+
+#endif /* __cplusplus */
+
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __ardour_spline_h__ */
diff --git a/libs/ardour/ardour/stretch.h b/libs/ardour/ardour/stretch.h
new file mode 100644
index 0000000000..b01a9ae208
--- /dev/null
+++ b/libs/ardour/ardour/stretch.h
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2007 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_stretch_h__
+#define __ardour_stretch_h__
+
+#include <ardour/filter.h>
+
+#ifdef USE_RUBBERBAND
+
+#include <ardour/rb_effect.h>
+
+namespace ARDOUR {
+
+class Stretch : public RBEffect {
+ public:
+ Stretch (ARDOUR::Session&, TimeFXRequest&);
+ ~Stretch() {}
+};
+
+} /* namespace */
+
+#else
+
+#include <soundtouch/SoundTouch.h>
+
+namespace ARDOUR {
+
+class Stretch : public Filter {
+ public:
+ Stretch (ARDOUR::Session&, TimeFXRequest&);
+ ~Stretch ();
+
+ int run (boost::shared_ptr<ARDOUR::Region>);
+
+ private:
+ TimeFXRequest& tsr;
+
+ soundtouch::SoundTouch st;
+};
+
+} /* namespace */
+
+#endif
+
+#endif /* __ardour_stretch_h__ */
diff --git a/libs/ardour/ardour/tape_file_matcher.h b/libs/ardour/ardour/tape_file_matcher.h
new file mode 100644
index 0000000000..59ec18e818
--- /dev/null
+++ b/libs/ardour/ardour/tape_file_matcher.h
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ 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_TAPE_FILE_MATCHER_INCLUDED
+#define ARDOUR_TAPE_FILE_MATCHER_INCLUDED
+
+#include <string>
+
+#include <regex.h>
+
+namespace ARDOUR {
+
+using std::string;
+
+class TapeFileMatcher
+{
+public:
+
+ TapeFileMatcher();
+
+ bool matches (const string& filename) const;
+
+private:
+
+ regex_t m_compiled_pattern;
+};
+
+} // namespace ARDOUR
+
+#endif
diff --git a/libs/ardour/ardour/template_utils.h b/libs/ardour/ardour/template_utils.h
new file mode 100644
index 0000000000..0676d5b245
--- /dev/null
+++ b/libs/ardour/ardour/template_utils.h
@@ -0,0 +1,20 @@
+
+#ifndef TEMPLATE_UTILS_INCLUDED
+#define TEMPLATE_UTILS_INCLUDED
+
+#include <vector>
+
+#include <pbd/filesystem.h>
+
+namespace ARDOUR {
+
+ using std::vector;
+ using namespace PBD;
+
+ sys::path system_template_directory ();
+
+ sys::path user_template_directory ();
+
+} // namespace ARDOUR
+
+#endif
diff --git a/libs/ardour/ardour/tempo.h b/libs/ardour/ardour/tempo.h
new file mode 100644
index 0000000000..c4915072c5
--- /dev/null
+++ b/libs/ardour/ardour/tempo.h
@@ -0,0 +1,322 @@
+/*
+ Copyright (C) 2000 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_tempo_h__
+#define __ardour_tempo_h__
+
+#include <list>
+#include <string>
+#include <vector>
+#include <cmath>
+#include <glibmm/thread.h>
+
+#include <pbd/undo.h>
+#include <pbd/stateful.h>
+#include <pbd/statefuldestructible.h>
+
+#include <sigc++/signal.h>
+
+#include <ardour/ardour.h>
+
+class XMLNode;
+
+using std::list;
+using std::vector;
+
+namespace ARDOUR {
+class Meter;
+class Tempo {
+ public:
+ Tempo (double bpm, double type=4.0) // defaulting to quarter note
+ : _beats_per_minute (bpm), _note_type(type) {}
+ Tempo (const Tempo& other) {
+ _beats_per_minute = other._beats_per_minute;
+ _note_type = other._note_type;
+ }
+ void operator= (const Tempo& other) {
+ if (&other != this) {
+ _beats_per_minute = other._beats_per_minute;
+ _note_type = other._note_type;
+ }
+ }
+
+ double beats_per_minute () const { return _beats_per_minute;}
+ double note_type () const { return _note_type;}
+ double frames_per_beat (nframes_t sr, const Meter& meter) const;
+
+ protected:
+ double _beats_per_minute;
+ double _note_type;
+};
+
+class Meter {
+ public:
+ static const double ticks_per_beat;
+
+ Meter (double bpb, double bt)
+ : _beats_per_bar (bpb), _note_type (bt) {}
+ Meter (const Meter& other) {
+ _beats_per_bar = other._beats_per_bar;
+ _note_type = other._note_type;
+ }
+ void operator= (const Meter& other) {
+ if (&other != this) {
+ _beats_per_bar = other._beats_per_bar;
+ _note_type = other._note_type;
+ }
+ }
+
+ double beats_per_bar () const { return _beats_per_bar; }
+ double note_divisor() const { return _note_type; }
+
+ double frames_per_bar (const Tempo&, nframes_t sr) const;
+
+ protected:
+
+ /* this is the number of beats in a bar. it is a real value
+ because there are musical traditions on our planet
+ that do not limit themselves to integral numbers of beats
+ per bar.
+ */
+
+ double _beats_per_bar;
+
+ /* this is the type of "note" that a beat represents. for example,
+ 4.0 would be a quarter (crotchet) note, 8.0 would be an eighth
+ (quaver) note, etc.
+ */
+
+ double _note_type;
+};
+
+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; }
+ const nframes_t frame() const { return _frame; }
+
+ void set_movable (bool yn) { _movable = yn; }
+ bool movable() const { return _movable; }
+
+ virtual void set_frame (nframes_t f) {
+ _frame = f;
+ };
+
+ virtual void set_start (const BBT_Time& w) {
+ _start = w;
+ }
+
+ /* MeterSections are not stateful in the full sense,
+ but we do want them to control their own
+ XML state information.
+ */
+
+ virtual XMLNode& get_state() const = 0;
+
+ private:
+ BBT_Time _start;
+ nframes_t _frame;
+ bool _movable;
+};
+
+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;
+
+ XMLNode& get_state() const;
+};
+
+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;
+
+ XMLNode& get_state() const;
+};
+
+typedef list<MetricSection*> Metrics;
+
+class TempoMap : public PBD::StatefulDestructible
+{
+ public:
+ TempoMap (nframes_t frame_rate);
+ ~TempoMap();
+
+ /* measure-based stuff */
+
+ enum BBTPointType {
+ Bar,
+ Beat,
+ };
+
+ struct BBTPoint {
+ BBTPointType type;
+ nframes_t frame;
+ const Meter* meter;
+ const Tempo* tempo;
+ uint32_t bar;
+ uint32_t beat;
+
+ BBTPoint (const Meter& m, const Tempo& t, nframes_t f, BBTPointType ty, uint32_t b, uint32_t e)
+ : type (ty), frame (f), meter (&m), tempo (&t), bar (b), beat (e) {}
+ };
+
+ typedef vector<BBTPoint> BBTPointList;
+
+ template<class T> void apply_with_metrics (T& obj, void (T::*method)(const Metrics&)) {
+ Glib::RWLock::ReaderLock lm (lock);
+ (obj.*method)(*metrics);
+ }
+
+ BBTPointList *get_points (nframes_t start, nframes_t end) const;
+
+ void bbt_time (nframes_t when, BBT_Time&) const;
+ nframes_t frame_time (const BBT_Time&) const;
+ nframes_t bbt_duration_at (nframes_t, const BBT_Time&, int dir) const;
+
+ static const Tempo& default_tempo() { return _default_tempo; }
+ static const Meter& default_meter() { return _default_meter; }
+
+ 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);
+
+ void remove_tempo(const TempoSection&);
+ void remove_meter(const MeterSection&);
+
+ void replace_tempo (TempoSection& existing, const Tempo& replacement);
+ void replace_meter (MeterSection& existing, const Meter& replacement);
+
+
+ nframes_t round_to_bar (nframes_t frame, int dir);
+
+ nframes_t round_to_beat (nframes_t frame, int dir);
+
+ nframes_t round_to_beat_subdivision (nframes_t fr, int sub_num);
+
+ nframes_t round_to_tick (nframes_t frame, int dir);
+
+ void set_length (nframes_t frames);
+
+ XMLNode& get_state (void);
+ int set_state (const XMLNode&);
+
+ void dump (std::ostream&) const;
+ void clear ();
+
+ /* this is a helper class that we use to be able to keep
+ track of which meter *AND* tempo are in effect at
+ a given point in time.
+ */
+
+ class Metric {
+ public:
+ Metric (const Meter& m, const Tempo& t) : _meter (&m), _tempo (&t), _frame (0) {}
+
+ void set_tempo (const Tempo& t) { _tempo = &t; }
+ void set_meter (const Meter& m) { _meter = &m; }
+ void set_frame (nframes_t f) { _frame = f; }
+ void set_start (const BBT_Time& t) { _start = t; }
+
+ const Meter& meter() const { return *_meter; }
+ const Tempo& tempo() const { return *_tempo; }
+ nframes_t frame() const { return _frame; }
+ const BBT_Time& start() const { return _start; }
+
+ private:
+ const Meter* _meter;
+ const Tempo* _tempo;
+ nframes_t _frame;
+ BBT_Time _start;
+
+ };
+
+ Metric metric_at (BBT_Time bbt) const;
+ 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);
+ void change_initial_tempo (double bpm, double note_type);
+
+ int n_tempos () const;
+ int n_meters () const;
+
+ sigc::signal<void,ARDOUR::Change> StateChanged;
+
+ private:
+ static Tempo _default_tempo;
+ static Meter _default_meter;
+
+ Metrics* metrics;
+ nframes_t _frame_rate;
+ nframes_t last_bbt_when;
+ bool last_bbt_valid;
+ BBT_Time last_bbt;
+ mutable Glib::RWLock lock;
+
+ void timestamp_metrics (bool use_bbt);
+
+ nframes_t round_to_type (nframes_t fr, int dir, BBTPointType);
+
+ nframes_t frame_time_unlocked (const BBT_Time&) const;
+
+ void bbt_time_unlocked (nframes_t, BBT_Time&) const;
+
+ nframes_t bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const;
+
+ const MeterSection& first_meter() const;
+ const TempoSection& first_tempo() const;
+
+ nframes_t count_frames_between (const BBT_Time&, const BBT_Time&) const;
+ 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, bool with_bbt);
+};
+
+}; /* namespace ARDOUR */
+
+#endif /* __ardour_tempo_h__ */
diff --git a/libs/ardour/ardour/timestamps.h b/libs/ardour/ardour/timestamps.h
new file mode 100644
index 0000000000..a0aeeae13d
--- /dev/null
+++ b/libs/ardour/ardour/timestamps.h
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 2000-2007 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_timestamps_h__
+#define __ardour_timestamps_h__
+
+#ifdef WITH_JACK_TIMESTAMPS
+#include <jack/timestamps.h>
+#else
+#define jack_timestamp(s)
+#define jack_init_timestamps(n)
+#define jack_dump_timestamps(o)
+#define jack_reset_timestamps()
+#endif
+
+#endif /* __ardour_timestamps_h__ */
diff --git a/libs/ardour/ardour/track.h b/libs/ardour/ardour/track.h
new file mode 100644
index 0000000000..4d5545c0dc
--- /dev/null
+++ b/libs/ardour/ardour/track.h
@@ -0,0 +1,151 @@
+/*
+ Copyright (C) 2006 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_track_h__
+#define __ardour_track_h__
+
+#include <boost/shared_ptr.hpp>
+
+#include <ardour/route.h>
+
+namespace ARDOUR {
+
+class Session;
+class Diskstream;
+class Playlist;
+class RouteGroup;
+
+class Track : public Route
+{
+ public:
+ Track (Session&, string name, Route::Flag f = Route::Flag (0), TrackMode m = Normal, DataType default_type = DataType::AUDIO);
+
+ virtual ~Track ();
+
+ bool set_name (const std::string& str);
+
+ TrackMode mode () const { return _mode; }
+ virtual int set_mode (TrackMode m) { return false; }
+ virtual bool can_use_mode (TrackMode m, bool& bounce_required) { return false; }
+ sigc::signal<void> TrackModeChanged;
+
+ virtual int roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, int declick, bool can_record, bool rec_monitors_input) = 0;
+
+ virtual int no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, bool state_changing, bool can_record, bool rec_monitors_input) = 0;
+
+ virtual int silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame,
+ nframes_t offset, bool can_record, bool rec_monitors_input) = 0;
+
+ void toggle_monitor_input ();
+
+ bool can_record();
+
+ boost::shared_ptr<Diskstream> diskstream() const { return _diskstream; }
+
+ virtual int use_diskstream (string name) = 0;
+ virtual int use_diskstream (const PBD::ID& id) = 0;
+
+ nframes_t update_total_latency();
+ void set_latency_delay (nframes_t);
+
+ enum FreezeState {
+ NoFreeze,
+ Frozen,
+ UnFrozen
+ };
+
+ FreezeState freeze_state() const;
+
+ virtual void freeze (InterThreadInfo&) = 0;
+ virtual void unfreeze () = 0;
+
+ virtual void bounce (InterThreadInfo&) = 0;
+ virtual void bounce_range (nframes_t start, nframes_t end, InterThreadInfo&) = 0;
+
+ XMLNode& get_state();
+ XMLNode& get_template();
+ virtual int set_state(const XMLNode& node) = 0;
+
+ boost::shared_ptr<PBD::Controllable> rec_enable_control() { return _rec_enable_control; }
+
+ bool record_enabled() const;
+ void set_record_enable (bool yn, void *src);
+
+ void set_meter_point (MeterPoint, void* src);
+
+ sigc::signal<void> DiskstreamChanged;
+ sigc::signal<void> FreezeChange;
+
+ protected:
+ Track (Session& sess, const XMLNode& node, DataType default_type = DataType::AUDIO);
+
+ virtual XMLNode& state (bool full) = 0;
+
+ boost::shared_ptr<Diskstream> _diskstream;
+ MeterPoint _saved_meter_point;
+ TrackMode _mode;
+
+ //private: (FIXME)
+ struct FreezeRecordProcessorInfo {
+ FreezeRecordProcessorInfo(XMLNode& st, boost::shared_ptr<Processor> proc)
+ : state (st), processor (proc) {}
+
+ XMLNode state;
+ boost::shared_ptr<Processor> processor;
+ PBD::ID id;
+ UndoAction memento;
+ };
+
+ struct FreezeRecord {
+ FreezeRecord()
+ : have_mementos(false)
+ {}
+
+ ~FreezeRecord();
+
+ boost::shared_ptr<Playlist> playlist;
+ vector<FreezeRecordProcessorInfo*> processor_info;
+ bool have_mementos;
+ FreezeState state;
+ };
+
+ struct RecEnableControllable : public PBD::Controllable {
+ RecEnableControllable (Track&);
+
+ void set_value (float);
+ float get_value (void) const;
+
+ Track& track;
+ };
+
+ virtual void set_state_part_two () = 0;
+
+ FreezeRecord _freeze_record;
+ XMLNode* pending_state;
+ sigc::connection recenable_connection;
+ sigc::connection ic_connection;
+ bool _destructive;
+
+ boost::shared_ptr<RecEnableControllable> _rec_enable_control;
+};
+
+}; /* namespace ARDOUR*/
+
+#endif /* __ardour_track_h__ */
diff --git a/libs/ardour/ardour/transient_detector.h b/libs/ardour/ardour/transient_detector.h
new file mode 100644
index 0000000000..259b79176f
--- /dev/null
+++ b/libs/ardour/ardour/transient_detector.h
@@ -0,0 +1,58 @@
+/*
+ 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();
+
+ static std::string operational_identifier();
+
+ void set_threshold (float);
+ void set_sensitivity (float);
+
+ float get_threshold () const;
+ float get_sensitivity () const;
+
+ int run (const std::string& path, Readable*, uint32_t channel, AnalysisFeatureList& results);
+
+ static void cleanup_transients (AnalysisFeatureList&, float sr, float gap_msecs);
+
+ protected:
+ AnalysisFeatureList* current_results;
+ int use_features (Vamp::Plugin::FeatureSet&, std::ostream*);
+
+ static std::string _op_id;
+};
+
+} /* namespace */
+
+#endif /* __ardour_audioanalyser_h__ */
diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h
new file mode 100644
index 0000000000..9bfd93cdbf
--- /dev/null
+++ b/libs/ardour/ardour/types.h
@@ -0,0 +1,443 @@
+/*
+ Copyright (C) 2002 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_types_h__
+#define __ardour_types_h__
+
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS /* PRI<foo>; C++ requires explicit requesting of these */
+#endif
+
+#include <istream>
+#include <vector>
+#include <boost/shared_ptr.hpp>
+
+#include <inttypes.h>
+#include <jack/types.h>
+#include <jack/midiport.h>
+#include <control_protocol/smpte.h>
+#include <pbd/id.h>
+
+#include <map>
+
+#if __GNUC__ < 3
+
+typedef int intptr_t;
+#endif
+
+/* eventually, we'd like everything (including JACK) to
+ move to this. for now, its a dedicated type.
+*/
+
+typedef int64_t nframes64_t;
+
+namespace ARDOUR {
+
+ class Source;
+ class AudioSource;
+
+ typedef jack_default_audio_sample_t Sample;
+ typedef float pan_t;
+ typedef float gain_t;
+ typedef uint32_t layer_t;
+ typedef uint64_t microseconds_t;
+ typedef uint32_t nframes_t;
+
+ enum IOChange {
+ NoChange = 0,
+ ConfigurationChanged = 0x1,
+ ConnectionsChanged = 0x2
+ };
+
+ enum OverlapType {
+ OverlapNone, // no overlap
+ OverlapInternal, // the overlap is 100% with the object
+ OverlapStart, // overlap covers start, but ends within
+ OverlapEnd, // overlap begins within and covers end
+ OverlapExternal // overlap extends to (at least) begin+end
+ };
+
+ OverlapType coverage (nframes_t start_a, nframes_t end_a,
+ nframes_t start_b, nframes_t end_b);
+
+ /** See parameter.h
+ * XXX: I don't think/hope these hex values matter anymore.
+ */
+ enum AutomationType {
+ NullAutomation = 0x0,
+ GainAutomation = 0x1,
+ PanAutomation = 0x2,
+ PluginAutomation = 0x4,
+ SoloAutomation = 0x8,
+ MuteAutomation = 0x10,
+ MidiCCAutomation = 0x20,
+ MidiPgmChangeAutomation = 0x21,
+ MidiPitchBenderAutomation = 0x22,
+ MidiChannelAftertouchAutomation = 0x23,
+ FadeInAutomation = 0x40,
+ FadeOutAutomation = 0x80,
+ EnvelopeAutomation = 0x100
+ };
+
+ enum AutoState {
+ Off = 0x0,
+ Write = 0x1,
+ Touch = 0x2,
+ Play = 0x4
+ };
+
+ std::string auto_state_to_string (AutoState);
+ AutoState string_to_auto_state (std::string);
+
+ enum AutoStyle {
+ Absolute = 0x1,
+ Trim = 0x2
+ };
+
+ std::string auto_style_to_string (AutoStyle);
+ AutoStyle string_to_auto_style (std::string);
+
+ enum AlignStyle {
+ CaptureTime,
+ ExistingMaterial
+ };
+
+ enum MeterPoint {
+ MeterInput,
+ MeterPreFader,
+ MeterPostFader
+ };
+
+ enum TrackMode {
+ Normal,
+ Destructive
+ };
+
+ enum NoteMode {
+ Sustained,
+ Percussive
+ };
+
+ enum ChannelMode {
+ AllChannels = 0, ///< Pass through all channel information unmodified
+ FilterChannels, ///< Ignore events on certain channels
+ ForceChannel ///< Force all events to a certain channel
+ };
+
+ enum EventTimeUnit {
+ Frames,
+ Beats
+ };
+
+ struct BBT_Time {
+ uint32_t bars;
+ uint32_t beats;
+ uint32_t ticks;
+
+ BBT_Time() {
+ bars = 1;
+ beats = 1;
+ ticks = 0;
+ }
+
+ /* we can't define arithmetic operators for BBT_Time, because
+ the results depend on a TempoMap, but we can define
+ a useful check on the less-than condition.
+ */
+
+ bool operator< (const BBT_Time& other) const {
+ return bars < other.bars ||
+ (bars == other.bars && beats < other.beats) ||
+ (bars == other.bars && beats == other.beats && ticks < other.ticks);
+ }
+
+ bool operator== (const BBT_Time& other) const {
+ return bars == other.bars && beats == other.beats && ticks == other.ticks;
+ }
+
+ };
+ enum SmpteFormat {
+ smpte_23976,
+ smpte_24,
+ smpte_24976,
+ smpte_25,
+ smpte_2997,
+ smpte_2997drop,
+ smpte_30,
+ smpte_30drop,
+ smpte_5994,
+ smpte_60
+ };
+
+ struct AnyTime {
+ enum Type {
+ SMPTE,
+ BBT,
+ Frames,
+ Seconds
+ };
+
+ Type type;
+
+ SMPTE::Time smpte;
+ BBT_Time bbt;
+
+ union {
+ nframes_t frames;
+ double seconds;
+ };
+
+ AnyTime() { type = Frames; frames = 0; }
+ };
+
+ struct AudioRange {
+ nframes_t start;
+ nframes_t end;
+ uint32_t id;
+
+ AudioRange (nframes_t s, nframes_t e, uint32_t i) : start (s), end (e) , id (i) {}
+
+ nframes_t length() { return end - start + 1; }
+
+ bool operator== (const AudioRange& other) const {
+ return start == other.start && end == other.end && id == other.id;
+ }
+
+ bool equal (const AudioRange& other) const {
+ return start == other.start && end == other.end;
+ }
+
+ OverlapType coverage (nframes_t s, nframes_t e) const {
+ return ARDOUR::coverage (start, end, s, e);
+ }
+ };
+
+ struct MusicRange {
+ BBT_Time start;
+ BBT_Time end;
+ uint32_t id;
+
+ MusicRange (BBT_Time& s, BBT_Time& e, uint32_t i)
+ : start (s), end (e), id (i) {}
+
+ bool operator== (const MusicRange& other) const {
+ return start == other.start && end == other.end && id == other.id;
+ }
+
+ bool equal (const MusicRange& other) const {
+ return start == other.start && end == other.end;
+ }
+ };
+
+ /*
+ Slowest = 6.6dB/sec falloff at update rate of 40ms
+ Slow = 6.8dB/sec falloff at update rate of 40ms
+ */
+
+ enum MeterFalloff {
+ MeterFalloffOff = 0,
+ MeterFalloffSlowest = 1,
+ MeterFalloffSlow = 2,
+ MeterFalloffMedium = 3,
+ MeterFalloffFast = 4,
+ MeterFalloffFaster = 5,
+ MeterFalloffFastest = 6
+ };
+
+ enum MeterHold {
+ MeterHoldOff = 0,
+ MeterHoldShort = 40,
+ MeterHoldMedium = 100,
+ MeterHoldLong = 200
+ };
+
+ enum EditMode {
+ Slide,
+ Splice,
+ Lock
+ };
+
+ enum RegionPoint {
+ Start,
+ End,
+ SyncPoint
+ };
+
+ enum Change {
+ range_guarantee = ~0
+ };
+
+
+ enum Placement {
+ PreFader,
+ PostFader
+ };
+
+ enum MonitorModel {
+ HardwareMonitoring,
+ SoftwareMonitoring,
+ ExternalMonitoring
+ };
+
+ enum DenormalModel {
+ DenormalNone,
+ DenormalFTZ,
+ DenormalDAZ,
+ DenormalFTZDAZ
+ };
+
+ enum RemoteModel {
+ UserOrdered,
+ MixerOrdered,
+ EditorOrdered
+ };
+
+ enum CrossfadeModel {
+ FullCrossfade,
+ ShortCrossfade
+ };
+
+ enum LayerModel {
+ LaterHigher,
+ MoveAddHigher,
+ AddHigher
+ };
+
+ enum SoloModel {
+ InverseMute,
+ SoloBus
+ };
+
+ enum AutoConnectOption {
+ AutoConnectPhysical = 0x1,
+ AutoConnectMaster = 0x2
+ };
+
+ struct InterThreadInfo {
+ volatile bool done;
+ volatile bool cancel;
+ volatile float progress;
+ pthread_t thread;
+ };
+
+ enum SampleFormat {
+ FormatFloat = 0,
+ FormatInt24,
+ FormatInt16
+ };
+
+
+ enum HeaderFormat {
+ BWF,
+ WAVE,
+ WAVE64,
+ CAF,
+ AIFF,
+ iXML,
+ RF64
+ };
+
+ struct PeakData {
+ typedef Sample PeakDatum;
+
+ PeakDatum min;
+ PeakDatum max;
+ };
+
+ enum PluginType {
+ AudioUnit,
+ LADSPA,
+ LV2,
+ VST
+ };
+
+ enum SlaveSource {
+ None = 0,
+ MTC,
+ JACK
+ };
+
+ enum ShuttleBehaviour {
+ Sprung,
+ Wheel
+ };
+
+ enum ShuttleUnits {
+ Percentage,
+ Semitones
+ };
+
+ typedef std::vector<boost::shared_ptr<Source> > SourceList;
+
+ enum SrcQuality {
+ SrcBest,
+ SrcGood,
+ SrcQuick,
+ SrcFast,
+ SrcFastest
+ };
+
+ struct TimeFXRequest : public InterThreadInfo {
+ TimeFXRequest() : time_fraction(0), pitch_fraction(0),
+ quick_seek(false), antialias(false), opts(0) {}
+ float time_fraction;
+ float pitch_fraction;
+ /* SoundTouch */
+ bool quick_seek;
+ bool antialias;
+ /* RubberBand */
+ int opts; // really RubberBandStretcher::Options
+ };
+
+ typedef std::list<nframes64_t> AnalysisFeatureList;
+
+} // namespace ARDOUR
+
+std::istream& operator>>(std::istream& o, ARDOUR::SampleFormat& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::HeaderFormat& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::AutoConnectOption& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::EditMode& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::MonitorModel& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::RemoteModel& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::SoloModel& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::LayerModel& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeModel& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::SlaveSource& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::ShuttleBehaviour& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::ShuttleUnits& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::SmpteFormat& sf);
+std::istream& operator>>(std::istream& o, ARDOUR::DenormalModel& sf);
+
+using ARDOUR::nframes_t;
+
+static inline nframes_t
+session_frame_to_track_frame (nframes_t session_frame, double speed)
+{
+ return (nframes_t)( (double)session_frame * speed );
+}
+
+static inline nframes_t
+track_frame_to_session_frame (nframes_t track_frame, double speed)
+{
+ return (nframes_t)( (double)track_frame / speed );
+}
+
+
+#endif /* __ardour_types_h__ */
+
diff --git a/libs/ardour/ardour/user_bundle.h b/libs/ardour/ardour/user_bundle.h
new file mode 100644
index 0000000000..954e93d5d1
--- /dev/null
+++ b/libs/ardour/ardour/user_bundle.h
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 2007 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_user_bundle_h__
+#define __ardour_user_bundle_h__
+
+#include <vector>
+#include <glibmm/thread.h>
+#include "pbd/stateful.h"
+#include "ardour/bundle.h"
+
+namespace ARDOUR {
+
+class Session;
+
+class UserBundle : public Bundle, public PBD::Stateful {
+
+ public:
+ UserBundle (std::string const &);
+ UserBundle (XMLNode const &, bool);
+
+ uint32_t nchannels () const;
+ const ARDOUR::PortList& channel_ports (uint32_t) const;
+
+ void add_channel ();
+ void set_channels (uint32_t);
+ void remove_channel (uint32_t);
+ void add_port_to_channel (uint32_t, std::string const &);
+ void remove_port_from_channel (uint32_t, std::string const &);
+ bool port_attached_to_channel (uint32_t, std::string const &) const;
+ XMLNode& get_state ();
+
+ /// The number of channels is about to change
+ sigc::signal<void> ConfigurationWillChange;
+ /// The number of channels has changed
+ sigc::signal<void> ConfigurationHasChanged;
+ /// The port set associated with one of our channels is about to change
+ /// Parameter is the channel number
+ sigc::signal<void, int> PortsWillChange;
+ /// The port set associated with one of our channels has changed
+ /// Parameter is the channel number
+ sigc::signal<void, int> PortsHaveChanged;
+
+ private:
+
+ int set_state (const XMLNode &);
+
+ /// mutex for _ports;
+ /// XXX: is this necessary?
+ mutable Glib::Mutex _ports_mutex;
+ std::vector<PortList> _ports;
+};
+
+}
+
+#endif
diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h
new file mode 100644
index 0000000000..eecde2e85f
--- /dev/null
+++ b/libs/ardour/ardour/utils.h
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 1999 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_utils_h__
+#define __ardour_utils_h__
+
+#include <iostream>
+#include <string>
+#include <cmath>
+
+#if defined(HAVE_COREAUDIO) || defined(HAVE_AUDIOUNITS)
+#include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#include "ardour.h"
+
+class XMLNode;
+
+Glib::ustring legalize_for_path (Glib::ustring str);
+std::ostream& operator<< (std::ostream& o, const ARDOUR::BBT_Time& bbt);
+XMLNode* find_named_node (const XMLNode& node, std::string name);
+
+static inline float f_max(float x, float a) {
+ x -= a;
+ x += fabsf (x);
+ x *= 0.5f;
+ x += a;
+
+ return (x);
+}
+
+std::string bump_name_once(std::string s);
+
+int cmp_nocase (const std::string& s, const std::string& s2);
+
+int touch_file(Glib::ustring path);
+
+Glib::ustring path_expand (Glib::ustring);
+Glib::ustring region_name_from_path (Glib::ustring path, bool strip_channels, bool add_channel_suffix = false, uint32_t total = 0, uint32_t this_one = 0);
+bool path_is_paired (Glib::ustring path, Glib::ustring& pair_base);
+
+void compute_equal_power_fades (nframes_t nframes, float* in, float* out);
+
+const char* slave_source_to_string (ARDOUR::SlaveSource src);
+ARDOUR::SlaveSource string_to_slave_source (std::string str);
+
+const char* edit_mode_to_string (ARDOUR::EditMode);
+ARDOUR::EditMode string_to_edit_mode (std::string);
+
+float meter_falloff_to_float (ARDOUR::MeterFalloff);
+ARDOUR::MeterFalloff meter_falloff_from_float (float);
+float meter_falloff_to_db_per_sec (float);
+float meter_hold_to_float (ARDOUR::MeterHold);
+
+#if defined(HAVE_COREAUDIO) || defined(HAVE_AUDIOUNITS)
+std::string CFStringRefToStdString(CFStringRef stringRef);
+#endif // HAVE_COREAUDIO
+
+#endif /* __ardour_utils_h__ */
+
diff --git a/libs/ardour/ardour/vst_plugin.h b/libs/ardour/ardour/vst_plugin.h
new file mode 100644
index 0000000000..e41d000f9c
--- /dev/null
+++ b/libs/ardour/ardour/vst_plugin.h
@@ -0,0 +1,116 @@
+/*
+ Copyright (C) 2004 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_vst_plugin_h__
+#define __ardour_vst_plugin_h__
+
+#include <list>
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
+#include <dlfcn.h>
+
+#include <sigc++/signal.h>
+#include <pbd/stateful.h>
+#include <jack/types.h>
+#include <ardour/plugin.h>
+
+using std::string;
+using std::vector;
+using std::list;
+using std::map;
+
+struct _FSTHandle;
+struct _FST;
+typedef struct _FSTHandle FSTHandle;
+typedef struct _FST FST;
+class AEffect;
+
+namespace ARDOUR {
+class AudioEngine;
+class Session;
+
+class VSTPlugin : public ARDOUR::Plugin
+{
+ public:
+ VSTPlugin (ARDOUR::AudioEngine&, ARDOUR::Session&, FSTHandle* handle);
+ VSTPlugin (const VSTPlugin &);
+ ~VSTPlugin ();
+
+ /* Plugin interface */
+
+ std::string unique_id() const;
+ const char * label() const;
+ const char * name() const;
+ const char * maker() const;
+ uint32_t parameter_count() const;
+ float default_value (uint32_t port);
+ nframes_t signal_latency() const;
+ void set_parameter (uint32_t port, float val);
+ float get_parameter (uint32_t port) const;
+ int get_parameter_descriptor (uint32_t which, ParameterDescriptor&) const;
+ std::set<uint32_t> automatable() const;
+ uint32_t nth_parameter (uint32_t port, bool& ok) const;
+ void activate ();
+ void deactivate ();
+ void set_block_size (nframes_t nframes);
+ int connect_and_run (BufferSet&, uint32_t& in, uint32_t& out, nframes_t nframes, nframes_t offset);
+ string describe_parameter (uint32_t);
+ string state_node_name() const { return "vst"; }
+ void print_parameter (uint32_t, char*, uint32_t len) const;
+
+ bool parameter_is_audio(uint32_t i) const { return false; }
+ bool parameter_is_control(uint32_t i) const { return true; }
+ bool parameter_is_input(uint32_t i) const { return true; }
+ bool parameter_is_output(uint32_t i) const { return false; }
+
+ bool load_preset (const string preset_label );
+ bool save_preset(string name);
+
+ bool has_editor () const;
+
+ XMLNode& get_state();
+ int set_state(const XMLNode& node);
+
+ AEffect* plugin() const { return _plugin; }
+ FST* fst() const { return _fst; }
+
+
+ private:
+ FSTHandle* handle;
+ FST* _fst;
+ AEffect* _plugin;
+ bool been_resumed;
+};
+
+class VSTPluginInfo : public PluginInfo
+{
+ public:
+ VSTPluginInfo () {}
+ ~VSTPluginInfo () {}
+
+ PluginPtr load (Session& session);
+};
+
+typedef boost::shared_ptr<VSTPluginInfo> VSTPluginInfoPtr;
+
+} // namespace ARDOUR
+
+#endif /* __ardour_vst_plugin_h__ */
diff --git a/libs/ardour/audio_buffer.cc b/libs/ardour/audio_buffer.cc
new file mode 100644
index 0000000000..915fdeb948
--- /dev/null
+++ b/libs/ardour/audio_buffer.cc
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 2006-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <ardour/audio_buffer.h>
+#include <pbd/error.h>
+#include <errno.h>
+
+#include "i18n.h"
+
+#ifdef __x86_64__
+static const int CPU_CACHE_ALIGN = 64;
+#else
+static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
+#endif
+
+using namespace PBD;
+
+namespace ARDOUR {
+
+
+AudioBuffer::AudioBuffer(size_t capacity)
+ : Buffer(DataType::AUDIO, capacity)
+ , _owns_data (false)
+ , _data (0)
+{
+ if (_capacity > 0) {
+ _owns_data = true; // prevent resize() from gagging
+ resize (_capacity);
+ silence (_capacity);
+ }
+}
+
+AudioBuffer::~AudioBuffer()
+{
+ if (_owns_data)
+ free(_data);
+}
+
+void
+AudioBuffer::resize (size_t size)
+{
+ if (!_owns_data || (size < _capacity)) {
+ return;
+ }
+
+ if (_data) {
+ free (_data);
+ }
+
+ _capacity = size;
+ _size = size;
+ _silent = false;
+
+#ifdef NO_POSIX_MEMALIGN
+ _data = (Sample *) malloc(sizeof(Sample) * _capacity);
+#else
+ if (posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(Sample) * _capacity)) {
+ fatal << string_compose (_("Memory allocation error: posix_memalign (%1 * %2) failed (%3)"),
+ CPU_CACHE_ALIGN, sizeof (Sample) * _capacity, strerror (errno)) << endmsg;
+ }
+#endif
+
+ _owns_data = true;
+}
+
+} // namespace ARDOUR
+
diff --git a/libs/ardour/audio_diskstream.cc b/libs/ardour/audio_diskstream.cc
new file mode 100644
index 0000000000..091e1df30f
--- /dev/null
+++ b/libs/ardour/audio_diskstream.cc
@@ -0,0 +1,2490 @@
+/*
+ Copyright (C) 2000-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <fstream>
+#include <cstdio>
+#include <unistd.h>
+#include <cmath>
+#include <cerrno>
+#include <cassert>
+#include <string>
+#include <climits>
+#include <fcntl.h>
+#include <cstdlib>
+#include <ctime>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <pbd/error.h>
+#include <glibmm/thread.h>
+#include <pbd/xml++.h>
+#include <pbd/memento_command.h>
+#include <pbd/enumwriter.h>
+#include <pbd/stacktrace.h>
+
+#include <ardour/ardour.h>
+#include <ardour/audioengine.h>
+#include <ardour/analyser.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/utils.h>
+#include <ardour/configuration.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/send.h>
+#include <ardour/region_factory.h>
+#include <ardour/audioplaylist.h>
+#include <ardour/playlist_factory.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/audioregion.h>
+#include <ardour/audio_port.h>
+#include <ardour/source_factory.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+size_t AudioDiskstream::_working_buffers_size = 0;
+Sample* AudioDiskstream::_mixdown_buffer = 0;
+gain_t* AudioDiskstream::_gain_buffer = 0;
+
+AudioDiskstream::AudioDiskstream (Session &sess, const string &name, Diskstream::Flag flag)
+ : Diskstream(sess, name, flag)
+ , deprecated_io_node(NULL)
+ , channels (new ChannelList)
+{
+ /* prevent any write sources from being created */
+
+ in_set_state = true;
+
+ init(flag);
+ use_new_playlist ();
+
+ in_set_state = false;
+}
+
+AudioDiskstream::AudioDiskstream (Session& sess, const XMLNode& node)
+ : Diskstream(sess, node)
+ , deprecated_io_node(NULL)
+ , channels (new ChannelList)
+{
+ in_set_state = true;
+ init (Recordable);
+
+ if (set_state (node)) {
+ in_set_state = false;
+ throw failed_constructor();
+ }
+
+ in_set_state = false;
+
+ if (destructive()) {
+ use_destructive_playlist ();
+ }
+}
+
+void
+AudioDiskstream::init (Diskstream::Flag f)
+{
+ Diskstream::init(f);
+
+ /* there are no channels at this point, so these
+ two calls just get speed_buffer_size and wrap_buffer
+ size setup without duplicating their code.
+ */
+
+ set_block_size (_session.get_block_size());
+ allocate_temporary_buffers ();
+
+ add_channel (1);
+ assert(_n_channels == ChanCount(DataType::AUDIO, 1));
+}
+
+AudioDiskstream::~AudioDiskstream ()
+{
+ notify_callbacks ();
+
+ {
+ RCUWriter<ChannelList> writer (channels);
+ boost::shared_ptr<ChannelList> c = writer.get_copy();
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ delete *chan;
+ }
+
+ c->clear();
+ }
+
+ channels.flush ();
+}
+
+void
+AudioDiskstream::allocate_working_buffers()
+{
+ assert(disk_io_frames() > 0);
+
+ _working_buffers_size = disk_io_frames();
+ _mixdown_buffer = new Sample[_working_buffers_size];
+ _gain_buffer = new gain_t[_working_buffers_size];
+}
+
+void
+AudioDiskstream::free_working_buffers()
+{
+ delete [] _mixdown_buffer;
+ delete [] _gain_buffer;
+ _working_buffers_size = 0;
+ _mixdown_buffer = 0;
+ _gain_buffer = 0;
+}
+
+void
+AudioDiskstream::non_realtime_input_change ()
+{
+ {
+ Glib::Mutex::Lock lm (state_lock);
+
+ if (input_change_pending == NoChange) {
+ return;
+ }
+
+ {
+ RCUWriter<ChannelList> writer (channels);
+ boost::shared_ptr<ChannelList> c = writer.get_copy();
+
+ _n_channels.set(DataType::AUDIO, c->size());
+
+ if (_io->n_inputs().n_audio() > _n_channels.n_audio()) {
+ add_channel_to (c, _io->n_inputs().n_audio() - _n_channels.n_audio());
+ } else if (_io->n_inputs().n_audio() < _n_channels.n_audio()) {
+ remove_channel_from (c, _n_channels.n_audio() - _io->n_inputs().n_audio());
+ }
+ }
+
+ get_input_sources ();
+ set_capture_offset ();
+
+ if (first_input_change) {
+ set_align_style (_persistent_alignment_style);
+ first_input_change = false;
+ } else {
+ set_align_style_from_io ();
+ }
+
+ input_change_pending = NoChange;
+
+ /* implicit unlock */
+ }
+
+ /* reset capture files */
+
+ reset_write_sources (false);
+
+ /* now refill channel buffers */
+
+ if (speed() != 1.0f || speed() != -1.0f) {
+ seek ((nframes_t) (_session.transport_frame() * (double) speed()));
+ } else {
+ seek (_session.transport_frame());
+ }
+}
+
+void
+AudioDiskstream::get_input_sources ()
+{
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ uint32_t n;
+ ChannelList::iterator chan;
+ uint32_t ni = _io->n_inputs().n_audio();
+ vector<string> connections;
+
+ for (n = 0, chan = c->begin(); chan != c->end() && n < ni; ++chan, ++n) {
+
+ connections.clear ();
+
+ if (_io->input(n)->get_connections (connections) == 0) {
+
+ if ((*chan)->source) {
+ // _source->disable_metering ();
+ }
+
+ (*chan)->source = 0;
+
+ } else {
+ (*chan)->source = dynamic_cast<AudioPort*>(_session.engine().get_port_by_name (connections[0]) );
+ }
+ }
+}
+
+int
+AudioDiskstream::find_and_use_playlist (const string& name)
+{
+ boost::shared_ptr<AudioPlaylist> playlist;
+
+ if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (_session.playlist_by_name (name))) == 0) {
+ playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (DataType::AUDIO, _session, name));
+ }
+
+ if (!playlist) {
+ error << string_compose(_("AudioDiskstream: Playlist \"%1\" isn't an audio playlist"), name) << endmsg;
+ return -1;
+ }
+
+ return use_playlist (playlist);
+}
+
+int
+AudioDiskstream::use_playlist (boost::shared_ptr<Playlist> playlist)
+{
+ assert(boost::dynamic_pointer_cast<AudioPlaylist>(playlist));
+
+ Diskstream::use_playlist(playlist);
+
+ return 0;
+}
+
+int
+AudioDiskstream::use_new_playlist ()
+{
+ string newname;
+ boost::shared_ptr<AudioPlaylist> playlist;
+
+ if (!in_set_state && destructive()) {
+ return 0;
+ }
+
+ if (_playlist) {
+ newname = Playlist::bump_name (_playlist->name(), _session);
+ } else {
+ newname = Playlist::bump_name (_name, _session);
+ }
+
+ if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist> (PlaylistFactory::create (DataType::AUDIO, _session, newname, hidden()))) != 0) {
+
+ playlist->set_orig_diskstream_id (id());
+ return use_playlist (playlist);
+
+ } else {
+ return -1;
+ }
+}
+
+int
+AudioDiskstream::use_copy_playlist ()
+{
+ assert(audio_playlist());
+
+ if (destructive()) {
+ return 0;
+ }
+
+ if (_playlist == 0) {
+ error << string_compose(_("AudioDiskstream %1: there is no existing playlist to make a copy of!"), _name) << endmsg;
+ return -1;
+ }
+
+ string newname;
+ boost::shared_ptr<AudioPlaylist> playlist;
+
+ newname = Playlist::bump_name (_playlist->name(), _session);
+
+ if ((playlist = boost::dynamic_pointer_cast<AudioPlaylist>(PlaylistFactory::create (audio_playlist(), newname))) != 0) {
+ playlist->set_orig_diskstream_id (id());
+ return use_playlist (playlist);
+ } else {
+ return -1;
+ }
+}
+
+void
+AudioDiskstream::setup_destructive_playlist ()
+{
+ SourceList srcs;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ srcs.push_back ((*chan)->write_source);
+ }
+
+ /* a single full-sized region */
+
+ boost::shared_ptr<Region> region (RegionFactory::create (srcs, 0, max_frames - srcs.front()->natural_position(), _name));
+ _playlist->add_region (region, srcs.front()->natural_position());
+}
+
+void
+AudioDiskstream::use_destructive_playlist ()
+{
+ /* this is called from the XML-based constructor or ::set_destructive. when called,
+ we already have a playlist and a region, but we need to
+ set up our sources for write. we use the sources associated
+ with the (presumed single, full-extent) region.
+ */
+
+ boost::shared_ptr<Region> rp = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
+
+ if (!rp) {
+ reset_write_sources (false, true);
+ return;
+ }
+
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion> (rp);
+
+ if (region == 0) {
+ throw failed_constructor();
+ }
+
+ /* be sure to stretch the region out to the maximum length */
+
+ region->set_length (max_frames - region->position(), this);
+
+ uint32_t n;
+ ChannelList::iterator chan;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+ (*chan)->write_source = boost::dynamic_pointer_cast<AudioFileSource>(region->source (n));
+ assert((*chan)->write_source);
+ (*chan)->write_source->set_allow_remove_if_empty (false);
+
+ /* this might be false if we switched modes, so force it */
+
+ (*chan)->write_source->set_destructive (true);
+ }
+
+ /* the source list will never be reset for a destructive track */
+}
+
+void
+AudioDiskstream::check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record)
+{
+ int possibly_recording;
+ int rolling;
+ int change;
+ const int transport_rolling = 0x4;
+ const int track_rec_enabled = 0x2;
+ const int global_rec_enabled = 0x1;
+
+ /* merge together the 3 factors that affect record status, and compute
+ what has changed.
+ */
+
+ rolling = _session.transport_speed() != 0.0f;
+ possibly_recording = (rolling << 2) | (record_enabled() << 1) | can_record;
+ change = possibly_recording ^ last_possibly_recording;
+
+ if (possibly_recording == last_possibly_recording) {
+ return;
+ }
+
+ /* change state */
+
+ /* if per-track or global rec-enable turned on while the other was already on, we've started recording */
+
+ if (((change & track_rec_enabled) && record_enabled() && (!(change & global_rec_enabled) && can_record)) ||
+ (((change & global_rec_enabled) && can_record && (!(change & track_rec_enabled) && record_enabled())))) {
+
+ /* starting to record: compute first+last frames */
+
+ first_recordable_frame = transport_frame + _capture_offset;
+ last_recordable_frame = max_frames;
+ capture_start_frame = transport_frame;
+
+ if (!(last_possibly_recording & transport_rolling) && (possibly_recording & transport_rolling)) {
+
+ /* was stopped, now rolling (and recording) */
+
+ if (_alignment_style == ExistingMaterial) {
+ first_recordable_frame += _session.worst_output_latency();
+ } else {
+ first_recordable_frame += _roll_delay;
+ }
+
+ } else {
+
+ /* was rolling, but record state changed */
+
+ if (_alignment_style == ExistingMaterial) {
+
+ if (!Config->get_punch_in()) {
+
+ /* manual punch in happens at the correct transport frame
+ because the user hit a button. but to get alignment correct
+ we have to back up the position of the new region to the
+ appropriate spot given the roll delay.
+ */
+
+ capture_start_frame -= _roll_delay;
+
+ /* XXX paul notes (august 2005): i don't know why
+ this is needed.
+ */
+
+ first_recordable_frame += _capture_offset;
+
+ } else {
+
+ /* autopunch toggles recording at the precise
+ transport frame, and then the DS waits
+ to start recording for a time that depends
+ on the output latency.
+ */
+
+ first_recordable_frame += _session.worst_output_latency();
+ }
+
+ } else {
+
+ if (Config->get_punch_in()) {
+ first_recordable_frame += _roll_delay;
+ } else {
+ capture_start_frame -= _roll_delay;
+ }
+ }
+
+ }
+
+ if (recordable() && destructive()) {
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ RingBufferNPT<CaptureTransition>::rw_vector transvec;
+ (*chan)->capture_transition_buf->get_write_vector(&transvec);
+
+ if (transvec.len[0] > 0) {
+ transvec.buf[0]->type = CaptureStart;
+ transvec.buf[0]->capture_val = capture_start_frame;
+ (*chan)->capture_transition_buf->increment_write_ptr(1);
+ }
+ else {
+ // bad!
+ fatal << X_("programming error: capture_transition_buf is full on rec start! inconceivable!")
+ << endmsg;
+ }
+ }
+ }
+
+ } else if (!record_enabled() || !can_record) {
+
+ /* stop recording */
+
+ last_recordable_frame = transport_frame + _capture_offset;
+
+ if (_alignment_style == ExistingMaterial) {
+ last_recordable_frame += _session.worst_output_latency();
+ } else {
+ last_recordable_frame += _roll_delay;
+ }
+ }
+
+ last_possibly_recording = possibly_recording;
+}
+
+int
+AudioDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t offset, bool can_record, bool rec_monitors_input)
+{
+ uint32_t n;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ ChannelList::iterator chan;
+ int ret = -1;
+ nframes_t rec_offset = 0;
+ nframes_t rec_nframes = 0;
+ bool nominally_recording;
+ bool re = record_enabled ();
+ bool collect_playback = false;
+
+ /* if we've already processed the frames corresponding to this call,
+ just return. this allows multiple routes that are taking input
+ from this diskstream to call our ::process() method, but have
+ this stuff only happen once. more commonly, it allows both
+ the AudioTrack that is using this AudioDiskstream *and* the Session
+ to call process() without problems.
+ */
+
+ if (_processed) {
+ return 0;
+ }
+
+ commit_should_unlock = false;
+
+ if (!_io->active()) {
+ _processed = true;
+ return 0;
+ }
+
+ check_record_status (transport_frame, nframes, can_record);
+
+ nominally_recording = (can_record && re);
+
+ if (nframes == 0) {
+ _processed = true;
+ return 0;
+ }
+
+ /* This lock is held until the end of AudioDiskstream::commit, so these two functions
+ must always be called as a pair. The only exception is if this function
+ returns a non-zero value, in which case, ::commit should not be called.
+ */
+
+ // If we can't take the state lock return.
+ if (!state_lock.trylock()) {
+ return 1;
+ }
+ commit_should_unlock = true;
+ adjust_capture_position = 0;
+
+ for (chan = c->begin(); chan != c->end(); ++chan) {
+ (*chan)->current_capture_buffer = 0;
+ (*chan)->current_playback_buffer = 0;
+ }
+
+ if (nominally_recording || (_session.get_record_enabled() && Config->get_punch_in())) {
+ OverlapType ot;
+
+ // Safeguard against situations where process() goes haywire when autopunching and last_recordable_frame < first_recordable_frame
+ if (last_recordable_frame < first_recordable_frame) {
+ last_recordable_frame = max_frames;
+ }
+
+ ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
+
+ switch (ot) {
+ case OverlapNone:
+ rec_nframes = 0;
+ break;
+
+ case OverlapInternal:
+ /* ---------- recrange
+ |---| transrange
+ */
+ rec_nframes = nframes;
+ rec_offset = 0;
+ break;
+
+ case OverlapStart:
+ /* |--------| recrange
+ -----| transrange
+ */
+ rec_nframes = transport_frame + nframes - first_recordable_frame;
+ if (rec_nframes) {
+ rec_offset = first_recordable_frame - transport_frame;
+ }
+ break;
+
+ case OverlapEnd:
+ /* |--------| recrange
+ |-------- transrange
+ */
+ rec_nframes = last_recordable_frame - transport_frame;
+ rec_offset = 0;
+ break;
+
+ case OverlapExternal:
+ /* |--------| recrange
+ -------------- transrange
+ */
+ rec_nframes = last_recordable_frame - first_recordable_frame;
+ rec_offset = first_recordable_frame - transport_frame;
+ break;
+ }
+
+ if (rec_nframes && !was_recording) {
+ capture_captured = 0;
+ was_recording = true;
+ }
+ }
+
+
+ if (can_record && !_last_capture_regions.empty()) {
+ _last_capture_regions.clear ();
+ }
+
+ if (nominally_recording || rec_nframes) {
+
+ uint32_t limit = _io->n_inputs ().n_audio();
+
+ /* one or more ports could already have been removed from _io, but our
+ channel setup hasn't yet been updated. prevent us from trying to
+ use channels that correspond to missing ports. note that the
+ process callback (from which this is called) is always atomic
+ with respect to port removal/addition.
+ */
+
+ for (n = 0, chan = c->begin(); chan != c->end() && n < limit; ++chan, ++n) {
+
+ ChannelInfo* chaninfo (*chan);
+
+ chaninfo->capture_buf->get_write_vector (&chaninfo->capture_vector);
+
+ if (rec_nframes <= chaninfo->capture_vector.len[0]) {
+
+ chaninfo->current_capture_buffer = chaninfo->capture_vector.buf[0];
+
+ /* note: grab the entire port buffer, but only copy what we were supposed to for recording, and use
+ rec_offset
+ */
+
+ AudioPort* const ap = _io->audio_input(n);
+ assert(ap);
+ assert(rec_nframes <= ap->get_audio_buffer().capacity());
+ memcpy (chaninfo->current_capture_buffer, ap->get_audio_buffer().data(rec_nframes, offset + rec_offset), sizeof (Sample) * rec_nframes);
+
+ } else {
+
+ nframes_t total = chaninfo->capture_vector.len[0] + chaninfo->capture_vector.len[1];
+
+ if (rec_nframes > total) {
+ DiskOverrun ();
+ goto out;
+ }
+
+ AudioPort* const ap = _io->audio_input(n);
+ assert(ap);
+
+ Sample* buf = ap->get_audio_buffer().data(nframes, offset);
+ nframes_t first = chaninfo->capture_vector.len[0];
+
+ memcpy (chaninfo->capture_wrap_buffer, buf, sizeof (Sample) * first);
+ memcpy (chaninfo->capture_vector.buf[0], buf, sizeof (Sample) * first);
+ memcpy (chaninfo->capture_wrap_buffer+first, buf + first, sizeof (Sample) * (rec_nframes - first));
+ memcpy (chaninfo->capture_vector.buf[1], buf + first, sizeof (Sample) * (rec_nframes - first));
+
+ chaninfo->current_capture_buffer = chaninfo->capture_wrap_buffer;
+ }
+ }
+
+ } else {
+
+ if (was_recording) {
+ finish_capture (rec_monitors_input, c);
+ }
+
+ }
+
+ if (rec_nframes) {
+
+ /* data will be written to disk */
+
+ if (rec_nframes == nframes && rec_offset == 0) {
+
+ for (chan = c->begin(); chan != c->end(); ++chan) {
+ (*chan)->current_playback_buffer = (*chan)->current_capture_buffer;
+ }
+
+ playback_distance = nframes;
+
+ } else {
+
+
+ /* we can't use the capture buffer as the playback buffer, because
+ we recorded only a part of the current process' cycle data
+ for capture.
+ */
+
+ collect_playback = true;
+ }
+
+ adjust_capture_position = rec_nframes;
+
+ } else if (nominally_recording) {
+
+ /* can't do actual capture yet - waiting for latency effects to finish before we start*/
+
+ for (chan = c->begin(); chan != c->end(); ++chan) {
+ (*chan)->current_playback_buffer = (*chan)->current_capture_buffer;
+ }
+
+ playback_distance = nframes;
+
+ } else {
+
+ collect_playback = true;
+ }
+
+ if (collect_playback) {
+
+ /* we're doing playback */
+
+ nframes_t necessary_samples;
+
+ /* no varispeed playback if we're recording, because the output .... TBD */
+
+ if (rec_nframes == 0 && _actual_speed != 1.0f) {
+ necessary_samples = (nframes_t) floor ((nframes * fabs (_actual_speed))) + 1;
+ } else {
+ necessary_samples = nframes;
+ }
+
+ for (chan = c->begin(); chan != c->end(); ++chan) {
+ (*chan)->playback_buf->get_read_vector (&(*chan)->playback_vector);
+ }
+
+ n = 0;
+
+ for (chan = c->begin(); chan != c->end(); ++chan, ++n) {
+
+ ChannelInfo* chaninfo (*chan);
+
+ if (necessary_samples <= chaninfo->playback_vector.len[0]) {
+
+ chaninfo->current_playback_buffer = chaninfo->playback_vector.buf[0];
+
+ } else {
+ nframes_t total = chaninfo->playback_vector.len[0] + chaninfo->playback_vector.len[1];
+
+ if (necessary_samples > total) {
+ cerr << "underrun for " << _name << endl;
+ DiskUnderrun ();
+ goto out;
+
+ } else {
+
+ memcpy ((char *) chaninfo->playback_wrap_buffer, chaninfo->playback_vector.buf[0],
+ chaninfo->playback_vector.len[0] * sizeof (Sample));
+ memcpy (chaninfo->playback_wrap_buffer + chaninfo->playback_vector.len[0], chaninfo->playback_vector.buf[1],
+ (necessary_samples - chaninfo->playback_vector.len[0]) * sizeof (Sample));
+
+ chaninfo->current_playback_buffer = chaninfo->playback_wrap_buffer;
+ }
+ }
+ }
+
+ if (rec_nframes == 0 && _actual_speed != 1.0f && _actual_speed != -1.0f) {
+
+ uint64_t phase = last_phase;
+ int64_t phi_delta;
+ nframes_t i = 0;
+
+ // Linearly interpolate into the alt buffer
+ // using 40.24 fixp maths (swh)
+
+ if (phi != target_phi) {
+ phi_delta = ((int64_t)(target_phi - phi)) / nframes;
+ } else {
+ phi_delta = 0;
+ }
+
+ for (chan = c->begin(); chan != c->end(); ++chan) {
+
+ float fr;
+ ChannelInfo* chaninfo (*chan);
+
+ i = 0;
+ phase = last_phase;
+
+ for (nframes_t outsample = 0; outsample < nframes; ++outsample) {
+ i = phase >> 24;
+ fr = (phase & 0xFFFFFF) / 16777216.0f;
+ chaninfo->speed_buffer[outsample] =
+ chaninfo->current_playback_buffer[i] * (1.0f - fr) +
+ chaninfo->current_playback_buffer[i+1] * fr;
+ phase += phi + phi_delta;
+ }
+
+ chaninfo->current_playback_buffer = chaninfo->speed_buffer;
+ }
+
+ playback_distance = i; // + 1;
+ last_phase = (phase & 0xFFFFFF);
+
+ } else {
+ playback_distance = nframes;
+ }
+
+ phi = target_phi;
+
+ }
+
+ ret = 0;
+
+ out:
+ _processed = true;
+
+ if (ret) {
+
+ /* we're exiting with failure, so ::commit will not
+ be called. unlock the state lock.
+ */
+
+ commit_should_unlock = false;
+ state_lock.unlock();
+ }
+
+ return ret;
+}
+
+bool
+AudioDiskstream::commit (nframes_t nframes)
+{
+ bool need_butler = false;
+
+ if (!_io->active()) {
+ return false;
+ }
+
+ if (_actual_speed < 0.0) {
+ playback_sample -= playback_distance;
+ } else {
+ playback_sample += playback_distance;
+ }
+
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ (*chan)->playback_buf->increment_read_ptr (playback_distance);
+
+ if (adjust_capture_position) {
+ (*chan)->capture_buf->increment_write_ptr (adjust_capture_position);
+ }
+ }
+
+ if (adjust_capture_position != 0) {
+ capture_captured += adjust_capture_position;
+ adjust_capture_position = 0;
+ }
+
+ if (_slaved) {
+ if (_io && _io->active()) {
+ need_butler = c->front()->playback_buf->write_space() >= c->front()->playback_buf->bufsize() / 2;
+ } else {
+ need_butler = false;
+ }
+ } else {
+ if (_io && _io->active()) {
+ need_butler = c->front()->playback_buf->write_space() >= disk_io_chunk_frames
+ || c->front()->capture_buf->read_space() >= disk_io_chunk_frames;
+ } else {
+ need_butler = c->front()->capture_buf->read_space() >= disk_io_chunk_frames;
+ }
+ }
+
+ if (commit_should_unlock) {
+ state_lock.unlock();
+ }
+
+ _processed = false;
+
+ return need_butler;
+}
+
+void
+AudioDiskstream::set_pending_overwrite (bool yn)
+{
+ /* called from audio thread, so we can use the read ptr and playback sample as we wish */
+
+ pending_overwrite = yn;
+
+ overwrite_frame = playback_sample;
+ overwrite_offset = channels.reader()->front()->playback_buf->get_read_ptr();
+}
+
+int
+AudioDiskstream::overwrite_existing_buffers ()
+{
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ Sample* mixdown_buffer;
+ float* gain_buffer;
+ int ret = -1;
+ bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
+
+ overwrite_queued = false;
+
+ /* assume all are the same size */
+ nframes_t size = c->front()->playback_buf->bufsize();
+
+ mixdown_buffer = new Sample[size];
+ gain_buffer = new float[size];
+
+ /* reduce size so that we can fill the buffer correctly. */
+ size--;
+
+ uint32_t n=0;
+ nframes_t start;
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan, ++n) {
+
+ start = overwrite_frame;
+ nframes_t cnt = size;
+
+ /* to fill the buffer without resetting the playback sample, we need to
+ do it one or two chunks (normally two).
+
+ |----------------------------------------------------------------------|
+
+ ^
+ overwrite_offset
+ |<- second chunk->||<----------------- first chunk ------------------>|
+
+ */
+
+ nframes_t to_read = size - overwrite_offset;
+
+ if (read ((*chan)->playback_buf->buffer() + overwrite_offset, mixdown_buffer, gain_buffer, start, to_read, *chan, n, reversed)) {
+ error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"),
+ _id, size, playback_sample) << endmsg;
+ goto out;
+ }
+
+ if (cnt > to_read) {
+
+ cnt -= to_read;
+
+ if (read ((*chan)->playback_buf->buffer(), mixdown_buffer, gain_buffer,
+ start, cnt, *chan, n, reversed)) {
+ error << string_compose(_("AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"),
+ _id, size, playback_sample) << endmsg;
+ goto out;
+ }
+ }
+ }
+
+ ret = 0;
+
+ out:
+ pending_overwrite = false;
+ delete [] gain_buffer;
+ delete [] mixdown_buffer;
+ return ret;
+}
+
+int
+AudioDiskstream::seek (nframes_t frame, bool complete_refill)
+{
+ uint32_t n;
+ int ret = -1;
+ ChannelList::iterator chan;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ Glib::Mutex::Lock lm (state_lock);
+
+ for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+ (*chan)->playback_buf->reset ();
+ (*chan)->capture_buf->reset ();
+ }
+
+ /* can't rec-enable in destructive mode if transport is before start */
+
+ if (destructive() && record_enabled() && frame < _session.current_start_frame()) {
+ disengage_record_enable ();
+ }
+
+ playback_sample = frame;
+ file_frame = frame;
+
+ if (complete_refill) {
+ while ((ret = do_refill_with_alloc ()) > 0) ;
+ } else {
+ ret = do_refill_with_alloc ();
+ }
+
+ return ret;
+}
+
+int
+AudioDiskstream::can_internal_playback_seek (nframes_t distance)
+{
+ ChannelList::iterator chan;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ for (chan = c->begin(); chan != c->end(); ++chan) {
+ if ((*chan)->playback_buf->read_space() < distance) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int
+AudioDiskstream::internal_playback_seek (nframes_t distance)
+{
+ ChannelList::iterator chan;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ for (chan = c->begin(); chan != c->end(); ++chan) {
+ (*chan)->playback_buf->increment_read_ptr (distance);
+ }
+
+ first_recordable_frame += distance;
+ playback_sample += distance;
+
+ return 0;
+}
+
+int
+AudioDiskstream::read (Sample* buf, Sample* mixdown_buffer, float* gain_buffer, nframes_t& start, nframes_t cnt,
+ ChannelInfo* channel_info, int channel, bool reversed)
+{
+ nframes_t this_read = 0;
+ bool reloop = false;
+ nframes_t loop_end = 0;
+ nframes_t loop_start = 0;
+ nframes_t loop_length = 0;
+ nframes_t offset = 0;
+ Location *loc = 0;
+
+ /* XXX we don't currently play loops in reverse. not sure why */
+
+ if (!reversed) {
+
+ /* Make the use of a Location atomic for this read operation.
+
+ Note: Locations don't get deleted, so all we care about
+ when I say "atomic" is that we are always pointing to
+ the same one and using a start/length values obtained
+ just once.
+ */
+
+ if ((loc = loop_location) != 0) {
+ loop_start = loc->start();
+ loop_end = loc->end();
+ loop_length = loop_end - loop_start;
+ }
+
+ /* if we are looping, ensure that the first frame we read is at the correct
+ position within the loop.
+ */
+
+ if (loc && start >= loop_end) {
+ //cerr << "start adjusted from " << start;
+ start = loop_start + ((start - loop_start) % loop_length);
+ //cerr << "to " << start << endl;
+ }
+
+ //cerr << "start is " << start << " loopstart: " << loop_start << " loopend: " << loop_end << endl;
+ }
+
+ while (cnt) {
+
+ if (reversed) {
+ start -= cnt;
+ }
+
+ /* take any loop into account. we can't read past the end of the loop. */
+
+ if (loc && (loop_end - start < cnt)) {
+ this_read = loop_end - start;
+ //cerr << "reloop true: thisread: " << this_read << " cnt: " << cnt << endl;
+ reloop = true;
+ } else {
+ reloop = false;
+ this_read = cnt;
+ }
+
+ if (this_read == 0) {
+ break;
+ }
+
+ this_read = min(cnt,this_read);
+
+ if (audio_playlist()->read (buf+offset, mixdown_buffer, gain_buffer, start, this_read, channel) != this_read) {
+ error << string_compose(_("AudioDiskstream %1: cannot read %2 from playlist at frame %3"), _id, this_read,
+ start) << endmsg;
+ return -1;
+ }
+
+ _read_data_count = _playlist->read_data_count();
+
+ if (reversed) {
+
+ swap_by_ptr (buf, buf + this_read - 1);
+
+ } else {
+
+ /* if we read to the end of the loop, go back to the beginning */
+
+ if (reloop) {
+ start = loop_start;
+ } else {
+ start += this_read;
+ }
+ }
+
+ cnt -= this_read;
+ offset += this_read;
+ }
+
+ return 0;
+}
+
+int
+AudioDiskstream::do_refill_with_alloc ()
+{
+ Sample* mix_buf = new Sample[disk_io_chunk_frames];
+ float* gain_buf = new float[disk_io_chunk_frames];
+
+ int ret = _do_refill(mix_buf, gain_buf);
+
+ delete [] mix_buf;
+ delete [] gain_buf;
+
+ return ret;
+}
+
+int
+AudioDiskstream::_do_refill (Sample* mixdown_buffer, float* gain_buffer)
+{
+ int32_t ret = 0;
+ nframes_t to_read;
+ RingBufferNPT<Sample>::rw_vector vector;
+ bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
+ nframes_t total_space;
+ nframes_t zero_fill;
+ uint32_t chan_n;
+ ChannelList::iterator i;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ nframes_t ts;
+
+ if (c->empty()) {
+ return 0;
+ }
+
+ assert(mixdown_buffer);
+ assert(gain_buffer);
+
+ vector.buf[0] = 0;
+ vector.len[0] = 0;
+ vector.buf[1] = 0;
+ vector.len[1] = 0;
+
+ c->front()->playback_buf->get_write_vector (&vector);
+
+ if ((total_space = vector.len[0] + vector.len[1]) == 0) {
+ return 0;
+ }
+
+ /* if there are 2+ chunks of disk i/o possible for
+ this track, let the caller know so that it can arrange
+ for us to be called again, ASAP.
+ */
+
+ if (total_space >= (_slaved?3:2) * disk_io_chunk_frames) {
+ ret = 1;
+ }
+
+ /* if we're running close to normal speed and there isn't enough
+ space to do disk_io_chunk_frames of I/O, then don't bother.
+
+ at higher speeds, just do it because the sync between butler
+ and audio thread may not be good enough.
+ */
+
+ if ((total_space < disk_io_chunk_frames) && fabs (_actual_speed) < 2.0f) {
+ return 0;
+ }
+
+ /* when slaved, don't try to get too close to the read pointer. this
+ leaves space for the buffer reversal to have something useful to
+ work with.
+ */
+
+ if (_slaved && total_space < (c->front()->playback_buf->bufsize() / 2)) {
+ return 0;
+ }
+
+ /* never do more than disk_io_chunk_frames worth of disk input per call (limit doesn't apply for memset) */
+
+ total_space = min (disk_io_chunk_frames, total_space);
+
+ if (reversed) {
+
+ if (file_frame == 0) {
+
+ /* at start: nothing to do but fill with silence */
+
+ for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
+
+ ChannelInfo* chan (*i);
+ chan->playback_buf->get_write_vector (&vector);
+ memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]);
+ if (vector.len[1]) {
+ memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]);
+ }
+ chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]);
+ }
+ return 0;
+ }
+
+ if (file_frame < total_space) {
+
+ /* too close to the start: read what we can,
+ and then zero fill the rest
+ */
+
+ zero_fill = total_space - file_frame;
+ total_space = file_frame;
+ file_frame = 0;
+
+ } else {
+
+ zero_fill = 0;
+ }
+
+ } else {
+
+ if (file_frame == max_frames) {
+
+ /* at end: nothing to do but fill with silence */
+
+ for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
+
+ ChannelInfo* chan (*i);
+ chan->playback_buf->get_write_vector (&vector);
+ memset (vector.buf[0], 0, sizeof(Sample) * vector.len[0]);
+ if (vector.len[1]) {
+ memset (vector.buf[1], 0, sizeof(Sample) * vector.len[1]);
+ }
+ chan->playback_buf->increment_write_ptr (vector.len[0] + vector.len[1]);
+ }
+ return 0;
+ }
+
+ if (file_frame > max_frames - total_space) {
+
+ /* to close to the end: read what we can, and zero fill the rest */
+
+ zero_fill = total_space - (max_frames - file_frame);
+ total_space = max_frames - file_frame;
+
+ } else {
+ zero_fill = 0;
+ }
+ }
+
+ nframes_t file_frame_tmp = 0;
+
+ for (chan_n = 0, i = c->begin(); i != c->end(); ++i, ++chan_n) {
+
+ ChannelInfo* chan (*i);
+ Sample* buf1;
+ Sample* buf2;
+ nframes_t len1, len2;
+
+ chan->playback_buf->get_write_vector (&vector);
+
+ if (vector.len[0] > disk_io_chunk_frames) {
+
+ /* we're not going to fill the first chunk, so certainly do not bother with the
+ other part. it won't be connected with the part we do fill, as in:
+
+ .... => writable space
+ ++++ => readable space
+ ^^^^ => 1 x disk_io_chunk_frames that would be filled
+
+ |......|+++++++++++++|...............................|
+ buf1 buf0
+ ^^^^^^^^^^^^^^^
+
+
+ So, just pretend that the buf1 part isn't there.
+
+ */
+
+ vector.buf[1] = 0;
+ vector.len[1] = 0;
+
+ }
+
+ ts = total_space;
+ file_frame_tmp = file_frame;
+
+ buf1 = vector.buf[0];
+ len1 = vector.len[0];
+ buf2 = vector.buf[1];
+ len2 = vector.len[1];
+
+ to_read = min (ts, len1);
+ to_read = min (to_read, disk_io_chunk_frames);
+
+ if (to_read) {
+
+ if (read (buf1, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan, chan_n, reversed)) {
+ ret = -1;
+ goto out;
+ }
+
+ chan->playback_buf->increment_write_ptr (to_read);
+ ts -= to_read;
+ }
+
+ to_read = min (ts, len2);
+
+ if (to_read) {
+
+ /* we read all of vector.len[0], but it wasn't an entire disk_io_chunk_frames of data,
+ so read some or all of vector.len[1] as well.
+ */
+
+ if (read (buf2, mixdown_buffer, gain_buffer, file_frame_tmp, to_read, chan, chan_n, reversed)) {
+ ret = -1;
+ goto out;
+ }
+
+ chan->playback_buf->increment_write_ptr (to_read);
+ }
+
+ if (zero_fill) {
+ /* do something */
+ }
+
+ }
+
+ file_frame = file_frame_tmp;
+
+ out:
+
+ return ret;
+}
+
+/** Flush pending data to disk.
+ *
+ * Important note: this function will write *AT MOST* disk_io_chunk_frames
+ * of data to disk. it will never write more than that. If it writes that
+ * much and there is more than that waiting to be written, it will return 1,
+ * otherwise 0 on success or -1 on failure.
+ *
+ * If there is less than disk_io_chunk_frames to be written, no data will be
+ * written at all unless @a force_flush is true.
+ */
+int
+AudioDiskstream::do_flush (Session::RunContext context, bool force_flush)
+{
+ uint32_t to_write;
+ int32_t ret = 0;
+ RingBufferNPT<Sample>::rw_vector vector;
+ RingBufferNPT<CaptureTransition>::rw_vector transvec;
+ nframes_t total;
+
+ _write_data_count = 0;
+
+ transvec.buf[0] = 0;
+ transvec.buf[1] = 0;
+ vector.buf[0] = 0;
+ vector.buf[1] = 0;
+
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ (*chan)->capture_buf->get_read_vector (&vector);
+
+ total = vector.len[0] + vector.len[1];
+
+ if (total == 0 || (total < disk_io_chunk_frames && !force_flush && was_recording)) {
+ goto out;
+ }
+
+ /* if there are 2+ chunks of disk i/o possible for
+ this track, let the caller know so that it can arrange
+ for us to be called again, ASAP.
+
+ if we are forcing a flush, then if there is* any* extra
+ work, let the caller know.
+
+ if we are no longer recording and there is any extra work,
+ let the caller know too.
+ */
+
+ if (total >= 2 * disk_io_chunk_frames || ((force_flush || !was_recording) && total > disk_io_chunk_frames)) {
+ ret = 1;
+ }
+
+ to_write = min (disk_io_chunk_frames, (nframes_t) vector.len[0]);
+
+ // check the transition buffer when recording destructive
+ // important that we get this after the capture buf
+
+ if (destructive()) {
+ (*chan)->capture_transition_buf->get_read_vector(&transvec);
+ size_t transcount = transvec.len[0] + transvec.len[1];
+ bool have_start = false;
+ size_t ti;
+
+ for (ti=0; ti < transcount; ++ti) {
+ CaptureTransition & captrans = (ti < transvec.len[0]) ? transvec.buf[0][ti] : transvec.buf[1][ti-transvec.len[0]];
+
+ if (captrans.type == CaptureStart) {
+ // by definition, the first data we got above represents the given capture pos
+
+ (*chan)->write_source->mark_capture_start (captrans.capture_val);
+ (*chan)->curr_capture_cnt = 0;
+
+ have_start = true;
+ }
+ else if (captrans.type == CaptureEnd) {
+
+ // capture end, the capture_val represents total frames in capture
+
+ if (captrans.capture_val <= (*chan)->curr_capture_cnt + to_write) {
+
+ // shorten to make the write a perfect fit
+ uint32_t nto_write = (captrans.capture_val - (*chan)->curr_capture_cnt);
+
+ if (nto_write < to_write) {
+ ret = 1; // should we?
+ }
+ to_write = nto_write;
+
+ (*chan)->write_source->mark_capture_end ();
+
+ // increment past this transition, but go no further
+ ++ti;
+ break;
+ }
+ else {
+ // actually ends just beyond this chunk, so force more work
+ ret = 1;
+ break;
+ }
+ }
+ }
+
+ if (ti > 0) {
+ (*chan)->capture_transition_buf->increment_read_ptr(ti);
+ }
+ }
+
+ if ((!(*chan)->write_source) || (*chan)->write_source->write (vector.buf[0], to_write) != to_write) {
+ error << string_compose(_("AudioDiskstream %1: cannot write to disk"), _id) << endmsg;
+ return -1;
+ }
+
+ (*chan)->capture_buf->increment_read_ptr (to_write);
+ (*chan)->curr_capture_cnt += to_write;
+
+ if ((to_write == vector.len[0]) && (total > to_write) && (to_write < disk_io_chunk_frames) && !destructive()) {
+
+ /* we wrote all of vector.len[0] but it wasn't an entire
+ disk_io_chunk_frames of data, so arrange for some part
+ of vector.len[1] to be flushed to disk as well.
+ */
+
+ to_write = min ((nframes_t)(disk_io_chunk_frames - to_write), (nframes_t) vector.len[1]);
+
+ if ((*chan)->write_source->write (vector.buf[1], to_write) != to_write) {
+ error << string_compose(_("AudioDiskstream %1: cannot write to disk"), _id) << endmsg;
+ return -1;
+ }
+
+ _write_data_count += (*chan)->write_source->write_data_count();
+
+ (*chan)->capture_buf->increment_read_ptr (to_write);
+ (*chan)->curr_capture_cnt += to_write;
+ }
+ }
+
+ out:
+ return ret;
+}
+
+void
+AudioDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_capture)
+{
+ uint32_t buffer_position;
+ bool more_work = true;
+ int err = 0;
+ boost::shared_ptr<AudioRegion> region;
+ nframes_t total_capture;
+ SourceList srcs;
+ SourceList::iterator src;
+ ChannelList::iterator chan;
+ vector<CaptureInfo*>::iterator ci;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ uint32_t n = 0;
+ bool mark_write_completed = false;
+
+ finish_capture (true, c);
+
+ /* butler is already stopped, but there may be work to do
+ to flush remaining data to disk.
+ */
+
+ while (more_work && !err) {
+ switch (do_flush (Session::TransportContext, true)) {
+ case 0:
+ more_work = false;
+ break;
+ case 1:
+ break;
+ case -1:
+ error << string_compose(_("AudioDiskstream \"%1\": cannot flush captured data to disk!"), _name) << endmsg;
+ err++;
+ }
+ }
+
+ /* XXX is there anything we can do if err != 0 ? */
+ Glib::Mutex::Lock lm (capture_info_lock);
+
+ if (capture_info.empty()) {
+ return;
+ }
+
+ if (abort_capture) {
+
+ if (destructive()) {
+ goto outout;
+ }
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ if ((*chan)->write_source) {
+
+ (*chan)->write_source->mark_for_remove ();
+ (*chan)->write_source->drop_references ();
+ (*chan)->write_source.reset ();
+ }
+
+ /* new source set up in "out" below */
+ }
+
+ goto out;
+ }
+
+ for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
+ total_capture += (*ci)->frames;
+ }
+
+ /* figure out the name for this take */
+
+ for (n = 0, chan = c->begin(); chan != c->end(); ++chan, ++n) {
+
+ boost::shared_ptr<AudioFileSource> s = (*chan)->write_source;
+
+ if (s) {
+ srcs.push_back (s);
+ s->update_header (capture_info.front()->start, when, twhen);
+ s->set_captured_for (_name);
+ s->mark_immutable ();
+ if (Config->get_auto_analyse_audio()) {
+ Analyser::queue_source_for_analysis (s, true);
+ }
+ }
+ }
+
+ /* destructive tracks have a single, never changing region */
+
+ if (destructive()) {
+
+ /* send a signal that any UI can pick up to do the right thing. there is
+ a small problem here in that a UI may need the peak data to be ready
+ for the data that was recorded and this isn't interlocked with that
+ process. this problem is deferred to the UI.
+ */
+
+ _playlist->Modified();
+
+ } else {
+
+ string whole_file_region_name;
+ whole_file_region_name = region_name_from_path (c->front()->write_source->name(), true);
+
+ /* Register a new region with the Session that
+ describes the entire source. Do this first
+ so that any sub-regions will obviously be
+ children of this one (later!)
+ */
+
+ try {
+ boost::shared_ptr<Region> rx (RegionFactory::create (srcs, c->front()->write_source->last_capture_start_frame(), total_capture,
+ whole_file_region_name,
+ 0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile)));
+
+ region = boost::dynamic_pointer_cast<AudioRegion> (rx);
+ region->special_set_position (capture_info.front()->start);
+ }
+
+
+ catch (failed_constructor& err) {
+ error << string_compose(_("%1: could not create region for complete audio file"), _name) << endmsg;
+ /* XXX what now? */
+ }
+
+ _last_capture_regions.push_back (region);
+
+ // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
+
+ XMLNode &before = _playlist->get_state();
+ _playlist->freeze ();
+
+ for (buffer_position = c->front()->write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
+
+ string region_name;
+
+ _session.region_name (region_name, whole_file_region_name, false);
+
+ // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add region " << region_name << endl;
+
+ try {
+ boost::shared_ptr<Region> rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name));
+ region = boost::dynamic_pointer_cast<AudioRegion> (rx);
+ }
+
+ catch (failed_constructor& err) {
+ error << _("AudioDiskstream: could not create region for captured audio!") << endmsg;
+ continue; /* XXX is this OK? */
+ }
+
+ region->GoingAway.connect (bind (mem_fun (*this, &Diskstream::remove_region_from_last_capture), boost::weak_ptr<Region>(region)));
+
+ _last_capture_regions.push_back (region);
+
+ i_am_the_modifier++;
+ _playlist->add_region (region, (*ci)->start);
+ i_am_the_modifier--;
+
+ buffer_position += (*ci)->frames;
+ }
+
+ _playlist->thaw ();
+ XMLNode &after = _playlist->get_state();
+ _session.add_command (new MementoCommand<Playlist>(*_playlist, &before, &after));
+ }
+
+ mark_write_completed = true;
+
+ out:
+ reset_write_sources (mark_write_completed);
+
+ outout:
+
+ for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
+ delete *ci;
+ }
+
+ capture_info.clear ();
+ capture_start_frame = 0;
+}
+
+void
+AudioDiskstream::transport_looped (nframes_t transport_frame)
+{
+ if (was_recording) {
+ // all we need to do is finish this capture, with modified capture length
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ // adjust the capture length knowing that the data will be recorded to disk
+ // only necessary after the first loop where we're recording
+ if (capture_info.size() == 0) {
+ capture_captured += _capture_offset;
+
+ if (_alignment_style == ExistingMaterial) {
+ capture_captured += _session.worst_output_latency();
+ } else {
+ capture_captured += _roll_delay;
+ }
+ }
+
+ finish_capture (true, c);
+
+ // the next region will start recording via the normal mechanism
+ // we'll set the start position to the current transport pos
+ // no latency adjustment or capture offset needs to be made, as that already happened the first time
+ capture_start_frame = transport_frame;
+ first_recordable_frame = transport_frame; // mild lie
+ last_recordable_frame = max_frames;
+ was_recording = true;
+
+ if (recordable() && destructive()) {
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ RingBufferNPT<CaptureTransition>::rw_vector transvec;
+ (*chan)->capture_transition_buf->get_write_vector(&transvec);
+
+ if (transvec.len[0] > 0) {
+ transvec.buf[0]->type = CaptureStart;
+ transvec.buf[0]->capture_val = capture_start_frame;
+ (*chan)->capture_transition_buf->increment_write_ptr(1);
+ }
+ else {
+ // bad!
+ fatal << X_("programming error: capture_transition_buf is full on rec loop! inconceivable!")
+ << endmsg;
+ }
+ }
+ }
+
+ }
+}
+
+void
+AudioDiskstream::finish_capture (bool rec_monitors_input, boost::shared_ptr<ChannelList> c)
+{
+ was_recording = false;
+
+ if (capture_captured == 0) {
+ return;
+ }
+
+ if (recordable() && destructive()) {
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ RingBufferNPT<CaptureTransition>::rw_vector transvec;
+ (*chan)->capture_transition_buf->get_write_vector(&transvec);
+
+ if (transvec.len[0] > 0) {
+ transvec.buf[0]->type = CaptureEnd;
+ transvec.buf[0]->capture_val = capture_captured;
+ (*chan)->capture_transition_buf->increment_write_ptr(1);
+ }
+ else {
+ // bad!
+ fatal << string_compose (_("programmer error: %1"), X_("capture_transition_buf is full when stopping record! inconceivable!")) << endmsg;
+ }
+ }
+ }
+
+
+ CaptureInfo* ci = new CaptureInfo;
+
+ ci->start = capture_start_frame;
+ ci->frames = capture_captured;
+
+ /* XXX theoretical race condition here. Need atomic exchange ?
+ However, the circumstances when this is called right
+ now (either on record-disable or transport_stopped)
+ mean that no actual race exists. I think ...
+ We now have a capture_info_lock, but it is only to be used
+ to synchronize in the transport_stop and the capture info
+ accessors, so that invalidation will not occur (both non-realtime).
+ */
+
+ // cerr << "Finish capture, add new CI, " << ci->start << '+' << ci->frames << endl;
+
+ capture_info.push_back (ci);
+ capture_captured = 0;
+
+ /* now we've finished a capture, reset first_recordable_frame for next time */
+ first_recordable_frame = max_frames;
+}
+
+void
+AudioDiskstream::set_record_enabled (bool yn)
+{
+ if (!recordable() || !_session.record_enabling_legal() || _io->n_inputs().n_audio() == 0) {
+ return;
+ }
+
+ /* can't rec-enable in destructive mode if transport is before start */
+
+ if (destructive() && yn && _session.transport_frame() < _session.current_start_frame()) {
+ return;
+ }
+
+ if (yn && channels.reader()->front()->source == 0) {
+
+ /* pick up connections not initiated *from* the IO object
+ we're associated with.
+ */
+
+ get_input_sources ();
+ }
+
+ /* yes, i know that this not proof against race conditions, but its
+ good enough. i think.
+ */
+
+ if (record_enabled() != yn) {
+ if (yn) {
+ engage_record_enable ();
+ } else {
+ disengage_record_enable ();
+ }
+ }
+}
+
+void
+AudioDiskstream::engage_record_enable ()
+{
+ bool rolling = _session.transport_speed() != 0.0f;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ g_atomic_int_set (&_record_enabled, 1);
+ capturing_sources.clear ();
+
+ if (Config->get_monitoring_model() == HardwareMonitoring) {
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ if ((*chan)->source) {
+ (*chan)->source->ensure_monitor_input (!(Config->get_auto_input() && rolling));
+ }
+ capturing_sources.push_back ((*chan)->write_source);
+ (*chan)->write_source->mark_streaming_write_started ();
+ }
+
+ } else {
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ capturing_sources.push_back ((*chan)->write_source);
+ (*chan)->write_source->mark_streaming_write_started ();
+ }
+ }
+
+ RecordEnableChanged (); /* EMIT SIGNAL */
+}
+
+void
+AudioDiskstream::disengage_record_enable ()
+{
+ g_atomic_int_set (&_record_enabled, 0);
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ if (Config->get_monitoring_model() == HardwareMonitoring) {
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ if ((*chan)->source) {
+ (*chan)->source->ensure_monitor_input (false);
+ }
+ }
+ }
+ capturing_sources.clear ();
+ RecordEnableChanged (); /* EMIT SIGNAL */
+}
+
+XMLNode&
+AudioDiskstream::get_state ()
+{
+ XMLNode* node = new XMLNode ("AudioDiskstream");
+ char buf[64] = "";
+ LocaleGuard lg (X_("POSIX"));
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ node->add_property ("flags", enum_2_string (_flags));
+
+ snprintf (buf, sizeof(buf), "%zd", c->size());
+ node->add_property ("channels", buf);
+
+ node->add_property ("playlist", _playlist->name());
+
+ snprintf (buf, sizeof(buf), "%.12g", _visible_speed);
+ node->add_property ("speed", buf);
+
+ node->add_property("name", _name);
+ id().print (buf, sizeof (buf));
+ node->add_property("id", buf);
+
+ if (!capturing_sources.empty() && _session.get_record_enabled()) {
+
+ XMLNode* cs_child = new XMLNode (X_("CapturingSources"));
+ XMLNode* cs_grandchild;
+
+ for (vector<boost::shared_ptr<AudioFileSource> >::iterator i = capturing_sources.begin(); i != capturing_sources.end(); ++i) {
+ cs_grandchild = new XMLNode (X_("file"));
+ cs_grandchild->add_property (X_("path"), (*i)->path());
+ cs_child->add_child_nocopy (*cs_grandchild);
+ }
+
+ /* store the location where capture will start */
+
+ Location* pi;
+
+ if (Config->get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
+ snprintf (buf, sizeof (buf), "%" PRIu32, pi->start());
+ } else {
+ snprintf (buf, sizeof (buf), "%" PRIu32, _session.transport_frame());
+ }
+
+ cs_child->add_property (X_("at"), buf);
+ node->add_child_nocopy (*cs_child);
+ }
+
+ if (_extra_xml) {
+ node->add_child_copy (*_extra_xml);
+ }
+
+ return* node;
+}
+
+int
+AudioDiskstream::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+ XMLNodeList nlist = node.children();
+ XMLNodeIterator niter;
+ uint32_t nchans = 1;
+ XMLNode* capture_pending_node = 0;
+ LocaleGuard lg (X_("POSIX"));
+
+ in_set_state = true;
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == IO::state_node_name) {
+ deprecated_io_node = new XMLNode (**niter);
+ }
+
+ if ((*niter)->name() == X_("CapturingSources")) {
+ capture_pending_node = *niter;
+ }
+ }
+
+ /* prevent write sources from being created */
+
+ in_set_state = true;
+
+ if ((prop = node.property ("name")) != 0) {
+ _name = prop->value();
+ }
+
+ if (deprecated_io_node) {
+ if ((prop = deprecated_io_node->property ("id")) != 0) {
+ _id = prop->value ();
+ }
+ } else {
+ if ((prop = node.property ("id")) != 0) {
+ _id = prop->value ();
+ }
+ }
+
+ if ((prop = node.property ("flags")) != 0) {
+ _flags = Flag (string_2_enum (prop->value(), _flags));
+ }
+
+ if ((prop = node.property ("channels")) != 0) {
+ nchans = atoi (prop->value().c_str());
+ }
+
+ // create necessary extra channels
+ // we are always constructed with one and we always need one
+
+ _n_channels.set(DataType::AUDIO, channels.reader()->size());
+
+ if (nchans > _n_channels.n_audio()) {
+
+ add_channel (nchans - _n_channels.n_audio());
+ IO::PortCountChanged(_n_channels);
+
+ } else if (nchans < _n_channels.n_audio()) {
+
+ remove_channel (_n_channels.n_audio() - nchans);
+ }
+
+ if ((prop = node.property ("playlist")) == 0) {
+ return -1;
+ }
+
+ {
+ bool had_playlist = (_playlist != 0);
+
+ if (find_and_use_playlist (prop->value())) {
+ return -1;
+ }
+
+ if (!had_playlist) {
+ _playlist->set_orig_diskstream_id (_id);
+ }
+
+ if (!destructive() && capture_pending_node) {
+ /* destructive streams have one and only one source per channel,
+ and so they never end up in pending capture in any useful
+ sense.
+ */
+ use_pending_capture_data (*capture_pending_node);
+ }
+
+ }
+
+ if ((prop = node.property ("speed")) != 0) {
+ double sp = atof (prop->value().c_str());
+
+ if (realtime_set_speed (sp, false)) {
+ non_realtime_set_speed ();
+ }
+ }
+
+ in_set_state = false;
+
+ /* make sure this is clear before we do anything else */
+
+ capturing_sources.clear ();
+
+ /* write sources are handled when we handle the input set
+ up of the IO that owns this DS (::non_realtime_input_change())
+ */
+
+ return 0;
+}
+
+int
+AudioDiskstream::use_new_write_source (uint32_t n)
+{
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ if (!recordable()) {
+ return 1;
+ }
+
+ if (n >= c->size()) {
+ error << string_compose (_("AudioDiskstream: channel %1 out of range"), n) << endmsg;
+ return -1;
+ }
+
+ ChannelInfo* chan = (*c)[n];
+
+ if (chan->write_source) {
+ chan->write_source->done_with_peakfile_writes ();
+ chan->write_source->set_allow_remove_if_empty (true);
+ chan->write_source.reset ();
+ }
+
+ try {
+ if ((chan->write_source = _session.create_audio_source_for_session (*this, n, destructive())) == 0) {
+ throw failed_constructor();
+ }
+ }
+
+ catch (failed_constructor &err) {
+ error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg;
+ chan->write_source.reset ();
+ return -1;
+ }
+
+ /* do not remove destructive files even if they are empty */
+
+ chan->write_source->set_allow_remove_if_empty (!destructive());
+
+ return 0;
+}
+
+void
+AudioDiskstream::reset_write_sources (bool mark_write_complete, bool force)
+{
+ ChannelList::iterator chan;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ uint32_t n;
+
+ if (!recordable()) {
+ return;
+ }
+
+ capturing_sources.clear ();
+
+ for (chan = c->begin(), n = 0; chan != c->end(); ++chan, ++n) {
+ if (!destructive()) {
+
+ if ((*chan)->write_source && mark_write_complete) {
+ (*chan)->write_source->mark_streaming_write_completed ();
+ }
+ use_new_write_source (n);
+
+ if (record_enabled()) {
+ capturing_sources.push_back ((*chan)->write_source);
+ }
+
+ } else {
+ if ((*chan)->write_source == 0) {
+ use_new_write_source (n);
+ }
+ }
+ }
+
+ if (destructive()) {
+
+ /* we now have all our write sources set up, so create the
+ playlist's single region.
+ */
+
+ if (_playlist->empty()) {
+ setup_destructive_playlist ();
+ }
+ }
+}
+
+int
+AudioDiskstream::rename_write_sources ()
+{
+ ChannelList::iterator chan;
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ uint32_t n;
+
+ for (chan = c->begin(), n = 0; chan != c->end(); ++chan, ++n) {
+ if ((*chan)->write_source != 0) {
+ (*chan)->write_source->set_source_name (_name, destructive());
+ /* XXX what to do if one of them fails ? */
+ }
+ }
+
+ return 0;
+}
+
+void
+AudioDiskstream::set_block_size (nframes_t nframes)
+{
+ if (_session.get_block_size() > speed_buffer_size) {
+ speed_buffer_size = _session.get_block_size();
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ if ((*chan)->speed_buffer) delete [] (*chan)->speed_buffer;
+ (*chan)->speed_buffer = new Sample[speed_buffer_size];
+ }
+ }
+ allocate_temporary_buffers ();
+}
+
+void
+AudioDiskstream::allocate_temporary_buffers ()
+{
+ /* make sure the wrap buffer is at least large enough to deal
+ with the speeds up to 1.2, to allow for micro-variation
+ when slaving to MTC, SMPTE etc.
+ */
+
+ double sp = max (fabsf (_actual_speed), 1.2f);
+ nframes_t required_wrap_size = (nframes_t) floor (_session.get_block_size() * sp) + 1;
+
+ if (required_wrap_size > wrap_buffer_size) {
+
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ if ((*chan)->playback_wrap_buffer) delete [] (*chan)->playback_wrap_buffer;
+ (*chan)->playback_wrap_buffer = new Sample[required_wrap_size];
+ if ((*chan)->capture_wrap_buffer) delete [] (*chan)->capture_wrap_buffer;
+ (*chan)->capture_wrap_buffer = new Sample[required_wrap_size];
+ }
+
+ wrap_buffer_size = required_wrap_size;
+ }
+}
+
+void
+AudioDiskstream::monitor_input (bool yn)
+{
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+
+ if ((*chan)->source) {
+ (*chan)->source->ensure_monitor_input (yn);
+ }
+ }
+}
+
+void
+AudioDiskstream::set_align_style_from_io ()
+{
+ bool have_physical = false;
+
+ if (_io == 0) {
+ return;
+ }
+
+ get_input_sources ();
+
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ for (ChannelList::iterator chan = c->begin(); chan != c->end(); ++chan) {
+ if ((*chan)->source && (*chan)->source->flags() & JackPortIsPhysical) {
+ have_physical = true;
+ break;
+ }
+ }
+
+ if (have_physical) {
+ set_align_style (ExistingMaterial);
+ } else {
+ set_align_style (CaptureTime);
+ }
+}
+
+int
+AudioDiskstream::add_channel_to (boost::shared_ptr<ChannelList> c, uint32_t how_many)
+{
+ while (how_many--) {
+ c->push_back (new ChannelInfo(_session.audio_diskstream_buffer_size(), speed_buffer_size, wrap_buffer_size));
+ }
+
+ _n_channels.set(DataType::AUDIO, c->size());
+
+ return 0;
+}
+
+int
+AudioDiskstream::add_channel (uint32_t how_many)
+{
+ RCUWriter<ChannelList> writer (channels);
+ boost::shared_ptr<ChannelList> c = writer.get_copy();
+
+ return add_channel_to (c, how_many);
+}
+
+int
+AudioDiskstream::remove_channel_from (boost::shared_ptr<ChannelList> c, uint32_t how_many)
+{
+ while (how_many-- && !c->empty()) {
+ delete c->back();
+ c->pop_back();
+ }
+
+ _n_channels.set(DataType::AUDIO, c->size());
+
+ return 0;
+}
+
+int
+AudioDiskstream::remove_channel (uint32_t how_many)
+{
+ RCUWriter<ChannelList> writer (channels);
+ boost::shared_ptr<ChannelList> c = writer.get_copy();
+
+ return remove_channel_from (c, how_many);
+}
+
+float
+AudioDiskstream::playback_buffer_load () const
+{
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ return (float) ((double) c->front()->playback_buf->read_space()/
+ (double) c->front()->playback_buf->bufsize());
+}
+
+float
+AudioDiskstream::capture_buffer_load () const
+{
+ boost::shared_ptr<ChannelList> c = channels.reader();
+
+ return (float) ((double) c->front()->capture_buf->write_space()/
+ (double) c->front()->capture_buf->bufsize());
+}
+
+int
+AudioDiskstream::use_pending_capture_data (XMLNode& node)
+{
+ const XMLProperty* prop;
+ XMLNodeList nlist = node.children();
+ XMLNodeIterator niter;
+ boost::shared_ptr<AudioFileSource> fs;
+ boost::shared_ptr<AudioFileSource> first_fs;
+ SourceList pending_sources;
+ nframes_t position;
+
+ if ((prop = node.property (X_("at"))) == 0) {
+ return -1;
+ }
+
+ if (sscanf (prop->value().c_str(), "%" PRIu32, &position) != 1) {
+ return -1;
+ }
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == X_("file")) {
+
+ if ((prop = (*niter)->property (X_("path"))) == 0) {
+ continue;
+ }
+
+ // This protects sessions from errant CapturingSources in stored sessions
+ struct stat sbuf;
+ if (stat (prop->value().c_str(), &sbuf)) {
+ continue;
+ }
+
+ try {
+ fs = boost::dynamic_pointer_cast<AudioFileSource> (
+ SourceFactory::createWritable (DataType::AUDIO, _session, prop->value(), false, _session.frame_rate()));
+ }
+
+ catch (failed_constructor& err) {
+ error << string_compose (_("%1: cannot restore pending capture source file %2"),
+ _name, prop->value())
+ << endmsg;
+ return -1;
+ }
+
+ pending_sources.push_back (fs);
+
+ if (first_fs == 0) {
+ first_fs = fs;
+ }
+
+ fs->set_captured_for (_name);
+ }
+ }
+
+ if (pending_sources.size() == 0) {
+ /* nothing can be done */
+ return 1;
+ }
+
+ if (pending_sources.size() != _n_channels.n_audio()) {
+ error << string_compose (_("%1: incorrect number of pending sources listed - ignoring them all"), _name)
+ << endmsg;
+ return -1;
+ }
+
+ boost::shared_ptr<AudioRegion> region;
+
+ try {
+ region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(),
+ region_name_from_path (first_fs->name(), true),
+ 0, AudioRegion::Flag (AudioRegion::DefaultFlags|AudioRegion::Automatic|AudioRegion::WholeFile)));
+ region->special_set_position (0);
+ }
+
+ catch (failed_constructor& err) {
+ error << string_compose (_("%1: cannot create whole-file region from pending capture sources"),
+ _name)
+ << endmsg;
+
+ return -1;
+ }
+
+ try {
+ region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, 0, first_fs->length(), region_name_from_path (first_fs->name(), true)));
+ }
+
+ catch (failed_constructor& err) {
+ error << string_compose (_("%1: cannot create region from pending capture sources"),
+ _name)
+ << endmsg;
+
+ return -1;
+ }
+
+ _playlist->add_region (region, position);
+
+ return 0;
+}
+
+int
+AudioDiskstream::set_destructive (bool yn)
+{
+ bool bounce_ignored;
+
+ if (yn != destructive()) {
+
+ if (yn) {
+ /* requestor should already have checked this and
+ bounced if necessary and desired
+ */
+ if (!can_become_destructive (bounce_ignored)) {
+ return -1;
+ }
+ _flags = Flag (_flags | Destructive);
+ use_destructive_playlist ();
+ } else {
+ _flags = Flag (_flags & ~Destructive);
+ reset_write_sources (true, true);
+ }
+ }
+
+ return 0;
+}
+
+bool
+AudioDiskstream::can_become_destructive (bool& requires_bounce) const
+{
+ if (!_playlist) {
+ requires_bounce = false;
+ return false;
+ }
+
+ /* is there only one region ? */
+
+ if (_playlist->n_regions() != 1) {
+ requires_bounce = true;
+ return false;
+ }
+
+ boost::shared_ptr<Region> first = _playlist->find_next_region (_session.current_start_frame(), Start, 1);
+ assert (first);
+
+ /* do the source(s) for the region cover the session start position ? */
+
+ if (first->position() != _session.current_start_frame()) {
+ if (first->start() > _session.current_start_frame()) {
+ requires_bounce = true;
+ return false;
+ }
+ }
+
+ /* is the source used by only 1 playlist ? */
+
+ boost::shared_ptr<AudioRegion> afirst = boost::dynamic_pointer_cast<AudioRegion> (first);
+
+ assert (afirst);
+
+ if (afirst->source()->used() > 1) {
+ requires_bounce = true;
+ return false;
+ }
+
+ requires_bounce = false;
+ return true;
+}
+
+AudioDiskstream::ChannelInfo::ChannelInfo (nframes_t bufsize, nframes_t speed_size, nframes_t wrap_size)
+{
+ peak_power = 0.0f;
+ source = 0;
+ current_capture_buffer = 0;
+ current_playback_buffer = 0;
+ curr_capture_cnt = 0;
+
+ speed_buffer = new Sample[speed_size];
+ playback_wrap_buffer = new Sample[wrap_size];
+ capture_wrap_buffer = new Sample[wrap_size];
+
+ playback_buf = new RingBufferNPT<Sample> (bufsize);
+ capture_buf = new RingBufferNPT<Sample> (bufsize);
+ capture_transition_buf = new RingBufferNPT<CaptureTransition> (256);
+
+ /* touch the ringbuffer buffers, which will cause
+ them to be mapped into locked physical RAM if
+ we're running with mlockall(). this doesn't do
+ much if we're not.
+ */
+
+ memset (playback_buf->buffer(), 0, sizeof (Sample) * playback_buf->bufsize());
+ memset (capture_buf->buffer(), 0, sizeof (Sample) * capture_buf->bufsize());
+ memset (capture_transition_buf->buffer(), 0, sizeof (CaptureTransition) * capture_transition_buf->bufsize());
+}
+
+AudioDiskstream::ChannelInfo::~ChannelInfo ()
+{
+ if (write_source) {
+ write_source.reset ();
+ }
+
+ if (speed_buffer) {
+ delete [] speed_buffer;
+ speed_buffer = 0;
+ }
+
+ if (playback_wrap_buffer) {
+ delete [] playback_wrap_buffer;
+ playback_wrap_buffer = 0;
+ }
+
+ if (capture_wrap_buffer) {
+ delete [] capture_wrap_buffer;
+ capture_wrap_buffer = 0;
+ }
+
+ if (playback_buf) {
+ delete playback_buf;
+ playback_buf = 0;
+ }
+
+ if (capture_buf) {
+ delete capture_buf;
+ capture_buf = 0;
+ }
+
+ if (capture_transition_buf) {
+ delete capture_transition_buf;
+ capture_transition_buf = 0;
+ }
+}
diff --git a/libs/ardour/audio_library.cc b/libs/ardour/audio_library.cc
new file mode 100644
index 0000000000..a35846ab29
--- /dev/null
+++ b/libs/ardour/audio_library.cc
@@ -0,0 +1,154 @@
+/*
+ Copyright (C) 2003-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <sstream>
+
+#include <libxml/uri.h>
+
+#include <lrdf.h>
+
+#include <glibmm/convert.h>
+
+#include <pbd/compose.h>
+
+#include <ardour/audio_library.h>
+#include <ardour/utils.h>
+#include <ardour/filesystem_paths.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+
+namespace {
+ const char* const sfdb_file_name = "sfdb";
+} // anonymous namespace
+
+static const char* TAG = "http://ardour.org/ontology/Tag";
+
+AudioLibrary::AudioLibrary ()
+{
+ sys::path sfdb_file_path(user_config_directory ());
+
+ sfdb_file_path /= sfdb_file_name;
+
+ src = Glib::filename_to_uri (sfdb_file_path.to_string ());
+
+ // workaround for possible bug in raptor that crashes when saving to a
+ // non-existant file.
+ touch_file(sfdb_file_path.to_string());
+
+ lrdf_read_file(src.c_str());
+}
+
+AudioLibrary::~AudioLibrary ()
+{
+}
+
+void
+AudioLibrary::save_changes ()
+{
+ if (lrdf_export_by_source(src.c_str(), src.substr(5).c_str())) {
+ PBD::warning << string_compose(_("Could not open %1. Audio Library not saved"), src) << endmsg;
+ }
+}
+
+void
+AudioLibrary::set_tags (string member, vector<string> tags)
+{
+ sort (tags.begin(), tags.end());
+ tags.erase (unique(tags.begin(), tags.end()), tags.end());
+
+ const string file_uri(Glib::filename_to_uri (member));
+
+ lrdf_remove_uri_matches (file_uri.c_str());
+
+ for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) {
+ lrdf_add_triple (src.c_str(), file_uri.c_str(), TAG, (*i).c_str(), lrdf_literal);
+ }
+}
+
+vector<string>
+AudioLibrary::get_tags (string member)
+{
+ vector<string> tags;
+
+ lrdf_statement pattern;
+ pattern.subject = strdup(Glib::filename_to_uri(member).c_str());
+ pattern.predicate = (char*)TAG;
+ pattern.object = 0;
+ pattern.object_type = lrdf_literal;
+
+ lrdf_statement* matches = lrdf_matches (&pattern);
+ free (pattern.subject);
+
+ lrdf_statement* current = matches;
+ while (current != 0) {
+ tags.push_back (current->object);
+
+ current = current->next;
+ }
+
+ lrdf_free_statements (matches);
+
+ sort (tags.begin(), tags.end());
+
+ return tags;
+}
+
+void
+AudioLibrary::search_members_and (vector<string>& members, const vector<string> tags)
+{
+ lrdf_statement **head;
+ lrdf_statement* pattern = 0;
+ lrdf_statement* old = 0;
+ head = &pattern;
+
+ vector<string>::const_iterator i;
+ for (i = tags.begin(); i != tags.end(); ++i){
+ pattern = new lrdf_statement;
+ pattern->subject = (char*)"?";
+ pattern->predicate = (char*)TAG;
+ pattern->object = strdup((*i).c_str());
+ pattern->next = old;
+
+ old = pattern;
+ }
+
+ if (*head != 0) {
+ lrdf_uris* ulist = lrdf_match_multi(*head);
+ for (uint32_t j = 0; ulist && j < ulist->count; ++j) {
+// cerr << "AND: " << Glib::filename_from_uri(ulist->items[j]) << endl;
+ members.push_back(Glib::filename_from_uri(ulist->items[j]));
+ }
+ lrdf_free_uris(ulist);
+
+ sort(members.begin(), members.end());
+ unique(members.begin(), members.end());
+ }
+
+ // memory clean up
+ pattern = *head;
+ while(pattern){
+ free(pattern->object);
+ old = pattern;
+ pattern = pattern->next;
+ delete old;
+ }
+}
diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc
new file mode 100644
index 0000000000..1506d204f1
--- /dev/null
+++ b/libs/ardour/audio_playlist.cc
@@ -0,0 +1,788 @@
+/*
+ Copyright (C) 2003 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+
+#include <cstdlib>
+
+#include <sigc++/bind.h>
+
+#include <ardour/types.h>
+#include <ardour/configuration.h>
+#include <ardour/audioplaylist.h>
+#include <ardour/audioregion.h>
+#include <ardour/crossfade.h>
+#include <ardour/crossfade_compare.h>
+#include <ardour/session.h>
+#include <pbd/enumwriter.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace sigc;
+using namespace std;
+using namespace PBD;
+
+AudioPlaylist::AudioPlaylist (Session& session, const XMLNode& node, bool hidden)
+ : Playlist (session, node, DataType::AUDIO, hidden)
+{
+ const XMLProperty* prop = node.property("type");
+ assert(!prop || DataType(prop->value()) == DataType::AUDIO);
+
+ in_set_state++;
+ set_state (node);
+ in_set_state--;
+}
+
+AudioPlaylist::AudioPlaylist (Session& session, string name, bool hidden)
+ : Playlist (session, name, DataType::AUDIO, hidden)
+{
+}
+
+AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, string name, bool hidden)
+ : Playlist (other, name, hidden)
+{
+ RegionList::const_iterator in_o = other->regions.begin();
+ RegionList::iterator in_n = regions.begin();
+
+ while (in_o != other->regions.end()) {
+ boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*in_o);
+
+ // We look only for crossfades which begin with the current region, so we don't get doubles
+ for (Crossfades::const_iterator xfades = other->_crossfades.begin(); xfades != other->_crossfades.end(); ++xfades) {
+ if ((*xfades)->in() == ar) {
+ // We found one! Now copy it!
+
+ RegionList::const_iterator out_o = other->regions.begin();
+ RegionList::const_iterator out_n = regions.begin();
+
+ while (out_o != other->regions.end()) {
+
+ boost::shared_ptr<AudioRegion>ar2 = boost::dynamic_pointer_cast<AudioRegion>(*out_o);
+
+ if ((*xfades)->out() == ar2) {
+ boost::shared_ptr<AudioRegion>in = boost::dynamic_pointer_cast<AudioRegion>(*in_n);
+ boost::shared_ptr<AudioRegion>out = boost::dynamic_pointer_cast<AudioRegion>(*out_n);
+ boost::shared_ptr<Crossfade> new_fade = boost::shared_ptr<Crossfade> (new Crossfade (*xfades, in, out));
+ add_crossfade(new_fade);
+ break;
+ }
+
+ out_o++;
+ out_n++;
+ }
+// cerr << "HUH!? second region in the crossfade not found!" << endl;
+ }
+ }
+
+ in_o++;
+ in_n++;
+ }
+}
+
+AudioPlaylist::AudioPlaylist (boost::shared_ptr<const AudioPlaylist> other, nframes_t start, nframes_t cnt, string name, bool hidden)
+ : Playlist (other, start, cnt, name, hidden)
+{
+ /* this constructor does NOT notify others (session) */
+}
+
+AudioPlaylist::~AudioPlaylist ()
+{
+ GoingAway (); /* EMIT SIGNAL */
+
+ /* drop connections to signals */
+
+ notify_callbacks ();
+
+ _crossfades.clear ();
+}
+
+struct RegionSortByLayer {
+ bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
+ return a->layer() < b->layer();
+ }
+};
+
+ARDOUR::nframes_t
+AudioPlaylist::read (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t start,
+ nframes_t cnt, unsigned chan_n)
+{
+ nframes_t ret = cnt;
+ nframes_t end;
+ nframes_t read_frames;
+ nframes_t skip_frames;
+
+ /* optimizing this memset() away involves a lot of conditionals
+ that may well cause more of a hit due to cache misses
+ and related stuff than just doing this here.
+
+ it would be great if someone could measure this
+ at some point.
+
+ one way or another, parts of the requested area
+ that are not written to by Region::region_at()
+ for all Regions that cover the area need to be
+ zeroed.
+ */
+
+ memset (buf, 0, sizeof (Sample) * cnt);
+
+ /* this function is never called from a realtime thread, so
+ its OK to block (for short intervals).
+ */
+
+ Glib::Mutex::Lock rm (region_lock);
+
+ end = start + cnt - 1;
+ read_frames = 0;
+ skip_frames = 0;
+ _read_data_count = 0;
+
+ _read_data_count = 0;
+
+ RegionList* rlist = regions_to_read (start, start+cnt);
+
+ if (rlist->empty()) {
+ delete rlist;
+ return cnt;
+ }
+
+ map<uint32_t,vector<boost::shared_ptr<Region> > > relevant_regions;
+ map<uint32_t,vector<boost::shared_ptr<Crossfade> > > relevant_xfades;
+ vector<uint32_t> relevant_layers;
+
+ for (RegionList::iterator i = rlist->begin(); i != rlist->end(); ++i) {
+ if ((*i)->coverage (start, end) != OverlapNone) {
+ relevant_regions[(*i)->layer()].push_back (*i);
+ relevant_layers.push_back ((*i)->layer());
+ }
+ }
+
+ for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+ if ((*i)->coverage (start, end) != OverlapNone) {
+ relevant_xfades[(*i)->upper_layer()].push_back (*i);
+ }
+ }
+
+// RegionSortByLayer layer_cmp;
+// relevant_regions.sort (layer_cmp);
+
+ /* XXX this whole per-layer approach is a hack that
+ should be removed once Crossfades become
+ CrossfadeRegions and we just grab a list of relevant
+ regions and call read_at() on all of them.
+ */
+
+ sort (relevant_layers.begin(), relevant_layers.end());
+
+ for (vector<uint32_t>::iterator l = relevant_layers.begin(); l != relevant_layers.end(); ++l) {
+
+ vector<boost::shared_ptr<Region> > r (relevant_regions[*l]);
+ vector<boost::shared_ptr<Crossfade> >& x (relevant_xfades[*l]);
+
+ for (vector<boost::shared_ptr<Region> >::iterator i = r.begin(); i != r.end(); ++i) {
+ boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(*i);
+ assert(ar);
+ ar->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n, read_frames, skip_frames);
+ _read_data_count += ar->read_data_count();
+ }
+
+ for (vector<boost::shared_ptr<Crossfade> >::iterator i = x.begin(); i != x.end(); ++i) {
+ (*i)->read_at (buf, mixdown_buffer, gain_buffer, start, cnt, chan_n);
+
+ /* don't JACK up _read_data_count, since its the same data as we just
+ read from the regions, and the OS should handle that for us.
+ */
+ }
+ }
+
+ delete rlist;
+ return ret;
+}
+
+
+void
+AudioPlaylist::remove_dependents (boost::shared_ptr<Region> region)
+{
+ boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
+
+ if (in_set_state) {
+ return;
+ }
+
+ if (r == 0) {
+ fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
+ << endmsg;
+ return;
+ }
+
+ for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ) {
+
+ if ((*i)->involves (r)) {
+ i = _crossfades.erase (i);
+ } else {
+ ++i;
+ }
+ }
+}
+
+
+void
+AudioPlaylist::flush_notifications ()
+{
+ Playlist::flush_notifications();
+
+ if (in_flush) {
+ return;
+ }
+
+ in_flush = true;
+
+ Crossfades::iterator a;
+ for (a = _pending_xfade_adds.begin(); a != _pending_xfade_adds.end(); ++a) {
+ NewCrossfade (*a); /* EMIT SIGNAL */
+ }
+
+ _pending_xfade_adds.clear ();
+
+ in_flush = false;
+}
+
+void
+AudioPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
+{
+ boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(r);
+ set<boost::shared_ptr<Crossfade> > updated;
+
+ if (ar == 0) {
+ return;
+ }
+
+ for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
+
+ Crossfades::iterator tmp;
+
+ tmp = x;
+ ++tmp;
+
+ /* only update them once */
+
+ if ((*x)->involves (ar)) {
+
+ pair<set<boost::shared_ptr<Crossfade> >::iterator, bool> const u = updated.insert (*x);
+
+ if (u.second) {
+ /* x was successfully inserted into the set, so it has not already been updated */
+ try {
+ (*x)->refresh ();
+ }
+
+ catch (Crossfade::NoCrossfadeHere& err) {
+ // relax, Invalidated during refresh
+ }
+ }
+ }
+
+ x = tmp;
+ }
+}
+
+void
+AudioPlaylist::finalize_split_region (boost::shared_ptr<Region> o, boost::shared_ptr<Region> l, boost::shared_ptr<Region> r)
+{
+ boost::shared_ptr<AudioRegion> orig = boost::dynamic_pointer_cast<AudioRegion>(o);
+ boost::shared_ptr<AudioRegion> left = boost::dynamic_pointer_cast<AudioRegion>(l);
+ boost::shared_ptr<AudioRegion> right = boost::dynamic_pointer_cast<AudioRegion>(r);
+
+ for (Crossfades::iterator x = _crossfades.begin(); x != _crossfades.end();) {
+ Crossfades::iterator tmp;
+ tmp = x;
+ ++tmp;
+
+ boost::shared_ptr<Crossfade> fade;
+
+ if ((*x)->_in == orig) {
+ if (! (*x)->covers(right->position())) {
+ fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, left, (*x)->_out));
+ } else {
+ // Overlap, the crossfade is copied on the left side of the right region instead
+ fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, right, (*x)->_out));
+ }
+ }
+
+ if ((*x)->_out == orig) {
+ if (! (*x)->covers(right->position())) {
+ fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, right));
+ } else {
+ // Overlap, the crossfade is copied on the right side of the left region instead
+ fade = boost::shared_ptr<Crossfade> (new Crossfade (*x, (*x)->_in, left));
+ }
+ }
+
+ if (fade) {
+ _crossfades.remove (*x);
+ add_crossfade (fade);
+ }
+ x = tmp;
+ }
+}
+
+void
+AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
+{
+ boost::shared_ptr<AudioRegion> other;
+ boost::shared_ptr<AudioRegion> region;
+ boost::shared_ptr<AudioRegion> top;
+ boost::shared_ptr<AudioRegion> bottom;
+ boost::shared_ptr<Crossfade> xfade;
+ RegionList* touched_regions;
+
+ if (in_set_state || in_partition) {
+ return;
+ }
+
+ if ((region = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
+ fatal << _("programming error: non-audio Region tested for overlap in audio playlist")
+ << endmsg;
+ return;
+ }
+
+ if (!norefresh) {
+ refresh_dependents (r);
+ }
+
+
+ if (!Config->get_auto_xfade()) {
+ return;
+ }
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ nframes_t xfade_length;
+
+ other = boost::dynamic_pointer_cast<AudioRegion> (*i);
+
+ if (other == region) {
+ continue;
+ }
+
+ if (other->muted() || region->muted()) {
+ continue;
+ }
+
+
+ if (other->layer() < region->layer()) {
+ top = region;
+ bottom = other;
+ } else {
+ top = other;
+ bottom = region;
+ }
+
+ if (!top->opaque()) {
+ continue;
+ }
+
+ OverlapType c = top->coverage (bottom->position(), bottom->last_frame());
+
+ try {
+ switch (c) {
+ case OverlapNone:
+ break;
+
+ case OverlapInternal:
+ /* {=============== top =============}
+ * [ ----- bottom ------- ]
+ */
+ break;
+
+ case OverlapExternal:
+
+ /* [ -------- top ------- ]
+ * {=========== bottom =============}
+ */
+
+ /* to avoid discontinuities at the region boundaries of an internal
+ overlap (this region is completely within another), we create
+ two hidden crossfades at each boundary. this is not dependent
+ on the auto-xfade option, because we require it as basic
+ audio engineering.
+ */
+
+ xfade_length = min ((nframes_t) 720, top->length());
+
+ if (top_region_at (top->first_frame()) == top) {
+
+ xfade = boost::shared_ptr<Crossfade> (new Crossfade (top, bottom, xfade_length, top->first_frame(), StartOfIn));
+ add_crossfade (xfade);
+ }
+
+ if (top_region_at (top->last_frame() - 1) == top) {
+
+ /*
+ only add a fade out if there is no region on top of the end of 'top' (which
+ would cover it).
+ */
+
+ xfade = boost::shared_ptr<Crossfade> (new Crossfade (bottom, top, xfade_length, top->last_frame() - xfade_length, EndOfOut));
+ add_crossfade (xfade);
+ }
+ break;
+ case OverlapStart:
+
+ /* { ==== top ============ }
+ * [---- bottom -------------------]
+ */
+
+ if (Config->get_xfade_model() == FullCrossfade) {
+ touched_regions = regions_touched (top->first_frame(), bottom->last_frame());
+ if (touched_regions->size() <= 2) {
+ xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+ add_crossfade (xfade);
+ }
+ } else {
+
+ touched_regions = regions_touched (top->first_frame(),
+ top->first_frame() + min ((nframes_t)Config->get_short_xfade_seconds() * _session.frame_rate(),
+ top->length()));
+ if (touched_regions->size() <= 2) {
+ xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+ add_crossfade (xfade);
+ }
+ }
+ break;
+ case OverlapEnd:
+
+
+ /* [---- top ------------------------]
+ * { ==== bottom ============ }
+ */
+
+ if (Config->get_xfade_model() == FullCrossfade) {
+
+ touched_regions = regions_touched (bottom->first_frame(), top->last_frame());
+ if (touched_regions->size() <= 2) {
+ xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
+ Config->get_xfade_model(), Config->get_xfades_active()));
+ add_crossfade (xfade);
+ }
+
+ } else {
+ touched_regions = regions_touched (bottom->first_frame(),
+ bottom->first_frame() + min ((nframes_t)Config->get_short_xfade_seconds() * _session.frame_rate(),
+ bottom->length()));
+ if (touched_regions->size() <= 2) {
+ xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other, Config->get_xfade_model(), Config->get_xfades_active()));
+ add_crossfade (xfade);
+ }
+ }
+ break;
+ default:
+ xfade = boost::shared_ptr<Crossfade> (new Crossfade (region, other,
+ Config->get_xfade_model(), Config->get_xfades_active()));
+ add_crossfade (xfade);
+ }
+ }
+
+ catch (failed_constructor& err) {
+ continue;
+ }
+
+ catch (Crossfade::NoCrossfadeHere& err) {
+ continue;
+ }
+
+ }
+}
+
+void
+AudioPlaylist::add_crossfade (boost::shared_ptr<Crossfade> xfade)
+{
+ Crossfades::iterator ci;
+
+ for (ci = _crossfades.begin(); ci != _crossfades.end(); ++ci) {
+ if (*(*ci) == *xfade) { // Crossfade::operator==()
+ break;
+ }
+ }
+
+ if (ci != _crossfades.end()) {
+ // it will just go away
+ } else {
+ _crossfades.push_back (xfade);
+
+ xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
+ xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
+
+ notify_crossfade_added (xfade);
+ }
+}
+
+void AudioPlaylist::notify_crossfade_added (boost::shared_ptr<Crossfade> x)
+{
+ if (g_atomic_int_get(&block_notifications)) {
+ _pending_xfade_adds.insert (_pending_xfade_adds.end(), x);
+ } else {
+
+ NewCrossfade (x); /* EMIT SIGNAL */
+ }
+}
+
+void
+AudioPlaylist::crossfade_invalidated (boost::shared_ptr<Region> r)
+{
+ Crossfades::iterator i;
+ boost::shared_ptr<Crossfade> xfade = boost::dynamic_pointer_cast<Crossfade> (r);
+
+ xfade->in()->resume_fade_in ();
+ xfade->out()->resume_fade_out ();
+
+ if ((i = find (_crossfades.begin(), _crossfades.end(), xfade)) != _crossfades.end()) {
+ _crossfades.erase (i);
+ }
+}
+
+int
+AudioPlaylist::set_state (const XMLNode& node)
+{
+ XMLNode *child;
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+
+ in_set_state++;
+ freeze ();
+
+ Playlist::set_state (node);
+
+ nlist = node.children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ child = *niter;
+
+ if (child->name() != "Crossfade") {
+ continue;
+ }
+
+ try {
+ boost::shared_ptr<Crossfade> xfade = boost::shared_ptr<Crossfade> (new Crossfade (*((const Playlist *)this), *child));
+ _crossfades.push_back (xfade);
+ xfade->Invalidated.connect (mem_fun (*this, &AudioPlaylist::crossfade_invalidated));
+ xfade->StateChanged.connect (mem_fun (*this, &AudioPlaylist::crossfade_changed));
+ NewCrossfade(xfade);
+ }
+
+ catch (failed_constructor& err) {
+ // cout << string_compose (_("could not create crossfade object in playlist %1"),
+ // _name)
+ // << endl;
+ continue;
+ }
+ }
+
+ thaw ();
+ in_set_state--;
+
+ return 0;
+}
+
+void
+AudioPlaylist::clear (bool with_signals)
+{
+ _crossfades.clear ();
+ Playlist::clear (with_signals);
+}
+
+XMLNode&
+AudioPlaylist::state (bool full_state)
+{
+ XMLNode& node = Playlist::state (full_state);
+
+ if (full_state) {
+ for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+ node.add_child_nocopy ((*i)->get_state());
+ }
+ }
+
+ return node;
+}
+
+void
+AudioPlaylist::dump () const
+{
+ boost::shared_ptr<Region>r;
+ boost::shared_ptr<Crossfade> x;
+
+ cerr << "Playlist \"" << _name << "\" " << endl
+ << regions.size() << " regions "
+ << _crossfades.size() << " crossfades"
+ << endl;
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ r = *i;
+ cerr << " " << r->name() << " @ " << r << " ["
+ << r->start() << "+" << r->length()
+ << "] at "
+ << r->position()
+ << " on layer "
+ << r->layer ()
+ << endl;
+ }
+
+ for (Crossfades::const_iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+ x = *i;
+ cerr << " xfade ["
+ << x->out()->name()
+ << ','
+ << x->in()->name()
+ << " @ "
+ << x->position()
+ << " length = "
+ << x->length ()
+ << " active ? "
+ << (x->active() ? "yes" : "no")
+ << endl;
+ }
+}
+
+bool
+AudioPlaylist::destroy_region (boost::shared_ptr<Region> region)
+{
+ boost::shared_ptr<AudioRegion> r = boost::dynamic_pointer_cast<AudioRegion> (region);
+ bool changed = false;
+ Crossfades::iterator c, ctmp;
+ set<boost::shared_ptr<Crossfade> > unique_xfades;
+
+ if (r == 0) {
+ fatal << _("programming error: non-audio Region passed to remove_overlap in audio playlist")
+ << endmsg;
+ /*NOTREACHED*/
+ return false;
+ }
+
+ {
+ RegionLock rlock (this);
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
+
+ RegionList::iterator tmp = i;
+ ++tmp;
+
+ if ((*i) == region) {
+ regions.erase (i);
+ changed = true;
+ }
+
+ i = tmp;
+ }
+
+ for (set<boost::shared_ptr<Region> >::iterator x = all_regions.begin(); x != all_regions.end(); ) {
+
+ set<boost::shared_ptr<Region> >::iterator xtmp = x;
+ ++xtmp;
+
+ if ((*x) == region) {
+ all_regions.erase (x);
+ changed = true;
+ }
+
+ x = xtmp;
+ }
+
+ region->set_playlist (boost::shared_ptr<Playlist>());
+ }
+
+ for (c = _crossfades.begin(); c != _crossfades.end(); ) {
+ ctmp = c;
+ ++ctmp;
+
+ if ((*c)->involves (r)) {
+ unique_xfades.insert (*c);
+ _crossfades.erase (c);
+ }
+
+ c = ctmp;
+ }
+
+ if (changed) {
+ /* overload this, it normally means "removed", not destroyed */
+ notify_region_removed (region);
+ }
+
+ return changed;
+}
+
+void
+AudioPlaylist::crossfade_changed (Change ignored)
+{
+ if (in_flush || in_set_state) {
+ return;
+ }
+
+ /* XXX is there a loop here? can an xfade change not happen
+ due to a playlist change? well, sure activation would
+ be an example. maybe we should check the type of change
+ that occured.
+ */
+
+ notify_modified ();
+}
+
+bool
+AudioPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
+{
+ if (in_flush || in_set_state) {
+ return false;
+ }
+
+ Change our_interests = Change (AudioRegion::FadeInChanged|
+ AudioRegion::FadeOutChanged|
+ AudioRegion::FadeInActiveChanged|
+ AudioRegion::FadeOutActiveChanged|
+ AudioRegion::EnvelopeActiveChanged|
+ AudioRegion::ScaleAmplitudeChanged|
+ AudioRegion::EnvelopeChanged);
+ bool parent_wants_notify;
+
+ parent_wants_notify = Playlist::region_changed (what_changed, region);
+
+ if ((parent_wants_notify || (what_changed & our_interests))) {
+ notify_modified ();
+ }
+
+ return true;
+}
+
+void
+AudioPlaylist::crossfades_at (nframes_t frame, Crossfades& clist)
+{
+ RegionLock rlock (this);
+
+ for (Crossfades::iterator i = _crossfades.begin(); i != _crossfades.end(); ++i) {
+ nframes_t start, end;
+
+ start = (*i)->position();
+ end = start + (*i)->overlap_length(); // not length(), important difference
+
+ if (frame >= start && frame <= end) {
+ clist.push_back (*i);
+ }
+ }
+}
+
diff --git a/libs/ardour/audio_port.cc b/libs/ardour/audio_port.cc
new file mode 100644
index 0000000000..714be28f34
--- /dev/null
+++ b/libs/ardour/audio_port.cc
@@ -0,0 +1,126 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cassert>
+#include <ardour/audio_port.h>
+#include <ardour/jack_audio_port.h>
+#include <ardour/audioengine.h>
+#include <ardour/data_type.h>
+
+using namespace ARDOUR;
+using namespace std;
+
+AudioPort::AudioPort (const std::string& name, Flags flags, bool external, nframes_t capacity)
+ : Port (name, flags)
+ , BaseAudioPort (name, flags)
+ , PortFacade (name, flags)
+{
+ if (!external || receives_input()) {
+
+ /* internal-only and input ports need their own buffers.
+ external output ports use the external port buffer.
+ */
+
+ _buffer = new AudioBuffer (capacity);
+ _own_buffer = true;
+ }
+
+ if (!external) {
+
+ _ext_port = 0;
+ set_name (name);
+
+ } else {
+
+ /* make the JackAudioPort create its own buffer. For input,
+ we will copy from it during cycle_start(). For output,
+ we will set up our buffer to point to its buffer, which
+ will in turn be using the JACK port buffer for data.
+ */
+
+ _ext_port = new JackAudioPort (name, flags, 0);
+
+ if (sends_output()) {
+ _buffer = &dynamic_cast<JackAudioPort*>(_ext_port)->get_audio_buffer();
+ }
+
+ Port::set_name (_ext_port->name());
+ }
+
+ reset ();
+}
+
+AudioPort::~AudioPort()
+{
+ if (_ext_port) {
+ delete _ext_port;
+ _ext_port = 0;
+ }
+}
+
+void
+AudioPort::reset()
+{
+ BaseAudioPort::reset();
+
+ if (_ext_port) {
+ _ext_port->reset ();
+ }
+}
+
+
+void
+AudioPort::cycle_start (nframes_t nframes, nframes_t offset)
+{
+ /* caller must hold process lock */
+
+ if (_ext_port) {
+ _ext_port->cycle_start (nframes, offset);
+ }
+
+ if (_flags & IsInput) {
+
+ if (_ext_port) {
+ _buffer->read_from (dynamic_cast<BaseAudioPort*>(_ext_port)->get_audio_buffer(), nframes, offset);
+
+ if (!_connections.empty()) {
+ (*_mixdown) (_connections, _buffer, nframes, offset, false);
+ }
+
+ } else {
+
+ if (_connections.empty()) {
+ _buffer->silence (nframes, offset);
+ } else {
+ (*_mixdown) (_connections, _buffer, nframes, offset, true);
+ }
+ }
+
+ } else {
+
+ // XXX if we could get the output stage to not purely mix into, but also
+ // to initially overwrite the buffer, we could avoid this silence step.
+
+ _buffer->silence (nframes, offset);
+ }
+}
+
+void
+AudioPort::cycle_end (nframes_t nframes, nframes_t offset)
+{
+}
diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc
new file mode 100644
index 0000000000..53f918ec4e
--- /dev/null
+++ b/libs/ardour/audio_track.cc
@@ -0,0 +1,884 @@
+/*
+ Copyright (C) 2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <sigc++/retype.h>
+#include <sigc++/retype_return.h>
+#include <sigc++/bind.h>
+
+#include <pbd/error.h>
+#include <pbd/enumwriter.h>
+
+#include <ardour/audio_track.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/session.h>
+#include <ardour/io_processor.h>
+#include <ardour/audioregion.h>
+#include <ardour/audiosource.h>
+#include <ardour/region_factory.h>
+#include <ardour/route_group_specialized.h>
+#include <ardour/processor.h>
+#include <ardour/plugin_insert.h>
+#include <ardour/audioplaylist.h>
+#include <ardour/playlist_factory.h>
+#include <ardour/panner.h>
+#include <ardour/utils.h>
+#include <ardour/buffer_set.h>
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+AudioTrack::AudioTrack (Session& sess, string name, Route::Flag flag, TrackMode mode)
+ : Track (sess, name, flag, mode)
+{
+ AudioDiskstream::Flag dflags = AudioDiskstream::Flag (0);
+
+ if (_flags & Hidden) {
+ dflags = AudioDiskstream::Flag (dflags | AudioDiskstream::Hidden);
+ } else {
+ dflags = AudioDiskstream::Flag (dflags | AudioDiskstream::Recordable);
+ }
+
+ if (mode == Destructive) {
+ dflags = AudioDiskstream::Flag (dflags | AudioDiskstream::Destructive);
+ }
+
+ boost::shared_ptr<AudioDiskstream> ds (new AudioDiskstream (_session, name, dflags));
+
+ _session.add_diskstream (ds);
+
+ set_diskstream (boost::dynamic_pointer_cast<AudioDiskstream> (ds), this);
+}
+
+AudioTrack::AudioTrack (Session& sess, const XMLNode& node)
+ : Track (sess, node)
+{
+ _set_state (node, false);
+}
+
+AudioTrack::~AudioTrack ()
+{
+}
+
+int
+AudioTrack::set_mode (TrackMode m)
+{
+ if (m != _mode) {
+
+ if (_diskstream->set_destructive (m == Destructive)) {
+ return -1;
+ }
+
+ _mode = m;
+
+ TrackModeChanged (); /* EMIT SIGNAL */
+ }
+
+ return 0;
+}
+
+bool
+AudioTrack::can_use_mode (TrackMode m, bool& bounce_required)
+{
+ switch (m) {
+ case Normal:
+ bounce_required = false;
+ return true;
+
+ case Destructive:
+ default:
+ return _diskstream->can_become_destructive (bounce_required);
+ }
+}
+
+int
+AudioTrack::deprecated_use_diskstream_connections ()
+{
+ boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream();
+
+ if (diskstream->deprecated_io_node == 0) {
+ return 0;
+ }
+
+ const XMLProperty* prop;
+ XMLNode& node (*diskstream->deprecated_io_node);
+
+ /* don't do this more than once. */
+
+ diskstream->deprecated_io_node = 0;
+
+ set_input_minimum (ChanCount::ZERO);
+ set_input_maximum (ChanCount::INFINITE);
+ set_output_minimum (ChanCount::ZERO);
+ set_output_maximum (ChanCount::INFINITE);
+
+ if ((prop = node.property ("gain")) != 0) {
+ set_gain (atof (prop->value().c_str()), this);
+ _gain = _desired_gain;
+ }
+
+ if ((prop = node.property ("input-connection")) != 0) {
+ boost::shared_ptr<Bundle> c = _session.bundle_by_name (prop->value());
+
+ if (c == 0) {
+ error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
+
+ if ((c = _session.bundle_by_name (_("in 1"))) == 0) {
+ error << _("No input bundles available as a replacement")
+ << endmsg;
+ return -1;
+ } else {
+ info << string_compose (_("Bundle %1 was not available - \"in 1\" used instead"), prop->value())
+ << endmsg;
+ }
+ }
+
+ connect_input_ports_to_bundle (c, this);
+
+ } else if ((prop = node.property ("inputs")) != 0) {
+ if (set_inputs (prop->value())) {
+ error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+AudioTrack::set_diskstream (boost::shared_ptr<AudioDiskstream> ds, void *src)
+{
+ _diskstream = ds;
+ _diskstream->set_io (*this);
+ _diskstream->set_destructive (_mode == Destructive);
+
+ if (audio_diskstream()->deprecated_io_node) {
+
+ if (!connecting_legal) {
+ ConnectingLegal.connect (mem_fun (*this, &AudioTrack::deprecated_use_diskstream_connections));
+ } else {
+ deprecated_use_diskstream_connections ();
+ }
+ }
+
+ _diskstream->set_record_enabled (false);
+ _diskstream->monitor_input (false);
+
+ ic_connection.disconnect();
+ ic_connection = input_changed.connect (mem_fun (*_diskstream, &Diskstream::handle_input_change));
+
+ DiskstreamChanged (); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+int
+AudioTrack::use_diskstream (string name)
+{
+ boost::shared_ptr<AudioDiskstream> dstream;
+
+ if ((dstream = boost::dynamic_pointer_cast<AudioDiskstream>(_session.diskstream_by_name (name))) == 0) {
+ error << string_compose(_("AudioTrack: audio diskstream \"%1\" not known by session"), name) << endmsg;
+ return -1;
+ }
+
+ return set_diskstream (dstream, this);
+}
+
+int
+AudioTrack::use_diskstream (const PBD::ID& id)
+{
+ boost::shared_ptr<AudioDiskstream> dstream;
+
+ if ((dstream = boost::dynamic_pointer_cast<AudioDiskstream> (_session.diskstream_by_id (id))) == 0) {
+ error << string_compose(_("AudioTrack: audio diskstream \"%1\" not known by session"), id) << endmsg;
+ return -1;
+ }
+
+ return set_diskstream (dstream, this);
+}
+
+boost::shared_ptr<AudioDiskstream>
+AudioTrack::audio_diskstream() const
+{
+ return boost::dynamic_pointer_cast<AudioDiskstream>(_diskstream);
+}
+
+int
+AudioTrack::set_state (const XMLNode& node)
+{
+ return _set_state (node, true);
+}
+
+int
+AudioTrack::_set_state (const XMLNode& node, bool call_base)
+{
+ const XMLProperty *prop;
+ XMLNodeConstIterator iter;
+
+ if (call_base) {
+ if (Route::_set_state (node, call_base)) {
+ return -1;
+ }
+ }
+
+ if ((prop = node.property (X_("mode"))) != 0) {
+ _mode = TrackMode (string_2_enum (prop->value(), _mode));
+ } else {
+ _mode = Normal;
+ }
+
+ if ((prop = node.property ("diskstream-id")) == 0) {
+
+ /* some old sessions use the diskstream name rather than the ID */
+
+ if ((prop = node.property ("diskstream")) == 0) {
+ fatal << _("programming error: AudioTrack given state without diskstream!") << endmsg;
+ /*NOTREACHED*/
+ return -1;
+ }
+
+ if (use_diskstream (prop->value())) {
+ return -1;
+ }
+
+ } else {
+
+ PBD::ID id (prop->value());
+
+ if (use_diskstream (id)) {
+ return -1;
+ }
+ }
+
+
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLNode *child;
+
+ nlist = node.children();
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+ child = *niter;
+
+ if (child->name() == X_("recenable")) {
+ _rec_enable_control->set_state (*child);
+ _session.add_controllable (_rec_enable_control);
+ }
+ }
+
+ pending_state = const_cast<XMLNode*> (&node);
+
+ _session.StateReady.connect (mem_fun (*this, &AudioTrack::set_state_part_two));
+
+ return 0;
+}
+
+XMLNode&
+AudioTrack::state(bool full_state)
+{
+ XMLNode& root (Route::state(full_state));
+ XMLNode* freeze_node;
+ char buf[64];
+
+ if (_freeze_record.playlist) {
+ XMLNode* inode;
+
+ freeze_node = new XMLNode (X_("freeze-info"));
+ freeze_node->add_property ("playlist", _freeze_record.playlist->name());
+ freeze_node->add_property ("state", enum_2_string (_freeze_record.state));
+
+ for (vector<FreezeRecordProcessorInfo*>::iterator i = _freeze_record.processor_info.begin(); i != _freeze_record.processor_info.end(); ++i) {
+ inode = new XMLNode (X_("processor"));
+ (*i)->id.print (buf, sizeof (buf));
+ inode->add_property (X_("id"), buf);
+ inode->add_child_copy ((*i)->state);
+
+ freeze_node->add_child_nocopy (*inode);
+ }
+
+ root.add_child_nocopy (*freeze_node);
+ }
+
+ /* Alignment: act as a proxy for the diskstream */
+
+ XMLNode* align_node = new XMLNode (X_("alignment"));
+ AlignStyle as = _diskstream->alignment_style ();
+ align_node->add_property (X_("style"), enum_2_string (as));
+ root.add_child_nocopy (*align_node);
+
+ root.add_property (X_("mode"), enum_2_string (_mode));
+
+ /* we don't return diskstream state because we don't
+ own the diskstream exclusively. control of the diskstream
+ state is ceded to the Session, even if we create the
+ diskstream.
+ */
+
+ _diskstream->id().print (buf, sizeof (buf));
+ root.add_property ("diskstream-id", buf);
+
+ root.add_child_nocopy (_rec_enable_control->get_state());
+
+ return root;
+}
+
+void
+AudioTrack::set_state_part_two ()
+{
+ XMLNode* fnode;
+ XMLProperty* prop;
+ LocaleGuard lg (X_("POSIX"));
+
+ /* This is called after all session state has been restored but before
+ have been made ports and connections are established.
+ */
+
+ if (pending_state == 0) {
+ return;
+ }
+
+ if ((fnode = find_named_node (*pending_state, X_("freeze-info"))) != 0) {
+
+
+ _freeze_record.have_mementos = false;
+ _freeze_record.state = Frozen;
+
+ for (vector<FreezeRecordProcessorInfo*>::iterator i = _freeze_record.processor_info.begin(); i != _freeze_record.processor_info.end(); ++i) {
+ delete *i;
+ }
+ _freeze_record.processor_info.clear ();
+
+ if ((prop = fnode->property (X_("playlist"))) != 0) {
+ boost::shared_ptr<Playlist> pl = _session.playlist_by_name (prop->value());
+ if (pl) {
+ _freeze_record.playlist = boost::dynamic_pointer_cast<AudioPlaylist> (pl);
+ } else {
+ _freeze_record.playlist.reset ();
+ _freeze_record.state = NoFreeze;
+ return;
+ }
+ }
+
+ if ((prop = fnode->property (X_("state"))) != 0) {
+ _freeze_record.state = FreezeState (string_2_enum (prop->value(), _freeze_record.state));
+ }
+
+ XMLNodeConstIterator citer;
+ XMLNodeList clist = fnode->children();
+
+ for (citer = clist.begin(); citer != clist.end(); ++citer) {
+ if ((*citer)->name() != X_("processor")) {
+ continue;
+ }
+
+ if ((prop = (*citer)->property (X_("id"))) == 0) {
+ continue;
+ }
+
+ FreezeRecordProcessorInfo* frii = new FreezeRecordProcessorInfo (*((*citer)->children().front()),
+ boost::shared_ptr<Processor>());
+ frii->id = prop->value ();
+ _freeze_record.processor_info.push_back (frii);
+ }
+ }
+
+ /* Alignment: act as a proxy for the diskstream */
+
+ if ((fnode = find_named_node (*pending_state, X_("alignment"))) != 0) {
+
+ if ((prop = fnode->property (X_("style"))) != 0) {
+
+ /* fix for older sessions from before EnumWriter */
+
+ string pstr;
+
+ if (prop->value() == "capture") {
+ pstr = "CaptureTime";
+ } else if (prop->value() == "existing") {
+ pstr = "ExistingMaterial";
+ } else {
+ pstr = prop->value();
+ }
+
+ AlignStyle as = AlignStyle (string_2_enum (pstr, as));
+ _diskstream->set_persistent_align_style (as);
+ }
+ }
+ return;
+}
+
+int
+AudioTrack::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset,
+ bool session_state_changing, bool can_record, bool rec_monitors_input)
+{
+ if (n_outputs().n_total() == 0) {
+ return 0;
+ }
+
+ if (!_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ if (session_state_changing) {
+
+ /* XXX is this safe to do against transport state changes? */
+
+ passthru_silence (start_frame, end_frame, nframes, offset, 0, false);
+ return 0;
+ }
+
+ audio_diskstream()->check_record_status (start_frame, nframes, can_record);
+
+ bool send_silence;
+
+ if (_have_internal_generator) {
+ /* since the instrument has no input streams,
+ there is no reason to send any signal
+ into the route.
+ */
+ send_silence = true;
+ } else {
+
+ if (!Config->get_tape_machine_mode()) {
+ /*
+ ADATs work in a strange way..
+ they monitor input always when stopped.and auto-input is engaged.
+ */
+ if ((Config->get_monitoring_model() == SoftwareMonitoring) && (Config->get_auto_input () || _diskstream->record_enabled())) {
+ send_silence = false;
+ } else {
+ send_silence = true;
+ }
+ } else {
+ /*
+ Other machines switch to input on stop if the track is record enabled,
+ regardless of the auto input setting (auto input only changes the
+ monitoring state when the transport is rolling)
+ */
+ if ((Config->get_monitoring_model() == SoftwareMonitoring) && _diskstream->record_enabled()) {
+ send_silence = false;
+ } else {
+ send_silence = true;
+ }
+ }
+ }
+
+ apply_gain_automation = false;
+
+ if (send_silence) {
+
+ /* if we're sending silence, but we want the meters to show levels for the signal,
+ meter right here.
+ */
+
+ if (_have_internal_generator) {
+ passthru_silence (start_frame, end_frame, nframes, offset, 0, true);
+ } else {
+ if (_meter_point == MeterInput) {
+ just_meter_input (start_frame, end_frame, nframes, offset);
+ }
+ passthru_silence (start_frame, end_frame, nframes, offset, 0, false);
+ }
+
+ } else {
+
+ /* we're sending signal, but we may still want to meter the input.
+ */
+
+ passthru (start_frame, end_frame, nframes, offset, 0, (_meter_point == MeterInput));
+ }
+
+ return 0;
+}
+
+int
+AudioTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick,
+ bool can_record, bool rec_monitors_input)
+{
+ int dret;
+ Sample* b;
+ Sample* tmpb;
+ nframes_t transport_frame;
+ boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream();
+
+ {
+ Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+ if (lm.locked()) {
+ // automation snapshot can also be called from the non-rt context
+ // and it uses the redirect list, so we take the lock out here
+ automation_snapshot (start_frame, false);
+ }
+ }
+
+
+ if (n_outputs().n_total() == 0 && _processors.empty()) {
+ return 0;
+ }
+
+ if (!_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ transport_frame = _session.transport_frame();
+
+ if ((nframes = check_initial_delay (nframes, offset, transport_frame)) == 0) {
+ /* need to do this so that the diskstream sets its
+ playback distance to zero, thus causing diskstream::commit
+ to do nothing.
+ */
+ return diskstream->process (transport_frame, 0, 0, can_record, rec_monitors_input);
+ }
+
+ _silent = false;
+ apply_gain_automation = false;
+
+ if ((dret = diskstream->process (transport_frame, nframes, offset, can_record, rec_monitors_input)) != 0) {
+
+ silence (nframes, offset);
+
+ return dret;
+ }
+
+ /* special condition applies */
+
+ if (_meter_point == MeterInput) {
+ just_meter_input (start_frame, end_frame, nframes, offset);
+ }
+
+ if (diskstream->record_enabled() && !can_record && !Config->get_auto_input()) {
+
+ /* not actually recording, but we want to hear the input material anyway,
+ at least potentially (depending on monitoring options)
+ */
+
+ passthru (start_frame, end_frame, nframes, offset, 0, true);
+
+ } else if ((b = diskstream->playback_buffer(0)) != 0) {
+
+ /*
+ XXX is it true that the earlier test on n_outputs()
+ means that we can avoid checking it again here? i think
+ so, because changing the i/o configuration of an IO
+ requires holding the AudioEngine lock, which we hold
+ while in the process() tree.
+ */
+
+
+ /* copy the diskstream data to all output buffers */
+
+ const size_t limit = n_process_buffers().n_audio();
+ BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
+
+ uint32_t n;
+ uint32_t i;
+
+ for (i = 0, n = 1; i < limit; ++i, ++n) {
+ memcpy (bufs.get_audio(i).data(), b, sizeof (Sample) * nframes);
+ if (n < diskstream->n_channels().n_audio()) {
+ tmpb = diskstream->playback_buffer(n);
+ if (tmpb!=0) {
+ b = tmpb;
+ }
+ }
+ }
+
+ /* don't waste time with automation if we're recording or we've just stopped (yes it can happen) */
+
+ if (!diskstream->record_enabled() && _session.transport_rolling()) {
+ Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK);
+
+ if (am.locked() && gain_control()->list()->automation_playback()) {
+ apply_gain_automation = gain_control()->list()->curve().rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes);
+ }
+ }
+
+ process_output_buffers (bufs, start_frame, end_frame, nframes, offset, (!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick, (_meter_point != MeterInput));
+
+ } else {
+ /* problem with the diskstream; just be quiet for a bit */
+ silence (nframes, offset);
+ }
+
+ return 0;
+}
+
+int
+AudioTrack::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset,
+ bool can_record, bool rec_monitors_input)
+{
+ if (n_outputs().n_total() == 0 && _processors.empty()) {
+ return 0;
+ }
+
+ if (!_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ _silent = true;
+ apply_gain_automation = false;
+
+ silence (nframes, offset);
+
+ return audio_diskstream()->process (_session.transport_frame() + offset, nframes, offset, can_record, rec_monitors_input);
+}
+
+int
+AudioTrack::export_stuff (BufferSet& buffers, nframes_t start, nframes_t nframes)
+{
+ gain_t gain_automation[nframes];
+ gain_t gain_buffer[nframes];
+ float mix_buffer[nframes];
+ ProcessorList::iterator i;
+ bool post_fader_work = false;
+ gain_t this_gain = _gain;
+ boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream();
+
+ Glib::RWLock::ReaderLock rlock (_processor_lock);
+
+ boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist>(diskstream->playlist());
+ assert(apl);
+
+ assert(buffers.get_audio(0).capacity() >= nframes);
+
+ if (apl->read (buffers.get_audio(0).data(), mix_buffer, gain_buffer, start, nframes) != nframes) {
+ return -1;
+ }
+
+ assert(buffers.count().n_audio() >= 1);
+ uint32_t n=1;
+ Sample* b = buffers.get_audio(0).data();
+ BufferSet::audio_iterator bi = buffers.audio_begin();
+ ++bi;
+ for ( ; bi != buffers.audio_end(); ++bi, ++n) {
+ if (n < diskstream->n_channels().n_audio()) {
+ if (apl->read (bi->data(), mix_buffer, gain_buffer, start, nframes, n) != nframes) {
+ return -1;
+ }
+ b = bi->data();
+ }
+ else {
+ /* duplicate last across remaining buffers */
+ memcpy (bi->data(), b, sizeof (Sample) * nframes);
+ }
+ }
+
+
+ /* note: only run processors during export. other layers in the machinery
+ will already have checked that there are no external port processors.
+ */
+
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<Processor> processor;
+
+ if ((processor = boost::dynamic_pointer_cast<Processor>(*i)) != 0) {
+ switch (processor->placement()) {
+ case PreFader:
+ processor->run_in_place (buffers, start, start+nframes, nframes, 0);
+ break;
+ case PostFader:
+ post_fader_work = true;
+ break;
+ }
+ }
+ }
+
+ if (gain_control()->list()->automation_state() == Play) {
+
+ gain_control()->list()->curve().get_vector (start, start + nframes, gain_automation, nframes);
+
+ for (BufferSet::audio_iterator bi = buffers.audio_begin(); bi != buffers.audio_end(); ++bi) {
+ Sample *b = bi->data();
+ for (nframes_t n = 0; n < nframes; ++n) {
+ b[n] *= gain_automation[n];
+ }
+ }
+
+ } else {
+
+ for (BufferSet::audio_iterator bi = buffers.audio_begin(); bi != buffers.audio_end(); ++bi) {
+ Sample *b = bi->data();
+ for (nframes_t n = 0; n < nframes; ++n) {
+ b[n] *= this_gain;
+ }
+ }
+ }
+
+ if (post_fader_work) {
+
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<PluginInsert> processor;
+
+ if ((processor = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+ switch ((*i)->placement()) {
+ case PreFader:
+ break;
+ case PostFader:
+ processor->run_in_place (buffers, start, start+nframes, nframes, 0);
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+void
+AudioTrack::bounce (InterThreadInfo& itt)
+{
+ vector<boost::shared_ptr<Source> > srcs;
+ _session.write_one_audio_track (*this, 0, _session.current_end_frame(), false, srcs, itt);
+ itt.done = true;
+}
+
+
+void
+AudioTrack::bounce_range (nframes_t start, nframes_t end, InterThreadInfo& itt)
+{
+ vector<boost::shared_ptr<Source> > srcs;
+ _session.write_one_audio_track (*this, start, end, false, srcs, itt);
+ itt.done = true;
+}
+
+void
+AudioTrack::freeze (InterThreadInfo& itt)
+{
+ vector<boost::shared_ptr<Source> > srcs;
+ string new_playlist_name;
+ boost::shared_ptr<Playlist> new_playlist;
+ string dir;
+ string region_name;
+ boost::shared_ptr<AudioDiskstream> diskstream = audio_diskstream();
+
+ if ((_freeze_record.playlist = boost::dynamic_pointer_cast<AudioPlaylist>(diskstream->playlist())) == 0) {
+ return;
+ }
+
+ uint32_t n = 1;
+
+ while (n < (UINT_MAX-1)) {
+
+ string candidate;
+
+ candidate = string_compose ("<F%2>%1", _freeze_record.playlist->name(), n);
+
+ if (_session.playlist_by_name (candidate) == 0) {
+ new_playlist_name = candidate;
+ break;
+ }
+
+ ++n;
+
+ }
+
+ if (n == (UINT_MAX-1)) {
+ error << string_compose (X_("There are too many frozen versions of playlist \"%1\""
+ " to create another one"), _freeze_record.playlist->name())
+ << endmsg;
+ return;
+ }
+
+ if (_session.write_one_audio_track (*this, _session.current_start_frame(), _session.current_end_frame(), true, srcs, itt)) {
+ return;
+ }
+
+ _freeze_record.processor_info.clear ();
+ _freeze_record.have_mementos = true;
+
+ {
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
+
+ boost::shared_ptr<Processor> processor;
+
+ if ((processor = boost::dynamic_pointer_cast<Processor>(*r)) != 0) {
+
+ FreezeRecordProcessorInfo* frii = new FreezeRecordProcessorInfo ((*r)->get_state(), processor);
+
+ frii->id = processor->id();
+
+ _freeze_record.processor_info.push_back (frii);
+
+ /* now deactivate the processor */
+
+ processor->set_active (false);
+ _session.set_dirty ();
+ }
+ }
+ }
+
+ new_playlist = PlaylistFactory::create (DataType::AUDIO, _session, new_playlist_name, false);
+ region_name = new_playlist_name;
+
+ /* create a new region from all filesources, keep it private */
+
+ boost::shared_ptr<Region> region (RegionFactory::create (srcs, 0, srcs[0]->length(),
+ region_name, 0,
+ (Region::Flag) (Region::WholeFile|Region::DefaultFlags),
+ false));
+
+ new_playlist->set_orig_diskstream_id (diskstream->id());
+ new_playlist->add_region (region, _session.current_start_frame());
+ new_playlist->set_frozen (true);
+ region->set_locked (true);
+
+ diskstream->use_playlist (boost::dynamic_pointer_cast<AudioPlaylist>(new_playlist));
+ diskstream->set_record_enabled (false);
+
+ _freeze_record.state = Frozen;
+ FreezeChange(); /* EMIT SIGNAL */
+}
+
+void
+AudioTrack::unfreeze ()
+{
+ if (_freeze_record.playlist) {
+ audio_diskstream()->use_playlist (_freeze_record.playlist);
+
+ if (_freeze_record.have_mementos) {
+
+ for (vector<FreezeRecordProcessorInfo*>::iterator i = _freeze_record.processor_info.begin(); i != _freeze_record.processor_info.end(); ++i) {
+ (*i)->memento ();
+ }
+
+ } else {
+
+ Glib::RWLock::ReaderLock lm (_processor_lock); // should this be a write lock? jlc
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ for (vector<FreezeRecordProcessorInfo*>::iterator ii = _freeze_record.processor_info.begin(); ii != _freeze_record.processor_info.end(); ++ii) {
+ if ((*ii)->id == (*i)->id()) {
+ (*i)->set_state (((*ii)->state));
+ break;
+ }
+ }
+ }
+ }
+
+ _freeze_record.playlist.reset ();
+ }
+
+ _freeze_record.state = UnFrozen;
+ FreezeChange (); /* EMIT SIGNAL */
+}
+
diff --git a/libs/ardour/audio_unit.cc b/libs/ardour/audio_unit.cc
new file mode 100644
index 0000000000..b941bc10bb
--- /dev/null
+++ b/libs/ardour/audio_unit.cc
@@ -0,0 +1,924 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <sstream>
+
+#include <pbd/transmitter.h>
+#include <pbd/xml++.h>
+#include <pbd/whitespace.h>
+
+#include <glibmm/thread.h>
+
+#include <ardour/audioengine.h>
+#include <ardour/io.h>
+#include <ardour/audio_unit.h>
+#include <ardour/session.h>
+#include <ardour/utils.h>
+
+#include <appleutility/CAAudioUnit.h>
+#include <appleutility/CAAUParameter.h>
+
+#include <CoreServices/CoreServices.h>
+#include <AudioUnit/AudioUnit.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace PBD;
+using namespace ARDOUR;
+
+static OSStatus
+_render_callback(void *userData,
+ AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList* ioData)
+{
+ return ((AUPlugin*)userData)->render_callback (ioActionFlags, inTimeStamp, inBusNumber, inNumberFrames, ioData);
+}
+
+AUPlugin::AUPlugin (AudioEngine& engine, Session& session, boost::shared_ptr<CAComponent> _comp)
+ : Plugin (engine, session),
+ comp (_comp),
+ unit (new CAAudioUnit),
+ initialized (false),
+ buffers (0),
+ current_maxbuf (0),
+ current_offset (0),
+ current_buffers (0),
+ frames_processed (0)
+{
+ init ();
+}
+
+AUPlugin::AUPlugin (const AUPlugin& other)
+ : Plugin (other)
+ , comp (other.get_comp())
+ , unit (new CAAudioUnit)
+ , initialized (false)
+ , buffers (0)
+ , current_maxbuf (0)
+ , current_offset (0)
+ , current_buffers (0)
+ , frames_processed (0)
+
+{
+ init ();
+}
+
+AUPlugin::~AUPlugin ()
+{
+ if (unit) {
+ unit->Uninitialize ();
+ }
+
+ if (buffers) {
+ free (buffers);
+ }
+}
+
+
+void
+AUPlugin::init ()
+{
+ OSErr err = CAAudioUnit::Open (*(comp.get()), *unit);
+
+ if (err != noErr) {
+ error << _("AudioUnit: Could not convert CAComponent to CAAudioUnit") << endmsg;
+ throw failed_constructor ();
+ }
+
+ AURenderCallbackStruct renderCallbackInfo;
+
+ renderCallbackInfo.inputProc = _render_callback;
+ renderCallbackInfo.inputProcRefCon = this;
+
+ if ((err = unit->SetProperty (kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input,
+ 0, (void*) &renderCallbackInfo, sizeof(renderCallbackInfo))) != 0) {
+ cerr << "cannot install render callback (err = " << err << ')' << endl;
+ throw failed_constructor();
+ }
+
+ unit->GetElementCount (kAudioUnitScope_Global, global_elements);
+ unit->GetElementCount (kAudioUnitScope_Input, input_elements);
+ unit->GetElementCount (kAudioUnitScope_Output, output_elements);
+
+ // set up the basic stream format. these fields do not change
+
+ streamFormat.mSampleRate = _session.frame_rate();
+ streamFormat.mFormatID = kAudioFormatLinearPCM;
+ streamFormat.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked|kAudioFormatFlagIsNonInterleaved;
+
+#ifdef __LITTLE_ENDIAN__
+ /* relax */
+#else
+ streamFormat.mFormatFlags |= kAudioFormatFlagIsBigEndian;
+#endif
+
+ streamFormat.mBitsPerChannel = 32;
+ streamFormat.mFramesPerPacket = 1;
+
+ // subject to later modification as we discover channel counts
+
+ streamFormat.mBytesPerPacket = 4;
+ streamFormat.mBytesPerFrame = 4;
+ streamFormat.mChannelsPerFrame = 1;
+
+ format_set = 0;
+
+ if (_set_block_size (_session.get_block_size())) {
+ error << _("AUPlugin: cannot set processing block size") << endmsg;
+ throw failed_constructor();
+ }
+
+ discover_parameters ();
+
+ Plugin::setup_controls ();
+}
+
+void
+AUPlugin::discover_parameters ()
+{
+ /* discover writable parameters */
+
+ AudioUnitScope scopes[] = {
+ kAudioUnitScope_Global,
+ kAudioUnitScope_Output,
+ kAudioUnitScope_Input
+ };
+
+ descriptors.clear ();
+
+ for (uint32_t i = 0; i < sizeof (scopes) / sizeof (scopes[0]); ++i) {
+
+ AUParamInfo param_info (unit->AU(), false, false, scopes[i]);
+
+ for (uint32_t i = 0; i < param_info.NumParams(); ++i) {
+
+ AUParameterDescriptor d;
+
+ d.id = param_info.ParamID (i);
+
+ const CAAUParameter* param = param_info.GetParamInfo (d.id);
+ const AudioUnitParameterInfo& info (param->ParamInfo());
+
+ const int len = CFStringGetLength (param->GetName());;
+ char local_buffer[len*2];
+ Boolean good = CFStringGetCString(param->GetName(),local_buffer,len*2,kCFStringEncodingMacRoman);
+ if (!good) {
+ d.label = "???";
+ } else {
+ d.label = local_buffer;
+ }
+
+ d.scope = param_info.GetScope ();
+ d.element = param_info.GetElement ();
+
+ /* info.units to consider */
+ /*
+ kAudioUnitParameterUnit_Generic = 0
+ kAudioUnitParameterUnit_Indexed = 1
+ kAudioUnitParameterUnit_Boolean = 2
+ kAudioUnitParameterUnit_Percent = 3
+ kAudioUnitParameterUnit_Seconds = 4
+ kAudioUnitParameterUnit_SampleFrames = 5
+ kAudioUnitParameterUnit_Phase = 6
+ kAudioUnitParameterUnit_Rate = 7
+ kAudioUnitParameterUnit_Hertz = 8
+ kAudioUnitParameterUnit_Cents = 9
+ kAudioUnitParameterUnit_RelativeSemiTones = 10
+ kAudioUnitParameterUnit_MIDINoteNumber = 11
+ kAudioUnitParameterUnit_MIDIController = 12
+ kAudioUnitParameterUnit_Decibels = 13
+ kAudioUnitParameterUnit_LinearGain = 14
+ kAudioUnitParameterUnit_Degrees = 15
+ kAudioUnitParameterUnit_EqualPowerCrossfade = 16
+ kAudioUnitParameterUnit_MixerFaderCurve1 = 17
+ kAudioUnitParameterUnit_Pan = 18
+ kAudioUnitParameterUnit_Meters = 19
+ kAudioUnitParameterUnit_AbsoluteCents = 20
+ kAudioUnitParameterUnit_Octaves = 21
+ kAudioUnitParameterUnit_BPM = 22
+ kAudioUnitParameterUnit_Beats = 23
+ kAudioUnitParameterUnit_Milliseconds = 24
+ kAudioUnitParameterUnit_Ratio = 25
+ */
+
+ /* info.flags to consider */
+
+ /*
+
+ kAudioUnitParameterFlag_CFNameRelease = (1L << 4)
+ kAudioUnitParameterFlag_HasClump = (1L << 20)
+ kAudioUnitParameterFlag_HasName = (1L << 21)
+ kAudioUnitParameterFlag_DisplayLogarithmic = (1L << 22)
+ kAudioUnitParameterFlag_IsHighResolution = (1L << 23)
+ kAudioUnitParameterFlag_NonRealTime = (1L << 24)
+ kAudioUnitParameterFlag_CanRamp = (1L << 25)
+ kAudioUnitParameterFlag_ExpertMode = (1L << 26)
+ kAudioUnitParameterFlag_HasCFNameString = (1L << 27)
+ kAudioUnitParameterFlag_IsGlobalMeta = (1L << 28)
+ kAudioUnitParameterFlag_IsElementMeta = (1L << 29)
+ kAudioUnitParameterFlag_IsReadable = (1L << 30)
+ kAudioUnitParameterFlag_IsWritable = (1L << 31)
+ */
+
+ d.lower = info.minValue;
+ d.upper = info.maxValue;
+ d.default_value = info.defaultValue;
+
+ d.integer_step = (info.unit & kAudioUnitParameterUnit_Indexed);
+ d.toggled = (info.unit & kAudioUnitParameterUnit_Boolean) ||
+ (d.integer_step && ((d.upper - d.lower) == 1.0));
+ d.sr_dependent = (info.unit & kAudioUnitParameterUnit_SampleFrames);
+ d.automatable = !d.toggled &&
+ !(info.flags & kAudioUnitParameterFlag_NonRealTime) &&
+ (info.flags & kAudioUnitParameterFlag_IsWritable);
+
+ d.logarithmic = (info.flags & kAudioUnitParameterFlag_DisplayLogarithmic);
+ d.unit = info.unit;
+
+ d.step = 1.0;
+ d.smallstep = 0.1;
+ d.largestep = 10.0;
+ d.min_unbound = 0; // lower is bound
+ d.max_unbound = 0; // upper is bound
+
+ descriptors.push_back (d);
+ }
+ }
+}
+
+
+string
+AUPlugin::unique_id () const
+{
+ return AUPluginInfo::stringify_descriptor (comp->Desc());
+}
+
+const char *
+AUPlugin::label () const
+{
+ return _info->name.c_str();
+}
+
+uint32_t
+AUPlugin::parameter_count () const
+{
+ return descriptors.size();
+}
+
+float
+AUPlugin::default_value (uint32_t port)
+{
+ if (port < descriptors.size()) {
+ return descriptors[port].default_value;
+ }
+
+ return 0;
+}
+
+nframes_t
+AUPlugin::signal_latency () const
+{
+ if (_user_latency) {
+ return _user_latency;
+ }
+
+ return unit->Latency() * _session.frame_rate();
+}
+
+void
+AUPlugin::set_parameter (uint32_t which, float val)
+{
+ if (which < descriptors.size()) {
+ const AUParameterDescriptor& d (descriptors[which]);
+ unit->SetParameter (d.id, d.scope, d.element, val);
+ }
+}
+
+float
+AUPlugin::get_parameter (uint32_t which) const
+{
+ float val = 0.0;
+ if (which < descriptors.size()) {
+ const AUParameterDescriptor& d (descriptors[which]);
+ unit->GetParameter(d.id, d.scope, d.element, val);
+ }
+ return val;
+}
+
+int
+AUPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& pd) const
+{
+ if (which < descriptors.size()) {
+ pd = descriptors[which];
+ return 0;
+ }
+ return -1;
+}
+
+uint32_t
+AUPlugin::nth_parameter (uint32_t which, bool& ok) const
+{
+ if (which < descriptors.size()) {
+ ok = true;
+ return which;
+ }
+ ok = false;
+ return 0;
+}
+
+void
+AUPlugin::activate ()
+{
+ if (!initialized) {
+ OSErr err;
+ if ((err = unit->Initialize()) != noErr) {
+ error << string_compose (_("AUPlugin: %1 cannot initialize plugin (err = %2)"), name(), err) << endmsg;
+ } else {
+ frames_processed = 0;
+ initialized = true;
+ }
+ }
+}
+
+void
+AUPlugin::deactivate ()
+{
+ unit->GlobalReset ();
+}
+
+void
+AUPlugin::set_block_size (nframes_t nframes)
+{
+ _set_block_size (nframes);
+}
+
+int
+AUPlugin::_set_block_size (nframes_t nframes)
+{
+ bool was_initialized = initialized;
+ UInt32 numFrames = nframes;
+ OSErr err;
+
+ if (initialized) {
+ unit->Uninitialize ();
+ }
+
+ if ((err = unit->SetProperty (kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global,
+ 0, &numFrames, sizeof (numFrames))) != noErr) {
+ cerr << "cannot set max frames (err = " << err << ')' << endl;
+ return -1;
+ }
+
+ if (was_initialized) {
+ activate ();
+ }
+
+ return 0;
+}
+
+int32_t
+AUPlugin::can_support_input_configuration (int32_t in)
+{
+ streamFormat.mChannelsPerFrame = in;
+ /* apple says that for non-interleaved data, these
+ values always refer to a single channel.
+ */
+ streamFormat.mBytesPerPacket = 4;
+ streamFormat.mBytesPerFrame = 4;
+
+ if (set_input_format () == 0) {
+ return 1;
+ } else {
+ return -1;
+ }
+}
+
+int
+AUPlugin::set_input_format ()
+{
+ return set_stream_format (kAudioUnitScope_Input, input_elements);
+}
+
+int
+AUPlugin::set_output_format ()
+{
+ return set_stream_format (kAudioUnitScope_Output, output_elements);
+}
+
+int
+AUPlugin::set_stream_format (int scope, uint32_t cnt)
+{
+ OSErr result;
+
+ for (uint32_t i = 0; i < cnt; ++i) {
+ if ((result = unit->SetFormat (scope, i, streamFormat)) != 0) {
+ error << string_compose (_("AUPlugin: could not set stream format for %1/%2 (err = %3)"),
+ (scope == kAudioUnitScope_Input ? "input" : "output"), i, result) << endmsg;
+ return -1;
+ }
+ }
+
+ if (scope == kAudioUnitScope_Input) {
+ format_set |= 0x1;
+ } else {
+ format_set |= 0x2;
+ }
+
+ return 0;
+}
+
+int32_t
+AUPlugin::compute_output_streams (int32_t nplugins)
+{
+ /* we will never replicate AU plugins - either they can do the I/O we need
+ or not. thus, we can ignore nplugins entirely.
+ */
+
+ if (set_output_format() == 0) {
+
+ if (buffers) {
+ free (buffers);
+ buffers = 0;
+ }
+
+ buffers = (AudioBufferList *) malloc (offsetof(AudioBufferList, mBuffers) +
+ streamFormat.mChannelsPerFrame * sizeof(AudioBuffer));
+
+ Glib::Mutex::Lock em (_session.engine().process_lock());
+ IO::MoreOutputs (streamFormat.mChannelsPerFrame);
+
+ return streamFormat.mChannelsPerFrame;
+ } else {
+ return -1;
+ }
+}
+
+uint32_t
+AUPlugin::output_streams() const
+{
+ if (!(format_set & 0x2)) {
+ warning << string_compose (_("AUPlugin: %1 output_streams() called without any format set!"), name()) << endmsg;
+ return 1;
+ }
+
+ return streamFormat.mChannelsPerFrame;
+}
+
+
+uint32_t
+AUPlugin::input_streams() const
+{
+ if (!(format_set & 0x1)) {
+ warning << _("AUPlugin: input_streams() called without any format set!") << endmsg;
+ return 1;
+ }
+ return streamFormat.mChannelsPerFrame;
+}
+
+OSStatus
+AUPlugin::render_callback(AudioUnitRenderActionFlags *ioActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ UInt32 inNumberFrames,
+ AudioBufferList* ioData)
+{
+ /* not much to do - the data is already in the buffers given to us in connect_and_run() */
+
+ if (current_maxbuf == 0) {
+ error << _("AUPlugin: render callback called illegally!") << endmsg;
+ return kAudioUnitErr_CannotDoInCurrentContext;
+ }
+
+ for (uint32_t i = 0; i < current_maxbuf; ++i) {
+ ioData->mBuffers[i].mNumberChannels = 1;
+ ioData->mBuffers[i].mDataByteSize = sizeof (Sample) * inNumberFrames;
+ ioData->mBuffers[i].mData = (*current_buffers)[i] + cb_offset + current_offset;
+ }
+
+ cb_offset += inNumberFrames;
+
+ return noErr;
+}
+
+int
+AUPlugin::connect_and_run (vector<Sample*>& bufs, uint32_t maxbuf, int32_t& in, int32_t& out, nframes_t nframes, nframes_t offset)
+{
+ AudioUnitRenderActionFlags flags = 0;
+ AudioTimeStamp ts;
+
+ current_buffers = &bufs;
+ current_maxbuf = maxbuf;
+ current_offset = offset;
+ cb_offset = 0;
+
+ buffers->mNumberBuffers = maxbuf;
+
+ for (uint32_t i = 0; i < maxbuf; ++i) {
+ buffers->mBuffers[i].mNumberChannels = 1;
+ buffers->mBuffers[i].mDataByteSize = nframes * sizeof (Sample);
+ buffers->mBuffers[i].mData = 0;
+ }
+
+ ts.mSampleTime = frames_processed;
+ ts.mFlags = kAudioTimeStampSampleTimeValid;
+
+ if (unit->Render (&flags, &ts, 0, nframes, buffers) == noErr) {
+
+ current_maxbuf = 0;
+ frames_processed += nframes;
+
+ for (uint32_t i = 0; i < maxbuf; ++i) {
+ if (bufs[i] + offset != buffers->mBuffers[i].mData) {
+ memcpy (bufs[i]+offset, buffers->mBuffers[i].mData, nframes * sizeof (Sample));
+ }
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+set<uint32_t>
+AUPlugin::automatable() const
+{
+ set<uint32_t> automates;
+
+ for (uint32_t i = 0; i < descriptors.size(); ++i) {
+ if (descriptors[i].automatable) {
+ automates.insert (i);
+ }
+ }
+
+ return automates;
+}
+
+string
+AUPlugin::describe_parameter (uint32_t param)
+{
+ return descriptors[param].label;
+}
+
+void
+AUPlugin::print_parameter (uint32_t param, char* buf, uint32_t len) const
+{
+ // NameValue stuff here
+}
+
+bool
+AUPlugin::parameter_is_audio (uint32_t) const
+{
+ return false;
+}
+
+bool
+AUPlugin::parameter_is_control (uint32_t) const
+{
+ return true;
+}
+
+bool
+AUPlugin::parameter_is_input (uint32_t) const
+{
+ return false;
+}
+
+bool
+AUPlugin::parameter_is_output (uint32_t) const
+{
+ return false;
+}
+
+XMLNode&
+AUPlugin::get_state()
+{
+ XMLNode *root = new XMLNode (state_node_name());
+ LocaleGuard lg (X_("POSIX"));
+ return *root;
+}
+
+int
+AUPlugin::set_state(const XMLNode& node)
+{
+ return -1;
+}
+
+bool
+AUPlugin::save_preset (string name)
+{
+ return false;
+}
+
+bool
+AUPlugin::load_preset (const string preset_label)
+{
+ return false;
+}
+
+vector<string>
+AUPlugin::get_presets ()
+{
+ vector<string> presets;
+
+ return presets;
+}
+
+bool
+AUPlugin::has_editor () const
+{
+ // even if the plugin doesn't have its own editor, the AU API can be used
+ // to create one that looks native.
+ return true;
+}
+
+AUPluginInfo::AUPluginInfo (boost::shared_ptr<CAComponentDescription> d)
+ : descriptor (d)
+{
+
+}
+
+AUPluginInfo::~AUPluginInfo ()
+{
+}
+
+PluginPtr
+AUPluginInfo::load (Session& session)
+{
+ try {
+ PluginPtr plugin;
+
+ boost::shared_ptr<CAComponent> comp (new CAComponent(*descriptor));
+
+ if (!comp->IsValid()) {
+ error << ("AudioUnit: not a valid Component") << endmsg;
+ } else {
+ plugin.reset (new AUPlugin (session.engine(), session, comp));
+ }
+
+ plugin->set_info (PluginInfoPtr (new AUPluginInfo (*this)));
+ return plugin;
+ }
+
+ catch (failed_constructor &err) {
+ return PluginPtr ((Plugin*) 0);
+ }
+}
+
+PluginInfoList
+AUPluginInfo::discover ()
+{
+ PluginInfoList plugs;
+
+ discover_fx (plugs);
+ discover_music (plugs);
+
+ return plugs;
+}
+
+void
+AUPluginInfo::discover_music (PluginInfoList& plugs)
+{
+ CAComponentDescription desc;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+ desc.componentSubType = 0;
+ desc.componentManufacturer = 0;
+ desc.componentType = kAudioUnitType_MusicEffect;
+
+ discover_by_description (plugs, desc);
+}
+
+void
+AUPluginInfo::discover_fx (PluginInfoList& plugs)
+{
+ CAComponentDescription desc;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+ desc.componentSubType = 0;
+ desc.componentManufacturer = 0;
+ desc.componentType = kAudioUnitType_Effect;
+
+ discover_by_description (plugs, desc);
+}
+
+void
+AUPluginInfo::discover_by_description (PluginInfoList& plugs, CAComponentDescription& desc)
+{
+ Component comp = 0;
+
+ comp = FindNextComponent (NULL, &desc);
+
+ while (comp != NULL) {
+ CAComponentDescription temp;
+ GetComponentInfo (comp, &temp, NULL, NULL, NULL);
+
+ AUPluginInfoPtr info (new AUPluginInfo
+ (boost::shared_ptr<CAComponentDescription> (new CAComponentDescription(temp))));
+
+ /* no panners, format converters or i/o AU's for our purposes
+ */
+
+ switch (info->descriptor->Type()) {
+ case kAudioUnitType_Panner:
+ case kAudioUnitType_OfflineEffect:
+ case kAudioUnitType_FormatConverter:
+ continue;
+ default:
+ break;
+ }
+
+ switch (info->descriptor->SubType()) {
+ case kAudioUnitSubType_DefaultOutput:
+ case kAudioUnitSubType_SystemOutput:
+ case kAudioUnitSubType_GenericOutput:
+ case kAudioUnitSubType_AUConverter:
+ continue;
+ break;
+
+ case kAudioUnitSubType_DLSSynth:
+ info->category = "DLSSynth";
+ break;
+
+ case kAudioUnitType_MusicEffect:
+ info->category = "MusicEffect";
+ break;
+
+ case kAudioUnitSubType_Varispeed:
+ info->category = "Varispeed";
+ break;
+
+ case kAudioUnitSubType_Delay:
+ info->category = "Delay";
+ break;
+
+ case kAudioUnitSubType_LowPassFilter:
+ info->category = "LowPassFilter";
+ break;
+
+ case kAudioUnitSubType_HighPassFilter:
+ info->category = "HighPassFilter";
+ break;
+
+ case kAudioUnitSubType_BandPassFilter:
+ info->category = "BandPassFilter";
+ break;
+
+ case kAudioUnitSubType_HighShelfFilter:
+ info->category = "HighShelfFilter";
+ break;
+
+ case kAudioUnitSubType_LowShelfFilter:
+ info->category = "LowShelfFilter";
+ break;
+
+ case kAudioUnitSubType_ParametricEQ:
+ info->category = "ParametricEQ";
+ break;
+
+ case kAudioUnitSubType_GraphicEQ:
+ info->category = "GraphicEQ";
+ break;
+
+ case kAudioUnitSubType_PeakLimiter:
+ info->category = "PeakLimiter";
+ break;
+
+ case kAudioUnitSubType_DynamicsProcessor:
+ info->category = "DynamicsProcessor";
+ break;
+
+ case kAudioUnitSubType_MultiBandCompressor:
+ info->category = "MultiBandCompressor";
+ break;
+
+ case kAudioUnitSubType_MatrixReverb:
+ info->category = "MatrixReverb";
+ break;
+
+ case kAudioUnitType_Mixer:
+ info->category = "Mixer";
+ break;
+
+ case kAudioUnitSubType_StereoMixer:
+ info->category = "StereoMixer";
+ break;
+
+ case kAudioUnitSubType_3DMixer:
+ info->category = "3DMixer";
+ break;
+
+ case kAudioUnitSubType_MatrixMixer:
+ info->category = "MatrixMixer";
+ break;
+
+ default:
+ info->category = "";
+ }
+
+ AUPluginInfo::get_names (temp, info->name, info->creator);
+
+ info->type = ARDOUR::AudioUnit;
+ info->unique_id = stringify_descriptor (*info->descriptor);
+
+ /* mark the plugin as having flexible i/o */
+
+ info->n_inputs = -1;
+ info->n_outputs = -1;
+
+ plugs.push_back (info);
+
+ comp = FindNextComponent (comp, &desc);
+ }
+}
+
+void
+AUPluginInfo::get_names (CAComponentDescription& comp_desc, std::string& name, Glib::ustring& maker)
+{
+ CFStringRef itemName = NULL;
+
+ // Marc Poirier-style item name
+ CAComponent auComponent (comp_desc);
+ if (auComponent.IsValid()) {
+ CAComponentDescription dummydesc;
+ Handle nameHandle = NewHandle(sizeof(void*));
+ if (nameHandle != NULL) {
+ OSErr err = GetComponentInfo(auComponent.Comp(), &dummydesc, nameHandle, NULL, NULL);
+ if (err == noErr) {
+ ConstStr255Param nameString = (ConstStr255Param) (*nameHandle);
+ if (nameString != NULL) {
+ itemName = CFStringCreateWithPascalString(kCFAllocatorDefault, nameString, CFStringGetSystemEncoding());
+ }
+ }
+ DisposeHandle(nameHandle);
+ }
+ }
+
+ // if Marc-style fails, do the original way
+ if (itemName == NULL) {
+ CFStringRef compTypeString = UTCreateStringForOSType(comp_desc.componentType);
+ CFStringRef compSubTypeString = UTCreateStringForOSType(comp_desc.componentSubType);
+ CFStringRef compManufacturerString = UTCreateStringForOSType(comp_desc.componentManufacturer);
+
+ itemName = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@ - %@ - %@"),
+ compTypeString, compManufacturerString, compSubTypeString);
+
+ if (compTypeString != NULL)
+ CFRelease(compTypeString);
+ if (compSubTypeString != NULL)
+ CFRelease(compSubTypeString);
+ if (compManufacturerString != NULL)
+ CFRelease(compManufacturerString);
+ }
+
+ string str = CFStringRefToStdString(itemName);
+ string::size_type colon = str.find (':');
+
+ if (colon) {
+ name = str.substr (colon+1);
+ maker = str.substr (0, colon);
+ // strip_whitespace_edges (maker);
+ // strip_whitespace_edges (name);
+ } else {
+ name = str;
+ maker = "unknown";
+ }
+}
+
+// from CAComponentDescription.cpp (in libs/appleutility in ardour source)
+extern char *StringForOSType (OSType t, char *writeLocation);
+
+std::string
+AUPluginInfo::stringify_descriptor (const CAComponentDescription& desc)
+{
+ char str[24];
+ stringstream s;
+
+ s << StringForOSType (desc.Type(), str);
+ s << " - ";
+
+ s << StringForOSType (desc.SubType(), str);
+ s << " - ";
+
+ s << StringForOSType (desc.Manu(), str);
+
+ return s.str();
+}
diff --git a/libs/ardour/audioanalyser.cc b/libs/ardour/audioanalyser.cc
new file mode 100644
index 0000000000..98c4206301
--- /dev/null
+++ b/libs/ardour/audioanalyser.cc
@@ -0,0 +1,166 @@
+#include <cstring>
+#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_key (key)
+{
+ /* create VAMP plugin and initialize */
+
+ if (initialize_plugin (plugin_key, sample_rate)) {
+ error << string_compose (_("cannot load VAMP plugin \"%1\""), key) << endmsg;
+ throw failed_constructor();
+ }
+}
+
+AudioAnalyser::~AudioAnalyser ()
+{
+ delete plugin;
+}
+
+int
+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 features;
+ int ret = -1;
+ bool done = false;
+ Sample* data = 0;
+ nframes64_t len = src->readable_length();
+ nframes64_t pos = 0;
+ float* bufs[1] = { 0 };
+ string tmp_path;
+
+ if (!path.empty()) {
+
+ /* store data in tmp file, not the real one */
+
+ tmp_path = path;
+ tmp_path += ".tmp";
+
+ ofile.open (tmp_path.c_str());
+ if (!ofile) {
+ goto out;
+ }
+ }
+
+ 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) {
+ goto out;
+ }
+
+ /* zero fill buffer if necessary */
+
+ if (to_read != bufsize) {
+ memset (data + to_read, 0, (bufsize - to_read) * sizeof (Sample));
+ }
+
+ features = plugin->process (bufs, RealTime::fromSeconds ((double) pos / sample_rate));
+
+ if (use_features (features, (path.empty() ? 0 : &ofile))) {
+ goto out;
+ }
+
+ pos += min (stepsize, to_read);
+
+ if (pos >= len) {
+ done = true;
+ }
+ }
+
+ /* finish up VAMP plugin */
+
+ features = plugin->getRemainingFeatures ();
+
+ if (use_features (features, (path.empty() ? &ofile : 0))) {
+ goto out;
+ }
+
+ ret = 0;
+
+ out:
+ /* works even if it has not been opened */
+ ofile.close ();
+
+ if (ret) {
+ g_remove (tmp_path.c_str());
+ } else if (!path.empty()) {
+ /* move the data file to the requested path */
+ g_rename (tmp_path.c_str(), path.c_str());
+ }
+
+ if (data) {
+ delete [] data;
+ }
+
+ return ret;
+}
+
diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc
new file mode 100644
index 0000000000..8bbed46733
--- /dev/null
+++ b/libs/ardour/audioengine.cc
@@ -0,0 +1,1379 @@
+/*
+ Copyright (C) 2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <unistd.h>
+#include <cerrno>
+#include <vector>
+#include <exception>
+#include <stdexcept>
+#include <sstream>
+
+#include <glibmm/timer.h>
+#include <pbd/pthread_utils.h>
+#include <pbd/stacktrace.h>
+#include <pbd/unknown_type.h>
+
+#include <midi++/jack.h>
+
+#include <ardour/audioengine.h>
+#include <ardour/buffer.h>
+#include <ardour/port.h>
+#include <ardour/jack_audio_port.h>
+#include <ardour/jack_midi_port.h>
+#include <ardour/audio_port.h>
+#include <ardour/session.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/utils.h>
+#ifdef VST_SUPPORT
+#include <fst.h>
+#endif
+
+#include <ardour/timestamps.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+gint AudioEngine::m_meter_exit;
+
+static void
+ardour_jack_error (const char* msg)
+{
+ error << "JACK: " << msg << endmsg;
+}
+
+AudioEngine::AudioEngine (string client_name)
+ : ports (new Ports)
+{
+ session = 0;
+ session_remove_pending = false;
+ _running = false;
+ _has_run = false;
+ last_monitor_check = 0;
+ monitor_check_interval = max_frames;
+ _processed_frames = 0;
+ _freewheeling = false;
+ _usecs_per_cycle = 0;
+ _jack = 0;
+ _frame_rate = 0;
+ _buffer_size = 0;
+ _freewheeling = false;
+ _freewheel_thread_registered = false;
+
+ m_meter_thread = 0;
+ g_atomic_int_set (&m_meter_exit, 0);
+
+ if (connect_to_jack (client_name)) {
+ throw NoBackendAvailable ();
+ }
+ Port::set_engine (this);
+}
+
+AudioEngine::~AudioEngine ()
+{
+ {
+ Glib::Mutex::Lock tm (_process_lock);
+ session_removed.signal ();
+
+ if (_running) {
+ jack_client_close (_jack);
+ }
+
+ stop_metering_thread ();
+ }
+}
+
+jack_client_t*
+AudioEngine::jack() const
+{
+ return _jack;
+}
+
+void
+_thread_init_callback (void *arg)
+{
+ /* make sure that anybody who needs to know about this thread
+ knows about it.
+ */
+
+ PBD::ThreadCreatedWithRequestSize (pthread_self(), X_("Audioengine"), 4096);
+ MIDI::JACK_MidiPort::set_process_thread (pthread_self());
+}
+
+int
+AudioEngine::start ()
+{
+ if (!_running) {
+
+ if (session) {
+ nframes_t blocksize = jack_get_buffer_size (_jack);
+
+ BootMessage (_("Connect session to engine"));
+
+ session->set_block_size (blocksize);
+ session->set_frame_rate (jack_get_sample_rate (_jack));
+
+ /* page in as much of the session process code as we
+ can before we really start running.
+ */
+
+ session->process (blocksize);
+ session->process (blocksize);
+ session->process (blocksize);
+ session->process (blocksize);
+ session->process (blocksize);
+ session->process (blocksize);
+ session->process (blocksize);
+ session->process (blocksize);
+ }
+
+ _processed_frames = 0;
+ last_monitor_check = 0;
+
+ jack_on_shutdown (_jack, halted, this);
+ jack_set_graph_order_callback (_jack, _graph_order_callback, this);
+ jack_set_thread_init_callback (_jack, _thread_init_callback, this);
+ jack_set_process_callback (_jack, _process_callback, this);
+ jack_set_sample_rate_callback (_jack, _sample_rate_callback, this);
+ jack_set_buffer_size_callback (_jack, _bufsize_callback, this);
+ jack_set_xrun_callback (_jack, _xrun_callback, this);
+ jack_set_sync_callback (_jack, _jack_sync_callback, this);
+ jack_set_freewheel_callback (_jack, _freewheel_callback, this);
+
+ if (Config->get_jack_time_master()) {
+ jack_set_timebase_callback (_jack, 0, _jack_timebase_callback, this);
+ }
+
+ if (jack_activate (_jack) == 0) {
+ _running = true;
+ _has_run = true;
+ Running(); /* EMIT SIGNAL */
+ } else {
+ // error << _("cannot activate JACK client") << endmsg;
+ }
+
+ start_metering_thread();
+ }
+
+ return _running ? 0 : -1;
+}
+
+int
+AudioEngine::stop (bool forever)
+{
+ if (_running) {
+ _running = false;
+ stop_metering_thread ();
+ if (forever) {
+ jack_client_t* foo = _jack;
+ _jack = 0;
+ jack_client_close (foo);
+ } else {
+ jack_deactivate (_jack);
+ }
+ Stopped(); /* EMIT SIGNAL */
+ }
+
+ return _running ? -1 : 0;
+}
+
+
+bool
+AudioEngine::get_sync_offset (nframes_t& offset) const
+{
+
+#ifdef HAVE_JACK_VIDEO_SUPPORT
+
+ jack_position_t pos;
+
+ (void) jack_transport_query (_jack, &pos);
+
+ if (pos.valid & JackVideoFrameOffset) {
+ offset = pos.video_offset;
+ return true;
+ }
+
+#endif
+
+ return false;
+}
+
+void
+AudioEngine::_jack_timebase_callback (jack_transport_state_t state, nframes_t nframes,
+ jack_position_t* pos, int new_position, void *arg)
+{
+ static_cast<AudioEngine*> (arg)->jack_timebase_callback (state, nframes, pos, new_position);
+}
+
+void
+AudioEngine::jack_timebase_callback (jack_transport_state_t state, nframes_t nframes,
+ jack_position_t* pos, int new_position)
+{
+ if (_jack && session && session->synced_to_jack()) {
+ session->jack_timebase_callback (state, nframes, pos, new_position);
+ }
+}
+
+int
+AudioEngine::_jack_sync_callback (jack_transport_state_t state, jack_position_t* pos, void* arg)
+{
+ return static_cast<AudioEngine*> (arg)->jack_sync_callback (state, pos);
+}
+
+int
+AudioEngine::jack_sync_callback (jack_transport_state_t state, jack_position_t* pos)
+{
+ if (_jack && session) {
+ return session->jack_sync_callback (state, pos);
+ }
+
+ return true;
+}
+
+int
+AudioEngine::_xrun_callback (void *arg)
+{
+ AudioEngine* ae = static_cast<AudioEngine*> (arg);
+ if (ae->jack()) {
+ ae->Xrun (); /* EMIT SIGNAL */
+ }
+ return 0;
+}
+
+int
+AudioEngine::_graph_order_callback (void *arg)
+{
+ AudioEngine* ae = static_cast<AudioEngine*> (arg);
+ if (ae->jack()) {
+ ae->GraphReordered (); /* EMIT SIGNAL */
+ }
+ return 0;
+}
+
+/** Wrapped which is called by JACK as its process callback. It is just
+ * here to get us back into C++ land by calling AudioEngine::process_callback()
+ * @param nframes Number of frames passed by JACK.
+ * @param arg User argument passed by JACK, which will be the AudioEngine*.
+ */
+int
+AudioEngine::_process_callback (nframes_t nframes, void *arg)
+{
+ return static_cast<AudioEngine *> (arg)->process_callback (nframes);
+}
+
+void
+AudioEngine::_freewheel_callback (int onoff, void *arg)
+{
+ static_cast<AudioEngine*>(arg)->_freewheeling = onoff;
+}
+
+/** Method called by JACK (via _process_callback) which says that there
+ * is work to be done.
+ * @param nframes Number of frames to process.
+ */
+int
+AudioEngine::process_callback (nframes_t nframes)
+{
+ // CycleTimer ct ("AudioEngine::process");
+ Glib::Mutex::Lock tm (_process_lock, Glib::TRY_LOCK);
+
+ /// The number of frames that will have been processed when we've finished
+ nframes_t next_processed_frames;
+
+ /* handle wrap around of total frames counter */
+
+ if (max_frames - _processed_frames < nframes) {
+ next_processed_frames = nframes - (max_frames - _processed_frames);
+ } else {
+ next_processed_frames = _processed_frames + nframes;
+ }
+
+ if (!tm.locked() || session == 0) {
+ /* return having done nothing */
+ _processed_frames = next_processed_frames;
+ return 0;
+ }
+
+ if (session_remove_pending) {
+ /* perform the actual session removal */
+ session = 0;
+ session_remove_pending = false;
+ session_removed.signal();
+ _processed_frames = next_processed_frames;
+ return 0;
+ }
+
+ if (_freewheeling) {
+ /* emit the Freewheel signal and stop freewheeling in the event of trouble */
+ if (Freewheel (nframes)) {
+ cerr << "Freewheeling returned non-zero!\n";
+ _freewheeling = false;
+ jack_set_freewheel (_jack, false);
+ }
+ return 0;
+ }
+
+ boost::shared_ptr<Ports> p = ports.reader();
+
+ // Prepare ports (ie read data if necessary)
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+ (*i)->cycle_start (nframes, 0);
+ }
+
+ if (session) {
+ session->process (nframes);
+ }
+
+ // Finalize ports (ie write data if necessary)
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+ (*i)->cycle_end (nframes, 0);
+ }
+
+ if (!_running) {
+ _processed_frames = next_processed_frames;
+ return 0;
+ }
+
+ if (last_monitor_check + monitor_check_interval < next_processed_frames) {
+
+ boost::shared_ptr<Ports> p = ports.reader();
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+
+ Port *port = (*i);
+ bool x;
+
+ if (port->_last_monitor != (x = port->monitoring_input ())) {
+ port->_last_monitor = x;
+ /* XXX I think this is dangerous, due to
+ a likely mutex in the signal handlers ...
+ */
+ port->MonitorInputChanged (x); /* EMIT SIGNAL */
+ }
+ }
+ last_monitor_check = next_processed_frames;
+ }
+
+ if (session->silent()) {
+
+ boost::shared_ptr<Ports> p = ports.reader();
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+
+ Port *port = (*i);
+
+ if (port->sends_output()) {
+ port->get_buffer().silence(nframes);
+ }
+ }
+ }
+
+ _processed_frames = next_processed_frames;
+ return 0;
+}
+
+int
+AudioEngine::_sample_rate_callback (nframes_t nframes, void *arg)
+{
+ return static_cast<AudioEngine *> (arg)->jack_sample_rate_callback (nframes);
+}
+
+int
+AudioEngine::jack_sample_rate_callback (nframes_t nframes)
+{
+ _frame_rate = nframes;
+ _usecs_per_cycle = (int) floor ((((double) frames_per_cycle() / nframes)) * 1000000.0);
+
+ /* check for monitor input change every 1/10th of second */
+
+ monitor_check_interval = nframes / 10;
+ last_monitor_check = 0;
+
+ if (session) {
+ session->set_frame_rate (nframes);
+ }
+
+ SampleRateChanged (nframes); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+int
+AudioEngine::_bufsize_callback (nframes_t nframes, void *arg)
+{
+ return static_cast<AudioEngine *> (arg)->jack_bufsize_callback (nframes);
+}
+
+int
+AudioEngine::jack_bufsize_callback (nframes_t nframes)
+{
+ _buffer_size = nframes;
+ _usecs_per_cycle = (int) floor ((((double) nframes / frame_rate())) * 1000000.0);
+ last_monitor_check = 0;
+
+ boost::shared_ptr<Ports> p = ports.reader();
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+ (*i)->reset();
+ }
+
+ if (session) {
+ session->set_block_size (_buffer_size);
+ }
+
+ return 0;
+}
+
+void
+AudioEngine::stop_metering_thread ()
+{
+ if (m_meter_thread) {
+ g_atomic_int_set (&m_meter_exit, 1);
+ m_meter_thread->join ();
+ m_meter_thread = 0;
+ }
+}
+
+void
+AudioEngine::start_metering_thread ()
+{
+ if (m_meter_thread == 0) {
+ g_atomic_int_set (&m_meter_exit, 0);
+ m_meter_thread = Glib::Thread::create (sigc::mem_fun(this, &AudioEngine::meter_thread),
+ 500000, true, true, Glib::THREAD_PRIORITY_NORMAL);
+ }
+}
+
+void
+AudioEngine::meter_thread ()
+{
+ while (true) {
+ Glib::usleep (10000); /* 1/100th sec interval */
+ if (g_atomic_int_get(&m_meter_exit)) {
+ break;
+ }
+ IO::update_meters ();
+ }
+}
+
+void
+AudioEngine::set_session (Session *s)
+{
+ Glib::Mutex::Lock pl (_process_lock);
+
+ if (!session) {
+
+ session = s;
+
+ nframes_t blocksize = jack_get_buffer_size (_jack);
+
+ /* page in as much of the session process code as we
+ can before we really start running.
+ */
+
+ boost::shared_ptr<Ports> p = ports.reader();
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+ (*i)->cycle_start (blocksize, 0);
+ }
+
+ s->process (blocksize);
+ s->process (blocksize);
+ s->process (blocksize);
+ s->process (blocksize);
+ s->process (blocksize);
+ s->process (blocksize);
+ s->process (blocksize);
+ s->process (blocksize);
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+ (*i)->cycle_end (blocksize, 0);
+ }
+ }
+}
+
+void
+AudioEngine::remove_session ()
+{
+ Glib::Mutex::Lock lm (_process_lock);
+
+ if (_running) {
+
+ if (session) {
+ session_remove_pending = true;
+ session_removed.wait(_process_lock);
+ }
+
+ } else {
+ session = 0;
+ }
+
+ //FIXME: Preliminary bugfix for http://tracker.ardour.org/view.php?id=1985
+ //remove_all_ports ();
+}
+
+void
+AudioEngine::port_registration_failure (const std::string& portname)
+{
+ string full_portname = jack_client_name;
+ full_portname += ':';
+ full_portname += portname;
+
+
+ jack_port_t* p = jack_port_by_name (_jack, full_portname.c_str());
+ string reason;
+
+ if (p) {
+ reason = _("a port with this name already exists: check for duplicated track/bus names");
+ } else {
+ reason = _("unknown error");
+ }
+
+ throw PortRegistrationFailure (string_compose (_("AudioEngine: cannot register port \"%1\": %2"), portname, reason).c_str());
+}
+
+Port *
+AudioEngine::register_port (DataType dtype, const string& portname, bool input, bool publish)
+{
+ Port* newport = 0;
+
+ /*cerr << "trying to register port with name " << portname << endl;*/
+ try {
+ if (dtype == DataType::AUDIO) {
+ newport = new AudioPort (portname, (input ? Port::IsInput : Port::IsOutput), publish, frames_per_cycle());
+ } else if (dtype == DataType::MIDI) {
+ newport = new MidiPort (portname, (input ? Port::IsInput : Port::IsOutput), publish, frames_per_cycle());
+ } else {
+ throw unknown_type();
+ }
+
+ /*cerr << "successfully got port " << portname << " with address " << newport << endl;*/
+
+ RCUWriter<Ports> writer (ports);
+ boost::shared_ptr<Ports> ps = writer.get_copy ();
+ /*cerr << "Address of ports list: " << ps << endl
+ << "Ports set size before insert: " << ps->size() << endl;*/
+ ps->insert (ps->begin(), newport);
+ /*cerr << "Ports set size after insert: " << ps->size() << endl;*/
+
+ /* writer goes out of scope, forces update */
+
+ return newport;
+ }
+
+ catch (...) {
+ throw PortRegistrationFailure("unable to create port (unknown type?)");
+ }
+}
+
+Port*
+AudioEngine::get_port (const std::string& full_name)
+{
+ boost::shared_ptr<Ports> p = ports.reader();
+
+ for (Ports::iterator i = p->begin(); i != p->end(); ++i) {
+ //cerr << "comparing port name '" << (*i)->name() << "' with '" << full_name << "'" << endl;
+ if ((*i)->name() == full_name) {
+ return *i;
+ }
+ }
+ return 0;
+}
+
+
+Port *
+AudioEngine::register_input_port (DataType type, const string& portname, bool publish)
+{
+ return register_port (type, portname, true, publish);
+}
+
+Port *
+AudioEngine::register_output_port (DataType type, const string& portname, bool publish)
+{
+ return register_port (type, portname, false, publish);
+}
+
+int
+AudioEngine::unregister_port (Port& port)
+{
+ /* caller must hold process lock */
+
+ cerr << "about to unregister Port xx x" << &port << "\n";
+
+ if (!_running) {
+ /* probably happening when the engine has been halted by JACK,
+ in which case, there is nothing we can do here.
+ */
+ cerr << "not running\n";
+ return 0;
+ }
+
+ {
+ cerr << "before getcopy\n";
+
+ RCUWriter<Ports> writer (ports);
+ boost::shared_ptr<Ports> ps = writer.get_copy ();
+
+ cerr << "Ports set size: " << ps.get()->size() << endl;
+
+ for (Ports::iterator i = ps->begin(); i != ps->end(); ++i) {
+ cerr << "before delete" << endl;
+ if ((*i) == &port) {
+ cerr << "About to delete " << &port << endl;
+ delete *i;
+ ps->erase (i);
+ cerr << "After erasing ports size: " << ps->size();
+ break;
+ }
+ }
+
+ /* writer goes out of scope, forces update */
+ }
+
+ cerr << "before remove_connections\n";
+ remove_connections_for (port);
+
+ return 0;
+}
+
+int
+AudioEngine::connect (const string& source, const string& destination)
+{
+ int ret;
+
+ if (!_running) {
+ if (!_has_run) {
+ fatal << _("connect called before engine was started") << endmsg;
+ /*NOTREACHED*/
+ } else {
+ return -1;
+ }
+ }
+
+ string s = make_port_name_non_relative (source);
+ string d = make_port_name_non_relative (destination);
+
+ //cerr << "Trying to connect source: " << s << " with destination " << d << endl;
+
+ Port* src = get_port (s);
+ Port* dst = get_port (d);
+
+ if (src && dst) {
+
+ /* both ports are known to us, so do the internal connect stuff */
+
+ if ((ret = src->connect (*dst)) == 0) {
+ ret = dst->connect (*src);
+ }
+
+ } else if (src || dst) {
+
+ /* one port is known to us, try to connect it to something external */
+
+ PortConnectableByName* pcn;
+ string other;
+
+ if (src) {
+ pcn = dynamic_cast<PortConnectableByName*>(src);
+ other = d;
+ } else {
+ pcn = dynamic_cast<PortConnectableByName*>(dst);
+ other = s;
+ }
+
+ if (pcn) {
+ ret = pcn->connect (other);
+ } else {
+ ret = -1;
+ }
+
+ } else {
+
+ /* neither port is known to us, and this API isn't intended for use as a general patch bay */
+
+ ret = -1;
+
+ }
+
+ if (ret > 0) {
+ error << string_compose(_("AudioEngine: connection already exists: %1 (%2) to %3 (%4)"),
+ source, s, destination, d)
+ << endmsg;
+ } else if (ret < 0) {
+ error << string_compose(_("AudioEngine: cannot connect %1 (%2) to %3 (%4)"),
+ source, s, destination, d)
+ << endmsg;
+ }
+
+ return ret;
+}
+
+int
+AudioEngine::disconnect (const string& source, const string& destination)
+{
+ int ret;
+
+ if (!_running) {
+ if (!_has_run) {
+ fatal << _("disconnect called before engine was started") << endmsg;
+ /*NOTREACHED*/
+ } else {
+ return -1;
+ }
+ }
+
+ string s = make_port_name_non_relative (source);
+ string d = make_port_name_non_relative (destination);
+
+ //cerr << "trying to disconnect port '" << s << "' from port '" << d << endl;
+
+ Port* src = get_port (s);
+ Port* dst = get_port (d);
+
+ if (src && dst) {
+
+ /* both ports are known to us, so do the internal connect stuff */
+
+ if ((ret = src->disconnect (*dst)) == 0) {
+ ret = dst->disconnect (*src);
+ }
+
+ } else if (src || dst) {
+
+ /* one port is known to us, try to connect it to something external */
+
+
+ PortConnectableByName* pcn;
+ string other;
+
+ if (src) {
+ pcn = dynamic_cast<PortConnectableByName*>(src);
+ other = d;
+ } else {
+ pcn = dynamic_cast<PortConnectableByName*>(dst);
+ other = s;
+ }
+
+ if (pcn) {
+ ret = pcn->disconnect (other);
+ } else {
+ ret = -1;
+ }
+
+ } else {
+
+ /* neither port is known to us, and this API isn't intended for use as a general patch bay */
+
+ ret = -1;
+
+ }
+
+ return ret;
+}
+
+int
+AudioEngine::disconnect (Port& port)
+{
+ if (!_running) {
+ if (!_has_run) {
+ fatal << _("disconnect called before engine was started") << endmsg;
+ /*NOTREACHED*/
+ } else {
+ return -1;
+ }
+ }
+
+ return port.disconnect_all ();
+}
+
+ARDOUR::nframes_t
+AudioEngine::frame_rate ()
+{
+ if (_jack) {
+ if (_frame_rate == 0) {
+ return (_frame_rate = jack_get_sample_rate (_jack));
+ } else {
+ return _frame_rate;
+ }
+ } else {
+ fatal << X_("programming error: AudioEngine::frame_rate() called while disconnected from JACK")
+ << endmsg;
+ /*NOTREACHED*/
+ return 0;
+ }
+}
+
+ARDOUR::nframes_t
+AudioEngine::frames_per_cycle ()
+{
+ if (_jack) {
+ if (_buffer_size == 0) {
+ return (_buffer_size = jack_get_buffer_size (_jack));
+ } else {
+ return _buffer_size;
+ }
+ } else {
+ fatal << X_("programming error: AudioEngine::frame_rate() called while disconnected from JACK")
+ << endmsg;
+ /*NOTREACHED*/
+ return 0;
+ }
+}
+
+/** Get a port by name.
+ * Note this can return NULL, it will NOT create a port if it is not found (any more).
+ */
+Port *
+AudioEngine::get_port_by_name (const string& portname, bool keep) const
+{
+ Glib::Mutex::Lock lm (_process_lock);
+
+ if (!_running) {
+ if (!_has_run) {
+ fatal << _("get_port_by_name() called before engine was started") << endmsg;
+ /*NOTREACHED*/
+ } else {
+ return 0;
+ }
+ }
+
+ boost::shared_ptr<Ports> pr = ports.reader();
+
+ for (Ports::iterator i = pr->begin(); i != pr->end(); ++i) {
+ if (portname == (*i)->name()) {
+ return (*i);
+ }
+ }
+
+ return 0;
+}
+
+const char **
+AudioEngine::get_ports (const string& port_name_pattern, const string& type_name_pattern, uint32_t flags)
+{
+ if (!_running) {
+ if (!_has_run) {
+ fatal << _("get_ports called before engine was started") << endmsg;
+ /*NOTREACHED*/
+ } else {
+ return 0;
+ }
+ }
+ return jack_get_ports (_jack, port_name_pattern.c_str(), type_name_pattern.c_str(), flags);
+}
+
+void
+AudioEngine::halted (void *arg)
+{
+ AudioEngine* ae = static_cast<AudioEngine *> (arg);
+ bool was_running = ae->_running;
+
+ ae->stop_metering_thread ();
+
+ ae->_running = false;
+ ae->_buffer_size = 0;
+ ae->_frame_rate = 0;
+ ae->_jack = 0;
+
+ if (was_running) {
+ ae->Halted(); /* EMIT SIGNAL */
+ }
+}
+
+bool
+AudioEngine::can_request_hardware_monitoring ()
+{
+ const char ** ports;
+
+ if (!_jack) {
+ return 0;
+ }
+
+ if ((ports = jack_get_ports (_jack, NULL, JACK_DEFAULT_AUDIO_TYPE, JackPortCanMonitor)) == 0) {
+ return false;
+ }
+
+ free (ports);
+
+ return true;
+}
+
+
+uint32_t
+AudioEngine::n_physical_outputs () const
+{
+ const char ** ports;
+ uint32_t i = 0;
+
+ if (!_jack) {
+ return 0;
+ }
+
+ if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == 0) {
+ return 0;
+ }
+
+ for (i = 0; ports[i]; ++i);
+ free (ports);
+
+ return i;
+}
+
+uint32_t
+AudioEngine::n_physical_inputs () const
+{
+ const char ** ports;
+ uint32_t i = 0;
+
+ if (!_jack) {
+ return 0;
+ }
+
+ if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == 0) {
+ return 0;
+ }
+
+ if (ports) {
+ for (i = 0; ports[i]; ++i);
+ free (ports);
+ }
+ return i;
+}
+
+void
+AudioEngine::get_physical_inputs (vector<string>& ins)
+{
+ const char ** ports;
+ uint32_t i = 0;
+
+ if (!_jack) {
+ return;
+ }
+
+ if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsOutput)) == 0) {
+ return;
+ }
+
+ if (ports) {
+ for (i = 0; ports[i]; ++i) {
+ ins.push_back (ports[i]);
+ }
+ free (ports);
+ }
+}
+
+void
+AudioEngine::get_physical_outputs (vector<string>& outs)
+{
+ const char ** ports;
+ uint32_t i = 0;
+
+ if (!_jack) {
+ return;
+ }
+
+ if ((ports = jack_get_ports (_jack, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == 0) {
+ return;
+ }
+
+ if (ports) {
+ for (i = 0; ports[i]; ++i) {
+ outs.push_back (ports[i]);
+ }
+ free (ports);
+ }
+}
+
+string
+AudioEngine::get_nth_physical (DataType type, uint32_t n, int flag)
+{
+ const char ** ports;
+ uint32_t i;
+ string ret;
+
+ assert(type != DataType::NIL);
+
+ if (!_running || !_jack) {
+ if (!_has_run) {
+ fatal << _("get_nth_physical called before engine was started") << endmsg;
+ /*NOTREACHED*/
+ } else {
+ return "";
+ }
+ }
+
+ ports = jack_get_ports (_jack, NULL, type.to_jack_type(), JackPortIsPhysical|flag);
+
+ if (ports == 0) {
+ return "";
+ }
+
+ for (i = 0; i < n && ports[i]; ++i);
+
+ if (ports[i]) {
+ ret = ports[i];
+ }
+
+ free ((char *) ports);
+
+ return ret;
+}
+
+ARDOUR::nframes_t
+AudioEngine::get_port_total_latency (const Port& port)
+{
+ return port.total_latency ();
+}
+
+void
+AudioEngine::update_total_latency (const Port& port)
+{
+ if (!_jack) {
+ fatal << _("update_total_latency() called with no JACK client connection") << endmsg;
+ /*NOTREACHED*/
+ }
+
+ if (!_running) {
+ if (!_has_run) {
+ fatal << _("update_total_latency() called before engine was started") << endmsg;
+ /*NOTREACHED*/
+ }
+ }
+
+ port.recompute_total_latency ();
+}
+
+void
+AudioEngine::transport_stop ()
+{
+ // cerr << "tell JACK to stop\n";
+ if (_jack) {
+ jack_transport_stop (_jack);
+ }
+}
+
+void
+AudioEngine::transport_start ()
+{
+ // cerr << "tell JACK to start\n";
+ if (_jack) {
+ jack_transport_start (_jack);
+ }
+}
+
+void
+AudioEngine::transport_locate (nframes_t where)
+{
+ // cerr << "tell JACK to locate to " << where << endl;
+ if (_jack) {
+ jack_transport_locate (_jack, where);
+ }
+}
+
+AudioEngine::TransportState
+AudioEngine::transport_state ()
+{
+ if (_jack) {
+ jack_position_t pos;
+ return (TransportState) jack_transport_query (_jack, &pos);
+ } else {
+ return (TransportState) JackTransportStopped;
+ }
+}
+
+int
+AudioEngine::reset_timebase ()
+{
+ if (_jack) {
+ if (Config->get_jack_time_master()) {
+ return jack_set_timebase_callback (_jack, 0, _jack_timebase_callback, this);
+ } else {
+ return jack_release_timebase (_jack);
+ }
+ } else {
+ return -1;
+ }
+}
+
+int
+AudioEngine::freewheel (bool onoff)
+{
+ if (_jack) {
+
+ if (onoff) {
+ _freewheel_thread_registered = false;
+ }
+
+ return jack_set_freewheel (_jack, onoff);
+
+ } else {
+ return -1;
+ }
+}
+
+void
+AudioEngine::remove_all_ports ()
+{
+ /* process lock MUST be held */
+
+ {
+ RCUWriter<Ports> writer (ports);
+ boost::shared_ptr<Ports> ps = writer.get_copy ();
+ ps->clear ();
+ }
+}
+
+void
+AudioEngine::remove_connections_for (Port& port)
+{
+ for (PortConnections::iterator i = port_connections.begin(); i != port_connections.end(); ) {
+ PortConnections::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ if ((*i).first == port.name()) {
+ port_connections.erase (i);
+ }
+
+ i = tmp;
+ }
+}
+
+
+#ifdef HAVE_JACK_CLIENT_OPEN
+
+int
+AudioEngine::connect_to_jack (string client_name)
+{
+ jack_options_t options = JackNullOption;
+ jack_status_t status;
+ const char *server_name = NULL;
+
+ jack_client_name = client_name; /* might be reset below */
+ _jack = jack_client_open (jack_client_name.c_str(), options, &status, server_name);
+
+ if (_jack == NULL) {
+
+ if (status & JackServerFailed) {
+ error << _("Unable to connect to JACK server") << endmsg;
+ }
+
+ // error message is not useful here
+ return -1;
+ }
+
+ if (status & JackServerStarted) {
+ info << _("JACK server started") << endmsg;
+ }
+
+ if (status & JackNameNotUnique) {
+ jack_client_name = jack_get_client_name (_jack);
+ }
+
+ jack_set_error_function (ardour_jack_error);
+
+ return 0;
+}
+
+#else
+
+int
+AudioEngine::connect_to_jack (string client_name)
+{
+ jack_client_name = client_name;
+
+ if ((_jack = jack_client_new (client_name.c_str())) == NULL) {
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* HAVE_JACK_CLIENT_OPEN */
+
+int
+AudioEngine::disconnect_from_jack ()
+{
+ if (_jack == 0) {
+ return 0;
+ }
+
+ jack_client_close (_jack);
+
+ _buffer_size = 0;
+ _frame_rate = 0;
+
+ if (_running) {
+ stop_metering_thread ();
+ _running = false;
+ Stopped(); /* EMIT SIGNAL */
+ }
+
+ _jack = 0;
+ return 0;
+}
+
+int
+AudioEngine::reconnect_to_jack ()
+{
+ if (_jack) {
+ disconnect_from_jack ();
+ /* XXX give jackd a chance */
+ Glib::usleep (250000);
+ }
+
+ if (connect_to_jack (jack_client_name)) {
+ error << _("failed to connect to JACK") << endmsg;
+ return -1;
+ }
+
+ Ports::iterator i;
+
+ boost::shared_ptr<Ports> p = ports.reader ();
+
+ for (i = p->begin(); i != p->end(); ++i) {
+ if ((*i)->reestablish ()) {
+ break;
+ }
+ }
+
+ if (i != p->end()) {
+ /* failed */
+ remove_all_ports ();
+ return -1;
+ }
+
+
+ if (session) {
+ session->reset_jack_connection (_jack);
+ nframes_t blocksize = jack_get_buffer_size (_jack);
+ session->set_block_size (blocksize);
+ session->set_frame_rate (jack_get_sample_rate (_jack));
+ }
+
+ last_monitor_check = 0;
+
+ jack_on_shutdown (_jack, halted, this);
+ jack_set_graph_order_callback (_jack, _graph_order_callback, this);
+ jack_set_thread_init_callback (_jack, _thread_init_callback, this);
+ jack_set_process_callback (_jack, _process_callback, this);
+ jack_set_sample_rate_callback (_jack, _sample_rate_callback, this);
+ jack_set_buffer_size_callback (_jack, _bufsize_callback, this);
+ jack_set_xrun_callback (_jack, _xrun_callback, this);
+ jack_set_sync_callback (_jack, _jack_sync_callback, this);
+ jack_set_freewheel_callback (_jack, _freewheel_callback, this);
+
+ if (Config->get_jack_time_master()) {
+ jack_set_timebase_callback (_jack, 0, _jack_timebase_callback, this);
+ }
+
+ if (jack_activate (_jack) == 0) {
+ _running = true;
+ _has_run = true;
+ } else {
+ return -1;
+ }
+
+ /* re-establish connections */
+
+ for (i = p->begin(); i != p->end(); ++i) {
+ (*i)->reconnect ();
+ }
+
+ Running (); /* EMIT SIGNAL*/
+
+ start_metering_thread ();
+
+ return 0;
+}
+
+int
+AudioEngine::request_buffer_size (nframes_t nframes)
+{
+ if (_jack) {
+
+ if (nframes == jack_get_buffer_size (_jack)) {
+ return 0;
+ }
+
+ return jack_set_buffer_size (_jack, nframes);
+
+ } else {
+ return -1;
+ }
+}
+
+void
+AudioEngine::update_total_latencies ()
+{
+#ifdef HAVE_JACK_RECOMPUTE_LATENCIES
+ jack_recompute_total_latencies (_jack);
+#endif
+}
+
+string
+AudioEngine::make_port_name_relative (string portname)
+{
+ string::size_type len;
+ string::size_type n;
+
+ len = portname.length();
+
+ for (n = 0; n < len; ++n) {
+ if (portname[n] == ':') {
+ break;
+ }
+ }
+
+ if ((n != len) && (portname.substr (0, n) == jack_client_name)) {
+ return portname.substr (n+1);
+ }
+
+ return portname;
+}
+
+string
+AudioEngine::make_port_name_non_relative (string portname)
+{
+ string str;
+
+ if (portname.find_first_of (':') != string::npos) {
+ return portname;
+ }
+
+ str = jack_client_name;
+ str += ':';
+ str += portname;
+
+ return str;
+}
+
+bool
+AudioEngine::is_realtime () const
+{
+ if (_jack) {
+ return jack_is_realtime (_jack);
+ } else {
+ return false;
+ }
+}
diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc
new file mode 100644
index 0000000000..026cb3e7c0
--- /dev/null
+++ b/libs/ardour/audiofilesource.cc
@@ -0,0 +1,759 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <vector>
+
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <stdio.h> // for rename(), sigh
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <pbd/convert.h>
+#include <pbd/basename.h>
+#include <pbd/mountpoint.h>
+#include <pbd/stl_delete.h>
+#include <pbd/strsplit.h>
+#include <pbd/shortpath.h>
+#include <pbd/enumwriter.h>
+
+#include <sndfile.h>
+
+#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
+#include <glibmm/thread.h>
+
+#include <ardour/audiofilesource.h>
+#include <ardour/sndfile_helpers.h>
+#include <ardour/sndfilesource.h>
+#include <ardour/session.h>
+#include <ardour/session_directory.h>
+#include <ardour/source_factory.h>
+#include <ardour/filename_extensions.h>
+
+// if these headers come before sigc++ is included
+// the parser throws ObjC++ errors. (nil is a keyword)
+#ifdef HAVE_COREAUDIO
+#include <ardour/coreaudiosource.h>
+#include <AudioToolbox/ExtendedAudioFile.h>
+#include <AudioToolbox/AudioFormat.h>
+#endif // HAVE_COREAUDIO
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using namespace Glib;
+
+ustring AudioFileSource::peak_dir = "";
+ustring AudioFileSource::search_path;
+
+sigc::signal<void> AudioFileSource::HeaderPositionOffsetChanged;
+uint64_t AudioFileSource::header_position_offset = 0;
+
+/* XXX maybe this too */
+char AudioFileSource::bwf_serial_number[13] = "000000000000";
+
+struct SizedSampleBuffer {
+ nframes_t size;
+ Sample* buf;
+
+ SizedSampleBuffer (nframes_t sz) : size (sz) {
+ buf = new Sample[size];
+ }
+
+ ~SizedSampleBuffer() {
+ delete [] buf;
+ }
+};
+
+Glib::StaticPrivate<SizedSampleBuffer> thread_interleave_buffer = GLIBMM_STATIC_PRIVATE_INIT;
+
+AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags)
+ : AudioSource (s, path), _flags (flags),
+ _channel (0)
+{
+ /* constructor used for existing external to session files. file must exist already */
+ _is_embedded = AudioFileSource::determine_embeddedness (path);
+
+ if (init (path, true)) {
+ throw failed_constructor ();
+ }
+
+}
+
+AudioFileSource::AudioFileSource (Session& s, ustring path, Flag flags, SampleFormat samp_format, HeaderFormat hdr_format)
+ : AudioSource (s, path), _flags (flags),
+ _channel (0)
+{
+ /* constructor used for new internal-to-session files. file cannot exist */
+ _is_embedded = false;
+
+ if (init (path, false)) {
+ throw failed_constructor ();
+ }
+}
+
+AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist)
+ : AudioSource (s, node), _flags (Flag (Writable|CanRename))
+ /* _channel is set in set_state() or init() */
+{
+ /* constructor used for existing internal-to-session files. file must exist */
+
+ if (set_state (node)) {
+ throw failed_constructor ();
+ }
+
+ string foo = _name;
+
+ if (init (foo, must_exist)) {
+ throw failed_constructor ();
+ }
+}
+
+AudioFileSource::~AudioFileSource ()
+{
+ if (removable()) {
+ unlink (_path.c_str());
+ unlink (peakpath.c_str());
+ }
+}
+
+bool
+AudioFileSource::determine_embeddedness (ustring path)
+{
+ return (path.find("/") == 0);
+}
+
+bool
+AudioFileSource::removable () const
+{
+ return (_flags & Removable) && ((_flags & RemoveAtDestroy) || ((_flags & RemovableIfEmpty) && length() == 0));
+}
+
+int
+AudioFileSource::init (ustring pathstr, bool must_exist)
+{
+ _length = 0;
+ timeline_position = 0;
+ _peaks_built = false;
+
+ if (!find (pathstr, must_exist, file_is_new, _channel)) {
+ throw non_existent_source ();
+ }
+
+ if (file_is_new && must_exist) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+ustring
+AudioFileSource::peak_path (ustring audio_path)
+{
+ ustring base;
+
+ base = PBD::basename_nosuffix (audio_path);
+ base += '%';
+ base += (char) ('A' + _channel);
+
+ return _session.peak_path (base);
+}
+
+ustring
+AudioFileSource::find_broken_peakfile (ustring peak_path, ustring audio_path)
+{
+ ustring str;
+
+ /* check for the broken location in use by 2.0 for several months */
+
+ str = broken_peak_path (audio_path);
+
+ if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) {
+
+ if (is_embedded()) {
+
+ /* it would be nice to rename it but the nature of
+ the bug means that we can't reliably use it.
+ */
+
+ peak_path = str;
+
+ } else {
+ /* all native files are mono, so we can just rename
+ it.
+ */
+ ::rename (str.c_str(), peak_path.c_str());
+ }
+
+ } else {
+ /* Nasty band-aid for older sessions that were created before we
+ used libsndfile for all audio files.
+ */
+
+
+ str = old_peak_path (audio_path);
+ if (Glib::file_test (str, Glib::FILE_TEST_EXISTS)) {
+ peak_path = str;
+ }
+ }
+
+ return peak_path;
+}
+
+ustring
+AudioFileSource::broken_peak_path (ustring audio_path)
+{
+ return _session.peak_path (audio_path);
+}
+
+ustring
+AudioFileSource::old_peak_path (ustring audio_path)
+{
+ /* XXX hardly bombproof! fix me */
+
+ struct stat stat_file;
+ struct stat stat_mount;
+
+ ustring mp = mountpoint (audio_path);
+
+ stat (audio_path.c_str(), &stat_file);
+ stat (mp.c_str(), &stat_mount);
+
+ char buf[32];
+#ifdef __APPLE__
+ snprintf (buf, sizeof (buf), "%u-%u-%d.peak", stat_mount.st_ino, stat_file.st_ino, _channel);
+#else
+ snprintf (buf, sizeof (buf), "%ld-%ld-%d.peak", stat_mount.st_ino, stat_file.st_ino, _channel);
+#endif
+
+ ustring res = peak_dir;
+ res += buf;
+ res += peakfile_suffix;
+
+ return res;
+}
+
+bool
+AudioFileSource::get_soundfile_info (ustring path, SoundFileInfo& _info, string& error_msg)
+{
+#ifdef HAVE_COREAUDIO
+ if (CoreAudioSource::get_soundfile_info (path, _info, error_msg) == 0) {
+ return true;
+ }
+#endif // HAVE_COREAUDIO
+
+ if (SndFileSource::get_soundfile_info (path, _info, error_msg) != 0) {
+ return true;
+ }
+
+ return false;
+}
+
+XMLNode&
+AudioFileSource::get_state ()
+{
+ XMLNode& root (AudioSource::get_state());
+ char buf[32];
+ root.add_property (X_("flags"), enum_2_string (_flags));
+ snprintf (buf, sizeof (buf), "%u", _channel);
+ root.add_property (X_("channel"), buf);
+ return root;
+}
+
+int
+AudioFileSource::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+
+ if (AudioSource::set_state (node)) {
+ return -1;
+ }
+
+ if ((prop = node.property (X_("flags"))) != 0) {
+ _flags = Flag (string_2_enum (prop->value(), _flags));
+ } else {
+ _flags = Flag (0);
+
+ }
+
+ if ((prop = node.property (X_("channel"))) != 0) {
+ _channel = atoi (prop->value());
+ } else {
+ _channel = 0;
+ }
+
+ if ((prop = node.property (X_("name"))) != 0) {
+ _is_embedded = AudioFileSource::determine_embeddedness (prop->value());
+ } else {
+ _is_embedded = false;
+ }
+
+ if ((prop = node.property (X_("destructive"))) != 0) {
+ /* old style, from the period when we had DestructiveFileSource */
+ _flags = Flag (_flags | Destructive);
+ }
+
+ return 0;
+}
+
+void
+AudioFileSource::mark_for_remove ()
+{
+ // This operation is not allowed for sources for destructive tracks or embedded files.
+ // Fortunately mark_for_remove() is never called for embedded files. This function
+ // must be fixed if that ever happens.
+ if (_flags & Destructive) {
+ return;
+ }
+
+ _flags = Flag (_flags | Removable | RemoveAtDestroy);
+}
+
+void
+AudioFileSource::mark_streaming_write_completed ()
+{
+ if (!writable()) {
+ return;
+ }
+
+ /* XXX notice that we're readers of _peaks_built
+ but we must hold a solid lock on PeaksReady.
+ */
+
+ Glib::Mutex::Lock lm (_lock);
+
+ if (_peaks_built) {
+ PeaksReady (); /* EMIT SIGNAL */
+ }
+}
+
+void
+AudioFileSource::mark_take (ustring id)
+{
+ if (writable()) {
+ _take_id = id;
+ }
+}
+
+int
+AudioFileSource::move_to_trash (const ustring& trash_dir_name)
+{
+ if (is_embedded()) {
+ cerr << "tried to move an embedded region to trash" << endl;
+ return -1;
+ }
+
+ ustring newpath;
+
+ if (!writable()) {
+ return -1;
+ }
+
+ /* don't move the file across filesystems, just
+ stick it in the `trash_dir_name' directory
+ on whichever filesystem it was already on.
+ */
+
+ newpath = Glib::path_get_dirname (_path);
+ newpath = Glib::path_get_dirname (newpath);
+
+ cerr << "from " << _path << " dead dir looks like " << newpath << endl;
+
+ newpath += '/';
+ newpath += trash_dir_name;
+ newpath += '/';
+ newpath += Glib::path_get_basename (_path);
+
+ if (access (newpath.c_str(), F_OK) == 0) {
+
+ /* the new path already exists, try versioning */
+
+ char buf[PATH_MAX+1];
+ int version = 1;
+ ustring newpath_v;
+
+ snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
+ newpath_v = buf;
+
+ while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
+ snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
+ newpath_v = buf;
+ }
+
+ if (version == 999) {
+ error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
+ newpath)
+ << endmsg;
+ } else {
+ newpath = newpath_v;
+ }
+
+ } else {
+
+ /* it doesn't exist, or we can't read it or something */
+
+ }
+
+ if (::rename (_path.c_str(), newpath.c_str()) != 0) {
+ error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"),
+ _path, newpath, strerror (errno))
+ << endmsg;
+ return -1;
+ }
+
+ if (::unlink (peakpath.c_str()) != 0) {
+ error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
+ peakpath, _path, strerror (errno))
+ << endmsg;
+ /* try to back out */
+ rename (newpath.c_str(), _path.c_str());
+ return -1;
+ }
+
+ _path = newpath;
+ peakpath = "";
+
+ /* file can not be removed twice, since the operation is not idempotent */
+
+ _flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
+
+ return 0;
+}
+
+bool
+AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew, uint16_t& chan)
+{
+ ustring::size_type pos;
+ bool ret = false;
+
+ isnew = false;
+
+ if (pathstr[0] != '/') {
+
+ /* non-absolute pathname: find pathstr in search path */
+
+ vector<ustring> dirs;
+ int cnt;
+ ustring fullpath;
+ ustring keeppath;
+
+ if (search_path.length() == 0) {
+ error << _("FileSource: search path not set") << endmsg;
+ goto out;
+ }
+
+ split (search_path, dirs, ':');
+
+ cnt = 0;
+
+ for (vector<ustring>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
+
+ fullpath = *i;
+ if (fullpath[fullpath.length()-1] != '/') {
+ fullpath += '/';
+ }
+
+ fullpath += pathstr;
+
+ /* i (paul) made a nasty design error by using ':' as a special character in
+ Ardour 0.99 .. this hack tries to make things sort of work.
+ */
+
+ if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
+
+ if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+
+ /* its a real file, no problem */
+
+ keeppath = fullpath;
+ ++cnt;
+
+ } else {
+
+ if (must_exist) {
+
+ /* might be an older session using file:channel syntax. see if the version
+ without the :suffix exists
+ */
+
+ ustring shorter = pathstr.substr (0, pos);
+ fullpath = *i;
+
+ if (fullpath[fullpath.length()-1] != '/') {
+ fullpath += '/';
+ }
+
+ fullpath += shorter;
+
+ if (Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+ chan = atoi (pathstr.substr (pos+1));
+ pathstr = shorter;
+ keeppath = fullpath;
+ ++cnt;
+ }
+
+ } else {
+
+ /* new derived file (e.g. for timefx) being created in a newer session */
+
+ }
+ }
+
+ } else {
+
+ if (Glib::file_test (fullpath, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+ keeppath = fullpath;
+ ++cnt;
+ }
+ }
+ }
+
+ if (cnt > 1) {
+
+ error << string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, search_path) << endmsg;
+ goto out;
+
+ } else if (cnt == 0) {
+
+ if (must_exist) {
+ error << string_compose(_("Filesource: cannot find required file (%1): while searching %2"), pathstr, search_path) << endmsg;
+ goto out;
+ } else {
+ isnew = true;
+ }
+ }
+
+ _name = pathstr;
+ _path = keeppath;
+ ret = true;
+
+ } else {
+
+ /* external files and/or very very old style sessions include full paths */
+
+ /* ugh, handle ':' situation */
+
+ if ((pos = pathstr.find_last_of (':')) != ustring::npos) {
+
+ ustring shorter = pathstr.substr (0, pos);
+
+ if (Glib::file_test (shorter, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+ chan = atoi (pathstr.substr (pos+1));
+ pathstr = shorter;
+ }
+ }
+
+ _path = pathstr;
+
+ if (is_embedded()) {
+ _name = pathstr;
+ } else {
+ _name = pathstr.substr (pathstr.find_last_of ('/') + 1);
+ }
+
+ if (!Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) {
+
+ /* file does not exist or we cannot read it */
+
+ if (must_exist) {
+ error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
+ goto out;
+ }
+
+ if (errno != ENOENT) {
+ error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
+ goto out;
+ }
+
+ /* a new file */
+
+ isnew = true;
+ ret = true;
+
+ } else {
+
+ /* already exists */
+
+ ret = true;
+
+ }
+ }
+
+ out:
+ return ret;
+}
+
+void
+AudioFileSource::set_search_path (ustring p)
+{
+ search_path = p;
+}
+
+void
+AudioFileSource::set_header_position_offset (nframes_t offset)
+{
+ header_position_offset = offset;
+ HeaderPositionOffsetChanged ();
+}
+
+void
+AudioFileSource::set_timeline_position (int64_t pos)
+{
+ timeline_position = pos;
+}
+
+void
+AudioFileSource::set_allow_remove_if_empty (bool yn)
+{
+ if (!writable()) {
+ return;
+ }
+
+ if (yn) {
+ _flags = Flag (_flags | RemovableIfEmpty);
+ } else {
+ _flags = Flag (_flags & ~RemovableIfEmpty);
+ }
+}
+
+int
+AudioFileSource::set_source_name (ustring newname, bool destructive)
+{
+ Glib::Mutex::Lock lm (_lock);
+ ustring oldpath = _path;
+ ustring newpath = Session::change_audio_path_by_name (oldpath, _name, newname, destructive);
+
+ if (newpath.empty()) {
+ error << string_compose (_("programming error: %1"), "cannot generate a changed audio path") << endmsg;
+ return -1;
+ }
+
+ // Test whether newpath exists, if yes notify the user but continue.
+ if (access(newpath.c_str(),F_OK) == 0) {
+ error << _("Programming error! Ardour tried to rename a file over another file! It's safe to continue working, but please report this to the developers.") << endmsg;
+ return -1;
+ }
+
+ if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
+ error << string_compose (_("cannot rename audio file %1 to %2"), _name, newpath) << endmsg;
+ return -1;
+ }
+
+ _name = Glib::path_get_basename (newpath);
+ _path = newpath;
+
+ return rename_peakfile (peak_path (_path));
+}
+
+bool
+AudioFileSource::is_empty (Session& s, ustring path)
+{
+ SoundFileInfo info;
+ string err;
+
+ if (!get_soundfile_info (path, info, err)) {
+ /* dangerous: we can't get info, so assume that its not empty */
+ return false;
+ }
+
+ return info.length == 0;
+}
+
+int
+AudioFileSource::setup_peakfile ()
+{
+ if (!(_flags & NoPeakFile)) {
+ return initialize_peakfile (file_is_new, _path);
+ } else {
+ return 0;
+ }
+}
+
+bool
+AudioFileSource::safe_file_extension(ustring file)
+{
+ const char* suffixes[] = {
+ ".wav", ".WAV",
+ ".aiff", ".AIFF",
+ ".caf", ".CAF",
+ ".aif", ".AIF",
+ ".amb", ".AMB",
+ ".snd", ".SND",
+ ".au", ".AU",
+ ".raw", ".RAW",
+ ".sf", ".SF",
+ ".cdr", ".CDR",
+ ".smp", ".SMP",
+ ".maud", ".MAUD",
+ ".vwe", ".VWE",
+ ".paf", ".PAF",
+ ".voc", ".VOC",
+#ifdef HAVE_FLAC
+ ".flac", ".FLAC",
+#endif // HAVE_FLAC
+#ifdef HAVE_COREAUDIO
+ ".mp3", ".MP3",
+ ".aac", ".AAC",
+ ".mp4", ".MP4",
+#endif // HAVE_COREAUDIO
+ };
+
+ for (size_t n = 0; n < sizeof(suffixes)/sizeof(suffixes[0]); ++n) {
+ if (file.rfind (suffixes[n]) == file.length() - strlen (suffixes[n])) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+AudioFileSource::mark_immutable ()
+{
+ /* destructive sources stay writable, and their other flags don't
+ change.
+ */
+
+ if (!(_flags & Destructive)) {
+ _flags = Flag (_flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy|CanRename));
+ }
+}
+
+
+Sample*
+AudioFileSource::get_interleave_buffer (nframes_t size)
+{
+ SizedSampleBuffer* ssb;
+
+ if ((ssb = thread_interleave_buffer.get()) == 0) {
+ ssb = new SizedSampleBuffer (size);
+ thread_interleave_buffer.set (ssb);
+ }
+
+ if (ssb->size < size) {
+ ssb = new SizedSampleBuffer (size);
+ thread_interleave_buffer.set (ssb);
+ }
+
+ return ssb->buf;
+}
diff --git a/libs/ardour/audioregion.cc b/libs/ardour/audioregion.cc
new file mode 100644
index 0000000000..f5f04eac6d
--- /dev/null
+++ b/libs/ardour/audioregion.cc
@@ -0,0 +1,1402 @@
+/*
+ Copyright (C) 2000-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cmath>
+#include <climits>
+#include <cfloat>
+#include <algorithm>
+
+#include <set>
+
+#include <sigc++/bind.h>
+#include <sigc++/class_slot.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/basename.h>
+#include <pbd/xml++.h>
+#include <pbd/stacktrace.h>
+#include <pbd/enumwriter.h>
+#include <pbd/convert.h>
+
+#include <ardour/audioregion.h>
+#include <ardour/session.h>
+#include <ardour/gain.h>
+#include <ardour/dB.h>
+#include <ardour/playlist.h>
+#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>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+/* a Session will reset these to its chosen defaults by calling AudioRegion::set_default_fade() */
+
+Change AudioRegion::FadeInChanged = ARDOUR::new_change();
+Change AudioRegion::FadeOutChanged = ARDOUR::new_change();
+Change AudioRegion::FadeInActiveChanged = ARDOUR::new_change();
+Change AudioRegion::FadeOutActiveChanged = ARDOUR::new_change();
+Change AudioRegion::EnvelopeActiveChanged = ARDOUR::new_change();
+Change AudioRegion::ScaleAmplitudeChanged = ARDOUR::new_change();
+Change AudioRegion::EnvelopeChanged = ARDOUR::new_change();
+
+void
+AudioRegion::init ()
+{
+ _scale_amplitude = 1.0;
+
+ set_default_fades ();
+ set_default_envelope ();
+
+ listen_to_my_curves ();
+ listen_to_my_sources ();
+}
+
+/* constructor for use by derived types only */
+AudioRegion::AudioRegion (Session& s, nframes_t start, nframes_t length, string name)
+ : Region (s, start, length, name, DataType::AUDIO)
+ , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
+ , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
+ , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
+{
+ init ();
+}
+
+/** Basic AudioRegion constructor (one channel) */
+AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length)
+ : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::AUDIO, 0, Region::Flag(Region::DefaultFlags|Region::External))
+ , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
+ , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
+ , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
+{
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
+ if (afs) {
+ afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
+ }
+
+ init ();
+}
+
+/* Basic AudioRegion constructor (one channel) */
+AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
+ : Region (src, 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))
+ , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
+{
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
+ if (afs) {
+ afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
+ }
+
+ init ();
+}
+
+/* Basic AudioRegion constructor (many channels) */
+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))
+ , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
+{
+ init ();
+ listen_to_my_sources ();
+}
+
+/** Create a new AudioRegion, that is part of an existing one */
+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)
+ , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
+ , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
+ , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
+{
+ set<boost::shared_ptr<Source> > unique_srcs;
+
+ for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
+ _sources.push_back (*i);
+
+ pair<set<boost::shared_ptr<Source> >::iterator,bool> result;
+
+ result = unique_srcs.insert (*i);
+
+ if (result.second) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*i);
+ if (afs) {
+ afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
+ }
+ }
+ }
+
+ /* return to default fades if the existing ones are too long */
+ init ();
+
+ if (_flags & LeftOfSplit) {
+ if (_fade_in->back()->when >= _length) {
+ set_default_fade_in ();
+ } else {
+ _fade_in_disabled = other->_fade_in_disabled;
+ }
+ set_default_fade_out ();
+ _flags = Flag (_flags & ~Region::LeftOfSplit);
+ }
+
+ if (_flags & RightOfSplit) {
+ if (_fade_out->back()->when >= _length) {
+ set_default_fade_out ();
+ } else {
+ _fade_out_disabled = other->_fade_out_disabled;
+ }
+ set_default_fade_in ();
+ _flags = Flag (_flags & ~Region::RightOfSplit);
+ }
+
+ _scale_amplitude = other->_scale_amplitude;
+
+ assert(_type == DataType::AUDIO);
+ listen_to_my_sources ();
+}
+
+AudioRegion::AudioRegion (boost::shared_ptr<const AudioRegion> other)
+ : Region (other)
+ , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
+ , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
+ , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
+{
+ assert(_type == DataType::AUDIO);
+ _scale_amplitude = other->_scale_amplitude;
+ _envelope = other->_envelope;
+
+ set_default_fades ();
+
+ listen_to_my_curves ();
+ listen_to_my_sources ();
+}
+
+AudioRegion::AudioRegion (boost::shared_ptr<AudioSource> src, const XMLNode& node)
+ : Region (src, node)
+ , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
+ , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
+ , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
+{
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (src);
+ if (afs) {
+ afs->HeaderPositionOffsetChanged.connect (mem_fun (*this, &AudioRegion::source_offset_changed));
+ }
+
+ init ();
+
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+
+ assert(_type == DataType::AUDIO);
+ listen_to_my_sources ();
+}
+
+AudioRegion::AudioRegion (SourceList& srcs, const XMLNode& node)
+ : Region (srcs, node)
+ , _fade_in (new AutomationList(Parameter(FadeInAutomation), 0.0, 2.0, 1.0))
+ , _fade_out (new AutomationList(Parameter(FadeOutAutomation), 0.0, 2.0, 1.0))
+ , _envelope (new AutomationList(Parameter(EnvelopeAutomation), 0.0, 2.0, 1.0))
+{
+ init ();
+
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+
+ assert(_type == DataType::AUDIO);
+ listen_to_my_sources ();
+}
+
+AudioRegion::~AudioRegion ()
+{
+}
+
+void
+AudioRegion::listen_to_my_sources ()
+{
+ for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+ (*i)->AnalysisChanged.connect (mem_fun (*this, &AudioRegion::invalidate_transients));
+ }
+}
+
+void
+AudioRegion::listen_to_my_curves ()
+{
+ _envelope->StateChanged.connect (mem_fun (*this, &AudioRegion::envelope_changed));
+ _fade_in->StateChanged.connect (mem_fun (*this, &AudioRegion::fade_in_changed));
+ _fade_out->StateChanged.connect (mem_fun (*this, &AudioRegion::fade_out_changed));
+}
+
+void
+AudioRegion::set_envelope_active (bool yn)
+{
+ if (envelope_active() != yn) {
+ char buf[64];
+ if (yn) {
+ snprintf (buf, sizeof (buf), "envelope active");
+ _flags = Flag (_flags|EnvelopeActive);
+ } else {
+ snprintf (buf, sizeof (buf), "envelope off");
+ _flags = Flag (_flags & ~EnvelopeActive);
+ }
+ send_change (EnvelopeActiveChanged);
+ }
+}
+
+ARDOUR::nframes_t
+AudioRegion::read_peaks (PeakData *buf, nframes_t npeaks, nframes_t offset, nframes_t cnt, uint32_t chan_n, double samples_per_unit) const
+{
+ if (chan_n >= _sources.size()) {
+ return 0;
+ }
+
+ if (audio_source(chan_n)->read_peaks (buf, npeaks, offset, cnt, samples_per_unit)) {
+ return 0;
+ } else {
+ if (_scale_amplitude != 1.0) {
+ for (nframes_t n = 0; n < npeaks; ++n) {
+ buf[n].max *= _scale_amplitude;
+ buf[n].min *= _scale_amplitude;
+ }
+ }
+ return cnt;
+ }
+}
+
+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, _length, 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
+{
+ /* regular diskstream/butler read complete with fades etc */
+ return _read_at (_sources, _length, buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, read_frames, skip_frames, false);
+}
+
+nframes_t
+AudioRegion::master_read_at (Sample *buf, Sample *mixdown_buffer, float *gain_buffer, nframes_t position,
+ nframes_t cnt, uint32_t chan_n) const
+{
+ return _read_at (_master_sources, _master_sources.front()->length(), buf, mixdown_buffer, gain_buffer, position, cnt, chan_n, 0, 0);
+}
+
+nframes_t
+AudioRegion::_read_at (const SourceList& srcs, nframes_t limit,
+ 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,
+ bool raw) const
+{
+ nframes_t internal_offset;
+ nframes_t buf_offset;
+ nframes_t to_read;
+
+ if (muted() && !raw) {
+ return 0; /* read nothing */
+ }
+
+ /* precondition: caller has verified that we cover the desired section */
+
+ if (position < _position) {
+ internal_offset = 0;
+ buf_offset = _position - position;
+ cnt -= buf_offset;
+ } else {
+ internal_offset = position - _position;
+ buf_offset = 0;
+ }
+
+ if (internal_offset >= limit) {
+ return 0; /* read nothing */
+ }
+
+ if ((to_read = min (cnt, limit - internal_offset)) == 0) {
+ return 0; /* read nothing */
+ }
+
+ if (opaque() || raw) {
+ /* overwrite whatever is there */
+ mixdown_buffer = buf + buf_offset;
+ } else {
+ mixdown_buffer += buf_offset;
+ }
+
+ if (!raw) {
+ _read_data_count = 0;
+ }
+
+ if (chan_n < n_channels()) {
+
+ boost::shared_ptr<AudioSource> src = audio_source(chan_n);
+ if (src->read (mixdown_buffer, _start + internal_offset, to_read) != to_read) {
+ return 0; /* "read nothing" */
+ }
+
+ if (!raw) {
+ _read_data_count += src->read_data_count();
+ }
+
+ } else {
+
+ /* track is N-channel, this region has less channels; silence the ones
+ we don't have.
+ */
+
+ memset (mixdown_buffer, 0, sizeof (Sample) * cnt);
+
+ /* no fades required */
+
+ if (!raw) {
+ goto merge;
+ }
+ }
+
+ /* fade in */
+
+ 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 fi_limit;
+
+ fi_limit = min (to_read, fade_in_length - internal_offset);
+
+ _fade_in->curve().get_vector (internal_offset, internal_offset+fi_limit, gain_buffer, fi_limit);
+
+ for (nframes_t n = 0; n < fi_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 */
+
+ /* ................. >| REGION
+ limit
+
+ { } FADE
+ fade_out_length
+ ^
+ limit - fade_out_length
+ |--------------|
+ ^internal_offset
+ ^internal_offset + to_read
+
+ we need the intersection of [internal_offset,internal_offset+to_read] with
+ [limit - fade_out_length, limit]
+
+ */
+
+
+ nframes_t fade_out_length = (nframes_t) _fade_out->back()->when;
+ nframes_t fade_interval_start = max(internal_offset, limit-fade_out_length);
+ nframes_t fade_interval_end = min(internal_offset + to_read, limit);
+
+ if (fade_interval_end > fade_interval_start) {
+ /* (part of the) the fade out is in this buffer */
+
+ nframes_t fo_limit = fade_interval_end - fade_interval_start;
+ nframes_t curve_offset = fade_interval_start - (limit-fade_out_length);
+ nframes_t fade_offset = fade_interval_start - internal_offset;
+
+ _fade_out->curve().get_vector (curve_offset, curve_offset+fo_limit, gain_buffer, fo_limit);
+
+ for (nframes_t n = 0, m = fade_offset; n < fo_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];
+ }
+ }
+ }
+
+ return to_read;
+}
+
+XMLNode&
+AudioRegion::state (bool full)
+{
+ XMLNode& node (Region::state (full));
+ XMLNode *child;
+ char buf[64];
+ char buf2[64];
+ LocaleGuard lg (X_("POSIX"));
+
+ node.add_property ("flags", enum_2_string (_flags));
+
+ snprintf (buf, sizeof(buf), "%.12g", _scale_amplitude);
+ node.add_property ("scale-gain", buf);
+
+ // XXX these should move into Region
+
+ for (uint32_t n=0; n < _sources.size(); ++n) {
+ snprintf (buf2, sizeof(buf2), "source-%d", n);
+ _sources[n]->id().print (buf, sizeof (buf));
+ node.add_property (buf2, buf);
+ }
+
+ for (uint32_t n=0; n < _master_sources.size(); ++n) {
+ snprintf (buf2, sizeof(buf2), "master-source-%d", n);
+ _master_sources[n]->id().print (buf, sizeof (buf));
+ node.add_property (buf2, buf);
+ }
+
+ snprintf (buf, sizeof (buf), "%u", (uint32_t) _sources.size());
+ node.add_property ("channels", buf);
+
+ if (full) {
+
+ child = node.add_child (X_("FadeIn"));
+
+ if ((_flags & DefaultFadeIn)) {
+ child->add_property (X_("default"), X_("yes"));
+ } else {
+ child->add_child_nocopy (_fade_in->get_state ());
+ }
+
+ child->add_property (X_("active"), _fade_in_disabled ? X_("no") : X_("yes"));
+
+ child = node.add_child (X_("FadeOut"));
+
+ if ((_flags & DefaultFadeOut)) {
+ child->add_property (X_("default"), X_("yes"));
+ } else {
+ child->add_child_nocopy (_fade_out->get_state ());
+ }
+
+ child->add_property (X_("active"), _fade_out_disabled ? X_("no") : X_("yes"));
+ }
+
+ child = node.add_child ("Envelope");
+
+ if (full) {
+ bool default_env = false;
+
+ // If there are only two points, the points are in the start of the region and the end of the region
+ // so, if they are both at 1.0f, that means the default region.
+
+ if (_envelope->size() == 2 &&
+ _envelope->front()->value == 1.0f &&
+ _envelope->back()->value==1.0f) {
+ if (_envelope->front()->when == 0 && _envelope->back()->when == _length) {
+ default_env = true;
+ }
+ }
+
+ if (default_env) {
+ child->add_property ("default", "yes");
+ } else {
+ child->add_child_nocopy (_envelope->get_state ());
+ }
+
+ } else {
+ child->add_property ("default", "yes");
+ }
+
+ if (full && _extra_xml) {
+ node.add_child_copy (*_extra_xml);
+ }
+
+ return node;
+}
+
+int
+AudioRegion::set_live_state (const XMLNode& node, Change& what_changed, bool send)
+{
+ const XMLNodeList& nlist = node.children();
+ const XMLProperty *prop;
+ LocaleGuard lg (X_("POSIX"));
+
+ Region::set_live_state (node, what_changed, false);
+
+ uint32_t old_flags = _flags;
+
+ if ((prop = node.property ("flags")) != 0) {
+ _flags = Flag (string_2_enum (prop->value(), _flags));
+
+ //_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
+
+ _flags = Flag (_flags & ~Region::LeftOfSplit);
+ _flags = Flag (_flags & ~Region::RightOfSplit);
+ }
+
+ if ((old_flags ^ _flags) & Muted) {
+ what_changed = Change (what_changed|MuteChanged);
+ }
+ if ((old_flags ^ _flags) & Opaque) {
+ what_changed = Change (what_changed|OpacityChanged);
+ }
+ if ((old_flags ^ _flags) & Locked) {
+ what_changed = Change (what_changed|LockChanged);
+ }
+
+ if ((prop = node.property ("scale-gain")) != 0) {
+ _scale_amplitude = atof (prop->value().c_str());
+ what_changed = Change (what_changed|ScaleAmplitudeChanged);
+ } else {
+ _scale_amplitude = 1.0;
+ }
+
+ /* Now find envelope description and other misc child items */
+
+ for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ XMLNode *child;
+ XMLProperty *prop;
+
+ child = (*niter);
+
+ if (child->name() == "Envelope") {
+
+ _envelope->clear ();
+
+ if ((prop = child->property ("default")) != 0 || _envelope->set_state (*child)) {
+ set_default_envelope ();
+ }
+
+ _envelope->set_max_xval (_length);
+ _envelope->truncate_end (_length);
+
+ } else if (child->name() == "FadeIn") {
+
+ _fade_in->clear ();
+
+ if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
+ set_default_fade_in ();
+ } else {
+ XMLNode* grandchild = child->child ("AutomationList");
+ if (grandchild) {
+ _fade_in->set_state (*grandchild);
+ }
+ }
+
+ if ((prop = child->property ("active")) != 0) {
+ if (prop->value() == "yes") {
+ set_fade_in_active (true);
+ } else {
+ set_fade_in_active (true);
+ }
+ }
+
+ } else if (child->name() == "FadeOut") {
+
+ _fade_out->clear ();
+
+ if ((prop = child->property ("default")) != 0 || (prop = child->property ("steepness")) != 0) {
+ set_default_fade_out ();
+ } else {
+ XMLNode* grandchild = child->child ("AutomationList");
+ if (grandchild) {
+ _fade_out->set_state (*grandchild);
+ }
+ }
+
+ if ((prop = child->property ("active")) != 0) {
+ if (prop->value() == "yes") {
+ set_fade_out_active (true);
+ } else {
+ set_fade_out_active (false);
+ }
+ }
+
+ }
+ }
+
+ if (send) {
+ send_change (what_changed);
+ }
+
+ return 0;
+}
+
+int
+AudioRegion::set_state (const XMLNode& node)
+{
+ /* Region::set_state() calls the virtual set_live_state(),
+ which will get us back to AudioRegion::set_live_state()
+ to handle the relevant stuff.
+ */
+
+ return Region::set_state (node);
+}
+
+void
+AudioRegion::set_fade_in_shape (FadeShape shape)
+{
+ set_fade_in (shape, (nframes_t) _fade_in->back()->when);
+}
+
+void
+AudioRegion::set_fade_out_shape (FadeShape shape)
+{
+ set_fade_out (shape, (nframes_t) _fade_out->back()->when);
+}
+
+void
+AudioRegion::set_fade_in (FadeShape shape, nframes_t len)
+{
+ _fade_in->freeze ();
+ _fade_in->clear ();
+
+ switch (shape) {
+ case Linear:
+ _fade_in->fast_simple_add (0.0, 0.0);
+ _fade_in->fast_simple_add (len, 1.0);
+ break;
+
+ case Fast:
+ _fade_in->fast_simple_add (0, 0);
+ _fade_in->fast_simple_add (len * 0.389401, 0.0333333);
+ _fade_in->fast_simple_add (len * 0.629032, 0.0861111);
+ _fade_in->fast_simple_add (len * 0.829493, 0.233333);
+ _fade_in->fast_simple_add (len * 0.9447, 0.483333);
+ _fade_in->fast_simple_add (len * 0.976959, 0.697222);
+ _fade_in->fast_simple_add (len, 1);
+ break;
+
+ case Slow:
+ _fade_in->fast_simple_add (0, 0);
+ _fade_in->fast_simple_add (len * 0.0207373, 0.197222);
+ _fade_in->fast_simple_add (len * 0.0645161, 0.525);
+ _fade_in->fast_simple_add (len * 0.152074, 0.802778);
+ _fade_in->fast_simple_add (len * 0.276498, 0.919444);
+ _fade_in->fast_simple_add (len * 0.481567, 0.980556);
+ _fade_in->fast_simple_add (len * 0.767281, 1);
+ _fade_in->fast_simple_add (len, 1);
+ break;
+
+ case LogA:
+ _fade_in->fast_simple_add (0, 0);
+ _fade_in->fast_simple_add (len * 0.0737327, 0.308333);
+ _fade_in->fast_simple_add (len * 0.246544, 0.658333);
+ _fade_in->fast_simple_add (len * 0.470046, 0.886111);
+ _fade_in->fast_simple_add (len * 0.652074, 0.972222);
+ _fade_in->fast_simple_add (len * 0.771889, 0.988889);
+ _fade_in->fast_simple_add (len, 1);
+ break;
+
+ case LogB:
+ _fade_in->fast_simple_add (0, 0);
+ _fade_in->fast_simple_add (len * 0.304147, 0.0694444);
+ _fade_in->fast_simple_add (len * 0.529954, 0.152778);
+ _fade_in->fast_simple_add (len * 0.725806, 0.333333);
+ _fade_in->fast_simple_add (len * 0.847926, 0.558333);
+ _fade_in->fast_simple_add (len * 0.919355, 0.730556);
+ _fade_in->fast_simple_add (len, 1);
+ break;
+ }
+
+ _fade_in->thaw ();
+ _fade_in_shape = shape;
+
+ send_change (FadeInChanged);
+}
+
+void
+AudioRegion::set_fade_out (FadeShape shape, nframes_t len)
+{
+ _fade_out->freeze ();
+ _fade_out->clear ();
+
+ switch (shape) {
+ case Fast:
+ _fade_out->fast_simple_add (len * 0, 1);
+ _fade_out->fast_simple_add (len * 0.023041, 0.697222);
+ _fade_out->fast_simple_add (len * 0.0553, 0.483333);
+ _fade_out->fast_simple_add (len * 0.170507, 0.233333);
+ _fade_out->fast_simple_add (len * 0.370968, 0.0861111);
+ _fade_out->fast_simple_add (len * 0.610599, 0.0333333);
+ _fade_out->fast_simple_add (len * 1, 0);
+ break;
+
+ case LogA:
+ _fade_out->fast_simple_add (len * 0, 1);
+ _fade_out->fast_simple_add (len * 0.228111, 0.988889);
+ _fade_out->fast_simple_add (len * 0.347926, 0.972222);
+ _fade_out->fast_simple_add (len * 0.529954, 0.886111);
+ _fade_out->fast_simple_add (len * 0.753456, 0.658333);
+ _fade_out->fast_simple_add (len * 0.9262673, 0.308333);
+ _fade_out->fast_simple_add (len * 1, 0);
+ break;
+
+ case Slow:
+ _fade_out->fast_simple_add (len * 0, 1);
+ _fade_out->fast_simple_add (len * 0.305556, 1);
+ _fade_out->fast_simple_add (len * 0.548611, 0.991736);
+ _fade_out->fast_simple_add (len * 0.759259, 0.931129);
+ _fade_out->fast_simple_add (len * 0.918981, 0.68595);
+ _fade_out->fast_simple_add (len * 0.976852, 0.22865);
+ _fade_out->fast_simple_add (len * 1, 0);
+ break;
+
+ case LogB:
+ _fade_out->fast_simple_add (len * 0, 1);
+ _fade_out->fast_simple_add (len * 0.080645, 0.730556);
+ _fade_out->fast_simple_add (len * 0.277778, 0.289256);
+ _fade_out->fast_simple_add (len * 0.470046, 0.152778);
+ _fade_out->fast_simple_add (len * 0.695853, 0.0694444);
+ _fade_out->fast_simple_add (len * 1, 0);
+ break;
+
+ case Linear:
+ _fade_out->fast_simple_add (len * 0, 1);
+ _fade_out->fast_simple_add (len * 1, 0);
+ break;
+ }
+
+ _fade_out->thaw ();
+ _fade_out_shape = shape;
+
+ send_change (FadeOutChanged);
+}
+
+void
+AudioRegion::set_fade_in_length (nframes_t len)
+{
+ if (len > _length) {
+ len = _length - 1;
+ }
+
+ bool changed = _fade_in->extend_to (len);
+
+ if (changed) {
+ _flags = Flag (_flags & ~DefaultFadeIn);
+ send_change (FadeInChanged);
+ }
+}
+
+void
+AudioRegion::set_fade_out_length (nframes_t len)
+{
+ if (len > _length) {
+ len = _length - 1;
+ }
+
+ bool changed = _fade_out->extend_to (len);
+
+ if (changed) {
+ _flags = Flag (_flags & ~DefaultFadeOut);
+ send_change (FadeOutChanged);
+ }
+}
+
+void
+AudioRegion::set_fade_in_active (bool yn)
+{
+ if (yn == (_flags & FadeIn)) {
+ return;
+ }
+ if (yn) {
+ _flags = Flag (_flags|FadeIn);
+ } else {
+ _flags = Flag (_flags & ~FadeIn);
+ }
+
+ send_change (FadeInActiveChanged);
+}
+
+void
+AudioRegion::set_fade_out_active (bool yn)
+{
+ if (yn == (_flags & FadeOut)) {
+ return;
+ }
+ if (yn) {
+ _flags = Flag (_flags | FadeOut);
+ } else {
+ _flags = Flag (_flags & ~FadeOut);
+ }
+
+ send_change (FadeOutActiveChanged);
+}
+
+bool
+AudioRegion::fade_in_is_default () const
+{
+ return _fade_in_shape == Linear && _fade_in->back()->when == 64;
+}
+
+bool
+AudioRegion::fade_out_is_default () const
+{
+ return _fade_out_shape == Linear && _fade_out->back()->when == 64;
+}
+
+void
+AudioRegion::set_default_fade_in ()
+{
+ set_fade_in (Linear, 64);
+}
+
+void
+AudioRegion::set_default_fade_out ()
+{
+ set_fade_out (Linear, 64);
+}
+
+void
+AudioRegion::set_default_fades ()
+{
+ _fade_in_disabled = 0;
+ _fade_out_disabled = 0;
+ set_default_fade_in ();
+ set_default_fade_out ();
+}
+
+void
+AudioRegion::set_default_envelope ()
+{
+ _envelope->freeze ();
+ _envelope->clear ();
+ _envelope->fast_simple_add (0, 1.0f);
+ _envelope->fast_simple_add (_length, 1.0f);
+ _envelope->thaw ();
+}
+
+void
+AudioRegion::recompute_at_end ()
+{
+ /* our length has changed. recompute a new final point by interpolating
+ based on the the existing curve.
+ */
+
+ _envelope->freeze ();
+ _envelope->truncate_end (_length);
+ _envelope->set_max_xval (_length);
+ _envelope->thaw ();
+
+ if (_fade_in->back()->when > _length) {
+ _fade_in->extend_to (_length);
+ send_change (FadeInChanged);
+ }
+
+ if (_fade_out->back()->when > _length) {
+ _fade_out->extend_to (_length);
+ send_change (FadeOutChanged);
+ }
+}
+
+void
+AudioRegion::recompute_at_start ()
+{
+ /* as above, but the shift was from the front */
+
+ _envelope->truncate_start (_length);
+
+ if (_fade_in->back()->when > _length) {
+ _fade_in->extend_to (_length);
+ send_change (FadeInChanged);
+ }
+
+ if (_fade_out->back()->when > _length) {
+ _fade_out->extend_to (_length);
+ send_change (FadeOutChanged);
+ }
+}
+
+int
+AudioRegion::separate_by_channel (Session& session, vector<boost::shared_ptr<AudioRegion> >& v) const
+{
+ SourceList srcs;
+ string new_name;
+ int n;
+
+ if (_sources.size() < 2) {
+ return 0;
+ }
+
+ n = 0;
+
+ for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+
+ srcs.clear ();
+ srcs.push_back (*i);
+
+ new_name = _name;
+
+ if (_sources.size() == 2) {
+ if (n == 0) {
+ new_name += "-L";
+ } else {
+ new_name += "-R";
+ }
+ } else {
+ new_name += '-';
+ new_name += ('0' + n + 1);
+ }
+
+ /* create a copy with just one source. prevent if from being thought of as "whole file" even if
+ it covers the entire source file(s).
+ */
+
+ Flag f = Flag (_flags & ~WholeFile);
+
+ boost::shared_ptr<Region> r = RegionFactory::create (srcs, _start, _length, new_name, _layer, f);
+ boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (r);
+
+ v.push_back (ar);
+
+ ++n;
+ }
+
+ return 0;
+}
+
+nframes_t
+AudioRegion::read_raw_internal (Sample* buf, nframes_t pos, nframes_t cnt) const
+{
+ return audio_source()->read (buf, pos, cnt);
+}
+
+int
+AudioRegion::exportme (Session& session, ARDOUR::ExportSpecification& spec)
+{
+ const nframes_t blocksize = 4096;
+ nframes_t to_read;
+ int status = -1;
+
+ spec.channels = _sources.size();
+
+ if (spec.prepare (blocksize, session.frame_rate())) {
+ goto out;
+ }
+
+ spec.pos = 0;
+ spec.total_frames = _length;
+
+ while (spec.pos < _length && !spec.stop) {
+
+
+ /* step 1: interleave */
+
+ to_read = min (_length - spec.pos, blocksize);
+
+ if (spec.channels == 1) {
+
+ if (read_raw_internal (spec.dataF, _start + spec.pos, to_read) != to_read) {
+ goto out;
+ }
+
+ } else {
+
+ Sample buf[blocksize];
+
+ for (uint32_t chan = 0; chan < spec.channels; ++chan) {
+
+ if (audio_source(chan)->read (buf, _start + spec.pos, to_read) != to_read) {
+ goto out;
+ }
+
+ for (nframes_t x = 0; x < to_read; ++x) {
+ spec.dataF[chan+(x*spec.channels)] = buf[x];
+ }
+ }
+ }
+
+ if (spec.process (to_read)) {
+ goto out;
+ }
+
+ spec.pos += to_read;
+ spec.progress = (double) spec.pos /_length;
+
+ }
+
+ status = 0;
+
+ out:
+ spec.running = false;
+ spec.status = status;
+ spec.clear();
+
+ return status;
+}
+
+void
+AudioRegion::set_scale_amplitude (gain_t g)
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ _scale_amplitude = g;
+
+ /* tell the diskstream we're in */
+
+ if (pl) {
+ pl->Modified();
+ }
+
+ /* tell everybody else */
+
+ send_change (ScaleAmplitudeChanged);
+}
+
+void
+AudioRegion::normalize_to (float target_dB)
+{
+ const nframes_t blocksize = 64 * 1024;
+ Sample buf[blocksize];
+ nframes_t fpos;
+ nframes_t fend;
+ nframes_t to_read;
+ double maxamp = 0;
+ gain_t target = dB_to_coefficient (target_dB);
+
+ if (target == 1.0f) {
+ /* do not normalize to precisely 1.0 (0 dBFS), to avoid making it appear
+ that we may have clipped.
+ */
+ target -= FLT_EPSILON;
+ }
+
+ fpos = _start;
+ fend = _start + _length;
+
+ /* first pass: find max amplitude */
+
+ while (fpos < fend) {
+
+ uint32_t n;
+
+ to_read = min (fend - fpos, blocksize);
+
+ for (n = 0; n < n_channels(); ++n) {
+
+ /* read it in */
+
+ if (read_raw_internal (buf, fpos, to_read) != to_read) {
+ return;
+ }
+
+ maxamp = compute_peak (buf, to_read, maxamp);
+ }
+
+ fpos += to_read;
+ };
+
+ if (maxamp == 0.0f) {
+ /* don't even try */
+ return;
+ }
+
+ if (maxamp == target) {
+ /* we can't do anything useful */
+ return;
+ }
+
+ /* compute scale factor */
+
+ _scale_amplitude = target/maxamp;
+
+ /* tell the diskstream we're in */
+
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (pl) {
+ pl->Modified();
+ }
+
+ /* tell everybody else */
+
+ send_change (ScaleAmplitudeChanged);
+}
+
+void
+AudioRegion::fade_in_changed ()
+{
+ send_change (FadeInChanged);
+}
+
+void
+AudioRegion::fade_out_changed ()
+{
+ send_change (FadeOutChanged);
+}
+
+void
+AudioRegion::envelope_changed ()
+{
+ send_change (EnvelopeChanged);
+}
+
+void
+AudioRegion::suspend_fade_in ()
+{
+ if (++_fade_in_disabled == 1) {
+ if (fade_in_is_default()) {
+ set_fade_in_active (false);
+ }
+ }
+}
+
+void
+AudioRegion::resume_fade_in ()
+{
+ if (--_fade_in_disabled == 0 && _fade_in_disabled) {
+ set_fade_in_active (true);
+ }
+}
+
+void
+AudioRegion::suspend_fade_out ()
+{
+ if (++_fade_out_disabled == 1) {
+ if (fade_out_is_default()) {
+ set_fade_out_active (false);
+ }
+ }
+}
+
+void
+AudioRegion::resume_fade_out ()
+{
+ if (--_fade_out_disabled == 0 &&_fade_out_disabled) {
+ set_fade_out_active (true);
+ }
+}
+
+bool
+AudioRegion::speed_mismatch (float sr) const
+{
+ if (_sources.empty()) {
+ /* impossible, but ... */
+ return false;
+ }
+
+ float fsr = audio_source()->sample_rate();
+
+ return fsr != sr;
+}
+
+void
+AudioRegion::source_offset_changed ()
+{
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(_sources.front());
+
+ if (afs && afs->destructive()) {
+ // set_start (source()->natural_position(), this);
+ set_position (source()->natural_position(), this);
+ }
+}
+
+boost::shared_ptr<AudioSource>
+AudioRegion::audio_source (uint32_t n) const
+{
+ // Guaranteed to succeed (use a static cast for speed?)
+ return boost::dynamic_pointer_cast<AudioSource>(source(n));
+}
+
+int
+AudioRegion::get_transients (AnalysisFeatureList& results, bool force_new)
+{
+ boost::shared_ptr<Playlist> pl = playlist();
+
+ if (!pl) {
+ return -1;
+ }
+
+ if (_valid_transients && !force_new) {
+ results = _transients;
+ return 0;
+ }
+
+ SourceList::iterator s;
+
+ for (s = _sources.begin() ; s != _sources.end(); ++s) {
+ if (!(*s)->has_been_analysed()) {
+ cerr << "For " << name() << " source " << (*s)->name() << " has not been analyzed\n";
+ break;
+ }
+ }
+
+ if (s == _sources.end()) {
+ /* all sources are analyzed, merge data from each one */
+
+ for (s = _sources.begin() ; s != _sources.end(); ++s) {
+
+ /* find the set of transients within the bounds of this region */
+
+ AnalysisFeatureList::iterator low = lower_bound ((*s)->transients.begin(),
+ (*s)->transients.end(),
+ _start);
+
+ AnalysisFeatureList::iterator high = upper_bound ((*s)->transients.begin(),
+ (*s)->transients.end(),
+ _start + _length);
+
+ /* and add them */
+
+ results.insert (results.end(), low, high);
+ }
+
+ TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
+
+ /* translate all transients to current position */
+
+ for (AnalysisFeatureList::iterator x = results.begin(); x != results.end(); ++x) {
+ (*x) -= _start;
+ (*x) += _position;
+ }
+
+ _transients = results;
+ _valid_transients = true;
+
+ return 0;
+ }
+
+ /* no existing/complete transient info */
+
+ if (!Config->get_auto_analyse_audio()) {
+ pl->session().Dialog (_("\
+You have requested an operation that requires audio analysis.\n\n\
+You currently have \"auto-analyse-audio\" disabled, which means\n\
+that transient data must be generated every time it is required.\n\n\
+If you are doing work that will require transient data on a\n\
+regular basis, you should probably enable \"auto-analyse-audio\"\n\
+then quit ardour and restart."));
+ }
+
+ TransientDetector t (pl->session().frame_rate());
+ bool existing_results = !results.empty();
+
+ _transients.clear ();
+ _valid_transients = false;
+
+ for (uint32_t i = 0; i < n_channels(); ++i) {
+
+ AnalysisFeatureList these_results;
+
+ t.reset ();
+
+ if (t.run ("", this, i, these_results)) {
+ return -1;
+ }
+
+ /* translate all transients to give absolute position */
+
+ for (AnalysisFeatureList::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());
+ TransientDetector::cleanup_transients (results, pl->session().frame_rate(), 3.0);
+ }
+
+ /* make sure ours are clean too */
+
+ TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0);
+
+ } else {
+
+ TransientDetector::cleanup_transients (_transients, pl->session().frame_rate(), 3.0);
+ results = _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)
+{
+ return ((AudioRegion *) arg)->read_peaks ((PeakData *) data, (nframes_t) npeaks, (nframes_t) start, (nframes_t) cnt, n_chan,samples_per_unit);
+}
+
+uint32_t region_length_from_c (void *arg)
+{
+
+ return ((AudioRegion *) arg)->length();
+}
+
+uint32_t sourcefile_length_from_c (void *arg, double zoom_factor)
+{
+ return ( (AudioRegion *) arg)->audio_source()->available_peaks (zoom_factor) ;
+}
+
+} /* extern "C" */
diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc
new file mode 100644
index 0000000000..01dea08d3e
--- /dev/null
+++ b/libs/ardour/audiosource.cc
@@ -0,0 +1,932 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <float.h>
+#include <utime.h>
+#include <cerrno>
+#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>
+
+#include <ardour/audiosource.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/session.h>
+#include <ardour/transient_detector.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+using Glib::ustring;
+
+bool AudioSource::_build_missing_peakfiles = false;
+bool AudioSource::_build_peakfiles = false;
+
+#define _FPP 256
+
+AudioSource::AudioSource (Session& s, ustring name)
+ : Source (s, name, DataType::AUDIO)
+{
+ _peaks_built = false;
+ _peak_byte_max = 0;
+ peakfile = -1;
+ _read_data_count = 0;
+ _write_data_count = 0;
+ peak_leftover_cnt = 0;
+ peak_leftover_size = 0;
+ peak_leftovers = 0;
+}
+
+AudioSource::AudioSource (Session& s, const XMLNode& node)
+ : Source (s, node)
+{
+
+ _peaks_built = false;
+ _peak_byte_max = 0;
+ peakfile = -1;
+ _read_data_count = 0;
+ _write_data_count = 0;
+ peak_leftover_cnt = 0;
+ peak_leftover_size = 0;
+ peak_leftovers = 0;
+
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+}
+
+AudioSource::~AudioSource ()
+{
+ /* shouldn't happen but make sure we don't leak file descriptors anyway */
+
+ if (peak_leftover_cnt) {
+ cerr << "AudioSource destroyed with leftover peak data pending" << endl;
+ }
+
+ if (peakfile >= 0) {
+ ::close (peakfile);
+ }
+
+ if (peak_leftovers) {
+ delete [] peak_leftovers;
+ }
+}
+
+XMLNode&
+AudioSource::get_state ()
+{
+ XMLNode& node (Source::get_state());
+
+ if (_captured_for.length()) {
+ node.add_property ("captured-for", _captured_for);
+ }
+
+ return node;
+}
+
+int
+AudioSource::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+
+ Source::set_state (node);
+
+ if ((prop = node.property ("captured-for")) != 0) {
+ _captured_for = prop->value();
+ }
+
+ return 0;
+}
+
+/***********************************************************************
+ PEAK FILE STUFF
+ ***********************************************************************/
+
+bool
+AudioSource::peaks_ready (sigc::slot<void> the_slot, sigc::connection& conn) const
+{
+ bool ret;
+ Glib::Mutex::Lock lm (_peaks_ready_lock);
+
+ /* check to see if the peak data is ready. if not
+ connect the slot while still holding the lock.
+ */
+
+ if (!(ret = _peaks_built)) {
+ conn = PeaksReady.connect (the_slot);
+ }
+
+ return ret;
+}
+
+void
+AudioSource::touch_peakfile ()
+{
+ struct stat statbuf;
+
+ if (stat (peakpath.c_str(), &statbuf) != 0 || statbuf.st_size == 0) {
+ return;
+ }
+
+ struct utimbuf tbuf;
+
+ tbuf.actime = statbuf.st_atime;
+ tbuf.modtime = time ((time_t) 0);
+
+ utime (peakpath.c_str(), &tbuf);
+}
+
+int
+AudioSource::rename_peakfile (ustring newpath)
+{
+ /* caller must hold _lock */
+
+ ustring oldpath = peakpath;
+
+ if (access (oldpath.c_str(), F_OK) == 0) {
+ if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
+ error << string_compose (_("cannot rename peakfile for %1 from %2 to %3 (%4)"), _name, oldpath, newpath, strerror (errno)) << endmsg;
+ return -1;
+ }
+ }
+
+ peakpath = newpath;
+
+ return 0;
+}
+
+int
+AudioSource::initialize_peakfile (bool newfile, ustring audio_path)
+{
+ struct stat statbuf;
+
+ peakpath = peak_path (audio_path);
+
+ /* if the peak file should be there, but isn't .... */
+
+ if (!newfile && !Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) {
+ peakpath = find_broken_peakfile (peakpath, audio_path);
+ }
+
+ if (stat (peakpath.c_str(), &statbuf)) {
+ if (errno != ENOENT) {
+ /* it exists in the peaks dir, but there is some kind of error */
+
+ error << string_compose(_("AudioSource: cannot stat peakfile \"%1\""), peakpath) << endmsg;
+ return -1;
+ }
+
+ /* peakfile does not exist */
+
+ _peaks_built = false;
+
+ } else {
+
+ /* we found it in the peaks dir, so check it out */
+
+ if (statbuf.st_size == 0 || (statbuf.st_size < ((length() / _FPP) * sizeof (PeakData)))) {
+ // empty
+ _peaks_built = false;
+ } else {
+ // Check if the audio file has changed since the peakfile was built.
+ struct stat stat_file;
+ int err = stat (audio_path.c_str(), &stat_file);
+
+ if (err) {
+ _peaks_built = false;
+ _peak_byte_max = 0;
+ } else {
+
+ /* allow 6 seconds slop on checking peak vs. file times because of various
+ disk action "races"
+ */
+
+ if (stat_file.st_mtime > statbuf.st_mtime && (stat_file.st_mtime - statbuf.st_mtime > 6)) {
+ _peaks_built = false;
+ _peak_byte_max = 0;
+ } else {
+ _peaks_built = true;
+ _peak_byte_max = statbuf.st_size;
+ }
+ }
+ }
+ }
+
+ if (!newfile && !_peaks_built && _build_missing_peakfiles && _build_peakfiles) {
+ build_peaks_from_scratch ();
+ }
+
+ return 0;
+}
+
+nframes_t
+AudioSource::read (Sample *dst, nframes_t start, nframes_t cnt) const
+{
+ Glib::Mutex::Lock lm (_lock);
+ return read_unlocked (dst, start, cnt);
+}
+
+nframes_t
+AudioSource::write (Sample *dst, nframes_t cnt)
+{
+ Glib::Mutex::Lock lm (_lock);
+ return write_unlocked (dst, cnt);
+}
+
+int
+AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt, double samples_per_visual_peak) const
+{
+ return read_peaks_with_fpp (peaks, npeaks, start, cnt, samples_per_visual_peak, _FPP);
+}
+
+int
+AudioSource::read_peaks_with_fpp (PeakData *peaks, nframes_t npeaks, nframes_t start, nframes_t cnt,
+ double samples_per_visual_peak, nframes_t samples_per_file_peak) const
+{
+ Glib::Mutex::Lock lm (_lock);
+ double scale;
+ double expected_peaks;
+ PeakData::PeakDatum xmax;
+ PeakData::PeakDatum xmin;
+ int32_t to_read;
+ uint32_t nread;
+ nframes_t zero_fill = 0;
+ int ret = -1;
+ PeakData* staging = 0;
+ Sample* raw_staging = 0;
+ int _peakfile = -1;
+
+ expected_peaks = (cnt / (double) samples_per_file_peak);
+ scale = npeaks/expected_peaks;
+
+#undef DEBUG_READ_PEAKS
+#ifdef DEBUG_READ_PEAKS
+ cerr << "======>RP: npeaks = " << npeaks
+ << " start = " << start
+ << " cnt = " << cnt
+ << " len = " << _length
+ << " samples_per_visual_peak =" << samples_per_visual_peak
+ << " expected was " << expected_peaks << " ... scale = " << scale
+ << " PD ptr = " << peaks
+ <<endl;
+
+#endif
+
+ /* fix for near-end-of-file conditions */
+
+ if (cnt > _length - start) {
+ // cerr << "too close to end @ " << _length << " given " << start << " + " << cnt << endl;
+ cnt = _length - start;
+ nframes_t old = npeaks;
+ npeaks = min ((nframes_t) floor (cnt / samples_per_visual_peak), npeaks);
+ zero_fill = old - npeaks;
+ }
+
+ // cerr << "actual npeaks = " << npeaks << " zf = " << zero_fill << endl;
+
+ if (npeaks == cnt) {
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "RAW DATA\n";
+#endif
+ /* no scaling at all, just get the sample data and duplicate it for
+ both max and min peak values.
+ */
+
+ Sample* raw_staging = new Sample[cnt];
+
+ if (read_unlocked (raw_staging, start, cnt) != cnt) {
+ error << _("cannot read sample data for unscaled peak computation") << endmsg;
+ return -1;
+ }
+
+ for (nframes_t i = 0; i < npeaks; ++i) {
+ peaks[i].max = raw_staging[i];
+ peaks[i].min = raw_staging[i];
+ }
+
+ delete [] raw_staging;
+ return 0;
+ }
+
+ if (scale == 1.0) {
+
+ off_t first_peak_byte = (start / samples_per_file_peak) * sizeof (PeakData);
+
+ /* open, read, close */
+
+ if ((_peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) {
+ error << string_compose(_("AudioSource: cannot open peakpath (a) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
+ return -1;
+ }
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "DIRECT PEAKS\n";
+#endif
+
+ nread = ::pread (_peakfile, peaks, sizeof (PeakData)* npeaks, first_peak_byte);
+ close (_peakfile);
+
+ if (nread != sizeof (PeakData) * npeaks) {
+ cerr << "AudioSource["
+ << _name
+ << "]: cannot read peaks from peakfile! (read only "
+ << nread
+ << " not "
+ << npeaks
+ << "at sample "
+ << start
+ << " = byte "
+ << first_peak_byte
+ << ')'
+ << endl;
+ return -1;
+ }
+
+ if (zero_fill) {
+ memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
+ }
+
+ return 0;
+ }
+
+
+ nframes_t tnp;
+
+ if (scale < 1.0) {
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "DOWNSAMPLE\n";
+#endif
+ /* the caller wants:
+
+ - more frames-per-peak (lower resolution) than the peakfile, or to put it another way,
+ - less peaks than the peakfile holds for the same range
+
+ So, read a block into a staging area, and then downsample from there.
+
+ to avoid confusion, I'll refer to the requested peaks as visual_peaks and the peakfile peaks as stored_peaks
+ */
+
+ const uint32_t chunksize = (uint32_t) min (expected_peaks, 65536.0);
+
+ staging = new PeakData[chunksize];
+
+ /* compute the rounded up frame position */
+
+ nframes_t current_frame = start;
+ nframes_t current_stored_peak = (nframes_t) ceil (current_frame / (double) samples_per_file_peak);
+ uint32_t next_visual_peak = (uint32_t) ceil (current_frame / samples_per_visual_peak);
+ double next_visual_peak_frame = next_visual_peak * samples_per_visual_peak;
+ uint32_t stored_peak_before_next_visual_peak = (nframes_t) next_visual_peak_frame / samples_per_file_peak;
+ uint32_t nvisual_peaks = 0;
+ uint32_t stored_peaks_read = 0;
+ uint32_t i = 0;
+
+ /* handle the case where the initial visual peak is on a pixel boundary */
+
+ current_stored_peak = min (current_stored_peak, stored_peak_before_next_visual_peak);
+
+ /* open ... close during out: handling */
+
+ if ((_peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) {
+ error << string_compose(_("AudioSource: cannot open peakpath (b) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
+ return 0;
+ }
+
+ while (nvisual_peaks < npeaks) {
+
+ if (i == stored_peaks_read) {
+
+ uint32_t start_byte = current_stored_peak * sizeof(PeakData);
+ tnp = min ((_length/samples_per_file_peak - current_stored_peak), (nframes_t) expected_peaks);
+ to_read = min (chunksize, tnp);
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "read " << sizeof (PeakData) * to_read << " from peakfile @ " << start_byte << endl;
+#endif
+
+ if ((nread = ::pread (_peakfile, staging, sizeof (PeakData) * to_read, start_byte))
+ != sizeof (PeakData) * to_read) {
+
+ off_t fend = lseek (_peakfile, 0, SEEK_END);
+
+ cerr << "AudioSource["
+ << _name
+ << "]: cannot read peak data from peakfile ("
+ << (nread / sizeof(PeakData))
+ << " peaks instead of "
+ << to_read
+ << ") ("
+ << strerror (errno)
+ << ')'
+ << " at start_byte = " << start_byte
+ << " _length = " << _length << " versus len = " << fend
+ << " expected maxpeaks = " << (_length - current_frame)/samples_per_file_peak
+ << " npeaks was " << npeaks
+ << endl;
+ goto out;
+ }
+
+ i = 0;
+ stored_peaks_read = nread / sizeof(PeakData);
+ }
+
+ xmax = -1.0;
+ xmin = 1.0;
+
+ while ((i < stored_peaks_read) && (current_stored_peak <= stored_peak_before_next_visual_peak)) {
+
+ xmax = max (xmax, staging[i].max);
+ xmin = min (xmin, staging[i].min);
+ ++i;
+ ++current_stored_peak;
+ --expected_peaks;
+ }
+
+ peaks[nvisual_peaks].max = xmax;
+ peaks[nvisual_peaks].min = xmin;
+ ++nvisual_peaks;
+ ++next_visual_peak;
+
+ //next_visual_peak_frame = min ((next_visual_peak * samples_per_visual_peak), (next_visual_peak_frame+samples_per_visual_peak) );
+ next_visual_peak_frame = min ((double) start+cnt, (next_visual_peak_frame+samples_per_visual_peak) );
+ stored_peak_before_next_visual_peak = (uint32_t) next_visual_peak_frame / samples_per_file_peak;
+ }
+
+ if (zero_fill) {
+ memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
+ }
+
+ ret = 0;
+
+ } else {
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "UPSAMPLE\n";
+#endif
+ /* the caller wants
+
+ - less frames-per-peak (more resolution)
+ - more peaks than stored in the Peakfile
+
+ So, fetch data from the raw source, and generate peak
+ data on the fly.
+ */
+
+ nframes_t frames_read = 0;
+ nframes_t current_frame = start;
+ nframes_t i = 0;
+ nframes_t nvisual_peaks = 0;
+ nframes_t chunksize = (nframes_t) min (cnt, (nframes_t) 4096);
+ raw_staging = new Sample[chunksize];
+
+ nframes_t frame_pos = start;
+ double pixel_pos = floor (frame_pos / samples_per_visual_peak);
+ double next_pixel_pos = ceil (frame_pos / samples_per_visual_peak);
+ double pixels_per_frame = 1.0 / samples_per_visual_peak;
+
+ xmin = 1.0;
+ xmax = -1.0;
+
+ while (nvisual_peaks < npeaks) {
+
+ if (i == frames_read) {
+
+ to_read = min (chunksize, (_length - current_frame));
+
+ if (to_read == 0) {
+ /* XXX ARGH .. out by one error ... need to figure out why this happens
+ and fix it rather than do this band-aid move.
+ */
+ zero_fill = npeaks - nvisual_peaks;
+ break;
+ }
+
+ if ((frames_read = read_unlocked (raw_staging, current_frame, to_read)) == 0) {
+ error << string_compose(_("AudioSource[%1]: peak read - cannot read %2 samples at offset %3 of %4 (%5)"),
+ _name, to_read, current_frame, _length, strerror (errno))
+ << endmsg;
+ goto out;
+ }
+
+ i = 0;
+ }
+
+ xmax = max (xmax, raw_staging[i]);
+ xmin = min (xmin, raw_staging[i]);
+ ++i;
+ ++current_frame;
+ pixel_pos += pixels_per_frame;
+
+ if (pixel_pos >= next_pixel_pos) {
+
+ peaks[nvisual_peaks].max = xmax;
+ peaks[nvisual_peaks].min = xmin;
+ ++nvisual_peaks;
+ xmin = 1.0;
+ xmax = -1.0;
+
+ next_pixel_pos = ceil (pixel_pos + 0.5);
+ }
+ }
+
+ if (zero_fill) {
+ memset (&peaks[npeaks], 0, sizeof (PeakData) * zero_fill);
+ }
+
+ ret = 0;
+ }
+
+ out:
+ if (_peakfile >= 0) {
+ close (_peakfile);
+ }
+
+ if (staging) {
+ delete [] staging;
+ }
+
+ if (raw_staging) {
+ delete [] raw_staging;
+ }
+
+#ifdef DEBUG_READ_PEAKS
+ cerr << "RP DONE\n";
+#endif
+
+ return ret;
+}
+
+#undef DEBUG_PEAK_BUILD
+
+int
+AudioSource::build_peaks_from_scratch ()
+{
+ nframes_t current_frame;
+ nframes_t cnt;
+ Sample* buf = 0;
+ nframes_t frames_read;
+ nframes_t frames_to_read;
+ const nframes_t bufsize = 65536; // 256kB per disk read for mono data is about ideal
+
+ int ret = -1;
+
+ {
+ /* hold lock while building peaks */
+
+ Glib::Mutex::Lock lp (_lock);
+
+ if (prepare_for_peakfile_writes ()) {
+ goto out;
+ }
+
+ current_frame = 0;
+ cnt = _length;
+ _peaks_built = false;
+ buf = new Sample[bufsize];
+
+ while (cnt) {
+
+ frames_to_read = min (bufsize, cnt);
+
+ if ((frames_read = read_unlocked (buf, current_frame, frames_to_read)) != frames_to_read) {
+ error << string_compose(_("%1: could not write read raw data for peak computation (%2)"), _name, strerror (errno)) << endmsg;
+ done_with_peakfile_writes (false);
+ goto out;
+ }
+
+ if (compute_and_write_peaks (buf, current_frame, frames_read, true, false, _FPP)) {
+ break;
+ }
+
+ current_frame += frames_read;
+ cnt -= frames_read;
+ }
+
+ if (cnt == 0) {
+ /* success */
+ truncate_peakfile();
+ }
+
+ done_with_peakfile_writes ((cnt == 0));
+ }
+
+ {
+ Glib::Mutex::Lock lm (_peaks_ready_lock);
+
+ if (_peaks_built) {
+ PeaksReady (); /* EMIT SIGNAL */
+ ret = 0;
+ }
+ }
+
+ out:
+ if (ret) {
+ unlink (peakpath.c_str());
+ }
+
+ if (buf) {
+ delete [] buf;
+ }
+
+ return ret;
+}
+
+int
+AudioSource::prepare_for_peakfile_writes ()
+{
+ if ((peakfile = ::open (peakpath.c_str(), O_RDWR|O_CREAT, 0664)) < 0) {
+ error << string_compose(_("AudioSource: cannot open peakpath (c) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg;
+ return -1;
+ }
+ return 0;
+}
+
+void
+AudioSource::done_with_peakfile_writes (bool done)
+{
+ if (peak_leftover_cnt) {
+ compute_and_write_peaks (0, 0, 0, true, false, _FPP);
+ }
+
+ if (done) {
+ _peaks_built = true;
+ }
+
+ if (peakfile >= 0) {
+ close (peakfile);
+ peakfile = -1;
+ }
+}
+
+int
+AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, bool intermediate_peaks_ready)
+{
+ return compute_and_write_peaks (buf, first_frame, cnt, force, intermediate_peaks_ready, _FPP);
+}
+
+int
+AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force,
+ bool intermediate_peaks_ready, nframes_t fpp)
+{
+ Sample* buf2 = 0;
+ nframes_t to_do;
+ uint32_t peaks_computed;
+ PeakData* peakbuf = 0;
+ int ret = -1;
+ nframes_t current_frame;
+ nframes_t frames_done;
+ const size_t blocksize = (128 * 1024);
+ off_t first_peak_byte;
+
+ if (peakfile < 0) {
+ prepare_for_peakfile_writes ();
+ }
+
+ restart:
+ if (peak_leftover_cnt) {
+
+ if (first_frame != peak_leftover_frame + peak_leftover_cnt) {
+
+ /* uh-oh, ::seek() since the last ::compute_and_write_peaks(),
+ and we have leftovers. flush a single peak (since the leftovers
+ never represent more than that, and restart.
+ */
+
+ PeakData x;
+
+ x.min = peak_leftovers[0];
+ x.max = peak_leftovers[0];
+
+ off_t byte = (peak_leftover_frame / fpp) * sizeof (PeakData);
+
+ if (::pwrite (peakfile, &x, sizeof (PeakData), byte) != sizeof (PeakData)) {
+ error << string_compose(_("%1: could not write peak file data (%2)"), _name, strerror (errno)) << endmsg;
+ goto out;
+ }
+
+ _peak_byte_max = max (_peak_byte_max, (off_t) (byte + sizeof(PeakData)));
+
+ {
+ Glib::Mutex::Lock lm (_peaks_ready_lock);
+ PeakRangeReady (peak_leftover_frame, peak_leftover_cnt); /* EMIT SIGNAL */
+ if (intermediate_peaks_ready) {
+ PeaksReady (); /* EMIT SIGNAL */
+ }
+ }
+
+ /* left overs are done */
+
+ peak_leftover_cnt = 0;
+ goto restart;
+ }
+
+ /* else ... had leftovers, but they immediately preceed the new data, so just
+ merge them and compute.
+ */
+
+ /* make a new contiguous buffer containing leftovers and the new stuff */
+
+ to_do = cnt + peak_leftover_cnt;
+ buf2 = new Sample[to_do];
+
+ /* the remnants */
+ memcpy (buf2, peak_leftovers, peak_leftover_cnt * sizeof (Sample));
+
+ /* the new stuff */
+ memcpy (buf2+peak_leftover_cnt, buf, cnt * sizeof (Sample));
+
+ /* no more leftovers */
+ peak_leftover_cnt = 0;
+
+ /* use the temporary buffer */
+ buf = buf2;
+
+ /* make sure that when we write into the peakfile, we startup where we left off */
+
+ first_frame = peak_leftover_frame;
+
+ } else {
+ to_do = cnt;
+ }
+
+ peakbuf = new PeakData[(to_do/fpp)+1];
+ peaks_computed = 0;
+ current_frame = first_frame;
+ frames_done = 0;
+
+ while (to_do) {
+
+ /* if some frames were passed in (i.e. we're not flushing leftovers)
+ and there are less than fpp to do, save them till
+ next time
+ */
+
+ if (force && (to_do < fpp)) {
+ /* keep the left overs around for next time */
+
+ if (peak_leftover_size < to_do) {
+ delete [] peak_leftovers;
+ peak_leftovers = new Sample[to_do];
+ peak_leftover_size = to_do;
+ }
+ memcpy (peak_leftovers, buf, to_do * sizeof (Sample));
+ peak_leftover_cnt = to_do;
+ peak_leftover_frame = current_frame;
+
+ /* done for now */
+
+ break;
+ }
+
+ nframes_t this_time = min (fpp, to_do);
+
+ peakbuf[peaks_computed].max = buf[0];
+ peakbuf[peaks_computed].min = buf[0];
+
+ ARDOUR::find_peaks (buf+1, this_time-1, &peakbuf[peaks_computed].min, &peakbuf[peaks_computed].max);
+
+ peaks_computed++;
+ buf += this_time;
+ to_do -= this_time;
+ frames_done += this_time;
+ current_frame += this_time;
+ }
+
+ first_peak_byte = (first_frame / fpp) * sizeof (PeakData);
+
+ if (can_truncate_peaks()) {
+
+ /* on some filesystems (ext3, at least) this helps to reduce fragmentation of
+ the peakfiles. its not guaranteed to do so, and even on ext3 (as of december 2006)
+ it does not cause single-extent allocation even for peakfiles of
+ less than BLOCKSIZE bytes. only call ftruncate if we'll make the file larger.
+ */
+
+ off_t endpos = lseek (peakfile, 0, SEEK_END);
+ off_t target_length = blocksize * ((first_peak_byte + blocksize + 1) / blocksize);
+
+ if (endpos < target_length) {
+ ftruncate (peakfile, target_length);
+ /* error doesn't actually matter though, so continue on without testing */
+ }
+ }
+
+ if (::pwrite (peakfile, peakbuf, sizeof (PeakData) * peaks_computed, first_peak_byte) != (ssize_t) (sizeof (PeakData) * peaks_computed)) {
+ error << string_compose(_("%1: could not write peak file data (%2)"), _name, strerror (errno)) << endmsg;
+ goto out;
+ }
+
+ _peak_byte_max = max (_peak_byte_max, (off_t) (first_peak_byte + sizeof(PeakData)*peaks_computed));
+
+ if (frames_done) {
+ Glib::Mutex::Lock lm (_peaks_ready_lock);
+ PeakRangeReady (first_frame, frames_done); /* EMIT SIGNAL */
+ if (intermediate_peaks_ready) {
+ PeaksReady (); /* EMIT SIGNAL */
+ }
+ }
+
+ ret = 0;
+
+ out:
+ delete [] peakbuf;
+ if (buf2) {
+ delete [] buf2;
+ }
+ return ret;
+}
+
+void
+AudioSource::truncate_peakfile ()
+{
+ if (peakfile < 0) {
+ error << string_compose (_("programming error: %1"), "AudioSource::truncate_peakfile() called without open peakfile descriptor")
+ << endmsg;
+ return;
+ }
+
+ /* truncate the peakfile down to its natural length if necessary */
+
+ off_t end = lseek (peakfile, 0, SEEK_END);
+
+ if (end > _peak_byte_max) {
+ ftruncate (peakfile, _peak_byte_max);
+ }
+}
+
+bool
+AudioSource::file_changed (ustring path)
+{
+ struct stat stat_file;
+ struct stat stat_peak;
+
+ int e1 = stat (path.c_str(), &stat_file);
+ int e2 = stat (peak_path(path).c_str(), &stat_peak);
+
+ if (!e1 && !e2 && stat_file.st_mtime > stat_peak.st_mtime){
+ return true;
+ } else {
+ return false;
+ }
+}
+
+nframes_t
+AudioSource::available_peaks (double zoom_factor) const
+{
+ off_t end;
+
+ if (zoom_factor < _FPP) {
+ return length(); // peak data will come from the audio file
+ }
+
+ /* peak data comes from peakfile, but the filesize might not represent
+ the valid data due to ftruncate optimizations, so use _peak_byte_max state.
+ XXX - there might be some atomicity issues here, we should probably add a lock,
+ but _peak_byte_max only monotonically increases after initialization.
+ */
+
+ end = _peak_byte_max;
+
+ return (end/sizeof(PeakData)) * _FPP;
+}
+
+void
+AudioSource::update_length (nframes_t pos, nframes_t cnt)
+{
+ if (pos + cnt > _length) {
+ _length = pos+cnt;
+ }
+}
+
diff --git a/libs/ardour/auditioner.cc b/libs/ardour/auditioner.cc
new file mode 100644
index 0000000000..d6f63c2f9d
--- /dev/null
+++ b/libs/ardour/auditioner.cc
@@ -0,0 +1,233 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <glibmm/thread.h>
+
+#include <pbd/error.h>
+
+#include <ardour/audio_diskstream.h>
+#include <ardour/audioregion.h>
+#include <ardour/audioengine.h>
+#include <ardour/route.h>
+#include <ardour/session.h>
+#include <ardour/auditioner.h>
+#include <ardour/audioplaylist.h>
+#include <ardour/panner.h>
+#include <ardour/data_type.h>
+#include <ardour/region_factory.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+#include "i18n.h"
+
+Auditioner::Auditioner (Session& s)
+ : AudioTrack (s, "auditioner", Route::Hidden)
+{
+ string left = Config->get_auditioner_output_left();
+ string right = Config->get_auditioner_output_right();
+
+ if (left == "default") {
+ left = _session.engine().get_nth_physical_output (DataType::AUDIO, 0);
+ }
+
+ if (right == "default") {
+ right = _session.engine().get_nth_physical_output (DataType::AUDIO, 1);
+ }
+
+ if ((left.length() == 0) && (right.length() == 0)) {
+ warning << _("no outputs available for auditioner - manual connection required") << endmsg;
+ return;
+ }
+
+ defer_pan_reset ();
+
+ if (left.length()) {
+ add_output_port (left, this, DataType::AUDIO);
+ }
+
+ if (right.length()) {
+ audio_diskstream()->add_channel (1);
+ add_output_port (right, this, DataType::AUDIO);
+ }
+
+ allow_pan_reset ();
+
+ reset_panner ();
+
+ IO::output_changed.connect (mem_fun (*this, &Auditioner::output_changed));
+
+ the_region.reset ((AudioRegion*) 0);
+ g_atomic_int_set (&_active, 0);
+}
+
+Auditioner::~Auditioner ()
+{
+}
+
+AudioPlaylist&
+Auditioner::prepare_playlist ()
+{
+ // FIXME auditioner is still audio-only
+ boost::shared_ptr<AudioPlaylist> apl = boost::dynamic_pointer_cast<AudioPlaylist>(_diskstream->playlist());
+ assert(apl);
+
+ apl->clear ();
+ return *apl;
+}
+
+void
+Auditioner::audition_current_playlist ()
+{
+ if (g_atomic_int_get (&_active)) {
+ /* don't go via session for this, because we are going
+ to remain active.
+ */
+ cancel_audition ();
+ }
+
+ Glib::Mutex::Lock lm (lock);
+ _diskstream->seek (0);
+ length = _diskstream->playlist()->get_maximum_extent();
+ current_frame = 0;
+
+ /* force a panner reset now that we have all channels */
+
+ _panner->reset (n_outputs().n_audio(), _diskstream->n_channels().n_audio());
+
+ g_atomic_int_set (&_active, 1);
+}
+
+void
+Auditioner::audition_region (boost::shared_ptr<Region> region)
+{
+ if (g_atomic_int_get (&_active)) {
+ /* don't go via session for this, because we are going
+ to remain active.
+ */
+ cancel_audition ();
+ }
+
+ if (boost::dynamic_pointer_cast<AudioRegion>(region) == 0) {
+ error << _("Auditioning of non-audio regions not yet supported") << endmsg;
+ return;
+ }
+
+ Glib::Mutex::Lock lm (lock);
+
+ /* copy it */
+
+ boost::shared_ptr<AudioRegion> the_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region)));
+ the_region->set_position (0, this);
+
+ _diskstream->playlist()->drop_regions ();
+ _diskstream->playlist()->add_region (the_region, 0, 1);
+
+ if (_diskstream->n_channels().n_audio() < the_region->n_channels()) {
+ audio_diskstream()->add_channel (the_region->n_channels() - _diskstream->n_channels().n_audio());
+ } else if (_diskstream->n_channels().n_audio() > the_region->n_channels()) {
+ audio_diskstream()->remove_channel (_diskstream->n_channels().n_audio() - the_region->n_channels());
+ }
+
+ /* force a panner reset now that we have all channels */
+
+ reset_panner();
+
+ length = the_region->length();
+
+ int dir;
+ nframes_t offset = the_region->sync_offset (dir);
+
+ /* can't audition from a negative sync point */
+
+ if (dir < 0) {
+ offset = 0;
+ }
+
+ _diskstream->seek (offset);
+ current_frame = offset;
+
+ g_atomic_int_set (&_active, 1);
+}
+
+int
+Auditioner::play_audition (nframes_t nframes)
+{
+ bool need_butler;
+ nframes_t this_nframes;
+ int ret;
+
+ if (g_atomic_int_get (&_active) == 0) {
+ silence (nframes, 0);
+ return 0;
+ }
+
+ this_nframes = min (nframes, length - current_frame);
+
+ _diskstream->prepare ();
+
+ if ((ret = roll (this_nframes, current_frame, current_frame + nframes, 0, false, false, false)) != 0) {
+ silence (nframes, 0);
+ return ret;
+ }
+
+ need_butler = _diskstream->commit (this_nframes);
+ current_frame += this_nframes;
+
+ if (current_frame >= length) {
+ _session.cancel_audition ();
+ return 0;
+ } else {
+ return need_butler ? 1 : 0;
+ }
+}
+
+void
+Auditioner::output_changed (IOChange change, void* src)
+{
+ string phys;
+
+ if (change & ConnectionsChanged) {
+ vector<string> connections;
+ if (output (0)->get_connections (connections)) {
+ phys = _session.engine().get_nth_physical_output (DataType::AUDIO, 0);
+ if (phys != connections[0]) {
+ Config->set_auditioner_output_left (connections[0]);
+ } else {
+ Config->set_auditioner_output_left ("default");
+ }
+ } else {
+ Config->set_auditioner_output_left ("");
+ }
+
+ connections.clear ();
+
+ if (output (1)->get_connections (connections)) {
+ phys = _session.engine().get_nth_physical_output (DataType::AUDIO, 1);
+ if (phys != connections[0]) {
+ Config->set_auditioner_output_right (connections[0]);
+ } else {
+ Config->set_auditioner_output_right ("default");
+ }
+ } else {
+ Config->set_auditioner_output_right ("");
+ }
+ }
+}
diff --git a/libs/ardour/auto_bundle.cc b/libs/ardour/auto_bundle.cc
new file mode 100644
index 0000000000..9da32bbb7a
--- /dev/null
+++ b/libs/ardour/auto_bundle.cc
@@ -0,0 +1,47 @@
+#include <cassert>
+#include "ardour/auto_bundle.h"
+
+ARDOUR::AutoBundle::AutoBundle (bool i)
+ : Bundle (i)
+{
+
+}
+
+ARDOUR::AutoBundle::AutoBundle (std::string const & n, bool i)
+ : Bundle (n, i)
+{
+
+}
+
+uint32_t
+ARDOUR::AutoBundle::nchannels () const
+{
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return _ports.size ();
+}
+
+const ARDOUR::PortList&
+ARDOUR::AutoBundle::channel_ports (uint32_t c) const
+{
+ assert (c < nchannels());
+
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return _ports[c];
+}
+
+void
+ARDOUR::AutoBundle::set_channels (uint32_t n)
+{
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports.resize (n);
+}
+
+void
+ARDOUR::AutoBundle::set_port (uint32_t c, std::string const & p)
+{
+ assert (c < nchannels ());
+
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports[c].resize (1);
+ _ports[c][0] = p;
+}
diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc
new file mode 100644
index 0000000000..3c076c371a
--- /dev/null
+++ b/libs/ardour/automatable.cc
@@ -0,0 +1,477 @@
+/*
+ Copyright (C) 2001,2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/ardour.h>
+#include <fstream>
+#include <inttypes.h>
+#include <cstdio>
+#include <errno.h>
+#include <pbd/error.h>
+#include <pbd/enumwriter.h>
+#include <midi++/names.h>
+#include <ardour/session.h>
+#include <ardour/automatable.h>
+#include <ardour/midi_track.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+nframes_t Automatable::_automation_interval = 0;
+
+Automatable::Automatable(Session& _session, const string& name)
+ : SessionObject(_session, name)
+ , _last_automation_snapshot(0)
+{}
+
+int
+Automatable::old_set_automation_state (const XMLNode& node)
+{
+ const XMLProperty *prop;
+
+ if ((prop = node.property ("path")) != 0) {
+ load_automation (prop->value());
+ } else {
+ warning << string_compose(_("%1: Automation node has no path property"), _name) << endmsg;
+ }
+
+ if ((prop = node.property ("visible")) != 0) {
+ uint32_t what;
+ stringstream sstr;
+
+ _visible_controls.clear ();
+
+ sstr << prop->value();
+ while (1) {
+ sstr >> what;
+ if (sstr.fail()) {
+ break;
+ }
+ mark_automation_visible (Parameter(PluginAutomation, what), true);
+ }
+ }
+
+ _last_automation_snapshot = 0;
+
+ return 0;
+}
+
+int
+Automatable::load_automation (const string& path)
+{
+ string fullpath;
+
+ if (path[0] == '/') { // legacy
+ fullpath = path;
+ } else {
+ fullpath = _session.automation_dir();
+ fullpath += path;
+ }
+ ifstream in (fullpath.c_str());
+
+ if (!in) {
+ warning << string_compose(_("%1: cannot open %2 to load automation data (%3)"), _name, fullpath, strerror (errno)) << endmsg;
+ return 1;
+ }
+
+ Glib::Mutex::Lock lm (_automation_lock);
+ set<Parameter> tosave;
+ _controls.clear ();
+
+ _last_automation_snapshot = 0;
+
+ while (in) {
+ double when;
+ double value;
+ uint32_t port;
+
+ in >> port; if (!in) break;
+ in >> when; if (!in) goto bad;
+ in >> value; if (!in) goto bad;
+
+ /* FIXME: this is legacy and only used for plugin inserts? I think? */
+ boost::shared_ptr<AutomationControl> c = control (Parameter(PluginAutomation, port), true);
+ c->list()->add (when, value);
+ tosave.insert (Parameter(PluginAutomation, port));
+ }
+
+ return 0;
+
+ bad:
+ error << string_compose(_("%1: cannot load automation data from %2"), _name, fullpath) << endmsg;
+ _controls.clear ();
+ return -1;
+}
+
+void
+Automatable::add_control(boost::shared_ptr<AutomationControl> ac)
+{
+ Parameter param = ac->parameter();
+
+ _controls[param] = ac;
+
+ _can_automate_list.insert(param);
+
+ // Sync everything (derived classes) up to initial values
+ auto_state_changed(param);
+}
+
+void
+Automatable::what_has_automation (set<Parameter>& s) const
+{
+ Glib::Mutex::Lock lm (_automation_lock);
+ Controls::const_iterator li;
+
+ // FIXME: correct semantics?
+ for (li = _controls.begin(); li != _controls.end(); ++li) {
+ s.insert ((*li).first);
+ }
+}
+
+void
+Automatable::what_has_visible_automation (set<Parameter>& s) const
+{
+ Glib::Mutex::Lock lm (_automation_lock);
+ set<Parameter>::const_iterator li;
+
+ for (li = _visible_controls.begin(); li != _visible_controls.end(); ++li) {
+ s.insert (*li);
+ }
+}
+
+/** Returns NULL if we don't have an AutomationList for \a parameter.
+ */
+boost::shared_ptr<AutomationControl>
+Automatable::control (Parameter parameter, bool create_if_missing)
+{
+ Controls::iterator i = _controls.find(parameter);
+
+ if (i != _controls.end()) {
+ return i->second;
+
+ } else if (create_if_missing) {
+ boost::shared_ptr<AutomationList> al (new AutomationList (
+ parameter, FLT_MIN, FLT_MAX, default_parameter_value (parameter)));
+ boost::shared_ptr<AutomationControl> ac(control_factory(al));
+ add_control(ac);
+ return ac;
+
+ } else {
+ //warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg;
+ return boost::shared_ptr<AutomationControl>();
+ }
+}
+
+boost::shared_ptr<const AutomationControl>
+Automatable::control (Parameter parameter) const
+{
+ Controls::const_iterator i = _controls.find(parameter);
+
+ if (i != _controls.end()) {
+ return i->second;
+ } else {
+ //warning << "AutomationList " << parameter.to_string() << " not found for " << _name << endmsg;
+ return boost::shared_ptr<AutomationControl>();
+ }
+}
+
+string
+Automatable::describe_parameter (Parameter param)
+{
+ /* derived classes like PluginInsert should override this */
+
+ if (param == Parameter(GainAutomation)) {
+ return _("Fader");
+ } else if (param.type() == PanAutomation) {
+ return (string_compose(_("Pan %1"), param.id()));
+ } else if (param.type() == MidiCCAutomation) {
+ return string_compose("CC %1 (%2) [%3]",
+ param.id(), midi_name(param.id()), int(param.channel()) + 1);
+ } else if (param.type() == MidiPgmChangeAutomation) {
+ return string_compose("Program [%1]", int(param.channel()) + 1);
+ } else if (param.type() == MidiPitchBenderAutomation) {
+ return string_compose("Bender [%1]", int(param.channel()) + 1);
+ } else if (param.type() == MidiChannelAftertouchAutomation) {
+ return string_compose("Aftertouch [%1]", int(param.channel()) + 1);
+ } else {
+ return param.to_string();
+ }
+}
+
+void
+Automatable::can_automate (Parameter what)
+{
+ _can_automate_list.insert (what);
+}
+
+void
+Automatable::mark_automation_visible (Parameter what, bool yn)
+{
+ if (yn) {
+ _visible_controls.insert (what);
+ } else {
+ set<Parameter>::iterator i;
+
+ if ((i = _visible_controls.find (what)) != _visible_controls.end()) {
+ _visible_controls.erase (i);
+ }
+ }
+}
+
+bool
+Automatable::find_next_event (nframes_t now, nframes_t end, ControlEvent& next_event) const
+{
+ Controls::const_iterator li;
+
+ next_event.when = max_frames;
+
+ for (li = _controls.begin(); li != _controls.end(); ++li) {
+
+ AutomationList::const_iterator i;
+ boost::shared_ptr<const AutomationList> alist (li->second->list());
+ ControlEvent cp (now, 0.0f);
+
+ for (i = lower_bound (alist->const_begin(), alist->const_end(), &cp, AutomationList::time_comparator);
+ i != alist->const_end() && (*i)->when < end; ++i) {
+ if ((*i)->when > now) {
+ break;
+ }
+ }
+
+ if (i != alist->const_end() && (*i)->when < end) {
+
+ if ((*i)->when < next_event.when) {
+ next_event.when = (*i)->when;
+ }
+ }
+ }
+
+ return next_event.when != max_frames;
+}
+
+/** \a legacy_param is used for loading legacy sessions where an object (IO, Panner)
+ * had a single automation parameter, with it's type implicit. Derived objects should
+ * pass that type and it will be used for the untyped AutomationList found.
+ */
+int
+Automatable::set_automation_state (const XMLNode& node, Parameter legacy_param)
+{
+ Glib::Mutex::Lock lm (_automation_lock);
+
+ /* Don't clear controls, since some may be special derived Controllable classes */
+
+ _visible_controls.clear ();
+
+ XMLNodeList nlist = node.children();
+ XMLNodeIterator niter;
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ /*if (sscanf ((*niter)->name().c_str(), "parameter-%" PRIu32, &param) != 1) {
+ error << string_compose (_("%2: badly formatted node name in XML automation state, ignored"), _name) << endmsg;
+ continue;
+ }*/
+
+ if ((*niter)->name() == "AutomationList") {
+
+ const XMLProperty* id_prop = (*niter)->property("automation-id");
+
+ Parameter param = (id_prop ? Parameter(id_prop->value()) : legacy_param);
+
+ boost::shared_ptr<AutomationList> al (new AutomationList(**niter, param));
+
+ if (!id_prop) {
+ warning << "AutomationList node without automation-id property, "
+ << "using default: " << legacy_param.to_string() << endmsg;
+ al->set_parameter(legacy_param);
+ }
+
+ boost::shared_ptr<AutomationControl> existing = control(param);
+ if (existing)
+ existing->set_list(al);
+ else
+ add_control(control_factory(al));
+
+ } else {
+ error << "Expected AutomationList node, got '" << (*niter)->name() << endmsg;
+ }
+ }
+
+ _last_automation_snapshot = 0;
+
+ return 0;
+}
+
+XMLNode&
+Automatable::get_automation_state ()
+{
+ Glib::Mutex::Lock lm (_automation_lock);
+ XMLNode* node = new XMLNode (X_("Automation"));
+
+ if (_controls.empty()) {
+ return *node;
+ }
+
+ for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) {
+ node->add_child_nocopy (li->second->list()->get_state ());
+ }
+
+ return *node;
+}
+
+void
+Automatable::clear_automation ()
+{
+ Glib::Mutex::Lock lm (_automation_lock);
+
+ for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li)
+ li->second->list()->clear();
+}
+
+void
+Automatable::set_parameter_automation_state (Parameter param, AutoState s)
+{
+ Glib::Mutex::Lock lm (_automation_lock);
+
+ boost::shared_ptr<AutomationControl> c = control (param, true);
+
+ if (s != c->list()->automation_state()) {
+ c->list()->set_automation_state (s);
+ _session.set_dirty ();
+ }
+}
+
+AutoState
+Automatable::get_parameter_automation_state (Parameter param, bool lock)
+{
+ AutoState result = Off;
+
+ if (lock)
+ _automation_lock.lock();
+
+ boost::shared_ptr<AutomationControl> c = control(param);
+
+ if (c)
+ result = c->list()->automation_state();
+
+ if (lock)
+ _automation_lock.unlock();
+
+ return result;
+}
+
+void
+Automatable::set_parameter_automation_style (Parameter param, AutoStyle s)
+{
+ Glib::Mutex::Lock lm (_automation_lock);
+
+ boost::shared_ptr<AutomationControl> c = control(param, true);
+
+ if (s != c->list()->automation_style()) {
+ c->list()->set_automation_style (s);
+ _session.set_dirty ();
+ }
+}
+
+AutoStyle
+Automatable::get_parameter_automation_style (Parameter param)
+{
+ Glib::Mutex::Lock lm (_automation_lock);
+
+ boost::shared_ptr<AutomationControl> c = control(param);
+
+ if (c) {
+ return c->list()->automation_style();
+ } else {
+ return Absolute; // whatever
+ }
+}
+
+void
+Automatable::protect_automation ()
+{
+ set<Parameter> automated_params;
+
+ what_has_automation (automated_params);
+
+ for (set<Parameter>::iterator i = automated_params.begin(); i != automated_params.end(); ++i) {
+
+ boost::shared_ptr<AutomationControl> c = control(*i);
+
+ switch (c->list()->automation_state()) {
+ case Write:
+ c->list()->set_automation_state (Off);
+ break;
+ case Touch:
+ c->list()->set_automation_state (Play);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void
+Automatable::automation_snapshot (nframes_t now, bool force)
+{
+ if (force || _last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
+
+ for (Controls::iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ if (i->second->list()->automation_write()) {
+ i->second->list()->rt_add (now, i->second->user_value());
+ }
+ }
+
+ _last_automation_snapshot = now;
+ }
+}
+
+void
+Automatable::transport_stopped (nframes_t now)
+{
+ for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li) {
+
+ boost::shared_ptr<AutomationControl> c = li->second;
+
+ c->list()->reposition_for_rt_add (now);
+
+ if (c->list()->automation_state() != Off) {
+ c->set_value(c->list()->eval(now));
+ }
+ }
+}
+
+/* FIXME: this probably doesn't belong here */
+boost::shared_ptr<AutomationControl>
+Automatable::control_factory(boost::shared_ptr<AutomationList> list)
+{
+ if (
+ list->parameter().type() == MidiCCAutomation ||
+ list->parameter().type() == MidiPgmChangeAutomation ||
+ list->parameter().type() == MidiChannelAftertouchAutomation
+ ) {
+ // FIXME: this will die horribly if this is not a MidiTrack
+ return boost::shared_ptr<AutomationControl>(new MidiTrack::MidiControl((MidiTrack*)this, list));
+ } else {
+ return boost::shared_ptr<AutomationControl>(new AutomationControl(_session, list));
+ }
+}
+
diff --git a/libs/ardour/automation.cc b/libs/ardour/automation.cc
new file mode 100644
index 0000000000..c6e96cfac8
--- /dev/null
+++ b/libs/ardour/automation.cc
@@ -0,0 +1,32 @@
+/*
+ Copyright (C) 2000-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <stdint.h>
+
+template<class AutomatedObject>
+struct AutomationEvent {
+ uint32_t frame;
+ AutomatedObject *object;
+ void (AutomatedObject::*function) (void *);
+ void *arg;
+
+ void operator() (){
+ object->function (arg);
+ }
+};
diff --git a/libs/ardour/automation_control.cc b/libs/ardour/automation_control.cc
new file mode 100644
index 0000000000..4885a6fed9
--- /dev/null
+++ b/libs/ardour/automation_control.cc
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2007 Paul Davis
+ Author: Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <ardour/automation_control.h>
+#include <ardour/session.h>
+#include <ardour/automatable.h>
+#include <ardour/midi_track.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+
+AutomationControl::AutomationControl(Session& session, boost::shared_ptr<AutomationList> list, string name)
+ : Controllable((name == "unnamed controllable") ? list->parameter().to_string() : name)
+ , _session(session)
+ , _list(list)
+ , _user_value(list->default_value())
+{
+}
+
+
+/** Get the currently effective value (ie the one that corresponds to current output)
+ */
+float
+AutomationControl::get_value() const
+{
+ if (_list->automation_playback())
+ return _list->eval(_session.transport_frame());
+ else
+ return _user_value;
+}
+
+
+void
+AutomationControl::set_value(float value)
+{
+ _user_value = value;
+
+ if (_session.transport_stopped() && _list->automation_write())
+ _list->add(_session.transport_frame(), value);
+
+ Changed(); /* EMIT SIGNAL */
+}
+
+
+/** Get the latest user-set value, which may not equal get_value() when automation
+ * is playing back, etc.
+ *
+ * Automation write/touch works by periodically sampling this value and adding it
+ * to the AutomationList.
+ */
+float
+AutomationControl::user_value() const
+{
+ return _user_value;
+}
+
+
+void
+AutomationControl::set_list(boost::shared_ptr<ARDOUR::AutomationList> list)
+{
+ _list = list;
+ _user_value = list->default_value();
+ Changed(); /* EMIT SIGNAL */
+}
+
+
+Parameter
+AutomationControl::parameter() const
+{
+ return _list->parameter();
+}
diff --git a/libs/ardour/automation_event.cc b/libs/ardour/automation_event.cc
new file mode 100644
index 0000000000..af390953f4
--- /dev/null
+++ b/libs/ardour/automation_event.cc
@@ -0,0 +1,1666 @@
+/*
+ Copyright (C) 2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <set>
+#include <climits>
+#include <float.h>
+#include <cmath>
+#include <sstream>
+#include <algorithm>
+#include <sigc++/bind.h>
+#include <ardour/parameter.h>
+#include <ardour/automation_event.h>
+#include <ardour/curve.h>
+#include <pbd/stacktrace.h>
+#include <pbd/enumwriter.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace sigc;
+using namespace PBD;
+
+sigc::signal<void,AutomationList *> AutomationList::AutomationListCreated;
+
+static bool sort_events_by_time (ControlEvent* a, ControlEvent* b)
+{
+ return a->when < b->when;
+}
+
+#if 0
+static void dumpit (const AutomationList& al, string prefix = "")
+{
+ cerr << prefix << &al << endl;
+ for (AutomationList::const_iterator i = al.const_begin(); i != al.const_end(); ++i) {
+ cerr << prefix << '\t' << (*i)->when << ',' << (*i)->value << endl;
+ }
+ cerr << "\n";
+}
+#endif
+
+/* XXX: min_val max_val redundant? (param.min() param.max()) */
+AutomationList::AutomationList (Parameter id, double min_val, double max_val, double default_val)
+ : _parameter(id)
+ , _interpolation(Linear)
+ , _curve(new Curve(*this))
+{
+ _parameter = id;
+ _frozen = 0;
+ _changed_when_thawed = false;
+ _state = Off;
+ _style = Absolute;
+ _min_yval = min_val;
+ _max_yval = max_val;
+ _touching = false;
+ _max_xval = 0; // means "no limit"
+ _default_value = default_val;
+ _rt_insertion_point = _events.end();
+ _lookup_cache.left = -1;
+ _lookup_cache.range.first = _events.end();
+ _search_cache.left = -1;
+ _search_cache.range.first = _events.end();
+ _sort_pending = false;
+
+ assert(_parameter.type() != NullAutomation);
+ AutomationListCreated(this);
+}
+
+AutomationList::AutomationList (const AutomationList& other)
+ : _parameter(other._parameter)
+ , _interpolation(Linear)
+ , _curve(new Curve(*this))
+{
+ _frozen = 0;
+ _changed_when_thawed = false;
+ _style = other._style;
+ _min_yval = other._min_yval;
+ _max_yval = other._max_yval;
+ _max_xval = other._max_xval;
+ _default_value = other._default_value;
+ _state = other._state;
+ _touching = other._touching;
+ _rt_insertion_point = _events.end();
+ _lookup_cache.range.first = _events.end();
+ _search_cache.range.first = _events.end();
+ _sort_pending = false;
+
+ for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) {
+ _events.push_back (new ControlEvent (**i));
+ }
+
+ mark_dirty ();
+ assert(_parameter.type() != NullAutomation);
+ AutomationListCreated(this);
+}
+
+AutomationList::AutomationList (const AutomationList& other, double start, double end)
+ : _parameter(other._parameter)
+ , _interpolation(Linear)
+ , _curve(new Curve(*this))
+{
+ _frozen = 0;
+ _changed_when_thawed = false;
+ _style = other._style;
+ _min_yval = other._min_yval;
+ _max_yval = other._max_yval;
+ _max_xval = other._max_xval;
+ _default_value = other._default_value;
+ _state = other._state;
+ _touching = other._touching;
+ _rt_insertion_point = _events.end();
+ _lookup_cache.range.first = _events.end();
+ _search_cache.range.first = _events.end();
+ _sort_pending = false;
+
+ /* now grab the relevant points, and shift them back if necessary */
+
+ AutomationList* section = const_cast<AutomationList*>(&other)->copy (start, end);
+
+ if (!section->empty()) {
+ for (iterator i = section->begin(); i != section->end(); ++i) {
+ _events.push_back (new ControlEvent ((*i)->when, (*i)->value));
+ }
+ }
+
+ delete section;
+
+ mark_dirty ();
+
+ assert(_parameter.type() != NullAutomation);
+ AutomationListCreated(this);
+}
+
+/** \a id is used for legacy sessions where the type is not present
+ * in or below the <AutomationList> node. It is used if \a id is non-null.
+ */
+AutomationList::AutomationList (const XMLNode& node, Parameter id)
+ : _interpolation(Linear)
+ , _curve(new Curve(*this))
+{
+ _frozen = 0;
+ _changed_when_thawed = false;
+ _touching = false;
+ _min_yval = FLT_MIN;
+ _max_yval = FLT_MAX;
+ _max_xval = 0; // means "no limit"
+ _state = Off;
+ _style = Absolute;
+ _rt_insertion_point = _events.end();
+ _lookup_cache.range.first = _events.end();
+ _search_cache.range.first = _events.end();
+ _sort_pending = false;
+
+ set_state (node);
+
+ if (id)
+ _parameter = id;
+
+ assert(_parameter.type() != NullAutomation);
+ AutomationListCreated(this);
+}
+
+AutomationList::~AutomationList()
+{
+ GoingAway ();
+
+ for (EventList::iterator x = _events.begin(); x != _events.end(); ++x) {
+ delete (*x);
+ }
+}
+
+bool
+AutomationList::operator== (const AutomationList& other)
+{
+ return _events == other._events;
+}
+
+AutomationList&
+AutomationList::operator= (const AutomationList& other)
+{
+ if (this != &other) {
+
+ _events.clear ();
+
+ for (const_iterator i = other._events.begin(); i != other._events.end(); ++i) {
+ _events.push_back (new ControlEvent (**i));
+ }
+
+ _min_yval = other._min_yval;
+ _max_yval = other._max_yval;
+ _max_xval = other._max_xval;
+ _default_value = other._default_value;
+
+ mark_dirty ();
+ maybe_signal_changed ();
+ }
+
+ return *this;
+}
+
+void
+AutomationList::maybe_signal_changed ()
+{
+ mark_dirty ();
+
+ if (_frozen) {
+ _changed_when_thawed = true;
+ } else {
+ StateChanged ();
+ }
+}
+
+void
+AutomationList::set_automation_state (AutoState s)
+{
+ if (s != _state) {
+ _state = s;
+ automation_state_changed (); /* EMIT SIGNAL */
+ }
+}
+
+void
+AutomationList::set_automation_style (AutoStyle s)
+{
+ if (s != _style) {
+ _style = s;
+ automation_style_changed (); /* EMIT SIGNAL */
+ }
+}
+
+void
+AutomationList::start_touch ()
+{
+ _touching = true;
+ _new_touch = true;
+}
+
+void
+AutomationList::stop_touch ()
+{
+ _touching = false;
+ _new_touch = false;
+}
+
+void
+AutomationList::clear ()
+{
+ {
+ Glib::Mutex::Lock lm (_lock);
+ _events.clear ();
+ mark_dirty ();
+ }
+
+ maybe_signal_changed ();
+}
+
+void
+AutomationList::x_scale (double factor)
+{
+ Glib::Mutex::Lock lm (_lock);
+ _x_scale (factor);
+}
+
+bool
+AutomationList::extend_to (double when)
+{
+ Glib::Mutex::Lock lm (_lock);
+ if (_events.empty() || _events.back()->when == when) {
+ return false;
+ }
+ double factor = when / _events.back()->when;
+ _x_scale (factor);
+ return true;
+}
+
+void AutomationList::_x_scale (double factor)
+{
+ for (iterator i = _events.begin(); i != _events.end(); ++i) {
+ (*i)->when = floor ((*i)->when * factor);
+ }
+
+ mark_dirty ();
+}
+
+void
+AutomationList::reposition_for_rt_add (double when)
+{
+ _rt_insertion_point = _events.end();
+}
+
+void
+AutomationList::rt_add (double when, double value)
+{
+ /* this is for automation recording */
+
+ if ((_state & Touch) && !_touching) {
+ return;
+ }
+
+ // cerr << "RT: alist @ " << this << " add " << value << " @ " << when << endl;
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ iterator where;
+ ControlEvent cp (when, 0.0);
+ bool done = false;
+
+ if ((_rt_insertion_point != _events.end()) && ((*_rt_insertion_point)->when < when) ) {
+
+ /* we have a previous insertion point, so we should delete
+ everything between it and the position where we are going
+ to insert this point.
+ */
+
+ iterator after = _rt_insertion_point;
+
+ if (++after != _events.end()) {
+ iterator far = after;
+
+ while (far != _events.end()) {
+ if ((*far)->when > when) {
+ break;
+ }
+ ++far;
+ }
+
+ if (_new_touch) {
+ where = far;
+ _rt_insertion_point = where;
+
+ if ((*where)->when == when) {
+ (*where)->value = value;
+ done = true;
+ }
+ } else {
+ where = _events.erase (after, far);
+ }
+
+ } else {
+
+ where = after;
+
+ }
+
+ iterator previous = _rt_insertion_point;
+ --previous;
+
+ if (_rt_insertion_point != _events.begin() && (*_rt_insertion_point)->value == value && (*previous)->value == value) {
+ (*_rt_insertion_point)->when = when;
+ done = true;
+
+ }
+
+ } else {
+
+ where = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
+
+ if (where != _events.end()) {
+ if ((*where)->when == when) {
+ (*where)->value = value;
+ done = true;
+ }
+ }
+ }
+
+ if (!done) {
+ _rt_insertion_point = _events.insert (where, new ControlEvent (when, value));
+ }
+
+ _new_touch = false;
+ mark_dirty ();
+ }
+
+ maybe_signal_changed ();
+}
+
+void
+AutomationList::fast_simple_add (double when, double value)
+{
+ /* to be used only for loading pre-sorted data from saved state */
+ _events.insert (_events.end(), new ControlEvent (when, value));
+ assert(_events.back());
+}
+
+void
+AutomationList::add (double when, double value)
+{
+ /* this is for graphical editing */
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+ ControlEvent cp (when, 0.0f);
+ bool insert = true;
+ iterator insertion_point;
+
+ for (insertion_point = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); insertion_point != _events.end(); ++insertion_point) {
+
+ /* only one point allowed per time point */
+
+ if ((*insertion_point)->when == when) {
+ (*insertion_point)->value = value;
+ insert = false;
+ break;
+ }
+
+ if ((*insertion_point)->when >= when) {
+ break;
+ }
+ }
+
+ if (insert) {
+
+ _events.insert (insertion_point, new ControlEvent (when, value));
+ reposition_for_rt_add (0);
+
+ }
+
+ mark_dirty ();
+ }
+
+ maybe_signal_changed ();
+}
+
+void
+AutomationList::erase (iterator i)
+{
+ {
+ Glib::Mutex::Lock lm (_lock);
+ _events.erase (i);
+ reposition_for_rt_add (0);
+ mark_dirty ();
+ }
+ maybe_signal_changed ();
+}
+
+void
+AutomationList::erase (iterator start, iterator end)
+{
+ {
+ Glib::Mutex::Lock lm (_lock);
+ _events.erase (start, end);
+ reposition_for_rt_add (0);
+ mark_dirty ();
+ }
+ maybe_signal_changed ();
+}
+
+void
+AutomationList::reset_range (double start, double endt)
+{
+ bool reset = false;
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+ ControlEvent cp (start, 0.0f);
+ iterator s;
+ iterator e;
+
+ if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) {
+
+ cp.when = endt;
+ e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
+
+ for (iterator i = s; i != e; ++i) {
+ (*i)->value = _default_value;
+ }
+
+ reset = true;
+
+ mark_dirty ();
+ }
+ }
+
+ if (reset) {
+ maybe_signal_changed ();
+ }
+}
+
+void
+AutomationList::erase_range (double start, double endt)
+{
+ bool erased = false;
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+ ControlEvent cp (start, 0.0f);
+ iterator s;
+ iterator e;
+
+ if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) != _events.end()) {
+ cp.when = endt;
+ e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
+ _events.erase (s, e);
+ reposition_for_rt_add (0);
+ erased = true;
+ mark_dirty ();
+ }
+
+ }
+
+ if (erased) {
+ maybe_signal_changed ();
+ }
+}
+
+void
+AutomationList::move_range (iterator start, iterator end, double xdelta, double ydelta)
+{
+ /* note: we assume higher level logic is in place to avoid this
+ reordering the time-order of control events in the list. ie. all
+ points after end are later than (end)->when.
+ */
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ while (start != end) {
+ (*start)->when += xdelta;
+ (*start)->value += ydelta;
+ if (isnan ((*start)->value)) {
+ abort ();
+ }
+ ++start;
+ }
+
+ if (!_frozen) {
+ _events.sort (sort_events_by_time);
+ } else {
+ _sort_pending = true;
+ }
+
+ mark_dirty ();
+ }
+
+ maybe_signal_changed ();
+}
+
+void
+AutomationList::slide (iterator before, double distance)
+{
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ if (before == _events.end()) {
+ return;
+ }
+
+ while (before != _events.end()) {
+ (*before)->when += distance;
+ ++before;
+ }
+ }
+
+ maybe_signal_changed ();
+}
+
+void
+AutomationList::modify (iterator iter, double when, double val)
+{
+ /* note: we assume higher level logic is in place to avoid this
+ reordering the time-order of control events in the list. ie. all
+ points after *iter are later than when.
+ */
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ (*iter)->when = when;
+ (*iter)->value = val;
+
+ if (isnan (val)) {
+ abort ();
+ }
+
+ if (!_frozen) {
+ _events.sort (sort_events_by_time);
+ } else {
+ _sort_pending = true;
+ }
+
+ mark_dirty ();
+ }
+
+ maybe_signal_changed ();
+}
+
+std::pair<AutomationList::iterator,AutomationList::iterator>
+AutomationList::control_points_adjacent (double xval)
+{
+ Glib::Mutex::Lock lm (_lock);
+ iterator i;
+ ControlEvent cp (xval, 0.0f);
+ std::pair<iterator,iterator> ret;
+
+ ret.first = _events.end();
+ ret.second = _events.end();
+
+ for (i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator); i != _events.end(); ++i) {
+
+ if (ret.first == _events.end()) {
+ if ((*i)->when >= xval) {
+ if (i != _events.begin()) {
+ ret.first = i;
+ --ret.first;
+ } else {
+ return ret;
+ }
+ }
+ }
+
+ if ((*i)->when > xval) {
+ ret.second = i;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void
+AutomationList::freeze ()
+{
+ _frozen++;
+}
+
+void
+AutomationList::thaw ()
+{
+ if (_frozen == 0) {
+ PBD::stacktrace (cerr);
+ fatal << string_compose (_("programming error: %1"), X_("AutomationList::thaw() called while not frozen")) << endmsg;
+ /*NOTREACHED*/
+ }
+
+ if (--_frozen > 0) {
+ return;
+ }
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ if (_sort_pending) {
+ _events.sort (sort_events_by_time);
+ _sort_pending = false;
+ }
+ }
+
+ if (_changed_when_thawed) {
+ StateChanged(); /* EMIT SIGNAL */
+ }
+}
+
+void
+AutomationList::set_max_xval (double x)
+{
+ _max_xval = x;
+}
+
+void
+AutomationList::mark_dirty ()
+{
+ _lookup_cache.left = -1;
+ _search_cache.left = -1;
+ Dirty (); /* EMIT SIGNAL */
+}
+
+void
+AutomationList::truncate_end (double last_coordinate)
+{
+ {
+ Glib::Mutex::Lock lm (_lock);
+ ControlEvent cp (last_coordinate, 0);
+ AutomationList::reverse_iterator i;
+ double last_val;
+
+ if (_events.empty()) {
+ return;
+ }
+
+ if (last_coordinate == _events.back()->when) {
+ return;
+ }
+
+ if (last_coordinate > _events.back()->when) {
+
+ /* extending end:
+ */
+
+ iterator foo = _events.begin();
+ bool lessthantwo;
+
+ if (foo == _events.end()) {
+ lessthantwo = true;
+ } else if (++foo == _events.end()) {
+ lessthantwo = true;
+ } else {
+ lessthantwo = false;
+ }
+
+ if (lessthantwo) {
+ /* less than 2 points: add a new point */
+ _events.push_back (new ControlEvent (last_coordinate, _events.back()->value));
+ } else {
+
+ /* more than 2 points: check to see if the last 2 values
+ are equal. if so, just move the position of the
+ last point. otherwise, add a new point.
+ */
+
+ iterator penultimate = _events.end();
+ --penultimate; /* points at last point */
+ --penultimate; /* points at the penultimate point */
+
+ if (_events.back()->value == (*penultimate)->value) {
+ _events.back()->when = last_coordinate;
+ } else {
+ _events.push_back (new ControlEvent (last_coordinate, _events.back()->value));
+ }
+ }
+
+ } else {
+
+ /* shortening end */
+
+ last_val = unlocked_eval (last_coordinate);
+ last_val = max ((double) _min_yval, last_val);
+ last_val = min ((double) _max_yval, last_val);
+
+ i = _events.rbegin();
+
+ /* make i point to the last control point */
+
+ ++i;
+
+ /* now go backwards, removing control points that are
+ beyond the new last coordinate.
+ */
+
+ uint32_t sz = _events.size();
+
+ while (i != _events.rend() && sz > 2) {
+ AutomationList::reverse_iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ if ((*i)->when < last_coordinate) {
+ break;
+ }
+
+ _events.erase (i.base());
+ --sz;
+
+ i = tmp;
+ }
+
+ _events.back()->when = last_coordinate;
+ _events.back()->value = last_val;
+ }
+
+ reposition_for_rt_add (0);
+ mark_dirty();
+ }
+
+ maybe_signal_changed ();
+}
+
+void
+AutomationList::truncate_start (double overall_length)
+{
+ {
+ Glib::Mutex::Lock lm (_lock);
+ iterator i;
+ double first_legal_value;
+ double first_legal_coordinate;
+
+ if (_events.empty()) {
+ fatal << _("programming error:")
+ << "AutomationList::truncate_start() called on an empty list"
+ << endmsg;
+ /*NOTREACHED*/
+ return;
+ }
+
+ if (overall_length == _events.back()->when) {
+ /* no change in overall length */
+ return;
+ }
+
+ if (overall_length > _events.back()->when) {
+
+ /* growing at front: duplicate first point. shift all others */
+
+ double shift = overall_length - _events.back()->when;
+ uint32_t np;
+
+ for (np = 0, i = _events.begin(); i != _events.end(); ++i, ++np) {
+ (*i)->when += shift;
+ }
+
+ if (np < 2) {
+
+ /* less than 2 points: add a new point */
+ _events.push_front (new ControlEvent (0, _events.front()->value));
+
+ } else {
+
+ /* more than 2 points: check to see if the first 2 values
+ are equal. if so, just move the position of the
+ first point. otherwise, add a new point.
+ */
+
+ iterator second = _events.begin();
+ ++second; /* points at the second point */
+
+ if (_events.front()->value == (*second)->value) {
+ /* first segment is flat, just move start point back to zero */
+ _events.front()->when = 0;
+ } else {
+ /* leave non-flat segment in place, add a new leading point. */
+ _events.push_front (new ControlEvent (0, _events.front()->value));
+ }
+ }
+
+ } else {
+
+ /* shrinking at front */
+
+ first_legal_coordinate = _events.back()->when - overall_length;
+ first_legal_value = unlocked_eval (first_legal_coordinate);
+ first_legal_value = max (_min_yval, first_legal_value);
+ first_legal_value = min (_max_yval, first_legal_value);
+
+ /* remove all events earlier than the new "front" */
+
+ i = _events.begin();
+
+ while (i != _events.end() && !_events.empty()) {
+ AutomationList::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ if ((*i)->when > first_legal_coordinate) {
+ break;
+ }
+
+ _events.erase (i);
+
+ i = tmp;
+ }
+
+
+ /* shift all remaining points left to keep their same
+ relative position
+ */
+
+ for (i = _events.begin(); i != _events.end(); ++i) {
+ (*i)->when -= first_legal_coordinate;
+ }
+
+ /* add a new point for the interpolated new value */
+
+ _events.push_front (new ControlEvent (0, first_legal_value));
+ }
+
+ reposition_for_rt_add (0);
+
+ mark_dirty();
+ }
+
+ maybe_signal_changed ();
+}
+
+double
+AutomationList::unlocked_eval (double x) const
+{
+ pair<EventList::iterator,EventList::iterator> range;
+ int32_t npoints;
+ double lpos, upos;
+ double lval, uval;
+ double fraction;
+
+ npoints = _events.size();
+
+ switch (npoints) {
+ case 0:
+ return _default_value;
+
+ case 1:
+ if (x >= _events.front()->when) {
+ return _events.front()->value;
+ } else {
+ // return _default_value;
+ return _events.front()->value;
+ }
+
+ case 2:
+ if (x >= _events.back()->when) {
+ return _events.back()->value;
+ } else if (x == _events.front()->when) {
+ return _events.front()->value;
+ } else if (x < _events.front()->when) {
+ // return _default_value;
+ return _events.front()->value;
+ }
+
+ lpos = _events.front()->when;
+ lval = _events.front()->value;
+ upos = _events.back()->when;
+ uval = _events.back()->value;
+
+ if (_interpolation == Discrete)
+ return lval;
+
+ /* linear interpolation betweeen the two points
+ */
+
+ fraction = (double) (x - lpos) / (double) (upos - lpos);
+ return lval + (fraction * (uval - lval));
+
+ default:
+
+ if (x >= _events.back()->when) {
+ return _events.back()->value;
+ } else if (x == _events.front()->when) {
+ return _events.front()->value;
+ } else if (x < _events.front()->when) {
+ // return _default_value;
+ return _events.front()->value;
+ }
+
+ return multipoint_eval (x);
+ break;
+ }
+
+ /*NOTREACHED*/ /* stupid gcc */
+ return 0.0;
+}
+
+double
+AutomationList::multipoint_eval (double x) const
+{
+ double upos, lpos;
+ double uval, lval;
+ double fraction;
+
+ /* "Stepped" lookup (no interpolation) */
+ /* FIXME: no cache. significant? */
+ if (_interpolation == Discrete) {
+ const ControlEvent cp (x, 0);
+ EventList::const_iterator i = lower_bound (_events.begin(), _events.end(), &cp, time_comparator);
+
+ // shouldn't have made it to multipoint_eval
+ assert(i != _events.end());
+
+ if (i == _events.begin() || (*i)->when == x)
+ return (*i)->value;
+ else
+ return (*(--i))->value;
+ }
+
+ /* Only do the range lookup if x is in a different range than last time
+ * this was called (or if the lookup cache has been marked "dirty" (left<0) */
+ if ((_lookup_cache.left < 0) ||
+ ((_lookup_cache.left > x) ||
+ (_lookup_cache.range.first == _events.end()) ||
+ ((*_lookup_cache.range.second)->when < x))) {
+
+ const ControlEvent cp (x, 0);
+
+ _lookup_cache.range = equal_range (_events.begin(), _events.end(), &cp, time_comparator);
+ }
+
+ pair<const_iterator,const_iterator> range = _lookup_cache.range;
+
+ if (range.first == range.second) {
+
+ /* x does not exist within the list as a control point */
+
+ _lookup_cache.left = x;
+
+ if (range.first != _events.begin()) {
+ --range.first;
+ lpos = (*range.first)->when;
+ lval = (*range.first)->value;
+ } else {
+ /* we're before the first point */
+ // return _default_value;
+ return _events.front()->value;
+ }
+
+ if (range.second == _events.end()) {
+ /* we're after the last point */
+ return _events.back()->value;
+ }
+
+ upos = (*range.second)->when;
+ uval = (*range.second)->value;
+
+ /* linear interpolation betweeen the two points
+ on either side of x
+ */
+
+ fraction = (double) (x - lpos) / (double) (upos - lpos);
+ return lval + (fraction * (uval - lval));
+
+ }
+
+ /* x is a control point in the data */
+ _lookup_cache.left = -1;
+ return (*range.first)->value;
+}
+
+void
+AutomationList::build_search_cache_if_necessary(double start, double end) const
+{
+ /* Only do the range lookup if x is in a different range than last time
+ * this was called (or if the search cache has been marked "dirty" (left<0) */
+ if (!_events.empty() && ((_search_cache.left < 0) ||
+ ((_search_cache.left > start) ||
+ (_search_cache.right < end)))) {
+
+ const ControlEvent start_point (start, 0);
+ const ControlEvent end_point (end, 0);
+
+ //cerr << "REBUILD: (" << _search_cache.left << ".." << _search_cache.right << ") := ("
+ // << start << ".." << end << ")" << endl;
+
+ _search_cache.range.first = lower_bound (_events.begin(), _events.end(), &start_point, time_comparator);
+ _search_cache.range.second = upper_bound (_events.begin(), _events.end(), &end_point, time_comparator);
+
+ _search_cache.left = start;
+ _search_cache.right = end;
+ }
+}
+
+/** Get the earliest event between \a start and \a end, using the current interpolation style.
+ *
+ * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
+ * \return true if event is found (and \a x and \a y are valid).
+ */
+bool
+AutomationList::rt_safe_earliest_event(double start, double end, double& x, double& y, bool inclusive) const
+{
+ // FIXME: It would be nice if this was unnecessary..
+ Glib::Mutex::Lock lm(_lock, Glib::TRY_LOCK);
+ if (!lm.locked()) {
+ return false;
+ }
+
+ return rt_safe_earliest_event_unlocked(start, end, x, y, inclusive);
+}
+
+
+/** Get the earliest event between \a start and \a end, using the current interpolation style.
+ *
+ * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
+ * \return true if event is found (and \a x and \a y are valid).
+ */
+bool
+AutomationList::rt_safe_earliest_event_unlocked(double start, double end, double& x, double& y, bool inclusive) const
+{
+ if (_interpolation == Discrete)
+ return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
+ else
+ return rt_safe_earliest_event_linear_unlocked(start, end, x, y, inclusive);
+}
+
+
+/** Get the earliest event between \a start and \a end (Discrete (lack of) interpolation)
+ *
+ * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
+ * \return true if event is found (and \a x and \a y are valid).
+ */
+bool
+AutomationList::rt_safe_earliest_event_discrete_unlocked (double start, double end, double& x, double& y, bool inclusive) const
+{
+ build_search_cache_if_necessary(start, end);
+
+ const pair<const_iterator,const_iterator>& range = _search_cache.range;
+
+ if (range.first != _events.end()) {
+ const ControlEvent* const first = *range.first;
+
+ const bool past_start = (inclusive ? first->when >= start : first->when > start);
+
+ /* Earliest points is in range, return it */
+ if (past_start >= start && first->when < end) {
+
+ x = first->when;
+ y = first->value;
+
+ /* Move left of cache to this point
+ * (Optimize for immediate call this cycle within range) */
+ _search_cache.left = x;
+ ++_search_cache.range.first;
+
+ assert(x >= start);
+ assert(x < end);
+ return true;
+
+ } else {
+ return false;
+ }
+
+ /* No points in range */
+ } else {
+ return false;
+ }
+}
+
+/** Get the earliest time the line crosses an integer (Linear interpolation).
+ *
+ * If an event is found, \a x and \a y are set to its coordinates.
+ *
+ * \param inclusive Include events with timestamp exactly equal to \a start
+ * \return true if event is found (and \a x and \a y are valid).
+ */
+bool
+AutomationList::rt_safe_earliest_event_linear_unlocked (double start, double end, double& x, double& y, bool inclusive) const
+{
+ //cerr << "earliest_event(" << start << ", " << end << ", " << x << ", " << y << ", " << inclusive << endl;
+
+ if (_events.size() == 0)
+ return false;
+ else if (_events.size() == 1)
+ return rt_safe_earliest_event_discrete_unlocked(start, end, x, y, inclusive);
+
+ // Hack to avoid infinitely repeating the same event
+ build_search_cache_if_necessary(start, end);
+
+ pair<const_iterator,const_iterator> range = _search_cache.range;
+
+ if (range.first != _events.end()) {
+
+ const ControlEvent* first = NULL;
+ const ControlEvent* next = NULL;
+
+ /* Step is after first */
+ if (range.first == _events.begin() || (*range.first)->when == start) {
+ first = *range.first;
+ next = *(++range.first);
+ ++_search_cache.range.first;
+
+ /* Step is before first */
+ } else {
+ const_iterator prev = range.first;
+ --prev;
+ first = *prev;
+ next = *range.first;
+ }
+
+ if (inclusive && first->when == start) {
+ x = first->when;
+ y = first->value;
+ /* Move left of cache to this point
+ * (Optimize for immediate call this cycle within range) */
+ _search_cache.left = x;
+ //++_search_cache.range.first;
+ return true;
+ }
+
+ if (abs(first->value - next->value) <= 1) {
+ if (next->when <= end && (!inclusive || next->when > start)) {
+ x = next->when;
+ y = next->value;
+ /* Move left of cache to this point
+ * (Optimize for immediate call this cycle within range) */
+ _search_cache.left = x;
+ //++_search_cache.range.first;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ const double slope = (next->value - first->value) / (double)(next->when - first->when);
+ //cerr << "start y: " << start_y << endl;
+
+ //y = first->value + (slope * fabs(start - first->when));
+ y = first->value;
+
+ if (first->value < next->value) // ramping up
+ y = ceil(y);
+ else // ramping down
+ y = floor(y);
+
+ x = first->when + (y - first->value) / (double)slope;
+
+ while ((inclusive && x < start) || (x <= start && y != next->value)) {
+
+ if (first->value < next->value) // ramping up
+ y += 1.0;
+ else // ramping down
+ y -= 1.0;
+
+ x = first->when + (y - first->value) / (double)slope;
+ }
+
+ /*cerr << first->value << " @ " << first->when << " ... "
+ << next->value << " @ " << next->when
+ << " = " << y << " @ " << x << endl;*/
+
+ assert( (y >= first->value && y <= next->value)
+ || (y <= first->value && y >= next->value) );
+
+
+ const bool past_start = (inclusive ? x >= start : x > start);
+ if (past_start && x < end) {
+ /* Move left of cache to this point
+ * (Optimize for immediate call this cycle within range) */
+ _search_cache.left = x;
+
+ return true;
+
+ } else {
+ return false;
+ }
+
+ /* No points in the future, so no steps (towards them) in the future */
+ } else {
+ return false;
+ }
+}
+
+AutomationList*
+AutomationList::cut (iterator start, iterator end)
+{
+ AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value);
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ for (iterator x = start; x != end; ) {
+ iterator tmp;
+
+ tmp = x;
+ ++tmp;
+
+ nal->_events.push_back (new ControlEvent (**x));
+ _events.erase (x);
+
+ reposition_for_rt_add (0);
+
+ x = tmp;
+ }
+
+ mark_dirty ();
+ }
+
+ maybe_signal_changed ();
+
+ return nal;
+}
+
+AutomationList*
+AutomationList::cut_copy_clear (double start, double end, int op)
+{
+ AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value);
+ iterator s, e;
+ ControlEvent cp (start, 0.0);
+ bool changed = false;
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ if ((s = lower_bound (_events.begin(), _events.end(), &cp, time_comparator)) == _events.end()) {
+ return nal;
+ }
+
+ cp.when = end;
+ e = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
+
+ if (op != 2 && (*s)->when != start) {
+ nal->_events.push_back (new ControlEvent (0, unlocked_eval (start)));
+ }
+
+ for (iterator x = s; x != e; ) {
+ iterator tmp;
+
+ tmp = x;
+ ++tmp;
+
+ changed = true;
+
+ /* adjust new points to be relative to start, which
+ has been set to zero.
+ */
+
+ if (op != 2) {
+ nal->_events.push_back (new ControlEvent ((*x)->when - start, (*x)->value));
+ }
+
+ if (op != 1) {
+ _events.erase (x);
+ }
+
+ x = tmp;
+ }
+
+ if (op != 2 && nal->_events.back()->when != end - start) {
+ nal->_events.push_back (new ControlEvent (end - start, unlocked_eval (end)));
+ }
+
+ if (changed) {
+ reposition_for_rt_add (0);
+ }
+
+ mark_dirty ();
+ }
+
+ maybe_signal_changed ();
+
+ return nal;
+
+}
+
+AutomationList*
+AutomationList::copy (iterator start, iterator end)
+{
+ AutomationList* nal = new AutomationList (_parameter, _min_yval, _max_yval, _default_value);
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ for (iterator x = start; x != end; ) {
+ iterator tmp;
+
+ tmp = x;
+ ++tmp;
+
+ nal->_events.push_back (new ControlEvent (**x));
+
+ x = tmp;
+ }
+ }
+
+ return nal;
+}
+
+AutomationList*
+AutomationList::cut (double start, double end)
+{
+ return cut_copy_clear (start, end, 0);
+}
+
+AutomationList*
+AutomationList::copy (double start, double end)
+{
+ return cut_copy_clear (start, end, 1);
+}
+
+void
+AutomationList::clear (double start, double end)
+{
+ (void) cut_copy_clear (start, end, 2);
+}
+
+bool
+AutomationList::paste (AutomationList& alist, double pos, float times)
+{
+ if (alist._events.empty()) {
+ return false;
+ }
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+ iterator where;
+ iterator prev;
+ double end = 0;
+ ControlEvent cp (pos, 0.0);
+
+ where = upper_bound (_events.begin(), _events.end(), &cp, time_comparator);
+
+ for (iterator i = alist.begin();i != alist.end(); ++i) {
+ _events.insert (where, new ControlEvent( (*i)->when+pos,( *i)->value));
+ end = (*i)->when + pos;
+ }
+
+
+ /* move all points after the insertion along the timeline by
+ the correct amount.
+ */
+
+ while (where != _events.end()) {
+ iterator tmp;
+ if ((*where)->when <= end) {
+ tmp = where;
+ ++tmp;
+ _events.erase(where);
+ where = tmp;
+
+ } else {
+ break;
+ }
+ }
+
+ reposition_for_rt_add (0);
+ mark_dirty ();
+ }
+
+ maybe_signal_changed ();
+ return true;
+}
+
+XMLNode&
+AutomationList::get_state ()
+{
+ return state (true);
+}
+
+XMLNode&
+AutomationList::state (bool full)
+{
+ XMLNode* root = new XMLNode (X_("AutomationList"));
+ char buf[64];
+ LocaleGuard lg (X_("POSIX"));
+
+ root->add_property ("automation-id", _parameter.to_string());
+
+ root->add_property ("id", _id.to_s());
+
+ snprintf (buf, sizeof (buf), "%.12g", _default_value);
+ root->add_property ("default", buf);
+ snprintf (buf, sizeof (buf), "%.12g", _min_yval);
+ root->add_property ("min_yval", buf);
+ snprintf (buf, sizeof (buf), "%.12g", _max_yval);
+ root->add_property ("max_yval", buf);
+ snprintf (buf, sizeof (buf), "%.12g", _max_xval);
+ root->add_property ("max_xval", buf);
+
+ root->add_property ("interpolation-style", enum_2_string (_interpolation));
+
+ if (full) {
+ root->add_property ("state", auto_state_to_string (_state));
+ } else {
+ /* never save anything but Off for automation state to a template */
+ root->add_property ("state", auto_state_to_string (Off));
+ }
+
+ root->add_property ("style", auto_style_to_string (_style));
+
+ if (!_events.empty()) {
+ root->add_child_nocopy (serialize_events());
+ }
+
+ return *root;
+}
+
+XMLNode&
+AutomationList::serialize_events ()
+{
+ XMLNode* node = new XMLNode (X_("events"));
+ stringstream str;
+
+ for (iterator xx = _events.begin(); xx != _events.end(); ++xx) {
+ str << (double) (*xx)->when;
+ str << ' ';
+ str <<(double) (*xx)->value;
+ str << '\n';
+ }
+
+ /* XML is a bit wierd */
+
+ XMLNode* content_node = new XMLNode (X_("foo")); /* it gets renamed by libxml when we set content */
+ content_node->set_content (str.str());
+
+ node->add_child_nocopy (*content_node);
+
+ return *node;
+}
+
+int
+AutomationList::deserialize_events (const XMLNode& node)
+{
+ if (node.children().empty()) {
+ return -1;
+ }
+
+ XMLNode* content_node = node.children().front();
+
+ if (content_node->content().empty()) {
+ return -1;
+ }
+
+ freeze ();
+ clear ();
+
+ stringstream str (content_node->content());
+
+ double x;
+ double y;
+ bool ok = true;
+
+ while (str) {
+ str >> x;
+ if (!str) {
+ break;
+ }
+ str >> y;
+ if (!str) {
+ ok = false;
+ break;
+ }
+ fast_simple_add (x, y);
+ }
+
+ if (!ok) {
+ clear ();
+ error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
+ } else {
+ mark_dirty ();
+ reposition_for_rt_add (0);
+ maybe_signal_changed ();
+ }
+
+ thaw ();
+
+ return 0;
+}
+
+int
+AutomationList::set_state (const XMLNode& node)
+{
+ XMLNodeList nlist = node.children();
+ XMLNode* nsos;
+ XMLNodeIterator niter;
+ const XMLProperty* prop;
+
+ if (node.name() == X_("events")) {
+ /* partial state setting*/
+ return deserialize_events (node);
+ }
+
+ if (node.name() == X_("Envelope") || node.name() == X_("FadeOut") || node.name() == X_("FadeIn")) {
+
+ if ((nsos = node.child (X_("AutomationList")))) {
+ /* new school in old school clothing */
+ return set_state (*nsos);
+ }
+
+ /* old school */
+
+ const XMLNodeList& elist = node.children();
+ XMLNodeConstIterator i;
+ XMLProperty* prop;
+ nframes_t x;
+ double y;
+
+ freeze ();
+ clear ();
+
+ for (i = elist.begin(); i != elist.end(); ++i) {
+
+ if ((prop = (*i)->property ("x")) == 0) {
+ error << _("automation list: no x-coordinate stored for control point (point ignored)") << endmsg;
+ continue;
+ }
+ x = atoi (prop->value().c_str());
+
+ if ((prop = (*i)->property ("y")) == 0) {
+ error << _("automation list: no y-coordinate stored for control point (point ignored)") << endmsg;
+ continue;
+ }
+ y = atof (prop->value().c_str());
+
+ fast_simple_add (x, y);
+ }
+
+ thaw ();
+
+ return 0;
+ }
+
+ if (node.name() != X_("AutomationList") ) {
+ error << string_compose (_("AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"), node.name()) << endmsg;
+ return -1;
+ }
+
+ if ((prop = node.property ("id")) != 0) {
+ _id = prop->value ();
+ /* update session AL list */
+ AutomationListCreated(this);
+ }
+
+ if ((prop = node.property (X_("automation-id"))) != 0){
+ _parameter = Parameter(prop->value());
+ } else {
+ warning << "Legacy session: automation list has no automation-id property.";
+ }
+
+ if ((prop = node.property (X_("interpolation-style"))) != 0) {
+ _interpolation = (InterpolationStyle)string_2_enum(prop->value(), _interpolation);
+ } else {
+ _interpolation = Linear;
+ }
+
+ if ((prop = node.property (X_("default"))) != 0){
+ _default_value = atof (prop->value().c_str());
+ } else {
+ _default_value = 0.0;
+ }
+
+ if ((prop = node.property (X_("style"))) != 0) {
+ _style = string_to_auto_style (prop->value());
+ } else {
+ _style = Absolute;
+ }
+
+ if ((prop = node.property (X_("state"))) != 0) {
+ _state = string_to_auto_state (prop->value());
+ } else {
+ _state = Off;
+ }
+
+ if ((prop = node.property (X_("min_yval"))) != 0) {
+ _min_yval = atof (prop->value ().c_str());
+ } else {
+ _min_yval = FLT_MIN;
+ }
+
+ if ((prop = node.property (X_("max_yval"))) != 0) {
+ _max_yval = atof (prop->value ().c_str());
+ } else {
+ _max_yval = FLT_MAX;
+ }
+
+ if ((prop = node.property (X_("max_xval"))) != 0) {
+ _max_xval = atof (prop->value ().c_str());
+ } else {
+ _max_xval = 0; // means "no limit ;
+ }
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == X_("events")) {
+ deserialize_events (*(*niter));
+ }
+ }
+
+ return 0;
+}
+
diff --git a/libs/ardour/base_audio_port.cc b/libs/ardour/base_audio_port.cc
new file mode 100644
index 0000000000..ceec4c1d3a
--- /dev/null
+++ b/libs/ardour/base_audio_port.cc
@@ -0,0 +1,82 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cassert>
+#include <glib.h>
+#include <ardour/base_audio_port.h>
+#include <ardour/audioengine.h>
+#include <ardour/data_type.h>
+
+using namespace ARDOUR;
+using namespace std;
+
+nframes_t BaseAudioPort::_short_over_length = 2;
+nframes_t BaseAudioPort::_long_over_length = 10;
+
+BaseAudioPort::BaseAudioPort (const std::string& name, Flags flgs)
+ : Port (name, flgs)
+ , _buffer (0)
+ , _own_buffer (false)
+{
+ _type = DataType::AUDIO;
+ _mixdown = default_mixdown;
+}
+
+BaseAudioPort::~BaseAudioPort ()
+{
+ if (_own_buffer && _buffer) {
+ delete _buffer;
+ }
+}
+
+void
+BaseAudioPort::reset()
+{
+ Port::reset();
+
+ if (_own_buffer && _buffer) {
+ _buffer->resize (engine->frames_per_cycle());
+ _buffer->clear ();
+ }
+
+ _metering = 0;
+ reset_meters ();
+}
+
+void
+BaseAudioPort::default_mixdown (const set<Port*>& ports, AudioBuffer* dest, nframes_t cnt, nframes_t offset, bool first_overwrite)
+{
+ set<Port*>::const_iterator p = ports.begin();
+
+ if (first_overwrite) {
+ dest->read_from ((dynamic_cast<BaseAudioPort*>(*p))->get_audio_buffer(), cnt, offset);
+ p++;
+ }
+
+ for (; p != ports.end(); ++p) {
+ dest->accumulate_from ((dynamic_cast<BaseAudioPort*>(*p))->get_audio_buffer(), cnt, offset);
+ }
+}
+
+void
+BaseAudioPort::set_mixdown_function (void (*func)(const set<Port*>&, AudioBuffer*, nframes_t, nframes_t, bool))
+{
+ g_atomic_pointer_set(&_mixdown, func);
+}
+
+
diff --git a/libs/ardour/base_midi_port.cc b/libs/ardour/base_midi_port.cc
new file mode 100644
index 0000000000..49d748dd20
--- /dev/null
+++ b/libs/ardour/base_midi_port.cc
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cassert>
+#include <iostream>
+#include <glib.h>
+#include <ardour/base_midi_port.h>
+#include <ardour/data_type.h>
+
+using namespace ARDOUR;
+using namespace std;
+
+BaseMidiPort::BaseMidiPort (const std::string& name, Flags flags)
+ : Port (name, flags)
+ , _buffer (0)
+ , _own_buffer (false)
+{
+ _type = DataType::MIDI;
+ _mixdown = default_mixdown;
+}
+
+BaseMidiPort::~BaseMidiPort()
+{
+ if (_own_buffer && _buffer) {
+ delete _buffer;
+ }
+}
+
+void
+BaseMidiPort::default_mixdown (const set<Port*>& ports, MidiBuffer* dest, nframes_t cnt, nframes_t offset, bool first_overwrite)
+{
+ set<Port*>::const_iterator p = ports.begin();
+
+ if (first_overwrite) {
+ cout << "first overwrite" << endl;
+ dest->read_from ((dynamic_cast<BaseMidiPort*>(*p))->get_midi_buffer(), cnt, offset);
+ p++;
+ }
+
+ // XXX DAVE: this is just a guess
+
+ for (; p != ports.end(); ++p) {
+ cout << "merge" << endl;
+ dest->merge (*dest, (dynamic_cast<BaseMidiPort*>(*p))->get_midi_buffer());
+ }
+}
+
+void
+BaseMidiPort::set_mixdown_function (void (*func)(const set<Port*>&, MidiBuffer*, nframes_t, nframes_t, bool))
+{
+ g_atomic_pointer_set(&_mixdown, func);
+}
diff --git a/libs/ardour/buffer.cc b/libs/ardour/buffer.cc
new file mode 100644
index 0000000000..8abe238a47
--- /dev/null
+++ b/libs/ardour/buffer.cc
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <ardour/buffer.h>
+#include <ardour/audio_buffer.h>
+#include <ardour/midi_buffer.h>
+
+#ifdef __x86_64__
+static const int CPU_CACHE_ALIGN = 64;
+#else
+static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
+#endif
+
+namespace ARDOUR {
+
+
+Buffer*
+Buffer::create(DataType type, size_t capacity)
+{
+ if (type == DataType::AUDIO)
+ return new AudioBuffer(capacity);
+ else if (type == DataType::MIDI)
+ return new MidiBuffer(capacity);
+ else
+ return NULL;
+}
+
+
+} // namespace ARDOUR
+
diff --git a/libs/ardour/buffer_set.cc b/libs/ardour/buffer_set.cc
new file mode 100644
index 0000000000..65e9f8ac8f
--- /dev/null
+++ b/libs/ardour/buffer_set.cc
@@ -0,0 +1,177 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <algorithm>
+#include <ardour/buffer_set.h>
+#include <ardour/buffer.h>
+#include <ardour/port.h>
+#include <ardour/port_set.h>
+
+namespace ARDOUR {
+
+/** Create a new, empty BufferSet */
+BufferSet::BufferSet()
+ : _is_mirror(false)
+{
+ for (size_t i=0; i < DataType::num_types; ++i)
+ _buffers.push_back( BufferVec() );
+
+ _count.reset();
+ _available.reset();
+}
+
+BufferSet::~BufferSet()
+{
+ clear();
+}
+
+/** Destroy all contained buffers.
+ */
+void
+BufferSet::clear()
+{
+ if (!_is_mirror) {
+ for (std::vector<BufferVec>::iterator i = _buffers.begin(); i != _buffers.end(); ++i) {
+ for (BufferVec::iterator j = (*i).begin(); j != (*i).end(); ++j) {
+ delete *j;
+ }
+ (*i).clear();
+ }
+ }
+ _buffers.clear();
+ _count.reset();
+ _available.reset();
+}
+
+/** Make this BufferSet a direct mirror of a PortSet's buffers.
+ */
+void
+BufferSet::attach_buffers(PortSet& ports)
+{
+ clear();
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ _buffers.push_back(BufferVec());
+ BufferVec& v = _buffers[*t];
+
+ for (PortSet::iterator p = ports.begin(*t); p != ports.end(*t); ++p) {
+ assert(p->type() == *t);
+ v.push_back(&(p->get_buffer()));
+ }
+
+ }
+
+ _count = ports.count();
+
+ _is_mirror = true;
+}
+
+void
+BufferSet::ensure_buffers(const ChanCount& count, size_t buffer_capacity)
+{
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ ensure_buffers(*t, count.get(*t), buffer_capacity);
+ }
+}
+
+
+/** Ensure that there are @a num_buffers buffers of type @a type available,
+ * each of size at least @a buffer_size
+ */
+void
+BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capacity)
+{
+ assert(type != DataType::NIL);
+ assert(type < _buffers.size());
+ assert(buffer_capacity > 0);
+
+ if (num_buffers == 0)
+ return;
+
+ // FIXME: Kludge to make MIDI buffers larger (size is bytes, not frames)
+ // See MidiPort::MidiPort
+ // We probably need a map<DataType, size_t> parameter for capacity
+ if (type == DataType::MIDI)
+ buffer_capacity *= 8;
+
+ // The vector of buffers of the type we care about
+ BufferVec& bufs = _buffers[type];
+
+ // If we're a mirror just make sure we're ok
+ if (_is_mirror) {
+ assert(_count.get(type) >= num_buffers);
+ assert(bufs[0]->type() == type);
+ return;
+ }
+
+ // If there's not enough or they're too small, just nuke the whole thing and
+ // rebuild it (so I'm lazy..)
+ if (bufs.size() < num_buffers
+ || (bufs.size() > 0 && bufs[0]->capacity() < buffer_capacity)) {
+
+ // Nuke it
+ for (BufferVec::iterator i = bufs.begin(); i != bufs.end(); ++i) {
+ delete (*i);
+ }
+ bufs.clear();
+
+ // Rebuild it
+ for (size_t i=0; i < num_buffers; ++i) {
+ bufs.push_back(Buffer::create(type, buffer_capacity));
+ }
+
+ _available.set(type, num_buffers);
+ }
+
+ // Post-conditions
+ assert(bufs[0]->type() == type);
+ assert(bufs.size() >= num_buffers);
+ assert(bufs.size() == _available.get(type));
+ assert(bufs[0]->capacity() >= buffer_capacity);
+}
+
+/** Get the capacity (size) of the available buffers of the given type.
+ *
+ * All buffers of a certain type always have the same capacity.
+ */
+size_t
+BufferSet::buffer_capacity(DataType type) const
+{
+ assert(_available.get(type) > 0);
+ return _buffers[type][0]->capacity();
+}
+
+// FIXME: make 'in' const
+void
+BufferSet::read_from(BufferSet& in, nframes_t nframes, nframes_t offset)
+{
+ assert(available() >= in.count());
+
+ // Copy all buffers 1:1
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ BufferSet::iterator o = begin(*t);
+ for (BufferSet::iterator i = in.begin(*t); i != in.end(*t); ++i, ++o) {
+ o->read_from(*i, nframes, offset);
+ }
+ }
+
+ set_count(in.count());
+}
+
+} // namespace ARDOUR
+
diff --git a/libs/ardour/bundle.cc b/libs/ardour/bundle.cc
new file mode 100644
index 0000000000..0d8c36a84f
--- /dev/null
+++ b/libs/ardour/bundle.cc
@@ -0,0 +1,282 @@
+/*
+ Copyright (C) 2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+
+#include <pbd/failed_constructor.h>
+#include <ardour/ardour.h>
+#include <ardour/bundle.h>
+#include <pbd/xml++.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+/** Construct a Bundle from an XML node.
+ * @param node XML node.
+ */
+Bundle::Bundle (const XMLNode& node)
+{
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+}
+
+/** Construct an InputBundle from an XML node.
+ * @param node XML node.
+ */
+InputBundle::InputBundle (const XMLNode& node)
+ : Bundle (node)
+{
+
+}
+
+/** Construct an OutputBundle from an XML node.
+ * @param node XML node.
+ */
+OutputBundle::OutputBundle (const XMLNode& node)
+ : Bundle (node)
+{
+
+}
+
+/** Set the name.
+ * @param name New name.
+ */
+void
+Bundle::set_name (string name, void *src)
+{
+ _name = name;
+ NameChanged (src);
+}
+
+/** Add an association between one of our channels and a JACK port.
+ * @param ch Channel index.
+ * @param portname JACK port name to associate with.
+ */
+void
+Bundle::add_port_to_channel (int ch, string portname)
+{
+ {
+ Glib::Mutex::Lock lm (channels_lock);
+ _channels[ch].push_back (portname);
+ }
+
+ PortsChanged (ch); /* EMIT SIGNAL */
+}
+
+/** Disassociate a JACK port from one of our channels.
+ * @param ch Channel index.
+ * @param portname JACK port name to disassociate from.
+ */
+
+void
+Bundle::remove_port_from_channel (int ch, string portname)
+{
+ bool changed = false;
+
+ {
+ Glib::Mutex::Lock lm (channels_lock);
+ PortList& pl = _channels[ch];
+ PortList::iterator i = find (pl.begin(), pl.end(), portname);
+
+ if (i != pl.end()) {
+ pl.erase (i);
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ PortsChanged (ch); /* EMIT SIGNAL */
+ }
+}
+
+/**
+ * @param ch Channel index.
+ * @return List of JACK ports that this channel is connected to.
+ */
+const Bundle::PortList&
+Bundle::channel_ports (int ch) const
+{
+ Glib::Mutex::Lock lm (channels_lock);
+ return _channels[ch];
+}
+
+/** operator== for Bundles; they are equal if their channels are the same.
+ * @param other Bundle to compare with this one.
+ */
+bool
+Bundle::operator== (const Bundle& other) const
+{
+ return other._channels == _channels;
+}
+
+
+/** Set the number of channels.
+ * @param n New number of channels.
+ */
+
+void
+Bundle::set_nchannels (int n)
+{
+ {
+ Glib::Mutex::Lock lm (channels_lock);
+ _channels.clear ();
+ for (int i = 0; i < n; ++i) {
+ _channels.push_back (PortList());
+ }
+ }
+
+ ConfigurationChanged (); /* EMIT SIGNAL */
+}
+
+XMLNode&
+Bundle::get_state ()
+{
+ XMLNode *node;
+ string str;
+
+ if (dynamic_cast<InputBundle *> (this)) {
+ node = new XMLNode ("InputConnection");
+ } else {
+ node = new XMLNode ("OutputConnection");
+ }
+
+ node->add_property ("name", _name);
+
+ for (vector<PortList>::iterator i = _channels.begin(); i != _channels.end(); ++i) {
+
+ str += '{';
+
+ for (vector<string>::iterator ii = (*i).begin(); ii != (*i).end(); ++ii) {
+ if (ii != (*i).begin()) {
+ str += ',';
+ }
+ str += *ii;
+ }
+ str += '}';
+ }
+
+ node->add_property ("connections", str);
+
+ return *node;
+}
+
+int
+Bundle::set_state (const XMLNode& node)
+{
+ const XMLProperty *prop;
+
+ if ((prop = node.property ("name")) == 0) {
+ error << _("Node for Connection has no \"name\" property") << endmsg;
+ return -1;
+ }
+
+ _name = prop->value();
+ _dynamic = false;
+
+ if ((prop = node.property ("connections")) == 0) {
+ error << _("Node for Connection has no \"connections\" property") << endmsg;
+ return -1;
+ }
+
+ set_channels (prop->value());
+
+ return 0;
+}
+
+/** Set up channels from an XML property string.
+ * @param str String.
+ * @return 0 on success, -1 on error.
+ */
+int
+Bundle::set_channels (const string& str)
+{
+ vector<string> ports;
+ int i;
+ int n;
+ int nchannels;
+
+ if ((nchannels = count (str.begin(), str.end(), '{')) == 0) {
+ return 0;
+ }
+
+ set_nchannels (nchannels);
+
+ string::size_type start, end, ostart;
+
+ ostart = 0;
+ start = 0;
+ end = 0;
+ i = 0;
+
+ while ((start = str.find_first_of ('{', ostart)) != string::npos) {
+ start += 1;
+
+ if ((end = str.find_first_of ('}', start)) == string::npos) {
+ error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
+ return -1;
+ }
+
+ if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
+ error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
+
+ return -1;
+
+ } else if (n > 0) {
+
+ for (int x = 0; x < n; ++x) {
+ add_port_to_channel (i, ports[x]);
+ }
+ }
+
+ ostart = end+1;
+ i++;
+ }
+
+ return 0;
+}
+
+int
+Bundle::parse_io_string (const string& str, vector<string>& ports)
+{
+ string::size_type pos, opos;
+
+ if (str.length() == 0) {
+ return 0;
+ }
+
+ pos = 0;
+ opos = 0;
+
+ ports.clear ();
+
+ while ((pos = str.find_first_of (',', opos)) != string::npos) {
+ ports.push_back (str.substr (opos, pos - opos));
+ opos = pos + 1;
+ }
+
+ if (opos < str.length()) {
+ ports.push_back (str.substr(opos));
+ }
+
+ return ports.size();
+}
+
diff --git a/libs/ardour/caimportable.cc b/libs/ardour/caimportable.cc
new file mode 100644
index 0000000000..229bfa8809
--- /dev/null
+++ b/libs/ardour/caimportable.cc
@@ -0,0 +1,118 @@
+#include <ardour/caimportable.h>
+#include <sndfile.h>
+#include <pbd/error.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+
+CAImportableSource::CAImportableSource (const string& path)
+{
+ try {
+ af.Open (path.c_str());
+
+ CAStreamBasicDescription file_format (af.GetFileDataFormat());
+ CAStreamBasicDescription client_format (file_format);
+
+ /* set canonial form (PCM, native float packed, 32 bit, with the correct number of channels
+ and interleaved (since we plan to deinterleave ourselves)
+ */
+
+ client_format.SetCanonical(client_format.NumberChannels(), true);
+ af.SetClientFormat (client_format);
+
+ } catch (CAXException& cax) {
+ error << string_compose ("CAImportable: %1", cax.mOperation) << endmsg;
+ throw failed_constructor ();
+ }
+
+}
+
+CAImportableSource::~CAImportableSource ()
+{
+}
+
+nframes_t
+CAImportableSource::read (Sample* buffer, nframes_t nframes)
+{
+ nframes_t nread = 0;
+ AudioBufferList abl;
+ nframes_t per_channel;
+ bool at_end = false;
+
+ abl.mNumberBuffers = 1;
+ abl.mBuffers[0].mNumberChannels = channels();
+
+ per_channel = nframes / abl.mBuffers[0].mNumberChannels;
+
+ while (nread < per_channel) {
+
+ UInt32 new_cnt = per_channel - nread;
+
+ abl.mBuffers[0].mDataByteSize = new_cnt * abl.mBuffers[0].mNumberChannels * sizeof(Sample);
+ abl.mBuffers[0].mData = buffer + nread;
+
+ try {
+ af.Read (new_cnt, &abl);
+ } catch (CAXException& cax) {
+ error << string_compose("CAImportable: %1", cax.mOperation);
+ return -1;
+ }
+
+ if (new_cnt == 0) {
+ /* EOF */
+ at_end = true;
+ break;
+ }
+
+ nread += new_cnt;
+ }
+
+ if (!at_end && nread < per_channel) {
+ return 0;
+ } else {
+ return nread * abl.mBuffers[0].mNumberChannels;
+ }
+}
+
+uint
+CAImportableSource::channels () const
+{
+ return af.GetFileDataFormat().NumberChannels();
+}
+
+nframes_t
+CAImportableSource::length () const
+{
+ return af.GetNumberFrames();
+}
+
+nframes_t
+CAImportableSource::samplerate() const
+{
+ CAStreamBasicDescription client_asbd;
+
+ try {
+ client_asbd = af.GetClientDataFormat ();
+ } catch (CAXException& cax) {
+ error << string_compose ("CAImportable: %1", cax.mOperation) << endmsg;
+ return 0.0;
+ }
+
+ return client_asbd.mSampleRate;
+}
+
+void
+CAImportableSource::seek (nframes_t pos)
+{
+ try {
+ af.Seek (pos);
+ } catch (CAXException& cax) {
+ error << string_compose ("CAImportable: %1 to %2", cax.mOperation, pos) << endmsg;
+ }
+}
+
+
+
diff --git a/libs/ardour/chan_count.cc b/libs/ardour/chan_count.cc
new file mode 100644
index 0000000000..b6f51a4d95
--- /dev/null
+++ b/libs/ardour/chan_count.cc
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2006 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.
+
+ $Id: insert.cc 712 2006-07-28 01:08:57Z drobilla $
+*/
+
+#define __STDC_LIMIT_MACROS 1
+#include <stdint.h>
+#include <ardour/chan_count.h>
+
+namespace ARDOUR {
+
+// infinite/zero chan count stuff, for setting minimums and maximums, etc.
+// FIXME: implement this in a less fugly way
+
+ChanCount
+infinity_factory()
+{
+ ChanCount ret;
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ ret.set(*t, UINT32_MAX);
+ }
+
+ return ret;
+}
+
+
+// Statics
+const ChanCount ChanCount::INFINITE = infinity_factory();
+const ChanCount ChanCount::ZERO = ChanCount();
+
+
+} // namespace ARDOUR
diff --git a/libs/ardour/configuration.cc b/libs/ardour/configuration.cc
new file mode 100644
index 0000000000..b164d418ab
--- /dev/null
+++ b/libs/ardour/configuration.cc
@@ -0,0 +1,371 @@
+/*
+ Copyright (C) 1999-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <unistd.h>
+#include <cstdio> /* for snprintf, grrr */
+
+#include <glib.h>
+#include <glib/gstdio.h> /* for g_stat() */
+
+#include <pbd/failed_constructor.h>
+#include <pbd/xml++.h>
+#include <pbd/filesystem.h>
+#include <pbd/file_utils.h>
+
+#include <midi++/manager.h>
+
+#include <ardour/ardour.h>
+#include <ardour/configuration.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/control_protocol_manager.h>
+#include <ardour/filesystem_paths.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+
+/* this is global so that we do not have to indirect through an object pointer
+ to reference it.
+*/
+
+namespace ARDOUR {
+ float speed_quietning = 0.251189; // -12dB reduction for ffwd or rewind
+}
+
+Configuration::Configuration ()
+ :
+/* construct variables */
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+#define CONFIG_VARIABLE(Type,var,name,value) var (name,value),
+#define CONFIG_VARIABLE_SPECIAL(Type,var,name,value,mutator) var (name,value,mutator),
+#include "ardour/configuration_vars.h"
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+
+
+ current_owner (ConfigVariableBase::Default)
+{
+ _control_protocol_state = 0;
+}
+
+Configuration::~Configuration ()
+{
+}
+
+void
+Configuration::set_current_owner (ConfigVariableBase::Owner owner)
+{
+ current_owner = owner;
+}
+
+int
+Configuration::load_state ()
+{
+ bool found = false;
+
+ sys::path system_rc_file;
+ struct stat statbuf;
+
+ /* load system configuration first */
+
+ if ( find_file_in_search_path (ardour_search_path() + system_config_search_path(),
+ "ardour_system.rc", system_rc_file) )
+ {
+ XMLTree tree;
+ found = true;
+
+ string rcfile = system_rc_file.to_string();
+
+ /* stupid XML Parser hates empty files */
+
+ if (g_stat (rcfile.c_str(), &statbuf)) {
+ return -1;
+ }
+
+ if (statbuf.st_size != 0) {
+ cerr << string_compose (_("loading system configuration file %1"), rcfile) << endl;
+
+ if (!tree.read (rcfile.c_str())) {
+ error << string_compose(_("Ardour: cannot read system configuration file \"%1\""), rcfile) << endmsg;
+ return -1;
+ }
+
+ current_owner = ConfigVariableBase::System;
+
+ if (set_state (*tree.root())) {
+ error << string_compose(_("Ardour: system configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
+ return -1;
+ }
+ } else {
+ error << _("your system Ardour configuration file is empty. This probably means that there as an error installing Ardour") << endmsg;
+ }
+ }
+
+ /* now load configuration file for user */
+
+ sys::path user_rc_file;
+
+ if (find_file_in_search_path (ardour_search_path() + user_config_directory(),
+ "ardour.rc", user_rc_file))
+ {
+ XMLTree tree;
+
+ string rcfile = user_rc_file.to_string();
+
+ /* stupid XML parser hates empty files */
+
+ if (g_stat (rcfile.c_str(), &statbuf)) {
+ return -1;
+ }
+
+ if (statbuf.st_size != 0) {
+ cerr << string_compose (_("loading user configuration file %1"), rcfile) << endl;
+
+ if (!tree.read (rcfile)) {
+ error << string_compose(_("Ardour: cannot read configuration file \"%1\""), rcfile) << endmsg;
+ return -1;
+ }
+
+ current_owner = ConfigVariableBase::Config;
+
+ if (set_state (*tree.root())) {
+ error << string_compose(_("Ardour: user configuration file \"%1\" not loaded successfully."), rcfile) << endmsg;
+ return -1;
+ }
+ } else {
+ warning << _("your Ardour configuration file is empty. This is not normal.") << endmsg;
+ }
+ }
+
+ if (!found)
+ error << "Ardour: could not find configuration file (ardour.rc), canvas will look broken." << endmsg;
+
+ return 0;
+}
+
+int
+Configuration::save_state()
+{
+ XMLTree tree;
+
+ try
+ {
+ sys::create_directories (user_config_directory ());
+ }
+ catch (const sys::filesystem_error& ex)
+ {
+ error << "Could not create user configuration directory" << endmsg;
+ return -1;
+ }
+
+ sys::path rcfile_path(user_config_directory());
+
+ rcfile_path /= "ardour.rc";
+ const string rcfile = rcfile_path.to_string();
+
+ // this test seems bogus?
+ if (rcfile.length()) {
+ tree.set_root (&get_state());
+ if (!tree.write (rcfile.c_str())){
+ error << string_compose (_("Config file %1 not saved"), rcfile) << endmsg;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void
+Configuration::add_instant_xml(XMLNode& node)
+{
+ Stateful::add_instant_xml (node, user_config_directory ());
+}
+
+XMLNode*
+Configuration::instant_xml(const string& node_name)
+{
+ return Stateful::instant_xml (node_name, user_config_directory ());
+}
+
+
+bool
+Configuration::save_config_options_predicate (ConfigVariableBase::Owner owner)
+{
+ /* only save things that were in the config file to start with */
+ return owner & ConfigVariableBase::Config;
+}
+
+XMLNode&
+Configuration::get_state ()
+{
+ XMLNode* root;
+ LocaleGuard lg (X_("POSIX"));
+
+ root = new XMLNode("Ardour");
+
+ MIDI::Manager::PortMap::const_iterator i;
+ const MIDI::Manager::PortMap& ports = MIDI::Manager::instance()->get_midi_ports();
+
+ for (i = ports.begin(); i != ports.end(); ++i) {
+ root->add_child_nocopy(i->second->get_state());
+ }
+
+ root->add_child_nocopy (get_variables (sigc::mem_fun (*this, &Configuration::save_config_options_predicate), "Config"));
+
+ if (_extra_xml) {
+ root->add_child_copy (*_extra_xml);
+ }
+
+ root->add_child_nocopy (ControlProtocolManager::instance().get_state());
+
+ return *root;
+}
+
+XMLNode&
+Configuration::get_variables (sigc::slot<bool,ConfigVariableBase::Owner> predicate, std::string which_node)
+{
+ XMLNode* node;
+ LocaleGuard lg (X_("POSIX"));
+
+ node = new XMLNode(which_node);
+
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+#define CONFIG_VARIABLE(type,var,Name,value) \
+ if (node->name() == "Config") { if (predicate (var.owner())) { var.add_to_node (*node); }}
+#define CONFIG_VARIABLE_SPECIAL(type,var,Name,value,mutator) \
+ if (node->name() == "Config") { if (predicate (var.owner())) { var.add_to_node (*node); }}
+#include "ardour/configuration_vars.h"
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+
+ return *node;
+}
+
+int
+Configuration::set_state (const XMLNode& root)
+{
+ if (root.name() != "Ardour") {
+ return -1;
+ }
+
+ XMLNodeList nlist = root.children();
+ XMLNodeConstIterator niter;
+ XMLNode *node;
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ node = *niter;
+
+ if (node->name() == "MIDI-port") {
+
+ try {
+
+ MIDI::Port::Descriptor desc (*node);
+ map<string,XMLNode>::iterator x;
+ if ((x = midi_ports.find (desc.tag)) != midi_ports.end()) {
+ midi_ports.erase (x);
+ }
+ midi_ports.insert (pair<string,XMLNode>(desc.tag,*node));
+ }
+
+ catch (failed_constructor& err) {
+ warning << _("ill-formed MIDI port specification in ardour rcfile (ignored)") << endmsg;
+ }
+
+ } else if (node->name() == "Config") {
+
+ set_variables (*node, ConfigVariableBase::Config);
+
+ } else if (node->name() == "extra") {
+ _extra_xml = new XMLNode (*node);
+
+ } else if (node->name() == ControlProtocolManager::state_node_name) {
+ _control_protocol_state = new XMLNode (*node);
+ }
+ }
+
+ Diskstream::set_disk_io_chunk_frames (minimum_disk_io_bytes.get() / sizeof (Sample));
+
+ return 0;
+}
+
+void
+Configuration::set_variables (const XMLNode& node, ConfigVariableBase::Owner owner)
+{
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+#define CONFIG_VARIABLE(type,var,name,value) \
+ if (var.set_from_node (node, owner)) { \
+ ParameterChanged (name); \
+ }
+#define CONFIG_VARIABLE_SPECIAL(type,var,name,value,mutator) \
+ if (var.set_from_node (node, owner)) { \
+ ParameterChanged (name); \
+ }
+
+#include "ardour/configuration_vars.h"
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+
+}
+void
+Configuration::map_parameters (sigc::slot<void,const char*> theSlot)
+{
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+#define CONFIG_VARIABLE(type,var,name,value) theSlot (name);
+#define CONFIG_VARIABLE_SPECIAL(type,var,name,value,mutator) theSlot (name);
+#include "ardour/configuration_vars.h"
+#undef CONFIG_VARIABLE
+#undef CONFIG_VARIABLE_SPECIAL
+}
+
+bool ConfigVariableBase::show_stores = false;
+
+void
+ConfigVariableBase::set_show_stored_values (bool yn)
+{
+ show_stores = yn;
+}
+
+void
+ConfigVariableBase::show_stored_value (const string& str)
+{
+ if (show_stores) {
+ cerr << "Config variable " << _name << " stored as " << str << endl;
+ }
+}
+
+void
+ConfigVariableBase::notify ()
+{
+ // placeholder for any debugging desired when a config variable is modified
+}
+
+void
+ConfigVariableBase::miss ()
+{
+ // placeholder for any debugging desired when a config variable
+ // is set but to the same value as it already has
+}
+
diff --git a/libs/ardour/control_protocol_manager.cc b/libs/ardour/control_protocol_manager.cc
new file mode 100644
index 0000000000..11c18175d1
--- /dev/null
+++ b/libs/ardour/control_protocol_manager.cc
@@ -0,0 +1,390 @@
+/*
+ Copyright (C) 2000-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <dlfcn.h>
+
+#include <pbd/compose.h>
+#include <pbd/file_utils.h>
+#include <pbd/error.h>
+
+#include <control_protocol/control_protocol.h>
+
+#include <ardour/session.h>
+#include <ardour/control_protocol_manager.h>
+#include <ardour/control_protocol_search_path.h>
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+
+#include "i18n.h"
+
+ControlProtocolManager* ControlProtocolManager::_instance = 0;
+const string ControlProtocolManager::state_node_name = X_("ControlProtocols");
+
+ControlProtocolManager::ControlProtocolManager ()
+{
+ if (_instance == 0) {
+ _instance = this;
+ }
+
+ _session = 0;
+}
+
+ControlProtocolManager::~ControlProtocolManager()
+{
+ Glib::Mutex::Lock lm (protocols_lock);
+
+ for (list<ControlProtocol*>::iterator i = control_protocols.begin(); i != control_protocols.end(); ++i) {
+ delete (*i);
+ }
+
+ control_protocols.clear ();
+
+
+ for (list<ControlProtocolInfo*>::iterator p = control_protocol_info.begin(); p != control_protocol_info.end(); ++p) {
+ delete (*p);
+ }
+
+ control_protocol_info.clear();
+}
+
+void
+ControlProtocolManager::set_session (Session& s)
+{
+ _session = &s;
+ _session->GoingAway.connect (mem_fun (*this, &ControlProtocolManager::drop_session));
+
+ for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+ if ((*i)->requested || (*i)->mandatory) {
+ instantiate (**i);
+ (*i)->requested = false;
+
+ if ((*i)->protocol && (*i)->state) {
+ (*i)->protocol->set_state (*(*i)->state);
+ }
+ }
+ }
+}
+
+void
+ControlProtocolManager::drop_session ()
+{
+ _session = 0;
+
+ {
+ Glib::Mutex::Lock lm (protocols_lock);
+ for (list<ControlProtocol*>::iterator p = control_protocols.begin(); p != control_protocols.end(); ++p) {
+ delete *p;
+ }
+ control_protocols.clear ();
+
+ for (list<ControlProtocolInfo*>::iterator p = control_protocol_info.begin(); p != control_protocol_info.end(); ++p) {
+ // otherwise the ControlProtocol instances are not recreated in set_session
+ if ((*p)->protocol) {
+ (*p)->requested = true;
+ (*p)->protocol = 0;
+ }
+ }
+ }
+}
+
+ControlProtocol*
+ControlProtocolManager::instantiate (ControlProtocolInfo& cpi)
+{
+ if (_session == 0) {
+ return 0;
+ }
+
+ cpi.descriptor = get_descriptor (cpi.path);
+
+ if (cpi.descriptor == 0) {
+ error << string_compose (_("control protocol name \"%1\" has no descriptor"), cpi.name) << endmsg;
+ return 0;
+ }
+
+ if ((cpi.protocol = cpi.descriptor->initialize (cpi.descriptor, _session)) == 0) {
+ error << string_compose (_("control protocol name \"%1\" could not be initialized"), cpi.name) << endmsg;
+ return 0;
+ }
+
+ Glib::Mutex::Lock lm (protocols_lock);
+ control_protocols.push_back (cpi.protocol);
+
+ return cpi.protocol;
+}
+
+int
+ControlProtocolManager::teardown (ControlProtocolInfo& cpi)
+{
+ if (!cpi.protocol) {
+ return 0;
+ }
+
+ if (!cpi.descriptor) {
+ return 0;
+ }
+
+ if (cpi.mandatory) {
+ return 0;
+ }
+
+ cpi.descriptor->destroy (cpi.descriptor, cpi.protocol);
+
+ {
+ Glib::Mutex::Lock lm (protocols_lock);
+ list<ControlProtocol*>::iterator p = find (control_protocols.begin(), control_protocols.end(), cpi.protocol);
+ if (p != control_protocols.end()) {
+ control_protocols.erase (p);
+ } else {
+ cerr << "Programming error: ControlProtocolManager::teardown() called for " << cpi.name << ", but it was not found in control_protocols" << endl;
+ }
+
+ list<ControlProtocolInfo*>::iterator p2 = find (control_protocol_info.begin(), control_protocol_info.end(), &cpi);
+ if (p2 != control_protocol_info.end()) {
+ control_protocol_info.erase (p2);
+ } else {
+ cerr << "Programming error: ControlProtocolManager::teardown() called for " << cpi.name << ", but it was not found in control_protocol_info" << endl;
+ }
+ }
+
+ cpi.protocol = 0;
+ dlclose (cpi.descriptor->module);
+ return 0;
+}
+
+void
+ControlProtocolManager::load_mandatory_protocols ()
+{
+ if (_session == 0) {
+ return;
+ }
+
+ for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+ if ((*i)->mandatory && ((*i)->protocol == 0)) {
+ info << string_compose (_("Instantiating mandatory control protocol %1"), (*i)->name) << endmsg;
+ instantiate (**i);
+ }
+ }
+}
+
+void
+ControlProtocolManager::discover_control_protocols ()
+{
+ vector<sys::path> cp_modules;
+
+ Glib::PatternSpec so_extension_pattern("*.so");
+ Glib::PatternSpec dylib_extension_pattern("*.dylib");
+
+ find_matching_files_in_search_path (control_protocol_search_path (),
+ so_extension_pattern, cp_modules);
+
+ find_matching_files_in_search_path (control_protocol_search_path (),
+ dylib_extension_pattern, cp_modules);
+
+ info << string_compose (_("looking for control protocols in %1"), control_protocol_search_path().to_string()) << endmsg;
+
+ for (vector<sys::path>::iterator i = cp_modules.begin(); i != cp_modules.end(); ++i) {
+ control_protocol_discover ((*i).to_string());
+ }
+}
+
+int
+ControlProtocolManager::control_protocol_discover (string path)
+{
+ ControlProtocolDescriptor* descriptor;
+
+ if ((descriptor = get_descriptor (path)) != 0) {
+
+ ControlProtocolInfo* cpi = new ControlProtocolInfo ();
+
+ if (!descriptor->probe (descriptor)) {
+ info << string_compose (_("Control protocol %1 not usable"), descriptor->name) << endmsg;
+ } else {
+
+ cpi->descriptor = descriptor;
+ cpi->name = descriptor->name;
+ cpi->path = path;
+ cpi->protocol = 0;
+ cpi->requested = false;
+ cpi->mandatory = descriptor->mandatory;
+ cpi->supports_feedback = descriptor->supports_feedback;
+ cpi->state = 0;
+
+ control_protocol_info.push_back (cpi);
+
+ info << string_compose(_("Control surface protocol discovered: \"%1\""), cpi->name) << endmsg;
+ }
+
+ dlclose (descriptor->module);
+ }
+
+ return 0;
+}
+
+ControlProtocolDescriptor*
+ControlProtocolManager::get_descriptor (string path)
+{
+ void *module;
+ ControlProtocolDescriptor *descriptor = 0;
+ ControlProtocolDescriptor* (*dfunc)(void);
+ const char *errstr;
+
+ if ((module = dlopen (path.c_str(), RTLD_NOW)) == 0) {
+ error << string_compose(_("ControlProtocolManager: cannot load module \"%1\" (%2)"), path, dlerror()) << endmsg;
+ return 0;
+ }
+
+
+ dfunc = (ControlProtocolDescriptor* (*)(void)) dlsym (module, "protocol_descriptor");
+
+ if ((errstr = dlerror()) != 0) {
+ error << string_compose(_("ControlProtocolManager: module \"%1\" has no descriptor function."), path) << endmsg;
+ error << errstr << endmsg;
+ dlclose (module);
+ return 0;
+ }
+
+ descriptor = dfunc();
+ if (descriptor) {
+ descriptor->module = module;
+ } else {
+ dlclose (module);
+ }
+
+ return descriptor;
+}
+
+void
+ControlProtocolManager::foreach_known_protocol (sigc::slot<void,const ControlProtocolInfo*> method)
+{
+ for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+ method (*i);
+ }
+}
+
+ControlProtocolInfo*
+ControlProtocolManager::cpi_by_name (string name)
+{
+ for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+ if (name == (*i)->name) {
+ return *i;
+ }
+ }
+ return 0;
+}
+
+int
+ControlProtocolManager::set_state (const XMLNode& node)
+{
+ XMLNodeList clist;
+ XMLNodeConstIterator citer;
+ XMLProperty* prop;
+
+ clist = node.children();
+
+ for (citer = clist.begin(); citer != clist.end(); ++citer) {
+ if ((*citer)->name() == X_("Protocol")) {
+
+ prop = (*citer)->property (X_("active"));
+
+ if (prop && prop->value() == X_("yes")) {
+ if ((prop = (*citer)->property (X_("name"))) != 0) {
+ ControlProtocolInfo* cpi = cpi_by_name (prop->value());
+ if (cpi) {
+ if (!(*citer)->children().empty()) {
+ cpi->state = (*citer)->children().front ();
+ } else {
+ cpi->state = 0;
+ }
+
+ if (_session) {
+ instantiate (*cpi);
+ } else {
+ cpi->requested = true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+XMLNode&
+ControlProtocolManager::get_state (void)
+{
+ XMLNode* root = new XMLNode (state_node_name);
+ Glib::Mutex::Lock lm (protocols_lock);
+
+ for (list<ControlProtocolInfo*>::iterator i = control_protocol_info.begin(); i != control_protocol_info.end(); ++i) {
+
+ XMLNode * child;
+
+ if ((*i)->protocol) {
+ child = &((*i)->protocol->get_state());
+ child->add_property (X_("active"), "yes");
+ // should we update (*i)->state here? probably.
+ root->add_child_nocopy (*child);
+ }
+ else if ((*i)->state) {
+ // keep ownership clear
+ root->add_child_copy (*(*i)->state);
+ }
+ else {
+ child = new XMLNode (X_("Protocol"));
+ child->add_property (X_("name"), (*i)->name);
+ child->add_property (X_("active"), "no");
+ root->add_child_nocopy (*child);
+ }
+ }
+
+ return *root;
+}
+
+void
+ControlProtocolManager::set_protocol_states (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLProperty* prop;
+
+ nlist = node.children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ XMLNode* child = (*niter);
+
+ if ((prop = child->property ("name")) == 0) {
+ error << _("control protocol XML node has no name property. Ignored.") << endmsg;
+ continue;
+ }
+
+ ControlProtocolInfo* cpi = cpi_by_name (prop->value());
+
+ if (!cpi) {
+ warning << string_compose (_("control protocol \"%1\" is not known. Ignored"), prop->value()) << endmsg;
+ continue;
+ }
+
+ /* copy the node so that ownership is clear */
+
+ cpi->state = new XMLNode (*child);
+ }
+}
diff --git a/libs/ardour/control_protocol_search_path.cc b/libs/ardour/control_protocol_search_path.cc
new file mode 100644
index 0000000000..713ef30e65
--- /dev/null
+++ b/libs/ardour/control_protocol_search_path.cc
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <glibmm/miscutils.h>
+
+#include <ardour/control_protocol_search_path.h>
+#include <ardour/directory_names.h>
+#include <ardour/filesystem_paths.h>
+
+namespace {
+ const char * const surfaces_env_variable_name = "ARDOUR_SURFACES_PATH";
+} // anonymous
+
+namespace ARDOUR {
+
+SearchPath
+control_protocol_search_path ()
+{
+ bool surfaces_path_defined = false;
+ SearchPath spath_env(Glib::getenv(surfaces_env_variable_name, surfaces_path_defined));
+
+ if (surfaces_path_defined)
+ {
+ return spath_env;
+ }
+
+ SearchPath spath(user_config_directory ());
+
+ spath += ardour_module_directory ();
+
+ spath.add_subdirectory_to_paths(surfaces_dir_name);
+
+ return spath;
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/coreaudiosource.cc b/libs/ardour/coreaudiosource.cc
new file mode 100644
index 0000000000..4383f1a696
--- /dev/null
+++ b/libs/ardour/coreaudiosource.cc
@@ -0,0 +1,270 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Taybin Rutkin
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+
+#include <pbd/error.h>
+#include <ardour/coreaudiosource.h>
+#include <ardour/utils.h>
+
+#include <appleutility/CAAudioFile.h>
+#include <appleutility/CAStreamBasicDescription.h>
+
+#include "i18n.h"
+
+#include <AudioToolbox/AudioFormat.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+CoreAudioSource::CoreAudioSource (Session& s, const XMLNode& node)
+ : AudioFileSource (s, node)
+{
+ init ();
+}
+
+CoreAudioSource::CoreAudioSource (Session& s, const string& path, int chn, Flag flags)
+ /* files created this way are never writable or removable */
+ : AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
+{
+ _channel = chn;
+ init ();
+}
+
+void
+CoreAudioSource::init ()
+{
+ /* note that we temporarily truncated _id at the colon */
+ try {
+ af.Open(_path.c_str());
+
+ CAStreamBasicDescription file_format (af.GetFileDataFormat());
+ n_channels = file_format.NumberChannels();
+
+ if (_channel >= n_channels) {
+ error << string_compose("CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number (%3)", n_channels, _channel, name()) << endmsg;
+ throw failed_constructor();
+ }
+
+ _length = af.GetNumberFrames();
+
+ CAStreamBasicDescription client_format (file_format);
+
+ /* set canonial form (PCM, native float packed, 32 bit, with the correct number of channels
+ and interleaved (since we plan to deinterleave ourselves)
+ */
+
+ client_format.SetCanonical(client_format.NumberChannels(), true);
+ af.SetClientFormat (client_format);
+
+ } catch (CAXException& cax) {
+
+ error << string_compose(_("CoreAudioSource: cannot open file \"%1\" for %2"),
+ _path, (writable() ? "read+write" : "reading")) << endmsg;
+ throw failed_constructor ();
+ }
+}
+
+CoreAudioSource::~CoreAudioSource ()
+{
+ GoingAway (); /* EMIT SIGNAL */
+}
+
+int
+CoreAudioSource::safe_read (Sample* dst, nframes_t start, nframes_t cnt, AudioBufferList& abl) const
+{
+ nframes_t nread = 0;
+
+ while (nread < cnt) {
+
+ try {
+ af.Seek (start+nread);
+ } catch (CAXException& cax) {
+ error << string_compose("CoreAudioSource: %1 to %2 (%3)", cax.mOperation, start+nread, _name.substr (1)) << endmsg;
+ return -1;
+ }
+
+ UInt32 new_cnt = cnt - nread;
+
+ abl.mBuffers[0].mDataByteSize = new_cnt * n_channels * sizeof(Sample);
+ abl.mBuffers[0].mData = dst + nread;
+
+ try {
+ af.Read (new_cnt, &abl);
+ } catch (CAXException& cax) {
+ error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name);
+ return -1;
+ }
+
+ if (new_cnt == 0) {
+ /* EOF */
+ if (start+cnt == _length) {
+ /* we really did hit the end */
+ nread = cnt;
+ }
+ break;
+ }
+
+ nread += new_cnt;
+ }
+
+ if (nread < cnt) {
+ return -1;
+ } else {
+ return 0;
+ }
+}
+
+
+nframes_t
+CoreAudioSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const
+{
+ nframes_t file_cnt;
+ AudioBufferList abl;
+
+ abl.mNumberBuffers = 1;
+ abl.mBuffers[0].mNumberChannels = n_channels;
+
+ if (start > _length) {
+
+ /* read starts beyond end of data, just memset to zero */
+
+ file_cnt = 0;
+
+ } else if (start + cnt > _length) {
+
+ /* read ends beyond end of data, read some, memset the rest */
+
+ file_cnt = _length - start;
+
+ } else {
+
+ /* read is entirely within data */
+
+ file_cnt = cnt;
+ }
+
+ if (file_cnt != cnt) {
+ nframes_t delta = cnt - file_cnt;
+ memset (dst+file_cnt, 0, sizeof (Sample) * delta);
+ }
+
+ if (file_cnt) {
+
+ if (n_channels == 1) {
+ if (safe_read (dst, start, file_cnt, abl) == 0) {
+ _read_data_count = cnt * sizeof (Sample);
+ return cnt;
+ }
+ return 0;
+ }
+ }
+
+ Sample* interleave_buf = get_interleave_buffer (file_cnt * n_channels);
+
+ if (safe_read (interleave_buf, start, file_cnt, abl) != 0) {
+ return 0;
+ }
+
+ _read_data_count = cnt * sizeof(float);
+
+ Sample *ptr = interleave_buf + _channel;
+
+ /* stride through the interleaved data */
+
+ for (uint32_t n = 0; n < file_cnt; ++n) {
+ dst[n] = *ptr;
+ ptr += n_channels;
+ }
+
+ return cnt;
+}
+
+float
+CoreAudioSource::sample_rate() const
+{
+ CAStreamBasicDescription client_asbd;
+
+ try {
+ client_asbd = af.GetClientDataFormat ();
+ } catch (CAXException& cax) {
+ error << string_compose("CoreAudioSource: %1 (%2)", cax.mOperation, _name);
+ return 0.0;
+ }
+
+ return client_asbd.mSampleRate;
+}
+
+int
+CoreAudioSource::update_header (nframes_t when, struct tm&, time_t)
+{
+ return 0;
+}
+
+int
+CoreAudioSource::get_soundfile_info (string path, SoundFileInfo& _info, string& error_msg)
+{
+ FSRef ref;
+ ExtAudioFileRef af = 0;
+ size_t size;
+ CFStringRef name;
+ int ret = -1;
+
+ if (FSPathMakeRef ((UInt8*)path.c_str(), &ref, 0) != noErr) {
+ goto out;
+ }
+
+ if (ExtAudioFileOpen(&ref, &af) != noErr) {
+ goto out;
+ }
+
+ AudioStreamBasicDescription absd;
+ memset(&absd, 0, sizeof(absd));
+ size = sizeof(AudioStreamBasicDescription);
+ if (ExtAudioFileGetProperty (af, kExtAudioFileProperty_FileDataFormat, &size, &absd) != noErr) {
+ goto out;
+ }
+
+ _info.samplerate = absd.mSampleRate;
+ _info.channels = absd.mChannelsPerFrame;
+
+ size = sizeof(_info.length);
+ if (ExtAudioFileGetProperty(af, kExtAudioFileProperty_FileLengthFrames, &size, &_info.length) != noErr) {
+ goto out;
+ }
+
+ size = sizeof(CFStringRef);
+ if (AudioFormatGetProperty(kAudioFormatProperty_FormatName, sizeof(absd), &absd, &size, &name) != noErr) {
+ goto out;
+ }
+
+ _info.format_name = CFStringRefToStdString(name);
+
+ // XXX it would be nice to find a way to get this information if it exists
+
+ _info.timecode = 0;
+ ret = 0;
+
+ out:
+ ExtAudioFileDispose (af);
+ return ret;
+
+}
diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc
new file mode 100644
index 0000000000..f3dfa28165
--- /dev/null
+++ b/libs/ardour/crossfade.cc
@@ -0,0 +1,904 @@
+/*
+ Copyright (C) 2003-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <sigc++/bind.h>
+
+#include <pbd/stacktrace.h>
+
+#include <ardour/types.h>
+#include <ardour/crossfade.h>
+#include <ardour/crossfade_compare.h>
+#include <ardour/audioregion.h>
+#include <ardour/playlist.h>
+#include <ardour/utils.h>
+#include <ardour/session.h>
+#include <ardour/source.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+nframes_t Crossfade::_short_xfade_length = 0;
+Change Crossfade::ActiveChanged = new_change();
+Change Crossfade::FollowOverlapChanged = new_change();
+
+/* XXX if and when we ever implement parallel processing of the process()
+ callback, these will need to be handled on a per-thread basis.
+*/
+
+Sample* Crossfade::crossfade_buffer_out = 0;
+Sample* Crossfade::crossfade_buffer_in = 0;
+
+void
+Crossfade::set_buffer_size (nframes_t sz)
+{
+ if (crossfade_buffer_out) {
+ delete [] crossfade_buffer_out;
+ crossfade_buffer_out = 0;
+ }
+
+ if (crossfade_buffer_in) {
+ delete [] crossfade_buffer_in;
+ crossfade_buffer_in = 0;
+ }
+
+ if (sz) {
+ crossfade_buffer_out = new Sample[sz];
+ crossfade_buffer_in = new Sample[sz];
+ }
+}
+
+bool
+Crossfade::operator== (const Crossfade& other)
+{
+ return (_in == other._in) && (_out == other._out);
+}
+
+Crossfade::Crossfade (boost::shared_ptr<AudioRegion> in, boost::shared_ptr<AudioRegion> out,
+ nframes_t length,
+ nframes_t position,
+ AnchorPoint ap)
+ : AudioRegion (in->session(), position, length, "foobar"),
+ _fade_in (Parameter(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
+ _fade_out (Parameter(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
+
+{
+ _in = in;
+ _out = out;
+
+ _anchor_point = ap;
+ _follow_overlap = false;
+
+ _active = Config->get_xfades_active ();
+ _fixed = true;
+
+ initialize ();
+}
+
+Crossfade::Crossfade (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model, bool act)
+ : AudioRegion (a->session(), 0, 0, "foobar"),
+ _fade_in (Parameter(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
+ _fade_out (Parameter(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
+{
+ _in_update = false;
+ _fixed = false;
+
+ if (compute (a, b, model)) {
+ throw failed_constructor();
+ }
+
+ _active = act;
+
+ initialize ();
+
+
+}
+
+Crossfade::Crossfade (const Playlist& playlist, XMLNode& node)
+ : AudioRegion (playlist.session(), 0, 0, "foobar"),
+ _fade_in (Parameter(FadeInAutomation), 0.0, 2.0, 1.0), // linear (gain coefficient) => -inf..+6dB
+ _fade_out (Parameter(FadeOutAutomation), 0.0, 2.0, 1.0) // linear (gain coefficient) => -inf..+6dB
+
+{
+ boost::shared_ptr<Region> r;
+ XMLProperty* prop;
+ LocaleGuard lg (X_("POSIX"));
+
+ /* we have to find the in/out regions before we can do anything else */
+
+ if ((prop = node.property ("in")) == 0) {
+ error << _("Crossfade: no \"in\" region in state") << endmsg;
+ throw failed_constructor();
+ }
+
+ PBD::ID id (prop->value());
+
+ if ((r = playlist.find_region (id)) == 0) {
+ error << string_compose (_("Crossfade: no \"in\" region %1 found in playlist %2"), id, playlist.name())
+ << endmsg;
+ throw failed_constructor();
+ }
+
+ if ((_in = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
+ throw failed_constructor();
+ }
+
+ if ((prop = node.property ("out")) == 0) {
+ error << _("Crossfade: no \"out\" region in state") << endmsg;
+ throw failed_constructor();
+ }
+
+ PBD::ID id2 (prop->value());
+
+ if ((r = playlist.find_region (id2)) == 0) {
+ error << string_compose (_("Crossfade: no \"out\" region %1 found in playlist %2"), id2, playlist.name())
+ << endmsg;
+ throw failed_constructor();
+ }
+
+ if ((_out = boost::dynamic_pointer_cast<AudioRegion> (r)) == 0) {
+ throw failed_constructor();
+ }
+
+ _length = 0;
+ initialize();
+ _active = true;
+
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+}
+
+Crossfade::Crossfade (boost::shared_ptr<Crossfade> orig, boost::shared_ptr<AudioRegion> newin, boost::shared_ptr<AudioRegion> newout)
+ : AudioRegion (boost::dynamic_pointer_cast<const AudioRegion> (orig)),
+ _fade_in (orig->_fade_in),
+ _fade_out (orig->_fade_out)
+{
+ _active = orig->_active;
+ _in_update = orig->_in_update;
+ _anchor_point = orig->_anchor_point;
+ _follow_overlap = orig->_follow_overlap;
+ _fixed = orig->_fixed;
+
+ _in = newin;
+ _out = newout;
+
+ // copied from Crossfade::initialize()
+ _in_update = false;
+
+ _out->suspend_fade_out ();
+ _in->suspend_fade_in ();
+
+ overlap_type = _in->coverage (_out->position(), _out->last_frame());
+ layer_relation = (int32_t) (_in->layer() - _out->layer());
+
+ // Let's make sure the fade isn't too long
+ set_length(_length);
+}
+
+
+Crossfade::~Crossfade ()
+{
+ notify_callbacks ();
+}
+
+void
+Crossfade::initialize ()
+{
+ /* merge source lists from regions */
+
+ _sources = _in->sources();
+ _sources.insert (_sources.end(), _out->sources().begin(), _out->sources().end());
+ _master_sources = _in->master_sources();
+ _master_sources.insert(_master_sources.end(), _out->master_sources().begin(), _out->master_sources().end());
+
+ _in_update = false;
+
+ _out->suspend_fade_out ();
+ _in->suspend_fade_in ();
+
+ _fade_out.freeze ();
+ _fade_out.clear ();
+ _fade_out.add (0.0, 1.0);
+ _fade_out.add ((_length * 0.1), 0.99);
+ _fade_out.add ((_length * 0.2), 0.97);
+ _fade_out.add ((_length * 0.8), 0.03);
+ _fade_out.add ((_length * 0.9), 0.01);
+ _fade_out.add (_length, 0.0);
+ _fade_out.thaw ();
+
+ _fade_in.freeze ();
+ _fade_in.clear ();
+ _fade_in.add (0.0, 0.0);
+ _fade_in.add ((_length * 0.1), 0.01);
+ _fade_in.add ((_length * 0.2), 0.03);
+ _fade_in.add ((_length * 0.8), 0.97);
+ _fade_in.add ((_length * 0.9), 0.99);
+ _fade_in.add (_length, 1.0);
+ _fade_in.thaw ();
+
+ overlap_type = _in->coverage (_out->position(), _out->last_frame());
+ layer_relation = (int32_t) (_in->layer() - _out->layer());
+}
+
+nframes_t
+Crossfade::read_raw_internal (Sample* buf, nframes_t start, nframes_t cnt) const
+{
+#if 0
+ Sample* mixdown = new Sample[cnt];
+ float* gain = new float[cnt];
+ nframes_t ret;
+
+ ret = read_at (buf, mixdown, gain, start, cnt, chan_n, cnt);
+
+ delete [] mixdown;
+ delete [] gain;
+
+ return ret;
+#endif
+ return cnt;
+}
+
+nframes_t
+Crossfade::read_at (Sample *buf, Sample *mixdown_buffer,
+ float *gain_buffer, nframes_t start, nframes_t cnt, uint32_t chan_n,
+ nframes_t read_frames, nframes_t skip_frames) const
+{
+ nframes_t offset;
+ nframes_t to_write;
+
+ if (!_active) {
+ return 0;
+ }
+
+ if (start < _position) {
+
+ /* handle an initial section of the read area that we do not
+ cover.
+ */
+
+ offset = _position - start;
+
+ if (offset < cnt) {
+ cnt -= offset;
+ } else {
+ return 0;
+ }
+
+ start = _position;
+ buf += offset;
+ to_write = min (_length, cnt);
+
+ } else {
+
+ to_write = min (_length - (start - _position), cnt);
+
+ }
+
+ offset = start - _position;
+
+ /* Prevent data from piling up inthe crossfade buffers when reading a transparent region */
+ if (!(_out->opaque())) {
+ memset (crossfade_buffer_out, 0, sizeof (Sample) * to_write);
+ } else if (!(_in->opaque())) {
+ memset (crossfade_buffer_in, 0, sizeof (Sample) * to_write);
+ }
+
+ _out->read_at (crossfade_buffer_out, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames);
+ _in->read_at (crossfade_buffer_in, mixdown_buffer, gain_buffer, start, to_write, chan_n, read_frames, skip_frames);
+
+ float* fiv = new float[to_write];
+ float* fov = new float[to_write];
+
+ _fade_in.curve().get_vector (offset, offset+to_write, fiv, to_write);
+ _fade_out.curve().get_vector (offset, offset+to_write, fov, to_write);
+
+ /* note: although we have not explicitly taken into account the return values
+ from _out->read_at() or _in->read_at(), the length() function does this
+ implicitly. why? because it computes a value based on the in+out regions'
+ position and length, and so we know precisely how much data they could return.
+ */
+
+ for (nframes_t n = 0; n < to_write; ++n) {
+ buf[n] = (crossfade_buffer_out[n] * fov[n]) + (crossfade_buffer_in[n] * fiv[n]);
+ }
+
+ delete [] fov;
+ delete [] fiv;
+
+ return to_write;
+}
+
+OverlapType
+Crossfade::coverage (nframes_t start, nframes_t end) const
+{
+ nframes_t my_end = _position + _length;
+
+ if ((start >= _position) && (end <= my_end)) {
+ return OverlapInternal;
+ }
+ if ((end >= _position) && (end <= my_end)) {
+ return OverlapStart;
+ }
+ if ((start >= _position) && (start <= my_end)) {
+ return OverlapEnd;
+ }
+ if ((_position >= start) && (_position <= end) && (my_end <= end)) {
+ return OverlapExternal;
+ }
+ return OverlapNone;
+}
+
+void
+Crossfade::set_active (bool yn)
+{
+ if (_active != yn) {
+ _active = yn;
+ StateChanged (ActiveChanged);
+ }
+}
+
+bool
+Crossfade::refresh ()
+{
+ /* crossfades must be between non-muted regions */
+
+ if (_out->muted() || _in->muted()) {
+ Invalidated (shared_from_this ());
+ return false;
+ }
+
+ /* Top layer shouldn't be transparent */
+
+ if (!((layer_relation > 0 ? _in : _out)->opaque())) {
+ Invalidated (shared_from_this());
+ return false;
+ }
+
+ /* layer ordering cannot change */
+
+ int32_t new_layer_relation = (int32_t) (_in->layer() - _out->layer());
+
+ if (new_layer_relation * layer_relation < 0) { // different sign, layers rotated
+ Invalidated (shared_from_this ());
+ return false;
+ }
+
+ OverlapType ot = _in->coverage (_out->first_frame(), _out->last_frame());
+
+ if (ot == OverlapNone) {
+ Invalidated (shared_from_this ());
+ return false;
+ }
+
+ bool send_signal;
+
+ if (ot != overlap_type) {
+
+ if (_follow_overlap) {
+
+ try {
+ compute (_in, _out, Config->get_xfade_model());
+ }
+
+ catch (NoCrossfadeHere& err) {
+ Invalidated (shared_from_this ());
+ return false;
+ }
+
+ send_signal = true;
+
+ } else {
+
+ Invalidated (shared_from_this ());
+ return false;
+ }
+
+ } else {
+
+ send_signal = update ();
+ }
+
+ if (send_signal) {
+ StateChanged (BoundsChanged); /* EMIT SIGNAL */
+ }
+
+ _in_update = false;
+
+ return true;
+}
+
+bool
+Crossfade::update ()
+{
+ nframes_t newlen;
+
+ if (_follow_overlap) {
+ newlen = _out->first_frame() + _out->length() - _in->first_frame();
+ } else {
+ newlen = _length;
+ }
+
+ if (newlen == 0) {
+ Invalidated (shared_from_this ());
+ return false;
+ }
+
+ _in_update = true;
+
+ if ((_follow_overlap && newlen != _length) || (_length > newlen)) {
+
+ double factor = newlen / (double) _length;
+
+ _fade_out.x_scale (factor);
+ _fade_in.x_scale (factor);
+
+ _length = newlen;
+ }
+
+ switch (_anchor_point) {
+ case StartOfIn:
+ _position = _in->first_frame();
+ break;
+
+ case EndOfIn:
+ _position = _in->last_frame() - _length;
+ break;
+
+ case EndOfOut:
+ _position = _out->last_frame() - _length;
+ }
+
+ return true;
+}
+
+int
+Crossfade::compute (boost::shared_ptr<AudioRegion> a, boost::shared_ptr<AudioRegion> b, CrossfadeModel model)
+{
+ boost::shared_ptr<AudioRegion> top;
+ boost::shared_ptr<AudioRegion> bottom;
+ nframes_t short_xfade_length;
+
+ short_xfade_length = _short_xfade_length;
+
+ if (a->layer() < b->layer()) {
+ top = b;
+ bottom = a;
+ } else {
+ top = a;
+ bottom = b;
+ }
+
+ /* first check for matching ends */
+
+ if (top->first_frame() == bottom->first_frame()) {
+
+ /* Both regions start at the same point */
+
+ if (top->last_frame() < bottom->last_frame()) {
+
+ /* top ends before bottom, so put an xfade
+ in at the end of top.
+ */
+
+ /* [-------- top ---------- ]
+ * {====== bottom =====================}
+ */
+
+ _in = bottom;
+ _out = top;
+
+ if (top->last_frame() < short_xfade_length) {
+ _position = 0;
+ } else {
+ _position = top->last_frame() - short_xfade_length;
+ }
+
+ _length = min (short_xfade_length, top->length());
+ _follow_overlap = false;
+ _anchor_point = EndOfIn;
+ _active = true;
+ _fixed = true;
+
+ } else {
+ /* top ends after (or same time) as bottom - no xfade
+ */
+
+ /* [-------- top ------------------------ ]
+ * {====== bottom =====================}
+ */
+
+ throw NoCrossfadeHere();
+ }
+
+ } else if (top->last_frame() == bottom->last_frame()) {
+
+ /* Both regions end at the same point */
+
+ if (top->first_frame() > bottom->first_frame()) {
+
+ /* top starts after bottom, put an xfade in at the
+ start of top
+ */
+
+ /* [-------- top ---------- ]
+ * {====== bottom =====================}
+ */
+
+ _in = top;
+ _out = bottom;
+ _position = top->first_frame();
+ _length = min (short_xfade_length, top->length());
+ _follow_overlap = false;
+ _anchor_point = StartOfIn;
+ _active = true;
+ _fixed = true;
+
+ } else {
+ /* top starts before bottom - no xfade
+ */
+
+ /* [-------- top ------------------------ ]
+ * {====== bottom =====================}
+ */
+
+ throw NoCrossfadeHere();
+ }
+
+ } else {
+
+ /* OK, time to do more regular overlapping */
+
+ OverlapType ot = top->coverage (bottom->first_frame(), bottom->last_frame());
+
+ switch (ot) {
+ case OverlapNone:
+ /* should be NOTREACHED as a precondition of creating
+ a new crossfade, but we need to handle it here.
+ */
+ throw NoCrossfadeHere();
+ break;
+
+ case OverlapInternal:
+ case OverlapExternal:
+ /* should be NOTREACHED because of tests above */
+ throw NoCrossfadeHere();
+ break;
+
+ case OverlapEnd: /* top covers start of bottom but ends within it */
+
+ /* [---- top ------------------------]
+ * { ==== bottom ============ }
+ */
+
+ _in = bottom;
+ _out = top;
+ _anchor_point = EndOfOut;
+
+ if (model == FullCrossfade) {
+ _position = bottom->first_frame(); // "{"
+ _length = _out->first_frame() + _out->length() - _in->first_frame();
+ /* leave active alone */
+ _follow_overlap = true;
+ } else {
+ _length = min (short_xfade_length, top->length());
+ _position = top->last_frame() - _length; // "]" - length
+ _active = true;
+ _follow_overlap = false;
+
+ }
+ break;
+
+ case OverlapStart: /* top starts within bottom but covers bottom's end */
+
+ /* { ==== top ============ }
+ * [---- bottom -------------------]
+ */
+
+ _in = top;
+ _out = bottom;
+ _position = top->first_frame();
+ _anchor_point = StartOfIn;
+
+ if (model == FullCrossfade) {
+ _length = _out->first_frame() + _out->length() - _in->first_frame();
+ /* leave active alone */
+ _follow_overlap = true;
+ } else {
+ _length = min (short_xfade_length, top->length());
+ _active = true;
+ _follow_overlap = false;
+
+ }
+
+ break;
+ }
+ }
+
+ return 0;
+}
+
+XMLNode&
+Crossfade::get_state ()
+{
+ XMLNode* node = new XMLNode (X_("Crossfade"));
+ XMLNode* child;
+ char buf[64];
+ LocaleGuard lg (X_("POSIX"));
+
+ _out->id().print (buf, sizeof (buf));
+ node->add_property ("out", buf);
+ _in->id().print (buf, sizeof (buf));
+ node->add_property ("in", buf);
+ node->add_property ("active", (_active ? "yes" : "no"));
+ node->add_property ("follow-overlap", (_follow_overlap ? "yes" : "no"));
+ node->add_property ("fixed", (_fixed ? "yes" : "no"));
+ snprintf (buf, sizeof(buf), "%" PRIu32, _length);
+ node->add_property ("length", buf);
+ snprintf (buf, sizeof(buf), "%" PRIu32, (uint32_t) _anchor_point);
+ node->add_property ("anchor-point", buf);
+ snprintf (buf, sizeof(buf), "%" PRIu32, (uint32_t) _position);
+ node->add_property ("position", buf);
+
+ child = node->add_child ("FadeIn");
+
+ for (AutomationList::iterator ii = _fade_in.begin(); ii != _fade_in.end(); ++ii) {
+ XMLNode* pnode;
+
+ pnode = new XMLNode ("point");
+
+ snprintf (buf, sizeof (buf), "%" PRIu32, (nframes_t) floor ((*ii)->when));
+ pnode->add_property ("x", buf);
+ snprintf (buf, sizeof (buf), "%.12g", (*ii)->value);
+ pnode->add_property ("y", buf);
+ child->add_child_nocopy (*pnode);
+ }
+
+ child = node->add_child ("FadeOut");
+
+ for (AutomationList::iterator ii = _fade_out.begin(); ii != _fade_out.end(); ++ii) {
+ XMLNode* pnode;
+
+ pnode = new XMLNode ("point");
+
+ snprintf (buf, sizeof (buf), "%" PRIu32, (nframes_t) floor ((*ii)->when));
+ pnode->add_property ("x", buf);
+ snprintf (buf, sizeof (buf), "%.12g", (*ii)->value);
+ pnode->add_property ("y", buf);
+ child->add_child_nocopy (*pnode);
+ }
+
+ return *node;
+}
+
+int
+Crossfade::set_state (const XMLNode& node)
+{
+ XMLNodeConstIterator i;
+ XMLNodeList children;
+ XMLNode* fi;
+ XMLNode* fo;
+ const XMLProperty* prop;
+ LocaleGuard lg (X_("POSIX"));
+ Change what_changed = Change (0);
+ nframes_t val;
+
+ if ((prop = node.property ("position")) != 0) {
+ sscanf (prop->value().c_str(), "%" PRIu32, &val);
+ if (val != _position) {
+ _position = val;
+ what_changed = Change (what_changed | PositionChanged);
+ }
+ } else {
+ warning << _("old-style crossfade information - no position information") << endmsg;
+ _position = _in->first_frame();
+ }
+
+ if ((prop = node.property ("active")) != 0) {
+ bool x = (prop->value() == "yes");
+ if (x != _active) {
+ _active = x;
+ what_changed = Change (what_changed | ActiveChanged);
+ }
+ } else {
+ _active = true;
+ }
+
+ if ((prop = node.property ("follow-overlap")) != 0) {
+ _follow_overlap = (prop->value() == "yes");
+ } else {
+ _follow_overlap = false;
+ }
+
+ if ((prop = node.property ("fixed")) != 0) {
+ _fixed = (prop->value() == "yes");
+ } else {
+ _fixed = false;
+ }
+
+ if ((prop = node.property ("anchor-point")) != 0) {
+ _anchor_point = AnchorPoint (atoi ((prop->value().c_str())));
+ } else {
+ _anchor_point = StartOfIn;
+ }
+
+ if ((prop = node.property ("length")) != 0) {
+
+ sscanf (prop->value().c_str(), "%" PRIu32, &val);
+ if (val != _length) {
+ _length = atol (prop->value().c_str());
+ what_changed = Change (what_changed | LengthChanged);
+ }
+
+ } else {
+
+ /* XXX this branch is legacy code from before
+ the point where we stored xfade lengths.
+ */
+
+ if ((_length = overlap_length()) == 0) {
+ throw failed_constructor();
+ }
+ }
+
+ if ((fi = find_named_node (node, "FadeIn")) == 0) {
+ return -1;
+ }
+
+ if ((fo = find_named_node (node, "FadeOut")) == 0) {
+ return -1;
+ }
+
+ /* fade in */
+
+ _fade_in.freeze ();
+ _fade_in.clear ();
+
+ children = fi->children();
+
+ for (i = children.begin(); i != children.end(); ++i) {
+ if ((*i)->name() == "point") {
+ nframes_t x;
+ float y;
+
+ prop = (*i)->property ("x");
+ sscanf (prop->value().c_str(), "%" PRIu32, &x);
+
+ prop = (*i)->property ("y");
+ sscanf (prop->value().c_str(), "%f", &y);
+
+ _fade_in.add (x, y);
+ }
+ }
+
+ _fade_in.thaw ();
+
+ /* fade out */
+
+ _fade_out.freeze ();
+ _fade_out.clear ();
+
+ children = fo->children();
+
+ for (i = children.begin(); i != children.end(); ++i) {
+ if ((*i)->name() == "point") {
+ nframes_t x;
+ float y;
+ XMLProperty* prop;
+
+ prop = (*i)->property ("x");
+ sscanf (prop->value().c_str(), "%" PRIu32, &x);
+
+ prop = (*i)->property ("y");
+ sscanf (prop->value().c_str(), "%f", &y);
+
+ _fade_out.add (x, y);
+ }
+ }
+
+ _fade_out.thaw ();
+
+ StateChanged (what_changed); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+bool
+Crossfade::can_follow_overlap () const
+{
+ return !_fixed;
+}
+
+void
+Crossfade::set_follow_overlap (bool yn)
+{
+ if (yn == _follow_overlap || _fixed) {
+ return;
+ }
+
+ _follow_overlap = yn;
+
+ if (!yn) {
+ set_length (_short_xfade_length);
+ } else {
+ set_length (_out->first_frame() + _out->length() - _in->first_frame());
+ }
+
+ StateChanged (FollowOverlapChanged);
+}
+
+nframes_t
+Crossfade::set_length (nframes_t len)
+{
+ nframes_t limit;
+
+ switch (_anchor_point) {
+ case StartOfIn:
+ limit = _in->length();
+ break;
+
+ case EndOfIn:
+ limit = _in->length();
+ break;
+
+ case EndOfOut:
+ limit = _out->length();
+ break;
+
+ }
+
+ len = min (limit, len);
+
+ double factor = len / (double) _length;
+
+ _in_update = true;
+ _fade_out.x_scale (factor);
+ _fade_in.x_scale (factor);
+ _in_update = false;
+
+ _length = len;
+
+ StateChanged (LengthChanged);
+
+ return len;
+}
+
+nframes_t
+Crossfade::overlap_length () const
+{
+ if (_fixed) {
+ return _length;
+ }
+ return _out->first_frame() + _out->length() - _in->first_frame();
+}
+
+void
+Crossfade::set_short_xfade_length (nframes_t n)
+{
+ _short_xfade_length = n;
+}
+
+void
+Crossfade::invalidate ()
+{
+ Invalidated (shared_from_this ()); /* EMIT SIGNAL */
+}
diff --git a/libs/ardour/curve.cc b/libs/ardour/curve.cc
new file mode 100644
index 0000000000..dcce3c0c6c
--- /dev/null
+++ b/libs/ardour/curve.cc
@@ -0,0 +1,406 @@
+/*
+ Copyright (C) 2001-2007 Paul Davis
+
+ Contains ideas derived from "Constrained Cubic Spline Interpolation"
+ by CJC Kruger (www.korf.co.uk/spline.pdf).
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <float.h>
+#include <cmath>
+#include <climits>
+#include <cfloat>
+#include <cmath>
+
+#include <glibmm/thread.h>
+#include <sigc++/bind.h>
+
+#include "ardour/curve.h"
+#include "ardour/automation_event.h"
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace sigc;
+using namespace PBD;
+
+Curve::Curve (const AutomationList& al)
+ : _dirty (true)
+ , _list (al)
+{
+ _list.Dirty.connect(mem_fun(*this, &Curve::on_list_dirty));
+}
+
+void
+Curve::solve ()
+{
+ uint32_t npoints;
+
+ if (!_dirty) {
+ return;
+ }
+
+ if ((npoints = _list.events().size()) > 2) {
+
+ /* Compute coefficients needed to efficiently compute a constrained spline
+ curve. See "Constrained Cubic Spline Interpolation" by CJC Kruger
+ (www.korf.co.uk/spline.pdf) for more details.
+ */
+
+ double x[npoints];
+ double y[npoints];
+ uint32_t i;
+ AutomationList::EventList::const_iterator xx;
+
+ for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
+ x[i] = (double) (*xx)->when;
+ y[i] = (double) (*xx)->value;
+ }
+
+ double lp0, lp1, fpone;
+
+ lp0 = (x[1] - x[0])/(y[1] - y[0]);
+ lp1 = (x[2] - x[1])/(y[2] - y[1]);
+
+ if (lp0*lp1 < 0) {
+ fpone = 0;
+ } else {
+ fpone = 2 / (lp1 + lp0);
+ }
+
+ double fplast = 0;
+
+ for (i = 0, xx = _list.events().begin(); xx != _list.events().end(); ++xx, ++i) {
+
+ double xdelta; /* gcc is wrong about possible uninitialized use */
+ double xdelta2; /* ditto */
+ double ydelta; /* ditto */
+ double fppL, fppR;
+ double fpi;
+
+ if (i > 0) {
+ xdelta = x[i] - x[i-1];
+ xdelta2 = xdelta * xdelta;
+ ydelta = y[i] - y[i-1];
+ }
+
+ /* compute (constrained) first derivatives */
+
+ if (i == 0) {
+
+ /* first segment */
+
+ fplast = ((3 * (y[1] - y[0]) / (2 * (x[1] - x[0]))) - (fpone * 0.5));
+
+ /* we don't store coefficients for i = 0 */
+
+ continue;
+
+ } else if (i == npoints - 1) {
+
+ /* last segment */
+
+ fpi = ((3 * ydelta) / (2 * xdelta)) - (fplast * 0.5);
+
+ } else {
+
+ /* all other segments */
+
+ double slope_before = ((x[i+1] - x[i]) / (y[i+1] - y[i]));
+ double slope_after = (xdelta / ydelta);
+
+ if (slope_after * slope_before < 0.0) {
+ /* slope changed sign */
+ fpi = 0.0;
+ } else {
+ fpi = 2 / (slope_before + slope_after);
+ }
+
+ }
+
+ /* compute second derivative for either side of control point `i' */
+
+ fppL = (((-2 * (fpi + (2 * fplast))) / (xdelta))) +
+ ((6 * ydelta) / xdelta2);
+
+ fppR = (2 * ((2 * fpi) + fplast) / xdelta) -
+ ((6 * ydelta) / xdelta2);
+
+ /* compute polynomial coefficients */
+
+ double b, c, d;
+
+ d = (fppR - fppL) / (6 * xdelta);
+ c = ((x[i] * fppL) - (x[i-1] * fppR))/(2 * xdelta);
+
+ double xim12, xim13;
+ double xi2, xi3;
+
+ xim12 = x[i-1] * x[i-1]; /* "x[i-1] squared" */
+ xim13 = xim12 * x[i-1]; /* "x[i-1] cubed" */
+ xi2 = x[i] * x[i]; /* "x[i] squared" */
+ xi3 = xi2 * x[i]; /* "x[i] cubed" */
+
+ b = (ydelta - (c * (xi2 - xim12)) - (d * (xi3 - xim13))) / xdelta;
+
+ /* store */
+
+ (*xx)->create_coeffs();
+ (*xx)->coeff[0] = y[i-1] - (b * x[i-1]) - (c * xim12) - (d * xim13);
+ (*xx)->coeff[1] = b;
+ (*xx)->coeff[2] = c;
+ (*xx)->coeff[3] = d;
+
+ fplast = fpi;
+ }
+
+ }
+
+ _dirty = false;
+}
+
+bool
+Curve::rt_safe_get_vector (double x0, double x1, float *vec, int32_t veclen)
+{
+ Glib::Mutex::Lock lm(_list.lock(), Glib::TRY_LOCK);
+
+ if (!lm.locked()) {
+ return false;
+ } else {
+ _get_vector (x0, x1, vec, veclen);
+ return true;
+ }
+}
+
+void
+Curve::get_vector (double x0, double x1, float *vec, int32_t veclen)
+{
+ Glib::Mutex::Lock lm(_list.lock());
+ _get_vector (x0, x1, vec, veclen);
+}
+
+void
+Curve::_get_vector (double x0, double x1, float *vec, int32_t veclen)
+{
+ double rx, dx, lx, hx, max_x, min_x;
+ int32_t i;
+ int32_t original_veclen;
+ int32_t npoints;
+
+ if ((npoints = _list.events().size()) == 0) {
+ for (i = 0; i < veclen; ++i) {
+ vec[i] = _list.default_value();
+ }
+ return;
+ }
+
+ /* events is now known not to be empty */
+
+ max_x = _list.events().back()->when;
+ min_x = _list.events().front()->when;
+
+ lx = max (min_x, x0);
+
+ if (x1 < 0) {
+ x1 = _list.events().back()->when;
+ }
+
+ hx = min (max_x, x1);
+
+ original_veclen = veclen;
+
+ if (x0 < min_x) {
+
+ /* fill some beginning section of the array with the
+ initial (used to be default) value
+ */
+
+ double frac = (min_x - x0) / (x1 - x0);
+ int32_t subveclen = (int32_t) floor (veclen * frac);
+
+ subveclen = min (subveclen, veclen);
+
+ for (i = 0; i < subveclen; ++i) {
+ vec[i] = _list.events().front()->value;
+ }
+
+ veclen -= subveclen;
+ vec += subveclen;
+ }
+
+ if (veclen && x1 > max_x) {
+
+ /* fill some end section of the array with the default or final value */
+
+ double frac = (x1 - max_x) / (x1 - x0);
+
+ int32_t subveclen = (int32_t) floor (original_veclen * frac);
+
+ float val;
+
+ subveclen = min (subveclen, veclen);
+
+ val = _list.events().back()->value;
+
+ i = veclen - subveclen;
+
+ for (i = veclen - subveclen; i < veclen; ++i) {
+ vec[i] = val;
+ }
+
+ veclen -= subveclen;
+ }
+
+ if (veclen == 0) {
+ return;
+ }
+
+ if (npoints == 1 ) {
+
+ for (i = 0; i < veclen; ++i) {
+ vec[i] = _list.events().front()->value;
+ }
+ return;
+ }
+
+
+ if (npoints == 2) {
+
+ /* linear interpolation between 2 points */
+
+ /* XXX I'm not sure that this is the right thing to
+ do here. but its not a common case for the envisaged
+ uses.
+ */
+
+ if (veclen > 1) {
+ dx = (hx - lx) / (veclen - 1) ;
+ } else {
+ dx = 0; // not used
+ }
+
+ double slope = (_list.events().back()->value - _list.events().front()->value)/
+ (_list.events().back()->when - _list.events().front()->when);
+ double yfrac = dx*slope;
+
+ vec[0] = _list.events().front()->value + slope * (lx - _list.events().front()->when);
+
+ for (i = 1; i < veclen; ++i) {
+ vec[i] = vec[i-1] + yfrac;
+ }
+
+ return;
+ }
+
+ if (_dirty) {
+ solve ();
+ }
+
+ rx = lx;
+
+ if (veclen > 1) {
+
+ dx = (hx - lx) / veclen;
+
+ for (i = 0; i < veclen; ++i, rx += dx) {
+ vec[i] = multipoint_eval (rx);
+ }
+ }
+}
+
+double
+Curve::unlocked_eval (double x)
+{
+ // I don't see the point of this...
+
+ if (_dirty) {
+ solve ();
+ }
+
+ return _list.unlocked_eval (x);
+}
+
+double
+Curve::multipoint_eval (double x)
+{
+ pair<AutomationList::EventList::const_iterator,AutomationList::EventList::const_iterator> range;
+
+ AutomationList::LookupCache& lookup_cache = _list.lookup_cache();
+
+ if ((lookup_cache.left < 0) ||
+ ((lookup_cache.left > x) ||
+ (lookup_cache.range.first == _list.events().end()) ||
+ ((*lookup_cache.range.second)->when < x))) {
+
+ ControlEvent cp (x, 0.0);
+
+ lookup_cache.range = equal_range (_list.events().begin(), _list.events().end(), &cp, AutomationList::time_comparator);
+ }
+
+ range = lookup_cache.range;
+
+ /* EITHER
+
+ a) x is an existing control point, so first == existing point, second == next point
+
+ OR
+
+ b) x is between control points, so range is empty (first == second, points to where
+ to insert x)
+
+ */
+
+ if (range.first == range.second) {
+
+ /* x does not exist within the list as a control point */
+
+ lookup_cache.left = x;
+
+ if (range.first == _list.events().begin()) {
+ /* we're before the first point */
+ // return default_value;
+ _list.events().front()->value;
+ }
+
+ if (range.second == _list.events().end()) {
+ /* we're after the last point */
+ return _list.events().back()->value;
+ }
+
+ double x2 = x * x;
+ ControlEvent* ev = *range.second;
+
+ return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x);
+ }
+
+ /* x is a control point in the data */
+ /* invalidate the cached range because its not usable */
+ lookup_cache.left = -1;
+ return (*range.first)->value;
+}
+
+extern "C" {
+
+void
+curve_get_vector_from_c (void *arg, double x0, double x1, float* vec, int32_t vecsize)
+{
+ static_cast<Curve*>(arg)->get_vector (x0, x1, vec, vecsize);
+}
+
+}
diff --git a/libs/ardour/cycle_timer.cc b/libs/ardour/cycle_timer.cc
new file mode 100644
index 0000000000..143cb841ec
--- /dev/null
+++ b/libs/ardour/cycle_timer.cc
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2002 Andrew Morton
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cstdio>
+#include <pbd/error.h>
+#include <ardour/cycle_timer.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+float CycleTimer::cycles_per_usec = 0;
+
+float
+CycleTimer::get_mhz()
+{
+ FILE *f;
+
+ if ((f = fopen("/proc/cpuinfo", "r")) == 0) {
+ fatal << _("CycleTimer::get_mhz(): can't open /proc/cpuinfo") << endmsg;
+ /*NOTREACHED*/
+ return 0.0f;
+ }
+
+ while (true) {
+
+ float mhz;
+ int ret;
+ char buf[1000];
+
+ if (fgets (buf, sizeof(buf), f) == 0) {
+ fatal << _("CycleTimer::get_mhz(): cannot locate cpu MHz in /proc/cpuinfo") << endmsg;
+ /*NOTREACHED*/
+ return 0.0f;
+ }
+
+#ifdef __powerpc__
+
+ int imhz;
+
+ /* why can't the PPC crew standardize their /proc/cpuinfo format ? */
+ ret = sscanf (buf, "clock\t: %dMHz", &imhz);
+ mhz = (float) imhz;
+
+#else /* XXX don't assume its x86 just because its not power pc */
+ ret = sscanf (buf, "cpu MHz : %f", &mhz);
+
+#endif
+ if (ret == 1) {
+ fclose(f);
+ return mhz;
+ }
+ }
+
+ fatal << _("cannot locate cpu MHz in /proc/cpuinfo") << endmsg;
+ /*NOTREACHED*/
+ return 0.0f;
+}
diff --git a/libs/ardour/default_click.cc b/libs/ardour/default_click.cc
new file mode 100644
index 0000000000..5bdbeb2ac5
--- /dev/null
+++ b/libs/ardour/default_click.cc
@@ -0,0 +1,1174 @@
+/*
+ Copyright (C) 20002 Paul Davis
+ Sounds by Nick Mainsbridge.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+
+using namespace ARDOUR;
+
+const Sample Session::default_click_emphasis[] = {
+
+
+ 0.011260986, 0.055389404, 0.10488892, 0.14285278, 0.17984009,
+ 0.20492554, 0.2244873, 0.23187256, 0.23144531, 0.21932983,
+ 0.19973755, 0.17034912, 0.13473511, 0.09274292, 0.04699707,
+ -0.0009765625, -0.048919678, -0.095123291, -0.13772583,
+ -0.17495728, -0.20523071, -0.22750854, -0.24038696, -0.24423218,
+ -0.23831177, -0.22305298, -0.19918823, -0.16748047, -0.12948608,
+ -0.086730957, -0.041015625, 0.0060424805, 0.052276611,
+ 0.09588623, 0.13513184, 0.16827393, 0.19415283, 0.21182251,
+ 0.22024536, 0.2194519, 0.20932007, 0.19030762, 0.1633606,
+ 0.1295166, 0.090118408, 0.046844482, 0.0015563965, -0.0440979,
+ -0.088043213, -0.12854004, -0.16415405, -0.19317627,
+ -0.21456909, -0.22750854, -0.23141479, -0.22619629, -0.21224976,
+ -0.18994141, -0.1602478, -0.12457275, -0.084228516,
+ -0.040893555, 0.0035400391, 0.047485352, 0.08895874, 0.12628174,
+ 0.15808105, 0.1829834, 0.19998169, 0.20849609, 0.2081604,
+ 0.19891357, 0.18139648, 0.15621948, 0.12438965, 0.087371826,
+ 0.046569824, 0.0036621094, -0.039489746, -0.081207275,
+ -0.11987305, -0.15371704, -0.18154907, -0.20220947, -0.21481323,
+ -0.21896362, -0.21453857, -0.20159912, -0.18084717, -0.15319824,
+ -0.11959839, -0.081604004, -0.040771484, 0.001373291,
+ 0.04296875, 0.082275391, 0.11791992, 0.14828491, 0.17214966,
+ 0.18869019, 0.19711304, 0.19714355, 0.18896484, 0.17272949,
+ 0.14916992, 0.11941528, 0.084564209, 0.046081543, 0.005645752,
+ -0.03527832, -0.074890137, -0.11154175, -0.14389038,
+ -0.17053223, -0.19036865, -0.20272827, -0.20709229, -0.20321655,
+ -0.19143677, -0.17221069, -0.14624023, -0.11477661,
+ -0.079040527, -0.040435791, -0.00064086914, 0.038726807,
+ 0.076171875, 0.11004639, 0.13900757, 0.16201782, 0.177948,
+ 0.18634033, 0.18685913, 0.17938232, 0.16436768, 0.14254761,
+ 0.11459351, 0.081817627, 0.045623779, 0.0073242188,
+ -0.031402588, -0.068939209, -0.10388184, -0.13473511,
+ -0.16018677, -0.17938232, -0.19143677, -0.19589233, -0.19271851,
+ -0.1819458, -0.16400146, -0.13980103, -0.11026001, -0.076507568,
+ -0.040130615, -0.0024414062, 0.034973145, 0.070465088,
+ 0.10275269, 0.13052368, 0.15255737, 0.1680603, 0.17642212,
+ 0.17718506, 0.17056274, 0.15673828, 0.13626099, 0.1100769,
+ 0.079284668, 0.045013428, 0.0088195801, -0.027862549,
+ -0.06362915, -0.096862793, -0.12631226, -0.15081787,
+ -0.16931152, -0.18109131, -0.18579102, -0.18310547, -0.17321777,
+ -0.15661621, -0.13391113, -0.10604858, -0.074310303,
+ -0.039794922, -0.0040588379, 0.031433105, 0.065338135,
+ 0.096191406, 0.1227417, 0.14398193, 0.15905762, 0.16726685,
+ 0.16845703, 0.16247559, 0.14962769, 0.1305542, 0.10595703,
+ 0.076812744, 0.044494629, 0.010162354, -0.024719238,
+ -0.058685303, -0.090423584, -0.11865234, -0.14208984,
+ -0.15994263, -0.17150879, -0.1762085, -0.17401123, -0.16500854,
+ -0.1494751, -0.12820435, -0.10205078, -0.071990967,
+ -0.039398193, -0.0055847168, 0.028198242, 0.060424805,
+ 0.089782715, 0.11523438, 0.13565063, 0.15014648, 0.1583252,
+ 0.159729, 0.15438843, 0.14260864, 0.12475586, 0.10162354,
+ 0.074310303, 0.043762207, 0.011291504, -0.02166748,
+ -0.053924561, -0.084136963, -0.1109314, -0.13342285,
+ -0.15060425, -0.16177368, -0.16659546, -0.16485596, -0.15655518,
+ -0.14221191, -0.12234497, -0.097747803, -0.069519043,
+ -0.038818359, -0.0067749023, 0.025115967, 0.055633545,
+ 0.083618164, 0.1078186, 0.12731934, 0.14138794, 0.14935303,
+ 0.15097046, 0.14630127, 0.13537598, 0.11877441, 0.097259521,
+ 0.071563721, 0.04284668, 0.012329102, -0.018859863,
+ -0.049346924, -0.077880859, -0.10342407, -0.12484741,
+ -0.14126587, -0.15213013, -0.1569519, -0.1555481, -0.14807129,
+ -0.13479614, -0.11624146, -0.093322754, -0.066864014,
+ -0.03793335, -0.0078735352, 0.022247314, 0.051116943,
+ 0.077575684, 0.10055542, 0.11920166, 0.13265991, 0.14047241,
+ 0.14233398, 0.13812256, 0.12814331, 0.11282349, 0.092681885,
+ 0.068695068, 0.0418396, 0.013122559, -0.016174316, -0.044891357,
+ -0.071929932, -0.096038818, -0.11636353, -0.1321106,
+ -0.14254761, -0.14733887, -0.14633179, -0.13955688, -0.12728882,
+ -0.11016846, -0.088745117, -0.063995361, -0.036987305,
+ -0.0087280273, 0.019592285, 0.04675293, 0.071746826,
+ 0.093536377, 0.11120605, 0.12411499, 0.13171387, 0.13366699,
+ 0.1300354, 0.12094116, 0.10671997, 0.088104248, 0.065765381,
+ 0.040588379, 0.013763428, -0.013702393, -0.040740967,
+ -0.066101074, -0.088897705, -0.10818481, -0.12310791,
+ -0.13314819, -0.13793945, -0.13720703, -0.13113403, -0.11993408,
+ -0.10400391, -0.084136963, -0.061126709, -0.035858154,
+ -0.0094299316, 0.017059326, 0.042633057, 0.066131592,
+ 0.086669922, 0.10345459, 0.11572266, 0.12304688, 0.12521362,
+ 0.12203979, 0.11373901, 0.10070801, 0.083435059, 0.062652588,
+ 0.039276123, 0.014221191, -0.011505127, -0.036773682,
+ -0.060638428, -0.082092285, -0.10025024, -0.11444092,
+ -0.12408447, -0.12875366, -0.12835693, -0.1229248, -0.11260986,
+ -0.097991943, -0.079620361, -0.058166504, -0.034698486,
+ -0.010040283, 0.014770508, 0.038665771, 0.06072998, 0.080108643,
+ 0.095916748, 0.10760498, 0.11471558, 0.11691284, 0.11419678,
+ 0.10671997, 0.094726562, 0.078765869, 0.059570312, 0.037811279,
+ 0.014465332, -0.0094604492, -0.03314209, -0.055480957,
+ -0.075592041, -0.09274292, -0.10617065, -0.11532593, -0.1199646,
+ -0.11981201, -0.11489868, -0.1055603, -0.092102051,
+ -0.075073242, -0.055267334, -0.033447266, -0.010467529,
+ 0.012634277, 0.035003662, 0.05569458, 0.073822021, 0.088745117,
+ 0.099853516, 0.10665894, 0.10894775, 0.10665894, 0.099853516,
+ 0.088897705, 0.07421875, 0.056427002, 0.0362854, 0.01461792,
+ -0.0077209473, -0.029754639, -0.050628662, -0.069519043,
+ -0.085571289, -0.098236084, -0.10702515, -0.11151123,
+ -0.11157227, -0.1072998, -0.098754883, -0.086364746,
+ -0.070739746, -0.052368164, -0.032104492, -0.01083374,
+ 0.01071167, 0.031585693, 0.050872803, 0.067932129, 0.081970215,
+ 0.092437744, 0.098999023, 0.10134888, 0.099395752, 0.093292236,
+ 0.083282471, 0.069763184, 0.053375244, 0.034729004, 0.01461792,
+ -0.0061035156, -0.026641846, -0.046142578, -0.063751221,
+ -0.078857422, -0.090820312, -0.099121094, -0.10351562,
+ -0.1038208, -0.099975586, -0.092254639, -0.080932617,
+ -0.066497803, -0.049560547, -0.030822754, -0.011016846,
+ 0.0090026855, 0.028411865, 0.046478271, 0.062408447,
+ 0.075592041, 0.085571289, 0.091827393, 0.094177246, 0.092590332,
+ 0.087097168, 0.077941895, 0.065582275, 0.050445557, 0.033203125,
+ 0.014587402, -0.0046691895, -0.023803711, -0.04196167,
+ -0.05847168, -0.072662354, -0.083892822, -0.091827393,
+ -0.096099854, -0.0965271, -0.093170166, -0.086181641,
+ -0.075775146, -0.062530518, -0.046875, -0.029510498,
+ -0.011169434, 0.0074462891, 0.02557373, 0.042419434,
+ 0.057342529, 0.069793701, 0.079162598, 0.085174561, 0.087585449,
+ 0.086273193, 0.081329346, 0.073028564, 0.061645508, 0.047668457,
+ 0.031768799, 0.014465332, -0.0034484863, -0.021209717,
+ -0.038208008, -0.053649902, -0.066925049, -0.077575684,
+ -0.085113525, -0.089233398, -0.08984375, -0.086914062,
+ -0.080535889, -0.071075439, -0.058837891, -0.044342041,
+ -0.028289795, -0.011230469, 0.0061340332, 0.023010254,
+ 0.038787842, 0.05279541, 0.064483643, 0.073394775, 0.079162598,
+ 0.081542969, 0.080535889, 0.07611084, 0.068481445, 0.058044434,
+ 0.045166016, 0.03036499, 0.014343262, -0.0023193359,
+ -0.018951416, -0.034820557, -0.049285889, -0.061828613,
+ -0.071868896, -0.079040527, -0.083099365, -0.083831787,
+ -0.081237793, -0.075500488, -0.066772461, -0.055480957,
+ -0.042114258, -0.027130127, -0.011230469, 0.0049133301,
+ 0.020751953, 0.035552979, 0.048706055, 0.059783936, 0.068237305,
+ 0.073760986, 0.076202393, 0.075408936, 0.07144165, 0.064483643,
+ 0.054840088, 0.042877197, 0.029174805, 0.014190674,
+ -0.0014038086, -0.016937256, -0.031829834, -0.045501709,
+ -0.057312012, -0.066864014, -0.073760986, -0.077667236,
+ -0.078552246, -0.076293945, -0.071044922, -0.063018799,
+ -0.052581787, -0.040100098, -0.026153564, -0.011291504,
+ 0.00390625, 0.018768311, 0.032714844, 0.045196533, 0.05569458,
+ 0.063751221, 0.069152832, 0.071563721, 0.070983887, 0.067443848,
+ 0.061035156, 0.052093506, 0.040985107, 0.028137207, 0.014099121,
+ -0.00051879883, -0.015197754, -0.029296875, -0.042205811,
+ -0.053466797, -0.062591553, -0.069213867, -0.073120117,
+ -0.07409668, -0.072113037, -0.067321777, -0.059906006,
+ -0.050140381, -0.038482666, -0.025360107, -0.011322021,
+ 0.0029907227, 0.017089844, 0.03036499, 0.042236328, 0.052276611,
+ 0.060089111, 0.0652771, 0.067749023, 0.067382812, 0.064117432,
+ 0.058227539, 0.04989624, 0.039428711, 0.02734375, 0.014099121,
+ 0.00021362305, -0.013702393, -0.027099609, -0.039459229,
+ -0.050231934, -0.058990479, -0.065429688, -0.069244385,
+ -0.0703125, -0.068634033, -0.064208984, -0.057250977,
+ -0.048126221, -0.037109375, -0.024688721, -0.011413574,
+ 0.0021972656, 0.015625, 0.028259277, 0.039642334, 0.049285889,
+ 0.056793213, 0.061889648, 0.064361572, 0.064117432, 0.061187744,
+ 0.05569458, 0.047851562, 0.03805542, 0.026611328, 0.014038086,
+ 0.00088500977, -0.012329102, -0.025146484, -0.03692627,
+ -0.047241211, -0.055725098, -0.061920166, -0.065673828,
+ -0.066864014, -0.065338135, -0.061279297, -0.05480957,
+ -0.046203613, -0.035797119, -0.024078369, -0.011474609,
+ 0.0014648438, 0.014251709, 0.02633667, 0.037200928, 0.046447754,
+ 0.053741455, 0.05871582, 0.061157227, 0.061096191, 0.058441162,
+ 0.053314209, 0.04598999, 0.036712646, 0.025909424, 0.014007568,
+ 0.0014953613, -0.011138916, -0.02331543, -0.034576416,
+ -0.044494629, -0.052612305, -0.058654785, -0.06237793,
+ -0.063598633, -0.062316895, -0.058563232, -0.052490234,
+ -0.044403076, -0.034606934, -0.023468018, -0.011505127,
+ 0.00079345703, 0.013000488, 0.024505615, 0.034942627,
+ 0.04385376, 0.050872803, 0.05569458, 0.058197021, 0.058227539,
+ 0.055786133, 0.051055908, 0.044158936, 0.035430908, 0.02520752,
+ 0.013916016, 0.0020141602, -0.009979248, -0.021636963,
+ -0.032440186, -0.041931152, -0.04977417, -0.055664062,
+ -0.059265137, -0.060577393, -0.05947876, -0.055999756,
+ -0.050354004, -0.042724609, -0.033447266, -0.022918701,
+ -0.011566162, 0.00018310547, 0.011810303, 0.022827148,
+ 0.032836914, 0.041381836, 0.048187256, 0.05291748, 0.055389404,
+ 0.055541992, 0.053344727, 0.048919678, 0.042449951, 0.034210205,
+ 0.024536133, 0.013793945, 0.0025024414, -0.008972168,
+ -0.020080566, -0.030426025, -0.039581299, -0.047149658,
+ -0.05279541, -0.056396484, -0.057739258, -0.056793213,
+ -0.053619385, -0.048309326, -0.041137695, -0.03237915,
+ -0.022369385, -0.011566162, -0.00039672852, 0.01071167,
+ 0.02130127, 0.030883789, 0.039123535, 0.045684814, 0.050292969,
+ 0.052764893, 0.053039551, 0.051055908, 0.046936035, 0.040863037,
+ 0.033081055, 0.023895264, 0.01373291, 0.0029296875,
+ -0.0079956055, -0.01864624, -0.028564453, -0.037353516,
+ -0.044647217, -0.050170898, -0.05368042, -0.055084229,
+ -0.054290771, -0.051361084, -0.046386719, -0.039642334,
+ -0.031311035, -0.021820068, -0.011566162, -0.00088500977,
+ 0.0097351074, 0.019836426, 0.029083252, 0.037017822,
+ 0.043365479, 0.04788208, 0.050323486, 0.050689697, 0.048919678,
+ 0.045074463, 0.039367676, 0.032012939, 0.02331543, 0.01361084,
+ 0.0033569336, -0.0071105957, -0.017303467, -0.026794434,
+ -0.035247803, -0.042327881, -0.047668457, -0.051147461,
+ -0.052581787, -0.0519104, -0.049224854, -0.044586182,
+ -0.03817749, -0.030334473, -0.021331787, -0.011505127,
+ -0.0013427734, 0.0088195801, 0.01852417, 0.027374268,
+ 0.03503418, 0.04119873, 0.045593262, 0.048034668, 0.048492432,
+ 0.046905518, 0.043334961, 0.037963867, 0.031005859, 0.022735596,
+ 0.013519287, 0.003692627, -0.0062866211, -0.016021729,
+ -0.025177002, -0.033325195, -0.040130615, -0.045349121,
+ -0.04876709, -0.050231934, -0.049713135, -0.047210693,
+ -0.04284668, -0.036834717, -0.029388428, -0.020812988,
+ -0.011474609, -0.001739502, 0.0079650879, 0.017272949,
+ 0.025787354, 0.033203125, 0.039154053, 0.043457031, 0.045898438,
+ 0.046417236, 0.04498291, 0.041687012, 0.036621094, 0.030029297,
+ 0.022186279, 0.013397217, 0.0040588379, -0.0054931641,
+ -0.014862061, -0.023651123, -0.031494141, -0.038085938,
+ -0.043151855, -0.046508789, -0.048034668, -0.047607422,
+ -0.045318604, -0.041259766, -0.035552979, -0.0284729,
+ -0.020324707, -0.011413574, -0.0021057129, 0.0071716309,
+ 0.016113281, 0.02432251, 0.031463623, 0.037231445, 0.041442871,
+ 0.043884277, 0.044464111, 0.043212891, 0.040100098, 0.035339355,
+ 0.02911377, 0.021636963, 0.013275146, 0.0043640137,
+ -0.0048217773, -0.013763428, -0.022216797, -0.029785156,
+ -0.03616333, -0.041107178, -0.044403076, -0.045928955,
+ -0.045623779, -0.043548584, -0.039703369, -0.034332275,
+ -0.027648926, -0.019866943, -0.011352539, -0.0024719238,
+ 0.0064697266, 0.015045166, 0.022918701, 0.029815674,
+ 0.035430908, 0.039520264, 0.04196167, 0.042602539, 0.041473389,
+ 0.038604736, 0.034118652, 0.02822876, 0.021148682, 0.013153076,
+ 0.0046081543, -0.0041503906, -0.012756348, -0.020874023,
+ -0.028167725, -0.034332275, -0.039154053, -0.042388916,
+ -0.043945312, -0.043762207, -0.041809082, -0.038238525,
+ -0.033172607, -0.026794434, -0.01940918, -0.011291504,
+ -0.0027770996, 0.0057678223, 0.014007568, 0.021636963,
+ 0.028259277, 0.033691406, 0.037719727, 0.040100098, 0.04083252,
+ 0.039825439, 0.03717041, 0.032958984, 0.027374268, 0.020599365,
+ 0.013000488, 0.0048522949, -0.0035400391, -0.011810303,
+ -0.019592285, -0.026641846, -0.032623291, -0.03729248,
+ -0.040466309, -0.042053223, -0.041931152, -0.04019165,
+ -0.036834717, -0.032012939, -0.026000977, -0.018951416,
+ -0.011199951, -0.0030517578, 0.0051269531, 0.013061523,
+ 0.020385742, 0.026794434, 0.032073975, 0.035980225, 0.038360596,
+ 0.039123535, 0.038238525, 0.035766602, 0.031799316, 0.026489258,
+ 0.020080566, 0.0128479, 0.0050354004, -0.0029907227,
+ -0.010894775, -0.018432617, -0.02520752, -0.030975342,
+ -0.035522461, -0.038635254, -0.040222168, -0.04019165,
+ -0.038574219, -0.035430908, -0.030914307, -0.025177002,
+ -0.018493652, -0.011108398, -0.0032958984, 0.0045471191,
+ 0.012145996, 0.019195557, 0.025390625, 0.030487061, 0.034301758,
+ 0.036651611, 0.037475586, 0.036712646, 0.034423828, 0.030670166,
+ 0.025665283, 0.019592285, 0.012664795, 0.0052185059,
+ -0.0024719238, -0.010070801, -0.017272949, -0.023803711,
+ -0.029388428, -0.033813477, -0.036865234, -0.038452148,
+ -0.038513184, -0.03704834, -0.034118652, -0.029815674,
+ -0.024383545, -0.018035889, -0.010955811, -0.0035400391,
+ 0.0039978027, 0.011291504, 0.018066406, 0.024047852,
+ 0.028991699, 0.032684326, 0.035003662, 0.035858154, 0.035217285,
+ 0.033081055, 0.029571533, 0.024810791, 0.019042969, 0.012481689,
+ 0.0053405762, -0.0020141602, -0.0092773438, -0.016204834,
+ -0.022491455, -0.027862549, -0.032165527, -0.03515625,
+ -0.036743164, -0.036865234, -0.035552979, -0.032775879,
+ -0.028778076, -0.023620605, -0.017547607, -0.01083374,
+ -0.0037231445, 0.0034790039, 0.010467529, 0.016967773,
+ 0.022735596, 0.027526855, 0.03112793, 0.033416748, 0.034301758,
+ 0.033752441, 0.031768799, 0.0284729, 0.023986816, 0.01852417,
+ 0.012237549, 0.0054626465, -0.0015563965, -0.0085449219,
+ -0.015167236, -0.021209717, -0.026428223, -0.030578613,
+ -0.033508301, -0.035095215, -0.03527832, -0.034057617,
+ -0.031524658, -0.027709961, -0.022827148, -0.017089844,
+ -0.010681152, -0.00390625, 0.0029907227, 0.0096740723,
+ 0.015960693, 0.021484375, 0.026123047, 0.029632568, 0.031890869,
+ 0.032806396, 0.032348633, 0.030517578, 0.027435303, 0.023193359,
+ 0.017974854, 0.012023926, 0.0055847168, -0.001159668,
+ -0.0078125, -0.014190674, -0.020019531, -0.025024414,
+ -0.029052734, -0.031921387, -0.033477783, -0.033752441,
+ -0.032653809, -0.030273438, -0.026702881, -0.022094727,
+ -0.016601562, -0.010498047, -0.0040588379, 0.002532959,
+ 0.008972168, 0.014953613, 0.020324707, 0.024780273, 0.028198242,
+ 0.030426025, 0.031341553, 0.030975342, 0.029296875, 0.026397705,
+ 0.022399902, 0.017486572, 0.011810303, 0.005645752,
+ -0.00076293945, -0.0071411133, -0.013275146, -0.018859863,
+ -0.023681641, -0.027587891, -0.03036499, -0.031951904,
+ -0.03225708, -0.03125, -0.029052734, -0.025695801, -0.02130127,
+ -0.016113281, -0.010345459, -0.0041503906, 0.0021362305,
+ 0.0082702637, 0.014068604, 0.019195557, 0.023498535,
+ 0.026824951, 0.028991699, 0.029937744, 0.029663086, 0.028106689,
+ 0.025390625, 0.021636963, 0.016967773, 0.011566162,
+ 0.0057067871, -0.00039672852, -0.0065307617, -0.012359619,
+ -0.01776123, -0.022399902, -0.026184082, -0.028900146,
+ -0.030456543, -0.030792236, -0.029937744, -0.027862549,
+ -0.024688721, -0.020568848, -0.015655518, -0.010131836,
+ -0.0042419434, 0.001739502, 0.0076599121, 0.013183594,
+ 0.018127441, 0.022277832, 0.025512695, 0.027618408, 0.028625488,
+ 0.028381348, 0.026977539, 0.02444458, 0.020874023, 0.016448975,
+ 0.011352539, 0.0057373047, -9.1552734e-05, -0.0059204102,
+ -0.011535645, -0.016693115, -0.021179199, -0.024841309,
+ -0.02746582, -0.029022217, -0.029418945, -0.028625488,
+ -0.026702881, -0.023742676, -0.019866943, -0.015197754,
+ -0.0099487305, -0.0043334961, 0.0014038086, 0.007019043,
+ 0.012329102, 0.017089844, 0.021087646, 0.024230957, 0.026306152,
+ 0.027282715, 0.027130127, 0.025848389, 0.023468018, 0.020111084,
+ 0.015930176, 0.011077881, 0.0057678223, 0.00021362305,
+ -0.0053710938, -0.010742188, -0.015686035, -0.020019531,
+ -0.023529053, -0.026123047, -0.027648926, -0.028076172,
+ -0.027374268, -0.025604248, -0.022827148, -0.019134521,
+ -0.01473999, -0.0097351074, -0.0043945312, 0.0010681152,
+ 0.0064697266, 0.011535645, 0.016113281, 0.019989014,
+ 0.023010254, 0.025024414, 0.026031494, 0.025939941, 0.024749756,
+ 0.02255249, 0.019378662, 0.015411377, 0.01083374, 0.0057678223,
+ 0.00045776367, -0.0048522949, -0.010009766, -0.01473999,
+ -0.018890381, -0.02230835, -0.024810791, -0.02633667,
+ -0.026794434, -0.026184082, -0.024536133, -0.021911621,
+ -0.018463135, -0.014251709, -0.009552002, -0.0044555664,
+ 0.00076293945, 0.0059204102, 0.010772705, 0.015167236,
+ 0.018890381, 0.021820068, 0.023803711, 0.024810791, 0.024749756,
+ 0.023681641, 0.021606445, 0.01864624, 0.014923096, 0.010559082,
+ 0.0057373047, 0.0007019043, -0.0043945312, -0.0093078613,
+ -0.01385498, -0.017852783, -0.021118164, -0.02355957,
+ -0.025054932, -0.025543213, -0.024993896, -0.023498535,
+ -0.021057129, -0.01776123, -0.013824463, -0.0093383789,
+ -0.004486084, 0.00048828125, 0.0054016113, 0.010040283,
+ 0.014251709, 0.017852783, 0.020690918, 0.022613525, 0.023620605,
+ 0.023620605, 0.022644043, 0.020721436, 0.017944336, 0.014404297,
+ 0.010284424, 0.0057373047, 0.00091552734, -0.0039367676,
+ -0.0086364746, -0.013000488, -0.016815186, -0.019989014,
+ -0.022338867, -0.023803711, -0.02432251, -0.023864746,
+ -0.022460938, -0.020172119, -0.017089844, -0.013366699,
+ -0.0090942383, -0.0045166016, 0.00021362305, 0.0049133301,
+ 0.0093688965, 0.013397217, 0.016845703, 0.019592285,
+ 0.021484375, 0.022491455, 0.022521973, 0.021636963, 0.019866943,
+ 0.017242432, 0.013916016, 0.010009766, 0.0056762695,
+ 0.0010986328, -0.0035095215, -0.0079956055, -0.012176514,
+ -0.015838623, -0.018890381, -0.021179199, -0.022613525,
+ -0.023162842, -0.022766113, -0.021484375, -0.019348145,
+ -0.016418457, -0.012908936, -0.0088806152, -0.0045166016, 0,
+ 0.0044555664, 0.0087280273, 0.012573242, 0.015899658,
+ 0.01852417, 0.020385742, 0.021362305, 0.021484375, 0.0206604,
+ 0.019012451, 0.016571045, 0.013427734, 0.0097045898,
+ 0.0056152344, 0.0012817383, -0.003112793, -0.0073852539,
+ -0.011383057, -0.014923096, -0.017822266, -0.020050049,
+ -0.021453857, -0.022003174, -0.021697998, -0.020507812,
+ -0.018493652, -0.015777588, -0.012451172, -0.0086364746,
+ -0.004486084, -0.00021362305, 0.0040588379, 0.0080871582,
+ 0.011779785, 0.014984131, 0.01751709, 0.019317627, 0.020294189,
+ 0.020446777, 0.019714355, 0.018188477, 0.015869141, 0.012908936,
+ 0.0094299316, 0.0055541992, 0.0014343262, -0.002746582,
+ -0.0068359375, -0.010620117, -0.014007568, -0.016815186,
+ -0.018981934, -0.020355225, -0.020904541, -0.020629883,
+ -0.019561768, -0.017700195, -0.015136719, -0.011993408,
+ -0.008392334, -0.0044555664, -0.00039672852, 0.0036621094,
+ 0.0075073242, 0.011047363, 0.014099121, 0.016540527,
+ 0.018280029, 0.019256592, 0.01940918, 0.018768311, 0.017364502,
+ 0.015197754, 0.012420654, 0.0091247559, 0.0054626465,
+ 0.0015563965, -0.0024108887, -0.0062866211, -0.0099182129,
+ -0.013153076, -0.015838623, -0.017913818, -0.019256592,
+ -0.019836426, -0.019622803, -0.018615723, -0.016876221,
+ -0.01449585, -0.011535645, -0.0081176758, -0.0044250488,
+ -0.00057983398, 0.0032958984, 0.0069580078, 0.010314941,
+ 0.013244629, 0.015594482, 0.017272949, 0.018249512, 0.018432617,
+ 0.017852783, 0.016540527, 0.014556885, 0.011932373,
+ 0.0088195801, 0.0053710938, 0.0016784668, -0.0021057129,
+ -0.0057678223, -0.0092468262, -0.012329102, -0.014892578,
+ -0.016906738, -0.018188477, -0.018768311, -0.018615723,
+ -0.017700195, -0.016082764, -0.01385498, -0.011047363,
+ -0.0078430176, -0.0043640137, -0.0007019043, 0.0029296875,
+ 0.006439209, 0.0096435547, 0.012451172, 0.014678955,
+ 0.016326904, 0.017272949, 0.017486572, 0.016967773, 0.01574707,
+ 0.013885498, 0.011444092, 0.0085144043, 0.0052490234,
+ 0.0017700195, -0.0018005371, -0.0053100586, -0.0085754395,
+ -0.011535645, -0.014007568, -0.015899658, -0.017181396,
+ -0.01776123, -0.01763916, -0.016815186, -0.015319824,
+ -0.013214111, -0.0105896, -0.007598877, -0.0042724609,
+ -0.00082397461, 0.0026245117, 0.0059509277, 0.0090026855,
+ 0.011657715, 0.013824463, 0.015380859, 0.016296387, 0.016540527,
+ 0.016082764, 0.014984131, 0.013244629, 0.010955811,
+ 0.0082092285, 0.0051269531, 0.0018310547, -0.0015258789,
+ -0.0048522949, -0.0079650879, -0.010772705, -0.013122559,
+ -0.014953613, -0.016204834, -0.01675415, -0.016693115,
+ -0.015930176, -0.014556885, -0.01260376, -0.010131836,
+ -0.0072937012, -0.0042114258, -0.00094604492, 0.0023193359,
+ 0.0054626465, 0.0083618164, 0.010894775, 0.012939453,
+ 0.014465332, 0.015380859, 0.015625, 0.015228271, 0.014221191,
+ 0.012573242, 0.010467529, 0.0078735352, 0.0049743652,
+ 0.0018920898, -0.0012817383, -0.0044250488, -0.0073852539,
+ -0.010040283, -0.012298584, -0.014038086, -0.015228271,
+ -0.015808105, -0.01574707, -0.015075684, -0.013793945,
+ -0.011962891, -0.0096740723, -0.007019043, -0.004119873,
+ -0.0010375977, 0.0020446777, 0.0050048828, 0.0077514648,
+ 0.010162354, 0.012115479, 0.013580322, 0.014465332, 0.01473999,
+ 0.014373779, 0.013458252, 0.011962891, 0.0099487305,
+ 0.0075378418, 0.0048522949, 0.0019226074, -0.0010681152,
+ -0.0040283203, -0.0068054199, -0.0093383789, -0.011474609,
+ -0.013153076, -0.014312744, -0.014862061, -0.014862061,
+ -0.014251709, -0.013061523, -0.011352539, -0.0092468262,
+ -0.0067443848, -0.0039978027, -0.0011291504, 0.0017700195,
+ 0.0045776367, 0.0071716309, 0.0094604492, 0.011322021,
+ 0.012695312, 0.013580322, 0.01385498, 0.013549805, 0.012695312,
+ 0.011322021, 0.0094604492, 0.007232666, 0.0046691895,
+ 0.001953125, -0.00085449219, -0.0036621094, -0.0062866211,
+ -0.0086669922, -0.010681152, -0.012298584, -0.013397217,
+ -0.013946533, -0.013946533, -0.013397217, -0.012329102,
+ -0.010772705, -0.0087585449, -0.006439209, -0.00390625,
+ -0.0011901855, 0.0015258789, 0.0041809082, 0.0066223145,
+ 0.0087585449, 0.010559082, 0.011871338, 0.012695312,
+ 0.013000488, 0.012756348, 0.011962891, 0.010681152, 0.008972168,
+ 0.0068664551, 0.0045166016, 0.001953125, -0.00067138672,
+ -0.0032958984, -0.0057678223, -0.0079956055, -0.0099182129,
+ -0.011444092, -0.012512207, -0.013061523, -0.013092041,
+ -0.01260376, -0.01159668, -0.010162354, -0.0083007812,
+ -0.0061645508, -0.0037536621, -0.0012512207, 0.0013122559,
+ 0.0037841797, 0.0061035156, 0.0081176758, 0.0097961426,
+ 0.011077881, 0.011871338, 0.012145996, 0.011932373, 0.011230469,
+ 0.010070801, 0.0084838867, 0.0065307617, 0.0043334961,
+ 0.001953125, -0.00051879883, -0.0029602051, -0.005279541,
+ -0.0073852539, -0.009185791, -0.010620117, -0.011627197,
+ -0.012176514, -0.012237549, -0.011779785, -0.010894775,
+ -0.009552002, -0.0078430176, -0.0058288574, -0.0036315918,
+ -0.0012817383, 0.0011291504, 0.0034179688, 0.0055847168,
+ 0.0075073242, 0.0090637207, 0.010284424, 0.011047363,
+ 0.011322021, 0.011169434, 0.010528564, 0.0094604492,
+ 0.0079956055, 0.0061950684, 0.0041503906, 0.0019226074,
+ -0.00036621094, -0.0026550293, -0.0048217773, -0.0067749023,
+ -0.0084838867, -0.0098266602, -0.010803223, -0.011322021,
+ -0.011383057, -0.010986328, -0.010192871, -0.0089416504,
+ -0.0073852539, -0.0055236816, -0.0034790039, -0.0012817383,
+ 0.00094604492, 0.0030822754, 0.0050964355, 0.0068969727,
+ 0.008392334, 0.0095214844, 0.010223389, 0.010528564,
+ 0.010406494, 0.0098266602, 0.0088500977, 0.0075073242,
+ 0.0058288574, 0.0039672852, 0.0018920898, -0.00021362305,
+ -0.0023498535, -0.0043640137, -0.0061950684, -0.0077819824,
+ -0.0090637207, -0.009979248, -0.010467529, -0.010559082,
+ -0.010223389, -0.0094909668, -0.0083618164, -0.0069274902,
+ -0.0052185059, -0.0032958984, -0.0012817383, 0.00076293945,
+ 0.0027770996, 0.0046386719, 0.0063171387, 0.0076904297,
+ 0.0087585449, 0.0094604492, 0.0097351074, 0.0096435547,
+ 0.0091247559, 0.0082397461, 0.0069885254, 0.0054931641,
+ 0.0037536621, 0.0018615723, -0.00012207031, -0.0020751953,
+ -0.0039367676, -0.005645752, -0.0071411133, -0.0083312988,
+ -0.009185791, -0.0096740723, -0.009765625, -0.0094604492,
+ -0.0087890625, -0.0077819824, -0.006439209, -0.0048828125,
+ -0.0031433105, -0.0012817383, 0.00061035156, 0.0024719238,
+ 0.0042114258, 0.0057373047, 0.0070495605, 0.008026123,
+ 0.0086975098, 0.008972168, 0.0088806152, 0.0084228516,
+ 0.0076293945, 0.0065002441, 0.0051269531, 0.0035400391,
+ 0.0018005371, 0, -0.0018005371, -0.0035400391, -0.0051269531,
+ -0.0065002441, -0.007598877, -0.008392334, -0.0088500977,
+ -0.008972168, -0.0086975098, -0.0081176758, -0.0071716309,
+ -0.0059814453, -0.0045471191, -0.0029602051, -0.0012512207,
+ 0.00048828125, 0.0021972656, 0.0037841797, 0.0052185059,
+ 0.0064086914, 0.0073242188, 0.0079345703, 0.0082092285,
+ 0.0081481934, 0.0077514648, 0.007019043, 0.0060119629,
+ 0.0047607422, 0.0032958984, 0.0017089844, 6.1035156e-05,
+ -0.0015869141, -0.0031738281, -0.0046081543, -0.0058898926,
+ -0.0068969727, -0.0076293945, -0.0080566406, -0.0081787109,
+ -0.0079650879, -0.0074157715, -0.0065917969, -0.0055236816,
+ -0.0042114258, -0.0027770996, -0.0012207031, 0.00036621094,
+ 0.0019226074, 0.0033874512, 0.0046691895, 0.0057678223,
+ 0.0066223145, 0.0072021484, 0.0074768066, 0.0074157715,
+ 0.0070800781, 0.0064086914, 0.0055236816, 0.0043945312,
+ 0.0030822754, 0.0016479492, 0.00015258789, -0.001373291,
+ -0.0028076172, -0.004119873, -0.005279541, -0.0062255859,
+ -0.0068969727, -0.0072937012, -0.0074157715, -0.007232666,
+ -0.0067443848, -0.0060119629, -0.0050354004, -0.0038757324,
+ -0.0025634766, -0.001159668, 0.0002746582, 0.0016784668,
+ 0.0029907227, 0.0041809082, 0.0051879883, 0.0059509277,
+ 0.0064697266, 0.0067443848, 0.0067138672, 0.0064086914,
+ 0.0058288574, 0.0050048828, 0.0039978027, 0.0028381348,
+ 0.0015258789, 0.00018310547, -0.001159668, -0.0024719238,
+ -0.0036621094, -0.004699707, -0.0055541992, -0.0061645508,
+ -0.0065307617, -0.006652832, -0.0065002441, -0.006072998,
+ -0.0054321289, -0.0045776367, -0.0035400391, -0.0023498535,
+ -0.0010986328, 0.00018310547, 0.0014343262, 0.0026245117,
+ 0.003692627, 0.0046081543, 0.0053100586, 0.0057678223,
+ 0.0060119629, 0.0060119629, 0.0057373047, 0.0052185059,
+ 0.0045166016, 0.0036315918, 0.0025634766, 0.0014343262,
+ 0.00024414062, -0.0009765625, -0.0021362305, -0.0032043457,
+ -0.0041503906, -0.0049133301, -0.0054626465, -0.0057983398,
+ -0.0059204102, -0.0057678223, -0.0054321289, -0.0048522949,
+ -0.0040893555, -0.0031738281, -0.0021362305, -0.0010375977,
+ 9.1552734e-05, 0.0012207031, 0.0022583008, 0.0032348633,
+ 0.0040283203, 0.0046691895, 0.0050964355, 0.0053100586,
+ 0.0053100586, 0.005065918, 0.0046386719, 0.0039978027,
+ 0.0032348633, 0.0023193359, 0.0013122559, 0.00024414062,
+ -0.00079345703, -0.0018310547, -0.0027770996, -0.0036010742,
+ -0.0042724609, -0.0047607422, -0.005065918, -0.0051879883,
+ -0.005065918, -0.0047607422, -0.0042724609, -0.0036010742,
+ -0.0028076172, -0.0019226074, -0.00094604492, 3.0517578e-05,
+ 0.0010070801, 0.0019226074, 0.0027770996, 0.0034790039,
+ 0.0040283203, 0.0043945312, 0.0046081543, 0.0046081543,
+ 0.0044250488, 0.0040283203, 0.0035095215, 0.0028381348,
+ 0.0020446777, 0.001159668, 0.0002746582, -0.00064086914,
+ -0.0015258789, -0.0023498535, -0.0030517578, -0.0036621094,
+ -0.0040893555, -0.0043640137, -0.0044555664, -0.0043640137,
+ -0.0040893555, -0.003692627, -0.003112793, -0.0024414062,
+ -0.0016784668, -0.00085449219, 0, 0.00082397461, 0.0016174316,
+ 0.0023193359, 0.0029296875, 0.0034179688, 0.0037536621,
+ 0.00390625, 0.00390625, 0.0037536621, 0.0034484863,
+ 0.0029907227, 0.0024414062, 0.0017700195, 0.0010375977,
+ 0.0002746582, -0.00051879883, -0.0012512207, -0.001953125,
+ -0.0025634766, -0.0030517578, -0.0034179688, -0.0036621094,
+ -0.0037231445, -0.0036621094, -0.0034484863, -0.003112793,
+ -0.0026245117, -0.0020751953, -0.0014343262, -0.00073242188,
+ -3.0517578e-05, 0.00067138672, 0.0013122559, 0.0019226074,
+ 0.0024108887, 0.0028076172, 0.0030822754, 0.0032348633,
+ 0.0032348633, 0.003112793, 0.0028686523, 0.0025024414,
+ 0.0020141602, 0.0014648438, 0.00088500977, 0.00024414062,
+ -0.00039672852, -0.0010070801, -0.0015563965, -0.0020446777,
+ -0.0024719238, -0.0027770996, -0.0029602051, -0.0030212402,
+ -0.0029602051, -0.0028076172, -0.0025024414, -0.0021362305,
+ -0.0016784668, -0.001159668, -0.00061035156, -6.1035156e-05,
+ 0.00048828125, 0.0010375977, 0.0014953613, 0.0018920898,
+ 0.0022277832, 0.0024414062, 0.0025634766, 0.0025634766,
+ 0.0024719238, 0.0022583008, 0.0019836426, 0.0016174316,
+ 0.0011901855, 0.0007019043, 0.00021362305, -0.0002746582,
+ -0.00076293945, -0.0011901855, -0.0015563965, -0.0018920898,
+ -0.0021057129, -0.0022583008, -0.0023193359, -0.0022583008,
+ -0.0021362305, -0.0019226074, -0.0016479492, -0.0012817383,
+ -0.00091552734, -0.00048828125, -6.1035156e-05, 0.00036621094,
+ 0.00076293945, 0.0010986328, 0.0014038086, 0.0016479492,
+ 0.0018005371, 0.0018920898, 0.0018920898, 0.0018005371,
+ 0.0016479492, 0.0014343262, 0.0011901855, 0.00085449219,
+ 0.00051879883, 0.00018310547, -0.00018310547, -0.00051879883,
+ -0.00082397461, -0.0010986328, -0.0013122559, -0.0014953613,
+ -0.0015869141, -0.0016174316, -0.0015869141, -0.0014953613,
+ -0.0013427734, -0.0011291504, -0.00088500977, -0.00064086914,
+ -0.00033569336, -6.1035156e-05, 0.00021362305, 0.00048828125,
+ 0.00073242188, 0.00091552734, 0.0010681152, 0.001159668,
+ 0.0012207031, 0.0012207031, 0.001159668, 0.0010681152,
+ 0.00091552734, 0.00073242188, 0.00054931641, 0.00033569336,
+ 0.00012207031, -9.1552734e-05, -0.00030517578, -0.00048828125,
+ -0.00064086914, -0.00076293945, -0.00085449219, -0.00091552734,
+ -0.00091552734, -0.00088500977, -0.00082397461, -0.00073242188,
+ -0.00061035156, -0.00048828125, -0.00033569336, -0.00018310547,
+ -3.0517578e-05, 0.00012207031, 0.00024414062, 0.00033569336,
+ 0.00042724609, 0.00048828125, 0.00054931641, 0.00054931641,
+ 0.00054931641, 0.00051879883, 0.00045776367, 0.00039672852,
+ 0.00030517578, 0.00021362305, 0.00012207031, 6.1035156e-05,
+ -3.0517578e-05, -9.1552734e-05, -0.00015258789, -0.00018310547,
+ -0.00021362305, -0.00024414062, -0.00024414062, -0.00021362305,
+ -0.00021362305, -0.00018310547, -0.00012207031, -9.1552734e-05,
+ -6.1035156e-05, -3.0517578e-05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0,
+};
+
+const nframes_t Session::default_click_emphasis_length = sizeof (default_click_emphasis) / sizeof (default_click_emphasis[0]);
+
+const Sample Session::default_click[] = {
+ 0, -0.014312744, -0.03338623, 0.019165039, 0.042541504,
+ 0.08984375, 0.082611084, 0.13909912, 0.17236328,
+ 0.19238281, 0.21087646, 0.22238159, 0.23114014,
+ 0.23321533, 0.23080444, 0.222229, 0.20944214,
+ 0.19146729, 0.16964722, 0.14352417, 0.11450195,
+ 0.082489014, 0.049316406, 0.014068604, -0.020507812,
+ -0.055969238, -0.089233398, -0.1211853, -0.15036011,
+ -0.1763916, -0.19882202, -0.2170105, -0.23062134,
+ -0.23916626, -0.24279785, -0.24142456, -0.23483276,
+ -0.22357178, -0.2074585, -0.18701172, -0.16308594,
+ -0.1355896, -0.10562134, -0.073547363, -0.040100098,
+ -0.0061645508, 0.027618408, 0.060394287, 0.091796875,
+ 0.12072754, 0.14685059, 0.16949463, 0.18804932,
+ 0.20245361, 0.21212769, 0.21694946, 0.21691895,
+ 0.21191406, 0.20214844, 0.18786621, 0.16943359,
+ 0.14706421, 0.12145996, 0.093139648, 0.06262207,
+ 0.030822754, -0.0018310547, -0.034545898, -0.066467285,
+ -0.097076416, -0.12564087, -0.15155029, -0.17422485,
+ -0.19326782, -0.20812988, -0.21862793, -0.22457886,
+ -0.22567749, -0.22210693, -0.21392822, -0.20120239,
+ -0.18438721, -0.16381836, -0.13986206, -0.11315918,
+ -0.084289551, -0.053771973, -0.02243042, 0.0090942383,
+ 0.040252686, 0.07019043, 0.098297119, 0.12408447,
+ 0.14685059, 0.16616821, 0.18170166, 0.19302368,
+ 0.19985962, 0.20230103, 0.20007324, 0.19329834,
+ 0.18231201, 0.16711426, 0.14819336, 0.1260376,
+ 0.10095215, 0.073608398, 0.044647217, 0.01461792,
+ -0.015808105, -0.04586792, -0.075073242, -0.10272217,
+ -0.12811279, -0.15084839, -0.17041016, -0.1862793,
+ -0.19827271, -0.20602417, -0.20941162, -0.20846558,
+ -0.203125, -0.19348145, -0.17990112, -0.16259766,
+ -0.14190674, -0.11846924, -0.092651367, -0.065002441,
+ -0.0362854, -0.0069885254, 0.022247314, 0.05065918,
+ 0.077758789, 0.10296631, 0.12561035, 0.14532471,
+ 0.16171265, 0.17428589, 0.18289185, 0.18737793,
+ 0.18756104, 0.18353271, 0.17541504, 0.16329956,
+ 0.14755249, 0.12854004, 0.10656738, 0.082244873,
+ 0.056091309, 0.028594971, 0.00045776367, -0.027709961,
+ -0.055419922, -0.08190918, -0.10665894, -0.12924194,
+ -0.14901733, -0.1656189, -0.17877197, -0.18811035,
+ -0.19342041, -0.19473267, -0.19189453, -0.18505859,
+ -0.17440796, -0.16009521, -0.14245605, -0.12203979,
+ -0.099121094, -0.07421875, -0.048034668, -0.020935059,
+ 0.0063781738, 0.033233643, 0.059234619, 0.083709717,
+ 0.1060791, 0.12600708, 0.14294434, 0.15655518,
+ 0.16662598, 0.17288208, 0.1751709, 0.17355347,
+ 0.16802979, 0.15866089, 0.14581299, 0.12966919,
+ 0.1105957, 0.089080811, 0.065551758, 0.040466309,
+ 0.014556885, -0.011779785, -0.037963867, -0.063293457,
+ -0.087341309, -0.10958862, -0.12942505, -0.14654541,
+ -0.16061401, -0.1711731, -0.17816162, -0.18145752,
+ -0.18081665, -0.17642212, -0.16836548, -0.15673828,
+ -0.14193726, -0.12426758, -0.10400391, -0.081695557,
+ -0.057891846, -0.032928467, -0.0075073242, 0.017791748,
+ 0.042602539, 0.066192627, 0.088134766, 0.10803223,
+ 0.12530518, 0.13967896, 0.15087891, 0.15859985,
+ 0.16265869, 0.16311646, 0.15982056, 0.1529541,
+ 0.14273071, 0.12921143, 0.11282349, 0.093963623,
+ 0.072967529, 0.050323486, 0.026580811, 0.002166748,
+ -0.022369385, -0.046356201, -0.069458008, -0.091094971,
+ -0.11071777, -0.12808228, -0.1427002, -0.15420532,
+ -0.16253662, -0.16741943, -0.16867065, -0.16647339,
+ -0.1607666, -0.15164185, -0.13946533, -0.12438965,
+ -0.10675049, -0.087036133, -0.065582275, -0.042877197,
+ -0.019500732, 0.004119873, 0.027496338, 0.049957275,
+ 0.071228027, 0.090759277, 0.10803223, 0.12283325,
+ 0.13482666, 0.14364624, 0.14923096, 0.15145874,
+ 0.150177, 0.14556885, 0.13772583, 0.1267395, 0.11294556,
+ 0.096679688, 0.078155518, 0.057952881, 0.036499023,
+ 0.014099121, -0.0085449219, -0.031066895, -0.052978516,
+ -0.073730469, -0.092895508, -0.11013794, -0.125,
+ -0.13717651, -0.14648438, -0.15264893, -0.15557861,
+ -0.15527344, -0.15164185, -0.14486694, -0.13510132,
+ -0.12252808, -0.10742188, -0.090240479, -0.07119751,
+ -0.05078125, -0.029510498, -0.0077514648, 0.014007568,
+ 0.035186768, 0.055480957, 0.074401855, 0.091430664,
+ 0.10638428, 0.11880493, 0.128479, 0.13525391,
+ 0.13891602, 0.1394043, 0.1368103, 0.13113403,
+ 0.12246704, 0.11114502, 0.097320557, 0.081268311,
+ 0.06350708, 0.044281006, 0.024017334, 0.0032958984,
+ -0.017578125, -0.038116455, -0.057769775, -0.07623291,
+ -0.093109131, -0.10791016, -0.12045288, -0.13046265,
+ -0.13763428, -0.14190674, -0.14321899, -0.14141846,
+ -0.13674927, -0.12921143, -0.11895752, -0.10629272,
+ -0.091491699, -0.074798584, -0.05670166, -0.037536621,
+ -0.017700195, 0.0022888184, 0.022033691, 0.04119873,
+ 0.059234619, 0.075805664, 0.090606689, 0.10324097,
+ 0.11346436, 0.12112427, 0.12594604, 0.12796021,
+ 0.12710571, 0.12335205, 0.11682129, 0.10772705,
+ 0.096191406, 0.082519531, 0.067077637, 0.050079346,
+ 0.031982422, 0.013244629, -0.0058898926, -0.024902344,
+ -0.043334961, -0.060882568, -0.077148438, -0.091674805,
+ -0.10430908, -0.11471558, -0.12261963, -0.12799072,
+ -0.13061523, -0.13043213, -0.12759399, -0.12203979,
+ -0.11392212, -0.10351562, -0.0909729, -0.076568604,
+ -0.060699463, -0.043640137, -0.025787354, -0.0075683594,
+ 0.010650635, 0.028503418, 0.045532227, 0.061431885,
+ 0.075836182, 0.088409424, 0.098968506, 0.10720825,
+ 0.11297607, 0.11621094, 0.11682129, 0.11471558,
+ 0.11010742, 0.10305786, 0.093658447, 0.082214355,
+ 0.068969727, 0.054138184, 0.038146973, 0.021331787,
+ 0.0039978027, -0.013397217, -0.030517578, -0.047027588,
+ -0.062469482, -0.076568604, -0.089080811, -0.099609375,
+ -0.10803223, -0.11419678, -0.1178894, -0.11907959,
+ -0.11779785, -0.11398315, -0.1078186, -0.099456787,
+ -0.089019775, -0.076751709, -0.063018799, -0.047973633,
+ -0.032073975, -0.015655518, 0.0010070801, 0.017456055,
+ 0.033355713, 0.048431396, 0.062286377, 0.074615479,
+ 0.085235596, 0.09387207, 0.10031128, 0.10449219,
+ 0.10629272, 0.10565186, 0.10272217, 0.097442627,
+ 0.089996338, 0.080566406, 0.069366455, 0.05657959,
+ 0.042633057, 0.027679443, 0.012115479, -0.003692627,
+ -0.01940918, -0.034759521, -0.049316406, -0.062835693,
+ -0.075012207, -0.085510254, -0.094238281, -0.10095215,
+ -0.10543823, -0.10775757, -0.10778809, -0.10552979,
+ -0.10107422, -0.094512939, -0.085968018, -0.075714111,
+ -0.063934326, -0.050842285, -0.036834717, -0.022125244,
+ -0.0070800781, 0.0079650879, 0.022705078, 0.036865234,
+ 0.050018311, 0.061981201, 0.072479248, 0.081268311,
+ 0.088165283, 0.093109131, 0.095855713, 0.096466064,
+ 0.094940186, 0.091247559, 0.085510254, 0.077911377,
+ 0.06854248, 0.057678223, 0.045593262, 0.032440186,
+ 0.018585205, 0.0043640137, -0.010009766, -0.024169922,
+ -0.037750244, -0.050567627, -0.062286377, -0.072631836,
+ -0.081451416, -0.088500977, -0.093658447, -0.096862793,
+ -0.097961426, -0.097015381, -0.094024658, -0.089080811,
+ -0.082275391, -0.073791504, -0.063812256, -0.052520752,
+ -0.040252686, -0.027191162, -0.013641357, 0,
+ 0.013580322, 0.026733398, 0.039154053, 0.050628662,
+ 0.060913086, 0.069702148, 0.076934814, 0.082366943,
+ 0.085906982, 0.087524414, 0.087158203, 0.08480835,
+ 0.080627441, 0.074615479, 0.066925049, 0.057800293,
+ 0.047393799, 0.035888672, 0.023651123, 0.010894775,
+ -0.0021362305, -0.015106201, -0.027740479, -0.039825439,
+ -0.050994873, -0.061096191, -0.069885254, -0.077148438,
+ -0.082763672, -0.086669922, -0.088684082, -0.088806152,
+ -0.087097168, -0.083526611, -0.07824707, -0.071350098,
+ -0.062957764, -0.053314209, -0.042633057, -0.031066895,
+ -0.018981934, -0.0065917969, 0.0058288574, 0.018035889,
+ 0.029693604, 0.040649414, 0.050628662, 0.059356689,
+ 0.066741943, 0.072570801, 0.076721191, 0.079162598,
+ 0.079772949, 0.078613281, 0.075714111, 0.071105957,
+ 0.064880371, 0.057281494, 0.048400879, 0.038421631,
+ 0.027618408, 0.016204834, 0.0043945312, -0.0074768066,
+ -0.019195557, -0.030548096, -0.041168213, -0.050964355,
+ -0.059661865, -0.067047119, -0.072998047, -0.077453613,
+ -0.080169678, -0.081237793, -0.080596924, -0.078216553,
+ -0.074249268, -0.068725586, -0.061767578, -0.05355835,
+ -0.044281006, -0.034088135, -0.023284912, -0.012084961,
+ -0.00067138672, 0.010650635, 0.021606445, 0.032043457,
+ 0.041687012, 0.050292969, 0.057769775, 0.063903809,
+ 0.06854248, 0.071655273, 0.073120117, 0.072937012,
+ 0.071166992, 0.067749023, 0.062835693, 0.056549072,
+ 0.048980713, 0.040313721, 0.030792236, 0.020568848,
+ 0.0098571777, -0.0010375977, -0.011932373, -0.022613525,
+ -0.032745361, -0.042236328, -0.050811768, -0.058288574,
+ -0.064544678, -0.069396973, -0.072784424, -0.074645996,
+ -0.074890137, -0.073547363, -0.070709229, -0.066375732,
+ -0.060638428, -0.05368042, -0.045593262, -0.036590576,
+ -0.026916504, -0.016693115, -0.0061950684, 0.0043334961,
+ 0.014709473, 0.024688721, 0.0340271, 0.042572021,
+ 0.050109863, 0.05645752, 0.061553955, 0.065246582,
+ 0.06741333, 0.068115234, 0.067260742, 0.064880371,
+ 0.061065674, 0.055908203, 0.049468994, 0.04196167,
+ 0.033508301, 0.024291992, 0.014556885, 0.004486084,
+ -0.0057067871, -0.015777588, -0.025512695, -0.034759521,
+ -0.043212891, -0.050750732, -0.057250977, -0.062469482,
+ -0.066375732, -0.068908691, -0.069915771, -0.069488525,
+ -0.067596436, -0.064239502, -0.05960083, -0.053710938,
+ -0.046691895, -0.038726807, -0.029998779, -0.020690918,
+ -0.011016846, -0.001159668, 0.0086364746, 0.018188477,
+ 0.027252197, 0.035675049, 0.043243408, 0.04977417,
+ 0.055175781, 0.059326172, 0.062103271, 0.063537598,
+ 0.063476562, 0.062011719, 0.059173584, 0.054992676,
+ 0.049591064, 0.043121338, 0.035675049, 0.027404785,
+ 0.018554688, 0.0092468262, -0.00024414062,
+ -0.0097351074, -0.019042969, -0.027954102, -0.036254883,
+ -0.043792725, -0.050415039, -0.055938721, -0.060272217,
+ -0.063323975, -0.065032959, -0.065368652, -0.064300537,
+ -0.061889648, -0.058197021, -0.053283691, -0.047241211,
+ -0.040283203, -0.032470703, -0.024017334, -0.015136719,
+ -0.0059814453, 0.0032653809, 0.012329102, 0.021087646,
+ 0.029327393, 0.036834717, 0.043487549, 0.049133301,
+ 0.053649902, 0.056945801, 0.058959961, 0.059631348,
+ 0.058990479, 0.057006836, 0.053741455, 0.049316406,
+ 0.043762207, 0.037231445, 0.029876709, 0.021881104,
+ 0.013366699, 0.0045471191, -0.0043640137, -0.013214111,
+ -0.021759033, -0.029876709, -0.037353516, -0.044006348,
+ -0.049743652, -0.054412842, -0.057922363, -0.060180664,
+ -0.061187744, -0.060913086, -0.059295654, -0.056488037,
+ -0.052459717, -0.047363281, -0.041290283, -0.034362793,
+ -0.026733398, -0.018615723, -0.010131836, -0.0014953613,
+ 0.0070800781, 0.01550293, 0.023498535, 0.030883789,
+ 0.037597656, 0.043426514, 0.048217773, 0.051940918,
+ 0.054473877, 0.055786133, 0.055847168, 0.054656982,
+ 0.052215576, 0.04864502, 0.04397583, 0.038330078,
+ 0.031829834, 0.024627686, 0.016845703, 0.0087280273,
+ 0.00039672852, -0.0079650879, -0.016143799,
+ -0.024017334, -0.03137207, -0.038024902, -0.043914795,
+ -0.048828125, -0.052703857, -0.055480957, -0.057067871,
+ -0.057434082, -0.05657959, -0.05456543, -0.051361084,
+ -0.047119141, -0.041900635, -0.035797119, -0.028961182,
+ -0.021575928, -0.013763428, -0.0057067871, 0.0023803711,
+ 0.010406494, 0.018127441, 0.025390625, 0.032073975,
+ 0.037994385, 0.043029785, 0.047088623, 0.050048828,
+ 0.0519104, 0.052581787, 0.052093506, 0.050415039,
+ 0.047637939, 0.043792725, 0.03894043, 0.03326416,
+ 0.026824951, 0.019775391, 0.012329102, 0.0045776367,
+ -0.0032958984, -0.011108398, -0.018676758, -0.025878906,
+ -0.032501221, -0.038421631, -0.043548584, -0.047729492,
+ -0.05090332, -0.053009033, -0.053955078, -0.053771973,
+ -0.052459717, -0.050018311, -0.046539307, -0.042114258,
+ -0.036773682, -0.030731201, -0.024047852, -0.016876221,
+ -0.0094299316, -0.0018005371, 0.0058288574, 0.013244629,
+ 0.020324707, 0.026916504, 0.032867432, 0.03805542,
+ 0.042388916, 0.04574585, 0.048034668, 0.049285889,
+ 0.049407959, 0.048400879, 0.046356201, 0.043243408,
+ 0.03918457, 0.034240723, 0.028533936, 0.022216797,
+ 0.015380859, 0.0082092285, 0.00082397461, -0.0065612793,
+ -0.013824463, -0.020812988, -0.02734375, -0.033294678,
+ -0.038543701, -0.042938232, -0.046478271, -0.048980713,
+ -0.050445557, -0.050842285, -0.050170898, -0.048431396,
+ -0.045684814, -0.041992188, -0.037384033, -0.032073975,
+ -0.026062012, -0.01953125, -0.012634277, -0.0055236816,
+ 0.0016784668, 0.0087890625, 0.015655518, 0.022125244,
+ 0.028076172, 0.033355713, 0.037902832, 0.041534424,
+ 0.044250488, 0.045959473, 0.046630859, 0.046264648,
+ 0.04486084, 0.042419434, 0.039093018, 0.034851074,
+ 0.029846191, 0.024200439, 0.018005371, 0.011383057,
+ 0.0045166016, -0.0024719238, -0.0093994141,
+ -0.016143799, -0.022521973, -0.0284729, -0.033752441,
+ -0.038360596, -0.042144775, -0.045013428, -0.046936035,
+ -0.047851562, -0.04776001, -0.046630859, -0.044555664,
+ -0.041534424, -0.037628174, -0.032989502, -0.027618408,
+ -0.021728516, -0.015411377, -0.0087585449,
+ -0.0020141602, 0.0047607422, 0.011383057, 0.017700195,
+ 0.023590088, 0.028930664, 0.033569336, 0.037475586,
+ 0.040527344, 0.042633057, 0.043792725, 0.04397583,
+ 0.043151855, 0.041381836, 0.038696289, 0.035125732,
+ 0.030792236, 0.025756836, 0.020172119, 0.014099121,
+ 0.0077514648, 0.0011901855, -0.0053710938, -0.01184082,
+ -0.018066406, -0.023925781, -0.02923584, -0.033966064,
+ -0.037963867, -0.041107178, -0.043426514, -0.044799805,
+ -0.045196533, -0.044677734, -0.043182373, -0.040802002,
+ -0.037567139, -0.033538818, -0.028808594, -0.023498535,
+ -0.017730713, -0.01159668, -0.005279541, 0.0011291504,
+ 0.0074768066, 0.013580322, 0.019378662, 0.024719238,
+ 0.02947998, 0.033538818, 0.036865234, 0.039306641,
+ 0.040893555, 0.041564941, 0.041290283, 0.040100098,
+ 0.038024902, 0.035064697, 0.031341553, 0.026947021,
+ 0.021942139, 0.016418457, 0.0105896, 0.0044555664,
+ -0.001739502, -0.0079345703, -0.013946533, -0.019683838,
+ -0.024963379, -0.029754639, -0.033905029, -0.03729248,
+ -0.039916992, -0.041687012, -0.042572021, -0.042541504,
+ -0.041625977, -0.039794922, -0.03717041, -0.033752441,
+ -0.029632568, -0.024902344, -0.019683838, -0.014038086,
+ -0.0081787109, -0.0021362305, 0.00390625, 0.0097961426,
+ 0.015472412, 0.020751953, 0.025512695, 0.029724121,
+ 0.033233643, 0.036010742, 0.037963867, 0.039031982,
+ 0.039245605, 0.038574219, 0.03704834, 0.034698486,
+ 0.031585693, 0.027740479, 0.02331543, 0.018341064,
+ 0.012969971, 0.0073242188, 0.0014953613, -0.0043640137,
+ -0.010162354, -0.015716553, -0.020965576, -0.025756836,
+ -0.029968262, -0.033569336, -0.036468506, -0.038543701,
+ -0.039825439, -0.040283203, -0.039825439, -0.038574219,
+ -0.036499023, -0.033630371, -0.030090332, -0.025939941,
+ -0.021240234, -0.016113281, -0.010681152, -0.0050354004,
+ 0.00067138672, 0.0063171387, 0.011810303, 0.016998291,
+ 0.021759033, 0.026031494, 0.029724121, 0.032684326,
+ 0.034942627, 0.036407471, 0.03704834, 0.036865234,
+ 0.035858154, 0.0340271, 0.031463623, 0.028198242,
+ 0.024291992, 0.019866943, 0.014984131, 0.0097961426,
+ 0.0043640137, -0.0011901855, -0.0066833496,
+ -0.012054443, -0.017181396, -0.021942139, -0.0262146,
+ -0.029937744, -0.03302002, -0.035400391, -0.037017822,
+ -0.037841797, -0.037872314, -0.037109375, -0.035552979,
+ -0.033233643, -0.03024292, -0.026611328, -0.02243042,
+ -0.017791748, -0.012817383, -0.0075683594,
+ -0.0022277832, 0.0031433105, 0.0084228516, 0.013458252,
+ 0.018188477, 0.022491455, 0.026245117, 0.029418945,
+ 0.031921387, 0.033691406, 0.034729004, 0.034973145,
+ 0.034423828, 0.033111572, 0.031066895, 0.028320312,
+ 0.024932861, 0.021026611, 0.01663208, 0.011871338,
+ 0.0068664551, 0.0016784668, -0.0035400391,
+ -0.0086669922, -0.013641357, -0.018310547, -0.022613525,
+ -0.026428223, -0.029632568, -0.032226562, -0.03414917,
+ -0.035339355, -0.035766602, -0.035430908, -0.034362793,
+ -0.032531738, -0.030059814, -0.026947021, -0.023284912,
+ -0.019134521, -0.014587402, -0.009765625, -0.0047912598,
+ 0.00030517578, 0.0053405762, 0.010192871, 0.014831543,
+ 0.019104004, 0.022918701, 0.0262146, 0.028930664,
+ 0.030944824, 0.032318115, 0.032928467, 0.032806396,
+ 0.031951904, 0.030395508, 0.028137207, 0.025268555,
+ 0.021850586, 0.017944336, 0.013641357, 0.0090332031,
+ 0.0042114258, -0.0007019043, -0.0056152344,
+ -0.010406494, -0.014953613, -0.019195557, -0.023040771,
+ -0.026367188, -0.029144287, -0.031280518, -0.032775879,
+ -0.033538818, -0.033630371, -0.032958984, -0.031646729,
+ -0.029632568, -0.027008057, -0.023803711, -0.020141602,
+ -0.016052246, -0.011627197, -0.007019043, -0.0022583008,
+ 0.0025024414, 0.0072021484, 0.011688232, 0.015899658,
+ 0.019744873, 0.023132324, 0.025970459, 0.02822876,
+ 0.029846191, 0.030792236, 0.031036377, 0.030609131,
+ 0.02947998, 0.027679443, 0.025299072, 0.022338867,
+ 0.018890381, 0.015045166, 0.01083374, 0.0063781738,
+ 0.0018005371, -0.0028381348, -0.0073852539,
+ -0.011810303, -0.015991211, -0.019805908, -0.023193359,
+ -0.026092529, -0.028442383, -0.030181885, -0.031280518,
+ -0.031677246, -0.031433105, -0.030517578, -0.028961182,
+ -0.026794434, -0.024078369, -0.020843506, -0.017181396,
+ -0.013183594, -0.0089416504, -0.0045166016,
+ -3.0517578e-05, 0.0044555664, 0.0087890625, 0.012908936,
+ 0.016723633, 0.020141602, 0.023071289, 0.025512695,
+ 0.02734375, 0.028564453, 0.029174805, 0.02911377,
+ 0.028381348, 0.027038574, 0.025085449, 0.022583008,
+ 0.019561768, 0.016143799, 0.012329102, 0.0082702637,
+ 0.0040283203, -0.00033569336, -0.0046691895,
+ -0.0089111328, -0.012969971, -0.01675415, -0.020172119,
+ -0.023162842, -0.025634766, -0.027557373, -0.028930664,
+ -0.029663086, -0.029754639, -0.02923584, -0.028076172,
+ -0.02633667, -0.024047852, -0.021240234, -0.018005371,
+ -0.014434814, -0.010528564, -0.006439209, -0.0022583008,
+ 0.0019836426, 0.0061340332, 0.010101318, 0.01385498,
+ 0.017272949, 0.020294189, 0.022857666, 0.024871826,
+ 0.02633667, 0.02722168, 0.027496338, 0.027130127,
+ 0.026184082, 0.024658203, 0.02255249, 0.019989014,
+ 0.016967773, 0.013549805, 0.0098571777, 0.0059509277,
+ 0.0018920898, -0.0021972656, -0.0062561035,
+ -0.010162354, -0.01385498, -0.017272949, -0.020294189,
+ -0.022888184, -0.024993896, -0.026550293, -0.027557373,
+ -0.027954102, -0.027770996, -0.027008057, -0.025665283,
+ -0.023773193, -0.021392822, -0.018585205, -0.015380859,
+ -0.01184082, -0.0080871582, -0.0042114258,
+ -0.00021362305, 0.0037231445, 0.0075378418, 0.011199951,
+ 0.014587402, 0.01763916, 0.020263672, 0.02243042,
+ 0.024108887, 0.025238037, 0.025756836, 0.025756836,
+ 0.025146484, 0.023986816, 0.02230835, 0.020141602,
+ 0.017486572, 0.01449585, 0.011169434, 0.0075683594,
+ 0.0038146973, 0, -0.0038452148, -0.007598877,
+ -0.011169434, -0.014526367, -0.017578125, -0.020233154,
+ -0.022460938, -0.024200439, -0.025421143, -0.026123047,
+ -0.026245117, -0.025787354, -0.024810791, -0.02331543,
+ -0.021331787, -0.018890381, -0.016052246, -0.012908936,
+ -0.0094909668, -0.0058898926, -0.0021972656,
+ 0.0015258789, 0.0051879883, 0.0087280273, 0.012054443,
+ 0.015075684, 0.01776123, 0.020019531, 0.021850586,
+ 0.023193359, 0.023986816, 0.024261475, 0.023986816,
+ 0.023162842, 0.021850586, 0.020050049, 0.017791748,
+ 0.015136719, 0.012176514, 0.0089111328, 0.0054931641,
+ 0.0019226074, -0.0016784668, -0.0052490234,
+ -0.0087280273, -0.011993408, -0.014984131, -0.017700195,
+ -0.019989014, -0.021881104, -0.023284912, -0.024200439,
+ -0.024597168, -0.024475098, -0.023834229, -0.022674561,
+ -0.021026611, -0.018981934, -0.01651001, -0.013702393,
+ -0.010620117, -0.0073242188, -0.00390625,
+ -0.00042724609, 0.0030517578, 0.006439209, 0.0096740723,
+ 0.012664795, 0.015350342, 0.017700195, 0.019622803,
+ 0.021118164, 0.022125244, 0.022644043, 0.022674561,
+ 0.022155762, 0.021179199, 0.019714355, 0.017822266,
+ 0.015563965, 0.012908936, 0.010009766, 0.0068664551,
+ 0.0036010742, 0.00021362305, -0.0031433105,
+ -0.0064697266, -0.0096130371, -0.012573242,
+ -0.015258789, -0.01763916, -0.019592285, -0.021148682,
+ -0.022277832, -0.022888184, -0.023040771, -0.022674561,
+ -0.021850586, -0.020568848, -0.018829346, -0.016723633,
+ -0.014251709, -0.011505127, -0.0085144043,
+ -0.0053710938, -0.0021362305, 0.001159668, 0.0043640137,
+ 0.0074768066, 0.010406494, 0.013061523, 0.015441895,
+ 0.017456055, 0.019073486, 0.020263672, 0.020996094,
+ 0.021270752, 0.021057129, 0.020385742, 0.019226074,
+ 0.017669678, 0.01574707, 0.013427734, 0.01083374,
+ 0.008026123, 0.0050048828, 0.0018920898, -0.0012512207,
+ -0.0043945312, -0.0074462891, -0.010314941,
+ -0.012969971, -0.015350342, -0.01739502, -0.019073486,
+ -0.020324707, -0.021148682, -0.021514893, -0.02142334,
+ -0.020874023, -0.019897461, -0.018493652, -0.016723633,
+ -0.014587402, -0.012145996, -0.0094604492,
+ -0.0065917969, -0.0036010742, -0.00054931641,
+ 0.0025024414, 0.0054626465, 0.0083007812, 0.010925293,
+ 0.013305664, 0.015380859, 0.017089844, 0.0184021,
+ 0.019317627, 0.019805908, 0.019836426, 0.019439697,
+ 0.018585205, 0.017333984, 0.015716553, 0.01373291,
+ 0.011444092, 0.0089111328, 0.0061950684, 0.003326416,
+ 0.00039672852, -0.0025634766, -0.0054321289,
+ -0.0082092285, -0.010803223, -0.013183594, -0.015258789,
+ -0.016998291, -0.018371582, -0.019378662, -0.019958496,
+ -0.020080566, -0.019805908, -0.019104004, -0.018005371,
+ -0.01651001, -0.014709473, -0.012573242, -0.010162354,
+ -0.007598877, -0.0048522949, -0.0020141602,
+ 0.00082397461, 0.0036315918, 0.0063476562, 0.0089111328,
+ 0.011260986, 0.013336182, 0.015106201, 0.016571045,
+ 0.017608643, 0.018280029, 0.01852417, 0.018371582,
+ 0.017791748, 0.016845703, 0.01550293, 0.013824463,
+ 0.01184082, 0.0096130371, 0.0071411133, 0.0045471191,
+ 0.0018310547, -0.00091552734, -0.0036315918,
+ -0.0062866211, -0.0087890625, -0.011108398,
+ -0.013214111, -0.015014648, -0.016479492, -0.017578125,
+ -0.018341064, -0.018676758, -0.018615723, -0.018188477,
+ -0.017333984, -0.016143799, -0.01461792, -0.012786865,
+ -0.010681152, -0.0083618164, -0.005859375,
+ -0.0032958984, -0.00064086914, 0.0020141602,
+ 0.0045776367, 0.0070495605, 0.0093383789, 0.011413574,
+ 0.013214111, 0.01473999, 0.015899658, 0.016723633,
+ 0.017150879, 0.017211914, 0.016876221, 0.016174316,
+ 0.015106201, 0.01373291, 0.012023926, 0.010070801,
+ 0.0078735352, 0.0055236816, 0.0030517578, 0.00051879883,
+ -0.0020446777, -0.0045471191, -0.0069580078,
+ -0.0092163086, -0.011260986, -0.013061523, -0.014587402,
+ -0.015808105, -0.016693115, -0.017211914, -0.017364502,
+ -0.017150879, -0.016540527, -0.015625, -0.014343262,
+ -0.012786865, -0.010955811, -0.0089111328,
+ -0.0066833496, -0.0043334961, -0.0018920898,
+ 0.00057983398, 0.0029907227, 0.0053405762, 0.0075683594,
+ 0.0096130371, 0.011413574, 0.012969971, 0.014221191,
+ 0.015167236, 0.01574707, 0.015991211, 0.015869141,
+ 0.015411377, 0.014587402, 0.013458252, 0.012023926,
+ 0.010345459, 0.0084228516, 0.0063171387, 0.0040588379,
+ 0.001739502, -0.00061035156, -0.0029602051,
+ -0.0052490234, -0.0074157715, -0.0094299316,
+ -0.011230469, -0.012786865, -0.014068604, -0.015045166,
+ -0.015716553, -0.016021729, -0.015991211, -0.015655518,
+ -0.014953613, -0.013946533, -0.012634277, -0.011077881,
+ -0.0092773438, -0.0072937012, -0.0051574707,
+ -0.0029602051, -0.00067138672, 0.0015869141,
+ 0.0038146973, 0.0059204102, 0.0079040527, 0.0097045898,
+ 0.011260986, 0.012573242, 0.01361084, 0.014312744,
+ 0.014709473, 0.014770508, 0.01449585, 0.013916016,
+ 0.013031006, 0.01184082, 0.010406494, 0.0087585449,
+ 0.0068969727, 0.0048828125, 0.0027770996, 0.00061035156,
+ -0.0015869141, -0.0037231445, -0.0057983398,
+ -0.0077514648, -0.0094909668, -0.011077881,
+ -0.012390137, -0.013427734, -0.014221191, -0.014678955,
+ -0.014801025, -0.014648438, -0.014160156, -0.013366699,
+ -0.012329102, -0.010986328, -0.0094604492,
+ -0.0077209473, -0.0058288574, -0.0038146973,
+ -0.001739502, 0.00036621094, 0.0024414062, 0.0044250488,
+ 0.0063476562, 0.0080871582, 0.0096435547, 0.010986328,
+ 0.012054443, 0.0128479, 0.013397217, 0.01361084,
+ 0.013519287, 0.013153076, 0.012481689, 0.011505127,
+ 0.010314941, 0.0088806152, 0.0072631836, 0.0054931641,
+ 0.0036010742, 0.0016174316, -0.00039672852,
+ -0.0023803711, -0.0043334961, -0.0061645508,
+ -0.0079040527, -0.0094299316, -0.010772705,
+ -0.011871338, -0.01272583, -0.013275146, -0.013580322,
+ -0.013580322, -0.013275146, -0.012695312, -0.011871338,
+ -0.010772705, -0.0094604492, -0.0079650879,
+ -0.0062866211, -0.004486084, -0.0026245117,
+ -0.0007019043, 0.0012207031, 0.003112793, 0.0049133301,
+ 0.0065917969, 0.0080871582, 0.0094299316, 0.010559082,
+ 0.011413574, 0.012054443, 0.012390137, 0.012451172,
+ 0.012268066, 0.011779785, 0.011047363, 0.010070801,
+ 0.0088500977, 0.0074768066, 0.0059204102, 0.0042114258,
+ 0.0024414062, 0.00061035156, -0.0012207031,
+ -0.0030212402, -0.0047607422, -0.0064086914,
+ -0.0079040527, -0.0092163086, -0.010345459,
+ -0.011260986, -0.011901855, -0.012298584, -0.012451172,
+ -0.012298584, -0.011901855, -0.011260986, -0.010406494,
+ -0.0093078613, -0.008026123, -0.0065612793,
+ -0.0049743652, -0.0032958984, -0.0015563965,
+ 0.00018310547, 0.0019226074, 0.0036010742, 0.0052185059,
+ 0.0066833496, 0.0079956055, 0.0091247559, 0.010040283,
+ 0.01071167, 0.011169434, 0.011352539, 0.011322021,
+ 0.010986328, 0.010437012, 0.0096740723, 0.0086669922,
+ 0.0075073242, 0.0061645508, 0.0046691895, 0.003112793,
+ 0.0014648438, -0.00021362305, -0.0018920898,
+ -0.0035095215, -0.0050354004, -0.0064697266,
+ -0.0077514648, -0.0088806152, -0.0098266602,
+ -0.010528564, -0.011016846, -0.011260986, -0.011260986,
+ -0.011047363, -0.0105896, -0.0098876953, -0.0090026855,
+ -0.0079040527, -0.0066833496, -0.005279541,
+ -0.0038146973, -0.0022583008, -0.00067138672,
+ 0.00091552734, 0.0024719238, 0.0039672852, 0.0053710938,
+ 0.0066223145, 0.0077514648, 0.0086669922, 0.0093994141,
+ 0.0099182129, 0.010223389, 0.010314941, 0.010131836,
+ 0.009765625, 0.0091552734, 0.0083618164, 0.0073852539,
+ 0.0062255859, 0.0049438477, 0.0035705566, 0.0021057129,
+ 0.00061035156, -0.00088500977, -0.0023803711,
+ -0.0038146973, -0.0051574707, -0.0064086914,
+ -0.0075073242, -0.0084228516, -0.009185791,
+ -0.0097351074, -0.010070801, -0.010192871, -0.010101318,
+ -0.0097961426, -0.0092773438, -0.0085449219,
+ -0.0076599121, -0.0066223145, -0.0054321289,
+ -0.0041503906, -0.0027770996, -0.001373291,
+ 6.1035156e-05, 0.0014953613, 0.0028686523, 0.0041809082,
+ 0.0053710938, 0.006439209, 0.0073852539, 0.0081481934,
+ 0.0086975098, 0.0090942383, 0.0092468262, 0.0092163086,
+ 0.008972168, 0.0085449219, 0.0079040527, 0.0071105957,
+ 0.0061645508, 0.005065918, 0.0038757324, 0.0025939941,
+ 0.0012817383, -9.1552734e-05, -0.0014343262,
+ -0.002746582, -0.0039978027, -0.0051574707,
+ -0.0062255859, -0.0071105957, -0.0078735352,
+ -0.0084533691, -0.0088806152, -0.0090637207,
+ -0.0090942383, -0.0089111328, -0.0085449219,
+ -0.0079956055, -0.0072937012, -0.006439209,
+ -0.0054321289, -0.0043334961, -0.0031433105,
+ -0.0018920898, -0.00061035156, 0.00067138672,
+ 0.0019226074, 0.003112793, 0.0042419434, 0.0052490234,
+ 0.0061645508, 0.0069274902, 0.0075073242, 0.0079345703,
+ 0.0081787109, 0.0082397461, 0.0081176758, 0.0078125,
+ 0.0073547363, 0.0067138672, 0.0059509277, 0.0050354004,
+ 0.0040283203, 0.0029296875, 0.0017700195, 0.00057983398,
+ -0.00064086914, -0.0018310547, -0.0029602051,
+ -0.0040283203, -0.0050354004, -0.0058898926,
+ -0.006652832, -0.007232666, -0.0076904297,
+ -0.0079650879, -0.0080566406, -0.0079956055,
+ -0.0077514648, -0.0073547363, -0.0068054199,
+ -0.0061035156, -0.005279541, -0.0043334961,
+ -0.003326416, -0.0022583008, -0.0011291504, 0,
+ 0.0010986328, 0.0021972656, 0.0032348633, 0.0041809082,
+ 0.0050354004, 0.0057678223, 0.0063476562, 0.0068054199,
+ 0.0071105957, 0.007232666, 0.007232666, 0.0070495605,
+ 0.0067138672, 0.0062255859, 0.0056152344, 0.0048522949,
+ 0.0040283203, 0.0030822754, 0.0021057129, 0.0010681152,
+ 0, -0.0010375977, -0.0020751953, -0.0030517578,
+ -0.0039367676, -0.0047607422, -0.0054626465,
+ -0.006072998, -0.0065307617, -0.0068359375,
+ -0.007019043, -0.007019043, -0.0068969727,
+ -0.0066223145, -0.0061950684, -0.005645752,
+ -0.0049743652, -0.0042114258, -0.0033874512,
+ -0.0024719238, -0.0014953613, -0.00051879883,
+ 0.00045776367, 0.0014038086, 0.0023193359, 0.0032043457,
+ 0.0039672852, 0.0046691895, 0.0052490234, 0.0057067871,
+ 0.0060424805, 0.0062255859, 0.0062866211, 0.0061950684,
+ 0.0059814453, 0.0056152344, 0.0051269531, 0.0045471191,
+ 0.0038757324, 0.003112793, 0.0022888184, 0.0014038086,
+ 0.00048828125, -0.00042724609, -0.0013122559,
+ -0.002166748, -0.0029907227, -0.0037231445,
+ -0.0043945312, -0.0049438477, -0.0054016113,
+ -0.0057373047, -0.0059509277, -0.0060424805,
+ -0.0059814453, -0.0057983398, -0.0054931641,
+ -0.0050964355, -0.0045776367, -0.0039672852,
+ -0.0032653809, -0.002532959, -0.001739502,
+ -0.00088500977, -6.1035156e-05, 0.00076293945,
+ 0.0015869141, 0.0023498535, 0.0030517578, 0.0036621094,
+ 0.0042114258, 0.0046386719, 0.0050048828, 0.0052185059,
+ 0.0053100586, 0.0053100586, 0.0051879883, 0.0049133301,
+ 0.0045776367, 0.004119873, 0.0036010742, 0.0029602051,
+ 0.0022888184, 0.0015869141, 0.00082397461,
+ 6.1035156e-05, -0.0007019043, -0.0014343262,
+ -0.002166748, -0.0028076172, -0.0033874512, -0.00390625,
+ -0.0043334961, -0.0046691895, -0.0049133301,
+ -0.0050354004, -0.0050354004, -0.0049438477,
+ -0.0047302246, -0.0044555664, -0.0040588379,
+ -0.0035705566, -0.0030517578, -0.0024414062,
+ -0.0018005371, -0.0010986328, -0.00042724609,
+ 0.0002746582, 0.00094604492, 0.0016174316, 0.0022277832,
+ 0.0027770996, 0.0032653809, 0.0036621094, 0.0039978027,
+ 0.0042114258, 0.0043334961, 0.0043945312, 0.0043334961,
+ 0.0041809082, 0.0039367676, 0.0036010742, 0.0031738281,
+ 0.0027160645, 0.002166748, 0.0016174316, 0.0010070801,
+ 0.00039672852, -0.00024414062, -0.00085449219,
+ -0.0014343262, -0.0020141602, -0.0025024414,
+ -0.0029602051, -0.0033569336, -0.0036621094,
+ -0.0038757324, -0.0040283203, -0.0040588379,
+ -0.0040283203, -0.00390625, -0.0037231445,
+ -0.0034484863, -0.0030822754, -0.0026855469,
+ -0.0022277832, -0.0017089844, -0.0011901855,
+ -0.00064086914, -6.1035156e-05, 0.00048828125,
+ 0.0010070801, 0.0014953613, 0.0019836426, 0.0023803711,
+ 0.002746582, 0.0030212402, 0.0032348633, 0.0033874512,
+ 0.0034484863, 0.0034484863, 0.0033569336, 0.0032043457,
+ 0.0029602051, 0.0026855469, 0.0023193359, 0.0019226074,
+ 0.0014953613, 0.0010375977, 0.00054931641,
+ 6.1035156e-05, -0.00039672852, -0.00088500977,
+ -0.0013122559, -0.001739502, -0.0021057129,
+ -0.0024108887, -0.0026855469, -0.0028991699,
+ -0.0030212402, -0.003112793, -0.003112793,
+ -0.0030517578, -0.0029296875, -0.0027160645,
+ -0.0025024414, -0.0021972656, -0.0018615723,
+ -0.0014953613, -0.0010986328, -0.0007019043,
+ -0.0002746582, 0.00015258789, 0.00054931641,
+ 0.00094604492, 0.0012817383, 0.0016174316, 0.0018920898,
+ 0.0021362305, 0.0023193359, 0.0024414062, 0.002532959,
+ 0.002532959, 0.0025024414, 0.0024108887, 0.0022583008,
+ 0.0020751953, 0.0018310547, 0.0015563965, 0.0012512207,
+ 0.00091552734, 0.00057983398, 0.00024414062,
+ -0.00012207031, -0.00045776367, -0.00076293945,
+ -0.0010681152, -0.0013427734, -0.0015869141,
+ -0.0018005371, -0.001953125, -0.0020751953,
+ -0.0021362305, -0.002166748, -0.0021362305,
+ -0.0020751953, -0.001953125, -0.0018005371,
+ -0.0016174316, -0.0014038086, -0.001159668,
+ -0.00088500977, -0.00061035156, -0.00033569336,
+ -6.1035156e-05, 0.00021362305, 0.00048828125,
+ 0.00073242188, 0.00094604492, 0.001159668, 0.0013122559,
+ 0.0014648438, 0.0015563965, 0.0016174316, 0.0016479492,
+ 0.0016174316, 0.0015563965, 0.0014953613, 0.001373291,
+ 0.0012207031, 0.0010681152, 0.00088500977,
+ 0.00067138672, 0.00045776367, 0.0002746582,
+ 6.1035156e-05, -0.00015258789, -0.00036621094,
+ -0.00054931641, -0.0007019043, -0.00085449219,
+ -0.0009765625, -0.0010986328, -0.001159668,
+ -0.0012207031, -0.0012207031, -0.0012207031,
+ -0.0011901855, -0.0011291504, -0.0010375977,
+ -0.00094604492, -0.00082397461, -0.00067138672,
+ -0.00054931641, -0.00039672852, -0.00024414062,
+ -9.1552734e-05, 3.0517578e-05, 0.00018310547,
+ 0.00030517578, 0.00042724609, 0.00051879883,
+ 0.00057983398, 0.00064086914, 0.0007019043,
+ 0.00073242188, 0.00073242188, 0.00073242188,
+ 0.0007019043, 0.00067138672, 0.00061035156,
+ 0.00054931641, 0.00045776367, 0.00039672852,
+ 0.00030517578, 0.00021362305, 0.00015258789,
+ 6.1035156e-05, -3.0517578e-05, -9.1552734e-05,
+ -0.00015258789, -0.00021362305, -0.00024414062,
+ -0.0002746582, -0.00030517578, -0.00030517578,
+ -0.00030517578, -0.00030517578, -0.0002746582,
+ -0.0002746582, -0.00024414062, -0.00021362305,
+ -0.00018310547, -0.00015258789, -9.1552734e-05,
+ -9.1552734e-05, -6.1035156e-05, -3.0517578e-05, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+const nframes_t Session::default_click_length = sizeof (default_click) / sizeof (default_click[0]);
diff --git a/libs/ardour/directory_names.cc b/libs/ardour/directory_names.cc
new file mode 100644
index 0000000000..2b6dfcbb39
--- /dev/null
+++ b/libs/ardour/directory_names.cc
@@ -0,0 +1,19 @@
+#include <ardour/directory_names.h>
+
+#include "i18n.h"
+
+namespace ARDOUR {
+
+const char* const old_sound_dir_name = X_("sounds");
+const char* const sound_dir_name = X_("audiofiles");
+const char* const midi_dir_name = X_("midifiles");
+const char* const peak_dir_name = X_("peaks");
+const char* const dead_sound_dir_name = X_("dead_sounds");
+const char* const dead_midi_dir_name = X_("dead_midi");
+const char* const interchange_dir_name = X_("interchange");
+const char* const export_dir_name = X_("export");
+const char* const templates_dir_name = X_("templates");
+const char* const surfaces_dir_name = X_("surfaces");
+const char* const user_config_dir_name = X_(".ardour3");
+
+}
diff --git a/libs/ardour/diskstream.cc b/libs/ardour/diskstream.cc
new file mode 100644
index 0000000000..9665176a67
--- /dev/null
+++ b/libs/ardour/diskstream.cc
@@ -0,0 +1,411 @@
+/*
+ Copyright (C) 2000-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <fstream>
+#include <cassert>
+#include <cstdio>
+#include <unistd.h>
+#include <cmath>
+#include <cerrno>
+#include <string>
+#include <climits>
+#include <fcntl.h>
+#include <cstdlib>
+#include <ctime>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <sigc++/bind.h>
+
+#include <pbd/error.h>
+#include <pbd/basename.h>
+#include <glibmm/thread.h>
+#include <pbd/xml++.h>
+
+#include <ardour/ardour.h>
+#include <ardour/audioengine.h>
+#include <ardour/diskstream.h>
+#include <ardour/utils.h>
+#include <ardour/configuration.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/send.h>
+#include <ardour/playlist.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/region.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+/* XXX This goes uninitialized when there is no ~/.ardour3 directory.
+ * I can't figure out why, so this will do for now (just stole the
+ * default from configuration_vars.h). 0 is not a good value for
+ * allocating buffer sizes..
+ */
+ARDOUR::nframes_t Diskstream::disk_io_chunk_frames = 1024 * 256;
+
+sigc::signal<void> Diskstream::DiskOverrun;
+sigc::signal<void> Diskstream::DiskUnderrun;
+
+Diskstream::Diskstream (Session &sess, const string &name, Flag flag)
+ : SessionObject(sess, name)
+{
+ init (flag);
+}
+
+Diskstream::Diskstream (Session& sess, const XMLNode& node)
+ : SessionObject(sess, "unnamed diskstream")
+{
+ init (Recordable);
+}
+
+void
+Diskstream::init (Flag f)
+{
+ _flags = f;
+ _io = 0;
+ _alignment_style = ExistingMaterial;
+ _persistent_alignment_style = ExistingMaterial;
+ first_input_change = true;
+ i_am_the_modifier = 0;
+ g_atomic_int_set (&_record_enabled, 0);
+ was_recording = false;
+ capture_start_frame = 0;
+ capture_captured = 0;
+ _visible_speed = 1.0f;
+ _actual_speed = 1.0f;
+ _buffer_reallocation_required = false;
+ _seek_required = false;
+ first_recordable_frame = max_frames;
+ last_recordable_frame = max_frames;
+ _roll_delay = 0;
+ _capture_offset = 0;
+ _processed = false;
+ _slaved = false;
+ adjust_capture_position = 0;
+ last_possibly_recording = 0;
+ loop_location = 0;
+ wrap_buffer_size = 0;
+ speed_buffer_size = 0;
+ last_phase = 0;
+ phi = (uint64_t) (0x1000000);
+ target_phi = phi;
+ file_frame = 0;
+ playback_sample = 0;
+ playback_distance = 0;
+ _read_data_count = 0;
+ _write_data_count = 0;
+ commit_should_unlock = false;
+
+ pending_overwrite = false;
+ overwrite_frame = 0;
+ overwrite_queued = false;
+ input_change_pending = NoChange;
+}
+
+Diskstream::~Diskstream ()
+{
+ if (_playlist)
+ _playlist->release ();
+}
+
+void
+Diskstream::set_io (IO& io)
+{
+ _io = &io;
+ set_align_style_from_io ();
+}
+
+void
+Diskstream::handle_input_change (IOChange change, void *src)
+{
+ Glib::Mutex::Lock lm (state_lock);
+
+ if (!(input_change_pending & change)) {
+ input_change_pending = IOChange (input_change_pending|change);
+ _session.request_input_change_handling ();
+ }
+}
+
+void
+Diskstream::non_realtime_set_speed ()
+{
+ if (_buffer_reallocation_required)
+ {
+ Glib::Mutex::Lock lm (state_lock);
+ allocate_temporary_buffers ();
+
+ _buffer_reallocation_required = false;
+ }
+
+ if (_seek_required) {
+ if (speed() != 1.0f || speed() != -1.0f) {
+ seek ((nframes_t) (_session.transport_frame() * (double) speed()), true);
+ }
+ else {
+ seek (_session.transport_frame(), true);
+ }
+
+ _seek_required = false;
+ }
+}
+
+bool
+Diskstream::realtime_set_speed (double sp, bool global)
+{
+ bool changed = false;
+ double new_speed = sp * _session.transport_speed();
+
+ if (_visible_speed != sp) {
+ _visible_speed = sp;
+ changed = true;
+ }
+
+ if (new_speed != _actual_speed) {
+
+ nframes_t required_wrap_size = (nframes_t) floor (_session.get_block_size() *
+ fabs (new_speed)) + 1;
+
+ if (required_wrap_size > wrap_buffer_size) {
+ _buffer_reallocation_required = true;
+ }
+
+ _actual_speed = new_speed;
+ target_phi = (uint64_t) (0x1000000 * fabs(_actual_speed));
+ }
+
+ if (changed) {
+ if (!global) {
+ _seek_required = true;
+ }
+ SpeedChanged (); /* EMIT SIGNAL */
+ }
+
+ return _buffer_reallocation_required || _seek_required;
+}
+
+void
+Diskstream::prepare ()
+{
+ _processed = false;
+ playback_distance = 0;
+}
+
+void
+Diskstream::recover ()
+{
+ if (commit_should_unlock) {
+ state_lock.unlock();
+ }
+ _processed = false;
+}
+
+void
+Diskstream::set_capture_offset ()
+{
+ if (_io == 0) {
+ /* can't capture, so forget it */
+ return;
+ }
+
+ _capture_offset = _io->input_latency();
+}
+
+void
+Diskstream::set_align_style (AlignStyle a)
+{
+ if (record_enabled() && _session.actively_recording()) {
+ return;
+ }
+
+ if (a != _alignment_style) {
+ _alignment_style = a;
+ AlignmentStyleChanged ();
+ }
+}
+
+int
+Diskstream::set_loop (Location *location)
+{
+ if (location) {
+ if (location->start() >= location->end()) {
+ error << string_compose(_("Location \"%1\" not valid for track loop (start >= end)"), location->name()) << endl;
+ return -1;
+ }
+ }
+
+ loop_location = location;
+
+ LoopSet (location); /* EMIT SIGNAL */
+ return 0;
+}
+
+ARDOUR::nframes_t
+Diskstream::get_capture_start_frame (uint32_t n)
+{
+ Glib::Mutex::Lock lm (capture_info_lock);
+
+ if (capture_info.size() > n) {
+ return capture_info[n]->start;
+ }
+ else {
+ return capture_start_frame;
+ }
+}
+
+ARDOUR::nframes_t
+Diskstream::get_captured_frames (uint32_t n)
+{
+ Glib::Mutex::Lock lm (capture_info_lock);
+
+ if (capture_info.size() > n) {
+ return capture_info[n]->frames;
+ }
+ else {
+ return capture_captured;
+ }
+}
+
+void
+Diskstream::set_roll_delay (ARDOUR::nframes_t nframes)
+{
+ _roll_delay = nframes;
+}
+
+void
+Diskstream::set_speed (double sp)
+{
+ _session.request_diskstream_speed (*this, sp);
+
+ /* to force a rebuffering at the right place */
+ playlist_modified();
+}
+
+int
+Diskstream::use_playlist (boost::shared_ptr<Playlist> playlist)
+{
+ {
+ Glib::Mutex::Lock lm (state_lock);
+
+ if (playlist == _playlist) {
+ return 0;
+ }
+
+ plmod_connection.disconnect ();
+ plgone_connection.disconnect ();
+
+ if (_playlist) {
+ _playlist->release();
+ }
+
+ _playlist = playlist;
+ _playlist->use();
+
+ if (!in_set_state && recordable()) {
+ reset_write_sources (false);
+ }
+
+ plmod_connection = _playlist->Modified.connect (mem_fun (*this, &Diskstream::playlist_modified));
+ plgone_connection = _playlist->GoingAway.connect (bind (mem_fun (*this, &Diskstream::playlist_deleted), boost::weak_ptr<Playlist>(_playlist)));
+ }
+
+ /* don't do this if we've already asked for it *or* if we are setting up
+ the diskstream for the very first time - the input changed handling will
+ take care of the buffer refill.
+ */
+
+ if (!overwrite_queued && !(_session.state_of_the_state() & Session::CannotSave)) {
+ _session.request_overwrite_buffer (this);
+ overwrite_queued = true;
+ }
+
+ PlaylistChanged (); /* EMIT SIGNAL */
+ _session.set_dirty ();
+
+ return 0;
+}
+
+void
+Diskstream::playlist_changed (Change ignored)
+{
+ playlist_modified ();
+}
+
+void
+Diskstream::playlist_modified ()
+{
+ if (!i_am_the_modifier && !overwrite_queued) {
+ _session.request_overwrite_buffer (this);
+ overwrite_queued = true;
+ }
+}
+
+void
+Diskstream::playlist_deleted (boost::weak_ptr<Playlist> wpl)
+{
+ boost::shared_ptr<Playlist> pl (wpl.lock());
+
+ if (pl == _playlist) {
+
+ /* this catches an ordering issue with session destruction. playlists
+ are destroyed before diskstreams. we have to invalidate any handles
+ we have to the playlist.
+ */
+
+ if (_playlist) {
+ _playlist.reset ();
+ }
+ }
+}
+
+bool
+Diskstream::set_name (const string& str)
+{
+ if (str != _name) {
+ assert(playlist());
+ playlist()->set_name (str);
+
+ SessionObject::set_name(str);
+
+ if (!in_set_state && recordable()) {
+ /* rename existing capture files so that they have the correct name */
+ return rename_write_sources ();
+ } else {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+Diskstream::remove_region_from_last_capture (boost::weak_ptr<Region> wregion)
+{
+ boost::shared_ptr<Region> region (wregion.lock());
+
+ if (!region) {
+ return;
+ }
+
+ _last_capture_regions.remove (region);
+}
+
diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc
new file mode 100644
index 0000000000..71b699396b
--- /dev/null
+++ b/libs/ardour/enums.cc
@@ -0,0 +1,388 @@
+/*
+ Copyright (C) 2000-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/enumwriter.h>
+
+#include <ardour/types.h>
+#include <ardour/session.h>
+#include <ardour/location.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/diskstream.h>
+#include <ardour/audioregion.h>
+#include <ardour/route_group.h>
+#include <ardour/panner.h>
+#include <ardour/track.h>
+#include <ardour/midi_track.h>
+
+using namespace std;
+using namespace PBD;
+using namespace ARDOUR;
+
+void
+setup_enum_writer ()
+{
+ EnumWriter* enum_writer = new EnumWriter();
+ vector<int> i;
+ vector<string> s;
+
+ OverlapType _OverlapType;
+ AlignStyle _AlignStyle;
+ MeterPoint _MeterPoint;
+ TrackMode _TrackMode;
+ NoteMode _NoteMode;
+ ChannelMode _ChannelMode;
+ MeterFalloff _MeterFalloff;
+ MeterHold _MeterHold;
+ EditMode _EditMode;
+ RegionPoint _RegionPoint;
+ Placement _Placement;
+ MonitorModel _MonitorModel;
+ RemoteModel _RemoteModel;
+ DenormalModel _DenormalModel;
+ CrossfadeModel _CrossfadeModel;
+ LayerModel _LayerModel;
+ SoloModel _SoloModel;
+ SampleFormat _SampleFormat;
+ HeaderFormat _HeaderFormat;
+ PluginType _PluginType;
+ SlaveSource _SlaveSource;
+ ShuttleBehaviour _ShuttleBehaviour;
+ ShuttleUnits _ShuttleUnits;
+ mute_type _mute_type;
+ Session::RecordState _Session_RecordState;
+ Session::Event::Type _Session_Event_Type;
+ SmpteFormat _Session_SmpteFormat;
+ Session::PullupFormat _Session_PullupFormat;
+ AudioRegion::FadeShape _AudioRegion_FadeShape;
+ Panner::LinkDirection _Panner_LinkDirection;
+ IOChange _IOChange;
+ AutomationType _AutomationType;
+ AutoState _AutoState;
+ AutoStyle _AutoStyle;
+ AutoConnectOption _AutoConnectOption;
+ Session::StateOfTheState _Session_StateOfTheState;
+ Route::Flag _Route_Flag;
+ AudioFileSource::Flag _AudioFileSource_Flag;
+ Diskstream::Flag _Diskstream_Flag;
+ Location::Flags _Location_Flags;
+ RouteGroup::Flag _RouteGroup_Flag;
+ Region::Flag _Region_Flag;
+ Region::PositionLockStyle _Region_PositionLockStyle;
+ Track::FreezeState _Track_FreezeState;
+ AutomationList::InterpolationStyle _AutomationList_InterpolationStyle;
+
+#define REGISTER(e) enum_writer->register_distinct (typeid(e).name(), i, s); i.clear(); s.clear()
+#define REGISTER_BITS(e) enum_writer->register_bits (typeid(e).name(), i, s); i.clear(); s.clear()
+#define REGISTER_ENUM(e) i.push_back (e); s.push_back (#e)
+#define REGISTER_CLASS_ENUM(t,e) i.push_back (t::e); s.push_back (#e)
+
+ REGISTER_ENUM (NoChange);
+ REGISTER_ENUM (ConfigurationChanged);
+ REGISTER_ENUM (ConnectionsChanged);
+ REGISTER_BITS (_IOChange);
+
+ REGISTER_ENUM (OverlapNone);
+ REGISTER_ENUM (OverlapInternal);
+ REGISTER_ENUM (OverlapStart);
+ REGISTER_ENUM (OverlapEnd);
+ REGISTER_ENUM (OverlapExternal);
+ REGISTER (_OverlapType);
+
+ REGISTER_ENUM (GainAutomation);
+ REGISTER_ENUM (PanAutomation);
+ REGISTER_ENUM (PluginAutomation);
+ REGISTER_ENUM (SoloAutomation);
+ REGISTER_ENUM (MuteAutomation);
+ REGISTER_ENUM (MidiCCAutomation);
+ REGISTER_ENUM (FadeInAutomation);
+ REGISTER_ENUM (FadeOutAutomation);
+ REGISTER_ENUM (EnvelopeAutomation);
+ REGISTER_BITS (_AutomationType);
+
+ REGISTER_ENUM (Off);
+ REGISTER_ENUM (Write);
+ REGISTER_ENUM (Touch);
+ REGISTER_ENUM (Play);
+ REGISTER_BITS (_AutoState);
+
+ REGISTER_ENUM (Absolute);
+ REGISTER_ENUM (Trim);
+ REGISTER_BITS (_AutoStyle);
+
+ REGISTER_ENUM (CaptureTime);
+ REGISTER_ENUM (ExistingMaterial);
+ REGISTER (_AlignStyle);
+
+ REGISTER_ENUM (MeterInput);
+ REGISTER_ENUM (MeterPreFader);
+ REGISTER_ENUM (MeterPostFader);
+ REGISTER (_MeterPoint);
+
+ REGISTER_ENUM (Normal);
+ REGISTER_ENUM (Destructive);
+ REGISTER (_TrackMode);
+
+ REGISTER_ENUM (Sustained);
+ REGISTER_ENUM (Percussive);
+ REGISTER (_NoteMode);
+
+ REGISTER_ENUM (AllChannels);
+ REGISTER_ENUM (FilterChannels);
+ REGISTER_ENUM (ForceChannel);
+ REGISTER (_ChannelMode);
+
+ REGISTER_ENUM (MeterFalloffOff);
+ REGISTER_ENUM (MeterFalloffSlowest);
+ REGISTER_ENUM (MeterFalloffSlow);
+ REGISTER_ENUM (MeterFalloffMedium);
+ REGISTER_ENUM (MeterFalloffFast);
+ REGISTER_ENUM (MeterFalloffFaster);
+ REGISTER_ENUM (MeterFalloffFastest);
+ REGISTER (_MeterFalloff);
+
+ REGISTER_ENUM (MeterHoldOff);
+ REGISTER_ENUM (MeterHoldShort);
+ REGISTER_ENUM (MeterHoldMedium);
+ REGISTER_ENUM (MeterHoldLong);
+ REGISTER (_MeterHold);
+
+ REGISTER_ENUM (Slide);
+ REGISTER_ENUM (Splice);
+ REGISTER (_EditMode);
+
+ REGISTER_ENUM (Start);
+ REGISTER_ENUM (End);
+ REGISTER_ENUM (SyncPoint);
+ REGISTER (_RegionPoint);
+
+ REGISTER_ENUM (PreFader);
+ REGISTER_ENUM (PostFader);
+ REGISTER (_Placement);
+
+ REGISTER_ENUM (HardwareMonitoring);
+ REGISTER_ENUM (SoftwareMonitoring);
+ REGISTER_ENUM (ExternalMonitoring);
+ REGISTER (_MonitorModel);
+
+ REGISTER_ENUM (DenormalNone);
+ REGISTER_ENUM (DenormalFTZ);
+ REGISTER_ENUM (DenormalDAZ);
+ REGISTER_ENUM (DenormalFTZDAZ);
+ REGISTER (_DenormalModel);
+
+ REGISTER_ENUM (UserOrdered);
+ REGISTER_ENUM (MixerOrdered);
+ REGISTER_ENUM (EditorOrdered);
+ REGISTER (_RemoteModel);
+
+ REGISTER_ENUM (FullCrossfade);
+ REGISTER_ENUM (ShortCrossfade);
+ REGISTER (_CrossfadeModel);
+
+ REGISTER_ENUM (LaterHigher);
+ REGISTER_ENUM (MoveAddHigher);
+ REGISTER_ENUM (AddHigher);
+ REGISTER (_LayerModel);
+
+ REGISTER_ENUM (InverseMute);
+ REGISTER_ENUM (SoloBus);
+ REGISTER (_SoloModel);
+
+ REGISTER_ENUM (AutoConnectPhysical);
+ REGISTER_ENUM (AutoConnectMaster);
+ REGISTER_BITS (_AutoConnectOption);
+
+ REGISTER_ENUM (FormatFloat);
+ REGISTER_ENUM (FormatInt24);
+ REGISTER_ENUM (FormatInt16);
+ REGISTER (_SampleFormat);
+
+ REGISTER_ENUM (BWF);
+ REGISTER_ENUM (WAVE);
+ REGISTER_ENUM (WAVE64);
+ REGISTER_ENUM (CAF);
+ REGISTER_ENUM (AIFF);
+ REGISTER_ENUM (iXML);
+ REGISTER_ENUM (RF64);
+ REGISTER (_HeaderFormat);
+
+ REGISTER_ENUM (AudioUnit);
+ REGISTER_ENUM (LADSPA);
+ REGISTER_ENUM (VST);
+ REGISTER (_PluginType);
+
+ REGISTER_ENUM (None);
+ REGISTER_ENUM (MTC);
+ REGISTER_ENUM (JACK);
+ REGISTER (_SlaveSource);
+
+ REGISTER_ENUM (Sprung);
+ REGISTER_ENUM (Wheel);
+ REGISTER (_ShuttleBehaviour);
+
+ REGISTER_ENUM (Percentage);
+ REGISTER_ENUM (Semitones);
+ REGISTER (_ShuttleUnits);
+
+ REGISTER_CLASS_ENUM (Session, Disabled);
+ REGISTER_CLASS_ENUM (Session, Enabled);
+ REGISTER_CLASS_ENUM (Session, Recording);
+ REGISTER (_Session_RecordState);
+
+ REGISTER_CLASS_ENUM (Session::Event, SetTransportSpeed);
+ REGISTER_CLASS_ENUM (Session::Event, SetDiskstreamSpeed);
+ REGISTER_CLASS_ENUM (Session::Event, Locate);
+ REGISTER_CLASS_ENUM (Session::Event, LocateRoll);
+ REGISTER_CLASS_ENUM (Session::Event, LocateRollLocate);
+ REGISTER_CLASS_ENUM (Session::Event, SetLoop);
+ REGISTER_CLASS_ENUM (Session::Event, PunchIn);
+ REGISTER_CLASS_ENUM (Session::Event, PunchOut);
+ REGISTER_CLASS_ENUM (Session::Event, RangeStop);
+ REGISTER_CLASS_ENUM (Session::Event, RangeLocate);
+ REGISTER_CLASS_ENUM (Session::Event, Overwrite);
+ REGISTER_CLASS_ENUM (Session::Event, SetSlaveSource);
+ REGISTER_CLASS_ENUM (Session::Event, Audition);
+ REGISTER_CLASS_ENUM (Session::Event, InputConfigurationChange);
+ REGISTER_CLASS_ENUM (Session::Event, SetAudioRange);
+ REGISTER_CLASS_ENUM (Session::Event, SetPlayRange);
+ REGISTER_CLASS_ENUM (Session::Event, StopOnce);
+ REGISTER_CLASS_ENUM (Session::Event, AutoLoop);
+ REGISTER (_Session_Event_Type);
+
+ REGISTER_CLASS_ENUM (Session, Clean);
+ REGISTER_CLASS_ENUM (Session, Dirty);
+ REGISTER_CLASS_ENUM (Session, CannotSave);
+ REGISTER_CLASS_ENUM (Session, Deletion);
+ REGISTER_CLASS_ENUM (Session, InitialConnecting);
+ REGISTER_CLASS_ENUM (Session, Loading);
+ REGISTER_CLASS_ENUM (Session, InCleanup);
+ REGISTER_BITS (_Session_StateOfTheState);
+
+ REGISTER_ENUM (smpte_23976);
+ REGISTER_ENUM (smpte_24);
+ REGISTER_ENUM (smpte_24976);
+ REGISTER_ENUM (smpte_25);
+ REGISTER_ENUM (smpte_2997);
+ REGISTER_ENUM (smpte_2997drop);
+ REGISTER_ENUM (smpte_30);
+ REGISTER_ENUM (smpte_30drop);
+ REGISTER_ENUM (smpte_5994);
+ REGISTER_ENUM (smpte_60);
+ REGISTER (_Session_SmpteFormat);
+
+ REGISTER_CLASS_ENUM (Session, pullup_Plus4Plus1);
+ REGISTER_CLASS_ENUM (Session, pullup_Plus4);
+ REGISTER_CLASS_ENUM (Session, pullup_Plus4Minus1);
+ REGISTER_CLASS_ENUM (Session, pullup_Plus1);
+ REGISTER_CLASS_ENUM (Session, pullup_None);
+ REGISTER_CLASS_ENUM (Session, pullup_Minus1);
+ REGISTER_CLASS_ENUM (Session, pullup_Minus4Plus1);
+ REGISTER_CLASS_ENUM (Session, pullup_Minus4);
+ REGISTER_CLASS_ENUM (Session, pullup_Minus4Minus1);
+ REGISTER (_Session_PullupFormat);
+
+ REGISTER_ENUM (PRE_FADER);
+ REGISTER_ENUM (POST_FADER);
+ REGISTER_ENUM (CONTROL_OUTS);
+ REGISTER_ENUM (MAIN_OUTS);
+ REGISTER (_mute_type);
+
+ REGISTER_CLASS_ENUM (Route, Hidden);
+ REGISTER_CLASS_ENUM (Route, MasterOut);
+ REGISTER_CLASS_ENUM (Route, ControlOut);
+ REGISTER_BITS (_Route_Flag);
+
+ REGISTER_CLASS_ENUM (AudioFileSource, Writable);
+ REGISTER_CLASS_ENUM (AudioFileSource, CanRename);
+ REGISTER_CLASS_ENUM (AudioFileSource, Broadcast);
+ REGISTER_CLASS_ENUM (AudioFileSource, Removable);
+ REGISTER_CLASS_ENUM (AudioFileSource, RemovableIfEmpty);
+ REGISTER_CLASS_ENUM (AudioFileSource, RemoveAtDestroy);
+ REGISTER_CLASS_ENUM (AudioFileSource, NoPeakFile);
+ REGISTER_CLASS_ENUM (AudioFileSource, Destructive);
+ REGISTER_BITS (_AudioFileSource_Flag);
+
+ REGISTER_CLASS_ENUM (AudioRegion, Linear);
+ REGISTER_CLASS_ENUM (AudioRegion, Fast);
+ REGISTER_CLASS_ENUM (AudioRegion, Slow);
+ REGISTER_CLASS_ENUM (AudioRegion, LogA);
+ REGISTER_CLASS_ENUM (AudioRegion, LogB);
+ REGISTER (_AudioRegion_FadeShape);
+
+ REGISTER_CLASS_ENUM (Diskstream, Recordable);
+ REGISTER_CLASS_ENUM (Diskstream, Hidden);
+ REGISTER_CLASS_ENUM (Diskstream, Destructive);
+ REGISTER_BITS (_Diskstream_Flag);
+
+ REGISTER_CLASS_ENUM (Location, IsMark);
+ REGISTER_CLASS_ENUM (Location, IsAutoPunch);
+ REGISTER_CLASS_ENUM (Location, IsAutoLoop);
+ REGISTER_CLASS_ENUM (Location, IsHidden);
+ REGISTER_CLASS_ENUM (Location, IsCDMarker);
+ REGISTER_CLASS_ENUM (Location, IsEnd);
+ REGISTER_CLASS_ENUM (Location, IsRangeMarker);
+ REGISTER_CLASS_ENUM (Location, IsStart);
+ REGISTER_BITS (_Location_Flags);
+
+
+ REGISTER_CLASS_ENUM (RouteGroup, Relative);
+ REGISTER_CLASS_ENUM (RouteGroup, Active);
+ REGISTER_CLASS_ENUM (RouteGroup, Hidden);
+ REGISTER_BITS (_RouteGroup_Flag);
+
+ REGISTER_CLASS_ENUM (Panner, SameDirection);
+ REGISTER_CLASS_ENUM (Panner, OppositeDirection);
+ REGISTER (_Panner_LinkDirection);
+
+ REGISTER_CLASS_ENUM (Region, Muted);
+ REGISTER_CLASS_ENUM (Region, Opaque);
+ REGISTER_CLASS_ENUM (Region, EnvelopeActive);
+ REGISTER_CLASS_ENUM (Region, DefaultFadeIn);
+ REGISTER_CLASS_ENUM (Region, DefaultFadeOut);
+ REGISTER_CLASS_ENUM (Region, Locked);
+ REGISTER_CLASS_ENUM (Region, PositionLocked);
+ REGISTER_CLASS_ENUM (Region, Automatic);
+ REGISTER_CLASS_ENUM (Region, WholeFile);
+ REGISTER_CLASS_ENUM (Region, FadeIn);
+ REGISTER_CLASS_ENUM (Region, FadeOut);
+ REGISTER_CLASS_ENUM (Region, Copied);
+ REGISTER_CLASS_ENUM (Region, Import);
+ REGISTER_CLASS_ENUM (Region, External);
+ REGISTER_CLASS_ENUM (Region, SyncMarked);
+ REGISTER_CLASS_ENUM (Region, LeftOfSplit);
+ REGISTER_CLASS_ENUM (Region, RightOfSplit);
+ REGISTER_CLASS_ENUM (Region, Hidden);
+ REGISTER_CLASS_ENUM (Region, DoNotSaveState);
+ REGISTER_BITS (_Region_Flag);
+
+ REGISTER_CLASS_ENUM (Region, AudioTime);
+ REGISTER_CLASS_ENUM (Region, MusicTime);
+ REGISTER_BITS (_Region_PositionLockStyle);
+
+ REGISTER_CLASS_ENUM (Track, NoFreeze);
+ REGISTER_CLASS_ENUM (Track, Frozen);
+ REGISTER_CLASS_ENUM (Track, UnFrozen);
+ REGISTER (_Track_FreezeState);
+
+ REGISTER_CLASS_ENUM (AutomationList, Discrete);
+ REGISTER_CLASS_ENUM (AutomationList, Linear);
+ REGISTER_CLASS_ENUM (AutomationList, Curved);
+ REGISTER (_AutomationList_InterpolationStyle);
+
+}
diff --git a/libs/ardour/filename_extensions.cc b/libs/ardour/filename_extensions.cc
new file mode 100644
index 0000000000..c51e7aa915
--- /dev/null
+++ b/libs/ardour/filename_extensions.cc
@@ -0,0 +1,15 @@
+#include <ardour/filename_extensions.h>
+
+#include "i18n.h"
+
+namespace ARDOUR {
+
+const char* const template_suffix = X_(".template");
+const char* const statefile_suffix = X_(".ardour");
+const char* const pending_suffix = X_(".pending");
+const char* const peakfile_suffix = X_(".peak");
+const char* const backup_suffix = X_(".bak");
+const char* const temp_suffix = X_(".tmp");
+const char* const history_suffix = X_(".history");
+
+}
diff --git a/libs/ardour/filesystem_paths.cc b/libs/ardour/filesystem_paths.cc
new file mode 100644
index 0000000000..d36bce2146
--- /dev/null
+++ b/libs/ardour/filesystem_paths.cc
@@ -0,0 +1,106 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/error.h>
+#include <pbd/filesystem_paths.h>
+
+#include <glibmm/miscutils.h>
+
+#include <ardour/directory_names.h>
+#include <ardour/filesystem_paths.h>
+
+#define WITH_STATIC_PATHS 1
+
+namespace ARDOUR {
+
+using std::string;
+
+sys::path
+user_config_directory ()
+{
+ const string home_dir = Glib::get_home_dir ();
+
+ if (home_dir.empty ())
+ {
+ const string error_msg = "Unable to determine home directory";
+
+ // log the error
+ error << error_msg << endmsg;
+
+ throw sys::filesystem_error(error_msg);
+ }
+
+ sys::path p(home_dir);
+ p /= user_config_dir_name;
+
+ return p;
+}
+
+sys::path
+ardour_module_directory ()
+{
+ sys::path module_directory(MODULE_DIR);
+ module_directory /= "ardour3";
+ return module_directory;
+}
+
+SearchPath
+ardour_search_path ()
+{
+ SearchPath spath_env(Glib::getenv("ARDOUR_PATH"));
+ return spath_env;
+}
+
+SearchPath
+system_config_search_path ()
+{
+#ifdef WITH_STATIC_PATHS
+
+ SearchPath config_path(string(CONFIG_DIR));
+
+#else
+
+ SearchPath config_path(system_config_directories());
+
+#endif
+
+ config_path.add_subdirectory_to_paths("ardour3");
+
+ return config_path;
+}
+
+SearchPath
+system_data_search_path ()
+{
+#ifdef WITH_STATIC_PATHS
+
+ SearchPath data_path(string(DATA_DIR));
+
+#else
+
+ SearchPath data_path(system_data_directories());
+
+#endif
+
+ data_path.add_subdirectory_to_paths("ardour3");
+
+ return data_path;
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/filter.cc b/libs/ardour/filter.cc
new file mode 100644
index 0000000000..be382f72da
--- /dev/null
+++ b/libs/ardour/filter.cc
@@ -0,0 +1,123 @@
+/*
+ Copyright (C) 2004-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <time.h>
+#include <cerrno>
+
+#include <pbd/basename.h>
+#include <ardour/sndfilesource.h>
+#include <ardour/smf_source.h>
+#include <ardour/session.h>
+#include <ardour/region.h>
+#include <ardour/filter.h>
+#include <ardour/region_factory.h>
+#include <ardour/source_factory.h>
+#include <ardour/analyser.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+int
+Filter::make_new_sources (boost::shared_ptr<Region> region, SourceList& nsrcs, string suffix)
+{
+ vector<string> names = region->master_source_names();
+
+ for (uint32_t i = 0; i < region->n_channels(); ++i) {
+
+ string name = PBD::basename_nosuffix (names[i]);
+
+ /* remove any existing version of suffix by assuming it starts
+ with some kind of "special" character.
+ */
+
+ if (!suffix.empty()) {
+ string::size_type pos = name.find (suffix[0]);
+ if (pos != string::npos && pos > 2) {
+ name = name.substr (0, pos - 1);
+ }
+ }
+
+ string path = session.path_from_region_name (region->data_type(),
+ PBD::basename_nosuffix (names[i]), string (""));
+
+ if (path.length() == 0) {
+ error << string_compose (_("filter: error creating name for new file based on %1"), region->name())
+ << endmsg;
+ return -1;
+ }
+
+ try {
+ nsrcs.push_back (boost::dynamic_pointer_cast<Source> (
+ SourceFactory::createWritable (region->data_type(), session, path, false, session.frame_rate())));
+ }
+
+ catch (failed_constructor& err) {
+ error << string_compose (_("filter: error creating new file %1 (%2)"), path, strerror (errno)) << endmsg;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+Filter::finish (boost::shared_ptr<Region> region, SourceList& nsrcs, string region_name)
+{
+ /* update headers on new sources */
+
+ time_t xnow;
+ struct tm* now;
+
+ time (&xnow);
+ now = localtime (&xnow);
+
+ /* this is ugly. */
+ for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*si);
+ if (afs) {
+ afs->update_header (region->position(), *now, xnow);
+ afs->mark_immutable ();
+ }
+
+ boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource>(*si);
+ if (smfs) {
+ smfs->set_timeline_position (region->position());
+ smfs->flush_footer ();
+ }
+
+ /* now that there is data there, requeue the file for analysis */
+
+ Analyser::queue_source_for_analysis (*si, false);
+ }
+
+ /* create a new region */
+
+ if (region_name.empty()) {
+ region_name = session.new_region_name (region->name());
+ }
+ results.clear ();
+ results.push_back (RegionFactory::create (nsrcs, 0, region->length(), region_name, 0,
+ Region::Flag (Region::WholeFile|Region::DefaultFlags)));
+
+ return 0;
+}
+
+
diff --git a/libs/ardour/find_session.cc b/libs/ardour/find_session.cc
new file mode 100644
index 0000000000..4388d8e299
--- /dev/null
+++ b/libs/ardour/find_session.cc
@@ -0,0 +1,167 @@
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <cstring>
+#include <climits>
+#include <cerrno>
+
+#include <pbd/compose.h>
+#include <pbd/error.h>
+
+#include <ardour/session_utils.h>
+#include <ardour/filename_extensions.h>
+#include <ardour/utils.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+int
+ARDOUR::find_session (string str, string& path, string& snapshot, bool& isnew)
+{
+ struct stat statbuf;
+ char buf[PATH_MAX+1];
+
+ isnew = false;
+
+ if (!realpath (str.c_str(), buf) && (errno != ENOENT && errno != ENOTDIR)) {
+ error << string_compose (_("Could not resolve path: %1 (%2)"), buf, strerror(errno)) << endmsg;
+ return -1;
+ }
+
+ str = buf;
+
+ /* check to see if it exists, and what it is */
+
+ if (stat (str.c_str(), &statbuf)) {
+ if (errno == ENOENT) {
+ isnew = true;
+ } else {
+ error << string_compose (_("cannot check session path %1 (%2)"), str, strerror (errno))
+ << endmsg;
+ return -1;
+ }
+ }
+
+ if (!isnew) {
+
+ /* it exists, so it must either be the name
+ of the directory, or the name of the statefile
+ within it.
+ */
+
+ if (S_ISDIR (statbuf.st_mode)) {
+
+ string::size_type slash = str.find_last_of ('/');
+
+ if (slash == string::npos) {
+
+ /* a subdirectory of cwd, so statefile should be ... */
+
+ string tmp;
+ tmp = str;
+ tmp += '/';
+ tmp += str;
+ tmp += statefile_suffix;
+
+ /* is it there ? */
+
+ if (stat (tmp.c_str(), &statbuf)) {
+ error << string_compose (_("cannot check statefile %1 (%2)"), tmp, strerror (errno))
+ << endmsg;
+ return -1;
+ }
+
+ path = str;
+ snapshot = str;
+
+ } else {
+
+ /* some directory someplace in the filesystem.
+ the snapshot name is the directory name
+ itself.
+ */
+
+ path = str;
+ snapshot = str.substr (slash+1);
+
+ }
+
+ } else if (S_ISREG (statbuf.st_mode)) {
+
+ string::size_type slash = str.find_last_of ('/');
+ string::size_type suffix;
+
+ /* remove the suffix */
+
+ if (slash != string::npos) {
+ snapshot = str.substr (slash+1);
+ } else {
+ snapshot = str;
+ }
+
+ suffix = snapshot.find (statefile_suffix);
+
+ if (suffix == string::npos) {
+ error << string_compose (_("%1 is not an Ardour snapshot file"), str) << endmsg;
+ return -1;
+ }
+
+ /* remove suffix */
+
+ snapshot = snapshot.substr (0, suffix);
+
+ if (slash == string::npos) {
+
+ /* we must be in the directory where the
+ statefile lives. get it using cwd().
+ */
+
+ char cwd[PATH_MAX+1];
+
+ if (getcwd (cwd, sizeof (cwd)) == 0) {
+ error << string_compose (_("cannot determine current working directory (%1)"), strerror (errno))
+ << endmsg;
+ return -1;
+ }
+
+ path = cwd;
+
+ } else {
+
+ /* full path to the statefile */
+
+ path = str.substr (0, slash);
+ }
+
+ } else {
+
+ /* what type of file is it? */
+ error << string_compose (_("unknown file type for session %1"), str) << endmsg;
+ return -1;
+ }
+
+ } else {
+
+ /* its the name of a new directory. get the name
+ as "dirname" does.
+ */
+
+ string::size_type slash = str.find_last_of ('/');
+
+ if (slash == string::npos) {
+
+ /* no slash, just use the name, but clean it up */
+
+ path = legalize_for_path (str);
+ snapshot = path;
+
+ } else {
+
+ path = str;
+ snapshot = str.substr (slash+1);
+ }
+ }
+
+ return 0;
+}
diff --git a/libs/ardour/gain.cc b/libs/ardour/gain.cc
new file mode 100644
index 0000000000..49596d6614
--- /dev/null
+++ b/libs/ardour/gain.cc
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/gain.h>
+
+using namespace ARDOUR;
+
+Gain::Gain ()
+ : AutomationList (Parameter(GainAutomation), 0.0, 2.0, 1.0f) /* XXX yuck; clamps gain to -inf .. +6db */
+{
+}
+
+Gain::Gain (const Gain& other)
+ : AutomationList (other)
+{
+}
+
+Gain&
+Gain::operator= (const Gain& other)
+{
+ if (this != &other) {
+ AutomationList::operator= (other);
+ }
+ return *this;
+}
+
+void
+Gain::fill_linear_volume_fade_in (Gain& gain, nframes_t frames)
+{
+}
+
+void
+Gain::fill_linear_volume_fade_out (Gain& gain, nframes_t frames)
+{
+}
+
+void
+Gain::fill_linear_fade_in (Gain& gain, nframes_t frames)
+{
+}
+
+void
+Gain::fill_linear_fade_out (Gain& gain, nframes_t frames)
+{
+}
diff --git a/libs/ardour/gdither.cc b/libs/ardour/gdither.cc
new file mode 100644
index 0000000000..ac47a0a61e
--- /dev/null
+++ b/libs/ardour/gdither.cc
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2002 Steve Harris <steve@plugin.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <ardour/gdither_types_internal.h>
+#include <ardour/gdither.h>
+#include <ardour/noise.h>
+
+/* this monstrosity is necessary to get access to lrintf() and random().
+ whoever is writing the glibc headers <cmath> and <cstdlib> should be
+ hauled off to a programmer re-education camp. for the rest of
+ their natural lives. or longer. <paul@linuxaudiosystems.com>
+*/
+
+#define _ISOC9X_SOURCE 1
+#define _ISOC99_SOURCE 1
+#ifdef __cplusplus
+#include <cmath>
+#else
+#include <math.h>
+#endif
+
+#undef __USE_SVID
+#define __USE_SVID 1
+#ifdef __cplusplus
+#include <cstdlib>
+#else
+#include <stdlib.h>
+#endif
+
+#include <sys/types.h>
+
+/* Lipshitz's minimally audible FIR, only really works for 46kHz-ish signals */
+static const float shaped_bs[] = { 2.033f, -2.165f, 1.959f, -1.590f, 0.6149f };
+
+/* Some useful constants */
+#define MAX_U8 255
+#define MIN_U8 0
+#define SCALE_U8 128.0f
+
+#define MAX_S16 32767
+#define MIN_S16 -32768
+#define SCALE_S16 32768.0f
+
+#define MAX_S24 8388607
+#define MIN_S24 -8388608
+#define SCALE_S24 8388608.0f
+
+GDither gdither_new(GDitherType type, uint32_t channels,
+
+ GDitherSize bit_depth, int dither_depth)
+{
+ GDither s;
+
+ s = (GDither)calloc(1, sizeof(struct GDither_s));
+ s->type = type;
+ s->channels = channels;
+ s->bit_depth = (int)bit_depth;
+
+ if (dither_depth <= 0 || dither_depth > (int)bit_depth) {
+ dither_depth = (int)bit_depth;
+ }
+ s->dither_depth = dither_depth;
+
+ s->scale = (float)(1LL << (dither_depth - 1));
+ if (bit_depth == GDitherFloat || bit_depth == GDitherDouble) {
+ s->post_scale_fp = 1.0f / s->scale;
+ s->post_scale = 0;
+ } else {
+ s->post_scale_fp = 0.0f;
+ s->post_scale = 1 << ((int)bit_depth - dither_depth);
+ }
+
+ switch (bit_depth) {
+ case GDither8bit:
+ /* Unsigned 8 bit */
+ s->bias = 1.0f;
+ s->clamp_u = 255;
+ s->clamp_l = 0;
+ break;
+ case GDither16bit:
+ /* Signed 16 bit */
+ s->bias = 0.0f;
+ s->clamp_u = 32767;
+ s->clamp_l = -32768;
+ break;
+ case GDither32bit:
+ /* Signed 24 bit, in upper 24 bits of 32 bit word */
+ s->bias = 0.0f;
+ s->clamp_u = 8388607;
+ s->clamp_l = -8388608;
+ break;
+ case GDitherFloat:
+ /* normalised float */
+ s->bias = 0.0f;
+ s->clamp_u = lrintf(s->scale);
+ s->clamp_l = lrintf(-s->scale);
+ break;
+ case GDitherDouble:
+ /* normalised float */
+ s->bias = 0.0f;
+ s->clamp_u = lrintf(s->scale);
+ s->clamp_l = lrintf(-s->scale);
+ break;
+ case 23:
+ /* special performance test case */
+ s->scale = SCALE_S24;
+ s->post_scale = 256;
+ s->bias = 0.0f;
+ s->clamp_u = 8388607;
+ s->clamp_l = -8388608;
+ break;
+ default:
+ /* Not a bit depth we can handle */
+ free(s);
+
+ return NULL;
+ break;
+ }
+
+ switch (type) {
+ case GDitherNone:
+ case GDitherRect:
+ /* No state */
+ break;
+
+ case GDitherTri:
+ /* The last whitenoise sample */
+ s->tri_state = (float *) calloc(channels, sizeof(float));
+ break;
+
+ case GDitherShaped:
+ /* The error from the last few samples encoded */
+ s->shaped_state = (GDitherShapedState*)
+ calloc(channels, sizeof(GDitherShapedState));
+ break;
+ }
+
+ return s;
+}
+
+void gdither_free(GDither s)
+{
+ if (s) {
+ free(s->tri_state);
+ free(s->shaped_state);
+ free(s);
+ }
+}
+
+inline static void gdither_innner_loop(const GDitherType dt,
+ const uint32_t stride, const float bias, const float scale,
+
+ const uint32_t post_scale, const int bit_depth,
+ const uint32_t channel, const uint32_t length, float *ts,
+
+ GDitherShapedState *ss, float *x, void *y, const int clamp_u,
+
+ const int clamp_l)
+{
+ uint32_t pos, i;
+ uint8_t *o8 = (uint8_t*) y;
+ int16_t *o16 = (int16_t*) y;
+ int32_t *o32 = (int32_t*) y;
+ float tmp, r, ideal;
+ int64_t clamped;
+
+ i = channel;
+ for (pos = 0; pos < length; pos++, i += stride) {
+ tmp = x[i] * scale + bias;
+
+ switch (dt) {
+ case GDitherNone:
+ break;
+ case GDitherRect:
+ tmp -= GDITHER_NOISE;
+ break;
+ case GDitherTri:
+ r = GDITHER_NOISE - 0.5f;
+ tmp -= r - ts[channel];
+ ts[channel] = r;
+ break;
+ case GDitherShaped:
+ /* Save raw value for error calculations */
+ ideal = tmp;
+
+ /* Run FIR and add white noise */
+ ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f;
+ tmp += ss->buffer[ss->phase] * shaped_bs[0]
+ + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[1]
+ + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[2]
+ + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[3]
+ + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[4];
+
+ /* Roll buffer and store last error */
+ ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK;
+ ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal;
+ break;
+ }
+
+ clamped = lrintf(tmp);
+ if (clamped > clamp_u) {
+ clamped = clamp_u;
+ } else if (clamped < clamp_l) {
+ clamped = clamp_l;
+ }
+
+ switch (bit_depth) {
+ case GDither8bit:
+ o8[i] = (u_int8_t) (clamped * post_scale);
+ break;
+ case GDither16bit:
+ o16[i] = (int16_t) (clamped * post_scale);
+ break;
+ case GDither32bit:
+ o32[i] = (int32_t) (clamped * post_scale);
+ break;
+ }
+ }
+}
+
+/* floating pint version of the inner loop function */
+inline static void gdither_innner_loop_fp(const GDitherType dt,
+ const uint32_t stride, const float bias, const float scale,
+
+ const float post_scale, const int bit_depth,
+ const uint32_t channel, const uint32_t length, float *ts,
+
+ GDitherShapedState *ss, float *x, void *y, const int clamp_u,
+
+ const int clamp_l)
+{
+ uint32_t pos, i;
+ float *oflt = (float*) y;
+ double *odbl = (double*) y;
+ float tmp, r, ideal;
+ double clamped;
+
+ i = channel;
+ for (pos = 0; pos < length; pos++, i += stride) {
+ tmp = x[i] * scale + bias;
+
+ switch (dt) {
+ case GDitherNone:
+ break;
+ case GDitherRect:
+ tmp -= GDITHER_NOISE;
+ break;
+ case GDitherTri:
+ r = GDITHER_NOISE - 0.5f;
+ tmp -= r - ts[channel];
+ ts[channel] = r;
+ break;
+ case GDitherShaped:
+ /* Save raw value for error calculations */
+ ideal = tmp;
+
+ /* Run FIR and add white noise */
+ ss->buffer[ss->phase] = GDITHER_NOISE * 0.5f;
+ tmp += ss->buffer[ss->phase] * shaped_bs[0]
+ + ss->buffer[(ss->phase - 1) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[1]
+ + ss->buffer[(ss->phase - 2) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[2]
+ + ss->buffer[(ss->phase - 3) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[3]
+ + ss->buffer[(ss->phase - 4) & GDITHER_SH_BUF_MASK]
+ * shaped_bs[4];
+
+ /* Roll buffer and store last error */
+ ss->phase = (ss->phase + 1) & GDITHER_SH_BUF_MASK;
+ ss->buffer[ss->phase] = (float)lrintf(tmp) - ideal;
+ break;
+ }
+
+ clamped = rintf(tmp);
+ if (clamped > clamp_u) {
+ clamped = clamp_u;
+ } else if (clamped < clamp_l) {
+ clamped = clamp_l;
+ }
+
+ switch (bit_depth) {
+ case GDitherFloat:
+ oflt[i] = (float) (clamped * post_scale);
+ break;
+ case GDitherDouble:
+ odbl[i] = (double) (clamped * post_scale);
+ break;
+ }
+ }
+}
+
+#define GDITHER_CONV_BLOCK 512
+
+void gdither_run(GDither s, uint32_t channel, uint32_t length,
+ double *x, void *y)
+{
+ float conv[GDITHER_CONV_BLOCK];
+ uint32_t i, pos;
+ char *ycast = (char *)y;
+
+ int step;
+
+ switch (s->bit_depth) {
+ case GDither8bit:
+ step = 1;
+ break;
+ case GDither16bit:
+ step = 2;
+ break;
+ case GDither32bit:
+ case GDitherFloat:
+ step = 4;
+ break;
+ case GDitherDouble:
+ step = 8;
+ break;
+ default:
+ step = 0;
+ break;
+ }
+
+ pos = 0;
+ while (pos < length) {
+ for (i=0; (i + pos) < length && i < GDITHER_CONV_BLOCK; i++) {
+ conv[i] = x[pos + i];
+ }
+ gdither_runf(s, channel, i, conv, ycast + s->channels * step);
+ pos += i;
+ }
+}
+
+void gdither_runf(GDither s, uint32_t channel, uint32_t length,
+ float *x, void *y)
+{
+ uint32_t pos, i;
+ float tmp;
+ int64_t clamped;
+ GDitherShapedState *ss = NULL;
+
+ if (!s || channel >= s->channels) {
+ return;
+ }
+
+ if (s->shaped_state) {
+ ss = s->shaped_state + channel;
+ }
+
+ if (s->type == GDitherNone && s->bit_depth == 23) {
+ int32_t *o32 = (int32_t*) y;
+
+ for (pos = 0; pos < length; pos++) {
+ i = channel + (pos * s->channels);
+ tmp = x[i] * 8388608.0f;
+
+ clamped = lrintf(tmp);
+ if (clamped > 8388607) {
+ clamped = 8388607;
+ } else if (clamped < -8388608) {
+ clamped = -8388608;
+ }
+
+ o32[i] = (int32_t) (clamped * 256);
+ }
+
+ return;
+ }
+
+ /* some common case handling code - looks a bit wierd, but it allows
+ * the compiler to optimise out the branches in the inner loop */
+ if (s->bit_depth == 8 && s->dither_depth == 8) {
+ switch (s->type) {
+ case GDitherNone:
+ gdither_innner_loop(GDitherNone, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, NULL, NULL, x, y,
+ MAX_U8, MIN_U8);
+ break;
+ case GDitherRect:
+ gdither_innner_loop(GDitherRect, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, NULL, NULL, x, y,
+ MAX_U8, MIN_U8);
+ break;
+ case GDitherTri:
+ gdither_innner_loop(GDitherTri, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, s->tri_state,
+ NULL, x, y, MAX_U8, MIN_U8);
+ break;
+ case GDitherShaped:
+ gdither_innner_loop(GDitherShaped, s->channels, 128.0f, SCALE_U8,
+ 1, 8, channel, length, NULL,
+ ss, x, y, MAX_U8, MIN_U8);
+ break;
+ }
+ } else if (s->bit_depth == 16 && s->dither_depth == 16) {
+ switch (s->type) {
+ case GDitherNone:
+ gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S16,
+ 1, 16, channel, length, NULL, NULL, x, y,
+ MAX_S16, MIN_S16);
+ break;
+ case GDitherRect:
+ gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S16,
+ 1, 16, channel, length, NULL, NULL, x, y,
+ MAX_S16, MIN_S16);
+ break;
+ case GDitherTri:
+ gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S16,
+ 1, 16, channel, length, s->tri_state,
+ NULL, x, y, MAX_S16, MIN_S16);
+ break;
+ case GDitherShaped:
+ gdither_innner_loop(GDitherShaped, s->channels, 0.0f,
+ SCALE_S16, 1, 16, channel, length, NULL,
+ ss, x, y, MAX_S16, MIN_S16);
+ break;
+ }
+ } else if (s->bit_depth == 32 && s->dither_depth == 24) {
+ switch (s->type) {
+ case GDitherNone:
+ gdither_innner_loop(GDitherNone, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length, NULL, NULL, x,
+ y, MAX_S24, MIN_S24);
+ break;
+ case GDitherRect:
+ gdither_innner_loop(GDitherRect, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length, NULL, NULL, x,
+ y, MAX_S24, MIN_S24);
+ break;
+ case GDitherTri:
+ gdither_innner_loop(GDitherTri, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length, s->tri_state,
+ NULL, x, y, MAX_S24, MIN_S24);
+ break;
+ case GDitherShaped:
+ gdither_innner_loop(GDitherShaped, s->channels, 0.0f, SCALE_S24,
+ 256, 32, channel, length,
+ NULL, ss, x, y, MAX_S24, MIN_S24);
+ break;
+ }
+ } else if (s->bit_depth == GDitherFloat || s->bit_depth == GDitherDouble) {
+ gdither_innner_loop_fp(s->type, s->channels, s->bias, s->scale,
+ s->post_scale_fp, s->bit_depth, channel, length,
+ s->tri_state, ss, x, y, s->clamp_u, s->clamp_l);
+ } else {
+ /* no special case handling, just process it from the struct */
+
+ gdither_innner_loop(s->type, s->channels, s->bias, s->scale,
+ s->post_scale, s->bit_depth, channel,
+ length, s->tri_state, ss, x, y, s->clamp_u,
+ s->clamp_l);
+ }
+}
+
+/* vi:set ts=8 sts=4 sw=4: */
diff --git a/libs/ardour/gettext.h b/libs/ardour/gettext.h
new file mode 100644
index 0000000000..339c74ffe7
--- /dev/null
+++ b/libs/ardour/gettext.h
@@ -0,0 +1,82 @@
+/* Convenience header for conditional use of GNU <libintl.h>.
+ Copyright (C) 1995-1998, 2000-2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU Library General Public License as published
+ by the Free Software Foundation; either version 2, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ USA. */
+
+#ifndef _LIBGETTEXT_H
+#define _LIBGETTEXT_H 1
+
+/* NLS can be disabled through the configure --disable-nls option. */
+#if ENABLE_NLS
+
+/* Get declarations of GNU message catalog functions. */
+# include <libintl.h>
+
+#else
+
+/* Solaris /usr/include/locale.h includes /usr/include/libintl.h, which
+ chokes if dcgettext is defined as a macro. So include it now, to make
+ later inclusions of <locale.h> a NOP. We don't include <libintl.h>
+ as well because people using "gettext.h" will not include <libintl.h>,
+ and also including <libintl.h> would fail on SunOS 4, whereas <locale.h>
+ is OK. */
+#if defined(__sun)
+# include <locale.h>
+#endif
+
+/* Disabled NLS.
+ The casts to 'const char *' serve the purpose of producing warnings
+ for invalid uses of the value returned from these functions.
+ On pre-ANSI systems without 'const', the config.h file is supposed to
+ contain "#define const". */
+
+/* other headers may have included libintl.h */
+
+# undef gettext
+# undef dgettext
+# undef dcgettext
+# undef ngettext
+# undef dngettext
+# undef dcngettext
+# undef textdomain
+# undef bindtextdomain
+# undef bind_textdomain_codeset
+
+# define gettext(Msgid) ((const char *) (Msgid))
+# define dgettext(Domainname, Msgid) ((const char *) (Msgid))
+# define dcgettext(Domainname, Msgid, Category) ((const char *) (Msgid))
+# define ngettext(Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dngettext(Domainname, Msgid1, Msgid2, N) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define dcngettext(Domainname, Msgid1, Msgid2, N, Category) \
+ ((N) == 1 ? (const char *) (Msgid1) : (const char *) (Msgid2))
+# define textdomain(Domainname) ((const char *) (Domainname))
+# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname))
+# define bind_textdomain_codeset(Domainname, Codeset) ((const char *) (Codeset))
+
+#endif
+
+/* A pseudo function call that serves as a marker for the automated
+ extraction of messages, but does not call gettext(). The run-time
+ translation is done at a different place in the code.
+ The argument, String, should be a literal string. Concatenated strings
+ and other string expressions won't work.
+ The macro's expansion is not parenthesized, so that it is suitable as
+ initializer for static 'char[]' or 'const char[]' variables. */
+#define gettext_noop(String) String
+
+#endif /* _LIBGETTEXT_H */
diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc
new file mode 100644
index 0000000000..02c4a5ced6
--- /dev/null
+++ b/libs/ardour/globals.cc
@@ -0,0 +1,619 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cstdio> // Needed so that libraptor (included in lrdf) won't complain
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <errno.h>
+
+#ifdef VST_SUPPORT
+#include <fst.h>
+#endif
+
+#ifdef __SSE__
+#include <xmmintrin.h>
+#endif
+
+#include <glibmm/fileutils.h>
+#include <glibmm/miscutils.h>
+
+#include <lrdf.h>
+
+#include <pbd/error.h>
+#include <pbd/id.h>
+#include <pbd/strsplit.h>
+#include <pbd/fpu.h>
+#include <pbd/file_utils.h>
+
+#include <midi++/port.h>
+#include <midi++/manager.h>
+#include <midi++/mmc.h>
+
+#include <ardour/ardour.h>
+#include <ardour/analyser.h>
+#include <ardour/audio_library.h>
+#include <ardour/configuration.h>
+#include <ardour/profile.h>
+#include <ardour/plugin_manager.h>
+#include <ardour/audiosource.h>
+#include <ardour/utils.h>
+#include <ardour/session.h>
+#include <ardour/source_factory.h>
+#include <ardour/control_protocol_manager.h>
+#include <ardour/audioengine.h>
+#include <ardour/filesystem_paths.h>
+
+#ifdef HAVE_LIBLO
+#include <ardour/osc.h>
+#endif
+
+#include <ardour/mix.h>
+#include <ardour/runtime_functions.h>
+
+#if defined (__APPLE__)
+ #include <Carbon/Carbon.h> // For Gestalt
+#endif
+
+#include "i18n.h"
+
+ARDOUR::Configuration* ARDOUR::Config = 0;
+ARDOUR::RuntimeProfile* ARDOUR::Profile = 0;
+ARDOUR::AudioLibrary* ARDOUR::Library = 0;
+
+#ifdef HAVE_LIBLO
+ARDOUR::OSC* ARDOUR::osc = 0;
+#endif
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+
+MIDI::Port *default_mmc_port = 0;
+MIDI::Port *default_mtc_port = 0;
+MIDI::Port *default_midi_port = 0;
+
+Change ARDOUR::StartChanged = ARDOUR::new_change ();
+Change ARDOUR::LengthChanged = ARDOUR::new_change ();
+Change ARDOUR::PositionChanged = ARDOUR::new_change ();
+Change ARDOUR::NameChanged = ARDOUR::new_change ();
+Change ARDOUR::BoundsChanged = Change (0); // see init(), below
+
+compute_peak_t ARDOUR::compute_peak = 0;
+find_peaks_t ARDOUR::find_peaks = 0;
+apply_gain_to_buffer_t ARDOUR::apply_gain_to_buffer = 0;
+mix_buffers_with_gain_t ARDOUR::mix_buffers_with_gain = 0;
+mix_buffers_no_gain_t ARDOUR::mix_buffers_no_gain = 0;
+
+sigc::signal<void,std::string> ARDOUR::BootMessage;
+
+#ifdef HAVE_LIBLO
+static int
+setup_osc ()
+{
+ /* no real cost to creating this object, and it avoids
+ conditionals anywhere that uses it
+ */
+
+ osc = new OSC (Config->get_osc_port());
+
+ if (Config->get_use_osc ()) {
+ BootMessage (_("Starting OSC"));
+ return osc->start ();
+ } else {
+ return 0;
+ }
+}
+#endif
+
+int
+setup_midi ()
+{
+ if (Config->midi_ports.size() == 0) {
+ warning << _("no MIDI ports specified: no MMC or MTC control possible") << endmsg;
+ return 0;
+ }
+
+ BootMessage (_("Configuring MIDI ports"));
+
+ for (std::map<string,XMLNode>::iterator i = Config->midi_ports.begin(); i != Config->midi_ports.end(); ++i) {
+ MIDI::Manager::instance()->add_port (i->second);
+ }
+
+ MIDI::Port* first;
+ const MIDI::Manager::PortMap& ports = MIDI::Manager::instance()->get_midi_ports();
+
+ if (ports.size() > 1) {
+
+ first = ports.begin()->second;
+
+ /* More than one port, so try using specific names for each port */
+
+ if (Config->get_mmc_port_name() != N_("default")) {
+ default_mmc_port = MIDI::Manager::instance()->port (Config->get_mmc_port_name());
+ }
+
+ if (Config->get_mtc_port_name() != N_("default")) {
+ default_mtc_port = MIDI::Manager::instance()->port (Config->get_mtc_port_name());
+ }
+
+ if (Config->get_midi_port_name() != N_("default")) {
+ default_midi_port = MIDI::Manager::instance()->port (Config->get_midi_port_name());
+ }
+
+ /* If that didn't work, just use the first listed port */
+
+ if (default_mmc_port == 0) {
+ default_mmc_port = first;
+ }
+
+ if (default_mtc_port == 0) {
+ default_mtc_port = first;
+ }
+
+ if (default_midi_port == 0) {
+ default_midi_port = first;
+ }
+
+ } else if (ports.size() == 1) {
+
+ first = ports.begin()->second;
+
+ /* Only one port described, so use it for both MTC and MMC */
+
+ default_mmc_port = first;
+ default_mtc_port = default_mmc_port;
+ default_midi_port = default_mmc_port;
+ }
+
+ if (default_mmc_port == 0) {
+ warning << string_compose (_("No MMC control (MIDI port \"%1\" not available)"), Config->get_mmc_port_name())
+ << endmsg;
+ return 0;
+ }
+
+ if (default_mtc_port == 0) {
+ warning << string_compose (_("No MTC support (MIDI port \"%1\" not available)"), Config->get_mtc_port_name())
+ << endmsg;
+ }
+
+ if (default_midi_port == 0) {
+ warning << string_compose (_("No MIDI parameter support (MIDI port \"%1\" not available)"), Config->get_midi_port_name())
+ << endmsg;
+ }
+
+ return 0;
+}
+
+void
+setup_hardware_optimization (bool try_optimization)
+{
+ bool generic_mix_functions = true;
+
+ if (try_optimization) {
+
+ FPU fpu;
+
+#if defined (ARCH_X86) && defined (BUILD_SSE_OPTIMIZATIONS)
+
+ if (fpu.has_sse()) {
+
+ info << "Using SSE optimized routines" << endmsg;
+
+ // SSE SET
+ compute_peak = x86_sse_compute_peak;
+ find_peaks = x86_sse_find_peaks;
+ apply_gain_to_buffer = x86_sse_apply_gain_to_buffer;
+ mix_buffers_with_gain = x86_sse_mix_buffers_with_gain;
+ mix_buffers_no_gain = x86_sse_mix_buffers_no_gain;
+
+ generic_mix_functions = false;
+
+ }
+
+#elif defined (__APPLE__) && defined (BUILD_VECLIB_OPTIMIZATIONS)
+ long sysVersion = 0;
+
+ if (noErr != Gestalt(gestaltSystemVersion, &sysVersion))
+ sysVersion = 0;
+
+ if (sysVersion >= 0x00001040) { // Tiger at least
+ compute_peak = veclib_compute_peak;
+ find_peaks = veclib_find_peaks;
+ apply_gain_to_buffer = veclib_apply_gain_to_buffer;
+ mix_buffers_with_gain = veclib_mix_buffers_with_gain;
+ mix_buffers_no_gain = veclib_mix_buffers_no_gain;
+
+ generic_mix_functions = false;
+
+ info << "Apple VecLib H/W specific optimizations in use" << endmsg;
+ }
+#endif
+
+ /* consider FPU denormal handling to be "h/w optimization" */
+
+ setup_fpu ();
+ }
+
+ if (generic_mix_functions) {
+
+ compute_peak = default_compute_peak;
+ find_peaks = default_find_peaks;
+ apply_gain_to_buffer = default_apply_gain_to_buffer;
+ mix_buffers_with_gain = default_mix_buffers_with_gain;
+ mix_buffers_no_gain = default_mix_buffers_no_gain;
+
+ info << "No H/W specific optimizations in use" << endmsg;
+ }
+}
+
+static void
+lotsa_files_please ()
+{
+ struct rlimit rl;
+
+ if (getrlimit (RLIMIT_NOFILE, &rl) == 0) {
+
+ rl.rlim_cur = rl.rlim_max;
+
+ if (setrlimit (RLIMIT_NOFILE, &rl) != 0) {
+ if (rl.rlim_cur == RLIM_INFINITY) {
+ error << _("Could not set system open files limit to \"unlimited\"") << endmsg;
+ } else {
+ error << string_compose (_("Could not set system open files limit to %1"), rl.rlim_cur) << endmsg;
+ }
+ } else {
+ if (rl.rlim_cur == RLIM_INFINITY) {
+ info << _("Removed open file count limit. Excellent!") << endmsg;
+ } else {
+ info << string_compose (_("Ardour will be limited to %1 open files"), rl.rlim_cur) << endmsg;
+ }
+ }
+ } else {
+ error << string_compose (_("Could not get system open files limit (%1)"), strerror (errno)) << endmsg;
+ }
+}
+
+int
+ARDOUR::init (bool use_vst, bool try_optimization)
+{
+ extern void setup_enum_writer ();
+
+ (void) bindtextdomain(PACKAGE, LOCALEDIR);
+
+ setup_enum_writer ();
+
+ // allow ardour the absolute maximum number of open files
+ lotsa_files_please ();
+
+ lrdf_init();
+ Library = new AudioLibrary;
+
+ BootMessage (_("Loading configuration"));
+
+ Config = new Configuration;
+
+ if (Config->load_state ()) {
+ return -1;
+ }
+
+ Config->set_use_vst (use_vst);
+
+ Profile = new RuntimeProfile;
+
+#ifdef HAVE_LIBLO
+ if (setup_osc ()) {
+ return -1;
+ }
+#endif
+
+#ifdef VST_SUPPORT
+ if (Config->get_use_vst() && fst_init ()) {
+ 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);
+
+ SourceFactory::init ();
+ Analyser::init ();
+
+ /* singleton - first object is "it" */
+ new PluginManager ();
+
+ /* singleton - first object is "it" */
+ new ControlProtocolManager ();
+ ControlProtocolManager::instance().discover_control_protocols ();
+
+ XMLNode* node;
+ if ((node = Config->control_protocol_state()) != 0) {
+ ControlProtocolManager::instance().set_state (*node);
+ }
+
+ BoundsChanged = Change (StartChanged|PositionChanged|LengthChanged);
+
+ return 0;
+}
+
+int
+ARDOUR::cleanup ()
+{
+ delete Library;
+ lrdf_cleanup ();
+ delete &ControlProtocolManager::instance();
+ return 0;
+}
+
+
+microseconds_t
+ARDOUR::get_microseconds ()
+{
+ /* XXX need JACK to export its functionality */
+
+ struct timeval now;
+ gettimeofday (&now, 0);
+ return now.tv_sec * 1000000ULL + now.tv_usec;
+}
+
+ARDOUR::Change
+ARDOUR::new_change ()
+{
+ Change c;
+ static uint32_t change_bit = 1;
+
+ /* catch out-of-range */
+ if (!change_bit)
+ {
+ fatal << _("programming error: ")
+ << "change_bit out of range in ARDOUR::new_change()"
+ << endmsg;
+ /*NOTREACHED*/
+ }
+
+ c = Change (change_bit);
+ change_bit <<= 1; // if it shifts too far, change_bit == 0
+
+ return c;
+}
+
+string
+ARDOUR::get_ardour_revision ()
+{
+ return "$Rev$";
+}
+
+void
+ARDOUR::find_bindings_files (map<string,string>& files)
+{
+ vector<sys::path> found;
+
+ SearchPath spath = ardour_search_path() + user_config_directory() + system_config_search_path();
+
+ if (getenv ("ARDOUR_SAE")) {
+ Glib::PatternSpec pattern("*SAE-*.bindings");
+ find_matching_files_in_search_path (spath, pattern, found);
+ } else {
+ Glib::PatternSpec pattern("*.bindings");
+ find_matching_files_in_search_path (spath, pattern, found);
+ }
+
+ if (found.empty()) {
+ return;
+ }
+
+ for (vector<sys::path>::iterator x = found.begin(); x != found.end(); ++x) {
+ sys::path path = *x;
+ pair<string,string> namepath;
+ namepath.second = path.to_string();
+ namepath.first = path.leaf().substr (0, path.leaf().find_first_of ('.'));
+ files.insert (namepath);
+ }
+}
+
+ARDOUR::LocaleGuard::LocaleGuard (const char* str)
+{
+ old = strdup (setlocale (LC_NUMERIC, NULL));
+ if (strcmp (old, str)) {
+ setlocale (LC_NUMERIC, str);
+ }
+}
+
+ARDOUR::LocaleGuard::~LocaleGuard ()
+{
+ setlocale (LC_NUMERIC, old);
+ free ((char*)old);
+}
+
+void
+ARDOUR::setup_fpu ()
+{
+
+ if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) {
+ // valgrind doesn't understand this assembler stuff
+ // September 10th, 2007
+ return;
+ }
+
+#if defined(ARCH_X86) && defined(USE_XMMINTRIN)
+
+ int MXCSR;
+ FPU fpu;
+
+ /* XXX use real code to determine if the processor supports
+ DenormalsAreZero and FlushToZero
+ */
+
+ if (!fpu.has_flush_to_zero() && !fpu.has_denormals_are_zero()) {
+ return;
+ }
+
+ MXCSR = _mm_getcsr();
+
+ switch (Config->get_denormal_model()) {
+ case DenormalNone:
+ MXCSR &= ~(_MM_FLUSH_ZERO_ON|0x8000);
+ break;
+
+ case DenormalFTZ:
+ if (fpu.has_flush_to_zero()) {
+ MXCSR |= _MM_FLUSH_ZERO_ON;
+ }
+ break;
+
+ case DenormalDAZ:
+ MXCSR &= ~_MM_FLUSH_ZERO_ON;
+ if (fpu.has_denormals_are_zero()) {
+ MXCSR |= 0x8000;
+ }
+ break;
+
+ case DenormalFTZDAZ:
+ if (fpu.has_flush_to_zero()) {
+ if (fpu.has_denormals_are_zero()) {
+ MXCSR |= _MM_FLUSH_ZERO_ON | 0x8000;
+ } else {
+ MXCSR |= _MM_FLUSH_ZERO_ON;
+ }
+ }
+ break;
+ }
+
+ _mm_setcsr (MXCSR);
+
+#endif
+}
+
+ARDOUR::OverlapType
+ARDOUR::coverage (nframes_t sa, nframes_t ea,
+ nframes_t sb, nframes_t eb)
+{
+ /* OverlapType returned reflects how the second (B)
+ range overlaps the first (A).
+
+ The diagrams show various relative placements
+ of A and B for each OverlapType.
+
+ Notes:
+ Internal: the start points cannot coincide
+ External: the start and end points can coincide
+ Start: end points can coincide
+ End: start points can coincide
+
+ XXX Logically, Internal should disallow end
+ point equality.
+ */
+
+ /*
+ |--------------------| A
+ |------| B
+ |-----------------| B
+
+
+ "B is internal to A"
+
+ */
+#ifdef OLD_COVERAGE
+ if ((sb >= sa) && (eb <= ea)) {
+#else
+ if ((sb > sa) && (eb <= ea)) {
+#endif
+ return OverlapInternal;
+ }
+
+ /*
+ |--------------------| A
+ ----| B
+ -----------------------| B
+ --| B
+
+ "B overlaps the start of A"
+
+ */
+
+ if ((eb >= sa) && (eb <= ea)) {
+ return OverlapStart;
+ }
+ /*
+ |---------------------| A
+ |----------------- B
+ |----------------------- B
+ |- B
+
+ "B overlaps the end of A"
+
+ */
+ if ((sb > sa) && (sb <= ea)) {
+ return OverlapEnd;
+ }
+ /*
+ |--------------------| A
+ -------------------------- B
+ |----------------------- B
+ ----------------------| B
+ |--------------------| B
+
+
+ "B overlaps all of A"
+ */
+ if ((sa >= sb) && (sa <= eb) && (ea <= eb)) {
+ return OverlapExternal;
+ }
+
+ return OverlapNone;
+}
+
+/* not sure where to put these */
+
+template<class T>
+std::istream& int_to_type (std::istream& o, T& hf) {
+ int val;
+ o >> val;
+ hf = (T) val;
+ return o;
+}
+
+std::istream& operator>>(std::istream& o, HeaderFormat& var) { return int_to_type<HeaderFormat> (o, var); }
+std::istream& operator>>(std::istream& o, SampleFormat& var) { return int_to_type<SampleFormat> (o, var); }
+std::istream& operator>>(std::istream& o, AutoConnectOption& var) { return int_to_type<AutoConnectOption> (o, var); }
+std::istream& operator>>(std::istream& o, MonitorModel& var) { return int_to_type<MonitorModel> (o, var); }
+std::istream& operator>>(std::istream& o, RemoteModel& var) { return int_to_type<RemoteModel> (o, var); }
+std::istream& operator>>(std::istream& o, EditMode& var) { return int_to_type<EditMode> (o, var); }
+std::istream& operator>>(std::istream& o, SoloModel& var) { return int_to_type<SoloModel> (o, var); }
+std::istream& operator>>(std::istream& o, LayerModel& var) { return int_to_type<LayerModel> (o, var); }
+std::istream& operator>>(std::istream& o, CrossfadeModel& var) { return int_to_type<CrossfadeModel> (o, var); }
+std::istream& operator>>(std::istream& o, SlaveSource& var) { return int_to_type<SlaveSource> (o, var); }
+std::istream& operator>>(std::istream& o, ShuttleBehaviour& var) { return int_to_type<ShuttleBehaviour> (o, var); }
+std::istream& operator>>(std::istream& o, ShuttleUnits& var) { return int_to_type<ShuttleUnits> (o, var); }
+std::istream& operator>>(std::istream& o, SmpteFormat& var) { return int_to_type<SmpteFormat> (o, var); }
+std::istream& operator>>(std::istream& o, DenormalModel& var) { return int_to_type<DenormalModel> (o, var); }
+
diff --git a/libs/ardour/i18n.h b/libs/ardour/i18n.h
new file mode 100644
index 0000000000..5d68c79edd
--- /dev/null
+++ b/libs/ardour/i18n.h
@@ -0,0 +1,16 @@
+#ifndef __i18n_h__
+#define __i18n_h__
+
+#include <pbd/compose.h>
+#include <pbd/convert.h>
+#include "gettext.h"
+
+#include <vector>
+#include <string>
+
+#define _(Text) dgettext (PACKAGE,Text)
+#define N_(Text) gettext_noop (Text)
+#define X_(Text) Text
+#define I18N(Array) PBD::internationalize (PACKAGE, Array)
+
+#endif // __i18n_h__
diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc
new file mode 100644
index 0000000000..d79c930e6d
--- /dev/null
+++ b/libs/ardour/import.cc
@@ -0,0 +1,491 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+#include <climits>
+#include <cerrno>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
+
+#include <sndfile.h>
+#include <samplerate.h>
+
+#include <glibmm.h>
+
+#include <boost/scoped_array.hpp>
+#include <boost/shared_array.hpp>
+
+#include <pbd/basename.h>
+#include <pbd/convert.h>
+
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+#include <ardour/session_directory.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/audioengine.h>
+#include <ardour/sndfilesource.h>
+#include <ardour/sndfile_helpers.h>
+#include <ardour/audioregion.h>
+#include <ardour/region_factory.h>
+#include <ardour/source_factory.h>
+#include <ardour/resampled_source.h>
+#include <ardour/sndfileimportable.h>
+#include <ardour/analyser.h>
+#include <ardour/smf_reader.h>
+#include <ardour/smf_source.h>
+#include <ardour/tempo.h>
+
+#ifdef HAVE_COREAUDIO
+#include <ardour/caimportable.h>
+#endif
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+
+static boost::shared_ptr<ImportableSource>
+open_importable_source (const string& path, nframes_t samplerate, ARDOUR::SrcQuality quality)
+{
+#ifdef HAVE_COREAUDIO
+
+ /* see if we can use CoreAudio to handle the IO */
+
+ try {
+ boost::shared_ptr<CAImportableSource> source(new CAImportableSource(path));
+
+ if (source->samplerate() == samplerate) {
+ return source;
+ }
+
+ /* rewrap as a resampled source */
+
+ return boost::shared_ptr<ImportableSource>(new ResampledImportableSource(source, samplerate, quality));
+ }
+
+ catch (...) {
+
+ /* fall back to SndFile */
+
+#endif
+
+ try {
+ boost::shared_ptr<SndFileImportableSource> source(new SndFileImportableSource(path));
+
+ if (source->samplerate() == samplerate) {
+ return source;
+ }
+
+ /* rewrap as a resampled source */
+
+ return boost::shared_ptr<ImportableSource>(new ResampledImportableSource(source, samplerate, quality));
+ }
+
+ catch (...) {
+ throw; // rethrow
+ }
+
+#ifdef HAVE_COREAUDIO
+ }
+#endif
+}
+
+static std::string
+get_non_existent_filename (DataType type, const bool allow_replacing, const std::string destdir, const std::string& basename, uint channel, uint channels)
+{
+ char buf[PATH_MAX+1];
+ bool goodfile = false;
+ string base(basename);
+ const char* ext = (type == DataType::AUDIO) ? "wav" : "mid";
+
+ do {
+
+ if (type == DataType::AUDIO && channels == 2) {
+ if (channel == 0) {
+ snprintf (buf, sizeof(buf), "%s-L.wav", base.c_str());
+ } else {
+ snprintf (buf, sizeof(buf), "%s-R.wav", base.c_str());
+ }
+ } else if (channels > 1) {
+ snprintf (buf, sizeof(buf), "%s-c%d.%s", base.c_str(), channel, ext);
+ } else {
+ snprintf (buf, sizeof(buf), "%s.%s", base.c_str(), ext);
+ }
+
+
+ string tempname = destdir + "/" + buf;
+ if (!allow_replacing && Glib::file_test (tempname, Glib::FILE_TEST_EXISTS)) {
+
+ /* if the file already exists, we must come up with
+ * a new name for it. for now we just keep appending
+ * _ to basename
+ */
+
+ base += "_";
+
+ } else {
+
+ goodfile = true;
+ }
+
+ } while ( !goodfile);
+
+ return buf;
+}
+
+static vector<string>
+get_paths_for_new_sources (const bool allow_replacing, const string& import_file_path, const string& session_dir, uint channels)
+{
+ vector<string> new_paths;
+ const string basename = basename_nosuffix (import_file_path);
+
+ SessionDirectory sdir(session_dir);
+
+ for (uint n = 0; n < channels; ++n) {
+
+ const DataType type = (import_file_path.rfind(".mid") != string::npos)
+ ? DataType::MIDI : DataType::AUDIO;
+
+ std::string filepath = (type == DataType::MIDI)
+ ? sdir.midi_path().to_string() : sdir.sound_path().to_string();
+
+ filepath += '/';
+ filepath += get_non_existent_filename (type, allow_replacing, filepath, basename, n, channels);
+ new_paths.push_back (filepath);
+ }
+
+ return new_paths;
+}
+
+static bool
+map_existing_mono_sources (const vector<string>& new_paths, Session& sess,
+ uint samplerate, vector<boost::shared_ptr<Source> >& newfiles, Session *session)
+{
+ for (vector<string>::const_iterator i = new_paths.begin();
+ i != new_paths.end(); ++i)
+ {
+ boost::shared_ptr<Source> source = session->source_by_path_and_channel(*i, 0);
+
+ if (source == 0) {
+ error << string_compose(_("Could not find a source for %1 even though we are updating this file!"), (*i)) << endl;
+ return false;
+ }
+
+ newfiles.push_back(boost::dynamic_pointer_cast<Source>(source));
+ }
+ return true;
+}
+
+static bool
+create_mono_sources_for_writing (const vector<string>& new_paths, Session& sess,
+ uint samplerate, vector<boost::shared_ptr<Source> >& newfiles)
+{
+ for (vector<string>::const_iterator i = new_paths.begin();
+ i != new_paths.end(); ++i)
+ {
+ boost::shared_ptr<Source> source;
+
+ try
+ {
+ const DataType type = ((*i).rfind(".mid") != string::npos)
+ ? DataType::MIDI : DataType::AUDIO;
+
+ source = SourceFactory::createWritable (
+ type,
+ sess,
+ i->c_str(),
+ false, // destructive
+ samplerate
+ );
+ }
+ catch (const failed_constructor& err)
+ {
+ error << string_compose (_("Unable to create file %1 during import"), *i) << endmsg;
+ return false;
+ }
+
+ newfiles.push_back(boost::dynamic_pointer_cast<Source>(source));
+ }
+ return true;
+}
+
+static Glib::ustring
+compose_status_message (const string& path,
+ uint file_samplerate,
+ uint session_samplerate,
+ uint current_file,
+ uint total_files)
+{
+ if (file_samplerate != session_samplerate) {
+ return string_compose (_("converting %1\n(resample from %2KHz to %3KHz)\n(%4 of %5)"),
+ Glib::path_get_basename (path),
+ file_samplerate/1000.0f,
+ session_samplerate/1000.0f,
+ current_file, total_files);
+ }
+
+ return string_compose (_("converting %1\n(%2 of %3)"),
+ Glib::path_get_basename (path),
+ current_file, total_files);
+}
+
+static void
+write_audio_data_to_new_files (ImportableSource* source, Session::import_status& status,
+ vector<boost::shared_ptr<Source> >& newfiles)
+{
+ const nframes_t nframes = ResampledImportableSource::blocksize;
+ boost::shared_ptr<AudioFileSource> afs;
+ uint channels = source->channels();
+
+ boost::scoped_array<float> data(new float[nframes * channels]);
+ vector<boost::shared_array<Sample> > channel_data;
+
+ for (uint n = 0; n < channels; ++n) {
+ channel_data.push_back(boost::shared_array<Sample>(new Sample[nframes]));
+ }
+
+ uint read_count = 0;
+ status.progress = 0.0f;
+
+ while (!status.cancel) {
+
+ nframes_t nread, nfread;
+ uint x;
+ uint chn;
+
+ if ((nread = source->read (data.get(), nframes)) == 0) {
+ break;
+ }
+ nfread = nread / channels;
+
+ /* de-interleave */
+
+ for (chn = 0; chn < channels; ++chn) {
+
+ nframes_t n;
+ for (x = chn, n = 0; n < nfread; x += channels, ++n) {
+ channel_data[chn][n] = (Sample) data[x];
+ }
+ }
+
+ /* flush to disk */
+
+ for (chn = 0; chn < channels; ++chn) {
+ if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(newfiles[chn])) != 0) {
+ afs->write (channel_data[chn].get(), nfread);
+ }
+ }
+
+ read_count += nread;
+ status.progress = read_count / (source->ratio () * source->length() * channels);
+ }
+}
+
+static void
+write_midi_data_to_new_files (SMFReader* source, Session::import_status& status,
+ vector<boost::shared_ptr<Source> >& newfiles)
+{
+ MIDI::Event ev(0.0, 4, NULL, true);
+
+ status.progress = 0.0f;
+
+ try {
+
+ for (unsigned i = 1; i <= source->num_tracks(); ++i) {
+
+ boost::shared_ptr<SMFSource> smfs = boost::dynamic_pointer_cast<SMFSource>(newfiles[i-1]);
+
+ source->seek_to_track(i);
+
+ uint64_t t = 0;
+ uint32_t delta_t = 0;
+ uint32_t size = 0;
+
+ while (!status.cancel) {
+
+ if (source->read_event(4, ev.buffer(), &size, &delta_t) < 0)
+ break;
+
+ t += delta_t;
+ ev.time() = (double)t / (double)source->ppqn();
+ ev.size() = size;
+
+ smfs->append_event_unlocked(Beats, ev);
+ if (status.progress < 0.99)
+ status.progress += 0.01;
+ }
+
+ nframes_t timeline_position = 0; // FIXME: ?
+
+ // FIXME: kluuuuudge: assumes tempo never changes after start
+ const double frames_per_beat = smfs->session().tempo_map().tempo_at(
+ timeline_position).frames_per_beat(
+ smfs->session().engine().frame_rate(),
+ smfs->session().tempo_map().meter_at(timeline_position));
+
+ smfs->update_length(0, (nframes_t) ceil ((t / (double)source->ppqn()) * frames_per_beat));
+
+ smfs->flush_header();
+ smfs->flush_footer();
+
+ if (status.cancel)
+ break;
+ }
+
+ } catch (...) {
+ error << "Corrupt MIDI file " << source->filename() << endl;
+ }
+}
+
+static void
+remove_file_source (boost::shared_ptr<Source> source)
+{
+ ::unlink (source->path().c_str());
+}
+
+// This function is still unable to cleanly update an existing source, even though
+// it is possible to set the import_status flag accordingly. The functinality
+// is disabled at the GUI until the Source implementations are able to provide
+// the necessary API.
+void
+Session::import_audiofiles (import_status& status)
+{
+ uint32_t cnt = 1;
+ typedef vector<boost::shared_ptr<Source> > Sources;
+ Sources all_new_sources;
+ boost::shared_ptr<AudioFileSource> afs;
+ boost::shared_ptr<SMFSource> smfs;
+ uint channels = 0;
+
+ status.sources.clear ();
+
+ for (vector<Glib::ustring>::iterator p = status.paths.begin();
+ p != status.paths.end() && !status.cancel;
+ ++p, ++cnt)
+ {
+ boost::shared_ptr<ImportableSource> source;
+ std::auto_ptr<SMFReader> smf_reader;
+ const DataType type = ((*p).rfind(".mid") != string::npos) ?
+ DataType::MIDI : DataType::AUDIO;
+
+ if (type == DataType::AUDIO) {
+ try {
+ source = open_importable_source (*p, frame_rate(), status.quality);
+ channels = source->channels();
+ } catch (const failed_constructor& err) {
+ error << string_compose(_("Import: cannot open input sound file \"%1\""), (*p)) << endmsg;
+ status.done = status.cancel = true;
+ return;
+ }
+
+ } else {
+ try {
+ smf_reader = std::auto_ptr<SMFReader>(new SMFReader(*p));
+ channels = smf_reader->num_tracks();
+ } catch (const SMFReader::UnsupportedTime& err) {
+ error << _("Import: unsupported MIDI time stamp format") << endmsg;
+ status.done = status.cancel = true;
+ return;
+ } catch (...) {
+ error << _("Import: error reading MIDI file") << endmsg;
+ status.done = status.cancel = true;
+ return;
+ }
+ }
+
+ vector<string> new_paths = get_paths_for_new_sources (status.replace_existing_source, *p,
+ get_best_session_directory_for_new_source (),
+ channels);
+ Sources newfiles;
+
+ if (status.replace_existing_source) {
+ fatal << "THIS IS NOT IMPLEMENTED YET, IT SHOULD NEVER GET CALLED!!! DYING!" << endl;
+ status.cancel = !map_existing_mono_sources (new_paths, *this, frame_rate(), newfiles, this);
+ } else {
+ status.cancel = !create_mono_sources_for_writing (new_paths, *this, frame_rate(), newfiles);
+ }
+
+ // copy on cancel/failure so that any files that were created will be removed below
+ std::copy (newfiles.begin(), newfiles.end(), std::back_inserter(all_new_sources));
+
+ if (status.cancel) break;
+
+ for (Sources::iterator i = newfiles.begin(); i != newfiles.end(); ++i) {
+ if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(*i)) != 0) {
+ afs->prepare_for_peakfile_writes ();
+ }
+ }
+
+ if (source) { // audio
+ status.doing_what = compose_status_message (*p, source->samplerate(),
+ frame_rate(), cnt, status.paths.size());
+ write_audio_data_to_new_files (source.get(), status, newfiles);
+ } else if (smf_reader.get()) { // midi
+ status.doing_what = string_compose(_("loading MIDI file %1"), *p);
+ write_midi_data_to_new_files (smf_reader.get(), status, newfiles);
+ }
+ }
+
+ if (!status.cancel) {
+ struct tm* now;
+ time_t xnow;
+ time (&xnow);
+ now = localtime (&xnow);
+ status.freeze = true;
+
+ /* flush the final length(s) to the header(s) */
+
+ for (Sources::iterator x = all_new_sources.begin(); x != all_new_sources.end(); ) {
+ if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(*x)) != 0) {
+ afs->update_header(0, *now, xnow);
+ afs->done_with_peakfile_writes ();
+ }
+
+ /* now that there is data there, requeue the file for analysis */
+
+ if (Config->get_auto_analyse_audio()) {
+ Analyser::queue_source_for_analysis (boost::static_pointer_cast<Source>(*x), false);
+ }
+
+ /* don't create tracks for empty MIDI sources (channels) */
+ if ((smfs = boost::dynamic_pointer_cast<SMFSource>(*x)) != 0 && smfs->is_empty()) {
+ x = all_new_sources.erase(x);
+ } else {
+ ++x;
+ }
+ }
+
+ /* save state so that we don't lose these new Sources */
+
+ save_state (_name);
+
+ std::copy (all_new_sources.begin(), all_new_sources.end(),
+ std::back_inserter(status.sources));
+ } else {
+ // this can throw...but it seems very unlikely
+ std::for_each (all_new_sources.begin(), all_new_sources.end(), remove_file_source);
+ }
+
+ status.done = true;
+}
+
diff --git a/libs/ardour/internal_audio_port.cc b/libs/ardour/internal_audio_port.cc
new file mode 100644
index 0000000000..e5362cde95
--- /dev/null
+++ b/libs/ardour/internal_audio_port.cc
@@ -0,0 +1,71 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cassert>
+#include <ardour/internal_audio_port.h>
+#include <ardour/audioengine.h>
+
+using namespace ARDOUR;
+using namespace std;
+
+void
+InternalAudioPort::default_mixdown (const list<InternalPort*>& ports, AudioBuffer& dest, nframes_t cnt, nframes_t offset)
+{
+ list<InternalPort*>::const_iterator p = ports.begin();
+
+ dest.read_from ((dynamic_cast<AudioPort*>(*p))->get_audio_buffer(), cnt, offset);
+
+ for (; p != ports.end(); ++p) {
+ dest.accumulate_from ((dynamic_cast<AudioPort*>(*p))->get_audio_buffer(), cnt, offset);
+ }
+}
+
+InternalAudioPort::InternalAudioPort(const string& name, Flags flags)
+ : Port (DataType::AUDIO, flags)
+ , AudioPort (flags, engine->frames_per_cycle())
+ , InternalPort (name, DataType::AUDIO, flags)
+{
+ _mixdown = default_mixdown;
+}
+
+void
+InternalAudioPort::set_mixdown_function (void (*func)(const list<InternalPort*>&, AudioBuffer&, nframes_t, nframes_t))
+{
+ _mixdown = func;
+}
+
+void
+InternalAudioPort::reset ()
+{
+ _buffer.resize (engine->frames_per_cycle());
+ _buffer.silence (_buffer.size());
+}
+
+AudioBuffer&
+InternalAudioPort::get_audio_buffer ()
+{
+ if (_connections.empty()) {
+ return AudioPort::get_audio_buffer();
+ }
+
+ /* XXX what about offset/size being more dynamic ? */
+
+ (*_mixdown) (_connections, _buffer, _buffer.size(), 0);
+
+ return _buffer;
+}
diff --git a/libs/ardour/internal_port.cc b/libs/ardour/internal_port.cc
new file mode 100644
index 0000000000..cec11742e0
--- /dev/null
+++ b/libs/ardour/internal_port.cc
@@ -0,0 +1,163 @@
+/*
+ Copyright (C) 2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/error.h>
+#include <ardour/internal_port.h>
+#include <ardour/audioengine.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+
+AudioEngine* InternalPort::engine = 0;
+
+void
+InternalPort::set_engine (AudioEngine* e)
+{
+ engine = e;
+}
+
+InternalPort::InternalPort (const string& str, DataType type, Flags flags)
+ : Port (type, flags)
+{
+ set_name (str);
+}
+
+InternalPort::~InternalPort ()
+{
+ disconnect ();
+}
+
+void
+InternalPort::set_latency (nframes_t val)
+{
+ _latency = val;
+}
+
+bool
+InternalPort::connected_to (const string& portname) const
+{
+ /* caller must hold process lock */
+
+ for (list<InternalPort*>::const_iterator p = _connections.begin(); p != _connections.end(); ++p) {
+ if ((*p)->name() == portname) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const char**
+InternalPort::get_connections () const
+{
+ /* caller must hold process lock */
+
+ int i;
+ list<InternalPort*>::const_iterator p;
+
+ if (_connections.empty()) {
+ return 0;
+ }
+
+ char **names = (char**) malloc (sizeof (char*) * ( _connections.size() + 1));
+
+
+ for (i = 0, p = _connections.begin(); p != _connections.end(); ++p, ++i) {
+ names[i] = (char*) (*p)->name().c_str();
+ }
+
+ names[i] = 0;
+
+ return (const char**) names;
+}
+
+int
+InternalPort::connected() const
+{
+ /* caller must hold process lock */
+ return !_connections.empty();
+}
+
+int
+InternalPort::set_name (string str)
+{
+ _name = "internal:";
+ _name += str;
+
+ return 0;
+}
+
+string
+InternalPort::short_name ()
+{
+ return _name.substr (9);
+}
+
+void
+InternalPort::connect (InternalPort& src, InternalPort& dst)
+{
+ /* caller must hold process lock */
+
+ src._connections.push_back (&dst);
+ dst._connections.push_back (&src);
+}
+
+void
+InternalPort::disconnect (InternalPort& a, InternalPort& b)
+{
+ /* caller must hold process lock */
+ a._connections.remove (&b);
+ b._connections.remove (&a);
+}
+
+int
+InternalPort::disconnect ()
+{
+ /* caller must hold process lock */
+
+ for (list<InternalPort*>::const_iterator p = _connections.begin(); p != _connections.end(); ) {
+ list<InternalPort*>::const_iterator tmp;
+
+ tmp = p;
+ ++tmp;
+
+ disconnect (*this, **p);
+
+ p = tmp;
+ }
+
+ _connections.clear ();
+
+ return 0;
+}
+
+int
+InternalPort::reestablish ()
+{
+ return 0;
+}
+
+void
+InternalPort::recompute_total_latency () const
+{
+ return;
+}
+
diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc
new file mode 100644
index 0000000000..5d4b41cf32
--- /dev/null
+++ b/libs/ardour/io.cc
@@ -0,0 +1,2612 @@
+/*
+ Copyright (C) 2000-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <fstream>
+#include <algorithm>
+#include <unistd.h>
+#include <locale.h>
+#include <errno.h>
+
+#include <sigc++/bind.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/xml++.h>
+#include <pbd/replace_all.h>
+#include <pbd/unknown_type.h>
+
+#include <ardour/audioengine.h>
+#include <ardour/io.h>
+#include <ardour/route.h>
+#include <ardour/port.h>
+#include <ardour/audio_port.h>
+#include <ardour/midi_port.h>
+#include <ardour/auto_bundle.h>
+#include <ardour/session.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/panner.h>
+#include <ardour/buffer_set.h>
+#include <ardour/meter.h>
+#include <ardour/amp.h>
+
+#include "i18n.h"
+
+#include <cmath>
+
+/*
+ A bug in OS X's cmath that causes isnan() and isinf() to be
+ "undeclared". the following works around that
+*/
+
+#if defined(__APPLE__) && defined(__MACH__)
+extern "C" int isnan (double);
+extern "C" int isinf (double);
+#endif
+
+#define BLOCK_PROCESS_CALLBACK() Glib::Mutex::Lock em (_session.engine().process_lock())
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+const string IO::state_node_name = "IO";
+bool IO::connecting_legal = false;
+bool IO::ports_legal = false;
+bool IO::panners_legal = false;
+sigc::signal<void> IO::Meter;
+sigc::signal<int> IO::ConnectingLegal;
+sigc::signal<int> IO::PortsLegal;
+sigc::signal<int> IO::PannersLegal;
+sigc::signal<void,ChanCount> IO::PortCountChanged;
+sigc::signal<int> IO::PortsCreated;
+
+Glib::StaticMutex IO::m_meter_signal_lock = GLIBMM_STATIC_MUTEX_INIT;
+
+/* this is a default mapper of [0 .. 1.0] control values to a gain coefficient.
+ others can be imagined.
+*/
+
+#if 0
+static gain_t direct_control_to_gain (double fract) {
+ /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
+ /* this maxes at +6dB */
+ return pow (2.0,(sqrt(sqrt(sqrt(fract)))*198.0-192.0)/6.0);
+}
+
+static double direct_gain_to_control (gain_t gain) {
+ /* XXX Marcus writes: this doesn't seem right to me. but i don't have a better answer ... */
+ if (gain == 0) return 0.0;
+
+ return pow((6.0*log(gain)/log(2.0)+192.0)/198.0, 8.0);
+}
+#endif
+
+/** @param default_type The type of port that will be created by ensure_io
+ * and friends if no type is explicitly requested (to avoid breakage).
+ */
+IO::IO (Session& s, const string& name,
+ int input_min, int input_max, int output_min, int output_max,
+ DataType default_type, bool public_ports)
+ : Automatable (s, name),
+ _output_buffers (new BufferSet()),
+ _active(true),
+ _default_type (default_type),
+ _public_ports (public_ports),
+ _input_minimum (ChanCount::ZERO),
+ _input_maximum (ChanCount::INFINITE),
+ _output_minimum (ChanCount::ZERO),
+ _output_maximum (ChanCount::INFINITE)
+{
+ _panner = new Panner (name, _session);
+ _meter = new PeakMeter (_session);
+
+ if (input_min > 0) {
+ _input_minimum = ChanCount(_default_type, input_min);
+ }
+ if (input_max >= 0) {
+ _input_maximum = ChanCount(_default_type, input_max);
+ }
+ if (output_min > 0) {
+ _output_minimum = ChanCount(_default_type, output_min);
+ }
+ if (output_max >= 0) {
+ _output_maximum = ChanCount(_default_type, output_max);
+ }
+
+ _gain = 1.0;
+ _desired_gain = 1.0;
+ pending_state_node = 0;
+ no_panner_reset = false;
+ _phase_invert = false;
+ deferred_state = 0;
+
+ boost::shared_ptr<AutomationList> gl(
+ new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0));
+
+ _gain_control = boost::shared_ptr<GainControl>(
+ new GainControl(X_("gaincontrol"), *this, gl));
+
+ add_control(_gain_control);
+
+ apply_gain_automation = false;
+
+ {
+ // IO::Meter is emitted from another thread so the
+ // Meter signal must be protected.
+ Glib::Mutex::Lock guard (m_meter_signal_lock);
+ m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
+ }
+
+ // Connect to our own PortCountChanged signal to connect output buffers
+ IO::PortCountChanged.connect (mem_fun (*this, &IO::attach_buffers));
+
+ _session.add_controllable (_gain_control);
+
+ create_bundles_for_inputs_and_outputs ();
+}
+
+IO::IO (Session& s, const XMLNode& node, DataType dt)
+ : Automatable (s, "unnamed io"),
+ _output_buffers (new BufferSet()),
+ _active(true),
+ _default_type (dt)
+{
+ _meter = new PeakMeter (_session);
+ _public_ports = true; // XXX get this from node
+ _panner = 0;
+ deferred_state = 0;
+ no_panner_reset = false;
+ _desired_gain = 1.0;
+ _gain = 1.0;
+
+ apply_gain_automation = false;
+
+ boost::shared_ptr<AutomationList> gl(
+ new AutomationList(Parameter(GainAutomation), 0.0, 2.0, 1.0));
+
+ _gain_control = boost::shared_ptr<GainControl>(
+ new GainControl(X_("gaincontrol"), *this, gl));
+
+ add_control(_gain_control);
+
+ set_state (node);
+
+ {
+ // IO::Meter is emitted from another thread so the
+ // Meter signal must be protected.
+ Glib::Mutex::Lock guard (m_meter_signal_lock);
+ m_meter_connection = Meter.connect (mem_fun (*this, &IO::meter));
+ }
+
+ // Connect to our own PortCountChanged signal to connect output buffers
+ IO::PortCountChanged.connect (mem_fun (*this, &IO::attach_buffers));
+
+ _session.add_controllable (_gain_control);
+
+ create_bundles_for_inputs_and_outputs ();
+}
+
+IO::~IO ()
+{
+ Glib::Mutex::Lock guard (m_meter_signal_lock);
+ Glib::Mutex::Lock lm (io_lock);
+
+ BLOCK_PROCESS_CALLBACK ();
+
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ _session.engine().unregister_port (*i);
+ }
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ _session.engine().unregister_port (*i);
+ }
+
+ m_meter_connection.disconnect();
+
+ delete _meter;
+ delete _panner;
+ delete _output_buffers;
+}
+
+void
+IO::silence (nframes_t nframes, nframes_t offset)
+{
+ /* io_lock, not taken: function must be called from Session::process() calltree */
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ i->get_buffer().silence (nframes, offset);
+ }
+}
+
+/** Deliver bufs to the IO's output ports
+ *
+ * This function should automatically do whatever it necessary to correctly deliver bufs
+ * to the outputs, eg applying gain or pan or whatever else needs to be done.
+ */
+void
+IO::deliver_output (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
+{
+ // FIXME: type specific code doesn't actually need to be here, it will go away in time
+
+ /* ********** AUDIO ********** */
+
+ // Apply gain if gain automation isn't playing
+ if ( ! apply_gain_automation) {
+
+ gain_t dg = _gain; // desired gain
+
+ {
+ Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
+
+ if (dm.locked()) {
+ dg = _desired_gain;
+ }
+
+ }
+
+ if (dg != _gain || dg != 1.0)
+ Amp::run_in_place(bufs, nframes, _gain, dg, _phase_invert);
+ }
+
+ // Use the panner to distribute audio to output port buffers
+ if (_panner && !_panner->empty() && !_panner->bypassed()) {
+ _panner->distribute (bufs, output_buffers(), start_frame, end_frame, nframes, offset);
+ } else {
+ const DataType type = DataType::AUDIO;
+
+ // Copy any audio 1:1 to outputs
+
+ BufferSet::iterator o = output_buffers().begin(type);
+ BufferSet::iterator i = bufs.begin(type);
+ BufferSet::iterator prev = i;
+
+ while (i != bufs.end(type) && o != output_buffers().end (type)) {
+ o->read_from(*i, nframes, offset);
+ prev = i;
+ ++i;
+ ++o;
+ }
+
+ /* extra outputs get a copy of the last buffer */
+
+ while (o != output_buffers().end(type)) {
+ o->read_from(*prev, nframes, offset);
+ ++o;
+ }
+ }
+
+ /* ********** MIDI ********** */
+
+ // No MIDI, we're done here
+ if (bufs.count().n_midi() == 0) {
+ return;
+ }
+
+ const DataType type = DataType::MIDI;
+
+ // Copy any MIDI 1:1 to outputs
+ assert(bufs.count().n_midi() == output_buffers().count().n_midi());
+ BufferSet::iterator o = output_buffers().begin(type);
+ for (BufferSet::iterator i = bufs.begin(type); i != bufs.end(type); ++i, ++o) {
+ o->read_from(*i, nframes, offset);
+ }
+}
+
+void
+IO::collect_input (BufferSet& outs, nframes_t nframes, nframes_t offset)
+{
+ assert(outs.available() >= n_inputs());
+
+ if (n_inputs() == ChanCount::ZERO)
+ return;
+
+ outs.set_count(n_inputs());
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+
+ BufferSet::iterator o = outs.begin(*t);
+ for (PortSet::iterator i = _inputs.begin(*t); i != _inputs.end(*t); ++i, ++o) {
+ o->read_from(i->get_buffer(), nframes, offset);
+ }
+
+ }
+}
+
+void
+IO::just_meter_input (nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset)
+{
+ BufferSet& bufs = _session.get_scratch_buffers (n_inputs());
+
+ collect_input (bufs, nframes, offset);
+
+ _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
+}
+
+
+void
+IO::check_bundles_connected_to_inputs ()
+{
+ check_bundles (_bundles_connected_to_inputs, inputs());
+}
+
+void
+IO::check_bundles_connected_to_outputs ()
+{
+ check_bundles (_bundles_connected_to_outputs, outputs());
+}
+
+void
+IO::check_bundles (std::vector<UserBundleInfo>& list, const PortSet& ports)
+{
+ std::vector<UserBundleInfo> new_list;
+
+ for (std::vector<UserBundleInfo>::iterator i = list.begin(); i != list.end(); ++i) {
+
+ uint32_t const N = i->bundle->nchannels ();
+
+ if (ports.num_ports() < N) {
+ continue;
+ }
+
+ bool ok = true;
+ for (uint32_t j = 0; j < N; ++j) {
+ /* Every port on bundle channel j must be connected to our input j */
+ PortList const pl = i->bundle->channel_ports (j);
+ for (uint32_t k = 0; k < pl.size(); ++k) {
+ if (ports.port(j)->connected_to (pl[k]) == false) {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok == false) {
+ break;
+ }
+ }
+
+ if (ok) {
+ new_list.push_back (*i);
+ } else {
+ i->configuration_will_change.disconnect ();
+ i->configuration_has_changed.disconnect ();
+ i->ports_will_change.disconnect ();
+ i->ports_have_changed.disconnect ();
+ }
+ }
+
+ list = new_list;
+}
+
+
+int
+IO::disconnect_input (Port* our_port, string other_port, void* src)
+{
+ if (other_port.length() == 0 || our_port == 0) {
+ return 0;
+ }
+
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ /* check that our_port is really one of ours */
+
+ if ( ! _inputs.contains(our_port)) {
+ return -1;
+ }
+
+ /* disconnect it from the source */
+
+ if (_session.engine().disconnect (other_port, our_port->name())) {
+ error << string_compose(_("IO: cannot disconnect input port %1 from %2"), our_port->name(), other_port) << endmsg;
+ return -1;
+ }
+
+ check_bundles_connected_to_inputs ();
+ }
+ }
+
+ input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
+ _session.set_dirty ();
+
+ return 0;
+}
+
+int
+IO::connect_input (Port* our_port, string other_port, void* src)
+{
+ if (other_port.length() == 0 || our_port == 0) {
+ return 0;
+ }
+
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ /* check that our_port is really one of ours */
+
+ if ( ! _inputs.contains(our_port) ) {
+ return -1;
+ }
+
+ /* connect it to the source */
+
+ if (_session.engine().connect (other_port, our_port->name())) {
+ return -1;
+ }
+ }
+ }
+
+ input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
+ _session.set_dirty ();
+ return 0;
+}
+
+int
+IO::disconnect_output (Port* our_port, string other_port, void* src)
+{
+ if (other_port.length() == 0 || our_port == 0) {
+ return 0;
+ }
+
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ /* check that our_port is really one of ours */
+
+ if ( ! _outputs.contains(our_port) ) {
+ return -1;
+ }
+
+ /* disconnect it from the destination */
+
+ if (_session.engine().disconnect (our_port->name(), other_port)) {
+ error << string_compose(_("IO: cannot disconnect output port %1 from %2"), our_port->name(), other_port) << endmsg;
+ return -1;
+ }
+
+ check_bundles_connected_to_outputs ();
+ }
+ }
+
+ output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
+ _session.set_dirty ();
+ return 0;
+}
+
+int
+IO::connect_output (Port* our_port, string other_port, void* src)
+{
+ if (other_port.length() == 0 || our_port == 0) {
+ return 0;
+ }
+
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ /* check that our_port is really one of ours */
+
+ if ( ! _outputs.contains(our_port) ) {
+ return -1;
+ }
+
+ /* connect it to the destination */
+
+ if (_session.engine().connect (our_port->name(), other_port)) {
+ return -1;
+ }
+ }
+ }
+
+ output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
+ _session.set_dirty ();
+ return 0;
+}
+
+int
+IO::set_input (Port* other_port, void* src)
+{
+ /* this removes all but one ports, and connects that one port
+ to the specified source.
+ */
+
+ if (_input_minimum.n_total() > 1) {
+ /* sorry, you can't do this */
+ return -1;
+ }
+
+ if (other_port == 0) {
+ if (_input_minimum == ChanCount::ZERO) {
+ return ensure_inputs (ChanCount::ZERO, false, true, src);
+ } else {
+ return -1;
+ }
+ }
+
+ if (ensure_inputs (ChanCount(other_port->type(), 1), true, true, src)) {
+ return -1;
+ }
+
+ return connect_input (_inputs.port(0), other_port->name(), src);
+}
+
+int
+IO::remove_output_port (Port* port, void* src)
+{
+ IOChange change (NoChange);
+
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ if (n_outputs() <= _output_minimum) {
+ /* sorry, you can't do this */
+ return -1;
+ }
+
+ if (_outputs.remove(port)) {
+ change = IOChange (change|ConfigurationChanged);
+
+ if (port->connected()) {
+ change = IOChange (change|ConnectionsChanged);
+ }
+
+ _session.engine().unregister_port (*port);
+ check_bundles_connected_to_outputs ();
+
+ setup_peak_meters ();
+ reset_panner ();
+ }
+ }
+
+ PortCountChanged (n_outputs()); /* EMIT SIGNAL */
+ }
+
+ if (change == ConnectionsChanged) {
+ setup_bundles_for_inputs_and_outputs ();
+ }
+
+ if (change != NoChange) {
+ output_changed (change, src);
+ _session.set_dirty ();
+ return 0;
+ }
+
+ return -1;
+}
+
+/** Add an output port.
+ *
+ * @param destination Name of input port to connect new port to.
+ * @param src Source for emitted ConfigurationChanged signal.
+ * @param type Data type of port. Default value (NIL) will use this IO's default type.
+ */
+int
+IO::add_output_port (string destination, void* src, DataType type)
+{
+ Port* our_port;
+
+ if (type == DataType::NIL)
+ type = _default_type;
+
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ if (n_outputs() >= _output_maximum) {
+ return -1;
+ }
+
+ /* Create a new output port */
+
+ string portname = build_legal_port_name (type, false);
+
+ if ((our_port = _session.engine().register_output_port (type, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
+ return -1;
+ }
+
+ _outputs.add (our_port);
+ setup_peak_meters ();
+ reset_panner ();
+ }
+
+ PortCountChanged (n_outputs()); /* EMIT SIGNAL */
+ }
+
+ if (destination.length()) {
+ if (_session.engine().connect (our_port->name(), destination)) {
+ return -1;
+ }
+ }
+
+ // pan_changed (src); /* EMIT SIGNAL */
+ output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundles_for_inputs_and_outputs ();
+ _session.set_dirty ();
+
+ return 0;
+}
+
+int
+IO::remove_input_port (Port* port, void* src)
+{
+ IOChange change (NoChange);
+
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ if (n_inputs() <= _input_minimum) {
+ /* sorry, you can't do this */
+ return -1;
+ }
+
+ if (_inputs.remove(port)) {
+ change = IOChange (change|ConfigurationChanged);
+
+ if (port->connected()) {
+ change = IOChange (change|ConnectionsChanged);
+ }
+
+ _session.engine().unregister_port (*port);
+ check_bundles_connected_to_inputs ();
+
+ setup_peak_meters ();
+ reset_panner ();
+ }
+ }
+
+ PortCountChanged (n_inputs ()); /* EMIT SIGNAL */
+ }
+
+ if (change == ConfigurationChanged) {
+ setup_bundles_for_inputs_and_outputs ();
+ }
+
+ if (change != NoChange) {
+ input_changed (change, src);
+ _session.set_dirty ();
+ return 0;
+ }
+
+ return -1;
+}
+
+
+/** Add an input port.
+ *
+ * @param type Data type of port. The appropriate port type, and @ref Port will be created.
+ * @param destination Name of input port to connect new port to.
+ * @param src Source for emitted ConfigurationChanged signal.
+ */
+int
+IO::add_input_port (string source, void* src, DataType type)
+{
+ Port* our_port;
+
+ if (type == DataType::NIL)
+ type = _default_type;
+
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ if (n_inputs() >= _input_maximum) {
+ return -1;
+ }
+
+ /* Create a new input port */
+
+ string portname = build_legal_port_name (type, true);
+
+ if ((our_port = _session.engine().register_input_port (type, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
+ return -1;
+ }
+
+ _inputs.add (our_port);
+ setup_peak_meters ();
+ reset_panner ();
+ }
+
+ PortCountChanged (n_inputs()); /* EMIT SIGNAL */
+ }
+
+ if (source.length()) {
+
+ if (_session.engine().connect (source, our_port->name())) {
+ return -1;
+ }
+ }
+
+ // pan_changed (src); /* EMIT SIGNAL */
+ input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundles_for_inputs_and_outputs ();
+ _session.set_dirty ();
+
+ return 0;
+}
+
+int
+IO::disconnect_inputs (void* src)
+{
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ _session.engine().disconnect (*i);
+ }
+
+ check_bundles_connected_to_inputs ();
+ }
+ }
+
+ input_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+int
+IO::disconnect_outputs (void* src)
+{
+ {
+ BLOCK_PROCESS_CALLBACK ();
+
+ {
+ Glib::Mutex::Lock lm (io_lock);
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ _session.engine().disconnect (*i);
+ }
+
+ check_bundles_connected_to_outputs ();
+ }
+ }
+
+ output_changed (ConnectionsChanged, src); /* EMIT SIGNAL */
+ _session.set_dirty ();
+
+ return 0;
+}
+
+bool
+IO::ensure_inputs_locked (ChanCount count, bool clear, void* src)
+{
+ Port* input_port = 0;
+ bool changed = false;
+
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+
+ const size_t n = count.get(*t);
+
+ /* remove unused ports */
+ for (size_t i = n_inputs().get(*t); i > n; --i) {
+ input_port = _inputs.port(*t, i-1);
+
+ assert(input_port);
+ _inputs.remove(input_port);
+ _session.engine().unregister_port (*input_port);
+
+ changed = true;
+ }
+
+ /* create any necessary new ports */
+ while (n_inputs().get(*t) < n) {
+
+ string portname = build_legal_port_name (*t, true);
+
+ try {
+
+ if ((input_port = _session.engine().register_input_port (*t, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
+ return -1;
+ }
+ }
+
+ catch (AudioEngine::PortRegistrationFailure& err) {
+ setup_peak_meters ();
+ reset_panner ();
+ /* pass it on */
+ throw AudioEngine::PortRegistrationFailure();
+ }
+
+ _inputs.add (input_port);
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ check_bundles_connected_to_inputs ();
+ setup_peak_meters ();
+ reset_panner ();
+ PortCountChanged (n_inputs()); /* EMIT SIGNAL */
+ _session.set_dirty ();
+ }
+
+ if (clear) {
+ /* disconnect all existing ports so that we get a fresh start */
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ _session.engine().disconnect (*i);
+ }
+ }
+
+ return changed;
+}
+
+/** Attach output_buffers to port buffers.
+ *
+ * Connected to IO's own PortCountChanged signal.
+ */
+void
+IO::attach_buffers(ChanCount ignored)
+{
+ _output_buffers->attach_buffers(_outputs);
+}
+
+int
+IO::ensure_io (ChanCount in, ChanCount out, bool clear, void* src)
+{
+ bool in_changed = false;
+ bool out_changed = false;
+ bool need_pan_reset = false;
+
+ in = min (_input_maximum, in);
+
+ out = min (_output_maximum, out);
+
+ if (in == n_inputs() && out == n_outputs() && !clear) {
+ return 0;
+ }
+
+ {
+ BLOCK_PROCESS_CALLBACK ();
+ Glib::Mutex::Lock lm (io_lock);
+
+ Port* port;
+
+ if (n_outputs() != out) {
+ need_pan_reset = true;
+ }
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+
+ const size_t nin = in.get(*t);
+ const size_t nout = out.get(*t);
+
+ Port* output_port = 0;
+ Port* input_port = 0;
+
+ /* remove unused output ports */
+ for (size_t i = n_outputs().get(*t); i > nout; --i) {
+ output_port = _outputs.port(*t, i-1);
+
+ assert(output_port);
+ _outputs.remove(output_port);
+ _session.engine().unregister_port (*output_port);
+
+ out_changed = true;
+ }
+
+ /* remove unused input ports */
+ for (size_t i = n_inputs().get(*t); i > nin; --i) {
+ input_port = _inputs.port(*t, i-1);
+
+ assert(input_port);
+ _inputs.remove(input_port);
+ _session.engine().unregister_port (*input_port);
+
+ in_changed = true;
+ }
+
+ /* create any necessary new input ports */
+
+ while (n_inputs().get(*t) < nin) {
+
+ string portname = build_legal_port_name (*t, true);
+
+ try {
+ if ((port = _session.engine().register_input_port (*t, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register input port %1"), portname) << endmsg;
+ return -1;
+ }
+ }
+
+ catch (AudioEngine::PortRegistrationFailure& err) {
+ setup_peak_meters ();
+ reset_panner ();
+ /* pass it on */
+ throw AudioEngine::PortRegistrationFailure();
+ }
+
+ _inputs.add (port);
+ in_changed = true;
+ }
+
+ /* create any necessary new output ports */
+
+ while (n_outputs().get(*t) < nout) {
+
+ string portname = build_legal_port_name (*t, false);
+
+ try {
+ if ((port = _session.engine().register_output_port (*t, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
+ return -1;
+ }
+ }
+
+ catch (AudioEngine::PortRegistrationFailure& err) {
+ setup_peak_meters ();
+ reset_panner ();
+ /* pass it on */
+ throw AudioEngine::PortRegistrationFailure ();
+ }
+
+ _outputs.add (port);
+ out_changed = true;
+ }
+ }
+
+ if (clear) {
+
+ /* disconnect all existing ports so that we get a fresh start */
+
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ _session.engine().disconnect (*i);
+ }
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ _session.engine().disconnect (*i);
+ }
+ }
+
+ if (in_changed || out_changed) {
+ setup_peak_meters ();
+ reset_panner ();
+ }
+ }
+
+ if (out_changed) {
+ check_bundles_connected_to_outputs ();
+ output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ }
+
+ if (in_changed) {
+ check_bundles_connected_to_inputs ();
+ input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ }
+
+ if (in_changed || out_changed) {
+ PortCountChanged (max (n_outputs(), n_inputs())); /* EMIT SIGNAL */
+ setup_bundles_for_inputs_and_outputs ();
+ _session.set_dirty ();
+ }
+
+ return 0;
+}
+
+int
+IO::ensure_inputs (ChanCount count, bool clear, bool lockit, void* src)
+{
+ bool changed = false;
+
+ count = min (_input_maximum, count);
+
+ if (count == n_inputs() && !clear) {
+ return 0;
+ }
+
+ if (lockit) {
+ BLOCK_PROCESS_CALLBACK ();
+ Glib::Mutex::Lock im (io_lock);
+ changed = ensure_inputs_locked (count, clear, src);
+ } else {
+ changed = ensure_inputs_locked (count, clear, src);
+ }
+
+ if (changed) {
+ input_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundles_for_inputs_and_outputs ();
+ _session.set_dirty ();
+ }
+ return 0;
+}
+
+bool
+IO::ensure_outputs_locked (ChanCount count, bool clear, void* src)
+{
+ Port* output_port = 0;
+ bool changed = false;
+ bool need_pan_reset = false;
+
+ if (n_outputs() != count) {
+ need_pan_reset = true;
+ }
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+
+ const size_t n = count.get(*t);
+
+ /* remove unused ports */
+ for (size_t i = n_outputs().get(*t); i > n; --i) {
+ output_port = _outputs.port(*t, i-1);
+
+ assert(output_port);
+ _outputs.remove(output_port);
+ _session.engine().unregister_port (*output_port);
+
+ changed = true;
+ }
+
+ /* create any necessary new ports */
+ while (n_outputs().get(*t) < n) {
+
+ string portname = build_legal_port_name (*t, false);
+
+ if ((output_port = _session.engine().register_output_port (*t, portname, _public_ports)) == 0) {
+ error << string_compose(_("IO: cannot register output port %1"), portname) << endmsg;
+ return -1;
+ }
+
+ _outputs.add (output_port);
+ changed = true;
+ setup_peak_meters ();
+
+ if (need_pan_reset) {
+ reset_panner ();
+ }
+ }
+ }
+
+ if (changed) {
+ check_bundles_connected_to_outputs ();
+ PortCountChanged (n_outputs()); /* EMIT SIGNAL */
+ _session.set_dirty ();
+ }
+
+ if (clear) {
+ /* disconnect all existing ports so that we get a fresh start */
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ _session.engine().disconnect (*i);
+ }
+ }
+
+ return changed;
+}
+
+int
+IO::ensure_outputs (ChanCount count, bool clear, bool lockit, void* src)
+{
+ bool changed = false;
+
+ if (_output_maximum < ChanCount::INFINITE) {
+ count = min (_output_maximum, count);
+ if (count == n_outputs() && !clear) {
+ return 0;
+ }
+ }
+
+ /* XXX caller should hold io_lock, but generally doesn't */
+
+ if (lockit) {
+ BLOCK_PROCESS_CALLBACK ();
+ Glib::Mutex::Lock im (io_lock);
+ changed = ensure_outputs_locked (count, clear, src);
+ } else {
+ changed = ensure_outputs_locked (count, clear, src);
+ }
+
+ if (changed) {
+ output_changed (ConfigurationChanged, src); /* EMIT SIGNAL */
+ setup_bundles_for_inputs_and_outputs ();
+ }
+
+ return 0;
+}
+
+gain_t
+IO::effective_gain () const
+{
+ if (_gain_control->list()->automation_playback()) {
+ return _gain_control->get_value();
+ } else {
+ return _desired_gain;
+ }
+}
+
+void
+IO::reset_panner ()
+{
+ if (panners_legal) {
+ if (!no_panner_reset) {
+ _panner->reset (n_outputs().n_audio(), pans_required());
+ }
+ } else {
+ panner_legal_c.disconnect ();
+ panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
+ }
+}
+
+int
+IO::panners_became_legal ()
+{
+ _panner->reset (n_outputs().n_audio(), pans_required());
+ _panner->load (); // automation
+ panner_legal_c.disconnect ();
+ return 0;
+}
+
+void
+IO::defer_pan_reset ()
+{
+ no_panner_reset = true;
+}
+
+void
+IO::allow_pan_reset ()
+{
+ no_panner_reset = false;
+ reset_panner ();
+}
+
+
+XMLNode&
+IO::get_state (void)
+{
+ return state (true);
+}
+
+XMLNode&
+IO::state (bool full_state)
+{
+ XMLNode* node = new XMLNode (state_node_name);
+ char buf[64];
+ string str;
+ vector<string>::iterator ci;
+ int n;
+ LocaleGuard lg (X_("POSIX"));
+ Glib::Mutex::Lock lm (io_lock);
+
+ node->add_property("name", _name);
+ id().print (buf, sizeof (buf));
+ node->add_property("id", buf);
+
+ for (
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin();
+ i != _bundles_connected_to_inputs.end();
+ ++i
+ )
+ {
+ XMLNode* n = new XMLNode ("InputBundle");
+ n->add_property ("name", i->bundle->name ());
+ node->add_child_nocopy (*n);
+ }
+
+ for (
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin();
+ i != _bundles_connected_to_outputs.end();
+ ++i
+ )
+ {
+ XMLNode* n = new XMLNode ("OutputBundle");
+ n->add_property ("name", i->bundle->name ());
+ node->add_child_nocopy (*n);
+ }
+
+ str = "";
+
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+
+ vector<string> connections;
+
+ if (i->get_connections (connections)) {
+
+ str += '{';
+
+ for (n = 0, ci = connections.begin(); ci != connections.end(); ++ci, ++n) {
+ if (n) {
+ str += ',';
+ }
+
+ /* if its a connection to our own port,
+ return only the port name, not the
+ whole thing. this allows connections
+ to be re-established even when our
+ client name is different.
+ */
+
+ str += _session.engine().make_port_name_relative (*ci);
+ }
+
+ str += '}';
+
+ } else {
+ str += "{}";
+ }
+ }
+
+ node->add_property ("inputs", str);
+
+ str = "";
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+
+ vector<string> connections;
+
+ if (i->get_connections (connections)) {
+
+ str += '{';
+
+ for (n = 0, ci = connections.begin(); ci != connections.end(); ++ci, ++n) {
+ if (n) {
+ str += ',';
+ }
+
+ str += _session.engine().make_port_name_relative (*ci);
+ }
+
+ str += '}';
+
+ } else {
+ str += "{}";
+ }
+ }
+
+ node->add_property ("outputs", str);
+
+ node->add_child_nocopy (_panner->state (full_state));
+ node->add_child_nocopy (_gain_control->get_state ());
+
+ snprintf (buf, sizeof(buf), "%2.12f", gain());
+ node->add_property ("gain", buf);
+
+ /* To make backwards compatibility a bit easier, write ChanCount::INFINITE to the session file
+ as -1.
+ */
+
+ int const in_max = _input_maximum == ChanCount::INFINITE ? -1 : _input_maximum.get(_default_type);
+ int const out_max = _output_maximum == ChanCount::INFINITE ? -1 : _output_maximum.get(_default_type);
+
+ snprintf (buf, sizeof(buf)-1, "%d,%d,%d,%d", _input_minimum.get(_default_type), in_max, _output_minimum.get(_default_type), out_max);
+
+ node->add_property ("iolimits", buf);
+
+ /* automation */
+
+ if (full_state)
+ node->add_child_nocopy (get_automation_state());
+
+ return *node;
+}
+
+int
+IO::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+ XMLNodeConstIterator iter;
+ LocaleGuard lg (X_("POSIX"));
+
+ /* force use of non-localized representation of decimal point,
+ since we use it a lot in XML files and so forth.
+ */
+
+ if (node.name() != state_node_name) {
+ error << string_compose(_("incorrect XML node \"%1\" passed to IO object"), node.name()) << endmsg;
+ return -1;
+ }
+
+ if ((prop = node.property ("name")) != 0) {
+ _name = prop->value();
+ /* used to set panner name with this, but no more */
+ }
+
+ if ((prop = node.property ("id")) != 0) {
+ _id = prop->value ();
+ }
+
+ int in_min = -1;
+ int in_max = -1;
+ int out_min = -1;
+ int out_max = -1;
+
+ if ((prop = node.property ("iolimits")) != 0) {
+ sscanf (prop->value().c_str(), "%d,%d,%d,%d",
+ &in_min, &in_max, &out_min, &out_max);
+
+ /* Correct for the difference between the way we write things to session files and the
+ way things are described by ChanCount; see comments in io.h about what the different
+ ChanCount values mean. */
+
+ if (in_min < 0) {
+ _input_minimum = ChanCount::ZERO;
+ } else {
+ _input_minimum = ChanCount (_default_type, in_min);
+ }
+
+ if (in_max < 0) {
+ _input_maximum = ChanCount::INFINITE;
+ } else {
+ _input_maximum = ChanCount (_default_type, in_max);
+ }
+
+ if (out_min < 0) {
+ _output_minimum = ChanCount::ZERO;
+ } else {
+ _output_minimum = ChanCount (_default_type, out_min);
+ }
+
+ if (out_max < 0) {
+ _output_maximum = ChanCount::INFINITE;
+ } else {
+ _output_maximum = ChanCount (_default_type, out_max);
+ }
+ }
+
+ if ((prop = node.property ("gain")) != 0) {
+ set_gain (atof (prop->value().c_str()), this);
+ _gain = _desired_gain;
+ }
+
+ if ((prop = node.property ("automation-state")) != 0 || (prop = node.property ("automation-style")) != 0) {
+ /* old school automation handling */
+ }
+
+ for (iter = node.children().begin(); iter != node.children().end(); ++iter) {
+
+ if ((*iter)->name() == "Panner") {
+ if (_panner == 0) {
+ _panner = new Panner (_name, _session);
+ }
+ _panner->set_state (**iter);
+ }
+
+ if ((*iter)->name() == X_("Automation")) {
+
+ set_automation_state (*(*iter), Parameter(GainAutomation));
+ }
+
+ if ((*iter)->name() == X_("controllable")) {
+ if ((prop = (*iter)->property("name")) != 0 && prop->value() == "gaincontrol") {
+ _gain_control->set_state (**iter);
+ }
+ }
+ }
+
+ if (ports_legal) {
+
+ if (create_ports (node)) {
+ return -1;
+ }
+
+ } else {
+
+ port_legal_c = PortsLegal.connect (mem_fun (*this, &IO::ports_became_legal));
+ }
+
+ if (panners_legal) {
+ reset_panner ();
+ } else {
+ panner_legal_c = PannersLegal.connect (mem_fun (*this, &IO::panners_became_legal));
+ }
+
+ if (connecting_legal) {
+
+ if (make_connections (node)) {
+ return -1;
+ }
+
+ } else {
+
+ connection_legal_c = ConnectingLegal.connect (mem_fun (*this, &IO::connecting_became_legal));
+ }
+
+ if (!ports_legal || !connecting_legal) {
+ pending_state_node = new XMLNode (node);
+ }
+
+ return 0;
+}
+
+int
+IO::load_automation (string path)
+{
+ string fullpath;
+ ifstream in;
+ char line[128];
+ uint32_t linecnt = 0;
+ float version;
+ LocaleGuard lg (X_("POSIX"));
+
+ fullpath = _session.automation_dir();
+ fullpath += path;
+
+ in.open (fullpath.c_str());
+
+ if (!in) {
+ fullpath = _session.automation_dir();
+ fullpath += _session.snap_name();
+ fullpath += '-';
+ fullpath += path;
+
+ in.open (fullpath.c_str());
+
+ if (!in) {
+ error << string_compose(_("%1: cannot open automation event file \"%2\""), _name, fullpath) << endmsg;
+ return -1;
+ }
+ }
+
+ clear_automation ();
+
+ while (in.getline (line, sizeof(line), '\n')) {
+ char type;
+ nframes_t when;
+ double value;
+
+ if (++linecnt == 1) {
+ if (memcmp (line, "version", 7) == 0) {
+ if (sscanf (line, "version %f", &version) != 1) {
+ error << string_compose(_("badly formed version number in automation event file \"%1\""), path) << endmsg;
+ return -1;
+ }
+ } else {
+ error << string_compose(_("no version information in automation event file \"%1\""), path) << endmsg;
+ return -1;
+ }
+
+ continue;
+ }
+
+ if (sscanf (line, "%c %" PRIu32 " %lf", &type, &when, &value) != 3) {
+ warning << string_compose(_("badly formatted automation event record at line %1 of %2 (ignored)"), linecnt, path) << endmsg;
+ continue;
+ }
+
+ switch (type) {
+ case 'g':
+ _gain_control->list()->fast_simple_add (when, value);
+ break;
+
+ case 's':
+ break;
+
+ case 'm':
+ break;
+
+ case 'p':
+ /* older (pre-1.0) versions of ardour used this */
+ break;
+
+ default:
+ warning << _("dubious automation event found (and ignored)") << endmsg;
+ }
+ }
+
+ return 0;
+}
+
+int
+IO::connecting_became_legal ()
+{
+ int ret;
+
+ if (pending_state_node == 0) {
+ fatal << _("IO::connecting_became_legal() called without a pending state node") << endmsg;
+ /*NOTREACHED*/
+ return -1;
+ }
+
+ connection_legal_c.disconnect ();
+
+ ret = make_connections (*pending_state_node);
+
+ if (ports_legal) {
+ delete pending_state_node;
+ pending_state_node = 0;
+ }
+
+ return ret;
+}
+int
+IO::ports_became_legal ()
+{
+ int ret;
+
+ if (pending_state_node == 0) {
+ fatal << _("IO::ports_became_legal() called without a pending state node") << endmsg;
+ /*NOTREACHED*/
+ return -1;
+ }
+
+ port_legal_c.disconnect ();
+
+ ret = create_ports (*pending_state_node);
+
+ if (connecting_legal) {
+ delete pending_state_node;
+ pending_state_node = 0;
+ }
+
+ return ret;
+}
+
+int
+IO::create_ports (const XMLNode& node)
+{
+ XMLProperty const * prop;
+ int num_inputs = 0;
+ int num_outputs = 0;
+
+ if ((prop = node.property ("inputs")) != 0) {
+ num_inputs = count (prop->value().begin(), prop->value().end(), '{');
+ } else if ((prop = node.property ("outputs")) != 0) {
+ num_outputs = count (prop->value().begin(), prop->value().end(), '{');
+ }
+
+ no_panner_reset = true;
+
+ // FIXME: audio-only
+ if (ensure_io (ChanCount(DataType::AUDIO, num_inputs), ChanCount(DataType::AUDIO, num_outputs), true, this)) {
+ error << string_compose(_("%1: cannot create I/O ports"), _name) << endmsg;
+ return -1;
+ }
+
+ no_panner_reset = false;
+
+ set_deferred_state ();
+
+ PortsCreated();
+ return 0;
+}
+
+
+int
+IO::make_connections (const XMLNode& node)
+{
+ XMLProperty const * prop;
+
+ if ((prop = node.property ("inputs")) != 0) {
+ if (set_inputs (prop->value())) {
+ error << string_compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg;
+ return -1;
+ }
+ }
+
+
+ if ((prop = node.property ("outputs")) != 0) {
+ if (set_outputs (prop->value())) {
+ error << string_compose(_("improper output channel list in XML node (%1)"), prop->value()) << endmsg;
+ return -1;
+ }
+ }
+
+ for (XMLNodeConstIterator i = node.children().begin(); i != node.children().end(); ++i) {
+
+ if ((*i)->name() == "InputBundle") {
+ XMLProperty const * prop = (*i)->property ("name");
+ if (prop) {
+ boost::shared_ptr<Bundle> b = _session.bundle_by_name (prop->value());
+ if (b) {
+ connect_input_ports_to_bundle (b, this);
+ } else {
+ error << string_compose(_("Unknown bundle \"%1\" listed for input of %2"), prop->value(), _name) << endmsg;
+ }
+ }
+
+ } else if ((*i)->name() == "OutputBundle") {
+ XMLProperty const * prop = (*i)->property ("name");
+ if (prop) {
+ boost::shared_ptr<Bundle> b = _session.bundle_by_name (prop->value());
+ if (b) {
+ connect_output_ports_to_bundle (b, this);
+ } else {
+ error << string_compose(_("Unknown bundle \"%1\" listed for output of %2"), prop->value(), _name) << endmsg;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int
+IO::set_inputs (const string& str)
+{
+ vector<string> ports;
+ int i;
+ int n;
+ uint32_t nports;
+
+ if ((nports = count (str.begin(), str.end(), '{')) == 0) {
+ return 0;
+ }
+
+ // FIXME: audio-only
+ if (ensure_inputs (ChanCount(DataType::AUDIO, nports), true, true, this)) {
+ return -1;
+ }
+
+ string::size_type start, end, ostart;
+
+ ostart = 0;
+ start = 0;
+ end = 0;
+ i = 0;
+
+ while ((start = str.find_first_of ('{', ostart)) != string::npos) {
+ start += 1;
+
+ if ((end = str.find_first_of ('}', start)) == string::npos) {
+ error << string_compose(_("IO: badly formed string in XML node for inputs \"%1\""), str) << endmsg;
+ return -1;
+ }
+
+ if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
+ error << string_compose(_("bad input string in XML node \"%1\""), str) << endmsg;
+
+ return -1;
+
+ } else if (n > 0) {
+
+ for (int x = 0; x < n; ++x) {
+ connect_input (input (i), ports[x], this);
+ }
+ }
+
+ ostart = end+1;
+ i++;
+ }
+
+ return 0;
+}
+
+int
+IO::set_outputs (const string& str)
+{
+ vector<string> ports;
+ int i;
+ int n;
+ uint32_t nports;
+
+ if ((nports = count (str.begin(), str.end(), '{')) == 0) {
+ return 0;
+ }
+
+ // FIXME: audio-only
+ if (ensure_outputs (ChanCount(DataType::AUDIO, nports), true, true, this)) {
+ return -1;
+ }
+
+ string::size_type start, end, ostart;
+
+ ostart = 0;
+ start = 0;
+ end = 0;
+ i = 0;
+
+ while ((start = str.find_first_of ('{', ostart)) != string::npos) {
+ start += 1;
+
+ if ((end = str.find_first_of ('}', start)) == string::npos) {
+ error << string_compose(_("IO: badly formed string in XML node for outputs \"%1\""), str) << endmsg;
+ return -1;
+ }
+
+ if ((n = parse_io_string (str.substr (start, end - start), ports)) < 0) {
+ error << string_compose(_("IO: bad output string in XML node \"%1\""), str) << endmsg;
+
+ return -1;
+
+ } else if (n > 0) {
+
+ for (int x = 0; x < n; ++x) {
+ connect_output (output (i), ports[x], this);
+ }
+ }
+
+ ostart = end+1;
+ i++;
+ }
+
+ return 0;
+}
+
+int
+IO::parse_io_string (const string& str, vector<string>& ports)
+{
+ string::size_type pos, opos;
+
+ if (str.length() == 0) {
+ return 0;
+ }
+
+ pos = 0;
+ opos = 0;
+
+ ports.clear ();
+
+ while ((pos = str.find_first_of (',', opos)) != string::npos) {
+ ports.push_back (str.substr (opos, pos - opos));
+ opos = pos + 1;
+ }
+
+ if (opos < str.length()) {
+ ports.push_back (str.substr(opos));
+ }
+
+ return ports.size();
+}
+
+int
+IO::parse_gain_string (const string& str, vector<string>& ports)
+{
+ string::size_type pos, opos;
+
+ pos = 0;
+ opos = 0;
+ ports.clear ();
+
+ while ((pos = str.find_first_of (',', opos)) != string::npos) {
+ ports.push_back (str.substr (opos, pos - opos));
+ opos = pos + 1;
+ }
+
+ if (opos < str.length()) {
+ ports.push_back (str.substr(opos));
+ }
+
+ return ports.size();
+}
+
+bool
+IO::set_name (const string& requested_name)
+{
+ if (requested_name == _name) {
+ return true;
+ }
+
+ string name;
+ Route *rt;
+ if ( (rt = dynamic_cast<Route *>(this))) {
+ name = Route::ensure_track_or_route_name(requested_name, _session);
+ } else {
+ name = requested_name;
+ }
+
+
+ /* replace all colons in the name. i wish we didn't have to do this */
+
+ if (replace_all (name, ":", "-")) {
+ warning << _("you cannot use colons to name objects with I/O connections") << endmsg;
+ }
+
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ string current_name = i->short_name();
+ current_name.replace (current_name.find (_name), _name.length(), name);
+ i->set_name (current_name);
+ }
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ string current_name = i->short_name();
+ current_name.replace (current_name.find (_name), _name.length(), name);
+ i->set_name (current_name);
+ }
+
+ bool const r = SessionObject::set_name(name);
+
+ setup_bundles_for_inputs_and_outputs ();
+
+ return r;
+}
+
+void
+IO::set_input_minimum (ChanCount n)
+{
+ _input_minimum = n;
+}
+
+void
+IO::set_input_maximum (ChanCount n)
+{
+ _input_maximum = n;
+}
+
+void
+IO::set_output_minimum (ChanCount n)
+{
+ _output_minimum = n;
+}
+
+void
+IO::set_output_maximum (ChanCount n)
+{
+ _output_maximum = n;
+}
+
+void
+IO::set_port_latency (nframes_t nframes)
+{
+ Glib::Mutex::Lock lm (io_lock);
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ i->set_latency (nframes);
+ }
+}
+
+nframes_t
+IO::output_latency () const
+{
+ nframes_t max_latency;
+ nframes_t latency;
+
+ max_latency = 0;
+
+ /* io lock not taken - must be protected by other means */
+
+ for (PortSet::const_iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
+ max_latency = latency;
+ }
+ }
+
+ return max_latency;
+}
+
+nframes_t
+IO::input_latency () const
+{
+ nframes_t max_latency;
+ nframes_t latency;
+
+ max_latency = 0;
+
+ /* io lock not taken - must be protected by other means */
+
+ for (PortSet::const_iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ if ((latency = _session.engine().get_port_total_latency (*i)) > max_latency) {
+ max_latency = latency;
+ }
+ }
+
+ return max_latency;
+}
+
+int
+IO::connect_input_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
+{
+ {
+ BLOCK_PROCESS_CALLBACK ();
+ Glib::Mutex::Lock lm2 (io_lock);
+
+ /* Connect to the bundle, not worrying about any connections
+ that are already made. */
+
+ uint32_t const channels = c->nchannels ();
+
+ for (uint32_t n = 0; n < channels; ++n) {
+ const PortList& pl = c->channel_ports (n);
+
+ for (PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
+
+ if (!_inputs.port(n)->connected_to (*i)) {
+
+ if (_session.engine().connect (*i, _inputs.port(n)->name())) {
+ return -1;
+ }
+ }
+
+ }
+ }
+
+ /* If this is a UserBundle, make a note of what we've done */
+
+ boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
+ if (ub) {
+
+ /* See if we already know about this one */
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin();
+ while (i != _bundles_connected_to_inputs.end() && i->bundle != ub) {
+ ++i;
+ }
+
+ if (i == _bundles_connected_to_inputs.end()) {
+ /* We don't, so make a note */
+ _bundles_connected_to_inputs.push_back (UserBundleInfo (this, ub));
+ }
+ }
+ }
+
+ input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), src); /* EMIT SIGNAL */
+ return 0;
+}
+
+int
+IO::connect_output_ports_to_bundle (boost::shared_ptr<Bundle> c, void* src)
+{
+ {
+ BLOCK_PROCESS_CALLBACK ();
+ Glib::Mutex::Lock lm2 (io_lock);
+
+ /* Connect to the bundle, not worrying about any connections
+ that are already made. */
+
+ uint32_t const channels = c->nchannels ();
+
+ for (uint32_t n = 0; n < channels; ++n) {
+
+ const PortList& pl = c->channel_ports (n);
+
+ for (PortList::const_iterator i = pl.begin(); i != pl.end(); ++i) {
+
+ if (!_outputs.port(n)->connected_to (*i)) {
+
+ if (_session.engine().connect (_outputs.port(n)->name(), *i)) {
+ return -1;
+ }
+ }
+ }
+ }
+
+ /* If this is a UserBundle, make a note of what we've done */
+
+ boost::shared_ptr<UserBundle> ub = boost::dynamic_pointer_cast<UserBundle> (c);
+ if (ub) {
+
+ /* See if we already know about this one */
+ std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin();
+ while (i != _bundles_connected_to_outputs.end() && i->bundle != ub) {
+ ++i;
+ }
+
+ if (i == _bundles_connected_to_outputs.end()) {
+ /* We don't, so make a note */
+ _bundles_connected_to_outputs.push_back (UserBundleInfo (this, ub));
+ }
+ }
+ }
+
+ output_changed (IOChange (ConnectionsChanged|ConfigurationChanged), src); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+int
+IO::disable_connecting ()
+{
+ connecting_legal = false;
+ return 0;
+}
+
+int
+IO::enable_connecting ()
+{
+ connecting_legal = true;
+ return ConnectingLegal ();
+}
+
+int
+IO::disable_ports ()
+{
+ ports_legal = false;
+ return 0;
+}
+
+int
+IO::enable_ports ()
+{
+ ports_legal = true;
+ return PortsLegal ();
+}
+
+int
+IO::disable_panners (void)
+{
+ panners_legal = false;
+ return 0;
+}
+
+int
+IO::reset_panners ()
+{
+ panners_legal = true;
+ return PannersLegal ();
+}
+
+void
+IO::bundle_configuration_will_change ()
+{
+ //XXX
+// connect_input_ports_to_bundle (_input_bundle, this);
+}
+
+void
+IO::bundle_configuration_has_changed ()
+{
+ //XXX
+// connect_input_ports_to_bundle (_input_bundle, this);
+}
+
+void
+IO::bundle_ports_will_change (int ignored)
+{
+//XXX
+// connect_output_ports_to_bundle (_output_bundle, this);
+}
+
+void
+IO::bundle_ports_have_changed (int ignored)
+{
+ //XXX
+// connect_output_ports_to_bundle (_output_bundle, this);
+}
+
+void
+IO::GainControl::set_value (float val)
+{
+ // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05))
+ if (val > 1.99526231f)
+ val = 1.99526231f;
+
+ _user_value = val;
+ _io.set_gain (val, this);
+
+ Changed(); /* EMIT SIGNAL */
+}
+
+float
+IO::GainControl::get_value (void) const
+{
+ return AutomationControl::get_value();
+}
+
+void
+IO::setup_peak_meters()
+{
+ ChanCount max_streams = std::max(_inputs.count(), _outputs.count());
+ _meter->configure_io(max_streams, max_streams);
+}
+
+/**
+ Update the peak meters.
+
+ The meter signal lock is taken to prevent modification of the
+ Meter signal while updating the meters, taking the meter signal
+ lock prior to taking the io_lock ensures that all IO will remain
+ valid while metering.
+*/
+void
+IO::update_meters()
+{
+ Glib::Mutex::Lock guard (m_meter_signal_lock);
+ Meter(); /* EMIT SIGNAL */
+}
+
+void
+IO::meter ()
+{
+ // FIXME: Ugly. Meter should manage the lock, if it's necessary
+
+ Glib::Mutex::Lock lm (io_lock); // READER: meter thread.
+ _meter->meter();
+}
+
+void
+IO::clear_automation ()
+{
+ Automatable::clear_automation (); // clears gain automation
+ _panner->clear_automation ();
+}
+
+void
+IO::set_parameter_automation_state (Parameter param, AutoState state)
+{
+ // XXX: would be nice to get rid of this special hack
+
+ if (param.type() == GainAutomation) {
+
+ bool changed = false;
+
+ {
+ Glib::Mutex::Lock lm (_automation_lock);
+
+ boost::shared_ptr<AutomationList> gain_auto = _gain_control->list();
+
+ if (state != gain_auto->automation_state()) {
+ changed = true;
+ _last_automation_snapshot = 0;
+ gain_auto->set_automation_state (state);
+
+ if (state != Off) {
+ // FIXME: shouldn't this use Curve?
+ set_gain (gain_auto->eval (_session.transport_frame()), this);
+ }
+ }
+ }
+
+ if (changed) {
+ _session.set_dirty ();
+ }
+
+ } else {
+ Automatable::set_parameter_automation_state(param, state);
+ }
+}
+
+void
+IO::inc_gain (gain_t factor, void *src)
+{
+ if (_desired_gain == 0.0f)
+ set_gain (0.000001f + (0.000001f * factor), src);
+ else
+ set_gain (_desired_gain + (_desired_gain * factor), src);
+}
+
+void
+IO::set_gain (gain_t val, void *src)
+{
+ // max gain at about +6dB (10.0 ^ ( 6 dB * 0.05))
+ if (val > 1.99526231f)
+ val = 1.99526231f;
+
+ if (src != _gain_control.get()) {
+ _gain_control->set_value(val);
+ // bit twisty, this will come back and call us again
+ // (this keeps control in sync with reality)
+ return;
+ }
+
+ {
+ Glib::Mutex::Lock dm (declick_lock);
+ _desired_gain = val;
+ }
+
+ if (_session.transport_stopped()) {
+ _gain = val;
+ }
+
+ if (_session.transport_stopped() && src != 0 && src != this && _gain_control->list()->automation_write()) {
+ _gain_control->list()->add (_session.transport_frame(), val);
+
+ }
+
+ _session.set_dirty();
+}
+
+void
+IO::start_pan_touch (uint32_t which)
+{
+ if (which < _panner->size()) {
+ (*_panner)[which]->pan_control()->list()->start_touch();
+ }
+}
+
+void
+IO::end_pan_touch (uint32_t which)
+{
+ if (which < _panner->size()) {
+ (*_panner)[which]->pan_control()->list()->stop_touch();
+ }
+
+}
+
+void
+IO::automation_snapshot (nframes_t now, bool force)
+{
+ Automatable::automation_snapshot (now, force);
+
+ if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) {
+ _panner->snapshot (now);
+ }
+
+ _panner->snapshot (now);
+ _last_automation_snapshot = now;
+}
+
+void
+IO::transport_stopped (nframes_t frame)
+{
+ _gain_control->list()->reposition_for_rt_add (frame);
+
+ if (_gain_control->list()->automation_state() != Off) {
+
+ /* the src=0 condition is a special signal to not propagate
+ automation gain changes into the mix group when locating.
+ */
+
+ // FIXME: shouldn't this use Curve?
+ set_gain (_gain_control->list()->eval (frame), 0);
+ }
+
+ _panner->transport_stopped (frame);
+}
+
+string
+IO::build_legal_port_name (DataType type, bool in)
+{
+ const int name_size = jack_port_name_size();
+ int limit;
+ string suffix;
+ int maxports;
+
+ if (type == DataType::AUDIO) {
+ suffix = _("audio");
+ } else if (type == DataType::MIDI) {
+ suffix = _("midi");
+ } else {
+ throw unknown_type();
+ }
+
+ if (in) {
+ suffix += _("_in");
+ maxports = _input_maximum.get(type);
+ } else {
+ suffix += _("_out");
+ maxports = _output_maximum.get(type);
+ }
+
+ if (maxports == 1) {
+ // allow space for the slash + the suffix
+ limit = name_size - _session.engine().client_name().length() - (suffix.length() + 1);
+ char buf[name_size+1];
+ snprintf (buf, name_size+1, ("%.*s/%s"), limit, _name.c_str(), suffix.c_str());
+ return string (buf);
+ }
+
+ // allow up to 4 digits for the output port number, plus the slash, suffix and extra space
+
+ limit = name_size - _session.engine().client_name().length() - (suffix.length() + 5);
+
+ char buf1[name_size+1];
+ char buf2[name_size+1];
+
+ snprintf (buf1, name_size+1, ("%.*s/%s"), limit, _name.c_str(), suffix.c_str());
+
+ int port_number;
+
+ if (in) {
+ port_number = find_input_port_hole (buf1);
+ } else {
+ port_number = find_output_port_hole (buf1);
+ }
+
+ snprintf (buf2, name_size+1, "%s %d", buf1, port_number);
+
+ return string (buf2);
+}
+
+int32_t
+IO::find_input_port_hole (const char* base)
+{
+ /* CALLER MUST HOLD IO LOCK */
+
+ uint32_t n;
+
+ if (_inputs.empty()) {
+ return 1;
+ }
+
+ /* we only allow up to 4 characters for the port number
+ */
+
+ for (n = 1; n < 9999; ++n) {
+ char buf[jack_port_name_size()];
+ PortSet::iterator i = _inputs.begin();
+
+ snprintf (buf, jack_port_name_size(), _("%s %u"), base, n);
+
+ for ( ; i != _inputs.end(); ++i) {
+ if (i->short_name() == buf) {
+ break;
+ }
+ }
+
+ if (i == _inputs.end()) {
+ break;
+ }
+ }
+ return n;
+}
+
+int32_t
+IO::find_output_port_hole (const char* base)
+{
+ /* CALLER MUST HOLD IO LOCK */
+
+ uint32_t n;
+
+ if (_outputs.empty()) {
+ return 1;
+ }
+
+ /* we only allow up to 4 characters for the port number
+ */
+
+ for (n = 1; n < 9999; ++n) {
+ char buf[jack_port_name_size()];
+ PortSet::iterator i = _outputs.begin();
+
+ snprintf (buf, jack_port_name_size(), _("%s %u"), base, n);
+
+ for ( ; i != _outputs.end(); ++i) {
+ if (i->short_name() == buf) {
+ break;
+ }
+ }
+
+ if (i == _outputs.end()) {
+ break;
+ }
+ }
+
+ return n;
+}
+
+void
+IO::set_active (bool yn)
+{
+ _active = yn;
+ active_changed(); /* EMIT SIGNAL */
+}
+
+
+AudioPort*
+IO::audio_input(uint32_t n) const
+{
+ return dynamic_cast<AudioPort*>(input(n));
+}
+
+AudioPort*
+IO::audio_output(uint32_t n) const
+{
+ return dynamic_cast<AudioPort*>(output(n));
+}
+
+MidiPort*
+IO::midi_input(uint32_t n) const
+{
+ return dynamic_cast<MidiPort*>(input(n));
+}
+
+MidiPort*
+IO::midi_output(uint32_t n) const
+{
+ return dynamic_cast<MidiPort*>(output(n));
+}
+
+void
+IO::set_phase_invert (bool yn, void *src)
+{
+ if (_phase_invert != yn) {
+ _phase_invert = yn;
+ // phase_invert_changed (src); /* EMIT SIGNAL */
+ }
+}
+
+void
+IO::set_denormal_protection (bool yn, void *src)
+{
+ if (_denormal_protection != yn) {
+ _denormal_protection = yn;
+ // denormal_protection_changed (src); /* EMIT SIGNAL */
+ }
+}
+
+void
+IO::update_port_total_latencies ()
+{
+ /* io_lock, not taken: function must be called from Session::process() calltree */
+
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ _session.engine().update_total_latency (*i);
+ }
+
+ for (PortSet::iterator i = _outputs.begin(); i != _outputs.end(); ++i) {
+ _session.engine().update_total_latency (*i);
+ }
+}
+
+
+/**
+ * Setup bundles that describe our inputs and outputs.
+ */
+
+void
+IO::setup_bundles_for_inputs_and_outputs ()
+{
+ char buf[32];
+
+ snprintf(buf, sizeof (buf), _("%s in"), _name.c_str());
+ _bundle_for_inputs->set_name (buf);
+ uint32_t const ni = inputs().num_ports();
+ _bundle_for_inputs->set_channels (ni);
+ for (uint32_t i = 0; i < ni; ++i) {
+ _bundle_for_inputs->set_port (i, inputs().port(i)->name());
+ }
+
+ snprintf(buf, sizeof (buf), _("%s out"), _name.c_str());
+ _bundle_for_outputs->set_name (buf);
+ uint32_t const no = outputs().num_ports();
+ _bundle_for_outputs->set_channels (no);
+ for (uint32_t i = 0; i < no; ++i) {
+ _bundle_for_outputs->set_port (i, outputs().port(i)->name());
+ }
+}
+
+
+/**
+ * Create and setup bundles that describe our inputs and outputs.
+ */
+
+void
+IO::create_bundles_for_inputs_and_outputs ()
+{
+ _bundle_for_inputs = boost::shared_ptr<AutoBundle> (new AutoBundle (true));
+ _bundle_for_outputs = boost::shared_ptr<AutoBundle> (new AutoBundle (false));
+ setup_bundles_for_inputs_and_outputs ();
+}
+
+/** Add a bundle to a list if is connected to our inputs.
+ * @param b Bundle to check.
+ * @param bundles List to add to.
+ */
+void
+IO::maybe_add_input_bundle_to_list (boost::shared_ptr<Bundle> b, std::vector<boost::shared_ptr<Bundle> >* bundles)
+{
+ boost::shared_ptr<AutoBundle> ab = boost::dynamic_pointer_cast<AutoBundle, Bundle> (b);
+ if (ab == 0 || ab->ports_are_outputs() == false) {
+ return;
+ }
+
+ if (ab->nchannels () != n_inputs().n_total ()) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < n_inputs().n_total (); ++i) {
+
+ PortList const & pl = b->channel_ports (i);
+
+ if (pl.empty()) {
+ return;
+ }
+
+ if (!input(i)->connected_to (pl[0])) {
+ return;
+ }
+ }
+
+ bundles->push_back (b);
+}
+
+/** @return Bundles connected to our inputs */
+std::vector<boost::shared_ptr<Bundle> >
+IO::bundles_connected_to_inputs ()
+{
+ std::vector<boost::shared_ptr<Bundle> > bundles;
+
+ /* User bundles */
+ for (std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_inputs.begin(); i != _bundles_connected_to_inputs.end(); ++i) {
+ bundles.push_back (i->bundle);
+ }
+
+ /* Auto bundles */
+ _session.foreach_bundle (
+ sigc::bind (sigc::mem_fun (*this, &IO::maybe_add_input_bundle_to_list), &bundles)
+ );
+
+ return bundles;
+}
+
+
+/** Add a bundle to a list if is connected to our outputs.
+ * @param b Bundle to check.
+ * @param bundles List to add to.
+ */
+void
+IO::maybe_add_output_bundle_to_list (boost::shared_ptr<Bundle> b, std::vector<boost::shared_ptr<Bundle> >* bundles)
+{
+ boost::shared_ptr<AutoBundle> ab = boost::dynamic_pointer_cast<AutoBundle, Bundle> (b);
+ if (ab == 0 || ab->ports_are_inputs() == false) {
+ return;
+ }
+
+ if (ab->nchannels () != n_outputs().n_total ()) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < n_outputs().n_total (); ++i) {
+
+ PortList const & pl = b->channel_ports (i);
+
+ if (pl.empty()) {
+ return;
+ }
+
+ if (!output(i)->connected_to (pl[0])) {
+ return;
+ }
+ }
+
+ bundles->push_back (b);
+}
+
+
+/* @return Bundles connected to our outputs */
+std::vector<boost::shared_ptr<Bundle> >
+IO::bundles_connected_to_outputs ()
+{
+ std::vector<boost::shared_ptr<Bundle> > bundles;
+
+ /* User bundles */
+ for (std::vector<UserBundleInfo>::iterator i = _bundles_connected_to_outputs.begin(); i != _bundles_connected_to_outputs.end(); ++i) {
+ bundles.push_back (i->bundle);
+ }
+
+ /* Auto bundles */
+ _session.foreach_bundle (
+ sigc::bind (sigc::mem_fun (*this, &IO::maybe_add_output_bundle_to_list), &bundles)
+ );
+
+ return bundles;
+}
+
+
+IO::UserBundleInfo::UserBundleInfo (IO* io, boost::shared_ptr<UserBundle> b)
+{
+ bundle = b;
+ configuration_will_change = b->ConfigurationWillChange.connect (
+ sigc::mem_fun (*io, &IO::bundle_configuration_will_change)
+ );
+ configuration_has_changed = b->ConfigurationHasChanged.connect (
+ sigc::mem_fun (*io, &IO::bundle_configuration_has_changed)
+ );
+ ports_will_change = b->PortsWillChange.connect (
+ sigc::mem_fun (*io, &IO::bundle_ports_will_change)
+ );
+ ports_have_changed = b->PortsHaveChanged.connect (
+ sigc::mem_fun (*io, &IO::bundle_ports_have_changed)
+ );
+}
+
diff --git a/libs/ardour/io_processor.cc b/libs/ardour/io_processor.cc
new file mode 100644
index 0000000000..9802c83330
--- /dev/null
+++ b/libs/ardour/io_processor.cc
@@ -0,0 +1,107 @@
+/*
+ Copyright (C) 2001 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <fstream>
+#include <algorithm>
+#include <string>
+#include <cerrno>
+#include <unistd.h>
+#include <sstream>
+
+#include <sigc++/bind.h>
+
+#include <pbd/xml++.h>
+#include <pbd/enumwriter.h>
+
+#include <ardour/io_processor.h>
+#include <ardour/session.h>
+#include <ardour/utils.h>
+#include <ardour/send.h>
+#include <ardour/port_insert.h>
+#include <ardour/plugin_insert.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+IOProcessor::IOProcessor (Session& s, const string& name, Placement p,
+ int input_min, int input_max,
+ int output_min, int output_max)
+ : Processor(s, name, p)
+ , _io(new IO(s, name, input_min, input_max, output_min, output_max))
+{
+ _active = false;
+ _sort_key = 0;
+ _gui = 0;
+ _extra_xml = 0;
+}
+
+IOProcessor::~IOProcessor ()
+{
+ notify_callbacks ();
+}
+
+XMLNode&
+IOProcessor::state (bool full_state)
+{
+ XMLNode& node = Processor::state(full_state);
+
+ node.add_child_nocopy (_io->state (full_state));
+
+ return node;
+}
+
+int
+IOProcessor::set_state (const XMLNode& node)
+{
+ const XMLProperty *prop;
+
+ Processor::set_state(node);
+
+ XMLNodeList nlist = node.children();
+ XMLNodeIterator niter;
+ bool have_io = false;
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == IO::state_node_name) {
+ have_io = true;
+ _io->set_state(**niter);
+
+ // legacy sessions: use IO name
+ if ((prop = node.property ("name")) == 0) {
+ set_name(_io->name());
+ }
+ }
+ }
+
+ if (!have_io) {
+ error << _("XML node describing a redirect is missing an IO node") << endmsg;
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+IOProcessor::silence (nframes_t nframes, nframes_t offset)
+{
+ _io->silence(nframes, offset);
+}
diff --git a/libs/ardour/jack_audio_port.cc b/libs/ardour/jack_audio_port.cc
new file mode 100644
index 0000000000..7ce00b8f11
--- /dev/null
+++ b/libs/ardour/jack_audio_port.cc
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cassert>
+#include <ardour/audioengine.h>
+#include <ardour/jack_audio_port.h>
+
+using namespace ARDOUR;
+
+JackAudioPort::JackAudioPort (const std::string& name, Flags flgs, AudioBuffer* buf)
+ : Port (name, flgs)
+ , JackPort (name, DataType::AUDIO, flgs)
+ , BaseAudioPort (name, flgs)
+{
+ if (buf) {
+
+ _buffer = buf;
+ _own_buffer = false;
+
+ } else {
+
+ /* data space will be provided by JACK */
+
+ _buffer = new AudioBuffer (0);
+ _own_buffer = true;
+ }
+}
+
+int
+JackAudioPort::reestablish ()
+{
+ int ret = JackPort::reestablish ();
+
+ if (ret == 0 && _flags & IsOutput) {
+ _buffer->clear ();
+ }
+
+ return ret;
+}
+
diff --git a/libs/ardour/jack_midi_port.cc b/libs/ardour/jack_midi_port.cc
new file mode 100644
index 0000000000..6d8e4c8c5d
--- /dev/null
+++ b/libs/ardour/jack_midi_port.cc
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cassert>
+#include <ardour/jack_midi_port.h>
+
+using namespace ARDOUR;
+JackMidiPort::JackMidiPort (const std::string& name, Flags flgs, MidiBuffer* buf)
+ : Port (name, flgs)
+ , JackPort (name, DataType::MIDI, flgs)
+ , BaseMidiPort (name, flgs)
+{
+ // MIDI ports always need a buffer since jack buffer format is different
+ assert(buf);
+
+ _buffer = buf;
+ _own_buffer = false;
+}
+
+void
+JackMidiPort::cycle_start (nframes_t nframes, nframes_t offset)
+{
+ /* FIXME: offset */
+
+ _buffer->clear();
+ assert(_buffer->size() == 0);
+
+ if (_flags & IsOutput) {
+ return;
+ }
+
+ // We're an input - copy Jack events to internal buffer
+
+ void* jack_buffer = jack_port_get_buffer(_port, nframes);
+ const nframes_t event_count = jack_midi_get_event_count(jack_buffer);
+
+ assert(event_count < _buffer->capacity());
+
+ jack_midi_event_t ev;
+
+ for (nframes_t i=0; i < event_count; ++i) {
+
+ jack_midi_event_get (&ev, jack_buffer, i);
+
+ _buffer->push_back (ev);
+ }
+
+ assert(_buffer->size() == event_count);
+
+ /*if (_buffer->size() > 0)
+ cerr << "JackMIDIPort got " << event_count << " events (buf " << _buffer << ")" << endl;*/
+}
+
+void
+JackMidiPort::cycle_end (nframes_t nframes, nframes_t offset)
+{
+ /* FIXME: offset */
+
+ if (_flags & IsInput) {
+ return;
+ }
+
+ // We're an output - copy events from source buffer to Jack buffer
+
+ void* jack_buffer = jack_port_get_buffer (_port, nframes);
+
+ jack_midi_clear_buffer (jack_buffer);
+
+ for (MidiBuffer::iterator i = _buffer->begin(); i != _buffer->end(); ++i) {
+ const MIDI::Event& ev = *i;
+ // event times should be frames, relative to cycle start
+ assert(ev.time() >= 0);
+ assert(ev.time() < nframes);
+ jack_midi_event_write (jack_buffer, (jack_nframes_t) ev.time(), ev.buffer(), ev.size());
+ }
+}
diff --git a/libs/ardour/jack_port.cc b/libs/ardour/jack_port.cc
new file mode 100644
index 0000000000..5fac52af68
--- /dev/null
+++ b/libs/ardour/jack_port.cc
@@ -0,0 +1,179 @@
+/*
+ Copyright (C) 2002-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/error.h>
+
+#include <ardour/jack_port.h>
+#include <ardour/audioengine.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using namespace std;
+
+JackPort::JackPort (const std::string& name, DataType type, Flags flgs)
+ : Port (name, flgs), _port (0)
+{
+ _port = jack_port_register (engine->jack(), name.c_str(), type.to_jack_type(), flgs, 0);
+
+ if (_port == 0) {
+ throw failed_constructor();
+ }
+
+ _flags = flgs;
+ _type = type;
+ _name = jack_port_name (_port);
+}
+
+JackPort::~JackPort ()
+{
+ cerr << "deleting jack port " << _name << endl;
+
+ jack_port_unregister (engine->jack(), _port);
+}
+
+int
+JackPort::set_name (const string& str)
+{
+ int ret;
+
+ if ((ret = jack_port_set_name (_port, str.c_str())) == 0) {
+ _name = str;
+ }
+
+ return ret;
+}
+
+int
+JackPort::disconnect ()
+{
+ return jack_port_disconnect (engine->jack(), _port);
+}
+
+nframes_t
+JackPort::total_latency () const
+{
+ return jack_port_get_total_latency (engine->jack(), _port);
+}
+
+int
+JackPort::reestablish ()
+{
+ string short_name;
+
+ short_name = _name.substr (_name.find_last_of (':') + 1);
+
+ _port = jack_port_register (engine->jack(), short_name.c_str(), type().to_jack_type(), _flags, 0);
+
+ if (_port == 0) {
+ error << string_compose (_("could not reregister %1"), _name) << endmsg;
+ return -1;
+ }
+
+ reset ();
+
+
+ return 0;
+}
+
+void
+JackPort::recompute_total_latency () const
+{
+#ifdef HAVE_JACK_RECOMPUTE_LATENCY
+ jack_recompute_total_latency (engine->jack(), _port);
+#endif
+}
+
+int
+JackPort::reconnect ()
+{
+ /* caller must hold process lock; intended to be used only after reestablish() */
+
+ for (set<string>::iterator i = _named_connections.begin(); i != _named_connections.end(); ++i) {
+ if (connect (*i)) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+JackPort::connect (const std::string& other)
+{
+ int ret;
+
+ if (_flags & IsOutput) {
+ /* this is the source */
+ ret = jack_connect (engine->jack(), _name.c_str(), other.c_str());
+ } else {
+ ret = jack_connect (engine->jack(), other.c_str(), _name.c_str());
+ }
+
+ if (ret == 0) {
+ _named_connections.insert (other);
+ }
+
+ return ret;
+}
+
+int
+JackPort::disconnect (const std::string& other)
+{
+ int ret;
+
+ if (_flags & IsInput) {
+ ret = jack_disconnect (engine->jack(), other.c_str(), _name.c_str());
+ } else {
+ ret = jack_disconnect (engine->jack(), _name.c_str(), other.c_str());
+ }
+
+ set<string>::iterator i = _named_connections.find (other);
+
+ if (i != _named_connections.end()) {
+ _named_connections.erase (i);
+ }
+
+ return ret;
+}
+
+int
+JackPort::disconnect_all ()
+{
+ _named_connections.clear ();
+ return jack_port_disconnect (engine->jack(), _port);
+}
+
+int
+JackPort::get_connections (vector<string>& names) const
+{
+ const char** cstrs = jack_port_get_connections (_port);
+ int i;
+
+ if (!cstrs) {
+ return 0;
+ }
+
+ for (i = 0; cstrs[i]; ++i) {
+ names.push_back (string (cstrs[i]));
+ }
+
+ return i;
+}
diff --git a/libs/ardour/jack_slave.cc b/libs/ardour/jack_slave.cc
new file mode 100644
index 0000000000..f65be1deea
--- /dev/null
+++ b/libs/ardour/jack_slave.cc
@@ -0,0 +1,95 @@
+/*
+ Copyright (C) 2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+
+#include <errno.h>
+#include <jack/jack.h>
+#include <jack/transport.h>
+
+#include <ardour/slave.h>
+#include <ardour/session.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace sigc;
+
+JACK_Slave::JACK_Slave (jack_client_t* j)
+ : jack (j)
+{
+ float x;
+ nframes_t p;
+ /* call this to initialize things */
+ speed_and_position (x, p);
+}
+
+JACK_Slave::~JACK_Slave ()
+{
+}
+
+void
+JACK_Slave::reset_client (jack_client_t* j)
+{
+ jack = j;
+}
+
+bool
+JACK_Slave::locked() const
+{
+ return true;
+}
+
+bool
+JACK_Slave::ok() const
+{
+ return true;
+}
+
+bool
+JACK_Slave::speed_and_position (float& sp, nframes_t& position)
+{
+ jack_position_t pos;
+ jack_transport_state_t state;
+
+ state = jack_transport_query (jack, &pos);
+
+ switch (state) {
+ case JackTransportStopped:
+ speed = 0;
+ _starting = false;
+ break;
+ case JackTransportRolling:
+ speed = 1.0;
+ _starting = false;
+ break;
+ case JackTransportLooping:
+ speed = 1.0;
+ _starting = false;
+ break;
+ case JackTransportStarting:
+ _starting = true;
+ // don't adjust speed here, just leave it as it was
+ break;
+ }
+
+ sp = speed;
+ position = pos.frame;
+ return true;
+}
diff --git a/libs/ardour/ladspa_plugin.cc b/libs/ardour/ladspa_plugin.cc
new file mode 100644
index 0000000000..29f2d16767
--- /dev/null
+++ b/libs/ardour/ladspa_plugin.cc
@@ -0,0 +1,663 @@
+/*
+ Copyright (C) 2000-2006 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.
+
+*/
+
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#include <vector>
+#include <string>
+
+#include <cstdlib>
+#include <cstdio> // so libraptor doesn't complain
+#include <cmath>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <cerrno>
+
+#include <lrdf.h>
+
+#include <pbd/compose.h>
+#include <pbd/error.h>
+#include <pbd/xml++.h>
+
+#include <midi++/manager.h>
+
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+#include <ardour/audioengine.h>
+#include <ardour/ladspa_plugin.h>
+#include <ardour/buffer_set.h>
+
+#include <pbd/stl_delete.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+LadspaPlugin::LadspaPlugin (void *mod, AudioEngine& e, Session& session, uint32_t index, nframes_t rate)
+ : Plugin (e, session)
+{
+ init (mod, index, rate);
+}
+
+LadspaPlugin::LadspaPlugin (const LadspaPlugin &other)
+ : Plugin (other)
+{
+ init (other._module, other._index, other._sample_rate);
+
+ for (uint32_t i = 0; i < parameter_count(); ++i) {
+ _control_data[i] = other._shadow_data[i];
+ _shadow_data[i] = other._shadow_data[i];
+ }
+}
+
+void
+LadspaPlugin::init (void *mod, uint32_t index, nframes_t rate)
+{
+ LADSPA_Descriptor_Function dfunc;
+ uint32_t i, port_cnt;
+ const char *errstr;
+
+ _module = mod;
+ _control_data = 0;
+ _shadow_data = 0;
+ _latency_control_port = 0;
+ _was_activated = false;
+
+ dfunc = (LADSPA_Descriptor_Function) dlsym (_module, "ladspa_descriptor");
+
+ if ((errstr = dlerror()) != NULL) {
+ error << _("LADSPA: module has no descriptor function.") << endmsg;
+ throw failed_constructor();
+ }
+
+ if ((_descriptor = dfunc (index)) == 0) {
+ error << _("LADSPA: plugin has gone away since discovery!") << endmsg;
+ throw failed_constructor();
+ }
+
+ _index = index;
+
+ if (LADSPA_IS_INPLACE_BROKEN(_descriptor->Properties)) {
+ error << string_compose(_("LADSPA: \"%1\" cannot be used, since it cannot do inplace processing"), _descriptor->Name) << endmsg;
+ throw failed_constructor();
+ }
+
+ _sample_rate = rate;
+
+ if (_descriptor->instantiate == 0) {
+ throw failed_constructor();
+ }
+
+ if ((_handle = _descriptor->instantiate (_descriptor, rate)) == 0) {
+ throw failed_constructor();
+ }
+
+ port_cnt = parameter_count();
+
+ _control_data = new LADSPA_Data[port_cnt];
+ _shadow_data = new LADSPA_Data[port_cnt];
+
+ for (i = 0; i < port_cnt; ++i) {
+ if (LADSPA_IS_PORT_CONTROL(port_descriptor (i))) {
+ connect_port (i, &_control_data[i]);
+
+ if (LADSPA_IS_PORT_OUTPUT(port_descriptor (i)) &&
+ strcmp (port_names()[i], X_("latency")) == 0) {
+ _latency_control_port = &_control_data[i];
+ *_latency_control_port = 0;
+ }
+
+ if (!LADSPA_IS_PORT_INPUT(port_descriptor (i))) {
+ continue;
+ }
+
+ _shadow_data[i] = default_value (i);
+ }
+ }
+
+ latency_compute_run ();
+}
+
+LadspaPlugin::~LadspaPlugin ()
+{
+ deactivate ();
+ cleanup ();
+
+ GoingAway (); /* EMIT SIGNAL */
+
+ /* XXX who should close a plugin? */
+
+ // dlclose (module);
+
+ if (_control_data) {
+ delete [] _control_data;
+ }
+
+ if (_shadow_data) {
+ delete [] _shadow_data;
+ }
+}
+
+string
+LadspaPlugin::unique_id() const
+{
+ char buf[32];
+ snprintf (buf, sizeof (buf), "%lu", _descriptor->UniqueID);
+ return string (buf);
+}
+
+float
+LadspaPlugin::default_value (uint32_t port)
+{
+ const LADSPA_PortRangeHint *prh = port_range_hints();
+ float ret = 0.0f;
+ bool bounds_given = false;
+ bool sr_scaling = false;
+ bool earlier_hint = false;
+
+ /* defaults - case 1 */
+
+ if (LADSPA_IS_HINT_HAS_DEFAULT(prh[port].HintDescriptor)) {
+ if (LADSPA_IS_HINT_DEFAULT_MINIMUM(prh[port].HintDescriptor)) {
+ ret = prh[port].LowerBound;
+ bounds_given = true;
+ sr_scaling = true;
+ earlier_hint = true;
+ }
+
+ /* FIXME: add support for logarithmic defaults */
+
+ else if (LADSPA_IS_HINT_DEFAULT_LOW(prh[port].HintDescriptor)) {
+ ret = prh[port].LowerBound * 0.75f + prh[port].UpperBound * 0.25f;
+ bounds_given = true;
+ sr_scaling = true;
+ earlier_hint = true;
+ }
+ else if (LADSPA_IS_HINT_DEFAULT_MIDDLE(prh[port].HintDescriptor)) {
+ ret = prh[port].LowerBound * 0.50f + prh[port].UpperBound * 0.50f;
+ bounds_given = true;
+ sr_scaling = true;
+ earlier_hint = true;
+ }
+ else if (LADSPA_IS_HINT_DEFAULT_HIGH(prh[port].HintDescriptor)) {
+ ret = prh[port].LowerBound * 0.25f + prh[port].UpperBound * 0.75f;
+ bounds_given = true;
+ sr_scaling = true;
+ earlier_hint = true;
+ }
+ else if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(prh[port].HintDescriptor)) {
+ ret = prh[port].UpperBound;
+ bounds_given = true;
+ sr_scaling = true;
+ earlier_hint = true;
+ }
+ else if (LADSPA_IS_HINT_DEFAULT_0(prh[port].HintDescriptor)) {
+ ret = 0.0f;
+ earlier_hint = true;
+ }
+ else if (LADSPA_IS_HINT_DEFAULT_1(prh[port].HintDescriptor)) {
+ ret = 1.0f;
+ earlier_hint = true;
+ }
+ else if (LADSPA_IS_HINT_DEFAULT_100(prh[port].HintDescriptor)) {
+ ret = 100.0f;
+ earlier_hint = true;
+ }
+ else if (LADSPA_IS_HINT_DEFAULT_440(prh[port].HintDescriptor)) {
+ ret = 440.0f;
+ earlier_hint = true;
+ }
+ else {
+ /* no hint found */
+ ret = 0.0f;
+ }
+ }
+
+ /* defaults - case 2 */
+ else if (LADSPA_IS_HINT_BOUNDED_BELOW(prh[port].HintDescriptor) &&
+ !LADSPA_IS_HINT_BOUNDED_ABOVE(prh[port].HintDescriptor)) {
+
+ if (prh[port].LowerBound < 0) {
+ ret = 0.0f;
+ } else {
+ ret = prh[port].LowerBound;
+ }
+
+ bounds_given = true;
+ sr_scaling = true;
+ }
+
+ /* defaults - case 3 */
+ else if (!LADSPA_IS_HINT_BOUNDED_BELOW(prh[port].HintDescriptor) &&
+ LADSPA_IS_HINT_BOUNDED_ABOVE(prh[port].HintDescriptor)) {
+
+ if (prh[port].UpperBound > 0) {
+ ret = 0.0f;
+ } else {
+ ret = prh[port].UpperBound;
+ }
+
+ bounds_given = true;
+ sr_scaling = true;
+ }
+
+ /* defaults - case 4 */
+ else if (LADSPA_IS_HINT_BOUNDED_BELOW(prh[port].HintDescriptor) &&
+ LADSPA_IS_HINT_BOUNDED_ABOVE(prh[port].HintDescriptor)) {
+
+ if (prh[port].LowerBound < 0 && prh[port].UpperBound > 0) {
+ ret = 0.0f;
+ } else if (prh[port].LowerBound < 0 && prh[port].UpperBound < 0) {
+ ret = prh[port].UpperBound;
+ } else {
+ ret = prh[port].LowerBound;
+ }
+ bounds_given = true;
+ sr_scaling = true;
+ }
+
+ /* defaults - case 5 */
+
+ if (LADSPA_IS_HINT_SAMPLE_RATE(prh[port].HintDescriptor) && !earlier_hint) {
+ if (bounds_given) {
+ if (sr_scaling) {
+ ret *= _sample_rate;
+ }
+ } else {
+ ret = _sample_rate;
+ }
+ }
+
+ return ret;
+}
+
+void
+LadspaPlugin::set_parameter (uint32_t which, float val)
+{
+ if (which < _descriptor->PortCount) {
+ _shadow_data[which] = (LADSPA_Data) val;
+#if 0
+ ParameterChanged (Parameter(PluginAutomation, which), val); /* EMIT SIGNAL */
+
+ if (which < parameter_count() && controls[which]) {
+ controls[which]->Changed ();
+ }
+#endif
+
+ } else {
+ warning << string_compose (_("illegal parameter number used with plugin \"%1\". This may"
+ "indicate a change in the plugin design, and presets may be"
+ "invalid"), name())
+ << endmsg;
+ }
+}
+
+float
+LadspaPlugin::get_parameter (uint32_t which) const
+{
+ if (LADSPA_IS_PORT_INPUT(port_descriptor (which))) {
+ return (float) _shadow_data[which];
+ } else {
+ return (float) _control_data[which];
+ }
+}
+
+uint32_t
+LadspaPlugin::nth_parameter (uint32_t n, bool& ok) const
+{
+ uint32_t x, c;
+
+ ok = false;
+
+ for (c = 0, x = 0; x < _descriptor->PortCount; ++x) {
+ if (LADSPA_IS_PORT_CONTROL (port_descriptor (x))) {
+ if (c++ == n) {
+ ok = true;
+ return x;
+ }
+ }
+ }
+ return 0;
+}
+
+XMLNode&
+LadspaPlugin::get_state()
+{
+ XMLNode *root = new XMLNode(state_node_name());
+ XMLNode *child;
+ char buf[16];
+ LocaleGuard lg (X_("POSIX"));
+
+ for (uint32_t i = 0; i < parameter_count(); ++i){
+
+ if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) &&
+ LADSPA_IS_PORT_CONTROL(port_descriptor (i))){
+
+ child = new XMLNode("port");
+ snprintf(buf, sizeof(buf), "%u", i);
+ child->add_property("number", string(buf));
+ snprintf(buf, sizeof(buf), "%+f", _shadow_data[i]);
+ child->add_property("value", string(buf));
+ root->add_child_nocopy (*child);
+ }
+ }
+
+ return *root;
+}
+
+bool
+LadspaPlugin::save_preset (string name)
+{
+ return Plugin::save_preset (name, "ladspa");
+}
+
+int
+LadspaPlugin::set_state(const XMLNode& node)
+{
+ XMLNodeList nodes;
+ XMLProperty *prop;
+ XMLNodeConstIterator iter;
+ XMLNode *child;
+ const char *port;
+ const char *data;
+ uint32_t port_id;
+ LocaleGuard lg (X_("POSIX"));
+
+ if (node.name() != state_node_name()) {
+ error << _("Bad node sent to LadspaPlugin::set_state") << endmsg;
+ return -1;
+ }
+
+ nodes = node.children ("port");
+
+ for(iter = nodes.begin(); iter != nodes.end(); ++iter){
+
+ child = *iter;
+
+ if ((prop = child->property("number")) != 0) {
+ port = prop->value().c_str();
+ } else {
+ warning << _("LADSPA: no ladspa port number") << endmsg;
+ continue;
+ }
+ if ((prop = child->property("value")) != 0) {
+ data = prop->value().c_str();
+ } else {
+ warning << _("LADSPA: no ladspa port data") << endmsg;
+ continue;
+ }
+
+ sscanf (port, "%" PRIu32, &port_id);
+ set_parameter (port_id, atof(data));
+ }
+
+ latency_compute_run ();
+
+ return 0;
+}
+
+int
+LadspaPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) const
+{
+ LADSPA_PortRangeHint prh;
+
+ prh = port_range_hints()[which];
+
+
+ if (LADSPA_IS_HINT_BOUNDED_BELOW(prh.HintDescriptor)) {
+ desc.min_unbound = false;
+ if (LADSPA_IS_HINT_SAMPLE_RATE(prh.HintDescriptor)) {
+ desc.lower = prh.LowerBound * _session.frame_rate();
+ } else {
+ desc.lower = prh.LowerBound;
+ }
+ } else {
+ desc.min_unbound = true;
+ desc.lower = 0;
+ }
+
+
+ if (LADSPA_IS_HINT_BOUNDED_ABOVE(prh.HintDescriptor)) {
+ desc.max_unbound = false;
+ if (LADSPA_IS_HINT_SAMPLE_RATE(prh.HintDescriptor)) {
+ desc.upper = prh.UpperBound * _session.frame_rate();
+ } else {
+ desc.upper = prh.UpperBound;
+ }
+ } else {
+ desc.max_unbound = true;
+ desc.upper = 4; /* completely arbitrary */
+ }
+
+ if (LADSPA_IS_HINT_INTEGER (prh.HintDescriptor)) {
+ desc.step = 1.0;
+ desc.smallstep = 0.1;
+ desc.largestep = 10.0;
+ } else {
+ float delta = desc.upper - desc.lower;
+ desc.step = delta / 1000.0f;
+ desc.smallstep = delta / 10000.0f;
+ desc.largestep = delta/10.0f;
+ }
+
+ desc.toggled = LADSPA_IS_HINT_TOGGLED (prh.HintDescriptor);
+ desc.logarithmic = LADSPA_IS_HINT_LOGARITHMIC (prh.HintDescriptor);
+ desc.sr_dependent = LADSPA_IS_HINT_SAMPLE_RATE (prh.HintDescriptor);
+ desc.integer_step = LADSPA_IS_HINT_INTEGER (prh.HintDescriptor);
+
+ desc.label = port_names()[which];
+
+ return 0;
+}
+
+string
+LadspaPlugin::describe_parameter (Parameter which)
+{
+ if (which.type() == PluginAutomation && which.id() < parameter_count()) {
+ return port_names()[which.id()];
+ } else {
+ return "??";
+ }
+}
+
+ARDOUR::nframes_t
+LadspaPlugin::signal_latency () const
+{
+ if (_user_latency) {
+ return _user_latency;
+ }
+
+ if (_latency_control_port) {
+ return (nframes_t) floor (*_latency_control_port);
+ } else {
+ return 0;
+ }
+}
+
+set<Parameter>
+LadspaPlugin::automatable () const
+{
+ set<Parameter> ret;
+
+ for (uint32_t i = 0; i < parameter_count(); ++i){
+ if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) &&
+ LADSPA_IS_PORT_CONTROL(port_descriptor (i))){
+
+ ret.insert (ret.end(), Parameter(PluginAutomation, i));
+ }
+ }
+
+ return ret;
+}
+
+int
+LadspaPlugin::connect_and_run (BufferSet& bufs, uint32_t& in_index, uint32_t& out_index, nframes_t nframes, nframes_t offset)
+{
+ uint32_t port_index = 0;
+ cycles_t then, now;
+
+ then = get_cycles ();
+
+ const uint32_t nbufs = bufs.count().n_audio();
+
+ while (port_index < parameter_count()) {
+ if (LADSPA_IS_PORT_AUDIO (port_descriptor(port_index))) {
+ if (LADSPA_IS_PORT_INPUT (port_descriptor(port_index))) {
+ const size_t index = min(in_index, nbufs - 1);
+ connect_port (port_index, bufs.get_audio(index).data(nframes, offset));
+ //cerr << this << ' ' << name() << " @ " << offset << " inport " << in_index << " = buf "
+ // << min((uint32_t)in_index,nbufs) << " = " << &bufs[min((uint32_t)in_index,nbufs)][offset] << endl;
+ in_index++;
+
+
+ } else if (LADSPA_IS_PORT_OUTPUT (port_descriptor (port_index))) {
+ const size_t index = min(out_index,nbufs - 1);
+ connect_port (port_index, bufs.get_audio(index).data(nframes, offset));
+ // cerr << this << ' ' << name() << " @ " << offset << " outport " << out_index << " = buf "
+ // << min((uint32_t)out_index,nbufs) << " = " << &bufs[min((uint32_t)out_index,nbufs)][offset] << endl;
+ out_index++;
+ }
+ }
+ port_index++;
+ }
+
+ run_in_place (nframes);
+ now = get_cycles ();
+ set_cycles ((uint32_t) (now - then));
+
+ return 0;
+}
+
+bool
+LadspaPlugin::parameter_is_control (uint32_t param) const
+{
+ return LADSPA_IS_PORT_CONTROL(port_descriptor (param));
+}
+
+bool
+LadspaPlugin::parameter_is_audio (uint32_t param) const
+{
+ return LADSPA_IS_PORT_AUDIO(port_descriptor (param));
+}
+
+bool
+LadspaPlugin::parameter_is_output (uint32_t param) const
+{
+ return LADSPA_IS_PORT_OUTPUT(port_descriptor (param));
+}
+
+bool
+LadspaPlugin::parameter_is_input (uint32_t param) const
+{
+ return LADSPA_IS_PORT_INPUT(port_descriptor (param));
+}
+
+void
+LadspaPlugin::print_parameter (uint32_t param, char *buf, uint32_t len) const
+{
+ if (buf && len) {
+ if (param < parameter_count()) {
+ snprintf (buf, len, "%.3f", get_parameter (param));
+ } else {
+ strcat (buf, "0");
+ }
+ }
+}
+
+void
+LadspaPlugin::run_in_place (nframes_t nframes)
+{
+ for (uint32_t i = 0; i < parameter_count(); ++i) {
+ if (LADSPA_IS_PORT_INPUT(port_descriptor (i)) && LADSPA_IS_PORT_CONTROL(port_descriptor (i))) {
+ _control_data[i] = _shadow_data[i];
+ }
+ }
+ _descriptor->run (_handle, nframes);
+}
+
+void
+LadspaPlugin::latency_compute_run ()
+{
+ if (!_latency_control_port) {
+ return;
+ }
+
+ /* we need to run the plugin so that it can set its latency
+ parameter.
+ */
+
+ activate ();
+
+ uint32_t port_index = 0;
+ uint32_t in_index = 0;
+ uint32_t out_index = 0;
+ const nframes_t bufsize = 1024;
+ LADSPA_Data buffer[bufsize];
+
+ memset(buffer,0,sizeof(LADSPA_Data)*bufsize);
+
+ /* Note that we've already required that plugins
+ be able to handle in-place processing.
+ */
+
+ port_index = 0;
+
+ while (port_index < parameter_count()) {
+ if (LADSPA_IS_PORT_AUDIO (port_descriptor (port_index))) {
+ if (LADSPA_IS_PORT_INPUT (port_descriptor (port_index))) {
+ connect_port (port_index, buffer);
+ in_index++;
+ } else if (LADSPA_IS_PORT_OUTPUT (port_descriptor (port_index))) {
+ connect_port (port_index, buffer);
+ out_index++;
+ }
+ }
+ port_index++;
+ }
+
+ run_in_place (bufsize);
+ deactivate ();
+}
+
+PluginPtr
+LadspaPluginInfo::load (Session& session)
+{
+ try {
+ PluginPtr plugin;
+ void *module;
+
+ if ((module = dlopen (path.c_str(), RTLD_NOW)) == 0) {
+ error << string_compose(_("LADSPA: cannot load module from \"%1\""), path) << endmsg;
+ error << dlerror() << endmsg;
+ } else {
+ plugin.reset (new LadspaPlugin (module, session.engine(), session, index, session.frame_rate()));
+ }
+
+ plugin->set_info(PluginInfoPtr(new LadspaPluginInfo(*this)));
+ return plugin;
+ }
+
+ catch (failed_constructor &err) {
+ return PluginPtr ((Plugin*) 0);
+ }
+}
diff --git a/libs/ardour/location.cc b/libs/ardour/location.cc
new file mode 100644
index 0000000000..0da75810ca
--- /dev/null
+++ b/libs/ardour/location.cc
@@ -0,0 +1,915 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+#include <set>
+#include <cstdio> /* for sprintf */
+#include <unistd.h>
+#include <cerrno>
+#include <ctime>
+#include <sigc++/bind.h>
+
+#include <pbd/stl_delete.h>
+#include <pbd/xml++.h>
+#include <pbd/enumwriter.h>
+
+#include <ardour/location.h>
+#include <ardour/session.h>
+#include <ardour/audiofilesource.h>
+
+#include "i18n.h"
+
+#define SUFFIX_MAX 32
+
+using namespace std;
+using namespace ARDOUR;
+using namespace sigc;
+using namespace PBD;
+
+Location::Location (const Location& other)
+ : _name (other._name),
+ _start (other._start),
+ _end (other._end),
+ _flags (other._flags)
+{
+ /* start and end flags can never be copied, because there can only ever be one of each */
+
+ _flags = Flags (_flags & ~IsStart);
+ _flags = Flags (_flags & ~IsEnd);
+
+ /* copy is not locked even if original was */
+
+ _locked = false;
+}
+
+Location::Location (const XMLNode& node)
+{
+ if (set_state (node)) {
+ throw failed_constructor ();
+ }
+}
+
+Location*
+Location::operator= (const Location& other)
+{
+ if (this == &other) {
+ return this;
+ }
+
+ _name = other._name;
+ _start = other._start;
+ _end = other._end;
+ _flags = other._flags;
+
+ /* copy is not locked even if original was */
+
+ _locked = false;
+
+ /* "changed" not emitted on purpose */
+
+ return this;
+}
+
+int
+Location::set_start (nframes_t s)
+{
+ if (_locked) {
+ return -1;
+ }
+
+ if (is_mark()) {
+ if (_start != s) {
+
+ _start = s;
+ _end = s;
+
+ start_changed(this); /* EMIT SIGNAL */
+
+ if ( is_start() ) {
+
+ Session::StartTimeChanged (); /* EMIT SIGNAL */
+ AudioFileSource::set_header_position_offset ( s );
+ }
+
+ if ( is_end() ) {
+ Session::EndTimeChanged (); /* EMIT SIGNAL */
+ }
+ }
+ return 0;
+ }
+
+ if (((is_auto_punch() || is_auto_loop()) && s >= _end) || s > _end) {
+ return -1;
+ }
+
+ if (s != _start) {
+ _start = s;
+ start_changed(this); /* EMIT SIGNAL */
+ }
+
+ return 0;
+}
+
+int
+Location::set_end (nframes_t e)
+{
+ if (_locked) {
+ return -1;
+ }
+
+ if (is_mark()) {
+ if (_start != e) {
+ _start = e;
+ _end = e;
+ end_changed(this); /* EMIT SIGNAL */
+ }
+ return 0;
+ }
+
+ if (((is_auto_punch() || is_auto_loop()) && e <= _start) || e < _start) {
+ return -1;
+ }
+
+ if (e != _end) {
+ _end = e;
+ end_changed(this); /* EMIT SIGNAL */
+ }
+ return 0;
+}
+
+int
+Location::set (nframes_t start, nframes_t end)
+{
+ if (_locked) {
+ return -1;
+ }
+
+ if (is_mark() && start != end) {
+ return -1;
+ } else if (((is_auto_punch() || is_auto_loop()) && start >= end) || (start > end)) {
+ return -1;
+ }
+
+ if (_start != start) {
+ _start = start;
+ start_changed(this); /* EMIT SIGNAL */
+ }
+
+ if (_end != end) {
+ _end = end;
+ end_changed(this); /* EMIT SIGNAL */
+ }
+ return 0;
+}
+
+int
+Location::move_to (nframes_t pos)
+{
+ if (_locked) {
+ return -1;
+ }
+
+ if (_start != pos) {
+ _start = pos;
+ _end = _start + length();
+
+ changed (this); /* EMIT SIGNAL */
+ }
+
+ return 0;
+}
+
+void
+Location::set_hidden (bool yn, void *src)
+{
+ if (set_flag_internal (yn, IsHidden)) {
+ FlagsChanged (this, src); /* EMIT SIGNAL */
+ }
+}
+
+void
+Location::set_cd (bool yn, void *src)
+{
+ // XXX this really needs to be session start
+ // but its not available here - leave to GUI
+
+ if (_start == 0) {
+ error << _("You cannot put a CD marker at this position") << endmsg;
+ return;
+ }
+
+ if (set_flag_internal (yn, IsCDMarker)) {
+ FlagsChanged (this, src); /* EMIT SIGNAL */
+ }
+}
+
+void
+Location::set_is_end (bool yn, void *src)
+{
+ if (set_flag_internal (yn, IsEnd)) {
+ FlagsChanged (this, src); /* EMIT SIGNAL */
+ }
+}
+
+void
+Location::set_is_start (bool yn, void *src)
+{
+ if (set_flag_internal (yn, IsStart)) {
+ FlagsChanged (this, src); /* EMIT SIGNAL */
+ }
+}
+
+void
+Location::set_auto_punch (bool yn, void *src)
+{
+ if (is_mark() || _start == _end) {
+ return;
+ }
+
+ if (set_flag_internal (yn, IsAutoPunch)) {
+ FlagsChanged (this, src); /* EMIT SIGNAL */
+ }
+}
+
+void
+Location::set_auto_loop (bool yn, void *src)
+{
+ if (is_mark() || _start == _end) {
+ return;
+ }
+
+ if (set_flag_internal (yn, IsAutoLoop)) {
+ FlagsChanged (this, src); /* EMIT SIGNAL */
+ }
+}
+
+bool
+Location::set_flag_internal (bool yn, Flags flag)
+{
+ if (yn) {
+ if (!(_flags & flag)) {
+ _flags = Flags (_flags | flag);
+ return true;
+ }
+ } else {
+ if (_flags & flag) {
+ _flags = Flags (_flags & ~flag);
+ return true;
+ }
+ }
+ return false;
+}
+
+void
+Location::set_mark (bool yn)
+{
+ /* This function is private, and so does not emit signals */
+
+ if (_start != _end) {
+ return;
+ }
+
+ set_flag_internal (yn, IsMark);
+}
+
+
+XMLNode&
+Location::cd_info_node(const string & name, const string & value)
+{
+ XMLNode* root = new XMLNode("CD-Info");
+
+ root->add_property("name", name);
+ root->add_property("value", value);
+
+ return *root;
+}
+
+
+XMLNode&
+Location::get_state (void)
+{
+ XMLNode *node = new XMLNode ("Location");
+ char buf[64];
+
+ typedef map<string, string>::const_iterator CI;
+
+ for(CI m = cd_info.begin(); m != cd_info.end(); ++m){
+ node->add_child_nocopy(cd_info_node(m->first, m->second));
+ }
+
+ id().print (buf, sizeof (buf));
+ node->add_property("id", buf);
+ node->add_property ("name", name());
+ snprintf (buf, sizeof (buf), "%u", start());
+ node->add_property ("start", buf);
+ snprintf (buf, sizeof (buf), "%u", end());
+ node->add_property ("end", buf);
+ node->add_property ("flags", enum_2_string (_flags));
+ node->add_property ("locked", (_locked ? "yes" : "no"));
+
+ return *node;
+}
+
+int
+Location::set_state (const XMLNode& node)
+{
+ const XMLProperty *prop;
+
+ XMLNodeList cd_list = node.children();
+ XMLNodeConstIterator cd_iter;
+ XMLNode *cd_node;
+
+ string cd_name;
+ string cd_value;
+
+ if (node.name() != "Location") {
+ error << _("incorrect XML node passed to Location::set_state") << endmsg;
+ return -1;
+ }
+
+ if ((prop = node.property ("id")) == 0) {
+ warning << _("XML node for Location has no ID information") << endmsg;
+ } else {
+ _id = prop->value ();
+ }
+
+ if ((prop = node.property ("name")) == 0) {
+ error << _("XML node for Location has no name information") << endmsg;
+ return -1;
+ }
+
+ set_name (prop->value());
+
+ if ((prop = node.property ("start")) == 0) {
+ error << _("XML node for Location has no start information") << endmsg;
+ return -1;
+ }
+
+ /* can't use set_start() here, because _end
+ may make the value of _start illegal.
+ */
+
+ _start = atoi (prop->value().c_str());
+
+ if ((prop = node.property ("end")) == 0) {
+ error << _("XML node for Location has no end information") << endmsg;
+ return -1;
+ }
+
+ _end = atoi (prop->value().c_str());
+
+ if ((prop = node.property ("flags")) == 0) {
+ error << _("XML node for Location has no flags information") << endmsg;
+ return -1;
+ }
+
+ _flags = Flags (string_2_enum (prop->value(), _flags));
+
+ if ((prop = node.property ("locked")) != 0) {
+ _locked = (prop->value() == "yes");
+ } else {
+ _locked = false;
+ }
+
+ for (cd_iter = cd_list.begin(); cd_iter != cd_list.end(); ++cd_iter) {
+
+ cd_node = *cd_iter;
+
+ if (cd_node->name() != "CD-Info") {
+ continue;
+ }
+
+ if ((prop = cd_node->property ("name")) != 0) {
+ cd_name = prop->value();
+ } else {
+ throw failed_constructor ();
+ }
+
+ if ((prop = cd_node->property ("value")) != 0) {
+ cd_value = prop->value();
+ } else {
+ throw failed_constructor ();
+ }
+
+
+ cd_info[cd_name] = cd_value;
+
+ }
+
+ changed(this); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+/*---------------------------------------------------------------------- */
+
+Locations::Locations ()
+
+{
+ current_location = 0;
+}
+
+Locations::~Locations ()
+{
+ for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
+ LocationList::iterator tmp = i;
+ ++tmp;
+ delete *i;
+ i = tmp;
+ }
+}
+
+int
+Locations::set_current (Location *loc, bool want_lock)
+
+{
+ int ret;
+
+ if (want_lock) {
+ Glib::Mutex::Lock lm (lock);
+ ret = set_current_unlocked (loc);
+ } else {
+ ret = set_current_unlocked (loc);
+ }
+
+ if (ret == 0) {
+ current_changed (current_location); /* EMIT SIGNAL */
+ }
+ return ret;
+}
+
+int
+Locations::next_available_name(string& result,string base)
+{
+ LocationList::iterator i;
+ Location* location;
+ string temp;
+ string::size_type l;
+ int suffix;
+ char buf[32];
+ bool available[SUFFIX_MAX+1];
+
+ result = base;
+ for (int k=1; k<SUFFIX_MAX; k++) {
+ available[k] = true;
+ }
+ l = base.length();
+ for (i = locations.begin(); i != locations.end(); ++i) {
+ location =* i;
+ temp = location->name();
+ if (l && !temp.find(base,0)) {
+ suffix = atoi(temp.substr(l,3).c_str());
+ if (suffix) available[suffix] = false;
+ }
+ }
+ for (int k=1; k<=SUFFIX_MAX; k++) {
+ if (available[k]) {
+ snprintf (buf, 31, "%d", k);
+ result += buf;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int
+Locations::set_current_unlocked (Location *loc)
+{
+ if (find (locations.begin(), locations.end(), loc) == locations.end()) {
+ error << _("Locations: attempt to use unknown location as selected location") << endmsg;
+ return -1;
+ }
+
+ current_location = loc;
+ return 0;
+}
+
+void
+Locations::clear ()
+{
+ {
+ Glib::Mutex::Lock lm (lock);
+
+ for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
+
+ LocationList::iterator tmp = i;
+ ++tmp;
+
+ if (!(*i)->is_end() && !(*i)->is_start()) {
+ locations.erase (i);
+ }
+
+ i = tmp;
+ }
+
+ current_location = 0;
+ }
+
+ changed (); /* EMIT SIGNAL */
+ current_changed (0); /* EMIT SIGNAL */
+}
+
+void
+Locations::clear_markers ()
+{
+ {
+ Glib::Mutex::Lock lm (lock);
+ LocationList::iterator tmp;
+
+ for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
+ tmp = i;
+ ++tmp;
+
+ if ((*i)->is_mark() && !(*i)->is_end() && !(*i)->is_start()) {
+ locations.erase (i);
+ }
+
+ i = tmp;
+ }
+ }
+
+ changed (); /* EMIT SIGNAL */
+}
+
+void
+Locations::clear_ranges ()
+{
+ {
+ Glib::Mutex::Lock lm (lock);
+ LocationList::iterator tmp;
+
+ for (LocationList::iterator i = locations.begin(); i != locations.end(); ) {
+
+ tmp = i;
+ ++tmp;
+
+ if (!(*i)->is_mark()) {
+ locations.erase (i);
+
+ }
+
+ i = tmp;
+ }
+
+ current_location = 0;
+ }
+
+ changed (); /* EMIT SIGNAL */
+ current_changed (0); /* EMIT SIGNAL */
+}
+
+void
+Locations::add (Location *loc, bool make_current)
+{
+ {
+ Glib::Mutex::Lock lm (lock);
+ locations.push_back (loc);
+
+ if (make_current) {
+ current_location = loc;
+ }
+ }
+
+ added (loc); /* EMIT SIGNAL */
+
+ if (make_current) {
+ current_changed (current_location); /* EMIT SIGNAL */
+ }
+}
+
+void
+Locations::remove (Location *loc)
+
+{
+ bool was_removed = false;
+ bool was_current = false;
+ LocationList::iterator i;
+
+ if (loc->is_end() || loc->is_start()) {
+ return;
+ }
+
+ {
+ Glib::Mutex::Lock lm (lock);
+
+ for (i = locations.begin(); i != locations.end(); ++i) {
+ if ((*i) == loc) {
+ locations.erase (i);
+ was_removed = true;
+ if (current_location == loc) {
+ current_location = 0;
+ was_current = true;
+ }
+ break;
+ }
+ }
+ }
+
+ if (was_removed) {
+
+ removed (loc); /* EMIT SIGNAL */
+
+ if (was_current) {
+ current_changed (0); /* EMIT SIGNAL */
+ }
+
+ changed (); /* EMIT_SIGNAL */
+ }
+}
+
+void
+Locations::location_changed (Location* loc)
+{
+ changed (); /* EMIT SIGNAL */
+}
+
+XMLNode&
+Locations::get_state ()
+{
+ XMLNode *node = new XMLNode ("Locations");
+ LocationList::iterator iter;
+ Glib::Mutex::Lock lm (lock);
+
+ for (iter = locations.begin(); iter != locations.end(); ++iter) {
+ node->add_child_nocopy ((*iter)->get_state ());
+ }
+
+ return *node;
+}
+
+int
+Locations::set_state (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+
+ if (node.name() != "Locations") {
+ error << _("incorrect XML mode passed to Locations::set_state") << endmsg;
+ return -1;
+ }
+
+ nlist = node.children();
+
+ locations.clear ();
+ current_location = 0;
+
+ {
+ Glib::Mutex::Lock lm (lock);
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ try {
+
+ Location *loc = new Location (**niter);
+ locations.push_back (loc);
+ }
+
+ catch (failed_constructor& err) {
+ error << _("could not load location from session file - ignored") << endmsg;
+ }
+ }
+
+ if (locations.size()) {
+
+ current_location = locations.front();
+ } else {
+ current_location = 0;
+ }
+ }
+
+ changed (); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+struct LocationStartEarlierComparison
+{
+ bool operator() (Location *a, Location *b) {
+ return a->start() < b->start();
+ }
+};
+
+struct LocationStartLaterComparison
+{
+ bool operator() (Location *a, Location *b) {
+ return a->start() > b->start();
+ }
+};
+
+Location *
+Locations::first_location_before (nframes_t frame, bool include_special_ranges)
+{
+ LocationList locs;
+
+ {
+ Glib::Mutex::Lock lm (lock);
+ locs = locations;
+ }
+
+ LocationStartLaterComparison cmp;
+ locs.sort (cmp);
+
+ /* locs is now sorted latest..earliest */
+
+ for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
+ if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
+ continue;
+ }
+ if (!(*i)->is_hidden() && (*i)->start() < frame) {
+ return (*i);
+ }
+ }
+
+ return 0;
+}
+
+Location *
+Locations::first_location_after (nframes_t frame, bool include_special_ranges)
+{
+ LocationList locs;
+
+ {
+ Glib::Mutex::Lock lm (lock);
+ locs = locations;
+ }
+
+ LocationStartEarlierComparison cmp;
+ locs.sort (cmp);
+
+ /* locs is now sorted earliest..latest */
+
+ for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
+ if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
+ continue;
+ }
+ if (!(*i)->is_hidden() && (*i)->start() > frame) {
+ return (*i);
+ }
+ }
+
+ return 0;
+}
+
+nframes_t
+Locations::first_mark_before (nframes_t frame, bool include_special_ranges)
+{
+ LocationList locs;
+
+ {
+ Glib::Mutex::Lock lm (lock);
+ locs = locations;
+ }
+
+ LocationStartLaterComparison cmp;
+ locs.sort (cmp);
+
+ /* locs is now sorted latest..earliest */
+
+ for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
+ if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
+ continue;
+ }
+ if (!(*i)->is_hidden()) {
+ if ((*i)->is_mark()) {
+ /* MARK: start == end */
+ if ((*i)->start() < frame) {
+ return (*i)->start();
+ }
+ } else {
+ /* RANGE: start != end, compare start and end */
+ if ((*i)->end() < frame) {
+ return (*i)->end();
+ }
+ if ((*i)->start () < frame) {
+ return (*i)->start();
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+nframes_t
+Locations::first_mark_after (nframes_t frame, bool include_special_ranges)
+{
+ LocationList locs;
+
+ {
+ Glib::Mutex::Lock lm (lock);
+ locs = locations;
+ }
+
+ LocationStartEarlierComparison cmp;
+ locs.sort (cmp);
+
+ /* locs is now sorted earliest..latest */
+
+ for (LocationList::iterator i = locs.begin(); i != locs.end(); ++i) {
+ if (!include_special_ranges && ((*i)->is_auto_loop() || (*i)->is_auto_punch())) {
+ continue;
+ }
+ if (!(*i)->is_hidden()) {
+ if ((*i)->is_mark()) {
+ /* MARK, start == end so just compare start */
+ if ((*i)->start() > frame) {
+ return (*i)->start();
+ }
+ } else {
+ /* RANGE, start != end, compare start and end */
+ if ((*i)->start() > frame ) {
+ return (*i)->start ();
+ }
+ if ((*i)->end() > frame) {
+ return (*i)->end ();
+ }
+ }
+ }
+ }
+
+ return max_frames;
+}
+
+Location*
+Locations::end_location () const
+{
+ for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
+ if ((*i)->is_end()) {
+ return const_cast<Location*> (*i);
+ }
+ }
+ return 0;
+}
+
+Location*
+Locations::start_location () const
+{
+ for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
+ if ((*i)->is_start()) {
+ return const_cast<Location*> (*i);
+ }
+ }
+ return 0;
+}
+
+Location*
+Locations::auto_loop_location () const
+{
+ for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
+ if ((*i)->is_auto_loop()) {
+ return const_cast<Location*> (*i);
+ }
+ }
+ return 0;
+}
+
+Location*
+Locations::auto_punch_location () const
+{
+ for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
+ if ((*i)->is_auto_punch()) {
+ return const_cast<Location*> (*i);
+ }
+ }
+ return 0;
+}
+
+uint32_t
+Locations::num_range_markers () const
+{
+ uint32_t cnt = 0;
+ Glib::Mutex::Lock lm (lock);
+ for (LocationList::const_iterator i = locations.begin(); i != locations.end(); ++i) {
+ if ((*i)->is_range_marker()) {
+ ++cnt;
+ }
+ }
+ return cnt;
+}
+
+Location *
+Locations::get_location_by_id(PBD::ID id)
+{
+ LocationList::iterator it;
+ for (it = locations.begin(); it != locations.end(); it++)
+ if (id == (*it)->id())
+ return *it;
+
+ return 0;
+}
diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc
new file mode 100644
index 0000000000..4553458831
--- /dev/null
+++ b/libs/ardour/lv2_plugin.cc
@@ -0,0 +1,625 @@
+/*
+ Copyright (C) 2008 Paul Davis
+ Author: Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <vector>
+#include <string>
+
+#include <cstdlib>
+#include <cmath>
+
+#include <pbd/compose.h>
+#include <pbd/error.h>
+#include <pbd/pathscanner.h>
+#include <pbd/xml++.h>
+
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+#include <ardour/audioengine.h>
+#include <ardour/lv2_plugin.h>
+
+#include <pbd/stl_delete.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+LV2Plugin::LV2Plugin (AudioEngine& e, Session& session, LV2World& world, SLV2Plugin plugin, nframes_t rate)
+ : Plugin (e, session)
+ , _world(world)
+{
+ init (world, plugin, rate);
+}
+
+LV2Plugin::LV2Plugin (const LV2Plugin &other)
+ : Plugin (other)
+ , _world(other._world)
+{
+ init (other._world, other._plugin, other._sample_rate);
+
+ for (uint32_t i = 0; i < parameter_count(); ++i) {
+ _control_data[i] = other._shadow_data[i];
+ _shadow_data[i] = other._shadow_data[i];
+ }
+}
+
+void
+LV2Plugin::init (LV2World& world, SLV2Plugin plugin, nframes_t rate)
+{
+ _world = world;
+ _plugin = 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, 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();
+ }
+
+ _sample_rate = rate;
+
+ const uint32_t num_ports = slv2_plugin_get_num_ports(plugin);
+
+ _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_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;
+ }
+
+ if (parameter_is_input(i)) {
+ _shadow_data[i] = default_value (i);
+ }
+ } else {
+ _defaults[i] = 0.0f;
+ }
+ }
+
+ latency_compute_run ();
+}
+
+LV2Plugin::~LV2Plugin ()
+{
+ deactivate ();
+ cleanup ();
+
+ GoingAway (); /* EMIT SIGNAL */
+
+ slv2_instance_free(_instance);
+ slv2_value_free(_name);
+ slv2_value_free(_author);
+
+ if (_control_data) {
+ delete [] _control_data;
+ }
+
+ if (_shadow_data) {
+ delete [] _shadow_data;
+ }
+}
+
+string
+LV2Plugin::unique_id() const
+{
+ return slv2_value_as_uri(slv2_plugin_get_uri(_plugin));
+}
+
+
+float
+LV2Plugin::default_value (uint32_t port)
+{
+ return _defaults[port];
+}
+
+void
+LV2Plugin::set_parameter (uint32_t which, float val)
+{
+ if (which < slv2_plugin_get_num_ports(_plugin)) {
+ _shadow_data[which] = val;
+#if 0
+ ParameterChanged (which, val); /* EMIT SIGNAL */
+
+ if (which < parameter_count() && controls[which]) {
+ controls[which]->Changed ();
+ }
+#endif
+
+ } else {
+ warning << string_compose (_("Illegal parameter number used with plugin \"%1\"."
+ "This is a bug in either Ardour or the LV2 plugin (%2)"),
+ name(), unique_id()) << endmsg;
+ }
+}
+
+float
+LV2Plugin::get_parameter (uint32_t which) const
+{
+ if (parameter_is_input(which)) {
+ return (float) _shadow_data[which];
+ } else {
+ return (float) _control_data[which];
+ }
+ return 0.0f;
+}
+
+uint32_t
+LV2Plugin::nth_parameter (uint32_t n, bool& ok) const
+{
+ uint32_t x, c;
+
+ ok = false;
+
+ for (c = 0, x = 0; x < slv2_plugin_get_num_ports(_plugin); ++x) {
+ if (parameter_is_control (x)) {
+ if (c++ == n) {
+ ok = true;
+ return x;
+ }
+ }
+ }
+
+ return 0;
+}
+
+XMLNode&
+LV2Plugin::get_state()
+{
+ XMLNode *root = new XMLNode(state_node_name());
+ XMLNode *child;
+ char buf[16];
+ LocaleGuard lg (X_("POSIX"));
+
+ for (uint32_t i = 0; i < parameter_count(); ++i){
+
+ if (parameter_is_input(i) && parameter_is_control(i)) {
+ child = new XMLNode("port");
+ snprintf(buf, sizeof(buf), "%u", i);
+ child->add_property("number", string(buf));
+ snprintf(buf, sizeof(buf), "%+f", _shadow_data[i]);
+ child->add_property("value", string(buf));
+ root->add_child_nocopy (*child);
+
+ /*if (i < controls.size() && controls[i]) {
+ root->add_child_nocopy (controls[i]->get_state());
+ }*/
+ }
+ }
+
+ return *root;
+}
+
+bool
+LV2Plugin::save_preset (string name)
+{
+ return Plugin::save_preset (name, "lv2");
+}
+
+int
+LV2Plugin::set_state(const XMLNode& node)
+{
+ XMLNodeList nodes;
+ XMLProperty *prop;
+ XMLNodeConstIterator iter;
+ XMLNode *child;
+ const char *port;
+ const char *data;
+ uint32_t port_id;
+ LocaleGuard lg (X_("POSIX"));
+
+ if (node.name() != state_node_name()) {
+ error << _("Bad node sent to LV2Plugin::set_state") << endmsg;
+ return -1;
+ }
+
+ nodes = node.children ("port");
+
+ for(iter = nodes.begin(); iter != nodes.end(); ++iter){
+
+ child = *iter;
+
+ if ((prop = child->property("number")) != 0) {
+ port = prop->value().c_str();
+ } else {
+ warning << _("LV2: no lv2 port number") << endmsg;
+ continue;
+ }
+
+ if ((prop = child->property("value")) != 0) {
+ data = prop->value().c_str();
+ } else {
+ warning << _("LV2: no lv2 port data") << endmsg;
+ continue;
+ }
+
+ sscanf (port, "%" PRIu32, &port_id);
+ set_parameter (port_id, atof(data));
+ }
+
+ latency_compute_run ();
+
+ return 0;
+}
+
+int
+LV2Plugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) const
+{
+ SLV2Port port = slv2_plugin_get_port_by_index(_plugin, which);
+
+ SLV2Value def, min, max;
+ slv2_port_get_range(_plugin, port, &def, &min, &max);
+
+ 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, _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)
+
+ if (desc.integer_step) {
+ desc.step = 1.0;
+ desc.smallstep = 0.1;
+ desc.largestep = 10.0;
+ } else {
+ const float delta = desc.upper - desc.lower;
+ desc.step = delta / 1000.0f;
+ desc.smallstep = delta / 10000.0f;
+ desc.largestep = delta/10.0f;
+ }
+
+ slv2_value_free(def);
+ slv2_value_free(min);
+ slv2_value_free(max);
+
+ return 0;
+}
+
+
+string
+LV2Plugin::describe_parameter (Parameter which)
+{
+ if (which.type() == PluginAutomation && which.id() < parameter_count()) {
+ 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 "??";
+ }
+}
+
+nframes_t
+LV2Plugin::signal_latency () const
+{
+ if (_latency_control_port) {
+ return (nframes_t) floor (*_latency_control_port);
+ } else {
+ return 0;
+ }
+}
+
+set<Parameter>
+LV2Plugin::automatable () const
+{
+ set<Parameter> ret;
+
+ for (uint32_t i = 0; i < parameter_count(); ++i){
+ if (parameter_is_input(i) && parameter_is_control(i)) {
+ ret.insert (ret.end(), Parameter(PluginAutomation, i));
+ }
+ }
+
+ return ret;
+}
+
+int
+LV2Plugin::connect_and_run (BufferSet& bufs, uint32_t& in_index, uint32_t& out_index, nframes_t nframes, nframes_t offset)
+{
+ uint32_t port_index;
+ cycles_t then, now;
+
+ port_index = 0;
+
+ then = get_cycles ();
+
+ const uint32_t nbufs = bufs.count().n_audio();
+
+ while (port_index < parameter_count()) {
+ if (parameter_is_audio(port_index)) {
+ if (parameter_is_input(port_index)) {
+ const size_t index = min(in_index, nbufs - 1);
+ slv2_instance_connect_port(_instance, port_index,
+ bufs.get_audio(index).data(nframes, offset));
+ in_index++;
+ } else if (parameter_is_output(port_index)) {
+ const size_t index = min(out_index,nbufs - 1);
+ slv2_instance_connect_port(_instance, port_index,
+ bufs.get_audio(index).data(nframes, offset));
+ out_index++;
+ }
+ } else if (parameter_is_midi(port_index)) {
+ // FIXME: Switch MIDI buffer format to LV2 event buffer
+ if (parameter_is_input(port_index)) {
+ //const size_t index = min(in_index, nbufs - 1);
+ //slv2_instance_connect_port(_instance, port_index,
+ // bufs.get_midi(index).data(nframes, offset));
+ // FIXME: hope it's connection optional...
+ slv2_instance_connect_port(_instance, port_index, NULL);
+ in_index++;
+ } else if (parameter_is_output(port_index)) {
+ //const size_t index = min(out_index,nbufs - 1);
+ //slv2_instance_connect_port(_instance, port_index,
+ // bufs.get_midi(index).data(nframes, offset));
+ // FIXME: hope it's connection optional...
+ slv2_instance_connect_port(_instance, port_index, NULL);
+ out_index++;
+ }
+ }
+ port_index++;
+ }
+
+ run (nframes);
+ now = get_cycles ();
+ set_cycles ((uint32_t) (now - then));
+
+ return 0;
+}
+
+bool
+LV2Plugin::parameter_is_control (uint32_t param) const
+{
+ 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
+{
+ SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
+ return slv2_port_is_a(_plugin, port, _world.audio_class);
+}
+
+bool
+LV2Plugin::parameter_is_midi (uint32_t param) const
+{
+ SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
+ return slv2_port_is_a(_plugin, port, _world.event_class)
+ && slv2_port_supports_event(_plugin, port, _world.midi_class);
+}
+
+bool
+LV2Plugin::parameter_is_output (uint32_t param) const
+{
+ 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
+{
+ SLV2Port port = slv2_plugin_get_port_by_index(_plugin, param);
+ return slv2_port_is_a(_plugin, port, _world.input_class);
+}
+
+void
+LV2Plugin::print_parameter (uint32_t param, char *buf, uint32_t len) const
+{
+ if (buf && len) {
+ if (param < parameter_count()) {
+ snprintf (buf, len, "%.3f", get_parameter (param));
+ } else {
+ strcat (buf, "0");
+ }
+ }
+}
+
+void
+LV2Plugin::run (nframes_t nframes)
+{
+ for (uint32_t i = 0; i < parameter_count(); ++i) {
+ if (parameter_is_control(i) && parameter_is_input(i)) {
+ _control_data[i] = _shadow_data[i];
+ }
+ }
+
+ slv2_instance_run(_instance, nframes);
+}
+
+void
+LV2Plugin::latency_compute_run ()
+{
+ if (!_latency_control_port) {
+ return;
+ }
+
+ /* we need to run the plugin so that it can set its latency
+ parameter.
+ */
+
+ activate ();
+
+ uint32_t port_index = 0;
+ uint32_t in_index = 0;
+ uint32_t out_index = 0;
+ const nframes_t bufsize = 1024;
+ float buffer[bufsize];
+
+ memset(buffer,0,sizeof(float)*bufsize);
+
+ /* Note that we've already required that plugins
+ be able to handle in-place processing.
+ */
+
+ port_index = 0;
+
+ while (port_index < parameter_count()) {
+ if (parameter_is_audio (port_index)) {
+ if (parameter_is_input (port_index)) {
+ slv2_instance_connect_port (_instance, port_index, buffer);
+ in_index++;
+ } else if (parameter_is_output (port_index)) {
+ slv2_instance_connect_port (_instance, port_index, buffer);
+ out_index++;
+ }
+ }
+ port_index++;
+ }
+
+ run (bufsize);
+ deactivate ();
+}
+
+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);
+ midi_class = slv2_value_new_uri(world, SLV2_EVENT_CLASS_MIDI);
+ 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(midi_class);
+ slv2_value_free(in_place_broken);
+}
+
+LV2PluginInfo::LV2PluginInfo (void* lv2_world, void* slv2_plugin)
+ : _lv2_world(lv2_world)
+ , _slv2_plugin(slv2_plugin)
+{
+}
+
+LV2PluginInfo::~LV2PluginInfo()
+{
+}
+
+PluginPtr
+LV2PluginInfo::load (Session& session)
+{
+ try {
+ PluginPtr plugin;
+
+ 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;
+ }
+
+ catch (failed_constructor &err) {
+ return PluginPtr ((Plugin*) 0);
+ }
+
+ return PluginPtr();
+}
+
+PluginInfoList
+LV2PluginInfo::discover (void* lv2_world)
+{
+ PluginInfoList plugs;
+
+ 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(lv2_world, 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);
+ SLV2Value label = slv2_plugin_class_get_label(pclass);
+ info->category = slv2_value_as_string(label);
+
+ 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
+
+ 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_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_value_as_uri(slv2_plugin_get_uri(p));
+ info->index = 0; // Meaningless for LV2
+
+ plugs.push_back (info);
+ }
+
+ return plugs;
+}
+
diff --git a/libs/ardour/macosx/English.lproj/InfoPlist.strings b/libs/ardour/macosx/English.lproj/InfoPlist.strings
new file mode 100644
index 0000000000..0c0cacad8b
--- /dev/null
+++ b/libs/ardour/macosx/English.lproj/InfoPlist.strings
Binary files differ
diff --git a/libs/ardour/macosx/Info.plist b/libs/ardour/macosx/Info.plist
new file mode 100644
index 0000000000..931491039f
--- /dev/null
+++ b/libs/ardour/macosx/Info.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>ardour</string>
+ <key>CFBundleIconFile</key>
+ <string></string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.carbonframeworktemplate</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.01</string>
+ <key>CSResourcesFileMapped</key>
+ <true/>
+</dict>
+</plist>
diff --git a/libs/ardour/macosx/ardour.xcodeproj/project.pbxproj b/libs/ardour/macosx/ardour.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..4026b65f59
--- /dev/null
+++ b/libs/ardour/macosx/ardour.xcodeproj/project.pbxproj
@@ -0,0 +1,1214 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 42;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 696149E90B97CEF500ECBDF0 /* glib in Frameworks */ = {isa = PBXBuildFile; fileRef = 696149E50B97CEF500ECBDF0 /* glib */; };
+ 696149EA0B97CEF500ECBDF0 /* gmodule in Frameworks */ = {isa = PBXBuildFile; fileRef = 696149E60B97CEF500ECBDF0 /* gmodule */; };
+ 696149EB0B97CEF500ECBDF0 /* gobject in Frameworks */ = {isa = PBXBuildFile; fileRef = 696149E70B97CEF500ECBDF0 /* gobject */; };
+ 696149EC0B97CEF500ECBDF0 /* gthread in Frameworks */ = {isa = PBXBuildFile; fileRef = 696149E80B97CEF500ECBDF0 /* gthread */; };
+ 6964FECA0B8E7A7900799BAE /* version.h in Headers */ = {isa = PBXBuildFile; fileRef = 6964FEC80B8E7A7900799BAE /* version.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 6964FECB0B8E7A7900799BAE /* version.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6964FEC90B8E7A7900799BAE /* version.cc */; };
+ 696C90530B8D526000D66CAF /* ardour.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FAF0B8D526000D66CAF /* ardour.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90540B8D526000D66CAF /* audio_diskstream.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FB00B8D526000D66CAF /* audio_diskstream.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90550B8D526000D66CAF /* audio_library.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FB10B8D526000D66CAF /* audio_library.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90560B8D526000D66CAF /* audio_track.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FB20B8D526000D66CAF /* audio_track.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90580B8D526000D66CAF /* audioengine.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FB40B8D526000D66CAF /* audioengine.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90590B8D526000D66CAF /* audiofilesource.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FB50B8D526000D66CAF /* audiofilesource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C905A0B8D526000D66CAF /* audiofilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FB60B8D526000D66CAF /* audiofilter.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C905B0B8D526000D66CAF /* audioplaylist.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FB70B8D526000D66CAF /* audioplaylist.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C905C0B8D526000D66CAF /* audioregion.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FB80B8D526000D66CAF /* audioregion.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C905D0B8D526000D66CAF /* audiosource.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FB90B8D526000D66CAF /* audiosource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C905E0B8D526000D66CAF /* auditioner.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FBA0B8D526000D66CAF /* auditioner.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C905F0B8D526000D66CAF /* automation_event.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FBB0B8D526000D66CAF /* automation_event.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90600B8D526000D66CAF /* buffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FBC0B8D526000D66CAF /* buffer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90610B8D526000D66CAF /* click.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FBD0B8D526000D66CAF /* click.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90620B8D526000D66CAF /* configuration.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FBE0B8D526000D66CAF /* configuration.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90630B8D526000D66CAF /* configuration_variable.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FBF0B8D526000D66CAF /* configuration_variable.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90640B8D526000D66CAF /* configuration_vars.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FC00B8D526000D66CAF /* configuration_vars.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90650B8D526000D66CAF /* connection.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FC10B8D526000D66CAF /* connection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90660B8D526000D66CAF /* control_protocol_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FC20B8D526000D66CAF /* control_protocol_manager.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90680B8D526000D66CAF /* crossfade.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FC40B8D526000D66CAF /* crossfade.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90690B8D526000D66CAF /* crossfade_compare.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FC50B8D526000D66CAF /* crossfade_compare.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C906A0B8D526000D66CAF /* curve.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FC60B8D526000D66CAF /* curve.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C906B0B8D526000D66CAF /* cycle_timer.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FC70B8D526000D66CAF /* cycle_timer.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C906C0B8D526000D66CAF /* cycles.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FC80B8D526000D66CAF /* cycles.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C906D0B8D526000D66CAF /* data_type.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FC90B8D526000D66CAF /* data_type.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C906E0B8D526000D66CAF /* dB.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FCA0B8D526000D66CAF /* dB.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90700B8D526000D66CAF /* diskstream.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FCC0B8D526000D66CAF /* diskstream.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90710B8D526000D66CAF /* export.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FCD0B8D526000D66CAF /* export.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90720B8D526000D66CAF /* gain.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FCE0B8D526000D66CAF /* gain.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90730B8D526000D66CAF /* gdither.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FCF0B8D526000D66CAF /* gdither.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90740B8D526000D66CAF /* gdither_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD00B8D526000D66CAF /* gdither_types.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90750B8D526000D66CAF /* gdither_types_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD10B8D526000D66CAF /* gdither_types_internal.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90760B8D526000D66CAF /* insert.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD20B8D526000D66CAF /* insert.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90770B8D526000D66CAF /* io.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD30B8D526000D66CAF /* io.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90780B8D526000D66CAF /* ladspa.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD40B8D526000D66CAF /* ladspa.h */; settings = {ATTRIBUTES = (); }; };
+ 696C90790B8D526000D66CAF /* ladspa_plugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD50B8D526000D66CAF /* ladspa_plugin.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C907A0B8D526000D66CAF /* location.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD60B8D526000D66CAF /* location.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C907B0B8D526000D66CAF /* logcurve.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD70B8D526000D66CAF /* logcurve.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C907C0B8D526000D66CAF /* mix.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD80B8D526000D66CAF /* mix.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C907D0B8D526000D66CAF /* named_selection.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FD90B8D526000D66CAF /* named_selection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C907E0B8D526000D66CAF /* noise.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FDA0B8D526000D66CAF /* noise.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C907F0B8D526000D66CAF /* osc.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FDB0B8D526000D66CAF /* osc.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90800B8D526000D66CAF /* panner.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FDC0B8D526000D66CAF /* panner.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90810B8D526000D66CAF /* pcm_utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FDD0B8D526000D66CAF /* pcm_utils.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90820B8D526000D66CAF /* peak.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FDE0B8D526000D66CAF /* peak.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90830B8D526000D66CAF /* playlist.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FDF0B8D526000D66CAF /* playlist.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90840B8D526000D66CAF /* playlist_factory.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE00B8D526000D66CAF /* playlist_factory.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90850B8D526000D66CAF /* playlist_templates.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE10B8D526000D66CAF /* playlist_templates.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90860B8D526000D66CAF /* plugin.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE20B8D526000D66CAF /* plugin.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90870B8D526000D66CAF /* plugin_manager.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE30B8D526000D66CAF /* plugin_manager.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90880B8D526000D66CAF /* port.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE40B8D526000D66CAF /* port.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90890B8D526000D66CAF /* recent_sessions.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE50B8D526000D66CAF /* recent_sessions.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C908A0B8D526000D66CAF /* redirect.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE60B8D526000D66CAF /* redirect.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C908B0B8D526000D66CAF /* region.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE70B8D526000D66CAF /* region.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C908C0B8D526000D66CAF /* region_factory.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE80B8D526000D66CAF /* region_factory.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C908D0B8D526000D66CAF /* reverse.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FE90B8D526000D66CAF /* reverse.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C908E0B8D526000D66CAF /* route.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FEA0B8D526000D66CAF /* route.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C908F0B8D526000D66CAF /* route_group.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FEB0B8D526000D66CAF /* route_group.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90900B8D526000D66CAF /* route_group_specialized.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FEC0B8D526000D66CAF /* route_group_specialized.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90910B8D526000D66CAF /* send.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FED0B8D526000D66CAF /* send.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90920B8D526000D66CAF /* session.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FEE0B8D526000D66CAF /* session.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90930B8D526000D66CAF /* session_connection.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FEF0B8D526000D66CAF /* session_connection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90940B8D526000D66CAF /* session_playlist.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF00B8D526000D66CAF /* session_playlist.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90950B8D526000D66CAF /* session_region.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF10B8D526000D66CAF /* session_region.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90960B8D526000D66CAF /* session_route.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF20B8D526000D66CAF /* session_route.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90970B8D526000D66CAF /* session_selection.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF30B8D526000D66CAF /* session_selection.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90980B8D526000D66CAF /* silentfilesource.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF40B8D526000D66CAF /* silentfilesource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90990B8D526000D66CAF /* slave.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF50B8D526000D66CAF /* slave.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C909A0B8D526000D66CAF /* sndfile_helpers.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF60B8D526000D66CAF /* sndfile_helpers.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C909B0B8D526000D66CAF /* sndfilesource.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF70B8D526000D66CAF /* sndfilesource.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C909C0B8D526000D66CAF /* soundseq.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF80B8D526000D66CAF /* soundseq.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C909D0B8D526000D66CAF /* source.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FF90B8D526000D66CAF /* source.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C909E0B8D526000D66CAF /* source_factory.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FFA0B8D526000D66CAF /* source_factory.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C909F0B8D526000D66CAF /* spline.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FFB0B8D526000D66CAF /* spline.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90A00B8D526000D66CAF /* tempo.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FFC0B8D526000D66CAF /* tempo.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90A10B8D526000D66CAF /* timestamps.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FFD0B8D526000D66CAF /* timestamps.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90A20B8D526000D66CAF /* track.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FFE0B8D526000D66CAF /* track.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90A30B8D526000D66CAF /* types.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C8FFF0B8D526000D66CAF /* types.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90A40B8D526000D66CAF /* utils.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C90000B8D526000D66CAF /* utils.h */; settings = {ATTRIBUTES = (Public, ); }; };
+ 696C90A60B8D526000D66CAF /* audio_diskstream.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90020B8D526000D66CAF /* audio_diskstream.cc */; };
+ 696C90A70B8D526000D66CAF /* audio_library.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90030B8D526000D66CAF /* audio_library.cc */; };
+ 696C90A80B8D526000D66CAF /* audio_playlist.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90040B8D526000D66CAF /* audio_playlist.cc */; };
+ 696C90A90B8D526000D66CAF /* audio_track.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90050B8D526000D66CAF /* audio_track.cc */; };
+ 696C90AB0B8D526000D66CAF /* audioengine.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90070B8D526000D66CAF /* audioengine.cc */; };
+ 696C90AC0B8D526000D66CAF /* audiofilesource.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90080B8D526000D66CAF /* audiofilesource.cc */; };
+ 696C90AD0B8D526000D66CAF /* audiofilter.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90090B8D526000D66CAF /* audiofilter.cc */; };
+ 696C90AE0B8D526000D66CAF /* audioregion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C900A0B8D526000D66CAF /* audioregion.cc */; };
+ 696C90AF0B8D526000D66CAF /* audiosource.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C900B0B8D526000D66CAF /* audiosource.cc */; };
+ 696C90B00B8D526000D66CAF /* auditioner.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C900C0B8D526000D66CAF /* auditioner.cc */; };
+ 696C90B10B8D526000D66CAF /* automation.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C900D0B8D526000D66CAF /* automation.cc */; };
+ 696C90B20B8D526000D66CAF /* automation_event.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C900E0B8D526000D66CAF /* automation_event.cc */; };
+ 696C90B30B8D526000D66CAF /* configuration.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C900F0B8D526000D66CAF /* configuration.cc */; };
+ 696C90B40B8D526000D66CAF /* connection.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90100B8D526000D66CAF /* connection.cc */; };
+ 696C90B50B8D526000D66CAF /* control_protocol_manager.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90110B8D526000D66CAF /* control_protocol_manager.cc */; };
+ 696C90B70B8D526000D66CAF /* crossfade.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90130B8D526000D66CAF /* crossfade.cc */; };
+ 696C90B80B8D526000D66CAF /* curve.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90140B8D526000D66CAF /* curve.cc */; };
+ 696C90B90B8D526000D66CAF /* cycle_timer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90150B8D526000D66CAF /* cycle_timer.cc */; };
+ 696C90BA0B8D526000D66CAF /* default_click.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90160B8D526000D66CAF /* default_click.cc */; };
+ 696C90BC0B8D526000D66CAF /* diskstream.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90180B8D526000D66CAF /* diskstream.cc */; };
+ 696C90BD0B8D526000D66CAF /* enums.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90190B8D526000D66CAF /* enums.cc */; };
+ 696C90BE0B8D526000D66CAF /* gain.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C901A0B8D526000D66CAF /* gain.cc */; };
+ 696C90BF0B8D526000D66CAF /* gdither.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C901B0B8D526000D66CAF /* gdither.cc */; };
+ 696C90C00B8D526000D66CAF /* gettext.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C901C0B8D526000D66CAF /* gettext.h */; settings = {ATTRIBUTES = (); }; };
+ 696C90C10B8D526000D66CAF /* globals.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C901D0B8D526000D66CAF /* globals.cc */; };
+ 696C90C20B8D526000D66CAF /* i18n.h in Headers */ = {isa = PBXBuildFile; fileRef = 696C901E0B8D526000D66CAF /* i18n.h */; settings = {ATTRIBUTES = (); }; };
+ 696C90C30B8D526000D66CAF /* import.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C901F0B8D526000D66CAF /* import.cc */; };
+ 696C90C40B8D526000D66CAF /* insert.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90200B8D526000D66CAF /* insert.cc */; };
+ 696C90C50B8D526000D66CAF /* io.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90210B8D526000D66CAF /* io.cc */; };
+ 696C90C60B8D526000D66CAF /* jack_slave.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90220B8D526000D66CAF /* jack_slave.cc */; };
+ 696C90C70B8D526000D66CAF /* ladspa_plugin.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90230B8D526000D66CAF /* ladspa_plugin.cc */; };
+ 696C90C80B8D526000D66CAF /* location.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90240B8D526000D66CAF /* location.cc */; };
+ 696C90C90B8D526000D66CAF /* mix.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90250B8D526000D66CAF /* mix.cc */; };
+ 696C90CA0B8D526000D66CAF /* mtc_slave.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90260B8D526000D66CAF /* mtc_slave.cc */; };
+ 696C90CB0B8D526000D66CAF /* named_selection.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90270B8D526000D66CAF /* named_selection.cc */; };
+ 696C90CC0B8D526000D66CAF /* osc.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90280B8D526000D66CAF /* osc.cc */; };
+ 696C90CD0B8D526000D66CAF /* panner.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90290B8D526000D66CAF /* panner.cc */; };
+ 696C90CE0B8D526000D66CAF /* pcm_utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C902A0B8D526000D66CAF /* pcm_utils.cc */; };
+ 696C90CF0B8D526000D66CAF /* playlist.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C902B0B8D526000D66CAF /* playlist.cc */; };
+ 696C90D00B8D526000D66CAF /* playlist_factory.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C902C0B8D526000D66CAF /* playlist_factory.cc */; };
+ 696C90D10B8D526000D66CAF /* plugin.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C902D0B8D526000D66CAF /* plugin.cc */; };
+ 696C90D20B8D526000D66CAF /* plugin_manager.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C902E0B8D526000D66CAF /* plugin_manager.cc */; };
+ 696C90D30B8D526000D66CAF /* port.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C902F0B8D526000D66CAF /* port.cc */; };
+ 696C90D40B8D526000D66CAF /* recent_sessions.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90300B8D526000D66CAF /* recent_sessions.cc */; };
+ 696C90D50B8D526000D66CAF /* redirect.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90310B8D526000D66CAF /* redirect.cc */; };
+ 696C90D60B8D526000D66CAF /* region.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90320B8D526000D66CAF /* region.cc */; };
+ 696C90D70B8D526000D66CAF /* region_factory.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90330B8D526000D66CAF /* region_factory.cc */; };
+ 696C90D80B8D526000D66CAF /* reverse.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90340B8D526000D66CAF /* reverse.cc */; };
+ 696C90D90B8D526000D66CAF /* route.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90350B8D526000D66CAF /* route.cc */; };
+ 696C90DA0B8D526000D66CAF /* route_group.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90360B8D526000D66CAF /* route_group.cc */; };
+ 696C90DB0B8D526000D66CAF /* send.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90370B8D526000D66CAF /* send.cc */; };
+ 696C90DC0B8D526000D66CAF /* session.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90380B8D526000D66CAF /* session.cc */; };
+ 696C90DD0B8D526000D66CAF /* session_butler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90390B8D526000D66CAF /* session_butler.cc */; };
+ 696C90DE0B8D526000D66CAF /* session_click.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C903A0B8D526000D66CAF /* session_click.cc */; };
+ 696C90DF0B8D526000D66CAF /* session_command.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C903B0B8D526000D66CAF /* session_command.cc */; };
+ 696C90E10B8D526000D66CAF /* session_events.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C903D0B8D526000D66CAF /* session_events.cc */; };
+ 696C90E20B8D526000D66CAF /* session_export.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C903E0B8D526000D66CAF /* session_export.cc */; };
+ 696C90E30B8D526000D66CAF /* session_feedback.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C903F0B8D526000D66CAF /* session_feedback.cc */; };
+ 696C90E40B8D526000D66CAF /* session_midi.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90400B8D526000D66CAF /* session_midi.cc */; };
+ 696C90E50B8D526000D66CAF /* session_process.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90410B8D526000D66CAF /* session_process.cc */; };
+ 696C90E60B8D526000D66CAF /* session_state.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90420B8D526000D66CAF /* session_state.cc */; };
+ 696C90E70B8D526000D66CAF /* session_time.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90430B8D526000D66CAF /* session_time.cc */; };
+ 696C90E80B8D526000D66CAF /* session_timefx.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90440B8D526000D66CAF /* session_timefx.cc */; };
+ 696C90E90B8D526000D66CAF /* session_transport.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90450B8D526000D66CAF /* session_transport.cc */; };
+ 696C90EB0B8D526000D66CAF /* silentfilesource.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90470B8D526000D66CAF /* silentfilesource.cc */; };
+ 696C90EC0B8D526000D66CAF /* sndfile_helpers.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90480B8D526000D66CAF /* sndfile_helpers.cc */; };
+ 696C90ED0B8D526000D66CAF /* sndfilesource.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90490B8D526000D66CAF /* sndfilesource.cc */; };
+ 696C90EE0B8D526000D66CAF /* source.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C904A0B8D526000D66CAF /* source.cc */; };
+ 696C90EF0B8D526000D66CAF /* source_factory.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C904B0B8D526000D66CAF /* source_factory.cc */; };
+ 696C90F20B8D526000D66CAF /* tempo.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C904E0B8D526000D66CAF /* tempo.cc */; };
+ 696C90F30B8D526000D66CAF /* track.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C904F0B8D526000D66CAF /* track.cc */; };
+ 696C90F40B8D526000D66CAF /* utils.cc in Sources */ = {isa = PBXBuildFile; fileRef = 696C90500B8D526000D66CAF /* utils.cc */; };
+ 696C91000B8D52F300D66CAF /* glibmm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696C90F70B8D52F300D66CAF /* glibmm.framework */; };
+ 696C91010B8D52F300D66CAF /* Jack.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696C90F80B8D52F300D66CAF /* Jack.framework */; };
+ 696C91020B8D52F300D66CAF /* lo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696C90F90B8D52F300D66CAF /* lo.framework */; };
+ 696C91030B8D52F300D66CAF /* LRdf.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696C90FA0B8D52F300D66CAF /* LRdf.framework */; };
+ 696C91040B8D52F300D66CAF /* Raptor.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696C90FB0B8D52F300D66CAF /* Raptor.framework */; };
+ 696C91050B8D52F300D66CAF /* SampleRate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696C90FC0B8D52F300D66CAF /* SampleRate.framework */; };
+ 696C91060B8D52F300D66CAF /* sigc.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696C90FD0B8D52F300D66CAF /* sigc.framework */; };
+ 696C91070B8D52F300D66CAF /* Sndfile-ardour.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696C90FE0B8D52F300D66CAF /* Sndfile-ardour.framework */; };
+ 696C91080B8D52F300D66CAF /* soundtouch.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 696C90FF0B8D52F300D66CAF /* soundtouch.framework */; };
+ 697D98090B8DCD8C0006A892 /* libxml2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 697D98080B8DCD8C0006A892 /* libxml2.dylib */; };
+ 698D9AB60B969A6F00C53B63 /* midi++.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 698D9AB50B969A5B00C53B63 /* midi++.framework */; };
+ 69AD45850B97D10600806E7E /* FLAC in Frameworks */ = {isa = PBXBuildFile; fileRef = 69AD45840B97D10600806E7E /* FLAC */; };
+ 69B1B5210B8E7DFD007E41C1 /* pbd.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 691B3B7C0B8D5508009155B5 /* pbd.framework */; };
+ 69B1B5500B8E80AD007E41C1 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69B1B54F0B8E80AD007E41C1 /* CoreAudio.framework */; };
+ 69DBC41B0BA794A500C19E65 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69DBC41A0BA794A500C19E65 /* Accelerate.framework */; };
+ 69DBC42B0BA7992800C19E65 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69DBC42A0BA7992800C19E65 /* Carbon.framework */; };
+ 69F7CE5A0B8DCB3300D76871 /* basic_ui.cc in Sources */ = {isa = PBXBuildFile; fileRef = 69F7CE520B8DCB3300D76871 /* basic_ui.cc */; };
+ 69F7CE5B0B8DCB3300D76871 /* basic_ui.h in Headers */ = {isa = PBXBuildFile; fileRef = 69F7CE540B8DCB3300D76871 /* basic_ui.h */; };
+ 69F7CE5C0B8DCB3300D76871 /* control_protocol.h in Headers */ = {isa = PBXBuildFile; fileRef = 69F7CE550B8DCB3300D76871 /* control_protocol.h */; };
+ 69F7CE5D0B8DCB3300D76871 /* smpte.h in Headers */ = {isa = PBXBuildFile; fileRef = 69F7CE560B8DCB3300D76871 /* smpte.h */; };
+ 69F7CE5E0B8DCB3300D76871 /* control_protocol.cc in Sources */ = {isa = PBXBuildFile; fileRef = 69F7CE570B8DCB3300D76871 /* control_protocol.cc */; };
+ 69F7CE600B8DCB3300D76871 /* smpte.cc in Sources */ = {isa = PBXBuildFile; fileRef = 69F7CE590B8DCB3300D76871 /* smpte.cc */; };
+ 8D07F2BE0486CC7A007CD1D0 /* ardour_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 32BAE0B70371A74B00C91783 /* ardour_Prefix.pch */; settings = {ATTRIBUTES = (); }; };
+ 8D07F2C00486CC7A007CD1D0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 691B3B7B0B8D5508009155B5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 691B3B770B8D5508009155B5 /* pbd.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 8D07F2C80486CC7A007CD1D0;
+ remoteInfo = pbd;
+ };
+ 698D9AB40B969A5B00C53B63 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 698D9AB00B969A5B00C53B63 /* midi++.xcodeproj */;
+ proxyType = 2;
+ remoteGlobalIDString = 8D07F2C80486CC7A007CD1D0;
+ remoteInfo = "midi++";
+ };
+ 698D9AB90B969ADC00C53B63 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 698D9AB00B969A5B00C53B63 /* midi++.xcodeproj */;
+ proxyType = 1;
+ remoteGlobalIDString = 8D07F2BC0486CC7A007CD1D0;
+ remoteInfo = "midi++";
+ };
+ 69D5F6250B8D58A500301E71 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 691B3B770B8D5508009155B5 /* pbd.xcodeproj */;
+ proxyType = 1;
+ remoteGlobalIDString = 8D07F2BC0486CC7A007CD1D0;
+ remoteInfo = pbd;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 089C1667FE841158C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+ 32BAE0B70371A74B00C91783 /* ardour_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ardour_Prefix.pch; sourceTree = "<group>"; };
+ 691B3B770B8D5508009155B5 /* pbd.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = pbd.xcodeproj; path = ../../pbd/macosx/pbd.xcodeproj; sourceTree = SOURCE_ROOT; };
+ 696149E50B97CEF500ECBDF0 /* glib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = glib; path = /Library/Frameworks/GLib.framework/Versions/2.12.3/Libraries/glib; sourceTree = "<absolute>"; };
+ 696149E60B97CEF500ECBDF0 /* gmodule */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = gmodule; path = /Library/Frameworks/GLib.framework/Versions/2.12.3/Libraries/gmodule; sourceTree = "<absolute>"; };
+ 696149E70B97CEF500ECBDF0 /* gobject */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = gobject; path = /Library/Frameworks/GLib.framework/Versions/2.12.3/Libraries/gobject; sourceTree = "<absolute>"; };
+ 696149E80B97CEF500ECBDF0 /* gthread */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = gthread; path = /Library/Frameworks/GLib.framework/Versions/2.12.3/Libraries/gthread; sourceTree = "<absolute>"; };
+ 6964FEC80B8E7A7900799BAE /* version.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = version.h; sourceTree = "<group>"; };
+ 6964FEC90B8E7A7900799BAE /* version.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = version.cc; sourceTree = "<group>"; };
+ 696C8FAF0B8D526000D66CAF /* ardour.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ardour.h; sourceTree = "<group>"; };
+ 696C8FB00B8D526000D66CAF /* audio_diskstream.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = audio_diskstream.h; sourceTree = "<group>"; };
+ 696C8FB10B8D526000D66CAF /* audio_library.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = audio_library.h; sourceTree = "<group>"; };
+ 696C8FB20B8D526000D66CAF /* audio_track.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = audio_track.h; sourceTree = "<group>"; };
+ 696C8FB40B8D526000D66CAF /* audioengine.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = audioengine.h; sourceTree = "<group>"; };
+ 696C8FB50B8D526000D66CAF /* audiofilesource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = audiofilesource.h; sourceTree = "<group>"; };
+ 696C8FB60B8D526000D66CAF /* audiofilter.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = audiofilter.h; sourceTree = "<group>"; };
+ 696C8FB70B8D526000D66CAF /* audioplaylist.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = audioplaylist.h; sourceTree = "<group>"; };
+ 696C8FB80B8D526000D66CAF /* audioregion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = audioregion.h; sourceTree = "<group>"; };
+ 696C8FB90B8D526000D66CAF /* audiosource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = audiosource.h; sourceTree = "<group>"; };
+ 696C8FBA0B8D526000D66CAF /* auditioner.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = auditioner.h; sourceTree = "<group>"; };
+ 696C8FBB0B8D526000D66CAF /* automation_event.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = automation_event.h; sourceTree = "<group>"; };
+ 696C8FBC0B8D526000D66CAF /* buffer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = buffer.h; sourceTree = "<group>"; };
+ 696C8FBD0B8D526000D66CAF /* click.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = click.h; sourceTree = "<group>"; };
+ 696C8FBE0B8D526000D66CAF /* configuration.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = configuration.h; sourceTree = "<group>"; };
+ 696C8FBF0B8D526000D66CAF /* configuration_variable.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = configuration_variable.h; sourceTree = "<group>"; };
+ 696C8FC00B8D526000D66CAF /* configuration_vars.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = configuration_vars.h; sourceTree = "<group>"; };
+ 696C8FC10B8D526000D66CAF /* connection.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = connection.h; sourceTree = "<group>"; };
+ 696C8FC20B8D526000D66CAF /* control_protocol_manager.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = control_protocol_manager.h; sourceTree = "<group>"; };
+ 696C8FC40B8D526000D66CAF /* crossfade.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = crossfade.h; sourceTree = "<group>"; };
+ 696C8FC50B8D526000D66CAF /* crossfade_compare.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = crossfade_compare.h; sourceTree = "<group>"; };
+ 696C8FC60B8D526000D66CAF /* curve.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = curve.h; sourceTree = "<group>"; };
+ 696C8FC70B8D526000D66CAF /* cycle_timer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cycle_timer.h; sourceTree = "<group>"; };
+ 696C8FC80B8D526000D66CAF /* cycles.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = cycles.h; sourceTree = "<group>"; };
+ 696C8FC90B8D526000D66CAF /* data_type.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = data_type.h; sourceTree = "<group>"; };
+ 696C8FCA0B8D526000D66CAF /* dB.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dB.h; sourceTree = "<group>"; };
+ 696C8FCC0B8D526000D66CAF /* diskstream.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = diskstream.h; sourceTree = "<group>"; };
+ 696C8FCD0B8D526000D66CAF /* export.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = export.h; sourceTree = "<group>"; };
+ 696C8FCE0B8D526000D66CAF /* gain.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gain.h; sourceTree = "<group>"; };
+ 696C8FCF0B8D526000D66CAF /* gdither.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gdither.h; sourceTree = "<group>"; };
+ 696C8FD00B8D526000D66CAF /* gdither_types.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gdither_types.h; sourceTree = "<group>"; };
+ 696C8FD10B8D526000D66CAF /* gdither_types_internal.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = gdither_types_internal.h; sourceTree = "<group>"; };
+ 696C8FD20B8D526000D66CAF /* insert.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = insert.h; sourceTree = "<group>"; };
+ 696C8FD30B8D526000D66CAF /* io.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = io.h; sourceTree = "<group>"; };
+ 696C8FD40B8D526000D66CAF /* ladspa.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ladspa.h; sourceTree = "<group>"; };
+ 696C8FD50B8D526000D66CAF /* ladspa_plugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = ladspa_plugin.h; sourceTree = "<group>"; };
+ 696C8FD60B8D526000D66CAF /* location.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = location.h; sourceTree = "<group>"; };
+ 696C8FD70B8D526000D66CAF /* logcurve.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = logcurve.h; sourceTree = "<group>"; };
+ 696C8FD80B8D526000D66CAF /* mix.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = mix.h; sourceTree = "<group>"; };
+ 696C8FD90B8D526000D66CAF /* named_selection.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = named_selection.h; sourceTree = "<group>"; };
+ 696C8FDA0B8D526000D66CAF /* noise.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = noise.h; sourceTree = "<group>"; };
+ 696C8FDB0B8D526000D66CAF /* osc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = osc.h; sourceTree = "<group>"; };
+ 696C8FDC0B8D526000D66CAF /* panner.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = panner.h; sourceTree = "<group>"; };
+ 696C8FDD0B8D526000D66CAF /* pcm_utils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = pcm_utils.h; sourceTree = "<group>"; };
+ 696C8FDE0B8D526000D66CAF /* peak.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = peak.h; sourceTree = "<group>"; };
+ 696C8FDF0B8D526000D66CAF /* playlist.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = playlist.h; sourceTree = "<group>"; };
+ 696C8FE00B8D526000D66CAF /* playlist_factory.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = playlist_factory.h; sourceTree = "<group>"; };
+ 696C8FE10B8D526000D66CAF /* playlist_templates.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = playlist_templates.h; sourceTree = "<group>"; };
+ 696C8FE20B8D526000D66CAF /* plugin.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = plugin.h; sourceTree = "<group>"; };
+ 696C8FE30B8D526000D66CAF /* plugin_manager.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = plugin_manager.h; sourceTree = "<group>"; };
+ 696C8FE40B8D526000D66CAF /* port.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = port.h; sourceTree = "<group>"; };
+ 696C8FE50B8D526000D66CAF /* recent_sessions.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = recent_sessions.h; sourceTree = "<group>"; };
+ 696C8FE60B8D526000D66CAF /* redirect.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = redirect.h; sourceTree = "<group>"; };
+ 696C8FE70B8D526000D66CAF /* region.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = region.h; sourceTree = "<group>"; };
+ 696C8FE80B8D526000D66CAF /* region_factory.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = region_factory.h; sourceTree = "<group>"; };
+ 696C8FE90B8D526000D66CAF /* reverse.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = reverse.h; sourceTree = "<group>"; };
+ 696C8FEA0B8D526000D66CAF /* route.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = route.h; sourceTree = "<group>"; };
+ 696C8FEB0B8D526000D66CAF /* route_group.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = route_group.h; sourceTree = "<group>"; };
+ 696C8FEC0B8D526000D66CAF /* route_group_specialized.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = route_group_specialized.h; sourceTree = "<group>"; };
+ 696C8FED0B8D526000D66CAF /* send.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = send.h; sourceTree = "<group>"; };
+ 696C8FEE0B8D526000D66CAF /* session.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = session.h; sourceTree = "<group>"; };
+ 696C8FEF0B8D526000D66CAF /* session_connection.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = session_connection.h; sourceTree = "<group>"; };
+ 696C8FF00B8D526000D66CAF /* session_playlist.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = session_playlist.h; sourceTree = "<group>"; };
+ 696C8FF10B8D526000D66CAF /* session_region.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = session_region.h; sourceTree = "<group>"; };
+ 696C8FF20B8D526000D66CAF /* session_route.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = session_route.h; sourceTree = "<group>"; };
+ 696C8FF30B8D526000D66CAF /* session_selection.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = session_selection.h; sourceTree = "<group>"; };
+ 696C8FF40B8D526000D66CAF /* silentfilesource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = silentfilesource.h; sourceTree = "<group>"; };
+ 696C8FF50B8D526000D66CAF /* slave.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = slave.h; sourceTree = "<group>"; };
+ 696C8FF60B8D526000D66CAF /* sndfile_helpers.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = sndfile_helpers.h; sourceTree = "<group>"; };
+ 696C8FF70B8D526000D66CAF /* sndfilesource.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = sndfilesource.h; sourceTree = "<group>"; };
+ 696C8FF80B8D526000D66CAF /* soundseq.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = soundseq.h; sourceTree = "<group>"; };
+ 696C8FF90B8D526000D66CAF /* source.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = source.h; sourceTree = "<group>"; };
+ 696C8FFA0B8D526000D66CAF /* source_factory.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = source_factory.h; sourceTree = "<group>"; };
+ 696C8FFB0B8D526000D66CAF /* spline.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = spline.h; sourceTree = "<group>"; };
+ 696C8FFC0B8D526000D66CAF /* tempo.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = tempo.h; sourceTree = "<group>"; };
+ 696C8FFD0B8D526000D66CAF /* timestamps.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = timestamps.h; sourceTree = "<group>"; };
+ 696C8FFE0B8D526000D66CAF /* track.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = track.h; sourceTree = "<group>"; };
+ 696C8FFF0B8D526000D66CAF /* types.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = types.h; sourceTree = "<group>"; };
+ 696C90000B8D526000D66CAF /* utils.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = utils.h; sourceTree = "<group>"; };
+ 696C90020B8D526000D66CAF /* audio_diskstream.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = audio_diskstream.cc; path = ../audio_diskstream.cc; sourceTree = SOURCE_ROOT; };
+ 696C90030B8D526000D66CAF /* audio_library.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = audio_library.cc; path = ../audio_library.cc; sourceTree = SOURCE_ROOT; };
+ 696C90040B8D526000D66CAF /* audio_playlist.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = audio_playlist.cc; path = ../audio_playlist.cc; sourceTree = SOURCE_ROOT; };
+ 696C90050B8D526000D66CAF /* audio_track.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = audio_track.cc; path = ../audio_track.cc; sourceTree = SOURCE_ROOT; };
+ 696C90070B8D526000D66CAF /* audioengine.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = audioengine.cc; path = ../audioengine.cc; sourceTree = SOURCE_ROOT; };
+ 696C90080B8D526000D66CAF /* audiofilesource.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = audiofilesource.cc; path = ../audiofilesource.cc; sourceTree = SOURCE_ROOT; };
+ 696C90090B8D526000D66CAF /* audiofilter.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = audiofilter.cc; path = ../audiofilter.cc; sourceTree = SOURCE_ROOT; };
+ 696C900A0B8D526000D66CAF /* audioregion.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = audioregion.cc; path = ../audioregion.cc; sourceTree = SOURCE_ROOT; };
+ 696C900B0B8D526000D66CAF /* audiosource.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = audiosource.cc; path = ../audiosource.cc; sourceTree = SOURCE_ROOT; };
+ 696C900C0B8D526000D66CAF /* auditioner.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = auditioner.cc; path = ../auditioner.cc; sourceTree = SOURCE_ROOT; };
+ 696C900D0B8D526000D66CAF /* automation.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = automation.cc; path = ../automation.cc; sourceTree = SOURCE_ROOT; };
+ 696C900E0B8D526000D66CAF /* automation_event.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = automation_event.cc; path = ../automation_event.cc; sourceTree = SOURCE_ROOT; };
+ 696C900F0B8D526000D66CAF /* configuration.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = configuration.cc; path = ../configuration.cc; sourceTree = SOURCE_ROOT; };
+ 696C90100B8D526000D66CAF /* connection.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = connection.cc; path = ../connection.cc; sourceTree = SOURCE_ROOT; };
+ 696C90110B8D526000D66CAF /* control_protocol_manager.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = control_protocol_manager.cc; path = ../control_protocol_manager.cc; sourceTree = SOURCE_ROOT; };
+ 696C90130B8D526000D66CAF /* crossfade.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = crossfade.cc; path = ../crossfade.cc; sourceTree = SOURCE_ROOT; };
+ 696C90140B8D526000D66CAF /* curve.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = curve.cc; path = ../curve.cc; sourceTree = SOURCE_ROOT; };
+ 696C90150B8D526000D66CAF /* cycle_timer.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = cycle_timer.cc; path = ../cycle_timer.cc; sourceTree = SOURCE_ROOT; };
+ 696C90160B8D526000D66CAF /* default_click.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = default_click.cc; path = ../default_click.cc; sourceTree = SOURCE_ROOT; };
+ 696C90180B8D526000D66CAF /* diskstream.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = diskstream.cc; path = ../diskstream.cc; sourceTree = SOURCE_ROOT; };
+ 696C90190B8D526000D66CAF /* enums.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = enums.cc; path = ../enums.cc; sourceTree = SOURCE_ROOT; };
+ 696C901A0B8D526000D66CAF /* gain.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = gain.cc; path = ../gain.cc; sourceTree = SOURCE_ROOT; };
+ 696C901B0B8D526000D66CAF /* gdither.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = gdither.cc; path = ../gdither.cc; sourceTree = SOURCE_ROOT; };
+ 696C901C0B8D526000D66CAF /* gettext.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = gettext.h; path = ../gettext.h; sourceTree = SOURCE_ROOT; };
+ 696C901D0B8D526000D66CAF /* globals.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = globals.cc; path = ../globals.cc; sourceTree = SOURCE_ROOT; };
+ 696C901E0B8D526000D66CAF /* i18n.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = i18n.h; path = ../i18n.h; sourceTree = SOURCE_ROOT; };
+ 696C901F0B8D526000D66CAF /* import.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = import.cc; path = ../import.cc; sourceTree = SOURCE_ROOT; };
+ 696C90200B8D526000D66CAF /* insert.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = insert.cc; path = ../insert.cc; sourceTree = SOURCE_ROOT; };
+ 696C90210B8D526000D66CAF /* io.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = io.cc; path = ../io.cc; sourceTree = SOURCE_ROOT; };
+ 696C90220B8D526000D66CAF /* jack_slave.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = jack_slave.cc; path = ../jack_slave.cc; sourceTree = SOURCE_ROOT; };
+ 696C90230B8D526000D66CAF /* ladspa_plugin.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = ladspa_plugin.cc; path = ../ladspa_plugin.cc; sourceTree = SOURCE_ROOT; };
+ 696C90240B8D526000D66CAF /* location.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = location.cc; path = ../location.cc; sourceTree = SOURCE_ROOT; };
+ 696C90250B8D526000D66CAF /* mix.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = mix.cc; path = ../mix.cc; sourceTree = SOURCE_ROOT; };
+ 696C90260B8D526000D66CAF /* mtc_slave.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = mtc_slave.cc; path = ../mtc_slave.cc; sourceTree = SOURCE_ROOT; };
+ 696C90270B8D526000D66CAF /* named_selection.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = named_selection.cc; path = ../named_selection.cc; sourceTree = SOURCE_ROOT; };
+ 696C90280B8D526000D66CAF /* osc.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = osc.cc; path = ../osc.cc; sourceTree = SOURCE_ROOT; };
+ 696C90290B8D526000D66CAF /* panner.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = panner.cc; path = ../panner.cc; sourceTree = SOURCE_ROOT; };
+ 696C902A0B8D526000D66CAF /* pcm_utils.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = pcm_utils.cc; path = ../pcm_utils.cc; sourceTree = SOURCE_ROOT; };
+ 696C902B0B8D526000D66CAF /* playlist.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = playlist.cc; path = ../playlist.cc; sourceTree = SOURCE_ROOT; };
+ 696C902C0B8D526000D66CAF /* playlist_factory.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = playlist_factory.cc; path = ../playlist_factory.cc; sourceTree = SOURCE_ROOT; };
+ 696C902D0B8D526000D66CAF /* plugin.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = plugin.cc; path = ../plugin.cc; sourceTree = SOURCE_ROOT; };
+ 696C902E0B8D526000D66CAF /* plugin_manager.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = plugin_manager.cc; path = ../plugin_manager.cc; sourceTree = SOURCE_ROOT; };
+ 696C902F0B8D526000D66CAF /* port.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = port.cc; path = ../port.cc; sourceTree = SOURCE_ROOT; };
+ 696C90300B8D526000D66CAF /* recent_sessions.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = recent_sessions.cc; path = ../recent_sessions.cc; sourceTree = SOURCE_ROOT; };
+ 696C90310B8D526000D66CAF /* redirect.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = redirect.cc; path = ../redirect.cc; sourceTree = SOURCE_ROOT; };
+ 696C90320B8D526000D66CAF /* region.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = region.cc; path = ../region.cc; sourceTree = SOURCE_ROOT; };
+ 696C90330B8D526000D66CAF /* region_factory.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = region_factory.cc; path = ../region_factory.cc; sourceTree = SOURCE_ROOT; };
+ 696C90340B8D526000D66CAF /* reverse.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = reverse.cc; path = ../reverse.cc; sourceTree = SOURCE_ROOT; };
+ 696C90350B8D526000D66CAF /* route.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = route.cc; path = ../route.cc; sourceTree = SOURCE_ROOT; };
+ 696C90360B8D526000D66CAF /* route_group.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = route_group.cc; path = ../route_group.cc; sourceTree = SOURCE_ROOT; };
+ 696C90370B8D526000D66CAF /* send.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = send.cc; path = ../send.cc; sourceTree = SOURCE_ROOT; };
+ 696C90380B8D526000D66CAF /* session.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session.cc; path = ../session.cc; sourceTree = SOURCE_ROOT; };
+ 696C90390B8D526000D66CAF /* session_butler.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_butler.cc; path = ../session_butler.cc; sourceTree = SOURCE_ROOT; };
+ 696C903A0B8D526000D66CAF /* session_click.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_click.cc; path = ../session_click.cc; sourceTree = SOURCE_ROOT; };
+ 696C903B0B8D526000D66CAF /* session_command.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_command.cc; path = ../session_command.cc; sourceTree = SOURCE_ROOT; };
+ 696C903D0B8D526000D66CAF /* session_events.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_events.cc; path = ../session_events.cc; sourceTree = SOURCE_ROOT; };
+ 696C903E0B8D526000D66CAF /* session_export.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_export.cc; path = ../session_export.cc; sourceTree = SOURCE_ROOT; };
+ 696C903F0B8D526000D66CAF /* session_feedback.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_feedback.cc; path = ../session_feedback.cc; sourceTree = SOURCE_ROOT; };
+ 696C90400B8D526000D66CAF /* session_midi.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_midi.cc; path = ../session_midi.cc; sourceTree = SOURCE_ROOT; };
+ 696C90410B8D526000D66CAF /* session_process.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_process.cc; path = ../session_process.cc; sourceTree = SOURCE_ROOT; };
+ 696C90420B8D526000D66CAF /* session_state.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_state.cc; path = ../session_state.cc; sourceTree = SOURCE_ROOT; };
+ 696C90430B8D526000D66CAF /* session_time.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_time.cc; path = ../session_time.cc; sourceTree = SOURCE_ROOT; };
+ 696C90440B8D526000D66CAF /* session_timefx.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_timefx.cc; path = ../session_timefx.cc; sourceTree = SOURCE_ROOT; };
+ 696C90450B8D526000D66CAF /* session_transport.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = session_transport.cc; path = ../session_transport.cc; sourceTree = SOURCE_ROOT; };
+ 696C90470B8D526000D66CAF /* silentfilesource.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = silentfilesource.cc; path = ../silentfilesource.cc; sourceTree = SOURCE_ROOT; };
+ 696C90480B8D526000D66CAF /* sndfile_helpers.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = sndfile_helpers.cc; path = ../sndfile_helpers.cc; sourceTree = SOURCE_ROOT; };
+ 696C90490B8D526000D66CAF /* sndfilesource.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = sndfilesource.cc; path = ../sndfilesource.cc; sourceTree = SOURCE_ROOT; };
+ 696C904A0B8D526000D66CAF /* source.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = source.cc; path = ../source.cc; sourceTree = SOURCE_ROOT; };
+ 696C904B0B8D526000D66CAF /* source_factory.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = source_factory.cc; path = ../source_factory.cc; sourceTree = SOURCE_ROOT; };
+ 696C904E0B8D526000D66CAF /* tempo.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = tempo.cc; path = ../tempo.cc; sourceTree = SOURCE_ROOT; };
+ 696C904F0B8D526000D66CAF /* track.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = track.cc; path = ../track.cc; sourceTree = SOURCE_ROOT; };
+ 696C90500B8D526000D66CAF /* utils.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = utils.cc; path = ../utils.cc; sourceTree = SOURCE_ROOT; };
+ 696C90F70B8D52F300D66CAF /* glibmm.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = glibmm.framework; path = /Library/Frameworks/glibmm.framework; sourceTree = "<absolute>"; };
+ 696C90F80B8D52F300D66CAF /* Jack.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Jack.framework; path = /Library/Frameworks/Jack.framework; sourceTree = "<absolute>"; };
+ 696C90F90B8D52F300D66CAF /* lo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = lo.framework; path = /Library/Frameworks/lo.framework; sourceTree = "<absolute>"; };
+ 696C90FA0B8D52F300D66CAF /* LRdf.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = LRdf.framework; path = /Library/Frameworks/LRdf.framework; sourceTree = "<absolute>"; };
+ 696C90FB0B8D52F300D66CAF /* Raptor.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Raptor.framework; path = /Library/Frameworks/Raptor.framework; sourceTree = "<absolute>"; };
+ 696C90FC0B8D52F300D66CAF /* SampleRate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SampleRate.framework; path = /Library/Frameworks/SampleRate.framework; sourceTree = "<absolute>"; };
+ 696C90FD0B8D52F300D66CAF /* sigc.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = sigc.framework; path = /Library/Frameworks/sigc.framework; sourceTree = "<absolute>"; };
+ 696C90FE0B8D52F300D66CAF /* Sndfile-ardour.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = "Sndfile-ardour.framework"; path = "/Library/Frameworks/Sndfile-ardour.framework"; sourceTree = "<absolute>"; };
+ 696C90FF0B8D52F300D66CAF /* soundtouch.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = soundtouch.framework; path = /Library/Frameworks/soundtouch.framework; sourceTree = "<absolute>"; };
+ 697D98080B8DCD8C0006A892 /* libxml2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libxml2.dylib; path = /usr/lib/libxml2.dylib; sourceTree = "<absolute>"; };
+ 698D9AB00B969A5B00C53B63 /* midi++.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "midi++.xcodeproj"; path = "../../midi++2/macosx/midi++.xcodeproj"; sourceTree = SOURCE_ROOT; };
+ 69AD45840B97D10600806E7E /* FLAC */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = FLAC; path = "/Library/Frameworks/Sndfile-ardour.framework/Versions/A/Libraries/FLAC"; sourceTree = "<absolute>"; };
+ 69B1B54F0B8E80AD007E41C1 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = /System/Library/Frameworks/CoreAudio.framework; sourceTree = "<absolute>"; };
+ 69DBC41A0BA794A500C19E65 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/Accelerate.framework; sourceTree = "<absolute>"; };
+ 69DBC42A0BA7992800C19E65 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /Developer/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks/Carbon.framework; sourceTree = "<absolute>"; };
+ 69F7CE520B8DCB3300D76871 /* basic_ui.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = basic_ui.cc; sourceTree = "<group>"; };
+ 69F7CE540B8DCB3300D76871 /* basic_ui.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = basic_ui.h; sourceTree = "<group>"; };
+ 69F7CE550B8DCB3300D76871 /* control_protocol.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = control_protocol.h; sourceTree = "<group>"; };
+ 69F7CE560B8DCB3300D76871 /* smpte.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = smpte.h; sourceTree = "<group>"; };
+ 69F7CE570B8DCB3300D76871 /* control_protocol.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = control_protocol.cc; sourceTree = "<group>"; };
+ 69F7CE590B8DCB3300D76871 /* smpte.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = smpte.cc; sourceTree = "<group>"; };
+ 8D07F2C70486CC7A007CD1D0 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
+ 8D07F2C80486CC7A007CD1D0 /* ardour.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ardour.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 8D07F2C30486CC7A007CD1D0 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 698D9AB60B969A6F00C53B63 /* midi++.framework in Frameworks */,
+ 69B1B5500B8E80AD007E41C1 /* CoreAudio.framework in Frameworks */,
+ 696C91000B8D52F300D66CAF /* glibmm.framework in Frameworks */,
+ 696C91010B8D52F300D66CAF /* Jack.framework in Frameworks */,
+ 696C91020B8D52F300D66CAF /* lo.framework in Frameworks */,
+ 696C91030B8D52F300D66CAF /* LRdf.framework in Frameworks */,
+ 696C91040B8D52F300D66CAF /* Raptor.framework in Frameworks */,
+ 696C91050B8D52F300D66CAF /* SampleRate.framework in Frameworks */,
+ 696C91060B8D52F300D66CAF /* sigc.framework in Frameworks */,
+ 696C91070B8D52F300D66CAF /* Sndfile-ardour.framework in Frameworks */,
+ 696C91080B8D52F300D66CAF /* soundtouch.framework in Frameworks */,
+ 697D98090B8DCD8C0006A892 /* libxml2.dylib in Frameworks */,
+ 69B1B5210B8E7DFD007E41C1 /* pbd.framework in Frameworks */,
+ 696149E90B97CEF500ECBDF0 /* glib in Frameworks */,
+ 696149EA0B97CEF500ECBDF0 /* gmodule in Frameworks */,
+ 696149EB0B97CEF500ECBDF0 /* gobject in Frameworks */,
+ 696149EC0B97CEF500ECBDF0 /* gthread in Frameworks */,
+ 69AD45850B97D10600806E7E /* FLAC in Frameworks */,
+ 69DBC41B0BA794A500C19E65 /* Accelerate.framework in Frameworks */,
+ 69DBC42B0BA7992800C19E65 /* Carbon.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 034768DDFF38A45A11DB9C8B /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 8D07F2C80486CC7A007CD1D0 /* ardour.framework */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 0867D691FE84028FC02AAC07 /* ardour */ = {
+ isa = PBXGroup;
+ children = (
+ 6964FEC80B8E7A7900799BAE /* version.h */,
+ 6964FEC90B8E7A7900799BAE /* version.cc */,
+ 08FB77ACFE841707C02AAC07 /* Source */,
+ 089C1665FE841158C02AAC07 /* Resources */,
+ 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */,
+ 034768DDFF38A45A11DB9C8B /* Products */,
+ );
+ name = ardour;
+ sourceTree = "<group>";
+ };
+ 0867D69AFE84028FC02AAC07 /* External Frameworks and Libraries */ = {
+ isa = PBXGroup;
+ children = (
+ 69DBC42A0BA7992800C19E65 /* Carbon.framework */,
+ 69DBC41A0BA794A500C19E65 /* Accelerate.framework */,
+ 69AD45840B97D10600806E7E /* FLAC */,
+ 696149E50B97CEF500ECBDF0 /* glib */,
+ 696149E60B97CEF500ECBDF0 /* gmodule */,
+ 696149E70B97CEF500ECBDF0 /* gobject */,
+ 696149E80B97CEF500ECBDF0 /* gthread */,
+ 698D9AB00B969A5B00C53B63 /* midi++.xcodeproj */,
+ 69B1B54F0B8E80AD007E41C1 /* CoreAudio.framework */,
+ 697D98080B8DCD8C0006A892 /* libxml2.dylib */,
+ 691B3B770B8D5508009155B5 /* pbd.xcodeproj */,
+ 696C90F70B8D52F300D66CAF /* glibmm.framework */,
+ 696C90F80B8D52F300D66CAF /* Jack.framework */,
+ 696C90F90B8D52F300D66CAF /* lo.framework */,
+ 696C90FA0B8D52F300D66CAF /* LRdf.framework */,
+ 696C90FB0B8D52F300D66CAF /* Raptor.framework */,
+ 696C90FC0B8D52F300D66CAF /* SampleRate.framework */,
+ 696C90FD0B8D52F300D66CAF /* sigc.framework */,
+ 696C90FE0B8D52F300D66CAF /* Sndfile-ardour.framework */,
+ 696C90FF0B8D52F300D66CAF /* soundtouch.framework */,
+ );
+ name = "External Frameworks and Libraries";
+ sourceTree = "<group>";
+ };
+ 089C1665FE841158C02AAC07 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 8D07F2C70486CC7A007CD1D0 /* Info.plist */,
+ 089C1666FE841158C02AAC07 /* InfoPlist.strings */,
+ );
+ name = Resources;
+ sourceTree = "<group>";
+ };
+ 08FB77ACFE841707C02AAC07 /* Source */ = {
+ isa = PBXGroup;
+ children = (
+ 69F7CE510B8DCB3300D76871 /* control_protocol */,
+ 696C8FAD0B8D526000D66CAF /* ardour */,
+ 696C90020B8D526000D66CAF /* audio_diskstream.cc */,
+ 696C90030B8D526000D66CAF /* audio_library.cc */,
+ 696C90040B8D526000D66CAF /* audio_playlist.cc */,
+ 696C90050B8D526000D66CAF /* audio_track.cc */,
+ 696C90070B8D526000D66CAF /* audioengine.cc */,
+ 696C90080B8D526000D66CAF /* audiofilesource.cc */,
+ 696C90090B8D526000D66CAF /* audiofilter.cc */,
+ 696C900A0B8D526000D66CAF /* audioregion.cc */,
+ 696C900B0B8D526000D66CAF /* audiosource.cc */,
+ 696C900C0B8D526000D66CAF /* auditioner.cc */,
+ 696C900D0B8D526000D66CAF /* automation.cc */,
+ 696C900E0B8D526000D66CAF /* automation_event.cc */,
+ 696C900F0B8D526000D66CAF /* configuration.cc */,
+ 696C90100B8D526000D66CAF /* connection.cc */,
+ 696C90110B8D526000D66CAF /* control_protocol_manager.cc */,
+ 696C90130B8D526000D66CAF /* crossfade.cc */,
+ 696C90140B8D526000D66CAF /* curve.cc */,
+ 696C90150B8D526000D66CAF /* cycle_timer.cc */,
+ 696C90160B8D526000D66CAF /* default_click.cc */,
+ 696C90180B8D526000D66CAF /* diskstream.cc */,
+ 696C90190B8D526000D66CAF /* enums.cc */,
+ 696C901A0B8D526000D66CAF /* gain.cc */,
+ 696C901B0B8D526000D66CAF /* gdither.cc */,
+ 696C901C0B8D526000D66CAF /* gettext.h */,
+ 696C901D0B8D526000D66CAF /* globals.cc */,
+ 696C901E0B8D526000D66CAF /* i18n.h */,
+ 696C901F0B8D526000D66CAF /* import.cc */,
+ 696C90200B8D526000D66CAF /* insert.cc */,
+ 696C90210B8D526000D66CAF /* io.cc */,
+ 696C90220B8D526000D66CAF /* jack_slave.cc */,
+ 696C90230B8D526000D66CAF /* ladspa_plugin.cc */,
+ 696C90240B8D526000D66CAF /* location.cc */,
+ 696C90250B8D526000D66CAF /* mix.cc */,
+ 696C90260B8D526000D66CAF /* mtc_slave.cc */,
+ 696C90270B8D526000D66CAF /* named_selection.cc */,
+ 696C90280B8D526000D66CAF /* osc.cc */,
+ 696C90290B8D526000D66CAF /* panner.cc */,
+ 696C902A0B8D526000D66CAF /* pcm_utils.cc */,
+ 696C902B0B8D526000D66CAF /* playlist.cc */,
+ 696C902C0B8D526000D66CAF /* playlist_factory.cc */,
+ 696C902D0B8D526000D66CAF /* plugin.cc */,
+ 696C902E0B8D526000D66CAF /* plugin_manager.cc */,
+ 696C902F0B8D526000D66CAF /* port.cc */,
+ 696C90300B8D526000D66CAF /* recent_sessions.cc */,
+ 696C90310B8D526000D66CAF /* redirect.cc */,
+ 696C90320B8D526000D66CAF /* region.cc */,
+ 696C90330B8D526000D66CAF /* region_factory.cc */,
+ 696C90340B8D526000D66CAF /* reverse.cc */,
+ 696C90350B8D526000D66CAF /* route.cc */,
+ 696C90360B8D526000D66CAF /* route_group.cc */,
+ 696C90370B8D526000D66CAF /* send.cc */,
+ 696C90380B8D526000D66CAF /* session.cc */,
+ 696C90390B8D526000D66CAF /* session_butler.cc */,
+ 696C903A0B8D526000D66CAF /* session_click.cc */,
+ 696C903B0B8D526000D66CAF /* session_command.cc */,
+ 696C903D0B8D526000D66CAF /* session_events.cc */,
+ 696C903E0B8D526000D66CAF /* session_export.cc */,
+ 696C903F0B8D526000D66CAF /* session_feedback.cc */,
+ 696C90400B8D526000D66CAF /* session_midi.cc */,
+ 696C90410B8D526000D66CAF /* session_process.cc */,
+ 696C90420B8D526000D66CAF /* session_state.cc */,
+ 696C90430B8D526000D66CAF /* session_time.cc */,
+ 696C90440B8D526000D66CAF /* session_timefx.cc */,
+ 696C90450B8D526000D66CAF /* session_transport.cc */,
+ 696C90470B8D526000D66CAF /* silentfilesource.cc */,
+ 696C90480B8D526000D66CAF /* sndfile_helpers.cc */,
+ 696C90490B8D526000D66CAF /* sndfilesource.cc */,
+ 696C904A0B8D526000D66CAF /* source.cc */,
+ 696C904B0B8D526000D66CAF /* source_factory.cc */,
+ 696C904E0B8D526000D66CAF /* tempo.cc */,
+ 696C904F0B8D526000D66CAF /* track.cc */,
+ 696C90500B8D526000D66CAF /* utils.cc */,
+ 32BAE0B70371A74B00C91783 /* ardour_Prefix.pch */,
+ );
+ name = Source;
+ sourceTree = "<group>";
+ };
+ 691B3B780B8D5508009155B5 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 691B3B7C0B8D5508009155B5 /* pbd.framework */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 696C8FAD0B8D526000D66CAF /* ardour */ = {
+ isa = PBXGroup;
+ children = (
+ 696C8FAF0B8D526000D66CAF /* ardour.h */,
+ 696C8FB00B8D526000D66CAF /* audio_diskstream.h */,
+ 696C8FB10B8D526000D66CAF /* audio_library.h */,
+ 696C8FB20B8D526000D66CAF /* audio_track.h */,
+ 696C8FB40B8D526000D66CAF /* audioengine.h */,
+ 696C8FB50B8D526000D66CAF /* audiofilesource.h */,
+ 696C8FB60B8D526000D66CAF /* audiofilter.h */,
+ 696C8FB70B8D526000D66CAF /* audioplaylist.h */,
+ 696C8FB80B8D526000D66CAF /* audioregion.h */,
+ 696C8FB90B8D526000D66CAF /* audiosource.h */,
+ 696C8FBA0B8D526000D66CAF /* auditioner.h */,
+ 696C8FBB0B8D526000D66CAF /* automation_event.h */,
+ 696C8FBC0B8D526000D66CAF /* buffer.h */,
+ 696C8FBD0B8D526000D66CAF /* click.h */,
+ 696C8FBE0B8D526000D66CAF /* configuration.h */,
+ 696C8FBF0B8D526000D66CAF /* configuration_variable.h */,
+ 696C8FC00B8D526000D66CAF /* configuration_vars.h */,
+ 696C8FC10B8D526000D66CAF /* connection.h */,
+ 696C8FC20B8D526000D66CAF /* control_protocol_manager.h */,
+ 696C8FC40B8D526000D66CAF /* crossfade.h */,
+ 696C8FC50B8D526000D66CAF /* crossfade_compare.h */,
+ 696C8FC60B8D526000D66CAF /* curve.h */,
+ 696C8FC70B8D526000D66CAF /* cycle_timer.h */,
+ 696C8FC80B8D526000D66CAF /* cycles.h */,
+ 696C8FC90B8D526000D66CAF /* data_type.h */,
+ 696C8FCA0B8D526000D66CAF /* dB.h */,
+ 696C8FCC0B8D526000D66CAF /* diskstream.h */,
+ 696C8FCD0B8D526000D66CAF /* export.h */,
+ 696C8FCE0B8D526000D66CAF /* gain.h */,
+ 696C8FCF0B8D526000D66CAF /* gdither.h */,
+ 696C8FD00B8D526000D66CAF /* gdither_types.h */,
+ 696C8FD10B8D526000D66CAF /* gdither_types_internal.h */,
+ 696C8FD20B8D526000D66CAF /* insert.h */,
+ 696C8FD30B8D526000D66CAF /* io.h */,
+ 696C8FD40B8D526000D66CAF /* ladspa.h */,
+ 696C8FD50B8D526000D66CAF /* ladspa_plugin.h */,
+ 696C8FD60B8D526000D66CAF /* location.h */,
+ 696C8FD70B8D526000D66CAF /* logcurve.h */,
+ 696C8FD80B8D526000D66CAF /* mix.h */,
+ 696C8FD90B8D526000D66CAF /* named_selection.h */,
+ 696C8FDA0B8D526000D66CAF /* noise.h */,
+ 696C8FDB0B8D526000D66CAF /* osc.h */,
+ 696C8FDC0B8D526000D66CAF /* panner.h */,
+ 696C8FDD0B8D526000D66CAF /* pcm_utils.h */,
+ 696C8FDE0B8D526000D66CAF /* peak.h */,
+ 696C8FDF0B8D526000D66CAF /* playlist.h */,
+ 696C8FE00B8D526000D66CAF /* playlist_factory.h */,
+ 696C8FE10B8D526000D66CAF /* playlist_templates.h */,
+ 696C8FE20B8D526000D66CAF /* plugin.h */,
+ 696C8FE30B8D526000D66CAF /* plugin_manager.h */,
+ 696C8FE40B8D526000D66CAF /* port.h */,
+ 696C8FE50B8D526000D66CAF /* recent_sessions.h */,
+ 696C8FE60B8D526000D66CAF /* redirect.h */,
+ 696C8FE70B8D526000D66CAF /* region.h */,
+ 696C8FE80B8D526000D66CAF /* region_factory.h */,
+ 696C8FE90B8D526000D66CAF /* reverse.h */,
+ 696C8FEA0B8D526000D66CAF /* route.h */,
+ 696C8FEB0B8D526000D66CAF /* route_group.h */,
+ 696C8FEC0B8D526000D66CAF /* route_group_specialized.h */,
+ 696C8FED0B8D526000D66CAF /* send.h */,
+ 696C8FEE0B8D526000D66CAF /* session.h */,
+ 696C8FEF0B8D526000D66CAF /* session_connection.h */,
+ 696C8FF00B8D526000D66CAF /* session_playlist.h */,
+ 696C8FF10B8D526000D66CAF /* session_region.h */,
+ 696C8FF20B8D526000D66CAF /* session_route.h */,
+ 696C8FF30B8D526000D66CAF /* session_selection.h */,
+ 696C8FF40B8D526000D66CAF /* silentfilesource.h */,
+ 696C8FF50B8D526000D66CAF /* slave.h */,
+ 696C8FF60B8D526000D66CAF /* sndfile_helpers.h */,
+ 696C8FF70B8D526000D66CAF /* sndfilesource.h */,
+ 696C8FF80B8D526000D66CAF /* soundseq.h */,
+ 696C8FF90B8D526000D66CAF /* source.h */,
+ 696C8FFA0B8D526000D66CAF /* source_factory.h */,
+ 696C8FFB0B8D526000D66CAF /* spline.h */,
+ 696C8FFC0B8D526000D66CAF /* tempo.h */,
+ 696C8FFD0B8D526000D66CAF /* timestamps.h */,
+ 696C8FFE0B8D526000D66CAF /* track.h */,
+ 696C8FFF0B8D526000D66CAF /* types.h */,
+ 696C90000B8D526000D66CAF /* utils.h */,
+ );
+ name = ardour;
+ path = ../ardour;
+ sourceTree = SOURCE_ROOT;
+ };
+ 698D9AB10B969A5B00C53B63 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 698D9AB50B969A5B00C53B63 /* midi++.framework */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 69F7CE510B8DCB3300D76871 /* control_protocol */ = {
+ isa = PBXGroup;
+ children = (
+ 69F7CE520B8DCB3300D76871 /* basic_ui.cc */,
+ 69F7CE530B8DCB3300D76871 /* control_protocol */,
+ 69F7CE570B8DCB3300D76871 /* control_protocol.cc */,
+ 69F7CE590B8DCB3300D76871 /* smpte.cc */,
+ );
+ name = control_protocol;
+ path = ../../surfaces/control_protocol;
+ sourceTree = SOURCE_ROOT;
+ };
+ 69F7CE530B8DCB3300D76871 /* control_protocol */ = {
+ isa = PBXGroup;
+ children = (
+ 69F7CE540B8DCB3300D76871 /* basic_ui.h */,
+ 69F7CE550B8DCB3300D76871 /* control_protocol.h */,
+ 69F7CE560B8DCB3300D76871 /* smpte.h */,
+ );
+ path = control_protocol;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+ 8D07F2BD0486CC7A007CD1D0 /* Headers */ = {
+ isa = PBXHeadersBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D07F2BE0486CC7A007CD1D0 /* ardour_Prefix.pch in Headers */,
+ 696C90530B8D526000D66CAF /* ardour.h in Headers */,
+ 696C90540B8D526000D66CAF /* audio_diskstream.h in Headers */,
+ 696C90550B8D526000D66CAF /* audio_library.h in Headers */,
+ 696C90560B8D526000D66CAF /* audio_track.h in Headers */,
+ 696C90580B8D526000D66CAF /* audioengine.h in Headers */,
+ 696C90590B8D526000D66CAF /* audiofilesource.h in Headers */,
+ 696C905A0B8D526000D66CAF /* audiofilter.h in Headers */,
+ 696C905B0B8D526000D66CAF /* audioplaylist.h in Headers */,
+ 696C905C0B8D526000D66CAF /* audioregion.h in Headers */,
+ 696C905D0B8D526000D66CAF /* audiosource.h in Headers */,
+ 696C905E0B8D526000D66CAF /* auditioner.h in Headers */,
+ 696C905F0B8D526000D66CAF /* automation_event.h in Headers */,
+ 696C90600B8D526000D66CAF /* buffer.h in Headers */,
+ 696C90610B8D526000D66CAF /* click.h in Headers */,
+ 696C90620B8D526000D66CAF /* configuration.h in Headers */,
+ 696C90630B8D526000D66CAF /* configuration_variable.h in Headers */,
+ 696C90640B8D526000D66CAF /* configuration_vars.h in Headers */,
+ 696C90650B8D526000D66CAF /* connection.h in Headers */,
+ 696C90660B8D526000D66CAF /* control_protocol_manager.h in Headers */,
+ 696C90680B8D526000D66CAF /* crossfade.h in Headers */,
+ 696C90690B8D526000D66CAF /* crossfade_compare.h in Headers */,
+ 696C906A0B8D526000D66CAF /* curve.h in Headers */,
+ 696C906B0B8D526000D66CAF /* cycle_timer.h in Headers */,
+ 696C906C0B8D526000D66CAF /* cycles.h in Headers */,
+ 696C906D0B8D526000D66CAF /* data_type.h in Headers */,
+ 696C906E0B8D526000D66CAF /* dB.h in Headers */,
+ 696C90700B8D526000D66CAF /* diskstream.h in Headers */,
+ 696C90710B8D526000D66CAF /* export.h in Headers */,
+ 696C90720B8D526000D66CAF /* gain.h in Headers */,
+ 696C90730B8D526000D66CAF /* gdither.h in Headers */,
+ 696C90740B8D526000D66CAF /* gdither_types.h in Headers */,
+ 696C90750B8D526000D66CAF /* gdither_types_internal.h in Headers */,
+ 696C90760B8D526000D66CAF /* insert.h in Headers */,
+ 696C90770B8D526000D66CAF /* io.h in Headers */,
+ 696C90780B8D526000D66CAF /* ladspa.h in Headers */,
+ 696C90790B8D526000D66CAF /* ladspa_plugin.h in Headers */,
+ 696C907A0B8D526000D66CAF /* location.h in Headers */,
+ 696C907B0B8D526000D66CAF /* logcurve.h in Headers */,
+ 696C907C0B8D526000D66CAF /* mix.h in Headers */,
+ 696C907D0B8D526000D66CAF /* named_selection.h in Headers */,
+ 696C907E0B8D526000D66CAF /* noise.h in Headers */,
+ 696C907F0B8D526000D66CAF /* osc.h in Headers */,
+ 696C90800B8D526000D66CAF /* panner.h in Headers */,
+ 696C90810B8D526000D66CAF /* pcm_utils.h in Headers */,
+ 696C90820B8D526000D66CAF /* peak.h in Headers */,
+ 696C90830B8D526000D66CAF /* playlist.h in Headers */,
+ 696C90840B8D526000D66CAF /* playlist_factory.h in Headers */,
+ 696C90850B8D526000D66CAF /* playlist_templates.h in Headers */,
+ 696C90860B8D526000D66CAF /* plugin.h in Headers */,
+ 696C90870B8D526000D66CAF /* plugin_manager.h in Headers */,
+ 696C90880B8D526000D66CAF /* port.h in Headers */,
+ 696C90890B8D526000D66CAF /* recent_sessions.h in Headers */,
+ 696C908A0B8D526000D66CAF /* redirect.h in Headers */,
+ 696C908B0B8D526000D66CAF /* region.h in Headers */,
+ 696C908C0B8D526000D66CAF /* region_factory.h in Headers */,
+ 696C908D0B8D526000D66CAF /* reverse.h in Headers */,
+ 696C908E0B8D526000D66CAF /* route.h in Headers */,
+ 696C908F0B8D526000D66CAF /* route_group.h in Headers */,
+ 696C90900B8D526000D66CAF /* route_group_specialized.h in Headers */,
+ 696C90910B8D526000D66CAF /* send.h in Headers */,
+ 696C90920B8D526000D66CAF /* session.h in Headers */,
+ 696C90930B8D526000D66CAF /* session_connection.h in Headers */,
+ 696C90940B8D526000D66CAF /* session_playlist.h in Headers */,
+ 696C90950B8D526000D66CAF /* session_region.h in Headers */,
+ 696C90960B8D526000D66CAF /* session_route.h in Headers */,
+ 696C90970B8D526000D66CAF /* session_selection.h in Headers */,
+ 696C90980B8D526000D66CAF /* silentfilesource.h in Headers */,
+ 696C90990B8D526000D66CAF /* slave.h in Headers */,
+ 696C909A0B8D526000D66CAF /* sndfile_helpers.h in Headers */,
+ 696C909B0B8D526000D66CAF /* sndfilesource.h in Headers */,
+ 696C909C0B8D526000D66CAF /* soundseq.h in Headers */,
+ 696C909D0B8D526000D66CAF /* source.h in Headers */,
+ 696C909E0B8D526000D66CAF /* source_factory.h in Headers */,
+ 696C909F0B8D526000D66CAF /* spline.h in Headers */,
+ 696C90A00B8D526000D66CAF /* tempo.h in Headers */,
+ 696C90A10B8D526000D66CAF /* timestamps.h in Headers */,
+ 696C90A20B8D526000D66CAF /* track.h in Headers */,
+ 696C90A30B8D526000D66CAF /* types.h in Headers */,
+ 696C90A40B8D526000D66CAF /* utils.h in Headers */,
+ 696C90C00B8D526000D66CAF /* gettext.h in Headers */,
+ 696C90C20B8D526000D66CAF /* i18n.h in Headers */,
+ 69F7CE5B0B8DCB3300D76871 /* basic_ui.h in Headers */,
+ 69F7CE5C0B8DCB3300D76871 /* control_protocol.h in Headers */,
+ 69F7CE5D0B8DCB3300D76871 /* smpte.h in Headers */,
+ 6964FECA0B8E7A7900799BAE /* version.h in Headers */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+ 8D07F2BC0486CC7A007CD1D0 /* ardour */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 4FADC24208B4156D00ABE55E /* Build configuration list for PBXNativeTarget "ardour" */;
+ buildPhases = (
+ 8D07F2BD0486CC7A007CD1D0 /* Headers */,
+ 8D07F2BF0486CC7A007CD1D0 /* Resources */,
+ 8D07F2C10486CC7A007CD1D0 /* Sources */,
+ 8D07F2C30486CC7A007CD1D0 /* Frameworks */,
+ 8D07F2C50486CC7A007CD1D0 /* Rez */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 69D5F6260B8D58A500301E71 /* PBXTargetDependency */,
+ 698D9ABA0B969ADC00C53B63 /* PBXTargetDependency */,
+ );
+ name = ardour;
+ productInstallPath = "$(HOME)/Library/Frameworks";
+ productName = ardour;
+ productReference = 8D07F2C80486CC7A007CD1D0 /* ardour.framework */;
+ productType = "com.apple.product-type.framework";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 0867D690FE84028FC02AAC07 /* Project object */ = {
+ isa = PBXProject;
+ buildConfigurationList = 4FADC24608B4156D00ABE55E /* Build configuration list for PBXProject "ardour" */;
+ hasScannedForEncodings = 1;
+ mainGroup = 0867D691FE84028FC02AAC07 /* ardour */;
+ productRefGroup = 034768DDFF38A45A11DB9C8B /* Products */;
+ projectDirPath = "";
+ projectReferences = (
+ {
+ ProductGroup = 698D9AB10B969A5B00C53B63 /* Products */;
+ ProjectRef = 698D9AB00B969A5B00C53B63 /* midi++.xcodeproj */;
+ },
+ {
+ ProductGroup = 691B3B780B8D5508009155B5 /* Products */;
+ ProjectRef = 691B3B770B8D5508009155B5 /* pbd.xcodeproj */;
+ },
+ );
+ projectRoot = "";
+ targets = (
+ 8D07F2BC0486CC7A007CD1D0 /* ardour */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXReferenceProxy section */
+ 691B3B7C0B8D5508009155B5 /* pbd.framework */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.framework;
+ path = pbd.framework;
+ remoteRef = 691B3B7B0B8D5508009155B5 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+ 698D9AB50B969A5B00C53B63 /* midi++.framework */ = {
+ isa = PBXReferenceProxy;
+ fileType = wrapper.framework;
+ path = "midi++.framework";
+ remoteRef = 698D9AB40B969A5B00C53B63 /* PBXContainerItemProxy */;
+ sourceTree = BUILT_PRODUCTS_DIR;
+ };
+/* End PBXReferenceProxy section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 8D07F2BF0486CC7A007CD1D0 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 8D07F2C00486CC7A007CD1D0 /* InfoPlist.strings in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXRezBuildPhase section */
+ 8D07F2C50486CC7A007CD1D0 /* Rez */ = {
+ isa = PBXRezBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXRezBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 8D07F2C10486CC7A007CD1D0 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 696C90A60B8D526000D66CAF /* audio_diskstream.cc in Sources */,
+ 696C90A70B8D526000D66CAF /* audio_library.cc in Sources */,
+ 696C90A80B8D526000D66CAF /* audio_playlist.cc in Sources */,
+ 696C90A90B8D526000D66CAF /* audio_track.cc in Sources */,
+ 696C90AB0B8D526000D66CAF /* audioengine.cc in Sources */,
+ 696C90AC0B8D526000D66CAF /* audiofilesource.cc in Sources */,
+ 696C90AD0B8D526000D66CAF /* audiofilter.cc in Sources */,
+ 696C90AE0B8D526000D66CAF /* audioregion.cc in Sources */,
+ 696C90AF0B8D526000D66CAF /* audiosource.cc in Sources */,
+ 696C90B00B8D526000D66CAF /* auditioner.cc in Sources */,
+ 696C90B10B8D526000D66CAF /* automation.cc in Sources */,
+ 696C90B20B8D526000D66CAF /* automation_event.cc in Sources */,
+ 696C90B30B8D526000D66CAF /* configuration.cc in Sources */,
+ 696C90B40B8D526000D66CAF /* connection.cc in Sources */,
+ 696C90B50B8D526000D66CAF /* control_protocol_manager.cc in Sources */,
+ 696C90B70B8D526000D66CAF /* crossfade.cc in Sources */,
+ 696C90B80B8D526000D66CAF /* curve.cc in Sources */,
+ 696C90B90B8D526000D66CAF /* cycle_timer.cc in Sources */,
+ 696C90BA0B8D526000D66CAF /* default_click.cc in Sources */,
+ 696C90BC0B8D526000D66CAF /* diskstream.cc in Sources */,
+ 696C90BD0B8D526000D66CAF /* enums.cc in Sources */,
+ 696C90BE0B8D526000D66CAF /* gain.cc in Sources */,
+ 696C90BF0B8D526000D66CAF /* gdither.cc in Sources */,
+ 696C90C10B8D526000D66CAF /* globals.cc in Sources */,
+ 696C90C30B8D526000D66CAF /* import.cc in Sources */,
+ 696C90C40B8D526000D66CAF /* insert.cc in Sources */,
+ 696C90C50B8D526000D66CAF /* io.cc in Sources */,
+ 696C90C60B8D526000D66CAF /* jack_slave.cc in Sources */,
+ 696C90C70B8D526000D66CAF /* ladspa_plugin.cc in Sources */,
+ 696C90C80B8D526000D66CAF /* location.cc in Sources */,
+ 696C90C90B8D526000D66CAF /* mix.cc in Sources */,
+ 696C90CA0B8D526000D66CAF /* mtc_slave.cc in Sources */,
+ 696C90CB0B8D526000D66CAF /* named_selection.cc in Sources */,
+ 696C90CC0B8D526000D66CAF /* osc.cc in Sources */,
+ 696C90CD0B8D526000D66CAF /* panner.cc in Sources */,
+ 696C90CE0B8D526000D66CAF /* pcm_utils.cc in Sources */,
+ 696C90CF0B8D526000D66CAF /* playlist.cc in Sources */,
+ 696C90D00B8D526000D66CAF /* playlist_factory.cc in Sources */,
+ 696C90D10B8D526000D66CAF /* plugin.cc in Sources */,
+ 696C90D20B8D526000D66CAF /* plugin_manager.cc in Sources */,
+ 696C90D30B8D526000D66CAF /* port.cc in Sources */,
+ 696C90D40B8D526000D66CAF /* recent_sessions.cc in Sources */,
+ 696C90D50B8D526000D66CAF /* redirect.cc in Sources */,
+ 696C90D60B8D526000D66CAF /* region.cc in Sources */,
+ 696C90D70B8D526000D66CAF /* region_factory.cc in Sources */,
+ 696C90D80B8D526000D66CAF /* reverse.cc in Sources */,
+ 696C90D90B8D526000D66CAF /* route.cc in Sources */,
+ 696C90DA0B8D526000D66CAF /* route_group.cc in Sources */,
+ 696C90DB0B8D526000D66CAF /* send.cc in Sources */,
+ 696C90DC0B8D526000D66CAF /* session.cc in Sources */,
+ 696C90DD0B8D526000D66CAF /* session_butler.cc in Sources */,
+ 696C90DE0B8D526000D66CAF /* session_click.cc in Sources */,
+ 696C90DF0B8D526000D66CAF /* session_command.cc in Sources */,
+ 696C90E10B8D526000D66CAF /* session_events.cc in Sources */,
+ 696C90E20B8D526000D66CAF /* session_export.cc in Sources */,
+ 696C90E30B8D526000D66CAF /* session_feedback.cc in Sources */,
+ 696C90E40B8D526000D66CAF /* session_midi.cc in Sources */,
+ 696C90E50B8D526000D66CAF /* session_process.cc in Sources */,
+ 696C90E60B8D526000D66CAF /* session_state.cc in Sources */,
+ 696C90E70B8D526000D66CAF /* session_time.cc in Sources */,
+ 696C90E80B8D526000D66CAF /* session_timefx.cc in Sources */,
+ 696C90E90B8D526000D66CAF /* session_transport.cc in Sources */,
+ 696C90EB0B8D526000D66CAF /* silentfilesource.cc in Sources */,
+ 696C90EC0B8D526000D66CAF /* sndfile_helpers.cc in Sources */,
+ 696C90ED0B8D526000D66CAF /* sndfilesource.cc in Sources */,
+ 696C90EE0B8D526000D66CAF /* source.cc in Sources */,
+ 696C90EF0B8D526000D66CAF /* source_factory.cc in Sources */,
+ 696C90F20B8D526000D66CAF /* tempo.cc in Sources */,
+ 696C90F30B8D526000D66CAF /* track.cc in Sources */,
+ 696C90F40B8D526000D66CAF /* utils.cc in Sources */,
+ 69F7CE5A0B8DCB3300D76871 /* basic_ui.cc in Sources */,
+ 69F7CE5E0B8DCB3300D76871 /* control_protocol.cc in Sources */,
+ 69F7CE600B8DCB3300D76871 /* smpte.cc in Sources */,
+ 6964FECB0B8E7A7900799BAE /* version.cc in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 698D9ABA0B969ADC00C53B63 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ name = "midi++";
+ targetProxy = 698D9AB90B969ADC00C53B63 /* PBXContainerItemProxy */;
+ };
+ 69D5F6260B8D58A500301E71 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ name = pbd;
+ targetProxy = 69D5F6250B8D58A500301E71 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 089C1666FE841158C02AAC07 /* InfoPlist.strings */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 089C1667FE841158C02AAC07 /* English */,
+ );
+ name = InfoPlist.strings;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 4FADC24408B4156D00ABE55E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = (
+ ppc,
+ i386,
+ );
+ DEBUG_INFORMATION_FORMAT = stabs;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
+ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_2)",
+ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)",
+ );
+ FRAMEWORK_SEARCH_PATHS_QUOTED_1 = "\"$(SYSTEM_DEVELOPER_DIR)/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks\"";
+ FRAMEWORK_VERSION = A;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = ardour_Prefix.pch;
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "@executable_path/../Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
+ "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_2)",
+ "$(LIBRARY_SEARCH_PATHS_QUOTED_1)",
+ "$(LIBRARY_SEARCH_PATHS_QUOTED_2)",
+ );
+ LIBRARY_SEARCH_PATHS_QUOTED_1 = "\"$(LOCAL_LIBRARY_DIR)/Frameworks/GLib.framework/Versions/2.12.3/Libraries\"";
+ LIBRARY_SEARCH_PATHS_QUOTED_2 = "\"$(LOCAL_LIBRARY_DIR)/Frameworks/Sndfile-ardour.framework/Versions/A/Libraries\"";
+ LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(LOCAL_LIBRARY_DIR)/Frameworks/GLib.framework/Versions/2.12.3/Libraries\"";
+ LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_2 = "\"$(LOCAL_LIBRARY_DIR)/Frameworks/Sndfile-ardour.framework/Versions/A/Libraries\"";
+ LIBRARY_STYLE = DYNAMIC;
+ MACH_O_TYPE = mh_dylib;
+ PRODUCT_NAME = ardour;
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Release;
+ };
+ 4FADC24808B4156D00ABE55E /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = (
+ "$(NATIVE_ARCH)",
+ ppc,
+ );
+ FRAMEWORK_SEARCH_PATHS = /opt/ardour/build;
+ GCC_FAST_OBJC_DISPATCH = NO;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
+ GCC_MODEL_TUNING = G4;
+ GCC_OPTIMIZATION_LEVEL = 3;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ BUILD_VECLIB_OPTIMIZATIONS,
+ HAVE_WORDEXP,
+ HAVE_WEAK_COREAUDIO,
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_1)",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_2)",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_3)",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_4)",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_5)",
+ NO_POSIX_MEMALIGN,
+ );
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_1 = "PACKAGE=\"\\\"libardour\\\"\"";
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_2 = "CONFIG_DIR=\"\\\"/Library/Application\\ Support/Ardour/config\\\"\"";
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_3 = "MODULE_DIR=\"\\\"/Library/Application\\ Support/Ardour/modules\\\"\"";
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_4 = "DATA_DIR=\"\\\"/Library/Application\\ Support/Ardour/data\\\"\"";
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_5 = "LOCALEDIR=\"\\\"/Library/Application\\ Support/Ardour/locales\\\"\"";
+ GCC_STRICT_ALIASING = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ /usr/include/libxml2,
+ /Library/Frameworks/sigc.framework/Headers,
+ /Library/Frameworks/GLib.framework/Headers,
+ /Library/Frameworks/glibmm.framework/Headers,
+ "/Library/Frameworks/Sndfile-ardour.framework/Headers",
+ /Library/Frameworks/SampleRate.framework/Headers,
+ /Library/Frameworks/Raptor.framework/Headers,
+ /Library/Frameworks/LRdf.framework/Headers,
+ /opt/ardour/src/ardour2/libs/surfaces/control_protocol,
+ );
+ INSTALL_PATH = "@executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = "";
+ PREBINDING = NO;
+ SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+ };
+ name = Release;
+ };
+ 694E7C8A0B97B0FE0018D03D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(NATIVE_ARCH)";
+ COPY_PHASE_STRIP = NO;
+ FRAMEWORK_SEARCH_PATHS = /opt/ardour/build;
+ GCC_ENABLE_FIX_AND_CONTINUE = YES;
+ GCC_ENABLE_OBJC_EXCEPTIONS = NO;
+ GCC_FAST_OBJC_DISPATCH = NO;
+ GCC_MODEL_TUNING = G4;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ BUILD_VECLIB_OPTIMIZATIONS,
+ HAVE_WORDEXP,
+ HAVE_WEAK_COREAUDIO,
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_1)",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_2)",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_3)",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_4)",
+ "$(GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_5)",
+ NO_POSIX_MEMALIGN,
+ );
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_1 = "PACKAGE=\"\\\"libardour\\\"\"";
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_2 = "CONFIG_DIR=\"\\\"/Library/Application\\ Support/Ardour/config\\\"\"";
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_3 = "MODULE_DIR=\"\\\"/Library/Application\\ Support/Ardour/modules\\\"\"";
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_4 = "DATA_DIR=\"\\\"/Library/Application\\ Support/Ardour/data\\\"\"";
+ GCC_PREPROCESSOR_DEFINITIONS_QUOTED_FOR_PROJECT_5 = "LOCALEDIR=\"\\\"/Library/Application\\ Support/Ardour/locales\\\"\"";
+ GCC_STRICT_ALIASING = YES;
+ GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
+ GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+ GCC_WARN_ABOUT_POINTER_SIGNEDNESS = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL = NO;
+ GCC_WARN_CHECK_SWITCH_STATEMENTS = YES;
+ GCC_WARN_EFFECTIVE_CPLUSPLUS_VIOLATIONS = NO;
+ GCC_WARN_MISSING_PARENTHESES = YES;
+ GCC_WARN_NON_VIRTUAL_DESTRUCTOR = NO;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ HEADER_SEARCH_PATHS = (
+ /usr/include/libxml2,
+ /Library/Frameworks/sigc.framework/Headers,
+ /Library/Frameworks/GLib.framework/Headers,
+ /Library/Frameworks/glibmm.framework/Headers,
+ "/Library/Frameworks/Sndfile-ardour.framework/Headers",
+ /Library/Frameworks/SampleRate.framework/Headers,
+ /Library/Frameworks/Raptor.framework/Headers,
+ /Library/Frameworks/LRdf.framework/Headers,
+ /opt/ardour/src/ardour2/libs/surfaces/control_protocol,
+ );
+ INSTALL_PATH = "@executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = "";
+ PREBINDING = NO;
+ SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk;
+ STRIP_INSTALLED_PRODUCT = NO;
+ };
+ name = Debug;
+ };
+ 694E7C8B0B97B0FE0018D03D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ARCHS = i386;
+ DEBUG_INFORMATION_FORMAT = stabs;
+ DYLIB_COMPATIBILITY_VERSION = 1;
+ DYLIB_CURRENT_VERSION = 1;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
+ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_FOR_TARGET_2)",
+ "$(FRAMEWORK_SEARCH_PATHS_QUOTED_1)",
+ );
+ FRAMEWORK_SEARCH_PATHS_QUOTED_1 = "\"$(SYSTEM_DEVELOPER_DIR)/SDKs/MacOSX10.4u.sdk/System/Library/Frameworks\"";
+ FRAMEWORK_VERSION = A;
+ GCC_GENERATE_DEBUGGING_SYMBOLS = YES;
+ GCC_MODEL_TUNING = G5;
+ GCC_PRECOMPILE_PREFIX_HEADER = YES;
+ GCC_PREFIX_HEADER = ardour_Prefix.pch;
+ INFOPLIST_FILE = Info.plist;
+ INSTALL_PATH = "@executable_path/../Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1)",
+ "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_2)",
+ "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_3)",
+ "$(LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_4)",
+ );
+ LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_1 = "\"$(LOCAL_LIBRARY_DIR)/Frameworks/GLib.framework/Versions/2.12.3/Libraries\"";
+ LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_2 = "\"$(LOCAL_LIBRARY_DIR)/Frameworks/Sndfile-ardour.framework/Versions/A/Libraries\"";
+ LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_3 = "\"$(LOCAL_LIBRARY_DIR)/Frameworks/GLib.framework/Versions/2.12.3/Libraries\"";
+ LIBRARY_SEARCH_PATHS_QUOTED_FOR_TARGET_4 = "\"$(LOCAL_LIBRARY_DIR)/Frameworks/Sndfile-ardour.framework/Versions/A/Libraries\"";
+ LIBRARY_STYLE = DYNAMIC;
+ MACH_O_TYPE = mh_dylib;
+ PRODUCT_NAME = ardour;
+ WRAPPER_EXTENSION = framework;
+ };
+ name = Debug;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 4FADC24208B4156D00ABE55E /* Build configuration list for PBXNativeTarget "ardour" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4FADC24408B4156D00ABE55E /* Release */,
+ 694E7C8B0B97B0FE0018D03D /* Debug */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 4FADC24608B4156D00ABE55E /* Build configuration list for PBXProject "ardour" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 4FADC24808B4156D00ABE55E /* Release */,
+ 694E7C8A0B97B0FE0018D03D /* Debug */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 0867D690FE84028FC02AAC07 /* Project object */;
+}
diff --git a/libs/ardour/macosx/ardour_Prefix.pch b/libs/ardour/macosx/ardour_Prefix.pch
new file mode 100644
index 0000000000..b03e5d3d98
--- /dev/null
+++ b/libs/ardour/macosx/ardour_Prefix.pch
@@ -0,0 +1,4 @@
+//
+// Prefix header for all source files of the 'ardour' target in the 'ardour' project.
+//
+
diff --git a/libs/ardour/macosx/version.cc b/libs/ardour/macosx/version.cc
new file mode 100644
index 0000000000..9da3d2c359
--- /dev/null
+++ b/libs/ardour/macosx/version.cc
@@ -0,0 +1,3 @@
+int libardour_major_version = 2;
+int libardour_minor_version = 0;
+int libardour_micro_version = 0;
diff --git a/libs/ardour/macosx/version.h b/libs/ardour/macosx/version.h
new file mode 100644
index 0000000000..9c575ee077
--- /dev/null
+++ b/libs/ardour/macosx/version.h
@@ -0,0 +1,17 @@
+/*
+ * version.h
+ * ardour
+ *
+ * Created by Taybin Rutkin on 2/22/07.
+ * Copyright 2007 Paul Davis. All rights reserved.
+ *
+ */
+
+#ifndef __libardour_version_h__
+#define __libardour_version_h__
+
+extern int libardour_major_version;
+extern int libardour_minor_version;
+extern int libardour_micro_version;
+
+#endif /* __libardour_version_h__ */
diff --git a/libs/ardour/meter.cc b/libs/ardour/meter.cc
new file mode 100644
index 0000000000..e75c1d89ca
--- /dev/null
+++ b/libs/ardour/meter.cc
@@ -0,0 +1,165 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <ardour/meter.h>
+#include <algorithm>
+#include <cmath>
+#include <ardour/buffer_set.h>
+#include <ardour/peak.h>
+#include <ardour/dB.h>
+#include <ardour/session.h>
+
+namespace ARDOUR {
+
+
+/** Get peaks from @a bufs
+ * Input acceptance is lenient - the first n buffers from @a bufs will
+ * be metered, where n was set by the last call to setup(), excess meters will
+ * be set to 0.
+ */
+void
+PeakMeter::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
+{
+ size_t meterable = std::min((size_t)bufs.count().n_total(), _peak_power.size());
+
+ size_t n = 0;
+
+ // Meter what we have (midi)
+ for ( ; n < meterable && n < bufs.count().n_midi(); ++n) {
+
+ float val = 0;
+
+ // GUI needs a better MIDI meter, not much information can be
+ // expressed through peaks alone
+ for (MidiBuffer::iterator i = bufs.get_midi(n).begin(); i != bufs.get_midi(n).end(); ++i) {
+ const MIDI::Event& ev = *i;
+ if (ev.is_note_on()) {
+ const float this_vel = log(ev.buffer()[2] / 127.0 * (M_E*M_E-M_E) + M_E) - 1.0;
+ //printf("V %d -> %f\n", (int)((Byte)ev.buffer[2]), this_vel);
+ if (this_vel > val)
+ val = this_vel;
+ } else {
+ val += 1.0 / bufs.get_midi(n).capacity();
+ if (val > 1.0)
+ val = 1.0;
+ }
+ }
+
+ _peak_power[n] = val;
+
+ }
+
+ // Meter what we have (audio)
+ for ( ; n < meterable && n < bufs.count().n_audio(); ++n) {
+ _peak_power[n] = compute_peak (bufs.get_audio(n).data(nframes, offset), nframes, _peak_power[n]);
+ }
+
+ // Zero any excess peaks
+ for (size_t n = meterable; n < _peak_power.size(); ++n) {
+ _peak_power[n] = 0;
+ }
+}
+
+void
+PeakMeter::reset ()
+{
+ for (size_t i = 0; i < _peak_power.size(); ++i) {
+ _peak_power[i] = 0;
+ }
+}
+
+void
+PeakMeter::reset_max ()
+{
+ for (size_t i = 0; i < _max_peak_power.size(); ++i) {
+ _max_peak_power[i] = -INFINITY;
+ }
+}
+
+bool
+PeakMeter::configure_io (ChanCount in, ChanCount out)
+{
+ /* we're transparent no matter what. fight the power. */
+ if (out != in)
+ return false;
+
+ uint32_t limit = in.n_total();
+
+ while (_peak_power.size() > limit) {
+ _peak_power.pop_back();
+ _visible_peak_power.pop_back();
+ _max_peak_power.pop_back();
+ }
+
+ while (_peak_power.size() < limit) {
+ _peak_power.push_back(0);
+ _visible_peak_power.push_back(minus_infinity());
+ _max_peak_power.push_back(minus_infinity());
+ }
+
+ assert(_peak_power.size() == limit);
+ assert(_visible_peak_power.size() == limit);
+ assert(_max_peak_power.size() == limit);
+
+ Processor::configure_io(in, out);
+
+ return true;
+}
+
+/** To be driven by the Meter signal from IO.
+ * Caller MUST hold io_lock!
+ */
+void
+PeakMeter::meter ()
+{
+ assert(_visible_peak_power.size() == _peak_power.size());
+
+ const size_t limit = _peak_power.size();
+
+ for (size_t n = 0; n < limit; ++n) {
+
+ /* XXX we should use atomic exchange here */
+
+ /* grab peak since last read */
+
+ float new_peak = _peak_power[n];
+ _peak_power[n] = 0;
+
+ /* compute new visible value using falloff */
+
+ if (new_peak > 0.0) {
+ new_peak = coefficient_to_dB (new_peak);
+ } else {
+ new_peak = minus_infinity();
+ }
+
+ /* update max peak */
+
+ _max_peak_power[n] = std::max (new_peak, _max_peak_power[n]);
+
+ if (Config->get_meter_falloff() == 0.0f || new_peak > _visible_peak_power[n]) {
+ _visible_peak_power[n] = new_peak;
+ } else {
+ // do falloff
+ new_peak = _visible_peak_power[n] - (Config->get_meter_falloff() * 0.01f);
+ _visible_peak_power[n] = std::max (new_peak, -INFINITY);
+ }
+ }
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/midi_buffer.cc b/libs/ardour/midi_buffer.cc
new file mode 100644
index 0000000000..1530babe34
--- /dev/null
+++ b/libs/ardour/midi_buffer.cc
@@ -0,0 +1,282 @@
+/*
+ Copyright (C) 2006-2007 Paul Davis
+ Author: Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <iostream>
+#include <ardour/midi_buffer.h>
+
+#ifdef __x86_64__
+static const int CPU_CACHE_ALIGN = 64;
+#else
+static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
+#endif
+
+using namespace std;
+using namespace ARDOUR;
+
+
+// FIXME: mirroring for MIDI buffers?
+MidiBuffer::MidiBuffer(size_t capacity)
+ : Buffer(DataType::MIDI, capacity)
+ , _events(0)
+ , _data(0)
+// , _owns_data(false)
+{
+ if (capacity) {
+ resize (_capacity);
+ silence(_capacity);
+ }
+}
+
+MidiBuffer::~MidiBuffer()
+{
+ if (_events) {
+ free(_events);
+ }
+ if (_data) {
+ free(_data);
+ }
+}
+
+void
+MidiBuffer::resize (size_t size)
+{
+ assert(size > 0);
+
+ if (size < _capacity) {
+ return;
+ }
+
+ if (_data) {
+ free (_data);
+ }
+
+ if (_events) {
+ free (_events);
+ }
+
+ _size = 0;
+ _capacity = size;
+
+#ifdef NO_POSIX_MEMALIGN
+ _events = (MIDI::Event *) malloc(sizeof(MIDI::Event) * _capacity);
+ _data = (uint8_t *) malloc(sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
+#else
+ posix_memalign((void**)&_events, CPU_CACHE_ALIGN, sizeof(MIDI::Event) * _capacity);
+ posix_memalign((void**)&_data, CPU_CACHE_ALIGN, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
+#endif
+ assert(_data);
+ assert(_events);
+}
+
+void
+MidiBuffer::copy(const MidiBuffer& copy)
+{
+ assert(_capacity >= copy._capacity);
+ _size = 0;
+
+ for (size_t i = 0; i < copy.size(); ++i)
+ push_back(copy[i]);
+}
+
+
+/** Read events from @a src starting at time @a offset into the START of this buffer, for
+ * time direction @a nframes. Relative time, where 0 = start of buffer.
+ *
+ * Note that offset and nframes refer to sample time, NOT buffer offsets or event counts.
+ */
+void
+MidiBuffer::read_from(const Buffer& src, nframes_t nframes, nframes_t offset)
+{
+ assert(src.type() == DataType::MIDI);
+ assert(&src != this);
+
+ const MidiBuffer& msrc = (MidiBuffer&)src;
+
+ assert(_capacity >= msrc.size());
+
+ clear();
+ assert(_size == 0);
+
+ // FIXME: slow
+ for (size_t i=0; i < msrc.size(); ++i) {
+ const MIDI::Event& ev = msrc[i];
+ if (ev.time() >= offset && ev.time() < offset+nframes) {
+ //cout << "MidiBuffer::read_from got event, " << int(ev.type()) << " time: " << ev.time() << " buffer size: " << _size << endl;
+ push_back(ev);
+ } else {
+ cerr << "MidiBuffer event out of range, " << ev.time() << endl;
+ }
+ }
+
+ _silent = src.silent();
+}
+
+
+/** Push an event into the buffer.
+ *
+ * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
+ * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
+ * Realtime safe.
+ * @return false if operation failed (not enough room)
+ */
+bool
+MidiBuffer::push_back(const MIDI::Event& ev)
+{
+ if (_size == _capacity)
+ return false;
+
+ uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
+
+ memcpy(write_loc, ev.buffer(), ev.size());
+ _events[_size] = ev;
+ _events[_size].set_buffer(ev.size(), write_loc, false);
+ ++_size;
+
+ //cerr << "MidiBuffer: pushed, size = " << _size << endl;
+
+ _silent = false;
+
+ return true;
+}
+
+
+/** Push an event into the buffer.
+ *
+ * Note that the raw MIDI pointed to by ev will be COPIED and unmodified.
+ * That is, the caller still owns it, if it needs freeing it's Not My Problem(TM).
+ * Realtime safe.
+ * @return false if operation failed (not enough room)
+ */
+bool
+MidiBuffer::push_back(const jack_midi_event_t& ev)
+{
+ if (_size == _capacity)
+ return false;
+
+ uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
+
+ memcpy(write_loc, ev.buffer, ev.size);
+ _events[_size].time() = (double)ev.time;
+ _events[_size].set_buffer(ev.size, write_loc, false);
+ ++_size;
+
+ //cerr << "MidiBuffer: pushed, size = " << _size << endl;
+
+ _silent = false;
+
+ return true;
+}
+
+
+/** Reserve space for a new event in the buffer.
+ *
+ * This call is for copying MIDI directly into the buffer, the data location
+ * (of sufficient size to write \a size bytes) is returned, or 0 on failure.
+ * This call MUST be immediately followed by a write to the returned data
+ * location, or the buffer will be corrupted and very nasty things will happen.
+ */
+uint8_t*
+MidiBuffer::reserve(double time, size_t size)
+{
+ if (size > MAX_EVENT_SIZE) {
+ cerr << "WARNING: Failed to reserve " << size << " bytes for event";
+ return 0;
+ }
+
+ if (_size == _capacity)
+ return 0;
+
+ uint8_t* const write_loc = _data + (_size * MAX_EVENT_SIZE);
+
+ _events[_size].time() = time;
+ _events[_size].set_buffer(size, write_loc, false);
+ ++_size;
+
+ //cerr << "MidiBuffer: reserved, size = " << _size << endl;
+
+ _silent = false;
+
+ return write_loc;
+}
+
+
+void
+MidiBuffer::silence(nframes_t dur, nframes_t offset)
+{
+ // FIXME use parameters
+ if (offset != 0)
+ cerr << "WARNING: MidiBuffer::silence w/ offset != 0 (not implemented)" << endl;
+
+ memset(_events, 0, sizeof(MIDI::Event) * _capacity);
+ memset(_data, 0, sizeof(uint8_t) * _capacity * MAX_EVENT_SIZE);
+ _size = 0;
+ _silent = true;
+}
+
+
+/** Clear, and merge \a a and \a b into this buffer.
+ *
+ * FIXME: This is slow.
+ *
+ * \return true if complete merge was successful
+ */
+bool
+MidiBuffer::merge(const MidiBuffer& a, const MidiBuffer& b)
+{
+ _size = 0;
+
+ // Die if a merge isn't necessary as it's expensive
+ assert(a.size() > 0 && b.size() > 0);
+
+ size_t a_index = 0;
+ size_t b_index = 0;
+ size_t count = a.size() + b.size();
+
+ while (count > 0 && a_index < a.size() && b_index < b.size()) {
+
+ if (size() == capacity()) {
+ cerr << "WARNING: MIDI buffer overrun, events lost!" << endl;
+ return false;
+ }
+
+ if (a_index == a.size()) {
+ push_back(a[a_index]);
+ ++a_index;
+ } else if (b_index == b.size()) {
+ push_back(b[b_index]);
+ ++b_index;
+ } else {
+ const MIDI::Event& a_ev = a[a_index];
+ const MIDI::Event& b_ev = b[b_index];
+
+ if (a_ev.time() <= b_ev.time()) {
+ push_back(a_ev);
+ ++a_index;
+ } else {
+ push_back(b_ev);
+ ++b_index;
+ }
+ }
+
+ --count;
+ }
+
+ return true;
+}
+
diff --git a/libs/ardour/midi_diskstream.cc b/libs/ardour/midi_diskstream.cc
new file mode 100644
index 0000000000..5b4716d51e
--- /dev/null
+++ b/libs/ardour/midi_diskstream.cc
@@ -0,0 +1,1461 @@
+/*
+ Copyright (C) 2000-2003 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <fstream>
+#include <cstdio>
+#include <unistd.h>
+#include <cmath>
+#include <cerrno>
+#include <string>
+#include <climits>
+#include <fcntl.h>
+#include <cstdlib>
+#include <ctime>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <pbd/error.h>
+#include <pbd/basename.h>
+#include <glibmm/thread.h>
+#include <pbd/xml++.h>
+#include <pbd/memento_command.h>
+#include <pbd/enumwriter.h>
+
+#include <ardour/ardour.h>
+#include <ardour/audioengine.h>
+#include <ardour/midi_diskstream.h>
+#include <ardour/utils.h>
+#include <ardour/configuration.h>
+#include <ardour/smf_source.h>
+#include <ardour/send.h>
+#include <ardour/region_factory.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/playlist_factory.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/midi_region.h>
+#include <ardour/midi_port.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+MidiDiskstream::MidiDiskstream (Session &sess, const string &name, Diskstream::Flag flag)
+ : Diskstream(sess, name, flag)
+ , _playback_buf(0)
+ , _capture_buf(0)
+ , _source_port(0)
+ , _last_flush_frame(0)
+ , _note_mode(Sustained)
+{
+ /* prevent any write sources from being created */
+
+ in_set_state = true;
+
+ init(flag);
+ use_new_playlist ();
+
+ in_set_state = false;
+
+ assert(!destructive());
+}
+
+MidiDiskstream::MidiDiskstream (Session& sess, const XMLNode& node)
+ : Diskstream(sess, node)
+ , _playback_buf(0)
+ , _capture_buf(0)
+ , _source_port(0)
+ , _last_flush_frame(0)
+ , _note_mode(Sustained)
+{
+ in_set_state = true;
+ init (Recordable);
+
+ if (set_state (node)) {
+ in_set_state = false;
+ throw failed_constructor();
+ }
+
+ in_set_state = false;
+
+ if (destructive()) {
+ use_destructive_playlist ();
+ }
+}
+
+void
+MidiDiskstream::init (Diskstream::Flag f)
+{
+ Diskstream::init(f);
+
+ /* there are no channels at this point, so these
+ two calls just get speed_buffer_size and wrap_buffer
+ size setup without duplicating their code.
+ */
+
+ set_block_size (_session.get_block_size());
+ allocate_temporary_buffers ();
+
+ _playback_buf = new MidiRingBuffer (_session.midi_diskstream_buffer_size());
+ _capture_buf = new MidiRingBuffer (_session.midi_diskstream_buffer_size());
+
+ _n_channels = ChanCount(DataType::MIDI, 1);
+
+ assert(recordable());
+}
+
+MidiDiskstream::~MidiDiskstream ()
+{
+ Glib::Mutex::Lock lm (state_lock);
+}
+
+
+void
+MidiDiskstream::non_realtime_locate (nframes_t position)
+{
+ assert(_write_source);
+ _write_source->set_timeline_position (position);
+ seek(position, false);
+}
+
+
+void
+MidiDiskstream::non_realtime_input_change ()
+{
+ {
+ Glib::Mutex::Lock lm (state_lock);
+
+ if (input_change_pending == NoChange) {
+ return;
+ }
+
+ if (input_change_pending & ConfigurationChanged) {
+ assert(_io->n_inputs() == _n_channels);
+ }
+
+ get_input_sources ();
+ set_capture_offset ();
+
+ if (first_input_change) {
+ set_align_style (_persistent_alignment_style);
+ first_input_change = false;
+ } else {
+ set_align_style_from_io ();
+ }
+
+ input_change_pending = NoChange;
+
+ /* implicit unlock */
+ }
+
+ /* reset capture files */
+
+ reset_write_sources (false);
+
+ /* now refill channel buffers */
+
+ if (speed() != 1.0f || speed() != -1.0f) {
+ seek ((nframes_t) (_session.transport_frame() * (double) speed()));
+ }
+ else {
+ seek (_session.transport_frame());
+ }
+
+ _last_flush_frame = _session.transport_frame();
+}
+
+void
+MidiDiskstream::get_input_sources ()
+{
+ uint32_t ni = _io->n_inputs().n_midi();
+
+ if (ni == 0) {
+ return;
+ }
+
+ // This is all we do for now at least
+ assert(ni == 1);
+
+ _source_port = _io->midi_input(0);
+
+ // do... stuff?
+}
+
+int
+MidiDiskstream::find_and_use_playlist (const string& name)
+{
+ boost::shared_ptr<MidiPlaylist> playlist;
+
+ if ((playlist = boost::dynamic_pointer_cast<MidiPlaylist> (_session.playlist_by_name (name))) == 0) {
+ playlist = boost::dynamic_pointer_cast<MidiPlaylist> (PlaylistFactory::create (DataType::MIDI, _session, name));
+ }
+
+ if (!playlist) {
+ error << string_compose(_("MidiDiskstream: Playlist \"%1\" isn't an midi playlist"), name) << endmsg;
+ return -1;
+ }
+
+ return use_playlist (playlist);
+}
+
+int
+MidiDiskstream::use_playlist (boost::shared_ptr<Playlist> playlist)
+{
+ assert(boost::dynamic_pointer_cast<MidiPlaylist>(playlist));
+
+ Diskstream::use_playlist(playlist);
+
+ return 0;
+}
+
+int
+MidiDiskstream::use_new_playlist ()
+{
+ string newname;
+ boost::shared_ptr<MidiPlaylist> playlist;
+
+ if (!in_set_state && destructive()) {
+ return 0;
+ }
+
+ if (_playlist) {
+ newname = Playlist::bump_name (_playlist->name(), _session);
+ } else {
+ newname = Playlist::bump_name (_name, _session);
+ }
+
+ if ((playlist = boost::dynamic_pointer_cast<MidiPlaylist> (PlaylistFactory::create (
+ DataType::MIDI, _session, newname, hidden()))) != 0) {
+
+ playlist->set_orig_diskstream_id (id());
+ return use_playlist (playlist);
+
+ } else {
+ return -1;
+ }
+}
+
+int
+MidiDiskstream::use_copy_playlist ()
+{
+ assert(midi_playlist());
+
+ if (destructive()) {
+ return 0;
+ }
+
+ if (_playlist == 0) {
+ error << string_compose(_("MidiDiskstream %1: there is no existing playlist to make a copy of!"), _name) << endmsg;
+ return -1;
+ }
+
+ string newname;
+ boost::shared_ptr<MidiPlaylist> playlist;
+
+ newname = Playlist::bump_name (_playlist->name(), _session);
+
+ if ((playlist = boost::dynamic_pointer_cast<MidiPlaylist>(PlaylistFactory::create (midi_playlist(), newname))) != 0) {
+ playlist->set_orig_diskstream_id (id());
+ return use_playlist (playlist);
+ } else {
+ return -1;
+ }
+}
+
+/** Overloaded from parent to die horribly
+ */
+int
+MidiDiskstream::set_destructive (bool yn)
+{
+ assert( ! destructive());
+ assert( ! yn);
+ return -1;
+}
+
+void
+MidiDiskstream::set_note_mode (NoteMode m)
+{
+ _note_mode = m;
+ midi_playlist()->set_note_mode(m);
+ if (_write_source && _write_source->model())
+ _write_source->model()->set_note_mode(m);
+}
+
+void
+MidiDiskstream::check_record_status (nframes_t transport_frame, nframes_t nframes, bool can_record)
+{
+ // FIXME: waaay too much code to duplicate (AudioDiskstream)
+
+ int possibly_recording;
+ int rolling;
+ int change;
+ const int transport_rolling = 0x4;
+ const int track_rec_enabled = 0x2;
+ const int global_rec_enabled = 0x1;
+
+ /* merge together the 3 factors that affect record status, and compute
+ what has changed.
+ */
+
+ rolling = _session.transport_speed() != 0.0f;
+ possibly_recording = (rolling << 2) | (record_enabled() << 1) | can_record;
+ change = possibly_recording ^ last_possibly_recording;
+
+ if (possibly_recording == last_possibly_recording) {
+ return;
+ }
+
+ /* change state */
+
+ /* if per-track or global rec-enable turned on while the other was already on, we've started recording */
+
+ if (((change & track_rec_enabled) && record_enabled() && (!(change & global_rec_enabled) && can_record)) ||
+ ((change & global_rec_enabled) && can_record && (!(change & track_rec_enabled) && record_enabled()))) {
+
+ /* starting to record: compute first+last frames */
+
+ first_recordable_frame = transport_frame + _capture_offset;
+ last_recordable_frame = max_frames;
+ capture_start_frame = transport_frame;
+
+ if (!(last_possibly_recording & transport_rolling) && (possibly_recording & transport_rolling)) {
+
+ /* was stopped, now rolling (and recording) */
+
+ if (_alignment_style == ExistingMaterial) {
+ first_recordable_frame += _session.worst_output_latency();
+ } else {
+ first_recordable_frame += _roll_delay;
+ }
+
+ } else {
+
+ /* was rolling, but record state changed */
+
+ if (_alignment_style == ExistingMaterial) {
+
+
+ if (!Config->get_punch_in()) {
+
+ /* manual punch in happens at the correct transport frame
+ because the user hit a button. but to get alignment correct
+ we have to back up the position of the new region to the
+ appropriate spot given the roll delay.
+ */
+
+ capture_start_frame -= _roll_delay;
+
+ /* XXX paul notes (august 2005): i don't know why
+ this is needed.
+ */
+
+ first_recordable_frame += _capture_offset;
+
+ } else {
+
+ /* autopunch toggles recording at the precise
+ transport frame, and then the DS waits
+ to start recording for a time that depends
+ on the output latency.
+ */
+
+ first_recordable_frame += _session.worst_output_latency();
+ }
+
+ } else {
+
+ if (Config->get_punch_in()) {
+ first_recordable_frame += _roll_delay;
+ } else {
+ capture_start_frame -= _roll_delay;
+ }
+ }
+
+ }
+
+ } else if (!record_enabled() || !can_record) {
+
+ /* stop recording */
+
+ last_recordable_frame = transport_frame + _capture_offset;
+
+ if (_alignment_style == ExistingMaterial) {
+ last_recordable_frame += _session.worst_output_latency();
+ } else {
+ last_recordable_frame += _roll_delay;
+ }
+ }
+
+ last_possibly_recording = possibly_recording;
+}
+
+int
+MidiDiskstream::process (nframes_t transport_frame, nframes_t nframes, nframes_t offset, bool can_record, bool rec_monitors_input)
+{
+ // FIXME: waay too much code to duplicate (AudioDiskstream::process)
+ int ret = -1;
+ nframes_t rec_offset = 0;
+ nframes_t rec_nframes = 0;
+ bool nominally_recording;
+ bool re = record_enabled ();
+ bool collect_playback = false;
+
+ /* if we've already processed the frames corresponding to this call,
+ just return. this allows multiple routes that are taking input
+ from this diskstream to call our ::process() method, but have
+ this stuff only happen once. more commonly, it allows both
+ the AudioTrack that is using this AudioDiskstream *and* the Session
+ to call process() without problems.
+ */
+
+ if (_processed) {
+ return 0;
+ }
+
+ commit_should_unlock = false;
+
+ check_record_status (transport_frame, nframes, can_record);
+
+ nominally_recording = (can_record && re);
+
+ if (nframes == 0) {
+ _processed = true;
+ return 0;
+ }
+
+ /* This lock is held until the end of ::commit, so these two functions
+ must always be called as a pair. The only exception is if this function
+ returns a non-zero value, in which case, ::commit should not be called.
+ */
+
+ // If we can't take the state lock return.
+ if (!state_lock.trylock()) {
+ return 1;
+ }
+ commit_should_unlock = true;
+ adjust_capture_position = 0;
+
+ if (nominally_recording || (_session.get_record_enabled() && Config->get_punch_in())) {
+ OverlapType ot;
+
+ ot = coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
+
+ switch (ot) {
+ case OverlapNone:
+ rec_nframes = 0;
+ break;
+
+ case OverlapInternal:
+ /* ---------- recrange
+ |---| transrange
+ */
+ rec_nframes = nframes;
+ rec_offset = 0;
+ break;
+
+ case OverlapStart:
+ /* |--------| recrange
+ -----| transrange
+ */
+ rec_nframes = transport_frame + nframes - first_recordable_frame;
+ if (rec_nframes) {
+ rec_offset = first_recordable_frame - transport_frame;
+ }
+ break;
+
+ case OverlapEnd:
+ /* |--------| recrange
+ |-------- transrange
+ */
+ rec_nframes = last_recordable_frame - transport_frame;
+ rec_offset = 0;
+ break;
+
+ case OverlapExternal:
+ /* |--------| recrange
+ -------------- transrange
+ */
+ rec_nframes = last_recordable_frame - last_recordable_frame;
+ rec_offset = first_recordable_frame - transport_frame;
+ break;
+ }
+
+ if (rec_nframes && !was_recording) {
+ capture_captured = 0;
+ was_recording = true;
+ }
+ }
+
+
+ if (can_record && !_last_capture_regions.empty()) {
+ _last_capture_regions.clear ();
+ }
+
+ if (nominally_recording || rec_nframes) {
+
+ assert(_source_port);
+
+ // Pump entire port buffer into the ring buffer (FIXME: split cycles?)
+ //_capture_buf->write(_source_port->get_midi_buffer(), transport_frame);
+ size_t num_events = _source_port->get_midi_buffer().size();
+ size_t to_write = std::min(_capture_buf->write_space(), num_events);
+
+ MidiBuffer::iterator port_iter = _source_port->get_midi_buffer().begin();
+
+ for (size_t i=0; i < to_write; ++i) {
+ const MIDI::Event& ev = *port_iter;
+ assert(ev.buffer());
+ _capture_buf->write(ev.time() + transport_frame, ev.size(), ev.buffer());
+ ++port_iter;
+ }
+
+ } else {
+
+ if (was_recording) {
+ finish_capture (rec_monitors_input);
+ }
+
+ }
+
+ if (rec_nframes) {
+
+ /* XXX XXX XXX XXX XXX XXX XXX XXX */
+
+ /* data will be written to disk */
+
+ if (rec_nframes == nframes && rec_offset == 0) {
+
+ playback_distance = nframes;
+ } else {
+
+ collect_playback = true;
+ }
+
+ adjust_capture_position = rec_nframes;
+
+ } else if (nominally_recording) {
+
+ /* can't do actual capture yet - waiting for latency effects to finish before we start*/
+
+ playback_distance = nframes;
+
+ } else {
+
+ collect_playback = true;
+ }
+
+ if (collect_playback) {
+
+ /* we're doing playback */
+
+ nframes_t necessary_samples;
+
+ /* no varispeed playback if we're recording, because the output .... TBD */
+
+ if (rec_nframes == 0 && _actual_speed != 1.0f) {
+ necessary_samples = (nframes_t) floor ((nframes * fabs (_actual_speed))) + 1;
+ } else {
+ necessary_samples = nframes;
+ }
+
+ // XXX XXX XXX XXX XXX XXX XXX XXX XXX XXX
+ // Write into playback buffer here, and whatnot?
+ //cerr << "MDS FIXME: collect playback" << endl;
+
+ }
+
+ ret = 0;
+
+ _processed = true;
+
+ if (ret) {
+
+ /* we're exiting with failure, so ::commit will not
+ be called. unlock the state lock.
+ */
+
+ commit_should_unlock = false;
+ state_lock.unlock();
+ }
+
+ return ret;
+}
+
+bool
+MidiDiskstream::commit (nframes_t nframes)
+{
+ bool need_butler = false;
+
+ if (_actual_speed < 0.0) {
+ playback_sample -= playback_distance;
+ } else {
+ playback_sample += playback_distance;
+ }
+
+ if (adjust_capture_position != 0) {
+ capture_captured += adjust_capture_position;
+ adjust_capture_position = 0;
+ }
+
+ /* what audio does:
+ * can't do this with midi: write space is in bytes, chunk_frames is in frames
+ if (_slaved) {
+ need_butler = _playback_buf->write_space() >= _playback_buf->capacity() / 2;
+ } else {
+ need_butler = _playback_buf->write_space() >= disk_io_chunk_frames
+ || _capture_buf->read_space() >= disk_io_chunk_frames;
+ }*/
+
+ /* we'll just keep the playback buffer full for now.
+ * this should be decreased to reduce edit latency */
+ need_butler = _playback_buf->write_space() >= _playback_buf->capacity() / 2;
+
+ if (commit_should_unlock) {
+ state_lock.unlock();
+ }
+
+ _processed = false;
+
+ return need_butler;
+}
+
+void
+MidiDiskstream::set_pending_overwrite (bool yn)
+{
+ /* called from audio thread, so we can use the read ptr and playback sample as we wish */
+
+ pending_overwrite = yn;
+
+ overwrite_frame = playback_sample;
+}
+
+int
+MidiDiskstream::overwrite_existing_buffers ()
+{
+ read(overwrite_frame, disk_io_chunk_frames, false);
+ overwrite_queued = false;
+ pending_overwrite = false;
+
+ return 0;
+}
+
+int
+MidiDiskstream::seek (nframes_t frame, bool complete_refill)
+{
+ Glib::Mutex::Lock lm (state_lock);
+ int ret = -1;
+
+ _playback_buf->reset();
+ _capture_buf->reset();
+
+ playback_sample = frame;
+ file_frame = frame;
+
+ if (complete_refill) {
+ while ((ret = do_refill_with_alloc ()) > 0) ;
+ } else {
+ ret = do_refill_with_alloc ();
+ }
+
+ return ret;
+}
+
+int
+MidiDiskstream::can_internal_playback_seek (nframes_t distance)
+{
+ if (_playback_buf->read_space() < distance) {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+int
+MidiDiskstream::internal_playback_seek (nframes_t distance)
+{
+ cerr << "MDS: internal_playback_seek " << distance << endl;
+
+ first_recordable_frame += distance;
+ playback_sample += distance;
+
+ return 0;
+}
+
+/** @a start is set to the new frame position (TIME) read up to */
+int
+MidiDiskstream::read (nframes_t& start, nframes_t dur, bool reversed)
+{
+ nframes_t this_read = 0;
+ bool reloop = false;
+ nframes_t loop_end = 0;
+ nframes_t loop_start = 0;
+ nframes_t loop_length = 0;
+ Location *loc = 0;
+
+ if (!reversed) {
+ /* Make the use of a Location atomic for this read operation.
+
+ Note: Locations don't get deleted, so all we care about
+ when I say "atomic" is that we are always pointing to
+ the same one and using a start/length values obtained
+ just once.
+ */
+
+ if ((loc = loop_location) != 0) {
+ loop_start = loc->start();
+ loop_end = loc->end();
+ loop_length = loop_end - loop_start;
+ }
+
+ /* if we are looping, ensure that the first frame we read is at the correct
+ position within the loop.
+ */
+
+ if (loc && start >= loop_end) {
+ //cerr << "start adjusted from " << start;
+ start = loop_start + ((start - loop_start) % loop_length);
+ //cerr << "to " << start << endl;
+ }
+ //cerr << "start is " << start << " loopstart: " << loop_start << " loopend: " << loop_end << endl;
+ }
+
+ while (dur) {
+
+ /* take any loop into account. we can't read past the end of the loop. */
+
+ if (loc && (loop_end - start < dur)) {
+ this_read = loop_end - start;
+ //cerr << "reloop true: thisread: " << this_read << " dur: " << dur << endl;
+ reloop = true;
+ } else {
+ reloop = false;
+ this_read = dur;
+ }
+
+ if (this_read == 0) {
+ break;
+ }
+
+ this_read = min(dur,this_read);
+
+ if (midi_playlist()->read (*_playback_buf, start, this_read) != this_read) {
+ error << string_compose(_("MidiDiskstream %1: cannot read %2 from playlist at frame %3"), _id, this_read,
+ start) << endmsg;
+ return -1;
+ }
+
+ _read_data_count = _playlist->read_data_count();
+
+ if (reversed) {
+
+ // Swap note ons with note offs here. etc?
+ // Fully reversing MIDI required look-ahead (well, behind) to find previous
+ // CC values etc. hard.
+
+ } else {
+
+ /* if we read to the end of the loop, go back to the beginning */
+
+ if (reloop) {
+ start = loop_start;
+ } else {
+ start += this_read;
+ }
+ }
+
+ dur -= this_read;
+ //offset += this_read;
+ }
+
+ return 0;
+}
+
+int
+MidiDiskstream::do_refill_with_alloc ()
+{
+ return do_refill();
+}
+
+int
+MidiDiskstream::do_refill ()
+{
+ int ret = 0;
+ size_t write_space = _playback_buf->write_space();
+ bool reversed = (_visible_speed * _session.transport_speed()) < 0.0f;
+
+ if (write_space == 0) {
+ return 0;
+ }
+
+ if (reversed) {
+ return 0;
+ }
+
+ /* at end: nothing to do */
+ if (file_frame == max_frames) {
+ return 0;
+ }
+
+ // At this point we...
+ assert(_playback_buf->write_space() > 0); // ... have something to write to, and
+ assert(file_frame <= max_frames); // ... something to write
+
+ nframes_t to_read = min(disk_io_chunk_frames, (max_frames - file_frame));
+
+ if (read (file_frame, to_read, reversed)) {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/** Flush pending data to disk.
+ *
+ * Important note: this function will write *AT MOST* disk_io_chunk_frames
+ * of data to disk. it will never write more than that. If it writes that
+ * much and there is more than that waiting to be written, it will return 1,
+ * otherwise 0 on success or -1 on failure.
+ *
+ * If there is less than disk_io_chunk_frames to be written, no data will be
+ * written at all unless @a force_flush is true.
+ */
+int
+MidiDiskstream::do_flush (Session::RunContext context, bool force_flush)
+{
+ uint32_t to_write;
+ int32_t ret = 0;
+ nframes_t total;
+
+ _write_data_count = 0;
+
+ if (_last_flush_frame > _session.transport_frame()
+ || _last_flush_frame < capture_start_frame) {
+ _last_flush_frame = _session.transport_frame();
+ }
+
+ total = _session.transport_frame() - _last_flush_frame;
+
+ if (total == 0 || (_capture_buf->read_space() == 0 && _session.transport_speed() == 0) || (total < disk_io_chunk_frames && !force_flush && was_recording)) {
+ goto out;
+ }
+
+ /* if there are 2+ chunks of disk i/o possible for
+ this track, let the caller know so that it can arrange
+ for us to be called again, ASAP.
+
+ if we are forcing a flush, then if there is* any* extra
+ work, let the caller know.
+
+ if we are no longer recording and there is any extra work,
+ let the caller know too.
+ */
+
+ if (total >= 2 * disk_io_chunk_frames || ((force_flush || !was_recording) && total > disk_io_chunk_frames)) {
+ ret = 1;
+ }
+
+ to_write = disk_io_chunk_frames;
+
+ assert(!destructive());
+
+ if (record_enabled() && _session.transport_frame() - _last_flush_frame > disk_io_chunk_frames) {
+ 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 {
+ _last_flush_frame = _session.transport_frame();
+ }
+ }
+
+out:
+ return ret;
+}
+
+void
+MidiDiskstream::transport_stopped (struct tm& when, time_t twhen, bool abort_capture)
+{
+ uint32_t buffer_position;
+ bool more_work = true;
+ int err = 0;
+ boost::shared_ptr<MidiRegion> region;
+ nframes_t total_capture;
+ MidiRegion::SourceList srcs;
+ MidiRegion::SourceList::iterator src;
+ vector<CaptureInfo*>::iterator ci;
+ bool mark_write_completed = false;
+
+ finish_capture (true);
+
+ /* butler is already stopped, but there may be work to do
+ to flush remaining data to disk.
+ */
+
+ while (more_work && !err) {
+ switch (do_flush (Session::TransportContext, true)) {
+ case 0:
+ more_work = false;
+ break;
+ case 1:
+ break;
+ case -1:
+ error << string_compose(_("MidiDiskstream \"%1\": cannot flush captured data to disk!"), _name) << endmsg;
+ err++;
+ }
+ }
+
+ /* XXX is there anything we can do if err != 0 ? */
+ Glib::Mutex::Lock lm (capture_info_lock);
+
+ if (capture_info.empty()) {
+ return;
+ }
+
+ if (abort_capture) {
+
+ if (_write_source) {
+
+ _write_source->mark_for_remove ();
+ _write_source->drop_references ();
+ _write_source.reset();
+ }
+
+ /* new source set up in "out" below */
+
+ } else {
+
+ assert(_write_source);
+
+ for (total_capture = 0, ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
+ total_capture += (*ci)->frames;
+ }
+
+ /* figure out the name for this take */
+
+ srcs.push_back (_write_source);
+ _write_source->set_timeline_position (capture_info.front()->start);
+ _write_source->set_captured_for (_name);
+
+ string whole_file_region_name;
+ whole_file_region_name = region_name_from_path (_write_source->name(), true);
+
+ /* Register a new region with the Session that
+ describes the entire source. Do this first
+ so that any sub-regions will obviously be
+ children of this one (later!)
+ */
+
+ try {
+ boost::shared_ptr<Region> rx (RegionFactory::create (srcs, _write_source->last_capture_start_frame(), total_capture,
+ whole_file_region_name,
+ 0, Region::Flag (Region::DefaultFlags|Region::Automatic|Region::WholeFile)));
+
+ region = boost::dynamic_pointer_cast<MidiRegion> (rx);
+ region->special_set_position (capture_info.front()->start);
+ }
+
+
+ catch (failed_constructor& err) {
+ error << string_compose(_("%1: could not create region for complete midi file"), _name) << endmsg;
+ /* XXX what now? */
+ }
+
+ _last_capture_regions.push_back (region);
+
+ // cerr << _name << ": there are " << capture_info.size() << " capture_info records\n";
+
+ XMLNode &before = _playlist->get_state();
+ _playlist->freeze ();
+
+ for (buffer_position = _write_source->last_capture_start_frame(), ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
+
+ string region_name;
+
+ _session.region_name (region_name, _write_source->name(), false);
+
+ // cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n";
+
+ try {
+ boost::shared_ptr<Region> rx (RegionFactory::create (srcs, buffer_position, (*ci)->frames, region_name));
+ region = boost::dynamic_pointer_cast<MidiRegion> (rx);
+ }
+
+ catch (failed_constructor& err) {
+ error << _("MidiDiskstream: could not create region for captured midi!") << endmsg;
+ continue; /* XXX is this OK? */
+ }
+
+ region->GoingAway.connect (bind (mem_fun (*this, &Diskstream::remove_region_from_last_capture), boost::weak_ptr<Region>(region)));
+
+ _last_capture_regions.push_back (region);
+
+ // cerr << "add new region, buffer position = " << buffer_position << " @ " << (*ci)->start << endl;
+
+ i_am_the_modifier++;
+ _playlist->add_region (region, (*ci)->start);
+ i_am_the_modifier--;
+
+ buffer_position += (*ci)->frames;
+ }
+
+ _playlist->thaw ();
+ XMLNode &after = _playlist->get_state();
+ _session.add_command (new MementoCommand<Playlist>(*_playlist, &before, &after));
+
+ }
+
+ mark_write_completed = true;
+
+ reset_write_sources (mark_write_completed);
+
+ for (ci = capture_info.begin(); ci != capture_info.end(); ++ci) {
+ delete *ci;
+ }
+
+ capture_info.clear ();
+ capture_start_frame = 0;
+}
+
+void
+MidiDiskstream::transport_looped (nframes_t transport_frame)
+{
+ if (was_recording) {
+
+ // adjust the capture length knowing that the data will be recorded to disk
+ // only necessary after the first loop where we're recording
+ if (capture_info.size() == 0) {
+ capture_captured += _capture_offset;
+
+ if (_alignment_style == ExistingMaterial) {
+ capture_captured += _session.worst_output_latency();
+ } else {
+ capture_captured += _roll_delay;
+ }
+ }
+
+ finish_capture (true);
+
+ // the next region will start recording via the normal mechanism
+ // we'll set the start position to the current transport pos
+ // no latency adjustment or capture offset needs to be made, as that already happened the first time
+ capture_start_frame = transport_frame;
+ first_recordable_frame = transport_frame; // mild lie
+ last_recordable_frame = max_frames;
+ was_recording = true;
+ }
+}
+
+void
+MidiDiskstream::finish_capture (bool rec_monitors_input)
+{
+ was_recording = false;
+
+ if (capture_captured == 0) {
+ return;
+ }
+
+ // Why must we destroy?
+ assert(!destructive());
+
+ CaptureInfo* ci = new CaptureInfo;
+
+ ci->start = capture_start_frame;
+ ci->frames = capture_captured;
+
+ /* XXX theoretical race condition here. Need atomic exchange ?
+ However, the circumstances when this is called right
+ now (either on record-disable or transport_stopped)
+ mean that no actual race exists. I think ...
+ We now have a capture_info_lock, but it is only to be used
+ to synchronize in the transport_stop and the capture info
+ accessors, so that invalidation will not occur (both non-realtime).
+ */
+
+ // cerr << "Finish capture, add new CI, " << ci->start << '+' << ci->frames << endl;
+
+ capture_info.push_back (ci);
+ capture_captured = 0;
+}
+
+void
+MidiDiskstream::set_record_enabled (bool yn)
+{
+ if (!recordable() || !_session.record_enabling_legal()) {
+ return;
+ }
+
+ assert(!destructive());
+
+ if (yn && _source_port == 0) {
+
+ /* pick up connections not initiated *from* the IO object
+ we're associated with.
+ */
+
+ get_input_sources ();
+ }
+
+ /* yes, i know that this not proof against race conditions, but its
+ good enough. i think.
+ */
+
+ if (record_enabled() != yn) {
+ if (yn) {
+ engage_record_enable ();
+ } else {
+ disengage_record_enable ();
+ }
+ }
+}
+
+void
+MidiDiskstream::engage_record_enable ()
+{
+ bool rolling = _session.transport_speed() != 0.0f;
+
+ g_atomic_int_set (&_record_enabled, 1);
+
+ if (_source_port && Config->get_monitoring_model() == HardwareMonitoring) {
+ _source_port->request_monitor_input (!(Config->get_auto_input() && rolling));
+ }
+
+ // FIXME: Why is this necessary? Isn't needed for AudioDiskstream...
+ if (!_write_source)
+ use_new_write_source();
+
+ _write_source->mark_streaming_midi_write_started (_note_mode, _session.transport_frame());
+
+ RecordEnableChanged (); /* EMIT SIGNAL */
+}
+
+void
+MidiDiskstream::disengage_record_enable ()
+{
+ g_atomic_int_set (&_record_enabled, 0);
+ if (_source_port && Config->get_monitoring_model() == HardwareMonitoring) {
+ if (_source_port) {
+ _source_port->request_monitor_input (false);
+ }
+ }
+
+ RecordEnableChanged (); /* EMIT SIGNAL */
+}
+
+XMLNode&
+MidiDiskstream::get_state ()
+{
+ XMLNode* node = new XMLNode ("MidiDiskstream");
+ char buf[64];
+ LocaleGuard lg (X_("POSIX"));
+
+ snprintf (buf, sizeof(buf), "0x%x", _flags);
+ node->add_property ("flags", buf);
+
+ node->add_property("channel-mode", enum_2_string(get_channel_mode()));
+
+ snprintf (buf, sizeof(buf), "0x%x", get_channel_mask());
+ node->add_property("channel-mask", buf);
+
+ node->add_property ("playlist", _playlist->name());
+
+ snprintf (buf, sizeof(buf), "%f", _visible_speed);
+ node->add_property ("speed", buf);
+
+ node->add_property("name", _name);
+ id().print(buf, sizeof(buf));
+ node->add_property("id", buf);
+
+ if (_write_source && _session.get_record_enabled()) {
+
+ XMLNode* cs_child = new XMLNode (X_("CapturingSources"));
+ XMLNode* cs_grandchild;
+
+ cs_grandchild = new XMLNode (X_("file"));
+ cs_grandchild->add_property (X_("path"), _write_source->path());
+ cs_child->add_child_nocopy (*cs_grandchild);
+
+ /* store the location where capture will start */
+
+ Location* pi;
+
+ if (Config->get_punch_in() && ((pi = _session.locations()->auto_punch_location()) != 0)) {
+ snprintf (buf, sizeof (buf), "%" PRIu32, pi->start());
+ } else {
+ snprintf (buf, sizeof (buf), "%" PRIu32, _session.transport_frame());
+ }
+
+ cs_child->add_property (X_("at"), buf);
+ node->add_child_nocopy (*cs_child);
+ }
+
+ if (_extra_xml) {
+ node->add_child_copy (*_extra_xml);
+ }
+
+ return* node;
+}
+
+int
+MidiDiskstream::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+ XMLNodeList nlist = node.children();
+ XMLNodeIterator niter;
+ uint32_t nchans = 1;
+ XMLNode* capture_pending_node = 0;
+ LocaleGuard lg (X_("POSIX"));
+
+ in_set_state = true;
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ /*if ((*niter)->name() == IO::state_node_name) {
+ deprecated_io_node = new XMLNode (**niter);
+ }*/
+ assert ((*niter)->name() != IO::state_node_name);
+
+ if ((*niter)->name() == X_("CapturingSources")) {
+ capture_pending_node = *niter;
+ }
+ }
+
+ /* prevent write sources from being created */
+
+ in_set_state = true;
+
+ if ((prop = node.property ("name")) != 0) {
+ _name = prop->value();
+ }
+
+ if ((prop = node.property ("id")) != 0) {
+ _id = prop->value ();
+ }
+
+ if ((prop = node.property ("flags")) != 0) {
+ _flags = Flag (string_2_enum (prop->value(), _flags));
+ }
+
+ ChannelMode channel_mode = AllChannels;
+ if ((prop = node.property ("channel-mode")) != 0) {
+ channel_mode = ChannelMode (string_2_enum(prop->value(), channel_mode));
+ }
+
+ unsigned int channel_mask = 0xFFFF;
+ if ((prop = node.property ("channel-mask")) != 0) {
+ sscanf (prop->value().c_str(), "0x%x", &channel_mask);
+ if (channel_mask & (~0xFFFF)) {
+ warning << _("MidiDiskstream: XML property channel-mask out of range") << endmsg;
+ }
+ }
+
+ set_channel_mode(channel_mode, channel_mask);
+
+ if ((prop = node.property ("channels")) != 0) {
+ nchans = atoi (prop->value().c_str());
+ }
+
+ if ((prop = node.property ("playlist")) == 0) {
+ return -1;
+ }
+
+ {
+ bool had_playlist = (_playlist != 0);
+
+ if (find_and_use_playlist (prop->value())) {
+ return -1;
+ }
+
+ if (!had_playlist) {
+ _playlist->set_orig_diskstream_id (_id);
+ }
+
+ if (capture_pending_node) {
+ use_pending_capture_data (*capture_pending_node);
+ }
+
+ }
+
+ if ((prop = node.property ("speed")) != 0) {
+ double sp = atof (prop->value().c_str());
+
+ if (realtime_set_speed (sp, false)) {
+ non_realtime_set_speed ();
+ }
+ }
+
+ in_set_state = false;
+
+ /* make sure this is clear before we do anything else */
+
+ // FIXME?
+ //_capturing_source = 0;
+
+ /* write sources are handled when we handle the input set
+ up of the IO that owns this DS (::non_realtime_input_change())
+ */
+
+ in_set_state = false;
+
+ return 0;
+}
+
+int
+MidiDiskstream::use_new_write_source (uint32_t n)
+{
+ if (!recordable()) {
+ return 1;
+ }
+
+ assert(n == 0);
+
+ if (_write_source) {
+
+ if (_write_source->is_empty ()) {
+ _write_source->mark_for_remove ();
+ _write_source.reset();
+ } else {
+ _write_source.reset();
+ }
+ }
+
+ try {
+ _write_source = boost::dynamic_pointer_cast<SMFSource>(_session.create_midi_source_for_session (*this));
+ if (!_write_source) {
+ throw failed_constructor();
+ }
+ }
+
+ catch (failed_constructor &err) {
+ error << string_compose (_("%1:%2 new capture file not initialized correctly"), _name, n) << endmsg;
+ _write_source.reset();
+ return -1;
+ }
+
+ _write_source->set_allow_remove_if_empty (true);
+
+ return 0;
+}
+
+void
+MidiDiskstream::reset_write_sources (bool mark_write_complete, bool force)
+{
+ if (!recordable()) {
+ return;
+ }
+
+ if (_write_source && mark_write_complete) {
+ _write_source->mark_streaming_write_completed ();
+ }
+
+ use_new_write_source (0);
+
+ if (record_enabled()) {
+ //_capturing_sources.push_back (_write_source);
+ }
+}
+
+int
+MidiDiskstream::rename_write_sources ()
+{
+ if (_write_source != 0) {
+ _write_source->set_source_name (_name, destructive());
+ /* XXX what to do if this fails ? */
+ }
+ return 0;
+}
+
+void
+MidiDiskstream::set_block_size (nframes_t nframes)
+{
+}
+
+void
+MidiDiskstream::allocate_temporary_buffers ()
+{
+}
+
+void
+MidiDiskstream::monitor_input (bool yn)
+{
+ if (_source_port)
+ _source_port->request_monitor_input (yn);
+ else
+ cerr << "MidiDiskstream NO SOURCE PORT TO MONITOR\n";
+}
+
+void
+MidiDiskstream::set_align_style_from_io ()
+{
+ bool have_physical = false;
+
+ if (_io == 0) {
+ return;
+ }
+
+ get_input_sources ();
+
+ if (_source_port && _source_port->flags() & JackPortIsPhysical) {
+ have_physical = true;
+ }
+
+ if (have_physical) {
+ set_align_style (ExistingMaterial);
+ } else {
+ set_align_style (CaptureTime);
+ }
+}
+
+
+float
+MidiDiskstream::playback_buffer_load () const
+{
+ return (float) ((double) _playback_buf->read_space()/
+ (double) _playback_buf->capacity());
+}
+
+float
+MidiDiskstream::capture_buffer_load () const
+{
+ return (float) ((double) _capture_buf->write_space()/
+ (double) _capture_buf->capacity());
+}
+
+
+int
+MidiDiskstream::use_pending_capture_data (XMLNode& node)
+{
+ return 0;
+}
+
+/** Writes playback events in the given range to \a dst, translating time stamps
+ * so that an event at \a start has time = 0
+ */
+void
+MidiDiskstream::get_playback(MidiBuffer& dst, nframes_t start, nframes_t end)
+{
+ dst.clear();
+ assert(dst.size() == 0);
+
+ // Reverse. ... We just don't do reverse, ok? Back off.
+ if (end <= start) {
+ return;
+ }
+
+ // Translates stamps to be relative to start
+ _playback_buf->read(dst, start, end);
+}
diff --git a/libs/ardour/midi_model.cc b/libs/ardour/midi_model.cc
new file mode 100644
index 0000000000..b21bf38873
--- /dev/null
+++ b/libs/ardour/midi_model.cc
@@ -0,0 +1,953 @@
+/*
+ Copyright (C) 2007 Paul Davis
+ Written by Dave Robillard, 2007
+
+ 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.
+
+ */
+
+#define __STDC_LIMIT_MACROS 1
+
+#include <iostream>
+#include <algorithm>
+#include <stdexcept>
+#include <stdint.h>
+#include <pbd/enumwriter.h>
+#include <midi++/events.h>
+
+#include <ardour/midi_model.h>
+#include <ardour/midi_source.h>
+#include <ardour/types.h>
+#include <ardour/session.h>
+
+using namespace std;
+using namespace ARDOUR;
+
+void MidiModel::write_lock() {
+ _lock.writer_lock();
+ _automation_lock.lock();
+}
+
+void MidiModel::write_unlock() {
+ _lock.writer_unlock();
+ _automation_lock.unlock();
+}
+
+void MidiModel::read_lock() const {
+ _lock.reader_lock();
+ /*_automation_lock.lock();*/
+}
+
+void MidiModel::read_unlock() const {
+ _lock.reader_unlock();
+ /*_automation_lock.unlock();*/
+}
+
+// Read iterator (const_iterator)
+
+MidiModel::const_iterator::const_iterator(const MidiModel& model, double t)
+ : _model(&model)
+ , _is_end( (t == DBL_MAX) || model.empty() )
+ , _locked( !_is_end )
+{
+ //cerr << "Created MIDI iterator @ " << t << " (is end: " << _is_end << ")" << endl;
+
+ if (_is_end) {
+ return;
+ }
+
+ model.read_lock();
+
+ _note_iter = model.notes().end();
+ // find first note which begins after t
+ for (MidiModel::Notes::const_iterator i = model.notes().begin(); i != model.notes().end(); ++i) {
+ if ((*i)->time() >= t) {
+ _note_iter = i;
+ break;
+ }
+ }
+
+ MidiControlIterator earliest_control(boost::shared_ptr<AutomationList>(), DBL_MAX, 0.0);
+
+ _control_iters.reserve(model.controls().size());
+
+ // find the earliest control event available
+ for (Automatable::Controls::const_iterator i = model.controls().begin();
+ i != model.controls().end(); ++i) {
+
+ assert(
+ i->first.type() == MidiCCAutomation ||
+ i->first.type() == MidiPgmChangeAutomation ||
+ i->first.type() == MidiPitchBenderAutomation ||
+ i->first.type() == MidiChannelAftertouchAutomation);
+
+ double x, y;
+ bool ret = i->second->list()->rt_safe_earliest_event_unlocked(t, DBL_MAX, x, y);
+ if (!ret) {
+ //cerr << "MIDI Iterator: CC " << i->first.id() << " (size " << i->second->list()->size()
+ // << ") has no events past " << t << endl;
+ continue;
+ }
+
+ assert(x >= 0);
+
+ if (y < i->first.min() || y > i->first.max()) {
+ cerr << "ERROR: Controller (" << i->first.to_string() << ") value '" << y
+ << "' out of range [" << i->first.min() << "," << i->first.max()
+ << "], event ignored" << endl;
+ continue;
+ }
+
+ const MidiControlIterator new_iter(i->second->list(), x, y);
+
+ //cerr << "MIDI Iterator: CC " << i->first.id() << " added (" << x << ", " << y << ")" << endl;
+ _control_iters.push_back(new_iter);
+
+ // if the x of the current control is less than earliest_control
+ // we have a new earliest_control
+ if (x < earliest_control.x) {
+ earliest_control = new_iter;
+ _control_iter = _control_iters.end();
+ --_control_iter;
+ // now _control_iter points to the last Element in _control_iters
+ }
+ }
+
+ if (_note_iter != model.notes().end()) {
+ _event = boost::shared_ptr<MIDI::Event>(new MIDI::Event((*_note_iter)->on_event(), true));
+ }
+
+ double time = DBL_MAX;
+ // in case we have no notes in the region, we still want to get controller messages
+ if (_event.get()) {
+ time = _event->time();
+ // if the note is going to make it this turn, advance _note_iter
+ if (earliest_control.x > time) {
+ _active_notes.push(*_note_iter);
+ ++_note_iter;
+ }
+ }
+
+ // <=, because we probably would want to send control events first
+ if (earliest_control.automation_list.get() && earliest_control.x <= time) {
+ model.control_to_midi_event(_event, earliest_control);
+ } else {
+ _control_iter = _control_iters.end();
+ }
+
+ if ( (! _event.get()) || _event->size() == 0) {
+ //cerr << "Created MIDI iterator @ " << t << " is at end." << endl;
+ _is_end = true;
+
+ // eliminate possible race condition here (ugly)
+ static Glib::Mutex mutex;
+ Glib::Mutex::Lock lock(mutex);
+ if (_locked) {
+ _model->read_unlock();
+ _locked = false;
+ }
+ } else {
+ //printf("New MIDI Iterator = %X @ %lf\n", _event->type(), _event->time());
+ }
+
+ assert(_is_end || (_event->buffer() && _event->buffer()[0] != '\0'));
+}
+
+MidiModel::const_iterator::~const_iterator()
+{
+ if (_locked) {
+ _model->read_unlock();
+ }
+}
+
+const MidiModel::const_iterator& MidiModel::const_iterator::operator++()
+{
+ if (_is_end) {
+ throw std::logic_error("Attempt to iterate past end of MidiModel");
+ }
+
+ assert(_event->buffer() && _event->buffer()[0] != '\0');
+
+ /*cerr << "const_iterator::operator++: " << _event->to_string() << endl;*/
+
+ if (! (_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()) ) {
+ cerr << "FAILED event buffer: " << hex << int(_event->buffer()[0]) << int(_event->buffer()[1]) << int(_event->buffer()[2]) << endl;
+ }
+ assert((_event->is_note() || _event->is_cc() || _event->is_pgm_change() || _event->is_pitch_bender() || _event->is_channel_aftertouch()));
+
+ // Increment past current control event
+ if (!_event->is_note() && _control_iter != _control_iters.end() && _control_iter->automation_list.get()) {
+ double x = 0.0, y = 0.0;
+ const bool ret = _control_iter->automation_list->rt_safe_earliest_event_unlocked(
+ _control_iter->x, DBL_MAX, x, y, false);
+
+ if (ret) {
+ _control_iter->x = x;
+ _control_iter->y = y;
+ } else {
+ _control_iter->automation_list.reset();
+ _control_iter->x = DBL_MAX;
+ }
+ }
+
+ const std::vector<MidiControlIterator>::iterator old_control_iter = _control_iter;
+ _control_iter = _control_iters.begin();
+
+ // find the _control_iter with the earliest event time
+ for (std::vector<MidiControlIterator>::iterator i = _control_iters.begin();
+ i != _control_iters.end(); ++i) {
+ if (i->x < _control_iter->x) {
+ _control_iter = i;
+ }
+ }
+
+ enum Type {NIL, NOTE_ON, NOTE_OFF, AUTOMATION};
+
+ Type type = NIL;
+ double t = 0;
+
+ // Next earliest note on
+ if (_note_iter != _model->notes().end()) {
+ type = NOTE_ON;
+ t = (*_note_iter)->time();
+ }
+
+ // Use the next earliest note off iff it's earlier than the note on
+ if (_model->note_mode() == Sustained && (! _active_notes.empty())) {
+ if (type == NIL || _active_notes.top()->end_time() <= (*_note_iter)->time()) {
+ type = NOTE_OFF;
+ t = _active_notes.top()->end_time();
+ }
+ }
+
+ // Use the next earliest controller iff it's earlier than the note event
+ if (_control_iter != _control_iters.end() && _control_iter->x != DBL_MAX /*&& _control_iter != old_control_iter */) {
+ if (type == NIL || _control_iter->x < t) {
+ type = AUTOMATION;
+ }
+ }
+
+ if (type == NOTE_ON) {
+ //cerr << "********** MIDI Iterator = note on" << endl;
+ *_event = (*_note_iter)->on_event();
+ cerr << "Event contents on note on: " << _event->to_string() << endl;
+ _active_notes.push(*_note_iter);
+ ++_note_iter;
+ } else if (type == NOTE_OFF) {
+ //cerr << "********** MIDI Iterator = note off" << endl;
+ *_event = _active_notes.top()->off_event();
+ _active_notes.pop();
+ } else if (type == AUTOMATION) {
+ //cerr << "********** MIDI Iterator = Automation" << endl;
+ _model->control_to_midi_event(_event, *_control_iter);
+ } else {
+ //cerr << "********** MIDI Iterator = End" << endl;
+ _is_end = true;
+ }
+
+ assert(_is_end || _event->size() > 0);
+
+ return *this;
+}
+
+bool MidiModel::const_iterator::operator==(const const_iterator& other) const
+{
+ if (_is_end || other._is_end) {
+ return (_is_end == other._is_end);
+ } else {
+ return (_event == other._event);
+ }
+}
+
+MidiModel::const_iterator& MidiModel::const_iterator::operator=(const const_iterator& other)
+{
+ if (_locked && _model != other._model) {
+ _model->read_unlock();
+ }
+
+ _model = other._model;
+ _active_notes = other._active_notes;
+ _is_end = other._is_end;
+ _locked = other._locked;
+ _note_iter = other._note_iter;
+ _control_iters = other._control_iters;
+ size_t index = other._control_iter - other._control_iters.begin();
+ _control_iter = _control_iters.begin() + index;
+
+ if (!_is_end) {
+ _event = boost::shared_ptr<MIDI::Event>(new MIDI::Event(*other._event, true));
+ }
+
+ return *this;
+}
+
+// MidiModel
+
+MidiModel::MidiModel(MidiSource *s, size_t size)
+ : Automatable(s->session(), "midi model")
+ , _notes(size)
+ , _note_mode(Sustained)
+ , _writing(false)
+ , _edited(false)
+ , _end_iter(*this, DBL_MAX)
+ , _next_read(UINT32_MAX)
+ , _read_iter(*this, DBL_MAX)
+ , _midi_source(s)
+{
+ assert(_end_iter._is_end);
+ assert( ! _end_iter._locked);
+}
+
+/** Read events in frame range \a start .. \a start+cnt into \a dst,
+ * adding \a stamp_offset to each event's timestamp.
+ * \return number of events written to \a dst
+ */
+size_t MidiModel::read(MidiRingBuffer& dst, nframes_t start, nframes_t nframes,
+ nframes_t stamp_offset, nframes_t negative_stamp_offset) const
+{
+ //cerr << this << " MM::read @ " << start << " frames: " << nframes << " -> " << stamp_offset << endl;
+ //cerr << this << " MM # notes: " << n_notes() << endl;
+
+ size_t read_events = 0;
+
+ if (start != _next_read) {
+ _read_iter = const_iterator(*this, (double)start);
+ //cerr << "Repositioning iterator from " << _next_read << " to " << start << endl;
+ } else {
+ //cerr << "Using cached iterator at " << _next_read << endl;
+ }
+
+ _next_read = start + nframes;
+
+ while (_read_iter != end() && _read_iter->time() < start + nframes) {
+ assert(_read_iter->size() > 0);
+ assert(_read_iter->buffer());
+ dst.write(_read_iter->time() + stamp_offset - negative_stamp_offset,
+ _read_iter->size(),
+ _read_iter->buffer());
+
+ /*cerr << this << " MidiModel::read event @ " << _read_iter->time()
+ << " type: " << hex << int(_read_iter->type()) << dec
+ << " note: " << int(_read_iter->note())
+ << " velocity: " << int(_read_iter->velocity())
+ << endl;*/
+
+ ++_read_iter;
+ ++read_events;
+ }
+
+ return read_events;
+}
+
+/** Write the controller event pointed to by \a iter to \a ev.
+ * The buffer of \a ev will be allocated or resized as necessary.
+ * \return true on success
+ */
+bool
+MidiModel::control_to_midi_event(boost::shared_ptr<MIDI::Event>& ev, const MidiControlIterator& iter) const
+{
+ assert(iter.automation_list.get());
+ if (!ev) {
+ ev = boost::shared_ptr<MIDI::Event>(new MIDI::Event(0, 3, NULL, true));
+ }
+
+ switch (iter.automation_list->parameter().type()) {
+ case MidiCCAutomation:
+ assert(iter.automation_list.get());
+ assert(iter.automation_list->parameter().channel() < 16);
+ assert(iter.automation_list->parameter().id() <= INT8_MAX);
+ assert(iter.y <= INT8_MAX);
+
+ ev->time() = iter.x;
+ ev->realloc(3);
+ ev->buffer()[0] = MIDI_CMD_CONTROL + iter.automation_list->parameter().channel();
+ ev->buffer()[1] = (uint8_t)iter.automation_list->parameter().id();
+ ev->buffer()[2] = (uint8_t)iter.y;
+ break;
+
+ case MidiPgmChangeAutomation:
+ assert(iter.automation_list.get());
+ assert(iter.automation_list->parameter().channel() < 16);
+ assert(iter.automation_list->parameter().id() == 0);
+ assert(iter.y <= INT8_MAX);
+
+ ev->time() = iter.x;
+ ev->realloc(2);
+ ev->buffer()[0] = MIDI_CMD_PGM_CHANGE + iter.automation_list->parameter().channel();
+ ev->buffer()[1] = (uint8_t)iter.y;
+ break;
+
+ case MidiPitchBenderAutomation:
+ assert(iter.automation_list.get());
+ assert(iter.automation_list->parameter().channel() < 16);
+ assert(iter.automation_list->parameter().id() == 0);
+ assert(iter.y < (1<<14));
+
+ ev->time() = iter.x;
+ ev->realloc(3);
+ ev->buffer()[0] = MIDI_CMD_BENDER + iter.automation_list->parameter().channel();
+ ev->buffer()[1] = uint16_t(iter.y) & 0x7F; // LSB
+ ev->buffer()[2] = (uint16_t(iter.y) >> 7) & 0x7F; // MSB
+ //cerr << "Pitch bender event: " << ev->to_string() << " value: " << ev->pitch_bender_value() << " original value: " << iter.y << std::endl;
+ break;
+
+ case MidiChannelAftertouchAutomation:
+ assert(iter.automation_list.get());
+ assert(iter.automation_list->parameter().channel() < 16);
+ assert(iter.automation_list->parameter().id() == 0);
+ assert(iter.y <= INT8_MAX);
+
+ ev->time() = iter.x;
+ ev->realloc(2);
+ ev->buffer()[0]
+ = MIDI_CMD_CHANNEL_PRESSURE + iter.automation_list->parameter().channel();
+ ev->buffer()[1] = (uint8_t)iter.y;
+ break;
+
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+
+/** Clear all events from the model.
+ */
+void MidiModel::clear()
+{
+ _lock.writer_lock();
+ _notes.clear();
+ clear_automation();
+ _next_read = 0;
+ _read_iter = end();
+ _lock.writer_unlock();
+}
+
+
+/** Begin a write of events to the model.
+ *
+ * If \a mode is Sustained, complete notes with duration are constructed as note
+ * on/off events are received. Otherwise (Percussive), only note on events are
+ * stored; note off events are discarded entirely and all contained notes will
+ * have duration 0.
+ */
+void MidiModel::start_write()
+{
+ //cerr << "MM " << this << " START WRITE, MODE = " << enum_2_string(_note_mode) << endl;
+ write_lock();
+ _writing = true;
+ for (int i = 0; i < 16; ++i)
+ _write_notes[i].clear();
+
+ _dirty_automations.clear();
+ write_unlock();
+}
+
+/** Finish a write of events to the model.
+ *
+ * If \a delete_stuck is true and the current mode is Sustained, note on events
+ * that were never resolved with a corresonding note off will be deleted.
+ * Otherwise they will remain as notes with duration 0.
+ */
+void MidiModel::end_write(bool delete_stuck)
+{
+ write_lock();
+ assert(_writing);
+
+ //cerr << "MM " << this << " END WRITE: " << _notes.size() << " NOTES\n";
+
+ if (_note_mode == Sustained && delete_stuck) {
+ for (Notes::iterator n = _notes.begin(); n != _notes.end() ;) {
+ if ((*n)->duration() == 0) {
+ cerr << "WARNING: Stuck note lost: " << (*n)->note() << endl;
+ n = _notes.erase(n);
+ // we have to break here because erase invalidates the iterator
+ break;
+ } else {
+ ++n;
+ }
+ }
+ }
+
+ for (int i = 0; i < 16; ++i) {
+ if (!_write_notes[i].empty()) {
+ cerr << "WARNING: MidiModel::end_write: Channel " << i << " has "
+ << _write_notes[i].size() << " stuck notes" << endl;
+ }
+ _write_notes[i].clear();
+ }
+
+ for (AutomationLists::const_iterator i = _dirty_automations.begin(); i != _dirty_automations.end(); ++i) {
+ (*i)->Dirty.emit();
+ (*i)->lookup_cache().left = -1;
+ (*i)->search_cache().left = -1;
+ }
+
+ _writing = false;
+ write_unlock();
+}
+
+/** Append \a in_event to model. NOT realtime safe.
+ *
+ * Timestamps of events in \a buf are expected to be relative to
+ * the start of this model (t=0) and MUST be monotonically increasing
+ * and MUST be >= the latest event currently in the model.
+ */
+void MidiModel::append(const MIDI::Event& ev)
+{
+ write_lock();
+ _edited = true;
+
+ assert(_notes.empty() || ev.time() >= _notes.back()->time());
+ assert(_writing);
+
+ if (ev.is_note_on()) {
+ append_note_on_unlocked(ev.channel(), ev.time(), ev.note(),
+ ev.velocity());
+ } else if (ev.is_note_off()) {
+ append_note_off_unlocked(ev.channel(), ev.time(), ev.note());
+ } else if (ev.is_cc()) {
+ append_automation_event_unlocked(MidiCCAutomation, ev.channel(),
+ ev.time(), ev.cc_number(), ev.cc_value());
+ } else if (ev.is_pgm_change()) {
+ append_automation_event_unlocked(MidiPgmChangeAutomation, ev.channel(),
+ ev.time(), ev.pgm_number(), 0);
+ } else if (ev.is_pitch_bender()) {
+ append_automation_event_unlocked(MidiPitchBenderAutomation,
+ ev.channel(), ev.time(), ev.pitch_bender_lsb(),
+ ev.pitch_bender_msb());
+ } else if (ev.is_channel_aftertouch()) {
+ append_automation_event_unlocked(MidiChannelAftertouchAutomation,
+ ev.channel(), ev.time(), ev.channel_aftertouch(), 0);
+ } else {
+ printf("WARNING: MidiModel: Unknown event type %X\n", ev.type());
+ }
+
+ write_unlock();
+}
+
+void MidiModel::append_note_on_unlocked(uint8_t chan, double time,
+ uint8_t note_num, uint8_t velocity)
+{
+ /*cerr << "MidiModel " << this << " chan " << (int)chan <<
+ " note " << (int)note_num << " on @ " << time << endl;*/
+
+ assert(note_num <= 127);
+ assert(chan < 16);
+ assert(_writing);
+ _edited = true;
+
+ boost::shared_ptr<Note> new_note(new Note(chan, time, 0, note_num, velocity));
+ _notes.push_back(new_note);
+ if (_note_mode == Sustained) {
+ //cerr << "MM Sustained: Appending active note on " << (unsigned)(uint8_t)note_num << endl;
+ _write_notes[chan].push_back(_notes.size() - 1);
+ }/* else {
+ cerr << "MM Percussive: NOT appending active note on" << endl;
+ }*/
+}
+
+void MidiModel::append_note_off_unlocked(uint8_t chan, double time,
+ uint8_t note_num)
+{
+ /*cerr << "MidiModel " << this << " chan " << (int)chan <<
+ " note " << (int)note_num << " off @ " << time << endl;*/
+
+ assert(note_num <= 127);
+ assert(chan < 16);
+ assert(_writing);
+ _edited = true;
+
+ if (_note_mode == Percussive) {
+ cerr << "MidiModel Ignoring note off (percussive mode)" << endl;
+ return;
+ }
+
+ /* FIXME: make _write_notes fixed size (127 noted) for speed */
+
+ /* FIXME: note off velocity for that one guy out there who actually has
+ * keys that send it */
+
+ bool resolved = false;
+
+ for (WriteNotes::iterator n = _write_notes[chan].begin(); n
+ != _write_notes[chan].end(); ++n) {
+ Note& note = *_notes[*n].get();
+ if (note.note() == note_num) {
+ assert(time >= note.time());
+ note.set_duration(time - note.time());
+ _write_notes[chan].erase(n);
+ //cerr << "MM resolved note, duration: " << note.duration() << endl;
+ resolved = true;
+ break;
+ }
+ }
+
+ if (!resolved) {
+ cerr << "MidiModel " << this << " spurious note off chan " << (int)chan
+ << ", note " << (int)note_num << " @ " << time << endl;
+ }
+}
+
+void MidiModel::append_automation_event_unlocked(AutomationType type,
+ uint8_t chan, double time, uint8_t first_byte, uint8_t second_byte)
+{
+ //cerr << "MidiModel " << this << " chan " << (int)chan <<
+ // " CC " << (int)number << " = " << (int)value << " @ " << time << endl;
+
+ assert(chan < 16);
+ assert(_writing);
+ _edited = true;
+ double value;
+
+ uint32_t id = 0;
+
+ switch (type) {
+ case MidiCCAutomation:
+ id = first_byte;
+ value = double(second_byte);
+ break;
+ case MidiChannelAftertouchAutomation:
+ case MidiPgmChangeAutomation:
+ id = 0;
+ value = double(first_byte);
+ break;
+ case MidiPitchBenderAutomation:
+ id = 0;
+ value = double((0x7F & second_byte) << 7 | (0x7F & first_byte));
+ break;
+ default:
+ assert(false);
+ }
+
+ Parameter param(type, id, chan);
+ boost::shared_ptr<AutomationControl> control = Automatable::control(param, true);
+ control->list()->rt_add(time, value);
+}
+
+void MidiModel::add_note_unlocked(const boost::shared_ptr<Note> note)
+{
+ //cerr << "MidiModel " << this << " add note " << (int)note.note() << " @ " << note.time() << endl;
+ _edited = true;
+ Notes::iterator i = upper_bound(_notes.begin(), _notes.end(), note,
+ note_time_comparator);
+ _notes.insert(i, note);
+}
+
+void MidiModel::remove_note_unlocked(const boost::shared_ptr<const Note> note)
+{
+ _edited = true;
+ //cerr << "MidiModel " << this << " remove note " << (int)note.note() << " @ " << note.time() << endl;
+ for (Notes::iterator n = _notes.begin(); n != _notes.end(); ++n) {
+ Note& _n = *(*n);
+ const Note& _note = *note;
+ // TODO: There is still the issue, that after restarting ardour
+ // persisted undo does not work, because of rounding errors in the
+ // event times after saving/restoring to/from MIDI files
+ /*cerr << "======================================= " << endl;
+ cerr << int(_n.note()) << "@" << int(_n.time()) << "[" << int(_n.channel()) << "] --" << int(_n.duration()) << "-- #" << int(_n.velocity()) << endl;
+ cerr << int(_note.note()) << "@" << int(_note.time()) << "[" << int(_note.channel()) << "] --" << int(_note.duration()) << "-- #" << int(_note.velocity()) << endl;
+ cerr << "Equal: " << bool(_n == _note) << endl;
+ cerr << endl << endl;*/
+ if (_n == _note) {
+ _notes.erase(n);
+ // we have to break here, because erase invalidates all iterators, ie. n itself
+ break;
+ }
+ }
+}
+
+/** Slow! for debugging only. */
+#ifndef NDEBUG
+bool MidiModel::is_sorted() const {
+ bool t = 0;
+ for (Notes::const_iterator n = _notes.begin(); n != _notes.end(); ++n)
+ if ((*n)->time() < t)
+ return false;
+ else
+ t = (*n)->time();
+
+ return true;
+}
+#endif
+
+/** Start a new command.
+ *
+ * This has no side-effects on the model or Session, the returned command
+ * can be held on to for as long as the caller wishes, or discarded without
+ * formality, until apply_command is called and ownership is taken.
+ */
+MidiModel::DeltaCommand* MidiModel::new_delta_command(const string name)
+{
+ DeltaCommand* cmd = new DeltaCommand(_midi_source->model(), name);
+ return cmd;
+}
+
+/** Apply a command.
+ *
+ * Ownership of cmd is taken, it must not be deleted by the caller.
+ * The command will constitute one item on the undo stack.
+ */
+void MidiModel::apply_command(Command* cmd)
+{
+ _session.begin_reversible_command(cmd->name());
+ (*cmd)();
+ assert(is_sorted());
+ _session.commit_reversible_command(cmd);
+ _edited = true;
+}
+
+// MidiEditCommand
+
+MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m,
+ const std::string& name)
+ : Command(name)
+ , _model(m)
+ , _name(name)
+{
+}
+
+MidiModel::DeltaCommand::DeltaCommand(boost::shared_ptr<MidiModel> m,
+ const XMLNode& node)
+ : _model(m)
+{
+ set_state(node);
+}
+
+void MidiModel::DeltaCommand::add(const boost::shared_ptr<Note> note)
+{
+ //cerr << "MEC: apply" << endl;
+ _removed_notes.remove(note);
+ _added_notes.push_back(note);
+}
+
+void MidiModel::DeltaCommand::remove(const boost::shared_ptr<Note> note)
+{
+ //cerr << "MEC: remove" << endl;
+ _added_notes.remove(note);
+ _removed_notes.push_back(note);
+}
+
+void MidiModel::DeltaCommand::operator()()
+{
+ // This could be made much faster by using a priority_queue for added and
+ // removed notes (or sort here), and doing a single iteration over _model
+
+ // Need to reset iterator to drop the read lock it holds, or we'll deadlock
+ const bool reset_iter = (_model->_read_iter.locked());
+ double iter_time = -1.0;
+
+ if (reset_iter) {
+ if (_model->_read_iter.get_event_pointer().get()) {
+ iter_time = _model->_read_iter->time();
+ } else {
+ cerr << "MidiModel::DeltaCommand::operator(): WARNING: _read_iter points to no event" << endl;
+ }
+ _model->_read_iter = _model->end(); // drop read lock
+ }
+
+ assert( ! _model->_read_iter.locked());
+
+ _model->write_lock();
+
+ for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i != _added_notes.end(); ++i)
+ _model->add_note_unlocked(*i);
+
+ for (std::list< boost::shared_ptr<Note> >::iterator i = _removed_notes.begin(); i != _removed_notes.end(); ++i)
+ _model->remove_note_unlocked(*i);
+
+ _model->write_unlock();
+
+ if (reset_iter && iter_time != -1.0) {
+ _model->_read_iter = const_iterator(*_model.get(), iter_time);
+ }
+
+ _model->ContentsChanged(); /* EMIT SIGNAL */
+}
+
+void MidiModel::DeltaCommand::undo()
+{
+ // This could be made much faster by using a priority_queue for added and
+ // removed notes (or sort here), and doing a single iteration over _model
+
+ // Need to reset iterator to drop the read lock it holds, or we'll deadlock
+ const bool reset_iter = (_model->_read_iter.locked());
+ double iter_time = -1.0;
+
+ if (reset_iter) {
+ if (_model->_read_iter.get_event_pointer().get()) {
+ iter_time = _model->_read_iter->time();
+ } else {
+ cerr << "MidiModel::DeltaCommand::undo(): WARNING: _read_iter points to no event" << endl;
+ }
+ _model->_read_iter = _model->end(); // drop read lock
+ }
+
+ assert( ! _model->_read_iter.locked());
+
+ _model->write_lock();
+
+ for (std::list< boost::shared_ptr<Note> >::iterator i = _added_notes.begin(); i
+ != _added_notes.end(); ++i)
+ _model->remove_note_unlocked(*i);
+
+ for (std::list< boost::shared_ptr<Note> >::iterator i =
+ _removed_notes.begin(); i != _removed_notes.end(); ++i)
+ _model->add_note_unlocked(*i);
+
+ _model->write_unlock();
+
+ if (reset_iter && iter_time != -1.0) {
+ _model->_read_iter = const_iterator(*_model.get(), iter_time);
+ }
+
+ _model->ContentsChanged(); /* EMIT SIGNAL */
+}
+
+XMLNode & MidiModel::DeltaCommand::marshal_note(const boost::shared_ptr<Note> note)
+{
+ XMLNode *xml_note = new XMLNode("note");
+ ostringstream note_str(ios::ate);
+ note_str << int(note->note());
+ xml_note->add_property("note", note_str.str());
+
+ ostringstream channel_str(ios::ate);
+ channel_str << int(note->channel());
+ xml_note->add_property("channel", channel_str.str());
+
+ ostringstream time_str(ios::ate);
+ time_str << int(note->time());
+ xml_note->add_property("time", time_str.str());
+
+ ostringstream duration_str(ios::ate);
+ duration_str <<(unsigned int) note->duration();
+ xml_note->add_property("duration", duration_str.str());
+
+ ostringstream velocity_str(ios::ate);
+ velocity_str << (unsigned int) note->velocity();
+ xml_note->add_property("velocity", velocity_str.str());
+
+ return *xml_note;
+}
+
+boost::shared_ptr<Note> MidiModel::DeltaCommand::unmarshal_note(XMLNode *xml_note)
+{
+ unsigned int note;
+ istringstream note_str(xml_note->property("note")->value());
+ note_str >> note;
+
+ unsigned int channel;
+ istringstream channel_str(xml_note->property("channel")->value());
+ channel_str >> channel;
+
+ unsigned int time;
+ istringstream time_str(xml_note->property("time")->value());
+ time_str >> time;
+
+ unsigned int duration;
+ istringstream duration_str(xml_note->property("duration")->value());
+ duration_str >> duration;
+
+ unsigned int velocity;
+ istringstream velocity_str(xml_note->property("velocity")->value());
+ velocity_str >> velocity;
+
+ boost::shared_ptr<Note> note_ptr(new Note(channel, time, duration, note, velocity));
+ return note_ptr;
+}
+
+#define ADDED_NOTES_ELEMENT "added_notes"
+#define REMOVED_NOTES_ELEMENT "removed_notes"
+#define DELTA_COMMAND_ELEMENT "DeltaCommand"
+
+int MidiModel::DeltaCommand::set_state(const XMLNode& delta_command)
+{
+ if (delta_command.name() != string(DELTA_COMMAND_ELEMENT)) {
+ return 1;
+ }
+
+ _added_notes.clear();
+ XMLNode *added_notes = delta_command.child(ADDED_NOTES_ELEMENT);
+ XMLNodeList notes = added_notes->children();
+ transform(notes.begin(), notes.end(), back_inserter(_added_notes),
+ sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
+
+ _removed_notes.clear();
+ XMLNode *removed_notes = delta_command.child(REMOVED_NOTES_ELEMENT);
+ notes = removed_notes->children();
+ transform(notes.begin(), notes.end(), back_inserter(_removed_notes),
+ sigc::mem_fun(*this, &DeltaCommand::unmarshal_note));
+
+ return 0;
+}
+
+XMLNode& MidiModel::DeltaCommand::get_state()
+{
+ XMLNode *delta_command = new XMLNode(DELTA_COMMAND_ELEMENT);
+ delta_command->add_property("midi_source", _model->midi_source()->id().to_s());
+
+ XMLNode *added_notes = delta_command->add_child(ADDED_NOTES_ELEMENT);
+ for_each(_added_notes.begin(), _added_notes.end(), sigc::compose(
+ sigc::mem_fun(*added_notes, &XMLNode::add_child_nocopy),
+ sigc::mem_fun(*this, &DeltaCommand::marshal_note)));
+
+ XMLNode *removed_notes = delta_command->add_child(REMOVED_NOTES_ELEMENT);
+ for_each(_removed_notes.begin(), _removed_notes.end(), sigc::compose(
+ sigc::mem_fun(*removed_notes, &XMLNode::add_child_nocopy),
+ sigc::mem_fun(*this, &DeltaCommand::marshal_note)));
+
+ return *delta_command;
+}
+
+struct EventTimeComparator {
+ typedef const MIDI::Event* value_type;
+ inline bool operator()(const MIDI::Event& a, const MIDI::Event& b) const {
+ return a.time() >= b.time();
+ }
+};
+
+/** Write the model to a MidiSource (i.e. save the model).
+ * This is different from manually using read to write to a source in that
+ * note off events are written regardless of the track mode. This is so the
+ * user can switch a recorded track (with note durations from some instrument)
+ * to percussive, save, reload, then switch it back to sustained without
+ * destroying the original note durations.
+ */
+bool MidiModel::write_to(boost::shared_ptr<MidiSource> source)
+{
+ read_lock();
+
+ const NoteMode old_note_mode = _note_mode;
+ _note_mode = Sustained;
+
+ for (const_iterator i = begin(); i != end(); ++i) {
+ source->append_event_unlocked(Frames, *i);
+ }
+
+ _note_mode = old_note_mode;
+
+ read_unlock();
+ _edited = false;
+
+ return true;
+}
+
+XMLNode& MidiModel::get_state()
+{
+ XMLNode *node = new XMLNode("MidiModel");
+ return *node;
+}
+
diff --git a/libs/ardour/midi_playlist.cc b/libs/ardour/midi_playlist.cc
new file mode 100644
index 0000000000..d258d49524
--- /dev/null
+++ b/libs/ardour/midi_playlist.cc
@@ -0,0 +1,318 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Dave Robillard, 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cassert>
+
+#include <algorithm>
+
+#include <stdlib.h>
+
+#include <sigc++/bind.h>
+
+#include <ardour/types.h>
+#include <ardour/configuration.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/midi_region.h>
+#include <ardour/session.h>
+#include <ardour/midi_ring_buffer.h>
+
+#include <pbd/error.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace sigc;
+using namespace std;
+
+MidiPlaylist::MidiPlaylist (Session& session, const XMLNode& node, bool hidden)
+ : Playlist (session, node, DataType::MIDI, hidden)
+ , _note_mode(Sustained)
+{
+ const XMLProperty* prop = node.property("type");
+ assert(prop && DataType(prop->value()) == DataType::MIDI);
+
+ in_set_state++;
+ set_state (node);
+ in_set_state--;
+}
+
+MidiPlaylist::MidiPlaylist (Session& session, string name, bool hidden)
+ : Playlist (session, name, DataType::MIDI, hidden)
+{
+}
+
+MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, string name, bool hidden)
+ : Playlist (other, name, hidden)
+{
+ throw; // nope
+
+ /*
+ list<Region*>::const_iterator in_o = other.regions.begin();
+ list<Region*>::iterator in_n = regions.begin();
+
+ while (in_o != other.regions.end()) {
+ MidiRegion *ar = dynamic_cast<MidiRegion *>( (*in_o) );
+
+ for (list<Crossfade *>::const_iterator xfades = other._crossfades.begin(); xfades != other._crossfades.end(); ++xfades) {
+ if ( &(*xfades)->in() == ar) {
+ // We found one! Now copy it!
+
+ list<Region*>::const_iterator out_o = other.regions.begin();
+ list<Region*>::const_iterator out_n = regions.begin();
+
+ while (out_o != other.regions.end()) {
+
+ MidiRegion *ar2 = dynamic_cast<MidiRegion *>( (*out_o) );
+
+ if ( &(*xfades)->out() == ar2) {
+ MidiRegion *in = dynamic_cast<MidiRegion*>( (*in_n) );
+ MidiRegion *out = dynamic_cast<MidiRegion*>( (*out_n) );
+ Crossfade *new_fade = new Crossfade( *(*xfades), in, out);
+ add_crossfade(*new_fade);
+ break;
+ }
+
+ out_o++;
+ out_n++;
+ }
+ // cerr << "HUH!? second region in the crossfade not found!" << endl;
+ }
+ }
+
+ in_o++;
+ in_n++;
+ }
+*/
+}
+
+MidiPlaylist::MidiPlaylist (boost::shared_ptr<const MidiPlaylist> other, nframes_t start, nframes_t dur, string name, bool hidden)
+ : Playlist (other, start, dur, name, hidden)
+{
+ /* this constructor does NOT notify others (session) */
+}
+
+MidiPlaylist::~MidiPlaylist ()
+{
+ GoingAway (); /* EMIT SIGNAL */
+
+ /* drop connections to signals */
+
+ notify_callbacks ();
+}
+
+struct RegionSortByLayer {
+ bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
+ return a->layer() < b->layer();
+ }
+};
+
+/** Returns the number of frames in time duration read (eg could be large when 0 events are read) */
+nframes_t
+MidiPlaylist::read (MidiRingBuffer& dst, nframes_t start,
+ nframes_t dur, unsigned chan_n)
+{
+ /* this function is never called from a realtime thread, so
+ its OK to block (for short intervals).
+ */
+
+ Glib::Mutex::Lock rm (region_lock);
+
+ nframes_t end = start + dur - 1;
+
+ _read_data_count = 0;
+
+ // relevent regions overlapping start <--> end
+ vector<boost::shared_ptr<Region> > regs;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ if ((*i)->coverage (start, end) != OverlapNone) {
+ regs.push_back(*i);
+ }
+ }
+
+ RegionSortByLayer layer_cmp;
+ sort(regs.begin(), regs.end(), layer_cmp);
+
+ for (vector<boost::shared_ptr<Region> >::iterator i = regs.begin(); i != regs.end(); ++i) {
+ // FIXME: ensure time is monotonic here?
+ boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*i);
+ if (mr) {
+ mr->read_at (dst, start, dur, chan_n, _note_mode);
+ _read_data_count += mr->read_data_count();
+ }
+ }
+
+ return dur;
+}
+
+
+void
+MidiPlaylist::remove_dependents (boost::shared_ptr<Region> region)
+{
+ /* MIDI regions have no dependents (crossfades) */
+}
+
+
+void
+MidiPlaylist::refresh_dependents (boost::shared_ptr<Region> r)
+{
+ /* MIDI regions have no dependents (crossfades) */
+}
+
+void
+MidiPlaylist::finalize_split_region (boost::shared_ptr<Region> original, boost::shared_ptr<Region> left, boost::shared_ptr<Region> right)
+{
+ /* No MIDI crossfading (yet?), so nothing to do here */
+}
+
+void
+MidiPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh)
+{
+ /* MIDI regions have no dependents (crossfades) */
+}
+
+
+int
+MidiPlaylist::set_state (const XMLNode& node)
+{
+ in_set_state++;
+ freeze ();
+
+ Playlist::set_state (node);
+
+ thaw();
+ in_set_state--;
+
+ return 0;
+}
+
+void
+MidiPlaylist::dump () const
+{
+ boost::shared_ptr<Region> r;
+
+ cerr << "Playlist \"" << _name << "\" " << endl
+ << regions.size() << " regions "
+ << endl;
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ r = *i;
+ cerr << " " << r->name() << " @ " << r << " ["
+ << r->start() << "+" << r->length()
+ << "] at "
+ << r->position()
+ << " on layer "
+ << r->layer ()
+ << endl;
+ }
+}
+
+bool
+MidiPlaylist::destroy_region (boost::shared_ptr<Region> region)
+{
+ boost::shared_ptr<MidiRegion> r = boost::dynamic_pointer_cast<MidiRegion> (region);
+ bool changed = false;
+
+ if (r == 0) {
+ PBD::fatal << _("programming error: non-midi Region passed to remove_overlap in midi playlist")
+ << endmsg;
+ /*NOTREACHED*/
+ return false;
+ }
+
+ {
+ RegionLock rlock (this);
+ RegionList::iterator i;
+ RegionList::iterator tmp;
+
+ for (i = regions.begin(); i != regions.end(); ) {
+
+ tmp = i;
+ ++tmp;
+
+ if ((*i) == region) {
+ regions.erase (i);
+ changed = true;
+ }
+
+ i = tmp;
+ }
+ }
+
+
+ if (changed) {
+ /* overload this, it normally means "removed", not destroyed */
+ notify_region_removed (region);
+ }
+
+ return changed;
+}
+
+set<Parameter>
+MidiPlaylist::contained_automation()
+{
+ /* this function is never called from a realtime thread, so
+ its OK to block (for short intervals).
+ */
+
+ Glib::Mutex::Lock rm (region_lock);
+
+ set<Parameter> ret;
+
+ for (RegionList::const_iterator r = regions.begin(); r != regions.end(); ++r) {
+ boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(*r);
+
+ for (Automatable::Controls::iterator c = mr->controls().begin();
+ c != mr->controls().end(); ++c) {
+ ret.insert(c->first);
+ }
+ }
+
+ return ret;
+}
+
+
+bool
+MidiPlaylist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
+{
+ if (in_flush || in_set_state) {
+ return false;
+ }
+
+ // Feeling rather uninterested today, but thanks for the heads up anyway!
+
+ Change our_interests = Change (/*MidiRegion::FadeInChanged|
+ MidiRegion::FadeOutChanged|
+ MidiRegion::FadeInActiveChanged|
+ MidiRegion::FadeOutActiveChanged|
+ MidiRegion::EnvelopeActiveChanged|
+ MidiRegion::ScaleAmplitudeChanged|
+ MidiRegion::EnvelopeChanged*/);
+ bool parent_wants_notify;
+
+ parent_wants_notify = Playlist::region_changed (what_changed, region);
+
+ if ((parent_wants_notify || (what_changed & our_interests))) {
+ notify_modified ();
+ }
+
+ return true;
+}
+
diff --git a/libs/ardour/midi_port.cc b/libs/ardour/midi_port.cc
new file mode 100644
index 0000000000..14f88f2ad5
--- /dev/null
+++ b/libs/ardour/midi_port.cc
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cassert>
+#include <iostream>
+
+#include <ardour/midi_port.h>
+#include <ardour/jack_midi_port.h>
+#include <ardour/data_type.h>
+
+using namespace ARDOUR;
+using namespace std;
+
+MidiPort::MidiPort (const std::string& name, Flags flags, bool external, nframes_t capacity)
+ : Port (name, flags)
+ , BaseMidiPort (name, flags)
+ , PortFacade (name, flags)
+{
+ // FIXME: size kludge (see BufferSet::ensure_buffers)
+ // Jack needs to tell us this
+ _buffer = new MidiBuffer (capacity * 8);
+
+ if (external) {
+ /* external ports use the same buffer for the jack port (_ext_port)
+ * and internal ports (this) */
+ _ext_port = new JackMidiPort (name, flags, _buffer);
+ Port::set_name (_ext_port->name());
+ } else {
+ /* internal ports just have a single buffer, no jack port */
+ _ext_port = 0;
+ set_name (name);
+ }
+
+ reset ();
+}
+
+MidiPort::~MidiPort()
+{
+ if (_ext_port) {
+ delete _ext_port;
+ _ext_port = 0;
+ }
+}
+
+void
+MidiPort::reset()
+{
+ BaseMidiPort::reset();
+
+ if (_ext_port) {
+ _ext_port->reset ();
+ }
+}
+
+void
+MidiPort::cycle_start (nframes_t nframes, nframes_t offset)
+{
+ if (_ext_port) {
+ _ext_port->cycle_start (nframes, offset);
+ }
+
+ if (_flags & IsInput) {
+
+ if (_ext_port) {
+
+ BaseMidiPort* mprt = dynamic_cast<BaseMidiPort*>(_ext_port);
+ assert(mprt);
+ assert(&mprt->get_midi_buffer() == _buffer);
+
+ if (!_connections.empty()) {
+ (*_mixdown) (_connections, _buffer, nframes, offset, false);
+ }
+
+ } else {
+
+ if (_connections.empty()) {
+ _buffer->silence (nframes, offset);
+ } else {
+ (*_mixdown) (_connections, _buffer, nframes, offset, true);
+ }
+ }
+
+ } else {
+
+ _buffer->silence (nframes, offset);
+ }
+}
+
+
+void
+MidiPort::cycle_end (nframes_t nframes, nframes_t offset)
+{
+ if (_ext_port) {
+ _ext_port->cycle_end (nframes, offset);
+ }
+}
+
diff --git a/libs/ardour/midi_region.cc b/libs/ardour/midi_region.cc
new file mode 100644
index 0000000000..e29fb1e659
--- /dev/null
+++ b/libs/ardour/midi_region.cc
@@ -0,0 +1,363 @@
+/*
+ Copyright (C) 2000-2006 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.
+
+ $Id: midiregion.cc 746 2006-08-02 02:44:23Z drobilla $
+*/
+
+#include <cmath>
+#include <climits>
+#include <cfloat>
+
+#include <set>
+
+#include <sigc++/bind.h>
+#include <sigc++/class_slot.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/basename.h>
+#include <pbd/xml++.h>
+#include <pbd/enumwriter.h>
+
+#include <ardour/midi_region.h>
+#include <ardour/session.h>
+#include <ardour/gain.h>
+#include <ardour/dB.h>
+#include <ardour/playlist.h>
+#include <ardour/midi_source.h>
+#include <ardour/types.h>
+#include <ardour/midi_ring_buffer.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+
+/** Basic MidiRegion constructor (one channel) */
+MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, nframes_t start, nframes_t length)
+ : Region (src, start, length, PBD::basename_nosuffix(src->name()), DataType::MIDI, 0, Region::Flag(Region::DefaultFlags|Region::External))
+{
+ assert(_name.find("/") == string::npos);
+ midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source));
+}
+
+/* Basic MidiRegion constructor (one channel) */
+MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Flag flags)
+ : Region (src, start, length, name, DataType::MIDI, layer, flags)
+{
+ assert(_name.find("/") == string::npos);
+ midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source));
+}
+
+/* Basic MidiRegion constructor (many channels) */
+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);
+ midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source));
+}
+
+
+/** Create a new MidiRegion, that is part of an existing one */
+MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
+ : Region (other, offset, length, name, layer, flags)
+{
+ assert(_name.find("/") == string::npos);
+ midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source));
+}
+
+MidiRegion::MidiRegion (boost::shared_ptr<const MidiRegion> other)
+ : Region (other)
+{
+ assert(_name.find("/") == string::npos);
+ midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source));
+}
+
+MidiRegion::MidiRegion (boost::shared_ptr<MidiSource> src, const XMLNode& node)
+ : Region (src, node)
+{
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+
+ midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source));
+ assert(_name.find("/") == string::npos);
+ assert(_type == DataType::MIDI);
+}
+
+MidiRegion::MidiRegion (const SourceList& srcs, const XMLNode& node)
+ : Region (srcs, node)
+{
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+
+ midi_source(0)->Switched.connect(sigc::mem_fun(this, &MidiRegion::switch_source));
+ assert(_name.find("/") == string::npos);
+ assert(_type == DataType::MIDI);
+}
+
+MidiRegion::~MidiRegion ()
+{
+}
+
+nframes_t
+MidiRegion::read_at (MidiRingBuffer& out, nframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode) const
+{
+ return _read_at (_sources, out, position, dur, chan_n, mode);
+}
+
+nframes_t
+MidiRegion::master_read_at (MidiRingBuffer& out, nframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode) const
+{
+ return _read_at (_master_sources, out, position, dur, chan_n, mode);
+}
+
+nframes_t
+MidiRegion::_read_at (const SourceList& srcs, MidiRingBuffer& dst, nframes_t position, nframes_t dur, uint32_t chan_n, NoteMode mode) const
+{
+ /*cerr << "MidiRegion " << _name << "._read_at(" << position << ") - "
+ << position << " duration: " << dur << endl;*/
+
+ nframes_t internal_offset = 0;
+ nframes_t src_offset = 0;
+ nframes_t to_read = 0;
+
+ /* precondition: caller has verified that we cover the desired section */
+
+ assert(chan_n == 0);
+
+ if (position < _position) {
+ internal_offset = 0;
+ src_offset = _position - position;
+ dur -= src_offset;
+ } else {
+ internal_offset = position - _position;
+ src_offset = 0;
+ }
+
+ if (internal_offset >= _length) {
+ return 0; /* read nothing */
+ }
+
+
+ if ((to_read = min (dur, _length - internal_offset)) == 0) {
+ return 0; /* read nothing */
+ }
+
+ // FIXME: non-opaque MIDI regions not yet supported
+ assert(opaque());
+
+ if (muted()) {
+ return 0; /* read nothing */
+ }
+
+ _read_data_count = 0;
+
+ boost::shared_ptr<MidiSource> src = midi_source(chan_n);
+ src->set_note_mode(mode);
+
+ nframes_t output_buffer_position = 0;
+ nframes_t negative_output_buffer_position = 0;
+ if (_position >= _start) {
+ // handle resizing of beginnings of regions correctly
+ output_buffer_position = _position - _start;
+ } else {
+ // when _start is greater than _position, we have to subtract
+ // _start from the note times in the midi source
+ negative_output_buffer_position = _start;
+ }
+
+ if (src->midi_read (
+ // the destination buffer
+ dst,
+ // where to start reading in the region
+ _start + internal_offset,
+ // how many bytes
+ to_read,
+ // the offset in the output buffer
+ output_buffer_position,
+ // what to substract from note times written in the output buffer
+ negative_output_buffer_position
+ ) != to_read) {
+ return 0; /* "read nothing" */
+ }
+
+ _read_data_count += src->read_data_count();
+
+ return to_read;
+}
+
+XMLNode&
+MidiRegion::state (bool full)
+{
+ XMLNode& node (Region::state (full));
+ char buf[64];
+ char buf2[64];
+ LocaleGuard lg (X_("POSIX"));
+
+ node.add_property ("flags", enum_2_string (_flags));
+
+ // XXX these should move into Region
+
+ for (uint32_t n=0; n < _sources.size(); ++n) {
+ snprintf (buf2, sizeof(buf2), "source-%d", n);
+ _sources[n]->id().print (buf, sizeof(buf));
+ node.add_property (buf2, buf);
+ }
+
+ for (uint32_t n=0; n < _master_sources.size(); ++n) {
+ snprintf (buf2, sizeof(buf2), "master-source-%d", n);
+ _master_sources[n]->id().print (buf, sizeof (buf));
+ node.add_property (buf2, buf);
+ }
+
+ if (full && _extra_xml) {
+ node.add_child_copy (*_extra_xml);
+ }
+
+ return node;
+}
+
+int
+MidiRegion::set_live_state (const XMLNode& node, Change& what_changed, bool send)
+{
+ const XMLProperty *prop;
+ LocaleGuard lg (X_("POSIX"));
+
+ Region::set_live_state (node, what_changed, false);
+
+ uint32_t old_flags = _flags;
+
+ if ((prop = node.property ("flags")) != 0) {
+ _flags = Flag (string_2_enum (prop->value(), _flags));
+
+ //_flags = Flag (strtol (prop->value().c_str(), (char **) 0, 16));
+
+ _flags = Flag (_flags & ~Region::LeftOfSplit);
+ _flags = Flag (_flags & ~Region::RightOfSplit);
+ }
+
+ if ((old_flags ^ _flags) & Muted) {
+ what_changed = Change (what_changed|MuteChanged);
+ }
+ if ((old_flags ^ _flags) & Opaque) {
+ what_changed = Change (what_changed|OpacityChanged);
+ }
+ if ((old_flags ^ _flags) & Locked) {
+ what_changed = Change (what_changed|LockChanged);
+ }
+
+ if (send) {
+ send_change (what_changed);
+ }
+
+ return 0;
+}
+
+int
+MidiRegion::set_state (const XMLNode& node)
+{
+ /* Region::set_state() calls the virtual set_live_state(),
+ which will get us back to AudioRegion::set_live_state()
+ to handle the relevant stuff.
+ */
+
+ return Region::set_state (node);
+}
+
+void
+MidiRegion::recompute_at_end ()
+{
+ /* our length has changed
+ * (non destructively) "chop" notes that pass the end boundary, to
+ * prevent stuck notes.
+ */
+}
+
+void
+MidiRegion::recompute_at_start ()
+{
+ /* as above, but the shift was from the front
+ * maybe bump currently active note's note-ons up so they sound here?
+ * that could be undesireable in certain situations though.. maybe
+ * remove the note entirely, including it's note off? something needs to
+ * be done to keep the played MIDI sane to avoid messing up voices of
+ * polyhonic things etc........
+ */
+}
+
+int
+MidiRegion::separate_by_channel (Session& session, vector<MidiRegion*>& v) const
+{
+ // Separate by MIDI channel? bit different from audio since this is separating based
+ // on the actual contained data and destructively modifies and creates new sources..
+
+#if 0
+ SourceList srcs;
+ string new_name;
+
+ for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
+
+ srcs.clear ();
+ srcs.push_back (*i);
+
+ /* generate a new name */
+
+ if (session.region_name (new_name, _name)) {
+ return -1;
+ }
+
+ /* create a copy with just one source */
+
+ v.push_back (new MidiRegion (srcs, _start, _length, new_name, _layer, _flags));
+ }
+#endif
+
+ // Actually, I would prefer not if that's alright
+ return -1;
+}
+
+int
+MidiRegion::exportme (ARDOUR::Session&, ARDOUR::ExportSpecification&)
+{
+ return -1;
+}
+
+boost::shared_ptr<MidiSource>
+MidiRegion::midi_source (uint32_t n) const
+{
+ // Guaranteed to succeed (use a static cast?)
+ return boost::dynamic_pointer_cast<MidiSource>(source(n));
+}
+
+
+void
+MidiRegion::switch_source(boost::shared_ptr<Source> src)
+{
+ boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
+ if (!msrc)
+ return;
+
+ // MIDI regions have only one source
+ _sources.clear();
+ _sources.push_back(msrc);
+
+ set_name(msrc->name());
+}
+
diff --git a/libs/ardour/midi_source.cc b/libs/ardour/midi_source.cc
new file mode 100644
index 0000000000..4e83413c13
--- /dev/null
+++ b/libs/ardour/midi_source.cc
@@ -0,0 +1,206 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Dave Robillard, 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <float.h>
+#include <cerrno>
+#include <ctime>
+#include <cmath>
+#include <iomanip>
+#include <algorithm>
+
+#include <pbd/xml++.h>
+#include <pbd/pthread_utils.h>
+#include <pbd/basename.h>
+
+#include <ardour/midi_source.h>
+#include <ardour/midi_ring_buffer.h>
+#include <ardour/session.h>
+#include <ardour/session_directory.h>
+#include <ardour/source_factory.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+sigc::signal<void,MidiSource *> MidiSource::MidiSourceCreated;
+
+MidiSource::MidiSource (Session& s, string name)
+ : Source (s, name, DataType::MIDI)
+ , _timeline_position(0)
+ , _model(new MidiModel(this))
+ , _writing (false)
+{
+ _read_data_count = 0;
+ _write_data_count = 0;
+}
+
+MidiSource::MidiSource (Session& s, const XMLNode& node)
+ : Source (s, node)
+ , _timeline_position(0)
+ , _model(new MidiModel(this))
+ , _writing (false)
+{
+ _read_data_count = 0;
+ _write_data_count = 0;
+
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+}
+
+MidiSource::~MidiSource ()
+{
+}
+
+XMLNode&
+MidiSource::get_state ()
+{
+ XMLNode& node (Source::get_state());
+
+ if (_captured_for.length()) {
+ node.add_property ("captured-for", _captured_for);
+ }
+
+ return node;
+}
+
+int
+MidiSource::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+
+ Source::set_state (node);
+
+ if ((prop = node.property ("captured-for")) != 0) {
+ _captured_for = prop->value();
+ }
+
+ return 0;
+}
+
+nframes_t
+MidiSource::midi_read (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const
+{
+ Glib::Mutex::Lock lm (_lock);
+ if (_model) {
+ //const size_t n_events =
+ _model->read(dst, start, cnt, stamp_offset, negative_stamp_offset);
+ //cout << "Read " << n_events << " events from model." << endl;
+ return cnt;
+ } else {
+ return read_unlocked (dst, start, cnt, stamp_offset, negative_stamp_offset);
+ }
+}
+
+nframes_t
+MidiSource::midi_write (MidiRingBuffer& dst, nframes_t cnt)
+{
+ Glib::Mutex::Lock lm (_lock);
+ return write_unlocked (dst, cnt);
+}
+
+bool
+MidiSource::file_changed (string path)
+{
+ struct stat stat_file;
+
+ int e1 = stat (path.c_str(), &stat_file);
+
+ return ( !e1 );
+}
+
+void
+MidiSource::mark_streaming_midi_write_started (NoteMode mode, nframes_t start_frame)
+{
+ set_timeline_position(start_frame); // why do I have a feeling this can break somehow...
+
+ if (_model) {
+ _model->set_note_mode(mode);
+ _model->start_write();
+ }
+
+ _writing = true;
+}
+
+void
+MidiSource::mark_streaming_write_started ()
+{
+ if (_model)
+ _model->start_write();
+
+ _writing = true;
+}
+
+void
+MidiSource::mark_streaming_write_completed ()
+{
+ if (_model)
+ _model->end_write(false); // FIXME: param?
+
+ _writing = false;
+}
+
+void
+MidiSource::session_saved()
+{
+ flush_header();
+ flush_footer();
+
+ if (_model && _model->edited()) {
+ string newname;
+ const string basename = PBD::basename_nosuffix(_name);
+ string::size_type last_dash = basename.find_last_of("-");
+ if (last_dash == string::npos || last_dash == basename.find_first_of("-")) {
+ newname = basename + "-1";
+ } else {
+ stringstream ss(basename.substr(last_dash+1));
+ unsigned write_count = 0;
+ ss >> write_count;
+ cerr << "WRITE COUNT: " << write_count << endl;
+ ++write_count; // start at 1
+ ss.clear();
+ ss << basename.substr(0, last_dash) << "-" << write_count;
+ newname = ss.str();
+ }
+
+ string newpath = _session.session_directory().midi_path().to_string() +"/"+ newname + ".mid";
+
+ boost::shared_ptr<MidiSource> newsrc = boost::dynamic_pointer_cast<MidiSource>(
+ SourceFactory::createWritable(DataType::MIDI, _session, newpath, 1, 0, true));
+
+ newsrc->set_timeline_position(_timeline_position);
+ _model->write_to(newsrc);
+
+ // cyclic dependency here, ugly :(
+ newsrc->set_model(_model);
+ _model->set_midi_source(newsrc.get());
+
+ newsrc->flush_header();
+ newsrc->flush_footer();
+
+ Switched.emit(newsrc);
+ }
+}
+
diff --git a/libs/ardour/midi_stretch.cc b/libs/ardour/midi_stretch.cc
new file mode 100644
index 0000000000..f33c58a0fd
--- /dev/null
+++ b/libs/ardour/midi_stretch.cc
@@ -0,0 +1,108 @@
+/*
+ Copyright (C) 2008 Paul Davis
+ Author: Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/error.h>
+
+#include <ardour/types.h>
+#include <ardour/midi_stretch.h>
+#include <ardour/session.h>
+#include <ardour/midi_region.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+MidiStretch::MidiStretch (Session& s, TimeFXRequest& req)
+ : Filter (s)
+ , _request (req)
+{
+}
+
+MidiStretch::~MidiStretch ()
+{
+}
+
+int
+MidiStretch::run (boost::shared_ptr<Region> r)
+{
+ SourceList nsrcs;
+ char suffix[32];
+
+ boost::shared_ptr<MidiRegion> region = boost::dynamic_pointer_cast<MidiRegion>(r);
+ if (!region)
+ return -1;
+
+ /* the name doesn't need to be super-precise, but allow for 2 fractional
+ digits just to disambiguate close but not identical stretches.
+ */
+
+ snprintf (suffix, sizeof (suffix), "@%d", (int) floor (_request.time_fraction * 100.0f));
+
+ string new_name = region->name();
+ string::size_type at = new_name.find ('@');
+
+ // remove any existing stretch indicator
+
+ if (at != string::npos && at > 2) {
+ new_name = new_name.substr (0, at - 1);
+ }
+
+ new_name += suffix;
+
+ /* create new sources */
+
+ if (make_new_sources (region, nsrcs, suffix))
+ return -1;
+
+ // FIXME: how to make a whole file region if it isn't?
+ //assert(region->whole_file());
+
+ boost::shared_ptr<MidiSource> src = region->midi_source(0);
+ src->load_model();
+
+ boost::shared_ptr<MidiModel> old_model = src->model();
+
+ boost::shared_ptr<MidiSource> new_src = boost::dynamic_pointer_cast<MidiSource>(nsrcs[0]);
+ assert(new_src);
+
+ boost::shared_ptr<MidiModel> new_model = new_src->model();
+ new_model->start_write();
+
+ for (MidiModel::const_iterator i = old_model->begin(); i != old_model->end(); ++i) {
+ const double new_time = i->time() * _request.time_fraction;
+
+ // FIXME: double copy
+ MIDI::Event ev = MIDI::Event(*i, true);
+ ev.time() = new_time;
+ new_model->append(ev);
+ }
+
+ new_model->end_write();
+ new_model->set_edited(true);
+
+ const int ret = finish (region, nsrcs, new_name);
+
+ results[0]->set_length((nframes_t) floor (r->length() * _request.time_fraction), NULL);
+
+ return ret;
+}
+
diff --git a/libs/ardour/midi_track.cc b/libs/ardour/midi_track.cc
new file mode 100644
index 0000000000..1000e68ba6
--- /dev/null
+++ b/libs/ardour/midi_track.cc
@@ -0,0 +1,769 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ By Dave Robillard, 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include <pbd/error.h>
+#include <sigc++/retype.h>
+#include <sigc++/retype_return.h>
+#include <sigc++/bind.h>
+
+#include <pbd/enumwriter.h>
+#include <midi++/events.h>
+
+#include <ardour/midi_track.h>
+#include <ardour/midi_diskstream.h>
+#include <ardour/session.h>
+#include <ardour/io_processor.h>
+#include <ardour/midi_region.h>
+#include <ardour/midi_source.h>
+#include <ardour/route_group_specialized.h>
+#include <ardour/processor.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/panner.h>
+#include <ardour/utils.h>
+#include <ardour/buffer_set.h>
+#include <ardour/meter.h>
+
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+MidiTrack::MidiTrack (Session& sess, string name, Route::Flag flag, TrackMode mode)
+ : Track (sess, name, flag, mode, DataType::MIDI)
+ , _immediate_events(1024) // FIXME: size?
+ , _note_mode(Sustained)
+{
+ MidiDiskstream::Flag dflags = MidiDiskstream::Flag (0);
+
+ if (_flags & Hidden) {
+ dflags = MidiDiskstream::Flag (dflags | MidiDiskstream::Hidden);
+ } else {
+ dflags = MidiDiskstream::Flag (dflags | MidiDiskstream::Recordable);
+ }
+
+ assert(mode != Destructive);
+
+ boost::shared_ptr<MidiDiskstream> ds (new MidiDiskstream (_session, name, dflags));
+ _session.add_diskstream (ds);
+
+ set_diskstream (boost::dynamic_pointer_cast<MidiDiskstream> (ds));
+
+ _declickable = true;
+ _freeze_record.state = NoFreeze;
+ _saved_meter_point = _meter_point;
+ _mode = mode;
+
+ set_input_minimum(ChanCount(DataType::MIDI, 1));
+ set_input_maximum(ChanCount(DataType::MIDI, 1));
+ set_output_minimum(ChanCount(DataType::MIDI, 1));
+ set_output_maximum(ChanCount(DataType::MIDI, 1));
+
+ PortCountChanged(ChanCount(DataType::MIDI, 2)); /* EMIT SIGNAL */
+}
+
+MidiTrack::MidiTrack (Session& sess, const XMLNode& node)
+ : Track (sess, node)
+ , _immediate_events(1024) // FIXME: size?
+ , _note_mode(Sustained)
+{
+ _set_state(node, false);
+
+ set_input_minimum(ChanCount(DataType::MIDI, 1));
+ set_input_maximum(ChanCount(DataType::MIDI, 1));
+ set_output_minimum(ChanCount(DataType::MIDI, 1));
+ set_output_maximum(ChanCount(DataType::MIDI, 1));
+
+ PortCountChanged(ChanCount(DataType::MIDI, 2)); /* EMIT SIGNAL */
+}
+
+MidiTrack::~MidiTrack ()
+{
+}
+
+
+int
+MidiTrack::set_diskstream (boost::shared_ptr<MidiDiskstream> ds)
+{
+ _diskstream = ds;
+ _diskstream->set_io (*this);
+ _diskstream->set_destructive (_mode == Destructive);
+
+ _diskstream->set_record_enabled (false);
+ //_diskstream->monitor_input (false);
+
+ ic_connection.disconnect();
+ ic_connection = input_changed.connect (mem_fun (*_diskstream, &MidiDiskstream::handle_input_change));
+
+ DiskstreamChanged (); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+int
+MidiTrack::use_diskstream (string name)
+{
+ boost::shared_ptr<MidiDiskstream> dstream;
+
+ if ((dstream = boost::dynamic_pointer_cast<MidiDiskstream>(_session.diskstream_by_name (name))) == 0) {
+ error << string_compose(_("MidiTrack: midi diskstream \"%1\" not known by session"), name) << endmsg;
+ return -1;
+ }
+
+ return set_diskstream (dstream);
+}
+
+int
+MidiTrack::use_diskstream (const PBD::ID& id)
+{
+ boost::shared_ptr<MidiDiskstream> dstream;
+
+ if ((dstream = boost::dynamic_pointer_cast<MidiDiskstream> (_session.diskstream_by_id (id))) == 0) {
+ error << string_compose(_("MidiTrack: midi diskstream \"%1\" not known by session"), id) << endmsg;
+ return -1;
+ }
+
+ return set_diskstream (dstream);
+}
+
+boost::shared_ptr<MidiDiskstream>
+MidiTrack::midi_diskstream() const
+{
+ return boost::dynamic_pointer_cast<MidiDiskstream>(_diskstream);
+}
+
+int
+MidiTrack::set_state (const XMLNode& node)
+{
+ return _set_state (node, true);
+}
+
+int
+MidiTrack::_set_state (const XMLNode& node, bool call_base)
+{
+ const XMLProperty *prop;
+ XMLNodeConstIterator iter;
+
+ if (Route::_set_state (node, call_base)) {
+ return -1;
+ }
+
+ // No destructive MIDI tracks (yet?)
+ _mode = Normal;
+
+ if ((prop = node.property (X_("note-mode"))) != 0) {
+ _note_mode = NoteMode (string_2_enum (prop->value(), _note_mode));
+ } else {
+ _note_mode = Sustained;
+ }
+
+ if ((prop = node.property ("diskstream-id")) == 0) {
+
+ /* some old sessions use the diskstream name rather than the ID */
+
+ if ((prop = node.property ("diskstream")) == 0) {
+ fatal << _("programming error: MidiTrack given state without diskstream!") << endmsg;
+ /*NOTREACHED*/
+ return -1;
+ }
+
+ if (use_diskstream (prop->value())) {
+ return -1;
+ }
+
+ } else {
+
+ PBD::ID id (prop->value());
+
+ if (use_diskstream (id)) {
+ return -1;
+ }
+ }
+
+
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLNode *child;
+
+ nlist = node.children();
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+ child = *niter;
+
+ if (child->name() == X_("recenable")) {
+ _rec_enable_control->set_state (*child);
+ _session.add_controllable (_rec_enable_control);
+ }
+ }
+
+ pending_state = const_cast<XMLNode*> (&node);
+
+ _session.StateReady.connect (mem_fun (*this, &MidiTrack::set_state_part_two));
+
+ return 0;
+}
+
+XMLNode&
+MidiTrack::state(bool full_state)
+{
+ XMLNode& root (Route::state(full_state));
+ XMLNode* freeze_node;
+ char buf[64];
+
+ if (_freeze_record.playlist) {
+ XMLNode* inode;
+
+ freeze_node = new XMLNode (X_("freeze-info"));
+ freeze_node->add_property ("playlist", _freeze_record.playlist->name());
+ freeze_node->add_property ("state", enum_2_string (_freeze_record.state));
+
+ for (vector<FreezeRecordProcessorInfo*>::iterator i = _freeze_record.processor_info.begin(); i != _freeze_record.processor_info.end(); ++i) {
+ inode = new XMLNode (X_("processor"));
+ (*i)->id.print (buf, sizeof(buf));
+ inode->add_property (X_("id"), buf);
+ inode->add_child_copy ((*i)->state);
+
+ freeze_node->add_child_nocopy (*inode);
+ }
+
+ root.add_child_nocopy (*freeze_node);
+ }
+
+ /* Alignment: act as a proxy for the diskstream */
+
+ XMLNode* align_node = new XMLNode (X_("alignment"));
+ AlignStyle as = _diskstream->alignment_style ();
+ align_node->add_property (X_("style"), enum_2_string (as));
+ root.add_child_nocopy (*align_node);
+
+ root.add_property (X_("note-mode"), enum_2_string (_note_mode));
+
+ /* we don't return diskstream state because we don't
+ own the diskstream exclusively. control of the diskstream
+ state is ceded to the Session, even if we create the
+ diskstream.
+ */
+
+ _diskstream->id().print (buf, sizeof(buf));
+ root.add_property ("diskstream-id", buf);
+
+ root.add_child_nocopy (_rec_enable_control->get_state());
+
+ return root;
+}
+
+void
+MidiTrack::set_state_part_two ()
+{
+ XMLNode* fnode;
+ XMLProperty* prop;
+ LocaleGuard lg (X_("POSIX"));
+
+ /* This is called after all session state has been restored but before
+ have been made ports and connections are established.
+ */
+
+ if (pending_state == 0) {
+ return;
+ }
+
+ if ((fnode = find_named_node (*pending_state, X_("freeze-info"))) != 0) {
+
+
+ _freeze_record.have_mementos = false;
+ _freeze_record.state = Frozen;
+
+ for (vector<FreezeRecordProcessorInfo*>::iterator i = _freeze_record.processor_info.begin(); i != _freeze_record.processor_info.end(); ++i) {
+ delete *i;
+ }
+ _freeze_record.processor_info.clear ();
+
+ if ((prop = fnode->property (X_("playlist"))) != 0) {
+ boost::shared_ptr<Playlist> pl = _session.playlist_by_name (prop->value());
+ if (pl) {
+ _freeze_record.playlist = boost::dynamic_pointer_cast<MidiPlaylist> (pl);
+ } else {
+ _freeze_record.playlist.reset();
+ _freeze_record.state = NoFreeze;
+ return;
+ }
+ }
+
+ if ((prop = fnode->property (X_("state"))) != 0) {
+ _freeze_record.state = FreezeState (string_2_enum (prop->value(), _freeze_record.state));
+ }
+
+ XMLNodeConstIterator citer;
+ XMLNodeList clist = fnode->children();
+
+ for (citer = clist.begin(); citer != clist.end(); ++citer) {
+ if ((*citer)->name() != X_("processor")) {
+ continue;
+ }
+
+ if ((prop = (*citer)->property (X_("id"))) == 0) {
+ continue;
+ }
+
+ FreezeRecordProcessorInfo* frii = new FreezeRecordProcessorInfo (*((*citer)->children().front()),
+ boost::shared_ptr<Processor>());
+ frii->id = prop->value ();
+ _freeze_record.processor_info.push_back (frii);
+ }
+ }
+
+ /* Alignment: act as a proxy for the diskstream */
+
+ if ((fnode = find_named_node (*pending_state, X_("alignment"))) != 0) {
+
+ if ((prop = fnode->property (X_("style"))) != 0) {
+
+ /* fix for older sessions from before EnumWriter */
+
+ string pstr;
+
+ if (prop->value() == "capture") {
+ pstr = "CaptureTime";
+ } else if (prop->value() == "existing") {
+ pstr = "ExistingMaterial";
+ } else {
+ pstr = prop->value();
+ }
+
+ AlignStyle as = AlignStyle (string_2_enum (pstr, as));
+ _diskstream->set_persistent_align_style (as);
+ }
+ }
+ return;
+}
+
+int
+MidiTrack::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset,
+ bool session_state_changing, bool can_record, bool rec_monitors_input)
+{
+ if (n_outputs().n_midi() == 0) {
+ return 0;
+ }
+
+ if (!_active) {
+ silence (nframes, offset);
+ }
+
+ if (session_state_changing) {
+
+ /* XXX is this safe to do against transport state changes? */
+
+ passthru_silence (start_frame, end_frame, nframes, offset, 0, false);
+ return 0;
+ }
+
+ midi_diskstream()->check_record_status (start_frame, nframes, can_record);
+
+ bool send_silence;
+
+ if (_have_internal_generator) {
+ /* since the instrument has no input streams,
+ there is no reason to send any signal
+ into the route.
+ */
+ send_silence = true;
+ } else {
+
+ if (Config->get_auto_input()) {
+ if (Config->get_monitoring_model() == SoftwareMonitoring) {
+ send_silence = false;
+ } else {
+ send_silence = true;
+ }
+ } else {
+ if (_diskstream->record_enabled()) {
+ if (Config->get_monitoring_model() == SoftwareMonitoring) {
+ send_silence = false;
+ } else {
+ send_silence = true;
+ }
+ } else {
+ send_silence = true;
+ }
+ }
+ }
+
+ apply_gain_automation = false;
+
+ if (send_silence) {
+
+ /* if we're sending silence, but we want the meters to show levels for the signal,
+ meter right here.
+ */
+
+ if (_have_internal_generator) {
+ passthru_silence (start_frame, end_frame, nframes, offset, 0, true);
+ } else {
+ if (_meter_point == MeterInput) {
+ just_meter_input (start_frame, end_frame, nframes, offset);
+ }
+ passthru_silence (start_frame, end_frame, nframes, offset, 0, false);
+ }
+
+ } else {
+
+ /* we're sending signal, but we may still want to meter the input.
+ */
+
+ passthru (start_frame, end_frame, nframes, offset, 0, (_meter_point == MeterInput));
+ }
+
+ return 0;
+}
+
+int
+MidiTrack::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick,
+ bool can_record, bool rec_monitors_input)
+{
+ int dret;
+ boost::shared_ptr<MidiDiskstream> diskstream = midi_diskstream();
+
+ {
+ Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+ if (lm.locked()) {
+ // automation snapshot can also be called from the non-rt context
+ // and it uses the redirect list, so we take the lock out here
+ automation_snapshot (start_frame);
+ }
+ }
+
+ if (n_outputs().n_total() == 0 && _processors.empty()) {
+ return 0;
+ }
+
+ if (!_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ nframes_t transport_frame = _session.transport_frame();
+
+ if ((nframes = check_initial_delay (nframes, offset, transport_frame)) == 0) {
+ /* need to do this so that the diskstream sets its
+ playback distance to zero, thus causing diskstream::commit
+ to do nothing.
+ */
+ return diskstream->process (transport_frame, 0, 0, can_record, rec_monitors_input);
+ }
+
+ _silent = false;
+
+ if ((dret = diskstream->process (transport_frame, nframes, offset, can_record, rec_monitors_input)) != 0) {
+
+ silence (nframes, offset);
+
+ return dret;
+ }
+
+ /* special condition applies */
+
+ if (_meter_point == MeterInput) {
+ just_meter_input (start_frame, end_frame, nframes, offset);
+ }
+
+ if (diskstream->record_enabled() && !can_record && !Config->get_auto_input()) {
+
+ /* not actually recording, but we want to hear the input material anyway,
+ at least potentially (depending on monitoring options)
+ */
+
+ passthru (start_frame, end_frame, nframes, offset, 0, true);
+
+ } else {
+ /*
+ XXX is it true that the earlier test on n_outputs()
+ means that we can avoid checking it again here? i think
+ so, because changing the i/o configuration of an IO
+ requires holding the AudioEngine lock, which we hold
+ while in the process() tree.
+ */
+
+
+ /* copy the diskstream data to all output buffers */
+
+ //const size_t limit = n_process_buffers().n_audio();
+ BufferSet& bufs = _session.get_scratch_buffers (n_process_buffers());
+
+ diskstream->get_playback(bufs.get_midi(0), start_frame, end_frame);
+
+ process_output_buffers (bufs, start_frame, end_frame, nframes, offset,
+ (!_session.get_record_enabled() || !Config->get_do_not_record_plugins()), declick, (_meter_point != MeterInput));
+
+ }
+
+ return 0;
+}
+
+int
+MidiTrack::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset,
+ bool can_record, bool rec_monitors_input)
+{
+ if (n_outputs().n_midi() == 0 && _processors.empty()) {
+ return 0;
+ }
+
+ if (!_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ _silent = true;
+ apply_gain_automation = false;
+
+ silence (nframes, offset);
+
+ return midi_diskstream()->process (_session.transport_frame() + offset, nframes, offset, can_record, rec_monitors_input);
+}
+
+void
+MidiTrack::process_output_buffers (BufferSet& bufs,
+ nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset, bool with_processors, int declick,
+ bool meter)
+{
+ /* There's no such thing as a MIDI bus for the time being.
+ * We'll do all the MIDI route work here for now, but the long-term goal is to have
+ * Route::process_output_buffers handle everything */
+
+ if (meter && (_meter_point == MeterInput || _meter_point == MeterPreFader)) {
+ _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
+ }
+
+ // Run all processors
+ if (with_processors) {
+ Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+ if (rm.locked()) {
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ (*i)->run_in_place (bufs, start_frame, end_frame, nframes, offset);
+ }
+ }
+ }
+
+ if (meter && (_meter_point == MeterPostFader)) {
+ _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
+ }
+
+ // Main output stage
+ if (muted()) {
+ IO::silence(nframes, offset);
+ } else {
+
+ // Write 'automation' controllers (e.g. CC events from a UI slider)
+ write_controller_messages(bufs.get_midi(0), start_frame, end_frame, nframes, offset);
+
+ deliver_output(bufs, start_frame, end_frame, nframes, offset);
+ }
+}
+
+void
+MidiTrack::write_controller_messages(MidiBuffer& output_buf, nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset)
+{
+#if 0
+ BufferSet& mix_buffers = _session.get_mix_buffers(ChanCount(DataType::MIDI, 2));
+
+ /* FIXME: this could be more realtimey */
+
+ // Write immediate events (UI controls)
+ MidiBuffer& cc_buf = mix_buffers.get_midi(0);
+ cc_buf.silence(nframes, offset);
+
+ uint8_t buf[3]; // CC = 3 bytes
+ buf[0] = MIDI_CMD_CONTROL;
+ MIDI::Event ev(0, 3, buf, false);
+
+ // Write track controller automation
+ // This now lives in MidiModel. Any need for track automation like this?
+ // Relative Velocity?
+ if (_session.transport_rolling()) {
+ for (Controls::const_iterator i = _controls.begin(); i != _controls.end(); ++i) {
+ const boost::shared_ptr<AutomationList> list = (*i).second->list();
+
+ if ( (!list->automation_playback())
+ || (list->parameter().type() != MidiCCAutomation))
+ continue;
+
+ double start = start_frame;
+ double x, y;
+ while ((*i).second->list()->rt_safe_earliest_event(start, end_frame, x, y)) {
+ assert(x >= start_frame);
+ assert(x <= end_frame);
+
+ const nframes_t stamp = (nframes_t)floor(x - start_frame);
+ assert(stamp < nframes);
+
+ assert(y >= 0.0);
+ assert(y <= 127.0);
+
+ ev.time() = stamp;
+ ev.buffer()[1] = (uint8_t)list->parameter().id();
+ ev.buffer()[2] = (uint8_t)y;
+
+ cc_buf.push_back(ev);
+
+ start = x + 1; // FIXME? maybe?
+ }
+ }
+ }
+
+ /* FIXME: too much copying! */
+
+ // Merge cc buf into output
+ if (cc_buf.size() > 0) {
+
+ // Both CC and route, must merge
+ if (output_buf.size() > 0) {
+
+ MidiBuffer& mix_buf = mix_buffers.get_midi(1);
+ mix_buf.merge(output_buf, cc_buf);
+ output_buf.copy(mix_buf);
+
+ } else {
+ output_buf.copy(cc_buf);
+ }
+ }
+#endif
+
+ // Append immediate events (UI controls)
+ _immediate_events.read(output_buf, 0, 0, offset + nframes-1); // all stamps = 0
+}
+
+int
+MidiTrack::export_stuff (BufferSet& bufs, nframes_t nframes, nframes_t end_frame)
+{
+ return -1;
+}
+
+void
+MidiTrack::set_latency_delay (nframes_t longest_session_latency)
+{
+ Route::set_latency_delay (longest_session_latency);
+ _diskstream->set_roll_delay (_roll_delay);
+}
+
+void
+MidiTrack::bounce (InterThreadInfo& itt)
+{
+ throw;
+ //vector<MidiSource*> srcs;
+ //_session.write_one_midi_track (*this, 0, _session.current_end_frame(), false, srcs, itt);
+}
+
+
+void
+MidiTrack::bounce_range (nframes_t start, nframes_t end, InterThreadInfo& itt)
+{
+ throw;
+ //vector<MidiSource*> srcs;
+ //_session.write_one_midi_track (*this, start, end, false, srcs, itt);
+}
+
+void
+MidiTrack::freeze (InterThreadInfo& itt)
+{
+}
+
+void
+MidiTrack::unfreeze ()
+{
+ _freeze_record.state = UnFrozen;
+ FreezeChange (); /* EMIT SIGNAL */
+}
+
+void
+MidiTrack::set_note_mode (NoteMode m)
+{
+ cout << _name << " SET NOTE MODE " << m << endl;
+ _note_mode = m;
+ midi_diskstream()->set_note_mode(m);
+}
+
+void
+MidiTrack::midi_panic()
+{
+ for (uint8_t channel = 0; channel <= 0xF; channel++) {
+ uint8_t ev[3] = { MIDI_CMD_CONTROL | channel, MIDI_CTL_SUSTAIN, 0 };
+ write_immediate_event(3, ev);
+ ev[1] = MIDI_CTL_ALL_NOTES_OFF;
+ write_immediate_event(3, ev);
+ ev[1] = MIDI_CTL_RESET_CONTROLLERS;
+ write_immediate_event(3, ev);
+ }
+}
+
+/** \return true on success, false on failure (no buffer space left)
+ */
+bool
+MidiTrack::write_immediate_event(size_t size, const uint8_t* buf)
+{
+ printf("Write immediate event: ");
+ for (size_t i=0; i < size; ++i) {
+ printf("%X ", buf[i]);
+ }
+ printf("\n");
+ return (_immediate_events.write(0, size, buf) == size);
+}
+
+void
+MidiTrack::MidiControl::set_value(float val)
+{
+ assert(val >= _list->parameter().min());
+ assert(val <= _list->parameter().max());
+ size_t size = 3;
+
+ if ( ! _list->automation_playback()) {
+ uint8_t ev[3] = { _list->parameter().channel(), int(val), 0.0 };
+ switch(_list->parameter().type()) {
+ case MidiCCAutomation:
+ ev[0] += MIDI_CMD_CONTROL;
+ ev[1] = _list->parameter().id();
+ ev[2] = int(val);
+ break;
+
+ case MidiPgmChangeAutomation:
+ size = 2;
+ ev[0] += MIDI_CMD_PGM_CHANGE;
+ ev[1] = int(val);
+ break;
+
+ case MidiChannelAftertouchAutomation:
+ size = 2;
+ ev[0] += MIDI_CMD_CHANNEL_PRESSURE;
+ ev[1] = int(val);
+ break;
+
+ case MidiPitchBenderAutomation:
+ ev[0] += MIDI_CMD_BENDER;
+ ev[1] = 0x7F & int(val);
+ ev[2] = 0x7F & (int(val) >> 7);
+ break;
+
+ default:
+ assert(false);
+ }
+ _route->write_immediate_event(size, ev);
+ }
+
+ AutomationControl::set_value(val);
+}
+
diff --git a/libs/ardour/mix.cc b/libs/ardour/mix.cc
new file mode 100644
index 0000000000..726d375453
--- /dev/null
+++ b/libs/ardour/mix.cc
@@ -0,0 +1,176 @@
+/*
+ Copyright (C) 2000-2005 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cmath>
+#include <ardour/types.h>
+#include <ardour/utils.h>
+#include <ardour/mix.h>
+#include <stdint.h>
+
+using namespace ARDOUR;
+
+#if defined (ARCH_X86) && defined (BUILD_SSE_OPTIMIZATIONS)
+// Debug wrappers
+
+float
+debug_compute_peak (ARDOUR::Sample *buf, nframes_t nsamples, float current)
+{
+ if ( ((intptr_t)buf % 16) != 0) {
+ cerr << "compute_peak(): buffer unaligned!" << endl;
+ }
+
+ return x86_sse_compute_peak(buf, nsamples, current);
+}
+
+void
+debug_apply_gain_to_buffer (ARDOUR::Sample *buf, nframes_t nframes, float gain)
+{
+ if ( ((intptr_t)buf % 16) != 0) {
+ cerr << "apply_gain_to_buffer(): buffer unaligned!" << endl;
+ }
+
+ x86_sse_apply_gain_to_buffer(buf, nframes, gain);
+}
+
+void
+debug_mix_buffers_with_gain (ARDOUR::Sample *dst, ARDOUR::Sample *src, nframes_t nframes, float gain)
+{
+ if ( ((intptr_t)dst & 15) != 0) {
+ cerr << "mix_buffers_with_gain(): dst unaligned!" << endl;
+ }
+
+ if ( ((intptr_t)dst & 15) != ((intptr_t)src & 15) ) {
+ cerr << "mix_buffers_with_gain(): dst & src don't have the same alignment!" << endl;
+ mix_buffers_with_gain(dst, src, nframes, gain);
+ } else {
+ x86_sse_mix_buffers_with_gain(dst, src, nframes, gain);
+ }
+}
+
+void
+debug_mix_buffers_no_gain (ARDOUR::Sample *dst, ARDOUR::Sample *src, nframes_t nframes)
+{
+ if ( ((intptr_t)dst & 15) != 0) {
+ cerr << "mix_buffers_no_gain(): dst unaligned!" << endl;
+ }
+
+ if ( ((intptr_t)dst & 15) != ((intptr_t)src & 15) ) {
+ cerr << "mix_buffers_no_gain(): dst & src don't have the same alignment!" << endl;
+ mix_buffers_no_gain(dst, src, nframes);
+ } else {
+ x86_sse_mix_buffers_no_gain(dst, src, nframes);
+ }
+}
+
+#endif
+
+
+float
+default_compute_peak (const ARDOUR::Sample * buf, nframes_t nsamples, float current)
+{
+ for (nframes_t i = 0; i < nsamples; ++i) {
+ current = f_max (current, fabsf (buf[i]));
+ }
+
+ return current;
+}
+
+void
+default_find_peaks (const ARDOUR::Sample * buf, nframes_t nframes, float *min, float *max)
+{
+ nframes_t i;
+ float a, b;
+
+ a = *max;
+ b = *min;
+
+ for (i = 0; i < nframes; i++)
+ {
+ a = fmax (buf[i], a);
+ b = fmin (buf[i], b);
+ }
+
+ *max = a;
+ *min = b;
+}
+
+void
+default_apply_gain_to_buffer (ARDOUR::Sample * buf, nframes_t nframes, float gain)
+{
+ for (nframes_t i=0; i<nframes; i++)
+ buf[i] *= gain;
+}
+
+void
+default_mix_buffers_with_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes, float gain)
+{
+ for (nframes_t i = 0; i < nframes; i++) {
+ dst[i] += src[i] * gain;
+ }
+}
+
+void
+default_mix_buffers_no_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes)
+{
+ for (nframes_t i=0; i < nframes; i++) {
+ dst[i] += src[i];
+ }
+}
+
+#if defined (__APPLE__) && defined (BUILD_VECLIB_OPTIMIZATIONS)
+#include <Accelerate/Accelerate.h>
+
+float
+veclib_compute_peak (const ARDOUR::Sample * buf, nframes_t nsamples, float current)
+{
+ float tmpmax = 0.0f;
+ vDSP_maxmgv(buf, 1, &tmpmax, nsamples);
+ return f_max(current, tmpmax);
+}
+
+void
+veclib_find_peaks (const ARDOUR::Sample * buf, nframes_t nframes, float *min, float *max)
+{
+ vDSP_maxv (const_cast<ARDOUR::Sample*>(buf), 1, max, nframes);
+ vDSP_minv (const_cast<ARDOUR::Sample*>(buf), 1, min, nframes);
+}
+
+void
+veclib_apply_gain_to_buffer (ARDOUR::Sample * buf, nframes_t nframes, float gain)
+{
+ vDSP_vsmul(buf, 1, &gain, buf, 1, nframes);
+}
+
+void
+veclib_mix_buffers_with_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes, float gain)
+{
+ vDSP_vsma(src, 1, &gain, dst, 1, dst, 1, nframes);
+}
+
+void
+veclib_mix_buffers_no_gain (ARDOUR::Sample * dst, const ARDOUR::Sample * src, nframes_t nframes)
+{
+ // It seems that a vector mult only operation does not exist...
+ float gain = 1.0f;
+ vDSP_vsma(src, 1, &gain, dst, 1, dst, 1, nframes);
+}
+
+#endif
+
+
diff --git a/libs/ardour/mtc_slave.cc b/libs/ardour/mtc_slave.cc
new file mode 100644
index 0000000000..0a0fbc1529
--- /dev/null
+++ b/libs/ardour/mtc_slave.cc
@@ -0,0 +1,357 @@
+/*
+ Copyright (C) 2002-4 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <errno.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pbd/error.h>
+#include <pbd/failed_constructor.h>
+#include <pbd/pthread_utils.h>
+
+#include <midi++/port.h>
+#include <ardour/slave.h>
+#include <ardour/session.h>
+#include <ardour/audioengine.h>
+#include <ardour/cycles.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace sigc;
+using namespace MIDI;
+using namespace PBD;
+
+MTC_Slave::MTC_Slave (Session& s, MIDI::Port& p)
+ : session (s)
+{
+ can_notify_on_unknown_rate = true;
+
+ rebind (p);
+ reset ();
+}
+
+MTC_Slave::~MTC_Slave()
+{
+}
+
+void
+MTC_Slave::rebind (MIDI::Port& p)
+{
+ for (vector<sigc::connection>::iterator i = connections.begin(); i != connections.end(); ++i) {
+ (*i).disconnect ();
+ }
+
+ port = &p;
+
+ connections.push_back (port->input()->mtc_time.connect (mem_fun (*this, &MTC_Slave::update_mtc_time)));
+ connections.push_back (port->input()->mtc_qtr.connect (mem_fun (*this, &MTC_Slave::update_mtc_qtr)));
+ connections.push_back (port->input()->mtc_status.connect (mem_fun (*this, &MTC_Slave::update_mtc_status)));
+}
+
+void
+MTC_Slave::update_mtc_qtr (Parser& p)
+{
+ cycles_t cnow = get_cycles ();
+ nframes_t now = session.engine().frame_time();
+ nframes_t qtr;
+ static cycles_t last_qtr = 0;
+
+ qtr = (long) (session.frames_per_smpte_frame() / 4);
+ mtc_frame += qtr;
+ last_qtr = cnow;
+
+ current.guard1++;
+ current.position = mtc_frame;
+ current.timestamp = now;
+ current.guard2++;
+
+ last_inbound_frame = now;
+}
+
+void
+MTC_Slave::update_mtc_time (const byte *msg, bool was_full)
+{
+ nframes_t now = session.engine().frame_time();
+ SMPTE::Time smpte;
+
+ smpte.hours = msg[3];
+ smpte.minutes = msg[2];
+ smpte.seconds = msg[1];
+ smpte.frames = msg[0];
+
+ switch (msg[4]) {
+ case MTC_24_FPS:
+ smpte.rate = 24;
+ smpte.drop = false;
+ can_notify_on_unknown_rate = true;
+ break;
+ case MTC_25_FPS:
+ smpte.rate = 25;
+ smpte.drop = false;
+ can_notify_on_unknown_rate = true;
+ break;
+ case MTC_30_FPS_DROP:
+ smpte.rate = 30;
+ smpte.drop = true;
+ can_notify_on_unknown_rate = true;
+ break;
+ case MTC_30_FPS:
+ smpte.rate = 30;
+ smpte.drop = false;
+ can_notify_on_unknown_rate = true;
+ break;
+ default:
+ /* throttle error messages about unknown MTC rates */
+ if (can_notify_on_unknown_rate) {
+ error << _("Unknown rate/drop value in incoming MTC stream, session values used instead") << endmsg;
+ can_notify_on_unknown_rate = false;
+ }
+ smpte.rate = session.smpte_frames_per_second();
+ smpte.drop = session.smpte_drop_frames();
+ }
+
+ session.smpte_to_sample (smpte, mtc_frame, true, false);
+
+ if (was_full) {
+
+ current.guard1++;
+ current.position = mtc_frame;
+ current.timestamp = 0;
+ current.guard2++;
+
+ session.request_locate (mtc_frame, false);
+ session.request_transport_speed (0);
+ update_mtc_status (MIDI::Parser::MTC_Stopped);
+
+ reset ();
+
+ } else {
+
+ /* We received the last quarter frame 7 quarter frames (1.75 mtc
+ frames) after the instance when the contents of the mtc quarter
+ frames were decided. Add time to compensate for the elapsed 1.75
+ frames.
+ Also compensate for audio latency.
+ */
+
+ mtc_frame += (long) (1.75 * session.frames_per_smpte_frame()) + session.worst_output_latency();
+
+ if (first_mtc_frame == 0) {
+ first_mtc_frame = mtc_frame;
+ first_mtc_time = now;
+ }
+
+ current.guard1++;
+ current.position = mtc_frame;
+ current.timestamp = now;
+ current.guard2++;
+ }
+
+ last_inbound_frame = now;
+}
+
+void
+MTC_Slave::handle_locate (const MIDI::byte* mmc_tc)
+{
+ MIDI::byte mtc[4];
+
+ mtc[3] = mmc_tc[0] & 0xf; /* hrs only */
+ mtc[2] = mmc_tc[1];
+ mtc[1] = mmc_tc[2];
+ mtc[0] = mmc_tc[3];
+
+ update_mtc_time (mtc, true);
+}
+
+void
+MTC_Slave::update_mtc_status (MIDI::Parser::MTC_Status status)
+{
+
+ switch (status) {
+ case MTC_Stopped:
+ mtc_speed = 0.0f;
+ mtc_frame = 0;
+
+ current.guard1++;
+ current.position = mtc_frame;
+ current.timestamp = 0;
+ current.guard2++;
+
+ break;
+
+ case MTC_Forward:
+ mtc_speed = 0.0f;
+ mtc_frame = 0;
+
+ current.guard1++;
+ current.position = mtc_frame;
+ current.timestamp = 0;
+ current.guard2++;
+
+ break;
+
+ case MTC_Backward:
+ mtc_speed = 0.0f;
+ mtc_frame = 0;
+
+ current.guard1++;
+ current.position = mtc_frame;
+ current.timestamp = 0;
+ current.guard2++;
+
+ break;
+ }
+}
+
+void
+MTC_Slave::read_current (SafeTime *st) const
+{
+ int tries = 0;
+ do {
+ if (tries == 10) {
+ error << _("MTC Slave: atomic read of current time failed, sleeping!") << endmsg;
+ usleep (20);
+ tries = 0;
+ }
+
+ *st = current;
+ tries++;
+
+ } while (st->guard1 != st->guard2);
+}
+
+bool
+MTC_Slave::locked () const
+{
+ return port->input()->mtc_locked();
+}
+
+bool
+MTC_Slave::ok() const
+{
+ return true;
+}
+
+bool
+MTC_Slave::speed_and_position (float& speed, nframes_t& pos)
+{
+ nframes_t now = session.engine().frame_time();
+ SafeTime last;
+ nframes_t frame_rate;
+ nframes_t elapsed;
+ float speed_now;
+
+ read_current (&last);
+
+ if (first_mtc_time == 0) {
+ speed = 0;
+ pos = last.position;
+ return true;
+ }
+
+ /* no timecode for 1/4 second ? conclude that its stopped */
+
+ if (last_inbound_frame && now > last_inbound_frame && now - last_inbound_frame > session.frame_rate() / 4) {
+ mtc_speed = 0;
+ pos = last.position;
+ session.request_locate (pos, false);
+ session.request_transport_speed (0);
+ update_mtc_status (MIDI::Parser::MTC_Stopped);
+ reset();
+ return false;
+ }
+
+ frame_rate = session.frame_rate();
+
+ speed_now = (float) ((last.position - first_mtc_frame) / (double) (now - first_mtc_time));
+
+ accumulator[accumulator_index++] = speed_now;
+
+ if (accumulator_index >= accumulator_size) {
+ have_first_accumulated_speed = true;
+ accumulator_index = 0;
+ }
+
+ if (have_first_accumulated_speed) {
+ float total = 0;
+
+ for (int32_t i = 0; i < accumulator_size; ++i) {
+ total += accumulator[i];
+ }
+
+ mtc_speed = total / accumulator_size;
+
+ } else {
+
+ mtc_speed = speed_now;
+
+ }
+
+ if (mtc_speed == 0.0f) {
+
+ elapsed = 0;
+
+ } else {
+
+ /* scale elapsed time by the current MTC speed */
+
+ if (last.timestamp && (now > last.timestamp)) {
+ elapsed = (nframes_t) floor (mtc_speed * (now - last.timestamp));
+ } else {
+ elapsed = 0; /* XXX is this right? */
+ }
+ }
+
+ /* now add the most recent timecode value plus the estimated elapsed interval */
+
+ pos = elapsed + last.position;
+
+ speed = mtc_speed;
+ return true;
+}
+
+ARDOUR::nframes_t
+MTC_Slave::resolution() const
+{
+ return (nframes_t) session.frames_per_smpte_frame();
+}
+
+void
+MTC_Slave::reset ()
+{
+ /* XXX massive thread safety issue here. MTC could
+ be being updated as we call this. but this
+ supposed to be a realtime-safe call.
+ */
+
+ port->input()->reset_mtc_state ();
+
+ last_inbound_frame = 0;
+ current.guard1++;
+ current.position = 0;
+ current.timestamp = 0;
+ current.guard2++;
+ first_mtc_frame = 0;
+ first_mtc_time = 0;
+
+ accumulator_index = 0;
+ have_first_accumulated_speed = false;
+ mtc_speed = 0;
+}
diff --git a/libs/ardour/named_selection.cc b/libs/ardour/named_selection.cc
new file mode 100644
index 0000000000..fbb4b748df
--- /dev/null
+++ b/libs/ardour/named_selection.cc
@@ -0,0 +1,130 @@
+/*
+ Copyright (C) 2003 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/failed_constructor.h>
+#include <pbd/error.h>
+
+#include <ardour/session.h>
+#include <ardour/utils.h>
+#include <ardour/playlist.h>
+#include <ardour/named_selection.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+sigc::signal<void,NamedSelection*> NamedSelection::NamedSelectionCreated;
+
+typedef std::list<boost::shared_ptr<Playlist> > PlaylistList;
+
+NamedSelection::NamedSelection (string n, PlaylistList& l)
+ : name (n)
+{
+ playlists = l;
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ string new_name;
+
+ /* rename playlists to reflect our ownership */
+
+ new_name = name;
+ new_name += '/';
+ new_name += (*i)->name();
+
+ (*i)->set_name (new_name);
+ (*i)->use();
+ }
+
+ NamedSelectionCreated (this);
+}
+
+NamedSelection::NamedSelection (Session& session, const XMLNode& node)
+{
+ XMLNode* lists_node;
+ const XMLProperty* property;
+
+ if ((property = node.property ("name")) == 0) {
+ throw failed_constructor();
+ }
+
+ name = property->value();
+
+ if ((lists_node = find_named_node (node, "Playlists")) == 0) {
+ return;
+ }
+
+ XMLNodeList nlist = lists_node->children();
+ XMLNodeConstIterator niter;
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ const XMLNode* plnode;
+ string playlist_name;
+ boost::shared_ptr<Playlist> playlist;
+
+ plnode = *niter;
+
+ if ((property = plnode->property ("name")) != 0) {
+ if ((playlist = session.playlist_by_name (property->value())) != 0) {
+ playlist->use();
+ playlists.push_back (playlist);
+ } else {
+ warning << string_compose (_("Chunk %1 uses an unknown playlist \"%2\""), name, property->value()) << endmsg;
+ }
+ } else {
+ error << string_compose (_("Chunk %1 contains misformed playlist information"), name) << endmsg;
+ throw failed_constructor();
+ }
+ }
+
+ NamedSelectionCreated (this);
+}
+
+NamedSelection::~NamedSelection ()
+{
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ (*i)->release ();
+ (*i)->GoingAway ();
+ }
+}
+
+int
+NamedSelection::set_state (const XMLNode& node)
+{
+ return 0;
+}
+
+XMLNode&
+NamedSelection::get_state ()
+{
+ XMLNode* root = new XMLNode ("NamedSelection");
+ XMLNode* child;
+
+ root->add_property ("name", name);
+ child = root->add_child ("Playlists");
+
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ XMLNode* plnode = new XMLNode ("Playlist");
+
+ plnode->add_property ("name", (*i)->name());
+ child->add_child_nocopy (*plnode);
+ }
+
+ return *root;
+}
diff --git a/libs/ardour/note.cc b/libs/ardour/note.cc
new file mode 100644
index 0000000000..ea1e7133af
--- /dev/null
+++ b/libs/ardour/note.cc
@@ -0,0 +1,110 @@
+/*
+ Copyright (C) 2007 Paul Davis
+ Author: Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/note.h>
+#include <iostream>
+
+namespace ARDOUR {
+
+Note::Note(uint8_t chan, double t, double d, uint8_t n, uint8_t v)
+ : _on_event(t, 3, NULL, true)
+ , _off_event(t + d, 3, NULL, true)
+{
+ assert(chan < 16);
+
+ _on_event.buffer()[0] = MIDI_CMD_NOTE_ON + chan;
+ _on_event.buffer()[1] = n;
+ _on_event.buffer()[2] = v;
+
+ _off_event.buffer()[0] = MIDI_CMD_NOTE_OFF + chan;
+ _off_event.buffer()[1] = n;
+ _off_event.buffer()[2] = 0x40;
+
+ assert(time() == t);
+ assert(duration() == d);
+ assert(note() == n);
+ assert(velocity() == v);
+ assert(_on_event.channel() == _off_event.channel());
+ assert(channel() == chan);
+}
+
+
+Note::Note(const Note& copy)
+ : _on_event(copy._on_event, true)
+ , _off_event(copy._off_event, true)
+{
+ assert(_on_event.buffer());
+ assert(_off_event.buffer());
+ /*
+ assert(copy._on_event.size == 3);
+ _on_event.buffer = _on_event_buffer;
+ memcpy(_on_event_buffer, copy._on_event_buffer, 3);
+
+ assert(copy._off_event.size == 3);
+ _off_event.buffer = _off_event_buffer;
+ memcpy(_off_event_buffer, copy._off_event_buffer, 3);
+ */
+
+ assert(time() == copy.time());
+ assert(end_time() == copy.end_time());
+ assert(note() == copy.note());
+ assert(velocity() == copy.velocity());
+ assert(duration() == copy.duration());
+ assert(_on_event.channel() == _off_event.channel());
+ assert(channel() == copy.channel());
+}
+
+Note::~Note()
+{
+ std::cerr << "Note::~Note() Note time: " << time()
+ << " pitch: " << int(note())
+ << " duration: " << duration()
+ << " end-time: " << end_time()
+ << " velocity: " << int(velocity())
+ << std::endl;
+}
+
+
+const Note&
+Note::operator=(const Note& copy)
+{
+ _on_event = copy._on_event;
+ _off_event = copy._off_event;
+ /*_on_event.time = copy._on_event.time;
+ assert(copy._on_event.size == 3);
+ memcpy(_on_event_buffer, copy._on_event_buffer, 3);
+
+ _off_event.time = copy._off_event.time;
+ assert(copy._off_event.size == 3);
+ memcpy(_off_event_buffer, copy._off_event_buffer, 3);
+ */
+
+ assert(time() == copy.time());
+ assert(end_time() == copy.end_time());
+ assert(note() == copy.note());
+ assert(velocity() == copy.velocity());
+ assert(duration() == copy.duration());
+ assert(_on_event.channel() == _off_event.channel());
+ assert(channel() == copy.channel());
+
+ return *this;
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/osc.cc b/libs/ardour/osc.cc
new file mode 100644
index 0000000000..b0bd35b8c6
--- /dev/null
+++ b/libs/ardour/osc.cc
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2006 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include <iostream>
+#include <fstream>
+#include <cstdio>
+#include <cstdlib>
+#include <cerrno>
+#include <algorithm>
+
+#include <sys/poll.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <pbd/pthread_utils.h>
+#include <pbd/file_utils.h>
+
+#include <ardour/osc.h>
+#include <ardour/session.h>
+#include <ardour/route.h>
+#include <ardour/audio_track.h>
+#include <ardour/filesystem_paths.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace sigc;
+using namespace std;
+
+static void error_callback(int num, const char *m, const char *path)
+{
+#ifdef DEBUG
+ fprintf(stderr, "liblo server error %d in path %s: %s\n", num, path, m);
+#endif
+}
+
+OSC::OSC (uint32_t port)
+ : _port(port)
+{
+ _shutdown = false;
+ _osc_server = 0;
+ _osc_unix_server = 0;
+ _osc_thread = 0;
+}
+
+int
+OSC::start ()
+{
+ char tmpstr[255];
+
+ if (_osc_server) {
+ /* already started */
+ return 0;
+ }
+
+ for (int j=0; j < 20; ++j) {
+ snprintf(tmpstr, sizeof(tmpstr), "%d", _port);
+
+ if ((_osc_server = lo_server_new (tmpstr, error_callback))) {
+ break;
+ }
+#ifdef DEBUG
+ cerr << "can't get osc at port: " << _port << endl;
+#endif
+ _port++;
+ continue;
+ }
+
+#ifdef ARDOUR_OSC_UNIX_SERVER
+
+ // APPEARS sluggish for now
+
+ // attempt to create unix socket server too
+
+ snprintf(tmpstr, sizeof(tmpstr), "/tmp/sooperlooper_XXXXXX");
+ int fd = mkstemp(tmpstr);
+
+ if (fd >= 0 ) {
+ unlink (tmpstr);
+ close (fd);
+
+ _osc_unix_server = lo_server_new (tmpstr, error_callback);
+
+ if (_osc_unix_server) {
+ _osc_unix_socket_path = tmpstr;
+ }
+ }
+#endif
+
+ cerr << "OSC @ " << get_server_url () << endl;
+
+ sys::path url_file;
+
+ if (find_file_in_search_path (ardour_search_path() + system_config_search_path(),
+ "osc_url", url_file)) {
+ _osc_url_file = url_file.to_string();
+ ofstream urlfile;
+ urlfile.open(_osc_url_file.c_str(), ios::trunc);
+ if ( urlfile )
+ {
+ urlfile << get_server_url () << endl;
+ urlfile.close();
+ }
+ else
+ {
+ cerr << "Couldn't write '" << _osc_url_file << "'" <<endl;
+ }
+ }
+
+ register_callbacks();
+
+ // lo_server_thread_add_method(_sthread, NULL, NULL, OSC::_dummy_handler, this);
+
+ if (!init_osc_thread()) {
+ return -1;
+ }
+ return 0;
+}
+
+int
+OSC::stop ()
+{
+ if (_osc_server == 0) {
+ /* already stopped */
+ return 0;
+ }
+
+ // stop server thread
+ terminate_osc_thread();
+
+ lo_server_free (_osc_server);
+ _osc_server = 0;
+
+ if (!_osc_unix_socket_path.empty()) {
+ // unlink it
+ unlink(_osc_unix_socket_path.c_str());
+ }
+
+ if (! _osc_url_file.empty() ) {
+ unlink(_osc_url_file.c_str() );
+ }
+ return 0;
+}
+
+OSC::~OSC()
+{
+ stop ();
+}
+
+void
+OSC::register_callbacks()
+{
+ lo_server srvs[2];
+ lo_server serv;
+
+ srvs[0] = _osc_server;
+ srvs[1] = _osc_unix_server;
+
+ for (size_t i = 0; i < 2; ++i) {
+
+ if (!srvs[i]) {
+ continue;
+ }
+
+ serv = srvs[i];
+
+#define REGISTER_CALLBACK(serv,path,types, function) lo_server_add_method (serv, path, types, OSC::_ ## function, this)
+
+ REGISTER_CALLBACK (serv, "/ardour/add_marker", "", add_marker);
+ REGISTER_CALLBACK (serv, "/ardour/loop_toggle", "", loop_toggle);
+ REGISTER_CALLBACK (serv, "/ardour/goto_start", "", goto_start);
+ REGISTER_CALLBACK (serv, "/ardour/goto_end", "", goto_end);
+ REGISTER_CALLBACK (serv, "/ardour/rewind", "", rewind);
+ REGISTER_CALLBACK (serv, "/ardour/ffwd", "", ffwd);
+ REGISTER_CALLBACK (serv, "/ardour/transport_stop", "", transport_stop);
+ REGISTER_CALLBACK (serv, "/ardour/transport_play", "", transport_play);
+ REGISTER_CALLBACK (serv, "/ardour/set_transport_speed", "f", set_transport_speed);
+ REGISTER_CALLBACK (serv, "/ardour/save_state", "", save_state);
+ REGISTER_CALLBACK (serv, "/ardour/prev_marker", "", prev_marker);
+ REGISTER_CALLBACK (serv, "/ardour/next_marker", "", next_marker);
+ REGISTER_CALLBACK (serv, "/ardour/undo", "", undo);
+ REGISTER_CALLBACK (serv, "/ardour/redo", "", redo);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_punch_in", "", toggle_punch_in);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_punch_out", "", toggle_punch_out);
+ REGISTER_CALLBACK (serv, "/ardour/rec_enable_toggle", "", rec_enable_toggle);
+ REGISTER_CALLBACK (serv, "/ardour/toggle_all_rec_enables", "", toggle_all_rec_enables);
+
+#if 0
+ REGISTER_CALLBACK (serv, "/ardour/*/#current_value", "", current_value);
+ REGISTER_CALLBACK (serv, "/ardour/set", "", set);
+#endif
+
+#if 0
+ // un/register_update args= s:ctrl s:returl s:retpath
+ lo_server_add_method(serv, "/register_update", "sss", OSC::global_register_update_handler, this);
+ lo_server_add_method(serv, "/unregister_update", "sss", OSC::global_unregister_update_handler, this);
+ lo_server_add_method(serv, "/register_auto_update", "siss", OSC::global_register_auto_update_handler, this);
+ lo_server_add_method(serv, "/unregister_auto_update", "sss", OSC::_global_unregister_auto_update_handler, this);
+#endif
+ }
+}
+
+bool
+OSC::init_osc_thread ()
+{
+ // create new thread to run server
+ if (pipe (_request_pipe)) {
+ cerr << "Cannot create osc request signal pipe" << strerror (errno) << endl;
+ return false;
+ }
+
+ if (fcntl (_request_pipe[0], F_SETFL, O_NONBLOCK)) {
+ cerr << "osc: cannot set O_NONBLOCK on signal read pipe " << strerror (errno) << endl;
+ return false;
+ }
+
+ if (fcntl (_request_pipe[1], F_SETFL, O_NONBLOCK)) {
+ cerr << "osc: cannot set O_NONBLOCK on signal write pipe " << strerror (errno) << endl;
+ return false;
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setstacksize(&attr, 500000);
+
+ pthread_create (&_osc_thread, &attr, &OSC::_osc_receiver, this);
+ if (!_osc_thread) {
+ return false;
+ }
+ pthread_attr_destroy(&attr);
+
+ //pthread_detach (_osc_thread);
+ return true;
+}
+
+void
+OSC::terminate_osc_thread ()
+{
+ void* status;
+
+ _shutdown = true;
+
+ poke_osc_thread ();
+
+ pthread_join (_osc_thread, &status);
+}
+
+void
+OSC::poke_osc_thread ()
+{
+ char c;
+
+ if (write (_request_pipe[1], &c, 1) != 1) {
+ cerr << "cannot send signal to osc thread! " << strerror (errno) << endl;
+ }
+}
+
+std::string
+OSC::get_server_url()
+{
+ string url;
+ char * urlstr;
+
+ if (_osc_server) {
+ urlstr = lo_server_get_url (_osc_server);
+ url = urlstr;
+ free (urlstr);
+ }
+
+ return url;
+}
+
+std::string
+OSC::get_unix_server_url()
+{
+ string url;
+ char * urlstr;
+
+ if (_osc_unix_server) {
+ urlstr = lo_server_get_url (_osc_unix_server);
+ url = urlstr;
+ free (urlstr);
+ }
+
+ return url;
+}
+
+
+/* server thread */
+
+void *
+OSC::_osc_receiver(void * arg)
+{
+ PBD::ThreadCreated (pthread_self(), X_("OSC"));
+
+ static_cast<OSC*> (arg)->osc_receiver();
+ return 0;
+}
+
+void
+OSC::osc_receiver()
+{
+ struct pollfd pfd[3];
+ int fds[3];
+ lo_server srvs[3];
+ int nfds = 0;
+ int timeout = -1;
+ int ret;
+
+ fds[0] = _request_pipe[0];
+ nfds++;
+
+ if (_osc_server && lo_server_get_socket_fd(_osc_server) >= 0) {
+ fds[nfds] = lo_server_get_socket_fd(_osc_server);
+ srvs[nfds] = _osc_server;
+ nfds++;
+ }
+
+ if (_osc_unix_server && lo_server_get_socket_fd(_osc_unix_server) >= 0) {
+ fds[nfds] = lo_server_get_socket_fd(_osc_unix_server);
+ srvs[nfds] = _osc_unix_server;
+ nfds++;
+ }
+
+
+ while (!_shutdown) {
+
+ for (int i=0; i < nfds; ++i) {
+ pfd[i].fd = fds[i];
+ pfd[i].events = POLLIN|POLLPRI|POLLHUP|POLLERR;
+ pfd[i].revents = 0;
+ }
+
+ again:
+ //cerr << "poll on " << nfds << " for " << timeout << endl;
+ if ((ret = poll (pfd, nfds, timeout)) < 0) {
+ if (errno == EINTR) {
+ /* gdb at work, perhaps */
+ goto again;
+ }
+
+ cerr << "OSC thread poll failed: " << strerror (errno) << endl;
+
+ break;
+ }
+
+ //cerr << "poll returned " << ret << " pfd[0].revents = " << pfd[0].revents << " pfd[1].revents = " << pfd[1].revents << endl;
+
+ if (_shutdown) {
+ break;
+ }
+
+ if ((pfd[0].revents & ~POLLIN)) {
+ cerr << "OSC: error polling extra port" << endl;
+ break;
+ }
+
+ for (int i=1; i < nfds; ++i) {
+ if (pfd[i].revents & POLLIN)
+ {
+ // this invokes callbacks
+ //cerr << "invoking recv on " << pfd[i].fd << endl;
+ lo_server_recv(srvs[i]);
+ }
+ }
+
+ }
+
+ //cerr << "SL engine shutdown" << endl;
+
+ if (_osc_server) {
+ int fd = lo_server_get_socket_fd(_osc_server);
+ if (fd >=0) {
+ // hack around
+ close(fd);
+ }
+ lo_server_free (_osc_server);
+ _osc_server = 0;
+ }
+
+ if (_osc_unix_server) {
+ cerr << "freeing unix server" << endl;
+ lo_server_free (_osc_unix_server);
+ _osc_unix_server = 0;
+ }
+
+ close(_request_pipe[0]);
+ close(_request_pipe[1]);
+}
+
+void
+OSC::set_session (Session& s)
+{
+ session = &s;
+ session->GoingAway.connect (mem_fun (*this, &OSC::session_going_away));
+}
+
+void
+OSC::session_going_away ()
+{
+ session = 0;
+}
+
+/* path callbacks */
+
+int
+OSC::current_value (const char *path, const char *types, lo_arg **argv, int argc, void *data, void* user_data)
+{
+#if 0
+ const char* returl;
+
+ if (argc < 3 || types == 0 || strlen (types) < 3 || types[0] != 's' || types[1] != 's' || types[2] != s) {
+ return 1;
+ }
+
+ const char *returl = argv[1]->s;
+ lo_address addr = find_or_cache_addr (returl);
+
+ const char *retpath = argv[2]->s;
+
+
+ if (strcmp (argv[0]->s, "transport_frame")) {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "transport_speed")) {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "transport_locked")) {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "punch_in") {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "punch_out") {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else if (strcmp (argv[0]->s, "rec_enable") {
+
+ if (session) {
+ lo_send (addr, retpath, "i", session->transport_frame());
+ }
+
+ } else {
+
+ /* error */
+ }
+#endif
+ return 0;
+}
diff --git a/libs/ardour/panner.cc b/libs/ardour/panner.cc
new file mode 100644
index 0000000000..ac8e4d05e3
--- /dev/null
+++ b/libs/ardour/panner.cc
@@ -0,0 +1,1489 @@
+/*
+ Copyright (C) 2004 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.
+
+*/
+
+#define __STDC_FORMAT_MACROS 1
+#include <inttypes.h>
+
+#include <cmath>
+#include <cerrno>
+#include <fstream>
+#include <cstdlib>
+#include <string>
+#include <cstdio>
+#include <locale.h>
+#include <unistd.h>
+#include <float.h>
+
+#include <glibmm.h>
+
+#include <pbd/error.h>
+#include <pbd/failed_constructor.h>
+#include <pbd/xml++.h>
+#include <pbd/enumwriter.h>
+
+#include <ardour/session.h>
+#include <ardour/panner.h>
+#include <ardour/utils.h>
+
+#include <ardour/runtime_functions.h>
+#include <ardour/buffer_set.h>
+
+#include "i18n.h"
+
+#include <pbd/mathfix.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+float Panner::current_automation_version_number = 1.0;
+
+string EqualPowerStereoPanner::name = "Equal Power Stereo";
+string Multi2dPanner::name = "Multiple (2D)";
+
+/* this is a default mapper of control values to a pan position
+ others can be imagined.
+*/
+
+static pan_t direct_control_to_pan (double fract) {
+ return fract;
+}
+
+static double direct_pan_to_control (pan_t val) {
+ return val;
+}
+
+StreamPanner::StreamPanner (Panner& p, Parameter param)
+ : parent (p)
+ , _control (new PanControllable(p.session(), X_("panner"), *this, param))
+{
+ assert(param.type() != NullAutomation);
+
+ _muted = false;
+
+ parent.session().add_controllable (_control);
+
+ x = 0.5;
+ y = 0.5;
+ z = 0.5;
+}
+
+StreamPanner::~StreamPanner ()
+{
+}
+
+void
+StreamPanner::PanControllable::set_value (float val)
+{
+ panner.set_position (direct_control_to_pan (val));
+}
+
+float
+StreamPanner::PanControllable::get_value (void) const
+{
+ float xpos;
+ panner.get_effective_position (xpos);
+ return direct_pan_to_control (xpos);
+}
+
+bool
+StreamPanner::PanControllable::can_send_feedback () const
+{
+ AutoState astate = panner.get_parent().automation_state ();
+
+ if ((astate == Play) || (astate == Touch && !panner.get_parent().touching())) {
+ return true;
+ }
+
+ return false;
+}
+
+void
+StreamPanner::set_muted (bool yn)
+{
+ if (yn != _muted) {
+ _muted = yn;
+ StateChanged ();
+ }
+}
+
+void
+StreamPanner::set_position (float xpos, bool link_call)
+{
+ if (!link_call && parent.linked()) {
+ parent.set_position (xpos, *this);
+ }
+
+ if (x != xpos) {
+ x = xpos;
+ update ();
+ Changed ();
+ _control->Changed ();
+ }
+}
+
+void
+StreamPanner::set_position (float xpos, float ypos, bool link_call)
+{
+ if (!link_call && parent.linked()) {
+ parent.set_position (xpos, ypos, *this);
+ }
+
+ if (x != xpos || y != ypos) {
+
+ x = xpos;
+ y = ypos;
+ update ();
+ Changed ();
+ }
+}
+
+void
+StreamPanner::set_position (float xpos, float ypos, float zpos, bool link_call)
+{
+ if (!link_call && parent.linked()) {
+ parent.set_position (xpos, ypos, zpos, *this);
+ }
+
+ if (x != xpos || y != ypos || z != zpos) {
+ x = xpos;
+ y = ypos;
+ z = zpos;
+ update ();
+ Changed ();
+ }
+}
+
+int
+StreamPanner::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+ XMLNodeConstIterator iter;
+
+ if ((prop = node.property (X_("muted")))) {
+ set_muted (prop->value() == "yes");
+ }
+
+ return 0;
+}
+
+void
+StreamPanner::add_state (XMLNode& node)
+{
+ node.add_property (X_("muted"), (muted() ? "yes" : "no"));
+}
+
+/*---------------------------------------------------------------------- */
+
+BaseStereoPanner::BaseStereoPanner (Panner& p, Parameter param)
+ : StreamPanner (p, param)
+{
+}
+
+BaseStereoPanner::~BaseStereoPanner ()
+{
+}
+
+int
+BaseStereoPanner::load (istream& in, string path, uint32_t& linecnt)
+{
+ char line[128];
+ LocaleGuard lg (X_("POSIX"));
+
+ _control->list()->clear ();
+
+ while (in.getline (line, sizeof (line), '\n')) {
+ nframes_t when;
+ double value;
+
+ ++linecnt;
+
+ if (strcmp (line, "end") == 0) {
+ break;
+ }
+
+ if (sscanf (line, "%" PRIu32 " %lf", &when, &value) != 2) {
+ warning << string_compose(_("badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"), linecnt, path, line) << endmsg;
+ continue;
+ }
+
+ _control->list()->fast_simple_add (when, value);
+ }
+
+ /* now that we are done loading */
+
+ _control->list()->StateChanged ();
+
+ return 0;
+}
+
+void
+BaseStereoPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
+{
+ assert(obufs.count().n_audio() == 2);
+
+ pan_t delta;
+ Sample* dst;
+ pan_t pan;
+
+ if (_muted) {
+ return;
+ }
+
+ Sample* const src = srcbuf.data();
+
+ /* LEFT */
+
+ dst = obufs.get_audio(0).data();
+
+ if (fabsf ((delta = (left - desired_left))) > 0.002) { // about 1 degree of arc
+
+ /* interpolate over 64 frames or nframes, whichever is smaller */
+
+ nframes_t limit = min ((nframes_t)64, nframes);
+ nframes_t n;
+
+ delta = -(delta / (float) (limit));
+
+ for (n = 0; n < limit; n++) {
+ left_interp = left_interp + delta;
+ left = left_interp + 0.9 * (left - left_interp);
+ dst[n] += src[n] * left * gain_coeff;
+ }
+
+ pan = left * gain_coeff;
+
+ mix_buffers_with_gain (dst+n,src+n,nframes-n,pan);
+
+ } else {
+
+ left = desired_left;
+ left_interp = left;
+
+ if ((pan = (left * gain_coeff)) != 1.0f) {
+
+ if (pan != 0.0f) {
+
+ mix_buffers_with_gain(dst,src,nframes,pan);
+
+ /* mark that we wrote into the buffer */
+
+ // obufs[0] = 0;
+
+ }
+
+ } else {
+
+ mix_buffers_no_gain(dst,src,nframes);
+
+ /* mark that we wrote into the buffer */
+
+ // obufs[0] = 0;
+ }
+ }
+
+ /* RIGHT */
+
+ dst = obufs.get_audio(1).data();
+
+ if (fabsf ((delta = (right - desired_right))) > 0.002) { // about 1 degree of arc
+
+ /* interpolate over 64 frames or nframes, whichever is smaller */
+
+ nframes_t limit = min ((nframes_t)64, nframes);
+ nframes_t n;
+
+ delta = -(delta / (float) (limit));
+
+ for (n = 0; n < limit; n++) {
+ right_interp = right_interp + delta;
+ right = right_interp + 0.9 * (right - right_interp);
+ dst[n] += src[n] * right * gain_coeff;
+ }
+
+ pan = right * gain_coeff;
+
+ mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
+
+ /* XXX it would be nice to mark the buffer as written to */
+
+ } else {
+
+ right = desired_right;
+ right_interp = right;
+
+ if ((pan = (right * gain_coeff)) != 1.0f) {
+
+ if (pan != 0.0f) {
+
+ mix_buffers_with_gain(dst,src,nframes,pan);
+
+ /* XXX it would be nice to mark the buffer as written to */
+ }
+
+ } else {
+
+ mix_buffers_no_gain(dst,src,nframes);
+
+ /* XXX it would be nice to mark the buffer as written to */
+ }
+ }
+}
+
+/*---------------------------------------------------------------------- */
+
+EqualPowerStereoPanner::EqualPowerStereoPanner (Panner& p, Parameter param)
+ : BaseStereoPanner (p, param)
+{
+ update ();
+
+ left = desired_left;
+ right = desired_right;
+ left_interp = left;
+ right_interp = right;
+}
+
+EqualPowerStereoPanner::~EqualPowerStereoPanner ()
+{
+}
+
+void
+EqualPowerStereoPanner::update ()
+{
+ /* it would be very nice to split this out into a virtual function
+ that can be accessed from BaseStereoPanner and used in distribute_automated().
+
+ but the place where its used in distribute_automated() is a tight inner loop,
+ and making "nframes" virtual function calls to compute values is an absurd
+ overhead.
+ */
+
+ /* x == 0 => hard left
+ x == 1 => hard right
+ */
+
+ float panR = x;
+ float panL = 1 - panR;
+
+ const float pan_law_attenuation = -3.0f;
+ const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
+
+ desired_left = panL * (scale * panL + 1.0f - scale);
+ desired_right = panR * (scale * panR + 1.0f - scale);
+
+ effective_x = x;
+ _control->set_value(x);
+}
+
+void
+EqualPowerStereoPanner::distribute_automated (AudioBuffer& srcbuf, BufferSet& obufs,
+ nframes_t start, nframes_t end, nframes_t nframes,
+ pan_t** buffers)
+{
+ assert(obufs.count().n_audio() == 2);
+
+ Sample* dst;
+ pan_t* pbuf;
+ Sample* const src = srcbuf.data();
+
+ /* fetch positional data */
+
+ if (!_control->list()->curve().rt_safe_get_vector (start, end, buffers[0], nframes)) {
+ /* fallback */
+ if (!_muted) {
+ distribute (srcbuf, obufs, 1.0, nframes);
+ }
+ return;
+ }
+
+ /* store effective pan position. do this even if we are muted */
+
+ if (nframes > 0) {
+ effective_x = buffers[0][nframes-1];
+ _control->set_value(effective_x); // signal, update UI
+ }
+
+ if (_muted) {
+ return;
+ }
+
+ /* apply pan law to convert positional data into pan coefficients for
+ each buffer (output)
+ */
+
+ const float pan_law_attenuation = -3.0f;
+ const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
+
+ for (nframes_t n = 0; n < nframes; ++n) {
+
+ float panR = buffers[0][n];
+ float panL = 1 - panR;
+
+ buffers[0][n] = panL * (scale * panL + 1.0f - scale);
+ buffers[1][n] = panR * (scale * panR + 1.0f - scale);
+ }
+
+ /* LEFT */
+
+ dst = obufs.get_audio(0).data();
+ pbuf = buffers[0];
+
+ for (nframes_t n = 0; n < nframes; ++n) {
+ dst[n] += src[n] * pbuf[n];
+ }
+
+ /* XXX it would be nice to mark the buffer as written to */
+
+ /* RIGHT */
+
+ dst = obufs.get_audio(1).data();
+ pbuf = buffers[1];
+
+ for (nframes_t n = 0; n < nframes; ++n) {
+ dst[n] += src[n] * pbuf[n];
+ }
+
+ /* XXX it would be nice to mark the buffer as written to */
+}
+
+StreamPanner*
+EqualPowerStereoPanner::factory (Panner& parent, Parameter param)
+{
+ return new EqualPowerStereoPanner (parent, param);
+}
+
+XMLNode&
+EqualPowerStereoPanner::get_state (void)
+{
+ return state (true);
+}
+
+XMLNode&
+EqualPowerStereoPanner::state (bool full_state)
+{
+ XMLNode* root = new XMLNode ("StreamPanner");
+ char buf[64];
+ LocaleGuard lg (X_("POSIX"));
+
+ snprintf (buf, sizeof (buf), "%.12g", x);
+ root->add_property (X_("x"), buf);
+ root->add_property (X_("type"), EqualPowerStereoPanner::name);
+
+ XMLNode* autonode = new XMLNode (X_("Automation"));
+ autonode->add_child_nocopy (_control->list()->state (full_state));
+ root->add_child_nocopy (*autonode);
+
+ StreamPanner::add_state (*root);
+
+ root->add_child_nocopy (_control->get_state ());
+
+ return *root;
+}
+
+int
+EqualPowerStereoPanner::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+ float pos;
+ LocaleGuard lg (X_("POSIX"));
+
+ if ((prop = node.property (X_("x")))) {
+ pos = atof (prop->value().c_str());
+ set_position (pos, true);
+ }
+
+ StreamPanner::set_state (node);
+
+ for (XMLNodeConstIterator iter = node.children().begin(); iter != node.children().end(); ++iter) {
+
+ if ((*iter)->name() == X_("controllable")) {
+ if ((prop = (*iter)->property("name")) != 0 && prop->value() == "panner") {
+ _control->set_state (**iter);
+ }
+
+ } else if ((*iter)->name() == X_("Automation")) {
+
+ _control->list()->set_state (*((*iter)->children().front()));
+
+ if (_control->list()->automation_state() != Off) {
+ set_position (_control->list()->eval (parent.session().transport_frame()));
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+Multi2dPanner::Multi2dPanner (Panner& p, Parameter param)
+ : StreamPanner (p, param)
+{
+ update ();
+}
+
+Multi2dPanner::~Multi2dPanner ()
+{
+}
+
+void
+Multi2dPanner::update ()
+{
+ static const float BIAS = FLT_MIN;
+ uint32_t i;
+ uint32_t nouts = parent.outputs.size();
+ float dsq[nouts];
+ float f, fr;
+ vector<pan_t> pans;
+
+ f = 0.0f;
+
+ for (i = 0; i < nouts; i++) {
+ dsq[i] = ((x - parent.outputs[i].x) * (x - parent.outputs[i].x) + (y - parent.outputs[i].y) * (y - parent.outputs[i].y) + BIAS);
+ if (dsq[i] < 0.0) {
+ dsq[i] = 0.0;
+ }
+ f += dsq[i] * dsq[i];
+ }
+#ifdef __APPLE__
+ // terrible hack to support OSX < 10.3.9 builds
+ fr = (float) (1.0 / sqrt((double)f));
+#else
+ fr = 1.0 / sqrtf(f);
+#endif
+ for (i = 0; i < nouts; ++i) {
+ parent.outputs[i].desired_pan = 1.0f - (dsq[i] * fr);
+ }
+
+ effective_x = x;
+ _control->set_value(x);
+}
+
+void
+Multi2dPanner::distribute (AudioBuffer& srcbuf, BufferSet& obufs, gain_t gain_coeff, nframes_t nframes)
+{
+ Sample* dst;
+ pan_t pan;
+ vector<Panner::Output>::iterator o;
+ uint32_t n;
+
+ if (_muted) {
+ return;
+ }
+
+ Sample* const src = srcbuf.data();
+
+
+ for (n = 0, o = parent.outputs.begin(); o != parent.outputs.end(); ++o, ++n) {
+
+ dst = obufs.get_audio(n).data();
+
+#ifdef CAN_INTERP
+ if (fabsf ((delta = (left_interp - desired_left))) > 0.002) { // about 1 degree of arc
+
+ /* interpolate over 64 frames or nframes, whichever is smaller */
+
+ nframes_t limit = min ((nframes_t)64, nframes);
+ nframes_t n;
+
+ delta = -(delta / (float) (limit));
+
+ for (n = 0; n < limit; n++) {
+ left_interp = left_interp + delta;
+ left = left_interp + 0.9 * (left - left_interp);
+ dst[n] += src[n] * left * gain_coeff;
+ }
+
+ pan = left * gain_coeff;
+ mix_buffers_with_gain(dst+n,src+n,nframes-n,pan);
+
+ } else {
+
+#else
+ pan = (*o).desired_pan;
+
+ if ((pan *= gain_coeff) != 1.0f) {
+
+ if (pan != 0.0f) {
+ mix_buffers_with_gain(dst,src,nframes,pan);
+ }
+ } else {
+ mix_buffers_no_gain(dst,src,nframes);
+ }
+#endif
+#ifdef CAN_INTERP
+ }
+#endif
+ }
+
+ return;
+}
+
+void
+Multi2dPanner::distribute_automated (AudioBuffer& src, BufferSet& obufs,
+ nframes_t start, nframes_t end, nframes_t nframes,
+ pan_t** buffers)
+{
+ if (_muted) {
+ return;
+ }
+
+ /* what ? */
+
+ return;
+}
+
+StreamPanner*
+Multi2dPanner::factory (Panner& p, Parameter param)
+{
+ return new Multi2dPanner (p, param);
+}
+
+int
+Multi2dPanner::load (istream& in, string path, uint32_t& linecnt)
+{
+ return 0;
+}
+
+XMLNode&
+Multi2dPanner::get_state (void)
+{
+ return state (true);
+}
+
+XMLNode&
+Multi2dPanner::state (bool full_state)
+{
+ XMLNode* root = new XMLNode ("StreamPanner");
+ char buf[64];
+ LocaleGuard lg (X_("POSIX"));
+
+ snprintf (buf, sizeof (buf), "%.12g", x);
+ root->add_property (X_("x"), buf);
+ snprintf (buf, sizeof (buf), "%.12g", y);
+ root->add_property (X_("y"), buf);
+ root->add_property (X_("type"), Multi2dPanner::name);
+
+ /* XXX no meaningful automation yet */
+
+ return *root;
+}
+
+int
+Multi2dPanner::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+ float newx,newy;
+ LocaleGuard lg (X_("POSIX"));
+
+ newx = -1;
+ newy = -1;
+
+ if ((prop = node.property (X_("x")))) {
+ newx = atof (prop->value().c_str());
+ }
+
+ if ((prop = node.property (X_("y")))) {
+ newy = atof (prop->value().c_str());
+ }
+
+ if (x < 0 || y < 0) {
+ error << _("badly-formed positional data for Multi2dPanner - ignored")
+ << endmsg;
+ return -1;
+ }
+
+ set_position (newx, newy);
+ return 0;
+}
+
+/*---------------------------------------------------------------------- */
+
+Panner::Panner (string name, Session& s)
+ : _session (s)
+{
+ set_name (name);
+
+ _linked = false;
+ _link_direction = SameDirection;
+ _bypassed = false;
+}
+
+Panner::~Panner ()
+{
+}
+
+void
+Panner::set_linked (bool yn)
+{
+ if (yn != _linked) {
+ _linked = yn;
+ _session.set_dirty ();
+ LinkStateChanged (); /* EMIT SIGNAL */
+ }
+}
+
+void
+Panner::set_link_direction (LinkDirection ld)
+{
+ if (ld != _link_direction) {
+ _link_direction = ld;
+ _session.set_dirty ();
+ LinkStateChanged (); /* EMIT SIGNAL */
+ }
+}
+
+void
+Panner::set_bypassed (bool yn)
+{
+ if (yn != _bypassed) {
+ _bypassed = yn;
+ StateChanged ();
+ }
+}
+
+
+void
+Panner::reset (uint32_t nouts, uint32_t npans)
+{
+ uint32_t n;
+ bool changed = false;
+
+ if (nouts < 2 || (nouts == outputs.size() && npans == size())) {
+ return;
+ }
+
+ n = size();
+ clear ();
+
+ if (n != npans) {
+ changed = true;
+ }
+
+ n = outputs.size();
+ outputs.clear ();
+
+ if (n != nouts) {
+ changed = true;
+ }
+
+ switch (nouts) {
+ case 0:
+ break;
+
+ case 1:
+ fatal << _("programming error:")
+ << X_("Panner::reset() called with a single output")
+ << endmsg;
+ /*NOTREACHED*/
+ break;
+
+ case 2:
+ /* line */
+ outputs.push_back (Output (0, 0));
+ outputs.push_back (Output (1.0, 0));
+
+ for (n = 0; n < npans; ++n) {
+ push_back (new EqualPowerStereoPanner (*this, Parameter(PanAutomation, n)));
+ }
+ break;
+
+ case 3: // triangle
+ outputs.push_back (Output (0.5, 0));
+ outputs.push_back (Output (0, 1.0));
+ outputs.push_back (Output (1.0, 1.0));
+
+ for (n = 0; n < npans; ++n) {
+ push_back (new Multi2dPanner (*this, Parameter(PanAutomation, n)));
+ }
+
+ break;
+
+ case 4: // square
+ outputs.push_back (Output (0, 0));
+ outputs.push_back (Output (1.0, 0));
+ outputs.push_back (Output (1.0, 1.0));
+ outputs.push_back (Output (0, 1.0));
+
+ for (n = 0; n < npans; ++n) {
+ push_back (new Multi2dPanner (*this, Parameter(PanAutomation, n)));
+ }
+
+ break;
+
+ case 5: //square+offcenter center
+ outputs.push_back (Output (0, 0));
+ outputs.push_back (Output (1.0, 0));
+ outputs.push_back (Output (1.0, 1.0));
+ outputs.push_back (Output (0, 1.0));
+ outputs.push_back (Output (0.5, 0.75));
+
+ for (n = 0; n < npans; ++n) {
+ push_back (new Multi2dPanner (*this, Parameter(PanAutomation, n)));
+ }
+
+ break;
+
+ default:
+ /* XXX horrible placement. FIXME */
+ for (n = 0; n < nouts; ++n) {
+ outputs.push_back (Output (0.1 * n, 0.1 * n));
+ }
+
+ for (n = 0; n < npans; ++n) {
+ push_back (new Multi2dPanner (*this, Parameter(PanAutomation, n)));
+ }
+
+ break;
+ }
+
+ for (iterator x = begin(); x != end(); ++x) {
+ (*x)->update ();
+ }
+
+ /* force hard left/right panning in a common case: 2in/2out
+ */
+
+ if (npans == 2 && outputs.size() == 2) {
+
+ /* Do this only if we changed configuration, or our configuration
+ appears to be the default set up (center).
+ */
+
+ float left;
+ float right;
+
+ front()->get_position (left);
+ back()->get_position (right);
+
+ if (changed || ((left == 0.5) && (right == 0.5))) {
+
+ front()->set_position (0.0);
+ front()->pan_control()->list()->reset_default (0.0);
+
+ back()->set_position (1.0);
+ back()->pan_control()->list()->reset_default (1.0);
+
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ Changed (); /* EMIT SIGNAL */
+ }
+
+ return;
+}
+
+void
+Panner::remove (uint32_t which)
+{
+ vector<StreamPanner*>::iterator i;
+ for (i = begin(); i != end() && which; ++i, --which);
+
+ if (i != end()) {
+ delete *i;
+ erase (i);
+ }
+}
+
+void
+Panner::clear ()
+{
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ delete *i;
+ }
+
+ vector<StreamPanner*>::clear ();
+}
+
+void
+Panner::set_automation_style (AutoStyle style)
+{
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ (*i)->pan_control()->list()->set_automation_style (style);
+ }
+ _session.set_dirty ();
+}
+
+void
+Panner::set_automation_state (AutoState state)
+{
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ (*i)->pan_control()->list()->set_automation_state (state);
+ }
+ _session.set_dirty ();
+}
+
+AutoState
+Panner::automation_state () const
+{
+ if (!empty()) {
+ return front()->pan_control()->list()->automation_state ();
+ } else {
+ return Off;
+ }
+}
+
+AutoStyle
+Panner::automation_style () const
+{
+ if (!empty()) {
+ return front()->pan_control()->list()->automation_style ();
+ } else {
+ return Absolute;
+ }
+}
+
+void
+Panner::transport_stopped (nframes_t frame)
+{
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ (*i)->pan_control()->list()->reposition_for_rt_add (frame);
+ }
+}
+
+void
+Panner::snapshot (nframes_t now)
+{
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ boost::shared_ptr<AutomationList> list = (*i)->pan_control()->list();
+ if (list->automation_write())
+ list->rt_add(now, (*i)->pan_control()->get_value());
+ }
+}
+
+void
+Panner::clear_automation ()
+{
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ (*i)->pan_control()->list()->clear ();
+ }
+ _session.set_dirty ();
+}
+
+struct PanPlugins {
+ string name;
+ uint32_t nouts;
+ StreamPanner* (*factory)(Panner&, Parameter);
+};
+
+PanPlugins pan_plugins[] = {
+ { EqualPowerStereoPanner::name, 2, EqualPowerStereoPanner::factory },
+ { Multi2dPanner::name, 3, Multi2dPanner::factory },
+ { string (""), 0, 0 }
+};
+
+XMLNode&
+Panner::get_state (void)
+{
+ return state (true);
+}
+
+XMLNode&
+Panner::state (bool full)
+{
+ XMLNode* root = new XMLNode (X_("Panner"));
+ char buf[32];
+
+ root->add_property (X_("linked"), (_linked ? "yes" : "no"));
+ root->add_property (X_("link_direction"), enum_2_string (_link_direction));
+ root->add_property (X_("bypassed"), (bypassed() ? "yes" : "no"));
+
+ /* add each output */
+
+ for (vector<Panner::Output>::iterator o = outputs.begin(); o != outputs.end(); ++o) {
+ XMLNode* onode = new XMLNode (X_("Output"));
+ snprintf (buf, sizeof (buf), "%.12g", (*o).x);
+ onode->add_property (X_("x"), buf);
+ snprintf (buf, sizeof (buf), "%.12g", (*o).y);
+ onode->add_property (X_("y"), buf);
+ root->add_child_nocopy (*onode);
+ }
+
+ for (vector<StreamPanner*>::const_iterator i = begin(); i != end(); ++i) {
+ root->add_child_nocopy ((*i)->state (full));
+ }
+
+ return *root;
+}
+
+int
+Panner::set_state (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ const XMLProperty *prop;
+ uint32_t i;
+ StreamPanner* sp;
+ LocaleGuard lg (X_("POSIX"));
+
+ clear ();
+ outputs.clear ();
+
+ if ((prop = node.property (X_("linked"))) != 0) {
+ set_linked (prop->value() == "yes");
+ }
+
+
+ if ((prop = node.property (X_("bypassed"))) != 0) {
+ set_bypassed (prop->value() == "yes");
+ }
+
+ if ((prop = node.property (X_("link_direction"))) != 0) {
+ LinkDirection ld; /* here to provide type information */
+ set_link_direction (LinkDirection (string_2_enum (prop->value(), ld)));
+ }
+
+ nlist = node.children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == X_("Output")) {
+
+ float x, y;
+
+ prop = (*niter)->property (X_("x"));
+ sscanf (prop->value().c_str(), "%g", &x);
+
+ prop = (*niter)->property (X_("y"));
+ sscanf (prop->value().c_str(), "%g", &y);
+
+ outputs.push_back (Output (x, y));
+ }
+ }
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ if ((*niter)->name() == X_("StreamPanner")) {
+
+ if ((prop = (*niter)->property (X_("type")))) {
+
+ for (i = 0; pan_plugins[i].factory; ++i) {
+ if (prop->value() == pan_plugins[i].name) {
+
+
+ /* note that we assume that all the stream panners
+ are of the same type. pretty good
+ assumption, but its still an assumption.
+ */
+
+ sp = pan_plugins[i].factory (*this, Parameter(PanAutomation, 0));
+
+ if (sp->set_state (**niter) == 0) {
+ push_back (sp);
+ }
+
+ break;
+ }
+ }
+
+
+ if (!pan_plugins[i].factory) {
+ error << string_compose (_("Unknown panner plugin \"%1\" found in pan state - ignored"),
+ prop->value())
+ << endmsg;
+ }
+
+ } else {
+ error << _("panner plugin node has no type information!")
+ << endmsg;
+ return -1;
+ }
+
+ }
+ }
+
+ /* don't try to do old-school automation loading if it wasn't marked as existing */
+
+ if ((prop = node.property (X_("automation")))) {
+
+ /* automation path is relative */
+
+ automation_path = _session.automation_dir();
+ automation_path += prop->value ();
+ }
+
+ return 0;
+}
+
+
+
+bool
+Panner::touching () const
+{
+ for (vector<StreamPanner*>::const_iterator i = begin(); i != end(); ++i) {
+ if ((*i)->pan_control()->list()->touching ()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+Panner::set_position (float xpos, StreamPanner& orig)
+{
+ float xnow;
+ float xdelta ;
+ float xnew;
+
+ orig.get_position (xnow);
+ xdelta = xpos - xnow;
+
+ if (_link_direction == SameDirection) {
+
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ if (*i == &orig) {
+ (*i)->set_position (xpos, true);
+ } else {
+ (*i)->get_position (xnow);
+ xnew = min (1.0f, xnow + xdelta);
+ xnew = max (0.0f, xnew);
+ (*i)->set_position (xnew, true);
+ }
+ }
+
+ } else {
+
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ if (*i == &orig) {
+ (*i)->set_position (xpos, true);
+ } else {
+ (*i)->get_position (xnow);
+ xnew = min (1.0f, xnow - xdelta);
+ xnew = max (0.0f, xnew);
+ (*i)->set_position (xnew, true);
+ }
+ }
+ }
+}
+
+void
+Panner::set_position (float xpos, float ypos, StreamPanner& orig)
+{
+ float xnow, ynow;
+ float xdelta, ydelta;
+ float xnew, ynew;
+
+ orig.get_position (xnow, ynow);
+ xdelta = xpos - xnow;
+ ydelta = ypos - ynow;
+
+ if (_link_direction == SameDirection) {
+
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ if (*i == &orig) {
+ (*i)->set_position (xpos, ypos, true);
+ } else {
+ (*i)->get_position (xnow, ynow);
+
+ xnew = min (1.0f, xnow + xdelta);
+ xnew = max (0.0f, xnew);
+
+ ynew = min (1.0f, ynow + ydelta);
+ ynew = max (0.0f, ynew);
+
+ (*i)->set_position (xnew, ynew, true);
+ }
+ }
+
+ } else {
+
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ if (*i == &orig) {
+ (*i)->set_position (xpos, ypos, true);
+ } else {
+ (*i)->get_position (xnow, ynow);
+
+ xnew = min (1.0f, xnow - xdelta);
+ xnew = max (0.0f, xnew);
+
+ ynew = min (1.0f, ynow - ydelta);
+ ynew = max (0.0f, ynew);
+
+ (*i)->set_position (xnew, ynew, true);
+ }
+ }
+ }
+}
+
+void
+Panner::set_position (float xpos, float ypos, float zpos, StreamPanner& orig)
+{
+ float xnow, ynow, znow;
+ float xdelta, ydelta, zdelta;
+ float xnew, ynew, znew;
+
+ orig.get_position (xnow, ynow, znow);
+ xdelta = xpos - xnow;
+ ydelta = ypos - ynow;
+ zdelta = zpos - znow;
+
+ if (_link_direction == SameDirection) {
+
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ if (*i == &orig) {
+ (*i)->set_position (xpos, ypos, zpos, true);
+ } else {
+ (*i)->get_position (xnow, ynow, znow);
+
+ xnew = min (1.0f, xnow + xdelta);
+ xnew = max (0.0f, xnew);
+
+ ynew = min (1.0f, ynow + ydelta);
+ ynew = max (0.0f, ynew);
+
+ znew = min (1.0f, znow + zdelta);
+ znew = max (0.0f, znew);
+
+ (*i)->set_position (xnew, ynew, znew, true);
+ }
+ }
+
+ } else {
+
+ for (vector<StreamPanner*>::iterator i = begin(); i != end(); ++i) {
+ if (*i == &orig) {
+ (*i)->set_position (xpos, ypos, true);
+ } else {
+ (*i)->get_position (xnow, ynow, znow);
+
+ xnew = min (1.0f, xnow - xdelta);
+ xnew = max (0.0f, xnew);
+
+ ynew = min (1.0f, ynow - ydelta);
+ ynew = max (0.0f, ynew);
+
+ znew = min (1.0f, znow + zdelta);
+ znew = max (0.0f, znew);
+
+ (*i)->set_position (xnew, ynew, znew, true);
+ }
+ }
+ }
+}
+
+void
+Panner::distribute_no_automation (BufferSet& inbufs, BufferSet& outbufs, nframes_t nframes, nframes_t offset, gain_t gain_coeff)
+{
+ if (outbufs.count().n_audio() == 0) {
+ // Don't want to lose audio...
+ assert(inbufs.count().n_audio() == 0);
+ return;
+ }
+
+ // We shouldn't be called in the first place...
+ assert(!bypassed());
+ assert(!empty());
+
+
+ if (outbufs.count().n_audio() == 1) {
+
+ AudioBuffer& dst = outbufs.get_audio(0);
+
+ if (gain_coeff == 0.0f) {
+
+ /* only one output, and gain was zero, so make it silent */
+
+ dst.silence(offset);
+
+ } else if (gain_coeff == 1.0f){
+
+ /* mix all buffers into the output */
+
+ // copy the first
+ dst.read_from(inbufs.get_audio(0), nframes, offset);
+
+ // accumulate starting with the second
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+ for (++i; i != inbufs.audio_end(); ++i) {
+ dst.accumulate_from(*i, nframes, offset);
+ }
+
+ } else {
+
+ /* mix all buffers into the output, scaling them all by the gain */
+
+ // copy the first
+ dst.read_from(inbufs.get_audio(0), nframes, offset);
+
+ // accumulate (with gain) starting with the second
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+ for (++i; i != inbufs.audio_end(); ++i) {
+ dst.accumulate_with_gain_from(*i, nframes, offset, gain_coeff);
+ }
+
+ }
+
+ return;
+ }
+
+ /* the terrible silence ... */
+ for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
+ i->silence(nframes, offset);
+ }
+
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+
+ for (iterator pan = begin(); pan != end() && i != inbufs.audio_end(); ++pan, ++i) {
+ (*pan)->distribute (*i, outbufs, gain_coeff, nframes);
+ }
+}
+
+void
+Panner::distribute (BufferSet& inbufs, BufferSet& outbufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
+{
+ if (outbufs.count().n_audio() == 0) {
+ // Failing to deliver audio we were asked to deliver is a bug
+ assert(inbufs.count().n_audio() == 0);
+ return;
+ }
+
+ // We shouldn't be called in the first place...
+ assert(!bypassed());
+ assert(!empty());
+
+ // If we shouldn't play automation defer to distribute_no_automation
+ if ( !( automation_state() & Play ||
+ ((automation_state() & Touch) && !touching()) ) ) {
+
+ // Speed quietning
+ gain_t gain_coeff = 1.0;
+ if (fabsf(_session.transport_speed()) > 1.5f) {
+ gain_coeff = speed_quietning;
+ }
+
+ distribute_no_automation(inbufs, outbufs, nframes, offset, gain_coeff);
+ return;
+ }
+
+ // Otherwise.. let the automation flow, baby
+
+ if (outbufs.count().n_audio() == 1) {
+
+ AudioBuffer& dst = outbufs.get_audio(0);
+
+ // FIXME: apply gain automation?
+
+ // copy the first
+ dst.read_from(inbufs.get_audio(0), nframes, offset);
+
+ // accumulate starting with the second
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+ for (++i; i != inbufs.audio_end(); ++i) {
+ dst.accumulate_from(*i, nframes, offset);
+ }
+
+ return;
+ }
+
+ // More than 1 output, we should have 1 panner for each input
+ assert(size() == inbufs.count().n_audio());
+
+ /* the terrible silence ... */
+ for (BufferSet::audio_iterator i = outbufs.audio_begin(); i != outbufs.audio_end(); ++i) {
+ i->silence(nframes, offset);
+ }
+
+ BufferSet::audio_iterator i = inbufs.audio_begin();
+ for (iterator pan = begin(); pan != end(); ++pan, ++i) {
+ (*pan)->distribute_automated (*i, outbufs, start_frame, end_frame, nframes, _session.pan_automation_buffer());
+ }
+}
+
+/* old school automation handling */
+
+void
+Panner::set_name (string str)
+{
+ automation_path = _session.automation_dir();
+ automation_path += _session.snap_name();
+ automation_path += "-pan-";
+ automation_path += legalize_for_path (str);
+ automation_path += ".automation";
+}
+
+int
+Panner::load ()
+{
+ char line[128];
+ uint32_t linecnt = 0;
+ float version;
+ iterator sp;
+ LocaleGuard lg (X_("POSIX"));
+
+ if (automation_path.length() == 0) {
+ return 0;
+ }
+
+ if (access (automation_path.c_str(), F_OK)) {
+ return 0;
+ }
+
+ ifstream in (automation_path.c_str());
+
+ if (!in) {
+ error << string_compose (_("cannot open pan automation file %1 (%2)"),
+ automation_path, strerror (errno))
+ << endmsg;
+ return -1;
+ }
+
+ sp = begin();
+
+ while (in.getline (line, sizeof(line), '\n')) {
+
+ if (++linecnt == 1) {
+ if (memcmp (line, X_("version"), 7) == 0) {
+ if (sscanf (line, "version %f", &version) != 1) {
+ error << string_compose(_("badly formed version number in pan automation event file \"%1\""), automation_path) << endmsg;
+ return -1;
+ }
+ } else {
+ error << string_compose(_("no version information in pan automation event file \"%1\" (first line = %2)"),
+ automation_path, line) << endmsg;
+ return -1;
+ }
+
+ continue;
+ }
+
+ if (strlen (line) == 0 || line[0] == '#') {
+ continue;
+ }
+
+ if (strcmp (line, "begin") == 0) {
+
+ if (sp == end()) {
+ error << string_compose (_("too many panner states found in pan automation file %1"),
+ automation_path)
+ << endmsg;
+ return -1;
+ }
+
+ if ((*sp)->load (in, automation_path, linecnt)) {
+ return -1;
+ }
+
+ ++sp;
+ }
+ }
+
+ return 0;
+}
diff --git a/libs/ardour/parameter.cc b/libs/ardour/parameter.cc
new file mode 100644
index 0000000000..1b94e5dc4d
--- /dev/null
+++ b/libs/ardour/parameter.cc
@@ -0,0 +1,121 @@
+/*
+ Copyright (C) 2008 Paul Davis
+ Written by Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <ardour/parameter.h>
+
+using namespace ARDOUR;
+
+
+/** Construct an Parameter from a string returned from Parameter::to_string
+ * (AutomationList automation-id property)
+ */
+Parameter::Parameter(const std::string& str)
+ : _type(NullAutomation)
+ , _id(0)
+ , _channel(0)
+{
+ if (str == "gain") {
+ _type = GainAutomation;
+ } else if (str == "solo") {
+ _type = SoloAutomation;
+ } else if (str == "mute") {
+ _type = MuteAutomation;
+ } else if (str == "fadein") {
+ _type = FadeInAutomation;
+ } else if (str == "fadeout") {
+ _type = FadeOutAutomation;
+ } else if (str == "envelope") {
+ _type = EnvelopeAutomation;
+ } else if (str == "pan") {
+ _type = PanAutomation;
+ } else if (str.length() > 4 && str.substr(0, 4) == "pan-") {
+ _type = PanAutomation;
+ _id = atoi(str.c_str()+4);
+ } else if (str.length() > 10 && str.substr(0, 10) == "parameter-") {
+ _type = PluginAutomation;
+ _id = atoi(str.c_str()+10);
+ } else if (str.length() > 7 && str.substr(0, 7) == "midicc-") {
+ _type = MidiCCAutomation;
+ uint32_t channel = 0;
+ sscanf(str.c_str(), "midicc-%d-%d", &channel, &_id);
+ assert(channel < 16);
+ _channel = channel;
+ } else if (str.length() > 16 && str.substr(0, 16) == "midi-pgm-change-") {
+ _type = MidiPgmChangeAutomation;
+ uint32_t channel = 0;
+ sscanf(str.c_str(), "midi-pgm-change-%d", &channel);
+ assert(channel < 16);
+ _id = 0;
+ _channel = channel;
+ } else if (str.length() > 18 && str.substr(0, 18) == "midi-pitch-bender-") {
+ _type = MidiPitchBenderAutomation;
+ uint32_t channel = 0;
+ sscanf(str.c_str(), "midi-pitch-bender-%d", &channel);
+ assert(channel < 16);
+ _id = 0;
+ _channel = channel;
+ } else if (str.length() > 24 && str.substr(0, 24) == "midi-channel-aftertouch-") {
+ _type = MidiChannelAftertouchAutomation;
+ uint32_t channel = 0;
+ sscanf(str.c_str(), "midi-channel-aftertouch-%d", &channel);
+ assert(channel < 16);
+ _id = 0;
+ _channel = channel;
+ } else {
+ PBD::warning << "Unknown Parameter '" << str << "'" << endmsg;
+ }
+}
+
+
+/** Unique string representation, suitable as an XML property value.
+ * e.g. <AutomationList automation-id="whatthisreturns">
+ */
+std::string
+Parameter::to_string() const
+{
+ if (_type == GainAutomation) {
+ return "gain";
+ } else if (_type == PanAutomation) {
+ return string_compose("pan-%1", _id);
+ } else if (_type == SoloAutomation) {
+ return "solo";
+ } else if (_type == MuteAutomation) {
+ return "mute";
+ } else if (_type == FadeInAutomation) {
+ return "fadein";
+ } else if (_type == FadeOutAutomation) {
+ return "fadeout";
+ } else if (_type == EnvelopeAutomation) {
+ return "envelope";
+ } else if (_type == PluginAutomation) {
+ return string_compose("parameter-%1", _id);
+ } else if (_type == MidiCCAutomation) {
+ return string_compose("midicc-%1-%2", int(_channel), _id);
+ } else if (_type == MidiPgmChangeAutomation) {
+ return string_compose("midi-pgm-change-%1", int(_channel));
+ } else if (_type == MidiPitchBenderAutomation) {
+ return string_compose("midi-pitch-bender-%1", int(_channel));
+ } else if (_type == MidiChannelAftertouchAutomation) {
+ return string_compose("midi-channel-aftertouch-%1", int(_channel));
+ } else {
+ PBD::warning << "Uninitialized Parameter to_string() called." << endmsg;
+ return "";
+ }
+}
+
diff --git a/libs/ardour/pcm_utils.cc b/libs/ardour/pcm_utils.cc
new file mode 100644
index 0000000000..08d8a63d6e
--- /dev/null
+++ b/libs/ardour/pcm_utils.cc
@@ -0,0 +1,177 @@
+/*
+ Copyright (C) 2006 Paul Davis , portions Erik de Castro Lopo
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/pcm_utils.h>
+
+#include <cmath>
+
+using namespace std;
+
+// TODO: check CPU_CLIPS_POSITIVE and CPU_CLIPS_NEGATIVE with scons
+#define CPU_CLIPS_NEGATIVE 0
+#define CPU_CLIPS_POSITIVE 0
+
+/* these routines deal with 24 bit int handling (tribytes)
+ * originally from libsndfile, but modified. XXX - Copyright Erik de Castro Lopo
+ */
+
+void
+pcm_let2f_array (tribyte *src, int count, float *dest)
+{
+ /* Special normfactor because tribyte value is read into an int. */
+ static const float normfact = 1.0 / ((float) 0x80000000);
+
+ unsigned char *ucptr ;
+ int value ;
+
+ ucptr = ((unsigned char*) src) + 3 * count ;
+ while (--count >= 0)
+ { ucptr -= 3 ;
+ value = LET2H_INT_PTR (ucptr) ;
+ dest [count] = ((float) value) * normfact ;
+ } ;
+} /* let2f_array */
+
+void
+pcm_bet2f_array (tribyte *src, int count, float *dest)
+{
+ /* Special normfactor because tribyte value is read into an int. */
+ static const float normfact = 1.0 / ((float) 0x80000000);
+
+ unsigned char *ucptr ;
+ int value ;
+
+
+ ucptr = ((unsigned char*) src) + 3 * count ;
+ while (--count >= 0)
+ { ucptr -= 3 ;
+ value = BET2H_INT_PTR (ucptr) ;
+ dest [count] = ((float) value) * normfact ;
+ } ;
+} /* bet2f_array */
+
+void
+pcm_f2let_array (float *src, tribyte *dest, int count)
+{
+ static const float normfact = (1.0 * 0x7FFFFF);
+
+ unsigned char *ucptr ;
+ int value ;
+
+ ucptr = ((unsigned char*) dest) + 3 * count ;
+
+ while (count)
+ { count -- ;
+ ucptr -= 3 ;
+ value = lrintf (src [count] * normfact) ;
+ ucptr [0] = value ;
+ ucptr [1] = value >> 8 ;
+ ucptr [2] = value >> 16 ;
+ } ;
+} /* f2let_array */
+
+void
+pcm_f2let_clip_array (float *src, tribyte *dest, int count)
+{
+ static const float normfact = (8.0 * 0x10000000);
+
+ unsigned char *ucptr ;
+ float scaled_value ;
+ int value ;
+
+ ucptr = ((unsigned char*) dest) + 3 * count ;
+
+ while (count)
+ { count -- ;
+ ucptr -= 3 ;
+ scaled_value = src [count] * normfact ;
+ if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF))
+ { ucptr [0] = 0xFF ;
+ ucptr [1] = 0xFF ;
+ ucptr [2] = 0x7F ;
+ continue ;
+ } ;
+ if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000))
+ { ucptr [0] = 0x00 ;
+ ucptr [1] = 0x00 ;
+ ucptr [2] = 0x80 ;
+ continue ;
+ } ;
+
+ value = lrintf (scaled_value) ;
+ ucptr [0] = value >> 8 ;
+ ucptr [1] = value >> 16 ;
+ ucptr [2] = value >> 24 ;
+ } ;
+} /* f2let_clip_array */
+
+void
+pcm_f2bet_array (const float *src, tribyte *dest, int count)
+{
+ static const float normfact = (1.0 * 0x7FFFFF);
+
+ unsigned char *ucptr ;
+ int value ;
+
+ ucptr = ((unsigned char*) dest) + 3 * count ;
+
+ while (--count >= 0)
+ { ucptr -= 3 ;
+ value = lrintf (src [count] * normfact) ;
+ ucptr [0] = value >> 16 ;
+ ucptr [1] = value >> 8 ;
+ ucptr [2] = value ;
+ } ;
+} /* f2bet_array */
+
+void
+pcm_f2bet_clip_array (const float *src, tribyte *dest, int count)
+{
+ static const float normfact = (8.0 * 0x10000000);
+
+ unsigned char *ucptr ;
+ float scaled_value ;
+ int value ;
+
+ ucptr = ((unsigned char*) dest) + 3 * count ;
+
+ while (--count >= 0)
+ { ucptr -= 3 ;
+ scaled_value = src [count] * normfact ;
+ if (CPU_CLIPS_POSITIVE == 0 && scaled_value >= (1.0 * 0x7FFFFFFF))
+ { ucptr [0] = 0x7F ;
+ ucptr [1] = 0xFF ;
+ ucptr [2] = 0xFF ;
+ continue ;
+ } ;
+ if (CPU_CLIPS_NEGATIVE == 0 && scaled_value <= (-8.0 * 0x10000000))
+ { ucptr [0] = 0x80 ;
+ ucptr [1] = 0x00 ;
+ ucptr [2] = 0x00 ;
+ continue ;
+ } ;
+
+ value = lrint (scaled_value) ;
+ ucptr [0] = value >> 24 ;
+ ucptr [1] = value >> 16 ;
+ ucptr [2] = value >> 8 ;
+ } ;
+} /* f2bet_clip_array */
+
+//@@@@@@@
diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc
new file mode 100644
index 0000000000..0b0d5ecc22
--- /dev/null
+++ b/libs/ardour/playlist.cc
@@ -0,0 +1,2367 @@
+/*
+ Copyright (C) 2000-2003 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <set>
+#include <fstream>
+#include <algorithm>
+#include <unistd.h>
+#include <cerrno>
+#include <string>
+#include <climits>
+
+#include <sigc++/bind.h>
+
+#include <pbd/failed_constructor.h>
+#include <pbd/stl_delete.h>
+#include <pbd/xml++.h>
+#include <pbd/stacktrace.h>
+
+#include <ardour/playlist.h>
+#include <ardour/session.h>
+#include <ardour/region.h>
+#include <ardour/region_factory.h>
+#include <ardour/playlist_factory.h>
+#include <ardour/transient_detector.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+struct ShowMeTheList {
+ ShowMeTheList (boost::shared_ptr<Playlist> pl, const string& n) : playlist (pl), name (n) {}
+ ~ShowMeTheList () {
+ cerr << ">>>>" << name << endl; playlist->dump(); cerr << "<<<<" << name << endl << endl;
+ };
+ boost::shared_ptr<Playlist> playlist;
+ string name;
+};
+
+struct RegionSortByLayer {
+ bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
+ return a->layer() < b->layer();
+ }
+};
+
+struct RegionSortByPosition {
+ bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
+ return a->position() < b->position();
+ }
+};
+
+struct RegionSortByLastLayerOp {
+ bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
+ return a->last_layer_op() < b->last_layer_op();
+ }
+};
+
+
+Playlist::Playlist (Session& sess, string nom, DataType type, bool hide)
+ : SessionObject(sess, nom)
+ , _type(type)
+{
+ init (hide);
+ first_set_state = false;
+ _name = nom;
+
+}
+
+Playlist::Playlist (Session& sess, const XMLNode& node, DataType type, bool hide)
+ : SessionObject(sess, "unnamed playlist")
+ , _type(type)
+{
+ const XMLProperty* prop = node.property("type");
+ assert(!prop || DataType(prop->value()) == _type);
+
+ init (hide);
+ _name = "unnamed"; /* reset by set_state */
+
+ /* set state called by derived class */
+}
+
+Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, bool hide)
+ : SessionObject(other->_session, namestr), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
+{
+ init (hide);
+
+ RegionList tmp;
+ other->copy_regions (tmp);
+
+ in_set_state++;
+
+ for (list<boost::shared_ptr<Region> >::iterator x = tmp.begin(); x != tmp.end(); ++x) {
+ add_region_internal( (*x), (*x)->position());
+ }
+
+ in_set_state--;
+
+ _splicing = other->_splicing;
+ _nudging = other->_nudging;
+ _edit_mode = other->_edit_mode;
+
+ in_set_state = 0;
+ first_set_state = false;
+ in_flush = false;
+ in_partition = false;
+ subcnt = 0;
+ _read_data_count = 0;
+ _frozen = other->_frozen;
+
+ layer_op_counter = other->layer_op_counter;
+ freeze_length = other->freeze_length;
+}
+
+Playlist::Playlist (boost::shared_ptr<const Playlist> other, nframes_t start, nframes_t cnt, string str, bool hide)
+ : SessionObject(other->_session, str), _type(other->_type), _orig_diskstream_id(other->_orig_diskstream_id)
+{
+ RegionLock rlock2 (const_cast<Playlist*> (other.get()));
+
+ nframes_t end = start + cnt - 1;
+
+ init (hide);
+
+ in_set_state++;
+
+ for (RegionList::const_iterator i = other->regions.begin(); i != other->regions.end(); i++) {
+
+ boost::shared_ptr<Region> region;
+ boost::shared_ptr<Region> new_region;
+ nframes_t offset = 0;
+ nframes_t position = 0;
+ nframes_t len = 0;
+ string new_name;
+ OverlapType overlap;
+
+ region = *i;
+
+ overlap = region->coverage (start, end);
+
+ switch (overlap) {
+ case OverlapNone:
+ continue;
+
+ case OverlapInternal:
+ offset = start - region->position();
+ position = 0;
+ len = cnt;
+ break;
+
+ case OverlapStart:
+ offset = 0;
+ position = region->position() - start;
+ len = end - region->position();
+ break;
+
+ case OverlapEnd:
+ offset = start - region->position();
+ position = 0;
+ len = region->length() - offset;
+ break;
+
+ case OverlapExternal:
+ offset = 0;
+ position = region->position() - start;
+ len = region->length();
+ break;
+ }
+
+ _session.region_name (new_name, region->name(), false);
+
+ new_region = RegionFactory::RegionFactory::create (region, offset, len, new_name, region->layer(), region->flags());
+
+ add_region_internal (new_region, position);
+ }
+
+ in_set_state--;
+ first_set_state = false;
+
+ /* this constructor does NOT notify others (session) */
+}
+
+void
+Playlist::use ()
+{
+ ++_refcnt;
+ InUse (true); /* EMIT SIGNAL */
+}
+
+void
+Playlist::release ()
+{
+ if (_refcnt > 0) {
+ _refcnt--;
+ }
+
+ if (_refcnt == 0) {
+ InUse (false); /* EMIT SIGNAL */
+ }
+}
+
+void
+Playlist::copy_regions (RegionList& newlist) const
+{
+ RegionLock rlock (const_cast<Playlist *> (this));
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ newlist.push_back (RegionFactory::RegionFactory::create (*i));
+ }
+}
+
+void
+Playlist::init (bool hide)
+{
+ g_atomic_int_set (&block_notifications, 0);
+ g_atomic_int_set (&ignore_state_changes, 0);
+ pending_modified = false;
+ pending_length = false;
+ first_set_state = true;
+ _refcnt = 0;
+ _hidden = hide;
+ _splicing = false;
+ _shuffling = false;
+ _nudging = false;
+ in_set_state = 0;
+ _edit_mode = Config->get_edit_mode();
+ in_flush = false;
+ in_partition = false;
+ subcnt = 0;
+ _read_data_count = 0;
+ _frozen = false;
+ layer_op_counter = 0;
+ freeze_length = 0;
+
+ Modified.connect (mem_fun (*this, &Playlist::mark_session_dirty));
+}
+
+Playlist::Playlist (const Playlist& pl)
+ : SessionObject(pl._session, pl._name)
+ , _type(pl.data_type())
+{
+ fatal << _("playlist const copy constructor called") << endmsg;
+}
+
+Playlist::Playlist (Playlist& pl)
+ : SessionObject(pl._session, pl._name)
+ , _type(pl.data_type())
+{
+ fatal << _("playlist non-const copy constructor called") << endmsg;
+}
+
+Playlist::~Playlist ()
+{
+ {
+ RegionLock rl (this);
+
+ for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
+ (*i)->set_playlist (boost::shared_ptr<Playlist>());
+ }
+ }
+
+ /* GoingAway must be emitted by derived classes */
+}
+
+bool
+Playlist::set_name (const string& str)
+{
+ /* in a typical situation, a playlist is being used
+ by one diskstream and also is referenced by the
+ Session. if there are more references than that,
+ then don't change the name.
+ */
+
+ if (_refcnt > 2) {
+ return false;
+ } else {
+ return SessionObject::set_name(str);
+ }
+}
+
+/***********************************************************************
+ CHANGE NOTIFICATION HANDLING
+
+ Notifications must be delayed till the region_lock is released. This
+ is necessary because handlers for the signals may need to acquire
+ the lock (e.g. to read from the playlist).
+ ***********************************************************************/
+
+void
+Playlist::freeze ()
+{
+ delay_notifications ();
+ g_atomic_int_inc (&ignore_state_changes);
+}
+
+void
+Playlist::thaw ()
+{
+ g_atomic_int_dec_and_test (&ignore_state_changes);
+ release_notifications ();
+}
+
+
+void
+Playlist::delay_notifications ()
+{
+ g_atomic_int_inc (&block_notifications);
+ freeze_length = _get_maximum_extent();
+}
+
+void
+Playlist::release_notifications ()
+{
+ if (g_atomic_int_dec_and_test (&block_notifications)) {
+ flush_notifications ();
+ }
+}
+
+void
+Playlist::notify_modified ()
+{
+ if (holding_state ()) {
+ pending_modified = true;
+ } else {
+ pending_modified = false;
+ Modified(); /* EMIT SIGNAL */
+ }
+}
+
+void
+Playlist::notify_region_removed (boost::shared_ptr<Region> r)
+{
+ if (holding_state ()) {
+ pending_removes.insert (r);
+ pending_modified = true;
+ pending_length = true;
+ } else {
+ /* this might not be true, but we have to act
+ as though it could be.
+ */
+ pending_length = false;
+ LengthChanged (); /* EMIT SIGNAL */
+ pending_modified = false;
+ Modified (); /* EMIT SIGNAL */
+ }
+}
+
+void
+Playlist::notify_region_added (boost::shared_ptr<Region> r)
+{
+ /* the length change might not be true, but we have to act
+ as though it could be.
+ */
+
+ if (holding_state()) {
+ pending_adds.insert (r);
+ pending_modified = true;
+ pending_length = true;
+ } else {
+ pending_length = false;
+ LengthChanged (); /* EMIT SIGNAL */
+ pending_modified = false;
+ Modified (); /* EMIT SIGNAL */
+ }
+}
+
+void
+Playlist::notify_length_changed ()
+{
+ if (holding_state ()) {
+ pending_length = true;
+ } else {
+ pending_length = false;
+ LengthChanged(); /* EMIT SIGNAL */
+ pending_modified = false;
+ Modified (); /* EMIT SIGNAL */
+ }
+}
+
+void
+Playlist::flush_notifications ()
+{
+ set<boost::shared_ptr<Region> > dependent_checks_needed;
+ set<boost::shared_ptr<Region> >::iterator s;
+ uint32_t n = 0;
+
+ if (in_flush) {
+ return;
+ }
+
+ in_flush = true;
+
+ /* we have no idea what order the regions ended up in pending
+ bounds (it could be based on selection order, for example).
+ so, to preserve layering in the "most recently moved is higher"
+ model, sort them by existing layer, then timestamp them.
+ */
+
+ // RegionSortByLayer cmp;
+ // pending_bounds.sort (cmp);
+
+ for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
+ if (Config->get_layer_model() == MoveAddHigher) {
+ timestamp_layer_op (*r);
+ }
+ pending_length = true;
+ dependent_checks_needed.insert (*r);
+ n++;
+ }
+
+ for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
+ dependent_checks_needed.insert (*s);
+ n++;
+ }
+
+ for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
+ remove_dependents (*s);
+ n++;
+ }
+
+ if ((freeze_length != _get_maximum_extent()) || pending_length) {
+ pending_length = 0;
+ LengthChanged(); /* EMIT SIGNAL */
+ n++;
+ }
+
+ if (n || pending_modified) {
+ if (!in_set_state) {
+ relayer ();
+ }
+ pending_modified = false;
+ Modified (); /* EMIT SIGNAL */
+
+ }
+
+ for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
+ check_dependents (*s, false);
+ }
+
+ pending_adds.clear ();
+ pending_removes.clear ();
+ pending_bounds.clear ();
+
+ in_flush = false;
+}
+
+/*************************************************************
+ PLAYLIST OPERATIONS
+ *************************************************************/
+
+void
+Playlist::add_region (boost::shared_ptr<Region> region, nframes_t position, float times)
+{
+ RegionLock rlock (this);
+ delay_notifications();
+ times = fabs (times);
+
+ int itimes = (int) floor (times);
+
+ nframes_t pos = position;
+
+ if (itimes >= 1) {
+ add_region_internal (region, pos);
+ pos += region->length();
+ --itimes;
+ }
+
+
+ /* note that itimes can be zero if we being asked to just
+ insert a single fraction of the region.
+ */
+
+ for (int i = 0; i < itimes; ++i) {
+ boost::shared_ptr<Region> copy = RegionFactory::create (region);
+ add_region_internal (copy, pos);
+ pos += region->length();
+ }
+
+ nframes_t length = 0;
+
+ if (floor (times) != times) {
+ length = (nframes_t) floor (region->length() * (times - floor (times)));
+ string name;
+ _session.region_name (name, region->name(), false);
+ boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
+ add_region_internal (sub, pos);
+ }
+
+
+ possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
+ release_notifications ();
+}
+
+void
+Playlist::set_region_ownership ()
+{
+ RegionLock rl (this);
+ RegionList::iterator i;
+ boost::weak_ptr<Playlist> pl (shared_from_this());
+
+ for (i = regions.begin(); i != regions.end(); ++i) {
+ (*i)->set_playlist (pl);
+ }
+}
+
+void
+Playlist::add_region_internal (boost::shared_ptr<Region> region, nframes_t position)
+{
+ assert(region->data_type() == _type);
+
+ RegionSortByPosition cmp;
+ nframes_t old_length = 0;
+
+ if (!holding_state()) {
+ old_length = _get_maximum_extent();
+ }
+
+ if (!first_set_state) {
+ boost::shared_ptr<Playlist> foo (shared_from_this());
+ region->set_playlist (boost::weak_ptr<Playlist>(foo));
+ }
+
+ region->set_position (position, this);
+
+ timestamp_layer_op (region);
+
+ regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
+ all_regions.insert (region);
+
+ possibly_splice_unlocked (position, region->length(), region);
+
+ if (!holding_state () && !in_set_state) {
+ /* layers get assigned from XML state */
+ relayer ();
+ }
+
+ /* we need to notify the existence of new region before checking dependents. Ick. */
+
+ notify_region_added (region);
+
+ if (!holding_state ()) {
+ check_dependents (region, false);
+ if (old_length != _get_maximum_extent()) {
+ notify_length_changed ();
+ }
+ }
+
+ region->StateChanged.connect (sigc::bind (mem_fun (this, &Playlist::region_changed_proxy),
+ boost::weak_ptr<Region> (region)));
+}
+
+void
+Playlist::replace_region (boost::shared_ptr<Region> old, boost::shared_ptr<Region> newr, nframes_t pos)
+{
+ RegionLock rlock (this);
+
+ bool old_sp = _splicing;
+ _splicing = true;
+
+ remove_region_internal (old);
+ add_region_internal (newr, pos);
+
+ _splicing = old_sp;
+
+ possibly_splice_unlocked (pos, (nframes64_t) old->length() - (nframes64_t) newr->length());
+}
+
+void
+Playlist::remove_region (boost::shared_ptr<Region> region)
+{
+ RegionLock rlock (this);
+ remove_region_internal (region);
+}
+
+int
+Playlist::remove_region_internal (boost::shared_ptr<Region> region)
+{
+ RegionList::iterator i;
+ nframes_t old_length = 0;
+
+ if (!holding_state()) {
+ old_length = _get_maximum_extent();
+ }
+
+ if (!in_set_state) {
+ /* unset playlist */
+ region->set_playlist (boost::weak_ptr<Playlist>());
+ }
+
+ for (i = regions.begin(); i != regions.end(); ++i) {
+ if (*i == region) {
+
+ nframes_t pos = (*i)->position();
+ nframes64_t distance = (*i)->length();
+
+ regions.erase (i);
+
+ possibly_splice_unlocked (pos, -distance);
+
+ if (!holding_state ()) {
+ relayer ();
+ remove_dependents (region);
+
+ if (old_length != _get_maximum_extent()) {
+ notify_length_changed ();
+ }
+ }
+
+ notify_region_removed (region);
+ return 0;
+ }
+ }
+
+
+
+ return -1;
+}
+
+void
+Playlist::get_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
+{
+ if (Config->get_use_overlap_equivalency()) {
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->overlap_equivalent (other)) {
+ results.push_back ((*i));
+ }
+ }
+ } else {
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->equivalent (other)) {
+ results.push_back ((*i));
+ }
+ }
+ }
+}
+
+void
+Playlist::get_region_list_equivalent_regions (boost::shared_ptr<Region> other, vector<boost::shared_ptr<Region> >& results)
+{
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ if ((*i) && (*i)->region_list_equivalent (other)) {
+ results.push_back (*i);
+ }
+ }
+}
+
+void
+Playlist::partition (nframes_t start, nframes_t end, bool just_top_level)
+{
+ RegionList thawlist;
+
+ partition_internal (start, end, false, thawlist);
+
+ for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
+ (*i)->thaw ("separation");
+ }
+}
+
+void
+Playlist::partition_internal (nframes_t start, nframes_t end, bool cutting, RegionList& thawlist)
+{
+ RegionList new_regions;
+
+ {
+ RegionLock rlock (this);
+ boost::shared_ptr<Region> region;
+ boost::shared_ptr<Region> current;
+ string new_name;
+ RegionList::iterator tmp;
+ OverlapType overlap;
+ nframes_t pos1, pos2, pos3, pos4;
+
+ in_partition = true;
+
+ /* need to work from a copy, because otherwise the regions we add during the process
+ get operated on as well.
+ */
+
+ RegionList copy = regions;
+
+ for (RegionList::iterator i = copy.begin(); i != copy.end(); i = tmp) {
+
+ tmp = i;
+ ++tmp;
+
+ current = *i;
+
+ if (current->first_frame() >= start && current->last_frame() < end) {
+ if (cutting) {
+ remove_region_internal (current);
+ }
+ continue;
+ }
+
+ /* coverage will return OverlapStart if the start coincides
+ with the end point. we do not partition such a region,
+ so catch this special case.
+ */
+
+ if (current->first_frame() >= end) {
+ continue;
+ }
+
+ if ((overlap = current->coverage (start, end)) == OverlapNone) {
+ continue;
+ }
+
+ pos1 = current->position();
+ pos2 = start;
+ pos3 = end;
+ pos4 = current->last_frame();
+
+ if (overlap == OverlapInternal) {
+
+ /* split: we need 3 new regions, the front, middle and end.
+ cut: we need 2 regions, the front and end.
+ */
+
+ /*
+ start end
+ ---------------*************************------------
+ P1 P2 P3 P4
+ SPLIT:
+ ---------------*****++++++++++++++++====------------
+ CUT
+ ---------------*****----------------====------------
+
+ */
+
+ if (!cutting) {
+
+ /* "middle" ++++++ */
+
+ _session.region_name (new_name, current->name(), false);
+ region = RegionFactory::create (current, pos2 - pos1, pos3 - pos2, new_name,
+ regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit|Region::RightOfSplit));
+ add_region_internal (region, start);
+ new_regions.push_back (region);
+ }
+
+ /* "end" ====== */
+
+ _session.region_name (new_name, current->name(), false);
+ region = RegionFactory::create (current, pos3 - pos1, pos4 - pos3, new_name,
+ regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
+
+ add_region_internal (region, end);
+ new_regions.push_back (region);
+
+ /* "front" ***** */
+
+ current->freeze ();
+ thawlist.push_back (current);
+ current->trim_end (pos2, this);
+
+ } else if (overlap == OverlapEnd) {
+
+ /*
+ start end
+ ---------------*************************------------
+ P1 P2 P4 P3
+ SPLIT:
+ ---------------**************+++++++++++------------
+ CUT:
+ ---------------**************-----------------------
+ */
+
+ if (!cutting) {
+
+ /* end +++++ */
+
+ _session.region_name (new_name, current->name(), false);
+ region = RegionFactory::create (current, pos2 - pos1, pos4 - pos2, new_name, (layer_t) regions.size(),
+ Region::Flag(current->flags()|Region::Automatic|Region::LeftOfSplit));
+ add_region_internal (region, start);
+ new_regions.push_back (region);
+ }
+
+ /* front ****** */
+
+ current->freeze ();
+ thawlist.push_back (current);
+ current->trim_end (pos2, this);
+
+ } else if (overlap == OverlapStart) {
+
+ /* split: we need 2 regions: the front and the end.
+ cut: just trim current to skip the cut area
+ */
+
+ /*
+ start end
+ ---------------*************************------------
+ P2 P1 P3 P4
+
+ SPLIT:
+ ---------------****+++++++++++++++++++++------------
+ CUT:
+ -------------------*********************------------
+
+ */
+
+ if (!cutting) {
+
+ /* front **** */
+ _session.region_name (new_name, current->name(), false);
+ region = RegionFactory::create (current, 0, pos3 - pos1, new_name,
+ regions.size(), Region::Flag(current->flags()|Region::Automatic|Region::RightOfSplit));
+ add_region_internal (region, pos1);
+ new_regions.push_back (region);
+ }
+
+ /* end */
+
+ current->freeze ();
+ thawlist.push_back (current);
+ current->trim_front (pos3, this);
+
+ } else if (overlap == OverlapExternal) {
+
+ /* split: no split required.
+ cut: remove the region.
+ */
+
+ /*
+ start end
+ ---------------*************************------------
+ P2 P1 P3 P4
+
+ SPLIT:
+ ---------------*************************------------
+ CUT:
+ ----------------------------------------------------
+
+ */
+
+ if (cutting) {
+ remove_region_internal (current);
+ }
+ new_regions.push_back (current);
+ }
+ }
+
+ in_partition = false;
+ }
+
+ for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
+ check_dependents (*i, false);
+ }
+}
+
+boost::shared_ptr<Playlist>
+Playlist::cut_copy (boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t, nframes_t,bool), list<AudioRange>& ranges, bool result_is_hidden)
+{
+ boost::shared_ptr<Playlist> ret;
+ boost::shared_ptr<Playlist> pl;
+ nframes_t start;
+
+ if (ranges.empty()) {
+ return boost::shared_ptr<Playlist>();
+ }
+
+ start = ranges.front().start;
+
+ for (list<AudioRange>::iterator i = ranges.begin(); i != ranges.end(); ++i) {
+
+ pl = (this->*pmf)((*i).start, (*i).length(), result_is_hidden);
+
+ if (i == ranges.begin()) {
+ ret = pl;
+ } else {
+
+ /* paste the next section into the nascent playlist,
+ offset to reflect the start of the first range we
+ chopped.
+ */
+
+ ret->paste (pl, (*i).start - start, 1.0f);
+ }
+ }
+
+ return ret;
+}
+
+boost::shared_ptr<Playlist>
+Playlist::cut (list<AudioRange>& ranges, bool result_is_hidden)
+{
+ boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::cut;
+ return cut_copy (pmf, ranges, result_is_hidden);
+}
+
+boost::shared_ptr<Playlist>
+Playlist::copy (list<AudioRange>& ranges, bool result_is_hidden)
+{
+ boost::shared_ptr<Playlist> (Playlist::*pmf)(nframes_t,nframes_t,bool) = &Playlist::copy;
+ return cut_copy (pmf, ranges, result_is_hidden);
+}
+
+boost::shared_ptr<Playlist>
+Playlist::cut (nframes_t start, nframes_t cnt, bool result_is_hidden)
+{
+ boost::shared_ptr<Playlist> the_copy;
+ RegionList thawlist;
+ char buf[32];
+
+ snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
+ string new_name = _name;
+ new_name += '.';
+ new_name += buf;
+
+ if ((the_copy = PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden)) == 0) {
+ return boost::shared_ptr<Playlist>();
+ }
+
+ partition_internal (start, start+cnt-1, true, thawlist);
+
+ for (RegionList::iterator i = thawlist.begin(); i != thawlist.end(); ++i) {
+ (*i)->thaw ("playlist cut");
+ }
+
+ return the_copy;
+}
+
+boost::shared_ptr<Playlist>
+Playlist::copy (nframes_t start, nframes_t cnt, bool result_is_hidden)
+{
+ char buf[32];
+
+ snprintf (buf, sizeof (buf), "%" PRIu32, ++subcnt);
+ string new_name = _name;
+ new_name += '.';
+ new_name += buf;
+
+ cnt = min (_get_maximum_extent() - start, cnt);
+ return PlaylistFactory::create (shared_from_this(), start, cnt, new_name, result_is_hidden);
+}
+
+int
+Playlist::paste (boost::shared_ptr<Playlist> other, nframes_t position, float times)
+{
+ times = fabs (times);
+ nframes_t old_length;
+
+ {
+ RegionLock rl1 (this);
+ RegionLock rl2 (other.get());
+
+ old_length = _get_maximum_extent();
+
+ int itimes = (int) floor (times);
+ nframes_t pos = position;
+ nframes_t shift = other->_get_maximum_extent();
+ layer_t top_layer = regions.size();
+
+ while (itimes--) {
+ for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
+ boost::shared_ptr<Region> copy_of_region = RegionFactory::create (*i);
+
+ /* put these new regions on top of all existing ones, but preserve
+ the ordering they had in the original playlist.
+ */
+
+ copy_of_region->set_layer (copy_of_region->layer() + top_layer);
+ add_region_internal (copy_of_region, copy_of_region->position() + pos);
+ }
+ pos += shift;
+ }
+
+
+ /* XXX shall we handle fractional cases at some point? */
+
+ if (old_length != _get_maximum_extent()) {
+ notify_length_changed ();
+ }
+
+
+ }
+
+ return 0;
+}
+
+
+void
+Playlist::duplicate (boost::shared_ptr<Region> region, nframes_t position, float times)
+{
+ times = fabs (times);
+
+ RegionLock rl (this);
+ int itimes = (int) floor (times);
+ nframes_t pos = position;
+
+ while (itimes--) {
+ boost::shared_ptr<Region> copy = RegionFactory::create (region);
+ add_region_internal (copy, pos);
+ pos += region->length();
+ }
+
+ if (floor (times) != times) {
+ nframes_t length = (nframes_t) floor (region->length() * (times - floor (times)));
+ string name;
+ _session.region_name (name, region->name(), false);
+ boost::shared_ptr<Region> sub = RegionFactory::create (region, 0, length, name, region->layer(), region->flags());
+ add_region_internal (sub, pos);
+ }
+}
+
+void
+Playlist::shift (nframes64_t at, nframes64_t distance, bool move_intersected, bool ignore_music_glue)
+{
+ RegionLock rlock (this);
+ RegionList copy (regions);
+ RegionList fixup;
+
+ for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
+
+ if ((*r)->last_frame() < at) {
+ /* too early */
+ continue;
+ }
+
+ if (at > (*r)->first_frame() && at < (*r)->last_frame()) {
+ /* intersected region */
+ if (!move_intersected) {
+ continue;
+ }
+ }
+
+ /* do not move regions glued to music time - that
+ has to be done separately.
+ */
+
+ if (!ignore_music_glue && (*r)->positional_lock_style() != Region::AudioTime) {
+ fixup.push_back (*r);
+ continue;
+ }
+
+ (*r)->set_position ((*r)->position() + distance, this);
+ }
+
+ for (RegionList::iterator r = fixup.begin(); r != fixup.end(); ++r) {
+ (*r)->recompute_position_from_lock_style ();
+ }
+}
+
+void
+Playlist::split (nframes64_t at)
+{
+ RegionLock rlock (this);
+ RegionList copy (regions);
+
+ /* use a copy since this operation can modify the region list
+ */
+
+ for (RegionList::iterator r = copy.begin(); r != copy.end(); ++r) {
+ _split_region (*r, at);
+ }
+}
+
+void
+Playlist::split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
+{
+ RegionLock rl (this);
+ _split_region (region, playlist_position);
+}
+
+void
+Playlist::_split_region (boost::shared_ptr<Region> region, nframes_t playlist_position)
+{
+ if (!region->covers (playlist_position)) {
+ return;
+ }
+
+ if (region->position() == playlist_position ||
+ region->last_frame() == playlist_position) {
+ return;
+ }
+
+ boost::shared_ptr<Region> left;
+ boost::shared_ptr<Region> right;
+ nframes_t before;
+ nframes_t after;
+ string before_name;
+ string after_name;
+
+ /* split doesn't change anything about length, so don't try to splice */
+
+ bool old_sp = _splicing;
+ _splicing = true;
+
+ before = playlist_position - region->position();
+ after = region->length() - before;
+
+ _session.region_name (before_name, region->name(), false);
+ left = RegionFactory::create (region, 0, before, before_name, region->layer(), Region::Flag (region->flags()|Region::LeftOfSplit));
+
+ _session.region_name (after_name, region->name(), false);
+ right = RegionFactory::create (region, before, after, after_name, region->layer(), Region::Flag (region->flags()|Region::RightOfSplit));
+
+ add_region_internal (left, region->position());
+ add_region_internal (right, region->position() + before);
+
+ uint64_t orig_layer_op = region->last_layer_op();
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->last_layer_op() > orig_layer_op) {
+ (*i)->set_last_layer_op( (*i)->last_layer_op() + 1 );
+ }
+ }
+
+ left->set_last_layer_op ( orig_layer_op );
+ right->set_last_layer_op ( orig_layer_op + 1);
+
+ layer_op_counter++;
+
+ finalize_split_region (region, left, right);
+
+ remove_region_internal (region);
+
+ _splicing = old_sp;
+}
+
+void
+Playlist::possibly_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+{
+ if (_splicing || in_set_state) {
+ /* don't respond to splicing moves or state setting */
+ return;
+ }
+
+ if (_edit_mode == Splice) {
+ splice_locked (at, distance, exclude);
+ }
+}
+
+void
+Playlist::possibly_splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+{
+ if (_splicing || in_set_state) {
+ /* don't respond to splicing moves or state setting */
+ return;
+ }
+
+ if (_edit_mode == Splice) {
+ splice_unlocked (at, distance, exclude);
+ }
+}
+
+void
+Playlist::splice_locked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+{
+ {
+ RegionLock rl (this);
+ core_splice (at, distance, exclude);
+ }
+}
+
+void
+Playlist::splice_unlocked (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+{
+ core_splice (at, distance, exclude);
+}
+
+void
+Playlist::core_splice (nframes_t at, nframes64_t distance, boost::shared_ptr<Region> exclude)
+{
+ _splicing = true;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ if (exclude && (*i) == exclude) {
+ continue;
+ }
+
+ if ((*i)->position() >= at) {
+ nframes64_t new_pos = (*i)->position() + distance;
+ if (new_pos < 0) {
+ new_pos = 0;
+ } else if (new_pos >= max_frames - (*i)->length()) {
+ new_pos = max_frames - (*i)->length();
+ }
+
+ (*i)->set_position (new_pos, this);
+ }
+ }
+
+ _splicing = false;
+
+ notify_length_changed ();
+}
+
+void
+Playlist::region_bounds_changed (Change what_changed, boost::shared_ptr<Region> region)
+{
+ if (in_set_state || _splicing || _nudging || _shuffling) {
+ return;
+ }
+
+ if (what_changed & ARDOUR::PositionChanged) {
+
+ /* remove it from the list then add it back in
+ the right place again.
+ */
+
+ RegionSortByPosition cmp;
+
+ RegionList::iterator i = find (regions.begin(), regions.end(), region);
+
+ if (i == regions.end()) {
+ warning << string_compose (_("%1: bounds changed received for region (%2)not in playlist"),
+ _name, region->name())
+ << endmsg;
+ return;
+ }
+
+ regions.erase (i);
+ regions.insert (upper_bound (regions.begin(), regions.end(), region, cmp), region);
+ }
+
+ if (what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged)) {
+
+ nframes64_t delta = 0;
+
+ if (what_changed & ARDOUR::PositionChanged) {
+ delta = (nframes64_t) region->position() - (nframes64_t) region->last_position();
+ }
+
+ if (what_changed & ARDOUR::LengthChanged) {
+ delta += (nframes64_t) region->length() - (nframes64_t) region->last_length();
+ }
+
+ if (delta) {
+ possibly_splice (region->last_position() + region->last_length(), delta, region);
+ }
+
+ if (holding_state ()) {
+ pending_bounds.push_back (region);
+ } else {
+ if (Config->get_layer_model() == MoveAddHigher) {
+ /* it moved or changed length, so change the timestamp */
+ timestamp_layer_op (region);
+ }
+
+ notify_length_changed ();
+ relayer ();
+ check_dependents (region, false);
+ }
+ }
+}
+
+void
+Playlist::region_changed_proxy (Change what_changed, boost::weak_ptr<Region> weak_region)
+{
+ boost::shared_ptr<Region> region (weak_region.lock());
+
+ if (!region) {
+ return;
+ }
+
+
+ /* this makes a virtual call to the right kind of playlist ... */
+
+ region_changed (what_changed, region);
+}
+
+bool
+Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region)
+{
+ Change our_interests = Change (Region::MuteChanged|Region::LayerChanged|Region::OpacityChanged);
+ bool save = false;
+
+ if (in_set_state || in_flush) {
+ return false;
+ }
+
+ {
+ if (what_changed & BoundsChanged) {
+ region_bounds_changed (what_changed, region);
+ save = !(_splicing || _nudging);
+ }
+
+ if ((what_changed & our_interests) &&
+ !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) {
+ check_dependents (region, false);
+ }
+
+ if (what_changed & our_interests) {
+ save = true;
+ }
+ }
+
+ return save;
+}
+
+void
+Playlist::drop_regions ()
+{
+ RegionLock rl (this);
+ regions.clear ();
+ all_regions.clear ();
+}
+
+void
+Playlist::clear (bool with_signals)
+{
+ {
+ RegionLock rl (this);
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ pending_removes.insert (*i);
+ }
+ regions.clear ();
+ }
+
+ if (with_signals) {
+ pending_length = false;
+ LengthChanged ();
+ pending_modified = false;
+ Modified ();
+ }
+
+}
+
+/***********************************************************************
+ FINDING THINGS
+ **********************************************************************/
+
+Playlist::RegionList *
+Playlist::regions_at (nframes_t frame)
+
+{
+ RegionLock rlock (this);
+ return find_regions_at (frame);
+}
+
+boost::shared_ptr<Region>
+Playlist::top_region_at (nframes_t frame)
+
+{
+ RegionLock rlock (this);
+ RegionList *rlist = find_regions_at (frame);
+ boost::shared_ptr<Region> region;
+
+ if (rlist->size()) {
+ RegionSortByLayer cmp;
+ rlist->sort (cmp);
+ region = rlist->back();
+ }
+
+ delete rlist;
+ return region;
+}
+
+Playlist::RegionList*
+Playlist::regions_to_read (nframes_t start, nframes_t end)
+{
+ /* Caller must hold lock */
+
+ RegionList covering;
+ set<nframes_t> to_check;
+ set<boost::shared_ptr<Region> > unique;
+ RegionList here;
+
+ to_check.insert (start);
+ to_check.insert (end);
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ /* find all/any regions that span start+end */
+
+ switch ((*i)->coverage (start, end)) {
+ case OverlapNone:
+ break;
+
+ case OverlapInternal:
+ covering.push_back (*i);
+ break;
+
+ case OverlapStart:
+ to_check.insert ((*i)->position());
+ covering.push_back (*i);
+ break;
+
+ case OverlapEnd:
+ to_check.insert ((*i)->last_frame());
+ covering.push_back (*i);
+ break;
+
+ case OverlapExternal:
+ covering.push_back (*i);
+ to_check.insert ((*i)->position());
+ to_check.insert ((*i)->last_frame());
+ break;
+ }
+
+ /* don't go too far */
+
+ if ((*i)->position() > end) {
+ break;
+ }
+ }
+
+ RegionList* rlist = new RegionList;
+
+ /* find all the regions that cover each position .... */
+
+ if (covering.size() == 1) {
+
+ rlist->push_back (covering.front());
+
+ } else {
+
+ for (set<nframes_t>::iterator t = to_check.begin(); t != to_check.end(); ++t) {
+
+ here.clear ();
+
+ for (RegionList::iterator x = covering.begin(); x != covering.end(); ++x) {
+
+ if ((*x)->covers (*t)) {
+ here.push_back (*x);
+ }
+ }
+
+ RegionSortByLayer cmp;
+ here.sort (cmp);
+
+ /* ... and get the top/transparent regions at "here" */
+
+ for (RegionList::reverse_iterator c = here.rbegin(); c != here.rend(); ++c) {
+
+ unique.insert (*c);
+
+ if ((*c)->opaque()) {
+
+ /* the other regions at this position are hidden by this one */
+
+ break;
+ }
+ }
+ }
+
+ for (set<boost::shared_ptr<Region> >::iterator s = unique.begin(); s != unique.end(); ++s) {
+ rlist->push_back (*s);
+ }
+
+ if (rlist->size() > 1) {
+ /* now sort by time order */
+
+ RegionSortByPosition cmp;
+ rlist->sort (cmp);
+ }
+ }
+
+ return rlist;
+}
+
+Playlist::RegionList *
+Playlist::find_regions_at (nframes_t frame)
+{
+ /* Caller must hold lock */
+
+ RegionList *rlist = new RegionList;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->covers (frame)) {
+ rlist->push_back (*i);
+ }
+ }
+
+ return rlist;
+}
+
+Playlist::RegionList *
+Playlist::regions_touched (nframes_t start, nframes_t end)
+{
+ RegionLock rlock (this);
+ RegionList *rlist = new RegionList;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->coverage (start, end) != OverlapNone) {
+ rlist->push_back (*i);
+ }
+ }
+
+ return rlist;
+}
+
+nframes64_t
+Playlist::find_next_transient (nframes64_t from, int dir)
+{
+ RegionLock rlock (this);
+ AnalysisFeatureList points;
+ AnalysisFeatureList these_points;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if (dir > 0) {
+ if ((*i)->last_frame() < from) {
+ continue;
+ }
+ } else {
+ if ((*i)->first_frame() > from) {
+ continue;
+ }
+ }
+
+ (*i)->get_transients (these_points);
+
+ /* add first frame, just, err, because */
+
+ these_points.push_back ((*i)->first_frame());
+
+ points.insert (points.end(), these_points.begin(), these_points.end());
+ these_points.clear ();
+ }
+
+ if (points.empty()) {
+ return -1;
+ }
+
+ TransientDetector::cleanup_transients (points, _session.frame_rate(), 3.0);
+ bool reached = false;
+
+ if (dir > 0) {
+ for (AnalysisFeatureList::iterator x = points.begin(); x != points.end(); ++x) {
+ if ((*x) >= from) {
+ reached = true;
+ }
+
+ if (reached && (*x) > from) {
+ return *x;
+ }
+ }
+ } else {
+ for (AnalysisFeatureList::reverse_iterator x = points.rbegin(); x != points.rend(); ++x) {
+ if ((*x) <= from) {
+ reached = true;
+ }
+
+ if (reached && (*x) < from) {
+ return *x;
+ }
+ }
+ }
+
+ return -1;
+}
+
+boost::shared_ptr<Region>
+Playlist::find_next_region (nframes_t frame, RegionPoint point, int dir)
+{
+ RegionLock rlock (this);
+ boost::shared_ptr<Region> ret;
+ nframes_t closest = max_frames;
+
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ nframes_t distance;
+ boost::shared_ptr<Region> r = (*i);
+ nframes_t pos = 0;
+
+ switch (point) {
+ case Start:
+ pos = r->first_frame ();
+ break;
+ case End:
+ pos = r->last_frame ();
+ break;
+ case SyncPoint:
+ pos = r->adjust_to_sync (r->first_frame());
+ break;
+ }
+
+ switch (dir) {
+ case 1: /* forwards */
+
+ if (pos >= frame) {
+ if ((distance = pos - frame) < closest) {
+ closest = distance;
+ ret = r;
+ }
+ }
+
+ break;
+
+ default: /* backwards */
+
+ if (pos <= frame) {
+ if ((distance = frame - pos) < closest) {
+ closest = distance;
+ ret = r;
+ }
+ }
+ break;
+ }
+ }
+
+ return ret;
+}
+
+nframes64_t
+Playlist::find_next_region_boundary (nframes64_t frame, int dir)
+{
+ RegionLock rlock (this);
+
+ nframes64_t closest = max_frames;
+ nframes64_t ret = -1;
+
+ if (dir > 0) {
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ boost::shared_ptr<Region> r = (*i);
+ nframes64_t distance;
+ nframes64_t end = r->position() + r->length();
+ bool reset;
+
+ reset = false;
+
+ if (r->first_frame() > frame) {
+
+ distance = r->first_frame() - frame;
+
+ if (distance < closest) {
+ ret = r->first_frame();
+ closest = distance;
+ reset = true;
+ }
+ }
+
+ if (end > frame) {
+
+ distance = end - frame;
+
+ if (distance < closest) {
+ ret = end;
+ closest = distance;
+ reset = true;
+ }
+ }
+
+ if (reset) {
+ break;
+ }
+ }
+
+ } else {
+
+ for (RegionList::reverse_iterator i = regions.rbegin(); i != regions.rend(); ++i) {
+
+ boost::shared_ptr<Region> r = (*i);
+ nframes64_t distance;
+ bool reset;
+
+ reset = false;
+
+ if (r->last_frame() < frame) {
+
+ distance = frame - r->last_frame();
+
+ if (distance < closest) {
+ ret = r->last_frame();
+ closest = distance;
+ reset = true;
+ }
+ }
+
+ if (r->first_frame() < frame) {
+ distance = frame - r->last_frame();
+
+ if (distance < closest) {
+ ret = r->first_frame();
+ closest = distance;
+ reset = true;
+ }
+ }
+
+ if (reset) {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+/***********************************************************************/
+
+
+
+void
+Playlist::mark_session_dirty ()
+{
+ if (!in_set_state && !holding_state ()) {
+ _session.set_dirty();
+ }
+}
+
+int
+Playlist::set_state (const XMLNode& node)
+{
+ XMLNode *child;
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLPropertyList plist;
+ XMLPropertyConstIterator piter;
+ XMLProperty *prop;
+ boost::shared_ptr<Region> region;
+ string region_name;
+
+ in_set_state++;
+
+ if (node.name() != "Playlist") {
+ in_set_state--;
+ return -1;
+ }
+
+ freeze ();
+
+ plist = node.properties();
+
+ for (piter = plist.begin(); piter != plist.end(); ++piter) {
+
+ prop = *piter;
+
+ if (prop->name() == X_("name")) {
+ _name = prop->value();
+ } else if (prop->name() == X_("orig_diskstream_id")) {
+ _orig_diskstream_id = prop->value ();
+ } else if (prop->name() == X_("frozen")) {
+ _frozen = (prop->value() == X_("yes"));
+ }
+ }
+
+ clear (false);
+
+ nlist = node.children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ child = *niter;
+
+ if (child->name() == "Region") {
+
+ if ((prop = child->property ("id")) == 0) {
+ error << _("region state node has no ID, ignored") << endmsg;
+ continue;
+ }
+
+ ID id = prop->value ();
+
+ if ((region = region_by_id (id))) {
+
+ Change what_changed = Change (0);
+
+ if (region->set_live_state (*child, what_changed, true)) {
+ error << _("Playlist: cannot reset region state from XML") << endmsg;
+ continue;
+ }
+
+ } else if ((region = RegionFactory::create (_session, *child, true)) == 0) {
+ error << _("Playlist: cannot create region from XML") << endmsg;
+ continue;
+ }
+
+ add_region (region, region->position(), 1.0);
+
+ // So that layer_op ordering doesn't get screwed up
+ region->set_last_layer_op( region->layer());
+
+ }
+ }
+
+ notify_modified ();
+
+ thaw ();
+
+ /* update dependents, which was not done during add_region_internal
+ due to in_set_state being true
+ */
+
+ for (RegionList::iterator r = regions.begin(); r != regions.end(); ++r) {
+ check_dependents (*r, false);
+ }
+
+ in_set_state--;
+ first_set_state = false;
+ return 0;
+}
+
+XMLNode&
+Playlist::get_state()
+{
+ return state(true);
+}
+
+XMLNode&
+Playlist::get_template()
+{
+ return state(false);
+}
+
+XMLNode&
+Playlist::state (bool full_state)
+{
+ XMLNode *node = new XMLNode (X_("Playlist"));
+ char buf[64];
+
+ node->add_property (X_("name"), _name);
+ node->add_property (X_("type"), _type.to_string());
+
+ _orig_diskstream_id.print (buf, sizeof (buf));
+ node->add_property (X_("orig_diskstream_id"), buf);
+ node->add_property (X_("frozen"), _frozen ? "yes" : "no");
+
+ if (full_state) {
+ RegionLock rlock (this, false);
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ node->add_child_nocopy ((*i)->get_state());
+ }
+ }
+
+ if (_extra_xml) {
+ node->add_child_copy (*_extra_xml);
+ }
+
+ return *node;
+}
+
+bool
+Playlist::empty() const
+{
+ RegionLock rlock (const_cast<Playlist *>(this), false);
+ return regions.empty();
+}
+
+uint32_t
+Playlist::n_regions() const
+{
+ RegionLock rlock (const_cast<Playlist *>(this), false);
+ return regions.size();
+}
+
+nframes_t
+Playlist::get_maximum_extent () const
+{
+ RegionLock rlock (const_cast<Playlist *>(this), false);
+ return _get_maximum_extent ();
+}
+
+ARDOUR::nframes_t
+Playlist::_get_maximum_extent () const
+{
+ RegionList::const_iterator i;
+ nframes_t max_extent = 0;
+ nframes_t end = 0;
+
+ for (i = regions.begin(); i != regions.end(); ++i) {
+ if ((end = (*i)->position() + (*i)->length()) > max_extent) {
+ max_extent = end;
+ }
+ }
+
+ return max_extent;
+}
+
+string
+Playlist::bump_name (string name, Session &session)
+{
+ string newname = name;
+
+ do {
+ newname = bump_name_once (newname);
+ } while (session.playlist_by_name (newname)!=NULL);
+
+ return newname;
+}
+
+
+layer_t
+Playlist::top_layer() const
+{
+ RegionLock rlock (const_cast<Playlist *> (this));
+ layer_t top = 0;
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ top = max (top, (*i)->layer());
+ }
+ return top;
+}
+
+void
+Playlist::set_edit_mode (EditMode mode)
+{
+ _edit_mode = mode;
+}
+
+/********************
+ * Region Layering
+ ********************/
+
+void
+Playlist::relayer ()
+{
+ /* don't send multiple Modified notifications
+ when multiple regions are relayered.
+ */
+
+ freeze ();
+
+ /* build up a new list of regions on each layer */
+
+ std::vector<RegionList> layers;
+
+ /* we want to go through regions from desired lowest to desired highest layer,
+ which depends on the layer model
+ */
+
+ RegionList copy = regions;
+
+ /* sort according to the model */
+
+ if (Config->get_layer_model() == MoveAddHigher || Config->get_layer_model() == AddHigher) {
+ RegionSortByLastLayerOp cmp;
+ copy.sort (cmp);
+ }
+
+ for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
+
+ /* find the lowest layer that this region can go on */
+ size_t j = layers.size();
+ while (j > 0) {
+ /* try layer j - 1; it can go on if it overlaps no other region
+ that is already on that layer
+ */
+ RegionList::iterator k = layers[j - 1].begin();
+ while (k != layers[j - 1].end()) {
+ if ((*k)->overlap_equivalent (*i)) {
+ break;
+ }
+ k++;
+ }
+
+ if (k != layers[j - 1].end()) {
+ /* no overlap, so we can use this layer */
+ break;
+ }
+
+ j--;
+ }
+
+ if (j == layers.size()) {
+ /* we need a new layer for this region */
+ layers.push_back (RegionList ());
+ }
+
+ layers[j].push_back (*i);
+ }
+
+ /* first pass: set up the layer numbers in the regions */
+ for (size_t j = 0; j < layers.size(); ++j) {
+ for (RegionList::iterator i = layers[j].begin(); i != layers[j].end(); ++i) {
+ (*i)->set_layer (j);
+ }
+ }
+
+ /* sending Modified means that various kinds of layering
+ models operate correctly at the GUI
+ level. slightly inefficient, but only slightly.
+
+ We force a Modified signal here in case no layers actually
+ changed.
+ */
+
+ notify_modified ();
+
+ thaw ();
+}
+
+/* XXX these layer functions are all deprecated */
+
+void
+Playlist::raise_region (boost::shared_ptr<Region> region)
+{
+ uint32_t rsz = regions.size();
+ layer_t target = region->layer() + 1U;
+
+ if (target >= rsz) {
+ /* its already at the effective top */
+ return;
+ }
+
+ move_region_to_layer (target, region, 1);
+}
+
+void
+Playlist::lower_region (boost::shared_ptr<Region> region)
+{
+ if (region->layer() == 0) {
+ /* its already at the bottom */
+ return;
+ }
+
+ layer_t target = region->layer() - 1U;
+
+ move_region_to_layer (target, region, -1);
+}
+
+void
+Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
+{
+ /* does nothing useful if layering mode is later=higher */
+ if ((Config->get_layer_model() == MoveAddHigher) ||
+ (Config->get_layer_model() == AddHigher)) {
+ timestamp_layer_op (region);
+ relayer ();
+ }
+}
+
+void
+Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
+{
+ /* does nothing useful if layering mode is later=higher */
+ if ((Config->get_layer_model() == MoveAddHigher) ||
+ (Config->get_layer_model() == AddHigher)) {
+ region->set_last_layer_op (0);
+ relayer ();
+ }
+}
+
+int
+Playlist::move_region_to_layer (layer_t target_layer, boost::shared_ptr<Region> region, int dir)
+{
+ RegionList::iterator i;
+ typedef pair<boost::shared_ptr<Region>,layer_t> LayerInfo;
+ list<LayerInfo> layerinfo;
+ layer_t dest;
+
+ {
+ RegionLock rlock (const_cast<Playlist *> (this));
+
+ for (i = regions.begin(); i != regions.end(); ++i) {
+
+ if (region == *i) {
+ continue;
+ }
+
+ if (dir > 0) {
+
+ /* region is moving up, move all regions on intermediate layers
+ down 1
+ */
+
+ if ((*i)->layer() > region->layer() && (*i)->layer() <= target_layer) {
+ dest = (*i)->layer() - 1;
+ } else {
+ /* not affected */
+ continue;
+ }
+ } else {
+
+ /* region is moving down, move all regions on intermediate layers
+ up 1
+ */
+
+ if ((*i)->layer() < region->layer() && (*i)->layer() >= target_layer) {
+ dest = (*i)->layer() + 1;
+ } else {
+ /* not affected */
+ continue;
+ }
+ }
+
+ LayerInfo newpair;
+
+ newpair.first = *i;
+ newpair.second = dest;
+
+ layerinfo.push_back (newpair);
+ }
+ }
+
+ /* now reset the layers without holding the region lock */
+
+ for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
+ x->first->set_layer (x->second);
+ }
+
+ region->set_layer (target_layer);
+
+#if 0
+ /* now check all dependents */
+
+ for (list<LayerInfo>::iterator x = layerinfo.begin(); x != layerinfo.end(); ++x) {
+ check_dependents (x->first, false);
+ }
+
+ check_dependents (region, false);
+#endif
+
+ return 0;
+}
+
+void
+Playlist::nudge_after (nframes_t start, nframes_t distance, bool forwards)
+{
+ RegionList::iterator i;
+ nframes_t new_pos;
+ bool moved = false;
+
+ _nudging = true;
+
+ {
+ RegionLock rlock (const_cast<Playlist *> (this));
+
+ for (i = regions.begin(); i != regions.end(); ++i) {
+
+ if ((*i)->position() >= start) {
+
+ if (forwards) {
+
+ if ((*i)->last_frame() > max_frames - distance) {
+ new_pos = max_frames - (*i)->length();
+ } else {
+ new_pos = (*i)->position() + distance;
+ }
+
+ } else {
+
+ if ((*i)->position() > distance) {
+ new_pos = (*i)->position() - distance;
+ } else {
+ new_pos = 0;
+ }
+ }
+
+ (*i)->set_position (new_pos, this);
+ moved = true;
+ }
+ }
+ }
+
+ if (moved) {
+ _nudging = false;
+ notify_length_changed ();
+ }
+
+}
+
+boost::shared_ptr<Region>
+Playlist::find_region (const ID& id) const
+{
+ RegionLock rlock (const_cast<Playlist*> (this));
+
+ /* searches all regions currently in use by the playlist */
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i)->id() == id) {
+ return *i;
+ }
+ }
+
+ return boost::shared_ptr<Region> ();
+}
+
+boost::shared_ptr<Region>
+Playlist::region_by_id (ID id)
+{
+ /* searches all regions ever added to this playlist */
+
+ for (set<boost::shared_ptr<Region> >::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
+ if ((*i)->id() == id) {
+ return *i;
+ }
+ }
+ return boost::shared_ptr<Region> ();
+}
+
+void
+Playlist::dump () const
+{
+ boost::shared_ptr<Region> r;
+
+ cerr << "Playlist \"" << _name << "\" " << endl
+ << regions.size() << " regions "
+ << endl;
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ r = *i;
+ cerr << " " << r->name() << " ["
+ << r->start() << "+" << r->length()
+ << "] at "
+ << r->position()
+ << " on layer "
+ << r->layer ()
+ << endl;
+ }
+}
+
+void
+Playlist::set_frozen (bool yn)
+{
+ _frozen = yn;
+}
+
+void
+Playlist::timestamp_layer_op (boost::shared_ptr<Region> region)
+{
+// struct timeval tv;
+// gettimeofday (&tv, 0);
+ region->set_last_layer_op (++layer_op_counter);
+}
+
+
+void
+Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
+{
+ bool moved = false;
+ nframes_t new_pos;
+
+ if (region->locked()) {
+ return;
+ }
+
+ _shuffling = true;
+
+ {
+ RegionLock rlock (const_cast<Playlist*> (this));
+
+
+ if (dir > 0) {
+
+ RegionList::iterator next;
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ++i) {
+ if ((*i) == region) {
+ next = i;
+ ++next;
+
+ if (next != regions.end()) {
+
+ if ((*next)->locked()) {
+ break;
+ }
+
+ if ((*next)->position() != region->last_frame() + 1) {
+ /* they didn't used to touch, so after shuffle,
+ just have them swap positions.
+ */
+ new_pos = (*next)->position();
+ } else {
+ /* they used to touch, so after shuffle,
+ make sure they still do. put the earlier
+ region where the later one will end after
+ it is moved.
+ */
+ new_pos = region->position() + (*next)->length();
+ }
+
+ (*next)->set_position (region->position(), this);
+ region->set_position (new_pos, this);
+
+ /* avoid a full sort */
+
+ regions.erase (i); // removes the region from the list */
+ next++;
+ regions.insert (next, region); // adds it back after next
+
+ moved = true;
+ }
+ break;
+ }
+ }
+ } else {
+
+ RegionList::iterator prev = regions.end();
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); prev = i, ++i) {
+ if ((*i) == region) {
+
+ if (prev != regions.end()) {
+
+ if ((*prev)->locked()) {
+ break;
+ }
+
+ if (region->position() != (*prev)->last_frame() + 1) {
+ /* they didn't used to touch, so after shuffle,
+ just have them swap positions.
+ */
+ new_pos = region->position();
+ } else {
+ /* they used to touch, so after shuffle,
+ make sure they still do. put the earlier
+ one where the later one will end after
+ */
+ new_pos = (*prev)->position() + region->length();
+ }
+
+ region->set_position ((*prev)->position(), this);
+ (*prev)->set_position (new_pos, this);
+
+ /* avoid a full sort */
+
+ regions.erase (i); // remove region
+ regions.insert (prev, region); // insert region before prev
+
+ moved = true;
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ _shuffling = false;
+
+ if (moved) {
+
+ relayer ();
+ check_dependents (region, false);
+
+ notify_modified();
+ }
+
+}
+
+bool
+Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
+{
+ RegionLock rlock (const_cast<Playlist*> (this));
+
+ if (regions.size() > 1) {
+ return true;
+ }
+
+ return false;
+}
+
+void
+Playlist::update_after_tempo_map_change ()
+{
+ RegionLock rlock (const_cast<Playlist*> (this));
+ RegionList copy (regions);
+
+ freeze ();
+
+ for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
+ (*i)->update_position_after_tempo_map_change ();
+ }
+
+ thaw ();
+}
diff --git a/libs/ardour/playlist_factory.cc b/libs/ardour/playlist_factory.cc
new file mode 100644
index 0000000000..a801bae76c
--- /dev/null
+++ b/libs/ardour/playlist_factory.cc
@@ -0,0 +1,111 @@
+/*
+ Copyright (C) 2000-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/error.h>
+
+#include <ardour/playlist.h>
+#include <ardour/audioplaylist.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/playlist_factory.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+sigc::signal<void,boost::shared_ptr<Playlist> > PlaylistFactory::PlaylistCreated;
+
+boost::shared_ptr<Playlist>
+PlaylistFactory::create (Session& s, const XMLNode& node, bool hidden)
+{
+ const XMLProperty* type = node.property("type");
+
+ boost::shared_ptr<Playlist> pl;
+
+ if ( !type || type->value() == "audio" )
+ pl = boost::shared_ptr<Playlist> (new AudioPlaylist (s, node, hidden));
+ else if ( type->value() == "midi" )
+ pl = boost::shared_ptr<Playlist> (new MidiPlaylist (s, node, hidden));
+
+ pl->set_region_ownership ();
+
+ if (pl && !hidden) {
+ PlaylistCreated (pl);
+ }
+ return pl;
+}
+
+boost::shared_ptr<Playlist>
+PlaylistFactory::create (DataType type, Session& s, string name, bool hidden)
+{
+ boost::shared_ptr<Playlist> pl;
+
+ if (type == DataType::AUDIO)
+ pl = boost::shared_ptr<Playlist> (new AudioPlaylist (s, name, hidden));
+ else if (type == DataType::MIDI)
+ pl = boost::shared_ptr<Playlist> (new MidiPlaylist (s, name, hidden));
+
+ if (pl && !hidden) {
+ PlaylistCreated (pl);
+ }
+
+ return pl;
+}
+
+boost::shared_ptr<Playlist>
+PlaylistFactory::create (boost::shared_ptr<const Playlist> old, string name, bool hidden)
+{
+ boost::shared_ptr<Playlist> pl;
+ boost::shared_ptr<const AudioPlaylist> apl;
+ boost::shared_ptr<const MidiPlaylist> mpl;
+
+ if ((apl = boost::dynamic_pointer_cast<const AudioPlaylist> (old)) != 0) {
+ pl = boost::shared_ptr<Playlist> (new AudioPlaylist (apl, name, hidden));
+ pl->set_region_ownership ();
+ } else if ((mpl = boost::dynamic_pointer_cast<const MidiPlaylist> (old)) != 0) {
+ pl = boost::shared_ptr<Playlist> (new MidiPlaylist (mpl, name, hidden));
+ pl->set_region_ownership ();
+ }
+
+ if (pl && !hidden) {
+ PlaylistCreated (pl);
+ }
+
+ return pl;
+}
+
+boost::shared_ptr<Playlist>
+PlaylistFactory::create (boost::shared_ptr<const Playlist> old, nframes_t start, nframes_t cnt, string name, bool hidden)
+{
+ boost::shared_ptr<Playlist> pl;
+ boost::shared_ptr<const AudioPlaylist> apl;
+ boost::shared_ptr<const MidiPlaylist> mpl;
+
+ if ((apl = boost::dynamic_pointer_cast<const AudioPlaylist> (old)) != 0) {
+ pl = boost::shared_ptr<Playlist> (new AudioPlaylist (apl, start, cnt, name, hidden));
+ pl->set_region_ownership ();
+ } else if ((mpl = boost::dynamic_pointer_cast<const MidiPlaylist> (old)) != 0) {
+ pl = boost::shared_ptr<Playlist> (new MidiPlaylist (mpl, start, cnt, name, hidden));
+ pl->set_region_ownership ();
+ }
+
+ /* this factory method does NOT notify others */
+
+ return pl;
+}
diff --git a/libs/ardour/plugin.cc b/libs/ardour/plugin.cc
new file mode 100644
index 0000000000..3d218448dd
--- /dev/null
+++ b/libs/ardour/plugin.cc
@@ -0,0 +1,243 @@
+/*
+ Copyright (C) 2000-2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <vector>
+#include <string>
+
+#include <cstdlib>
+#include <cstdio> // so libraptor doesn't complain
+#include <cmath>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <cerrno>
+
+#include <lrdf.h>
+
+#include <pbd/compose.h>
+#include <pbd/error.h>
+#include <pbd/xml++.h>
+
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+#include <ardour/audioengine.h>
+#include <ardour/plugin.h>
+#include <ardour/ladspa_plugin.h>
+#include <ardour/plugin_manager.h>
+
+#ifdef HAVE_AUDIOUNITS
+#include <ardour/audio_unit.h>
+#endif
+
+#ifdef HAVE_SLV2
+#include <ardour/lv2_plugin.h>
+#endif
+
+#include <pbd/stl_delete.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace ARDOUR;
+using namespace PBD;
+
+Plugin::Plugin (AudioEngine& e, Session& s)
+ : _engine (e), _session (s)
+{
+}
+
+Plugin::Plugin (const Plugin& other)
+ : _engine (other._engine), _session (other._session), _info (other._info)
+{
+}
+
+Plugin::~Plugin ()
+{
+}
+
+vector<string>
+Plugin::get_presets()
+{
+ vector<string> labels;
+ uint32_t id;
+ std::string unique (unique_id());
+
+ /* XXX problem: AU plugins don't have numeric ID's.
+ Solution: they have a different method of providing presets.
+ XXX sub-problem: implement it.
+ */
+
+ if (!isdigit (unique[0])) {
+ return labels;
+ }
+
+ id = atol (unique.c_str());
+
+ lrdf_uris* set_uris = lrdf_get_setting_uris(id);
+
+ if (set_uris) {
+ for (uint32_t i = 0; i < (uint32_t) set_uris->count; ++i) {
+ if (char* label = lrdf_get_label(set_uris->items[i])) {
+ labels.push_back(label);
+ presets[label] = set_uris->items[i];
+ }
+ }
+ lrdf_free_uris(set_uris);
+ }
+
+ // GTK2FIX find an equivalent way to do this with a vector (needed by GUI apis)
+ // labels.unique();
+
+ return labels;
+}
+
+bool
+Plugin::load_preset(const string preset_label)
+{
+ lrdf_defaults* defs = lrdf_get_setting_values(presets[preset_label].c_str());
+
+ if (defs) {
+ for (uint32_t i = 0; i < (uint32_t) defs->count; ++i) {
+ // The defs->items[i].pid < defs->count check is to work around
+ // a bug in liblrdf that saves invalid values into the presets file.
+ if (((uint32_t) defs->items[i].pid < (uint32_t) defs->count) && parameter_is_input (defs->items[i].pid)) {
+ set_parameter(defs->items[i].pid, defs->items[i].value);
+ }
+ }
+ lrdf_free_setting_values(defs);
+ }
+
+ return true;
+}
+
+bool
+Plugin::save_preset (string name, string domain)
+{
+ lrdf_portvalue portvalues[parameter_count()];
+ lrdf_defaults defaults;
+ uint32_t id;
+ std::string unique (unique_id());
+
+ /* XXX problem: AU plugins don't have numeric ID's.
+ Solution: they have a different method of providing/saving presets.
+ XXX sub-problem: implement it.
+ */
+
+ if (!isdigit (unique[0])) {
+ return false;
+ }
+
+ id = atol (unique.c_str());
+
+ defaults.count = parameter_count();
+ defaults.items = portvalues;
+
+ for (uint32_t i = 0; i < parameter_count(); ++i) {
+ if (parameter_is_input (i)) {
+ portvalues[i].pid = i;
+ portvalues[i].value = get_parameter(i);
+ }
+ }
+
+ char* envvar;
+ if ((envvar = getenv ("HOME")) == 0) {
+ warning << _("Could not locate HOME. Preset not saved.") << endmsg;
+ return false;
+ }
+
+ string source(string_compose("file:%1/.%2/rdf/ardour-presets.n3", envvar, domain));
+
+ free(lrdf_add_preset(source.c_str(), name.c_str(), id, &defaults));
+
+ string path = string_compose("%1/.%2", envvar, domain);
+ if (g_mkdir_with_parents (path.c_str(), 0775)) {
+ warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg;
+ return false;
+ }
+
+ path += "/rdf";
+ if (g_mkdir_with_parents (path.c_str(), 0775)) {
+ warning << string_compose(_("Could not create %1. Preset not saved. (%2)"), path, strerror(errno)) << endmsg;
+ return false;
+ }
+
+ if (lrdf_export_by_source(source.c_str(), source.substr(5).c_str())) {
+ warning << string_compose(_("Error saving presets file %1."), source) << endmsg;
+ return false;
+ }
+
+ return true;
+}
+
+PluginPtr
+ARDOUR::find_plugin(Session& session, string identifier, PluginType type)
+{
+ PluginManager *mgr = PluginManager::the_manager();
+ PluginInfoList plugs;
+
+ switch (type) {
+ case ARDOUR::LADSPA:
+ plugs = mgr->ladspa_plugin_info();
+ break;
+
+#ifdef HAVE_SLV2
+ case ARDOUR::LV2:
+ plugs = mgr->lv2_plugin_info();
+ break;
+#endif
+
+#ifdef VST_SUPPORT
+ case ARDOUR::VST:
+ plugs = mgr->vst_plugin_info();
+ break;
+#endif
+
+#ifdef HAVE_AUDIOUNITS
+ case ARDOUR::AudioUnit:
+ plugs = mgr->au_plugin_info();
+ break;
+#endif
+
+ default:
+ return PluginPtr ((Plugin *) 0);
+ }
+
+ PluginInfoList::iterator i;
+
+ for (i = plugs.begin(); i != plugs.end(); ++i) {
+ if (identifier == (*i)->unique_id){
+ return (*i)->load (session);
+ }
+ }
+
+#ifdef VST_SUPPORT
+ /* hmm, we didn't find it. could be because in older versions of Ardour.
+ we used to store the name of a VST plugin, not its unique ID. so try
+ again.
+ */
+
+ for (i = plugs.begin(); i != plugs.end(); ++i) {
+ if (identifier == (*i)->name){
+ return (*i)->load (session);
+ }
+ }
+#endif
+
+ return PluginPtr ((Plugin*) 0);
+}
+
diff --git a/libs/ardour/plugin_insert.cc b/libs/ardour/plugin_insert.cc
new file mode 100644
index 0000000000..26d344dee4
--- /dev/null
+++ b/libs/ardour/plugin_insert.cc
@@ -0,0 +1,917 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+
+#include <sigc++/bind.h>
+
+#include <pbd/failed_constructor.h>
+#include <pbd/xml++.h>
+
+#include <ardour/plugin_insert.h>
+#include <ardour/plugin.h>
+#include <ardour/port.h>
+#include <ardour/route.h>
+#include <ardour/ladspa_plugin.h>
+#include <ardour/buffer_set.h>
+#include <ardour/automation_event.h>
+
+#ifdef HAVE_SLV2
+#include <ardour/lv2_plugin.h>
+#endif
+
+#ifdef VST_SUPPORT
+#include <ardour/vst_plugin.h>
+#endif
+
+#ifdef HAVE_AUDIOUNITS
+#include <ardour/audio_unit.h>
+#endif
+
+#include <ardour/audioengine.h>
+#include <ardour/session.h>
+#include <ardour/types.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+const string PluginInsert::port_automation_node_name = "PortAutomation";
+
+PluginInsert::PluginInsert (Session& s, boost::shared_ptr<Plugin> plug, Placement placement)
+ : Processor (s, plug->name(), placement)
+{
+ /* the first is the master */
+
+ _plugins.push_back (plug);
+
+ init ();
+
+ {
+ Glib::Mutex::Lock em (_session.engine().process_lock());
+ IO::PortCountChanged (max(input_streams(), output_streams()));
+ }
+
+ ProcessorCreated (this); /* EMIT SIGNAL */
+}
+
+PluginInsert::PluginInsert (Session& s, const XMLNode& node)
+ : Processor (s, "unnamed plugin insert", PreFader)
+{
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+
+ // set_automatable ();
+
+ {
+ Glib::Mutex::Lock em (_session.engine().process_lock());
+ IO::PortCountChanged (max(input_streams(), output_streams()));
+ }
+}
+
+PluginInsert::PluginInsert (const PluginInsert& other)
+ : Processor (other._session, other._name, other.placement())
+{
+ uint32_t count = other._plugins.size();
+
+ /* make as many copies as requested */
+ for (uint32_t n = 0; n < count; ++n) {
+ _plugins.push_back (plugin_factory (other.plugin (n)));
+ }
+
+ init ();
+
+ ProcessorCreated (this); /* EMIT SIGNAL */
+}
+
+bool
+PluginInsert::set_count (uint32_t num)
+{
+ bool require_state = !_plugins.empty();
+
+ /* this is a bad idea.... we shouldn't do this while active.
+ only a route holding their redirect_lock should be calling this
+ */
+
+ if (num == 0) {
+ return false;
+ } else if (num > _plugins.size()) {
+ uint32_t diff = num - _plugins.size();
+
+ for (uint32_t n = 0; n < diff; ++n) {
+ _plugins.push_back (plugin_factory (_plugins[0]));
+
+ if (require_state) {
+ /* XXX do something */
+ }
+ }
+
+ } else if (num < _plugins.size()) {
+ uint32_t diff = _plugins.size() - num;
+ for (uint32_t n= 0; n < diff; ++n) {
+ _plugins.pop_back();
+ }
+ }
+
+ return true;
+}
+
+void
+PluginInsert::init ()
+{
+ set_automatable ();
+}
+
+PluginInsert::~PluginInsert ()
+{
+ GoingAway (); /* EMIT SIGNAL */
+}
+
+void
+PluginInsert::auto_state_changed (Parameter which)
+{
+ if (which.type() != PluginAutomation)
+ return;
+
+ boost::shared_ptr<AutomationControl> c = control (which);
+
+ if (c && c->list()->automation_state() != Off) {
+ _plugins[0]->set_parameter (which.id(), c->list()->eval (_session.transport_frame()));
+ }
+}
+
+ChanCount
+PluginInsert::output_streams() const
+{
+ if (_configured)
+ return output_for_input_configuration(_configured_input);
+ else
+ return natural_output_streams();
+}
+
+ChanCount
+PluginInsert::input_streams() const
+{
+ if (_configured)
+ return _configured_input;
+ else
+ return natural_input_streams();
+}
+
+ChanCount
+PluginInsert::natural_output_streams() const
+{
+ return _plugins[0]->get_info()->n_outputs;
+}
+
+ChanCount
+PluginInsert::natural_input_streams() const
+{
+ return _plugins[0]->get_info()->n_inputs;
+}
+
+bool
+PluginInsert::is_generator() const
+{
+ /* XXX more finesse is possible here. VST plugins have a
+ a specific "instrument" flag, for example.
+ */
+
+ return _plugins[0]->get_info()->n_inputs.n_audio() == 0;
+}
+
+void
+PluginInsert::set_automatable ()
+{
+ set<Parameter> a = _plugins.front()->automatable ();
+
+ Plugin::ParameterDescriptor desc;
+
+ for (set<Parameter>::iterator i = a.begin(); i != a.end(); ++i) {
+ if (i->type() == PluginAutomation) {
+ can_automate (*i);
+ _plugins.front()->get_parameter_descriptor(i->id(), desc);
+ boost::shared_ptr<AutomationList> list(new AutomationList(
+ *i,
+ //(desc.min_unbound ? FLT_MIN : desc.lower),
+ //(desc.max_unbound ? FLT_MAX : desc.upper),
+ desc.lower, desc.upper,
+ _plugins.front()->default_value(i->id())));
+
+ add_control(boost::shared_ptr<AutomationControl>(
+ new PluginControl(*this, list)));
+ }
+ }
+}
+
+void
+PluginInsert::parameter_changed (Parameter which, float val)
+{
+ if (which.type() != PluginAutomation)
+ return;
+
+ vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin();
+
+ /* don't set the first plugin, just all the slaves */
+
+ if (i != _plugins.end()) {
+ ++i;
+ for (; i != _plugins.end(); ++i) {
+ (*i)->set_parameter (which, val);
+ }
+ }
+}
+
+void
+PluginInsert::set_block_size (nframes_t nframes)
+{
+ for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
+ (*i)->set_block_size (nframes);
+ }
+}
+
+void
+PluginInsert::activate ()
+{
+ for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
+ (*i)->activate ();
+ }
+}
+
+void
+PluginInsert::deactivate ()
+{
+ for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
+ (*i)->deactivate ();
+ }
+}
+
+void
+PluginInsert::connect_and_run (BufferSet& bufs, nframes_t nframes, nframes_t offset, bool with_auto, nframes_t now)
+{
+ uint32_t in_index = 0;
+ uint32_t out_index = 0;
+
+ /* Note that we've already required that plugins
+ be able to handle in-place processing.
+ */
+
+ if (with_auto) {
+
+ uint32_t n = 0;
+
+ for (Controls::iterator li = _controls.begin(); li != _controls.end(); ++li, ++n) {
+
+ boost::shared_ptr<AutomationControl> c = li->second;
+
+ if (c->parameter().type() == PluginAutomation && c->list()->automation_playback()) {
+ bool valid;
+
+ const float val = c->list()->rt_safe_eval (now, valid);
+
+ if (valid) {
+ c->set_value(val);
+ }
+
+ }
+ }
+ }
+
+ for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
+ (*i)->connect_and_run (bufs, in_index, out_index, nframes, offset);
+ }
+
+ /* leave remaining channel buffers alone */
+}
+
+void
+PluginInsert::silence (nframes_t nframes, nframes_t offset)
+{
+ uint32_t in_index = 0;
+ uint32_t out_index = 0;
+
+ if (active()) {
+ for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
+ (*i)->connect_and_run (_session.get_silent_buffers ((*i)->get_info()->n_inputs), in_index, out_index, nframes, offset);
+ }
+ }
+}
+
+void
+PluginInsert::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
+{
+ if (active()) {
+
+ if (_session.transport_rolling()) {
+ automation_run (bufs, nframes, offset);
+ } else {
+ connect_and_run (bufs, nframes, offset, false);
+ }
+ } else {
+
+ /* FIXME: type, audio only */
+
+ uint32_t in = _plugins[0]->get_info()->n_inputs.n_audio();
+ uint32_t out = _plugins[0]->get_info()->n_outputs.n_audio();
+
+ if (out > in) {
+
+ /* not active, but something has make up for any channel count increase */
+
+ for (uint32_t n = out - in; n < out; ++n) {
+ memcpy (bufs.get_audio(n).data(nframes, offset), bufs.get_audio(in - 1).data(nframes, offset), sizeof (Sample) * nframes);
+ }
+ }
+
+ bufs.count().set_audio(out);
+ }
+}
+
+void
+PluginInsert::set_parameter (Parameter param, float val)
+{
+ if (param.type() != PluginAutomation)
+ return;
+
+ /* the others will be set from the event triggered by this */
+
+ _plugins[0]->set_parameter (param.id(), val);
+
+ boost::shared_ptr<AutomationControl> c = control (param);
+
+ if (c)
+ c->set_value(val);
+
+ _session.set_dirty();
+}
+
+float
+PluginInsert::get_parameter (Parameter param)
+{
+ if (param.type() != PluginAutomation)
+ return 0.0;
+ else
+ return
+ _plugins[0]->get_parameter (param.id());
+}
+
+void
+PluginInsert::automation_run (BufferSet& bufs, nframes_t nframes, nframes_t offset)
+{
+ ControlEvent next_event (0, 0.0f);
+ nframes_t now = _session.transport_frame ();
+ nframes_t end = now + nframes;
+
+ Glib::Mutex::Lock lm (_automation_lock, Glib::TRY_LOCK);
+
+ if (!lm.locked()) {
+ connect_and_run (bufs, nframes, offset, false);
+ return;
+ }
+
+ if (!find_next_event (now, end, next_event)) {
+
+ /* no events have a time within the relevant range */
+
+ connect_and_run (bufs, nframes, offset, true, now);
+ return;
+ }
+
+ while (nframes) {
+
+ nframes_t cnt = min (((nframes_t) ceil (next_event.when) - now), nframes);
+
+ connect_and_run (bufs, cnt, offset, true, now);
+
+ nframes -= cnt;
+ offset += cnt;
+ now += cnt;
+
+ if (!find_next_event (now, end, next_event)) {
+ break;
+ }
+ }
+
+ /* cleanup anything that is left to do */
+
+ if (nframes) {
+ connect_and_run (bufs, nframes, offset, true, now);
+ }
+}
+
+float
+PluginInsert::default_parameter_value (Parameter param)
+{
+ if (param.type() != PluginAutomation)
+ return 1.0;
+
+ if (_plugins.empty()) {
+ fatal << _("programming error: ") << X_("PluginInsert::default_parameter_value() called with no plugin")
+ << endmsg;
+ /*NOTREACHED*/
+ }
+
+ return _plugins[0]->default_value (param.id());
+}
+
+boost::shared_ptr<Plugin>
+PluginInsert::plugin_factory (boost::shared_ptr<Plugin> other)
+{
+ boost::shared_ptr<LadspaPlugin> lp;
+#ifdef HAVE_SLV2
+ boost::shared_ptr<LV2Plugin> lv2p;
+#endif
+#ifdef VST_SUPPORT
+ boost::shared_ptr<VSTPlugin> vp;
+#endif
+#ifdef HAVE_AUDIOUNITS
+ boost::shared_ptr<AUPlugin> ap;
+#endif
+
+ if ((lp = boost::dynamic_pointer_cast<LadspaPlugin> (other)) != 0) {
+ return boost::shared_ptr<Plugin> (new LadspaPlugin (*lp));
+#ifdef HAVE_SLV2
+ } else if ((lv2p = boost::dynamic_pointer_cast<LV2Plugin> (other)) != 0) {
+ return boost::shared_ptr<Plugin> (new LV2Plugin (*lv2p));
+#endif
+#ifdef VST_SUPPORT
+ } else if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (other)) != 0) {
+ return boost::shared_ptr<Plugin> (new VSTPlugin (*vp));
+#endif
+#ifdef HAVE_AUDIOUNITS
+ } else if ((ap = boost::dynamic_pointer_cast<AUPlugin> (other)) != 0) {
+ return boost::shared_ptr<Plugin> (new AUPlugin (*ap));
+#endif
+ }
+
+ fatal << string_compose (_("programming error: %1"),
+ X_("unknown plugin type in PluginInsert::plugin_factory"))
+ << endmsg;
+ /*NOTREACHED*/
+ return boost::shared_ptr<Plugin> ((Plugin*) 0);
+}
+
+bool
+PluginInsert::configure_io (ChanCount in, ChanCount out)
+{
+ ChanCount matching_out = output_for_input_configuration(out);
+ if (matching_out != out) {
+ _configured = false;
+ return false;
+ } else {
+ bool success = set_count (count_for_configuration(in, out));
+ if (success)
+ Processor::configure_io(in, out);
+ return success;
+ }
+}
+
+bool
+PluginInsert::can_support_input_configuration (ChanCount in) const
+{
+ ChanCount outputs = _plugins[0]->get_info()->n_outputs;
+ ChanCount inputs = _plugins[0]->get_info()->n_inputs;
+
+ /* see output_for_input_configuration below */
+ if ((inputs.n_total() == 0)
+ || (inputs.n_total() == 1 && outputs == inputs)
+ || (inputs.n_total() == 1 && outputs == inputs
+ && ((inputs.n_audio() == 0 && in.n_audio() == 0)
+ || (inputs.n_midi() == 0 && in.n_midi() == 0)))
+ || (inputs == in)) {
+ return true;
+ }
+
+ bool can_replicate = true;
+
+ /* if number of inputs is a factor of the requested input
+ configuration for every type, we can replicate.
+ */
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ if (inputs.get(*t) >= in.get(*t) || (inputs.get(*t) % in.get(*t) != 0)) {
+ can_replicate = false;
+ break;
+ }
+ }
+
+ if (can_replicate && (in.n_total() % inputs.n_total() == 0)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+ChanCount
+PluginInsert::output_for_input_configuration (ChanCount in) const
+{
+ ChanCount outputs = _plugins[0]->get_info()->n_outputs;
+ ChanCount inputs = _plugins[0]->get_info()->n_inputs;
+
+ if (inputs.n_total() == 0) {
+ /* instrument plugin, always legal, but throws away any existing streams */
+ return outputs;
+ }
+
+ if (inputs.n_total() == 1 && outputs == inputs
+ && ((inputs.n_audio() == 0 && in.n_audio() == 0)
+ || (inputs.n_midi() == 0 && in.n_midi() == 0))) {
+ /* mono plugin, replicate as needed to match in */
+ return in;
+ }
+
+ if (inputs == in) {
+ /* exact match */
+ return outputs;
+ }
+
+ bool can_replicate = true;
+
+ /* if number of inputs is a factor of the requested input
+ configuration for every type, we can replicate.
+ */
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ if (inputs.get(*t) >= in.get(*t) || (in.get(*t) % inputs.get(*t) != 0)) {
+ can_replicate = false;
+ break;
+ }
+ }
+
+ if (can_replicate && (inputs.n_total() % in.n_total() == 0)) {
+ ChanCount output;
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ output.set(*t, outputs.get(*t) * (in.get(*t) / inputs.get(*t)));
+ }
+
+ return output;
+ }
+
+ /* sorry */
+ return ChanCount();
+}
+
+/* Number of plugin instances required to support a given channel configuration.
+ * (private helper)
+ */
+int32_t
+PluginInsert::count_for_configuration (ChanCount in, ChanCount out) const
+{
+ // FIXME: take 'out' into consideration
+
+ ChanCount outputs = _plugins[0]->get_info()->n_outputs;
+ ChanCount inputs = _plugins[0]->get_info()->n_inputs;
+
+ if (inputs.n_total() == 0) {
+ /* instrument plugin, always legal, but throws away any existing streams */
+ return 1;
+ }
+
+ if (inputs.n_total() == 1 && outputs == inputs
+ && ((inputs.n_audio() == 0 && in.n_audio() == 0)
+ || (inputs.n_midi() == 0 && in.n_midi() == 0))) {
+ /* mono plugin, replicate as needed to match in */
+ return in.n_total();
+ }
+
+ if (inputs == in) {
+ /* exact match */
+ return 1;
+ }
+
+ // assumes in is valid, so we must be replicating
+ if (inputs.n_total() < in.n_total()
+ && (in.n_total() % inputs.n_total() == 0)) {
+
+ return in.n_total() / inputs.n_total();
+ }
+
+ /* err... */
+ return 0;
+}
+
+XMLNode&
+PluginInsert::get_state(void)
+{
+ return state (true);
+}
+
+XMLNode&
+PluginInsert::state (bool full)
+{
+ XMLNode& node = Processor::state (full);
+
+ node.add_property ("type", _plugins[0]->state_node_name());
+ node.add_property("unique-id", _plugins[0]->unique_id());
+ node.add_property("count", string_compose("%1", _plugins.size()));
+ node.add_child_nocopy (_plugins[0]->get_state());
+
+ /* add port automation state */
+ XMLNode *autonode = new XMLNode(port_automation_node_name);
+ set<Parameter> automatable = _plugins[0]->automatable();
+
+ for (set<Parameter>::iterator x = automatable.begin(); x != automatable.end(); ++x) {
+
+ /*XMLNode* child = new XMLNode("port");
+ snprintf(buf, sizeof(buf), "%" PRIu32, *x);
+ child->add_property("number", string(buf));
+
+ child->add_child_nocopy (automation_list (*x).state (full));
+ autonode->add_child_nocopy (*child);
+ */
+ autonode->add_child_nocopy (control(*x)->list()->state (full));
+ }
+
+ node.add_child_nocopy (*autonode);
+
+ return node;
+}
+
+int
+PluginInsert::set_state(const XMLNode& node)
+{
+ XMLNodeList nlist = node.children();
+ XMLNodeIterator niter;
+ XMLPropertyList plist;
+ const XMLProperty *prop;
+ ARDOUR::PluginType type;
+
+ if ((prop = node.property ("type")) == 0) {
+ error << _("XML node describing insert is missing the `type' field") << endmsg;
+ return -1;
+ }
+
+ if (prop->value() == X_("ladspa") || prop->value() == X_("Ladspa")) { /* handle old school sessions */
+ type = ARDOUR::LADSPA;
+ } else if (prop->value() == X_("lv2")) {
+ type = ARDOUR::LV2;
+ } else if (prop->value() == X_("vst")) {
+ type = ARDOUR::VST;
+ } else {
+ error << string_compose (_("unknown plugin type %1 in plugin insert state"),
+ prop->value())
+ << endmsg;
+ return -1;
+ }
+
+ prop = node.property ("unique-id");
+ if (prop == 0) {
+ error << _("Plugin has no unique ID field") << endmsg;
+ return -1;
+ }
+
+ boost::shared_ptr<Plugin> plugin;
+
+ plugin = find_plugin (_session, prop->value(), type);
+
+ if (plugin == 0) {
+ error << string_compose(_("Found a reference to a plugin (\"%1\") that is unknown.\n"
+ "Perhaps it was removed or moved since it was last used."), prop->value())
+ << endmsg;
+ return -1;
+ }
+
+ uint32_t count = 1;
+
+ if ((prop = node.property ("count")) != 0) {
+ sscanf (prop->value().c_str(), "%u", &count);
+ }
+
+ if (_plugins.size() != count) {
+
+ _plugins.push_back (plugin);
+
+ for (uint32_t n=1; n < count; ++n) {
+ _plugins.push_back (plugin_factory (plugin));
+ }
+ }
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == plugin->state_node_name()) {
+ for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugins.begin(); i != _plugins.end(); ++i) {
+ (*i)->set_state (**niter);
+ }
+ break;
+ }
+ }
+
+ const XMLNode* insert_node = &node;
+
+ // legacy sessions: search for child IOProcessor node
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == "IOProcessor") {
+ insert_node = *niter;
+ break;
+ }
+ }
+
+ Processor::set_state (*insert_node);
+
+ /* look for port automation node */
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ if ((*niter)->name() != port_automation_node_name) {
+ continue;
+ }
+
+ XMLNodeList cnodes;
+ XMLProperty *cprop;
+ XMLNodeConstIterator iter;
+ XMLNode *child;
+ const char *port;
+ uint32_t port_id;
+
+ cnodes = (*niter)->children ("port");
+
+ for(iter = cnodes.begin(); iter != cnodes.end(); ++iter){
+
+ child = *iter;
+
+ if ((cprop = child->property("number")) != 0) {
+ port = cprop->value().c_str();
+ } else {
+ warning << _("PluginInsert: Auto: no ladspa port number") << endmsg;
+ continue;
+ }
+
+ sscanf (port, "%" PRIu32, &port_id);
+
+ if (port_id >= _plugins[0]->parameter_count()) {
+ warning << _("PluginInsert: Auto: port id out of range") << endmsg;
+ continue;
+ }
+
+ if (!child->children().empty()) {
+ control (Parameter(PluginAutomation, port_id), true)->list()->set_state (*child->children().front());
+ } else {
+ if ((cprop = child->property("auto")) != 0) {
+
+ /* old school */
+
+ int x;
+ sscanf (cprop->value().c_str(), "0x%x", &x);
+ control (Parameter(PluginAutomation, port_id), true)->list()->set_automation_state (AutoState (x));
+
+ } else {
+
+ /* missing */
+
+ control (Parameter(PluginAutomation, port_id), true)->list()->set_automation_state (Off);
+ }
+ }
+
+ }
+
+ /* done */
+
+ break;
+ }
+
+ if (niter == nlist.end()) {
+ warning << string_compose(_("XML node describing a port automation is missing the `%1' information"), port_automation_node_name) << endmsg;
+ }
+
+ // The name of the PluginInsert comes from the plugin, nothing else
+ _name = plugin->get_info()->name;
+
+ return 0;
+}
+
+string
+PluginInsert::describe_parameter (Parameter param)
+{
+ if (param.type() != PluginAutomation)
+ return Automatable::describe_parameter(param);
+
+ return _plugins[0]->describe_parameter (param);
+}
+
+ARDOUR::nframes_t
+PluginInsert::signal_latency() const
+{
+ if (_user_latency) {
+ return _user_latency;
+ }
+
+ return _plugins[0]->signal_latency ();
+}
+
+ARDOUR::PluginType
+PluginInsert::type ()
+{
+ boost::shared_ptr<LadspaPlugin> lp;
+#ifdef VST_SUPPORT
+ boost::shared_ptr<VSTPlugin> vp;
+#endif
+#ifdef HAVE_AUDIOUNITS
+ boost::shared_ptr<AUPlugin> ap;
+#endif
+
+ PluginPtr other = plugin ();
+
+ if ((lp = boost::dynamic_pointer_cast<LadspaPlugin> (other)) != 0) {
+ return ARDOUR::LADSPA;
+#ifdef VST_SUPPORT
+ } else if ((vp = boost::dynamic_pointer_cast<VSTPlugin> (other)) != 0) {
+ return ARDOUR::VST;
+#endif
+#ifdef HAVE_AUDIOUNITS
+ } else if ((ap = boost::dynamic_pointer_cast<AUPlugin> (other)) != 0) {
+ return ARDOUR::AudioUnit;
+#endif
+ } else {
+ /* NOT REACHED */
+ return (ARDOUR::PluginType) 0;
+ }
+}
+
+PluginInsert::PluginControl::PluginControl (PluginInsert& p, boost::shared_ptr<AutomationList> list)
+ : AutomationControl (p.session(), list, p.describe_parameter(list->parameter()))
+ , _plugin (p)
+ , _list (list)
+{
+ Plugin::ParameterDescriptor desc;
+ p.plugin(0)->get_parameter_descriptor (list->parameter().id(), desc);
+ _logarithmic = desc.logarithmic;
+ _toggled = desc.toggled;
+}
+
+void
+PluginInsert::PluginControl::set_value (float val)
+{
+ /* FIXME: probably should be taking out some lock here.. */
+
+ if (_toggled) {
+ if (val > 0.5) {
+ val = 1.0;
+ } else {
+ val = 0.0;
+ }
+ } else {
+
+ /*const float range = _list->get_max_y() - _list->get_min_y();
+ const float lower = _list->get_min_y();
+
+ if (!_logarithmic) {
+ val = lower + (range * val);
+ } else {
+ float log_lower = 0.0f;
+ if (lower > 0.0f) {
+ log_lower = log(lower);
+ }
+
+ val = exp(log_lower + log(range) * val);
+ }*/
+
+ }
+
+ for (vector<boost::shared_ptr<Plugin> >::iterator i = _plugin._plugins.begin();
+ i != _plugin._plugins.end(); ++i) {
+ (*i)->set_parameter (_list->parameter().id(), val);
+ }
+
+ AutomationControl::set_value(val);
+}
+
+float
+PluginInsert::PluginControl::get_value (void) const
+{
+ /* FIXME: probably should be taking out some lock here.. */
+
+ float val = _plugin.get_parameter (_list->parameter());
+
+ return val;
+
+ /*if (_toggled) {
+
+ return val;
+
+ } else {
+
+ if (_logarithmic) {
+ val = log(val);
+ }
+
+ return ((val - lower) / range);
+ }*/
+}
+
diff --git a/libs/ardour/plugin_manager.cc b/libs/ardour/plugin_manager.cc
new file mode 100644
index 0000000000..6ff780a25f
--- /dev/null
+++ b/libs/ardour/plugin_manager.cc
@@ -0,0 +1,489 @@
+/*
+ Copyright (C) 2000-2006 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.
+
+*/
+
+#define __STDC_FORMAT_MACROS 1
+#include <stdint.h>
+
+#include <sys/types.h>
+#include <cstdio>
+#include <lrdf.h>
+#include <dlfcn.h>
+
+#ifdef VST_SUPPORT
+#include <fst.h>
+#include <pbd/basename.h>
+#include <string.h>
+#endif // VST_SUPPORT
+
+#include <pbd/pathscanner.h>
+
+#include <ardour/ladspa.h>
+#include <ardour/session.h>
+#include <ardour/plugin_manager.h>
+#include <ardour/plugin.h>
+#include <ardour/ladspa_plugin.h>
+
+#ifdef HAVE_SLV2
+#include <slv2/slv2.h>
+#include <ardour/lv2_plugin.h>
+#endif
+
+#ifdef VST_SUPPORT
+#include <ardour/vst_plugin.h>
+#endif
+
+#ifdef HAVE_AUDIOUNITS
+#include <ardour/audio_unit.h>
+#endif
+
+#include <pbd/error.h>
+#include <pbd/stl_delete.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+PluginManager* PluginManager::_manager = 0;
+
+PluginManager::PluginManager ()
+{
+ char* s;
+ string lrdf_path;
+
+ if ((s = getenv ("LADSPA_RDF_PATH"))){
+ lrdf_path = s;
+ }
+
+ if (lrdf_path.length() == 0) {
+ lrdf_path = "/usr/local/share/ladspa/rdf:/usr/share/ladspa/rdf";
+ }
+
+ add_lrdf_data(lrdf_path);
+ add_ladspa_presets();
+#ifdef VST_SUPPORT
+ if (Config->get_use_vst()) {
+ add_vst_presets();
+ }
+#endif /* VST_SUPPORT */
+
+ if ((s = getenv ("LADSPA_PATH"))) {
+ ladspa_path = s;
+ }
+
+ if ((s = getenv ("VST_PATH"))) {
+ vst_path = s;
+ } else if ((s = getenv ("VST_PLUGINS"))) {
+ vst_path = s;
+ }
+
+ if (_manager == 0) {
+ _manager = this;
+ }
+
+ /* the plugin manager is constructed too early to use Profile */
+
+ if (getenv ("ARDOUR_SAE")) {
+ ladspa_plugin_whitelist.push_back (1203); // single band parametric
+ ladspa_plugin_whitelist.push_back (1772); // caps compressor
+ ladspa_plugin_whitelist.push_back (1913); // fast lookahead limiter
+ ladspa_plugin_whitelist.push_back (1075); // simple RMS expander
+ ladspa_plugin_whitelist.push_back (1061); // feedback delay line (max 5s)
+ ladspa_plugin_whitelist.push_back (1216); // gverb
+ ladspa_plugin_whitelist.push_back (2150); // tap pitch shifter
+ }
+
+#ifdef HAVE_SLV2
+ _lv2_world = new LV2World();
+#endif
+
+ refresh ();
+}
+
+void
+PluginManager::refresh ()
+{
+ ladspa_refresh ();
+#ifdef HAVE_SLV2
+ lv2_refresh ();
+#endif
+#ifdef VST_SUPPORT
+ if (Config->get_use_vst()) {
+ vst_refresh ();
+ }
+#endif // VST_SUPPORT
+#ifdef HAVE_AUDIOUNITS
+ au_refresh ();
+#endif
+}
+
+void
+PluginManager::ladspa_refresh ()
+{
+ _ladspa_plugin_info.clear ();
+
+ if (ladspa_path.length() == 0) {
+ ladspa_path = "/usr/local/lib64/ladspa:/usr/local/lib/ladspa:/usr/lib64/ladspa:/usr/lib/ladspa:/Library/Audio/Plug-Ins/LADSPA";
+ }
+
+ ladspa_discover_from_path (ladspa_path);
+}
+
+
+int
+PluginManager::add_ladspa_directory (string path)
+{
+ if (ladspa_discover_from_path (path) == 0) {
+ ladspa_path += ':';
+ ladspa_path += path;
+ return 0;
+ }
+ return -1;
+}
+
+static bool ladspa_filter (const string& str, void *arg)
+{
+ /* Not a dotfile, has a prefix before a period, suffix is "so" */
+
+ return str[0] != '.' && (str.length() > 3 && str.find (".so") == (str.length() - 3));
+}
+
+int
+PluginManager::ladspa_discover_from_path (string path)
+{
+ PathScanner scanner;
+ vector<string *> *plugin_objects;
+ vector<string *>::iterator x;
+ int ret = 0;
+
+ plugin_objects = scanner (ladspa_path, ladspa_filter, 0, true, true);
+
+ if (plugin_objects) {
+ for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
+ ladspa_discover (**x);
+ }
+ }
+
+ vector_delete (plugin_objects);
+ return ret;
+}
+
+static bool rdf_filter (const string &str, void *arg)
+{
+ return str[0] != '.' &&
+ ((str.find(".rdf") == (str.length() - 4)) ||
+ (str.find(".rdfs") == (str.length() - 5)) ||
+ (str.find(".n3") == (str.length() - 3)));
+}
+
+void
+PluginManager::add_ladspa_presets()
+{
+ add_presets ("ladspa");
+}
+
+void
+PluginManager::add_vst_presets()
+{
+ add_presets ("vst");
+}
+void
+PluginManager::add_presets(string domain)
+{
+
+ PathScanner scanner;
+ vector<string *> *presets;
+ vector<string *>::iterator x;
+
+ char* envvar;
+ if ((envvar = getenv ("HOME")) == 0) {
+ return;
+ }
+
+ string path = string_compose("%1/.%2/rdf", envvar, domain);
+ presets = scanner (path, rdf_filter, 0, true, true);
+
+ if (presets) {
+ for (x = presets->begin(); x != presets->end (); ++x) {
+ string file = "file:" + **x;
+ if (lrdf_read_file(file.c_str())) {
+ warning << string_compose(_("Could not parse rdf file: %1"), *x) << endmsg;
+ }
+ }
+ }
+
+ vector_delete (presets);
+}
+
+void
+PluginManager::add_lrdf_data (const string &path)
+{
+ PathScanner scanner;
+ vector<string *>* rdf_files;
+ vector<string *>::iterator x;
+ string uri;
+
+ rdf_files = scanner (path, rdf_filter, 0, true, true);
+
+ if (rdf_files) {
+ for (x = rdf_files->begin(); x != rdf_files->end (); ++x) {
+ uri = "file://" + **x;
+
+ if (lrdf_read_file(uri.c_str())) {
+ warning << "Could not parse rdf file: " << uri << endmsg;
+ }
+ }
+ }
+
+ vector_delete (rdf_files);
+}
+
+int
+PluginManager::ladspa_discover (string path)
+{
+ void *module;
+ const LADSPA_Descriptor *descriptor;
+ LADSPA_Descriptor_Function dfunc;
+ const char *errstr;
+
+ if ((module = dlopen (path.c_str(), RTLD_NOW)) == 0) {
+ error << string_compose(_("LADSPA: cannot load module \"%1\" (%2)"), path, dlerror()) << endmsg;
+ return -1;
+ }
+
+ dfunc = (LADSPA_Descriptor_Function) dlsym (module, "ladspa_descriptor");
+
+ if ((errstr = dlerror()) != 0) {
+ error << string_compose(_("LADSPA: module \"%1\" has no descriptor function."), path) << endmsg;
+ error << errstr << endmsg;
+ dlclose (module);
+ return -1;
+ }
+
+ for (uint32_t i = 0; ; ++i) {
+ if ((descriptor = dfunc (i)) == 0) {
+ break;
+ }
+
+ if (!ladspa_plugin_whitelist.empty()) {
+ if (find (ladspa_plugin_whitelist.begin(), ladspa_plugin_whitelist.end(), descriptor->UniqueID) == ladspa_plugin_whitelist.end()) {
+ continue;
+ }
+ }
+
+ PluginInfoPtr info(new LadspaPluginInfo);
+ info->name = descriptor->Name;
+ info->category = get_ladspa_category(descriptor->UniqueID);
+ info->creator = descriptor->Maker;
+ info->path = path;
+ info->index = i;
+ info->n_inputs = ChanCount();
+ info->n_outputs = ChanCount();
+ info->type = ARDOUR::LADSPA;
+
+ char buf[32];
+ snprintf (buf, sizeof (buf), "%lu", descriptor->UniqueID);
+ info->unique_id = buf;
+
+ for (uint32_t n=0; n < descriptor->PortCount; ++n) {
+ if ( LADSPA_IS_PORT_AUDIO (descriptor->PortDescriptors[n]) ) {
+ if ( LADSPA_IS_PORT_INPUT (descriptor->PortDescriptors[n]) ) {
+ info->n_inputs.set_audio(info->n_inputs.n_audio() + 1);
+ }
+ else if ( LADSPA_IS_PORT_OUTPUT (descriptor->PortDescriptors[n]) ) {
+ info->n_outputs.set_audio(info->n_outputs.n_audio() + 1);
+ }
+ }
+ }
+
+ _ladspa_plugin_info.push_back (info);
+ }
+
+// GDB WILL NOT LIKE YOU IF YOU DO THIS
+// dlclose (module);
+
+ return 0;
+}
+
+string
+PluginManager::get_ladspa_category (uint32_t plugin_id)
+{
+ char buf[256];
+ lrdf_statement pattern;
+
+ snprintf(buf, sizeof(buf), "%s%" PRIu32, LADSPA_BASE, plugin_id);
+ pattern.subject = buf;
+ pattern.predicate = (char*)RDF_TYPE;
+ pattern.object = 0;
+ pattern.object_type = lrdf_uri;
+
+ lrdf_statement* matches1 = lrdf_matches (&pattern);
+
+ if (!matches1) {
+ return "";
+ }
+
+ pattern.subject = matches1->object;
+ pattern.predicate = (char*)(LADSPA_BASE "hasLabel");
+ pattern.object = 0;
+ pattern.object_type = lrdf_literal;
+
+ lrdf_statement* matches2 = lrdf_matches (&pattern);
+ lrdf_free_statements(matches1);
+
+ if (!matches2) {
+ return ("");
+ }
+
+ string label = matches2->object;
+ lrdf_free_statements(matches2);
+
+ return label;
+}
+
+#ifdef HAVE_SLV2
+void
+PluginManager::lv2_refresh ()
+{
+ lv2_discover();
+}
+
+int
+PluginManager::lv2_discover ()
+{
+ _lv2_plugin_info = LV2PluginInfo::discover(_lv2_world);
+ return 0;
+}
+#endif
+
+#ifdef HAVE_AUDIOUNITS
+void
+PluginManager::au_refresh ()
+{
+ au_discover();
+}
+
+int
+PluginManager::au_discover ()
+{
+ _au_plugin_info = AUPluginInfo::discover();
+ return 0;
+}
+
+#endif
+
+#ifdef VST_SUPPORT
+
+void
+PluginManager::vst_refresh ()
+{
+ _vst_plugin_info.clear ();
+
+ if (vst_path.length() == 0) {
+ vst_path = "/usr/local/lib/vst:/usr/lib/vst";
+ }
+
+ vst_discover_from_path (vst_path);
+}
+
+int
+PluginManager::add_vst_directory (string path)
+{
+ if (vst_discover_from_path (path) == 0) {
+ vst_path += ':';
+ vst_path += path;
+ return 0;
+ }
+ return -1;
+}
+
+static bool vst_filter (const string& str, void *arg)
+{
+ /* Not a dotfile, has a prefix before a period, suffix is "dll" */
+
+ return str[0] != '.' && (str.length() > 4 && str.find (".dll") == (str.length() - 4));
+}
+
+int
+PluginManager::vst_discover_from_path (string path)
+{
+ PathScanner scanner;
+ vector<string *> *plugin_objects;
+ vector<string *>::iterator x;
+ int ret = 0;
+
+ info << "detecting VST plugins along " << path << endmsg;
+
+ plugin_objects = scanner (vst_path, vst_filter, 0, true, true);
+
+ if (plugin_objects) {
+ for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
+ vst_discover (**x);
+ }
+ }
+
+ vector_delete (plugin_objects);
+ return ret;
+}
+
+int
+PluginManager::vst_discover (string path)
+{
+ FSTInfo* finfo;
+ char buf[32];
+
+ if ((finfo = fst_get_info (const_cast<char *> (path.c_str()))) == 0) {
+ warning << "Cannot get VST information from " << path << endmsg;
+ return -1;
+ }
+
+ if (!finfo->canProcessReplacing) {
+ warning << string_compose (_("VST plugin %1 does not support processReplacing, and so cannot be used in ardour at this time"),
+ finfo->name)
+ << endl;
+ }
+
+ PluginInfoPtr info(new VSTPluginInfo);
+
+ /* what a goddam joke freeware VST is */
+
+ if (!strcasecmp ("The Unnamed plugin", finfo->name)) {
+ info->name = PBD::basename_nosuffix (path);
+ } else {
+ info->name = finfo->name;
+ }
+
+
+ snprintf (buf, sizeof (buf), "%d", finfo->UniqueID);
+ info->unique_id = buf;
+ info->category = "VST";
+ info->path = path;
+ // need to set info->creator but FST doesn't provide it
+ info->index = 0;
+ info->n_inputs = finfo->numInputs;
+ info->n_outputs = finfo->numOutputs;
+ info->type = ARDOUR::VST;
+
+ _vst_plugin_info.push_back (info);
+ fst_free_info (finfo);
+
+ return 0;
+}
+
+#endif // VST_SUPPORT
diff --git a/libs/ardour/po/el_GR.po b/libs/ardour/po/el_GR.po
new file mode 100644
index 0000000000..9ddd3e7188
--- /dev/null
+++ b/libs/ardour/po/el_GR.po
@@ -0,0 +1,2195 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Paul Davis
+# This file is distributed under the same license as the PACKAGE package.
+# Klearchos Gourgourinis <muadib@in.gr>, 2004.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libardour 0.664.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2003-06-29 21:03+0200\n"
+"PO-Revision-Date: 2007-04-16 00:38+0200\n"
+"Last-Translator: Klearchos Gourgourinis <muadib@in.gr>\n"
+"Language-Team: Hellenic(Greek)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: libs/ardour/audio_diskstream.cc:337
+msgid "AudioDiskstream: Session doesn't know about a Playlist called \"%1\""
+msgstr "AudioDiskStream: Η ΣυνεδÏία δεν γνωÏίζει για λίστα ΑναπαÏ/γής με όνομα \"%1\""
+
+#: libs/ardour/audio_diskstream.cc:342
+msgid "AudioDiskstream: Playlist \"%1\" isn't an audio playlist"
+msgstr "AudioDiskStream: Η Λίστα \"%1\" δεν είναι ηχητική λίστα αναπαÏ/γής"
+
+#: libs/ardour/audio_diskstream.cc:433
+msgid "AudioDiskstream %1: there is no existing playlist to make a copy of!"
+msgstr "AudioDiskstream %1: δεν υπάÏχει λίστα αναπαÏ/γής για να γίνει αντιγÏαφή!"
+
+#: libs/ardour/audio_diskstream.cc:1114
+#: libs/ardour/audio_diskstream.cc:1125
+msgid "AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"
+msgstr "AudioDiskstream %1: κατα την αναγόμωση, δεν μπόÏεσα να διαβάσω %2 από τη λίστα αναπαÏ/γής στο frame %3"
+
+#: libs/ardour/audio_diskstream.cc:1254
+msgid "AudioDiskstream %1: cannot read %2 from playlist at frame %3"
+msgstr "AudioDiskstream %1: δεν μπόÏεσα να διαβάσω %2 από τη λίστα αναπαÏ/γής στο frame %3"
+
+#: libs/ardour/audio_diskstream.cc:1621
+#: libs/ardour/audio_diskstream.cc:1638
+msgid "AudioDiskstream %1: cannot write to disk"
+msgstr "AudioDiskstream %1: δεν μποÏÏŽ να γÏάψω στο δίσκο"
+
+#: libs/ardour/audio_diskstream.cc:1698
+msgid "AudioDiskstream \"%1\": cannot flush captured data to disk!"
+msgstr "AudioDiskstream \"%1\": αδÏνατη η εκκαθάÏιση δειγματοληπτικών δεδομένων στο δίσκο!"
+
+#: libs/ardour/audio_diskstream.cc:1796
+msgid "%1: could not create region for complete audio file"
+msgstr "%1: δεν μπόÏεσα να δημιουÏγήσω πεÏιοχή για ολόκληÏο audio file"
+
+#: libs/ardour/audio_diskstream.cc:1819
+msgid "AudioDiskstream: could not create region for captured audio!"
+msgstr "AudioDiskstream: δεν μπόÏεσα να δημιουÏγήσω πεÏιοχή για δειγματοληψίες!"
+
+#: libs/ardour/audio_diskstream.cc:1874
+msgid "programmer error: %1"
+msgstr "σφάλμα Ï€ÏογÏαμματιστή: %1"
+
+#: libs/ardour/audio_diskstream.cc:2146
+msgid "AudioDiskstream: channel %1 out of range"
+msgstr "AudioDiskstream: κανάλι %1 εκτός διαστήματος"
+
+#: libs/ardour/audio_diskstream.cc:2171
+msgid "%1:%2 new capture file not initialized correctly"
+msgstr "%1:%2 νέα δειγματοληψία δεν εκκινήθη σωστά"
+
+#: libs/ardour/audio_diskstream.cc:2404
+msgid "Location \"%1\" not valid for track loop (start >= end)"
+msgstr "Η Τοποθεσία \"%1\" δεν είναι ικανή για track loop (αÏχή >= τέλος)"
+
+#: libs/ardour/audio_diskstream.cc:2485
+msgid "%1: cannot restore pending capture source file %2"
+msgstr "%1: δεν μποÏÏŽ να ανοίξω το αÏχείο %2 από την απαιτοÏμενη πηγή"
+
+#: libs/ardour/audio_diskstream.cc:2507
+msgid "%1: incorrect number of pending sources listed - ignoring them all"
+msgstr "%1: ετυπώθη λανθασμένος αÏιθμός απαιτοÏμενων πηγών - αγνοήθηκαν όλες"
+
+#: libs/ardour/audio_diskstream.cc:2523
+msgid "%1: cannot create whole-file region from pending capture sources"
+msgstr "%1: αδÏνατη η δημιουÏγία ακέÏαιας πεÏιοχής από τις απαιτοÏμενες πηγές ηχοληψίας"
+
+#: libs/ardour/audio_diskstream.cc:2535
+msgid "%1: cannot create region from pending capture sources"
+msgstr "%1: δεν μπόÏεσα να δημιουÏγήσω πεÏιοχή για τις απαιτοÏμενες πηγές"
+
+#: libs/ardour/audio_library.cc:92
+msgid "channels"
+msgstr "κανάλια"
+
+#: libs/ardour/audio_library.cc:93
+msgid "samplerate"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:94
+msgid "resolution"
+msgstr "ανάλυση"
+
+#: libs/ardour/audio_library.cc:95
+msgid "format"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:102
+msgid "Could not open %1. Audio Library not saved"
+msgstr "Δεν μπόÏεσα να ανοίξω το %1. Η Audio Library δεν αποθηκεÏθηκε"
+
+#: libs/ardour/audio_playlist.cc:53
+#: libs/ardour/audio_playlist.cc:63
+#: libs/ardour/audio_playlist.cc:74
+#: libs/ardour/audio_playlist.cc:121
+#: libs/ardour/insert.cc:76
+#: libs/ardour/insert.cc:95
+#: libs/ardour/insert.cc:120
+#: libs/ardour/insert.cc:838
+#: libs/ardour/insert.cc:846
+#: libs/ardour/send.cc:39
+#: libs/ardour/send.cc:53
+#: libs/ardour/send.cc:62
+#: libs/ardour/session_state.cc:1621
+#: libs/ardour/session_state.cc:1667
+msgid "initial state"
+msgstr "Ï€ÏωταÏχική κατάσταση"
+
+#: libs/ardour/audio_playlist.cc:275
+#: libs/ardour/audio_playlist.cc:769
+msgid "programming error: non-audio Region passed to remove_overlap in audio playlist"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: μη-ηχητική ΠεÏιοχή πέÏασε σε remove_overlap στην audio playlist"
+
+#: libs/ardour/audio_playlist.cc:402
+msgid "programming error: non-audio Region tested for overlap in audio playlist"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: μη-ηχητική ΠεÏιοχή ελέγχθη για υπεÏπήδηση(overlap) στην λίστα αναπαÏ/γής του ήχου"
+
+#: libs/ardour/audio_playlist.cc:878
+msgid "xfade change"
+msgstr "αλλαγή xfade"
+
+#: libs/ardour/audio_playlist.cc:933
+msgid "region modified"
+msgstr "η πεÏιοχή μετεβλήθη"
+
+#: libs/ardour/audio_track.cc:125
+#: libs/ardour/io.cc:1716
+#: libs/ardour/io.cc:1826
+msgid "Unknown connection \"%1\" listed for input of %2"
+msgstr "Άγνωστη σÏνδεση \"%1\" στη λίστα εισόδου του %2"
+
+#: libs/ardour/audio_track.cc:127
+#: libs/ardour/io.cc:1718
+#: libs/ardour/io.cc:1828
+msgid "in 1"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:128
+#: libs/ardour/io.cc:1719
+#: libs/ardour/io.cc:1829
+msgid "No input connections available as a replacement"
+msgstr "Καμία διαθέσιμη input σÏνδεση ως εναλλακτική"
+
+#: libs/ardour/audio_track.cc:132
+#: libs/ardour/io.cc:1723
+#: libs/ardour/io.cc:1833
+msgid "Connection %1 was not available - \"in 1\" used instead"
+msgstr "Η ΣÏνδεση %1 δεν ήταν διαθέσιμη - Αντ'αυτής χÏησιμοποιήθηκε η \"in 1\" "
+
+#: libs/ardour/audio_track.cc:141
+#: libs/ardour/io.cc:1842
+msgid "improper input channel list in XML node (%1)"
+msgstr "ακατάλληλη λίστα καναλιών εισόδου στον κόμβο XML (%1)"
+
+#: libs/ardour/audio_track.cc:186
+#: libs/ardour/audio_track.cc:199
+msgid "AudioTrack: diskstream \"%1\" not known by session"
+msgstr "AudioTrack: το diskstream \"%1\" είναι μή αναγνωÏίσιμο από τη συνεδÏία"
+
+#: libs/ardour/audio_track.cc:297
+msgid "MIDI rec_enable control specification for %1 is incomplete, so it has been ignored"
+msgstr "Η Ï€ÏοδιαγÏαφή ελέγχου του MIDI rec_enable για το %1 είναι ημιτελής, με αποτέλεσμα να αγνοηθεί"
+
+#: libs/ardour/audio_track.cc:309
+msgid "programming error: AudioTrack given state without diskstream!"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: εδόθη κατάσταση στην AudioTrack δίχως diskstream!"
+
+#: libs/ardour/audioengine.cc:144
+msgid "cannot activate JACK client"
+msgstr "Ο JACK δεν μποÏεί να ενεÏγοποιηθεί"
+
+#: libs/ardour/audioengine.cc:395
+msgid "register audio input port called before engine was started"
+msgstr "η register audio input port εκλήθη Ï€Ïιν να εκκινηθεί η engine"
+
+#: libs/ardour/audioengine.cc:426
+msgid "register audio output port called before engine was started"
+msgstr "η register audio output port εκλήθη Ï€Ïίν να εκκινηθεί η engine"
+
+#: libs/ardour/audioengine.cc:487
+msgid "connect called before engine was started"
+msgstr "η σÏνδεση εκλήθη Ï€Ïιν να εκκινηθεί η engine"
+
+#: libs/ardour/audioengine.cc:503
+msgid "AudioEngine: cannot connect %1 (%2) to %3 (%4)"
+msgstr "AudioEngine: αδÏνατη η σÏνδεση %1 (%2) σε %3 (%4)"
+
+#: libs/ardour/audioengine.cc:516
+#: libs/ardour/audioengine.cc:545
+msgid "disconnect called before engine was started"
+msgstr "η αποσÏνδεση εκλήθη Ï€Ïιν να εκκινηθεί η engine"
+
+#: libs/ardour/audioengine.cc:603
+msgid "get_port_by_name() called before engine was started"
+msgstr "η Ïουτίνα get_port_by_name() εκλήθη Ï€Ïιν να εκκινηθεί η engine"
+
+#: libs/ardour/audioengine.cc:636
+msgid "get_ports called before engine was started"
+msgstr "η Ïουτίνα get_ports εκλήθη Ï€Ïιν να εκκινηθεί η engine"
+
+#: libs/ardour/audioengine.cc:711
+msgid "get_nth_physical called before engine was started"
+msgstr "η Ïουτίνα get_nth_physical εκλήθη Ï€Ïιν να εκκινηθεί η engine"
+
+#: libs/ardour/audioengine.cc:739
+msgid "get_port_total_latency() called with no JACK client connection"
+msgstr "η Ïουτίνα get_port_total_latency() εκλήθη χωÏίς την εκκίνηση κάποιου JACK client"
+
+#: libs/ardour/audioengine.cc:745
+msgid "get_port_total_latency() called before engine was started"
+msgstr "η Ïουτίνα get_port_total_latency() εκλήθη Ï€Ïιν να εκκινηθεί η engine"
+
+#: libs/ardour/audioengine.cc:869
+msgid "Unable to connect to JACK server"
+msgstr "ΑδÏνατη η σÏνδεση στον JACK server"
+
+#: libs/ardour/audioengine.cc:872
+msgid "Could not connect to JACK server as \"%1\""
+msgstr "ΑδÏνατη η σÏνδεση στον JACK server ως \"%1\""
+
+#: libs/ardour/audioengine.cc:877
+msgid "JACK server started"
+msgstr "ΈναÏξη JACK server"
+
+#: libs/ardour/audioengine.cc:911
+msgid "cannot shutdown connection to JACK"
+msgstr "ΑδÏνατος ο τεÏματισμός συνδέσεως με τον JACK"
+
+#: libs/ardour/audioengine.cc:936
+msgid "failed to connect to JACK"
+msgstr "Αποτυχία συνδέσεως με τον JACK"
+
+#: libs/ardour/audioengine.cc:952
+msgid "could not reregister %1"
+msgstr "αδÏνατη η επανακαταγÏαφή %1"
+
+#: libs/ardour/audioengine.cc:1009
+msgid "could not reconnect %1 and %2 (err = %3)"
+msgstr "ΑδÏνατη η επανασÏνδεση %1 και %2 (err = %3)"
+
+#: libs/ardour/audiofilesource.cc:444
+#: libs/ardour/session_state.cc:3095
+msgid "there are already 1000 files with names like %1; versioning discontinued"
+msgstr "ΥπάÏχουν ήδη 1000 αÏχεία με ονόματα όπως %1; μη-συνεχές versioning"
+
+#: libs/ardour/audiofilesource.cc:458
+#: libs/ardour/session_state.cc:3109
+msgid "cannot rename audio file source from %1 to %2 (%3)"
+msgstr "δεν μποÏÏŽ να μετονομάσω την πηγή του audio file από %1 σε %2 (%3)"
+
+#: libs/ardour/audiofilesource.cc:465
+#: libs/ardour/session_state.cc:3124
+msgid "cannot remove peakfile %1 for %2 (%3)"
+msgstr "δεν μποÏÏŽ να απαλοίψω το peakfile %1 για %2 (%3)"
+
+#: libs/ardour/audiofilesource.cc:509
+msgid "FileSource: search path not set"
+msgstr "FileSource: μονοπάτι αναζητήσεως δεν ετέθη"
+
+#: libs/ardour/audiofilesource.cc:533
+msgid ""
+"FileSource: \"%1\" is ambigous when searching %2\n"
+"\t"
+msgstr ""
+"FileSource: \"%1\" είναι αμφίβολο κατά την αναζήτηση του %2\n"
+"\t"
+
+#: libs/ardour/audiofilesource.cc:539
+msgid "Filesource: cannot find required file (%1): while searching %2"
+msgstr "Filesource: δεν ευÏέθη το απαιτοÏμενο αÏχείο (%1): κατά την αναζήτηση του %2"
+
+#: libs/ardour/audiofilesource.cc:562
+msgid "Filesource: cannot find required file (%1): %2"
+msgstr "Filesource: δεν ευÏέθη το απαιτοÏμενο αÏχείο (%1): %2"
+
+#: libs/ardour/audiofilesource.cc:567
+msgid "Filesource: cannot check for existing file (%1): %2"
+msgstr "Filesource: δεν μποÏÏŽ να ελέγξω για το υπάÏχον αÏχείο (%1): %2"
+
+#: libs/ardour/audiofilesource.cc:636
+#: libs/ardour/insert.cc:525
+#: libs/ardour/sndfilesource.cc:113
+msgid "programming error: %1"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: %1"
+
+#: libs/ardour/audiofilesource.cc:641
+msgid "cannot rename audio file for %1 to %2"
+msgstr "δεν μποÏÏŽ να μετονομάσω το audio file για το %1 σε %2"
+
+#: libs/ardour/audiofilter.cc:45
+msgid "audiofilter: error creating name for new audio file based on %1"
+msgstr "audiofilter: σφάλμα στη δημιουÏγία ονόματος για νέο audio file βασισμένο σε %1"
+
+#: libs/ardour/audiofilter.cc:58
+msgid "audiofilter: error creating new audio file %1 (%2)"
+msgstr "audiofilter: σφάλμα στη δημιουÏγία νέου audio file %1 (%2)"
+
+#: libs/ardour/audioregion.cc:857
+#: libs/ardour/audioregion.cc:919
+msgid "fade in change"
+msgstr "αλλαγή fade in"
+
+#: libs/ardour/audioregion.cc:1349
+#, c-format
+msgid "normalized to %.2fdB"
+msgstr "εξομαλÏνθηκε στα %.2fdB"
+
+#: libs/ardour/audioregion.cc:1367
+msgid "envelope change"
+msgstr "αλλαγή envelope"
+
+#: libs/ardour/audiosource.cc:143
+msgid "poll on peak request pipe failed (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:150
+msgid "Error on peak thread request pipe"
+msgstr "Σφάλμα στο peak thread request pipe"
+
+#: libs/ardour/audiosource.cc:183
+msgid "Error reading from peak request pipe"
+msgstr "Σφάλμα στην ανάγνωση από peak request pipe"
+
+#: libs/ardour/audiosource.cc:215
+#: libs/ardour/session_butler.cc:80
+#: libs/ardour/session_midi.cc:1183
+msgid "Cannot create transport request signal pipe (%1)"
+msgstr "Δεν μποÏÏŽ να δημιουÏγήσω transport request signal pipe (%1)"
+
+#: libs/ardour/audiosource.cc:220
+#: libs/ardour/audiosource.cc:225
+msgid "UI: cannot set O_NONBLOCK on peak request pipe (%1)"
+msgstr "UI: δεν μποÏÏŽ να θέσω O_NONBLOCK στο peak read pipe (%1)"
+
+#: libs/ardour/audiosource.cc:230
+msgid "AudioSource: could not create peak thread"
+msgstr "AudioSource: δεν μπόÏεσα να δημιουÏγήσω peak thread"
+
+#: libs/ardour/audiosource.cc:308
+msgid "cannot rename peakfile for %1 from %2 to %3 (%4)"
+msgstr "αδÏνατη η μετονομασία του peakfile για %1 από %2 σε %3 (%4)"
+
+#: libs/ardour/audiosource.cc:350
+msgid "AudioSource: cannot stat peakfile \"%1\""
+msgstr ""
+
+#: libs/ardour/audiosource.cc:451
+msgid "cannot read sample data for unscaled peak computation"
+msgstr "δεν μποÏÏŽ να διαβάσω δεδομένα δείγματος για υπολογισμό μη-κλιμακώτου peak"
+
+#: libs/ardour/audiosource.cc:472
+#: libs/ardour/audiosource.cc:543
+#: libs/ardour/audiosource.cc:787
+#: libs/ardour/audiosource.cc:878
+msgid "AudioSource: cannot open peakpath \"%1\" (%2)"
+msgstr "AudioSource: δεν μποÏÏŽ να ανοίξω το peakpath \"%1\" (%2)"
+
+#: libs/ardour/audiosource.cc:644
+msgid "AudioSource[%1]: peak read - cannot read %2 samples at offset %3"
+msgstr "AudioSource[%1]: ανάγνωση peak - δεν μποÏÏŽ να διαβάσω %2 δείγματα στο (offset) %3"
+
+#: libs/ardour/audiosource.cc:798
+msgid "%1: could not write read raw data for peak computation (%2)"
+msgstr "%1: δεν μποÏεσα να γÏάψω ανεγνωσμένα raw δεδομένα για τον υπολογισμό του peak (%2)"
+
+#: libs/ardour/audiosource.cc:823
+msgid "%1: could not write peak file data (%2)"
+msgstr "%1: δεν μπόÏεσα να γÏάψω δεδομένα του αÏχείου peak (%2)"
+
+#: libs/ardour/automation_event.cc:65
+#: libs/ardour/location.cc:345
+#: libs/ardour/tempo.cc:226
+msgid "initial"
+msgstr "Ï€ÏωταÏχικό"
+
+#: libs/ardour/automation_event.cc:232
+msgid "cleared"
+msgstr "εκκαθαÏίσθη"
+
+#: libs/ardour/automation_event.cc:404
+msgid "added event"
+msgstr "συμβάν Ï€Ïοστέθηκε"
+
+#: libs/ardour/automation_event.cc:421
+msgid "removed event"
+msgstr "συμβάν απαλοίφθηκε"
+
+#: libs/ardour/automation_event.cc:436
+msgid "removed multiple events"
+msgstr "πολλαπλά συμβάντα απαλοίφθηκαν"
+
+#: libs/ardour/automation_event.cc:467
+#: libs/ardour/automation_event.cc:498
+msgid "removed range"
+msgstr "διάστημα απαλοίφθηκε"
+
+#: libs/ardour/automation_event.cc:528
+msgid "event range adjusted"
+msgstr "διάστημα συμβάντων Ïυθμίστηκε"
+
+#: libs/ardour/automation_event.cc:550
+msgid "event adjusted"
+msgstr "συμβάν Ïυθμίστηκε"
+
+#: libs/ardour/automation_event.cc:665
+#: libs/ardour/automation_event.cc:770
+#: libs/ardour/panner.cc:1041
+msgid "programming error:"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ:"
+
+#: libs/ardour/automation_event.cc:1079
+msgid "cut/copy/clear"
+msgstr "κοπή/αντιγÏαφή/εκκαθάÏιση"
+
+#: libs/ardour/automation_event.cc:1112
+msgid "copy"
+msgstr "αντιγÏαφή"
+
+#: libs/ardour/automation_event.cc:1180
+#: libs/ardour/playlist.cc:939
+msgid "paste"
+msgstr "επικόλληση"
+
+#: libs/ardour/automation_event.cc:1235
+msgid "automation list: no x-coordinate stored for control point (point ignored)"
+msgstr "λίστα αυτοματισμοÏ: καμία x-συντεταγμένη αποθηκευμένη για σημείο ελέγχου (το σημείο αγνοήθηκε)"
+
+#: libs/ardour/automation_event.cc:1241
+msgid "automation list: no y-coordinate stored for control point (point ignored)"
+msgstr "λίστα αυτοματισμοÏ: καμία y-συντεταγμένη αποθηκευμένη για σημείο ελέγχου (το σημείο αγνοήθηκε)"
+
+#: libs/ardour/configuration.cc:80
+msgid "loading system configuration file %1"
+msgstr "Ανάκληση αÏχείου Ïυθμίσεων συστήματος %1"
+
+#: libs/ardour/configuration.cc:83
+msgid "Ardour: cannot read system configuration file \"%1\""
+msgstr "Ardour: δεν μποÏÏŽ να διαβάσω το αÏχείο διαÏÏυθμίσεως του συστήματος \"%1\""
+
+#: libs/ardour/configuration.cc:88
+msgid "Ardour: system configuration file \"%1\" not loaded successfully."
+msgstr "Ardour: το αÏχείο διαÏÏυθμίσεως του συστήματος \"%1\" δεν φοÏτώθηκε επιτυχώς."
+
+#: libs/ardour/configuration.cc:105
+msgid "loading user configuration file %1"
+msgstr "Ανάκληση αÏχείου Ïυθμίσεων χÏήστη %1"
+
+#: libs/ardour/configuration.cc:108
+msgid "Ardour: cannot read configuration file \"%1\""
+msgstr "Ardour: αÏχείο διαÏÏυθμίσεως μή αναγνώσιμο \"%1\""
+
+#: libs/ardour/configuration.cc:113
+msgid "Ardour: user configuration file \"%1\" not loaded successfully."
+msgstr "Ardour: αÏχείο Ïυθμίσεων χÏήστη \"%1\" δεν φοÏτώθηκε επιτυχώς."
+
+#: libs/ardour/configuration.cc:137
+msgid "Config file %1 not saved"
+msgstr "ΑÏχείο Ïυθμίσεων %1 δεν αποθηκεÏθηκε"
+
+#: libs/ardour/configuration.cc:210
+msgid "ill-formed MIDI port specification in ardour rcfile (ignored)"
+msgstr "κακοσχηματισμένος καθοÏισμός MIDI θÏÏας στο ardour rcfile (αγνοήθηκε)"
+
+#: libs/ardour/connection.cc:183
+msgid "Node for Connection has no \"name\" property"
+msgstr "Κόμβος Ï€Ïος ΣÏνδεση δεν έχει \"όνομα\" ιδιότητα"
+
+#: libs/ardour/connection.cc:191
+msgid "Node for Connection has no \"connections\" property"
+msgstr "Κόμβος Ï€Ïος ΣÏνδεση δεν έχει \"συνδέσεις\" ιδιότητα"
+
+#: libs/ardour/connection.cc:227
+#: libs/ardour/io.cc:1902
+msgid "IO: badly formed string in XML node for inputs \"%1\""
+msgstr "IO: κακοφτιαγμένη γÏαμμή στον XML κόμβο για εισόδους \"%1\""
+
+#: libs/ardour/connection.cc:232
+#: libs/ardour/io.cc:1907
+msgid "bad input string in XML node \"%1\""
+msgstr "κακή γÏαμμή εισόδου στον XML κόμβο \"%1\""
+
+#: libs/ardour/control_protocol_manager.cc:80
+msgid "control protocol name \"%1\" has no descriptor"
+msgstr "Το όνομα Ï€Ïωτοκόλλου ελέγχου \"%1\" δεν έχει descriptor"
+
+#: libs/ardour/control_protocol_manager.cc:85
+msgid "control protocol name \"%1\" could not be initialized"
+msgstr "Το όνομα Ï€Ïωτοκόλλου ελέγχου \"%1\" ήταν αδÏνατο να αÏχίσει"
+
+#: libs/ardour/control_protocol_manager.cc:141
+msgid "Instantiating mandatory control protocol %1"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:175
+msgid "Control protocol %1 not usable"
+msgstr "ΠÏωτόκολλο ελέγχου %1 μη χÏησιμοποιήσιμο"
+
+#: libs/ardour/control_protocol_manager.cc:187
+msgid "Control surface protocol discovered: \"%1\""
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:205
+msgid "ControlProtocolManager: cannot load module \"%1\" (%2)"
+msgstr "ControlProtocolManager: δεν μποÏÏŽ να φοÏτώσω το module \"%1\" (%2)"
+
+#: libs/ardour/control_protocol_manager.cc:213
+msgid "ControlProtocolManager: module \"%1\" has no descriptor function."
+msgstr "ControlProtocolManager: το module \"%1\" δεν έχει descriptor function."
+
+#: libs/ardour/crossfade.cc:121
+msgid "Crossfade: no \"in\" region in state"
+msgstr "Crossfade: χωÏίς \"in\" πεÏιοχή σε κατάσταση"
+
+#: libs/ardour/crossfade.cc:128
+msgid "Crossfade: no \"in\" region %1 found in playlist %2"
+msgstr "Crossfade: no \"in\" πεÏιοχή %1 δεν ευÏέθη στη λίστα αναπαÏ/γής %2"
+
+#: libs/ardour/crossfade.cc:138
+msgid "Crossfade: no \"out\" region in state"
+msgstr "Crossfade: χωÏίς \"out\" πεÏιοχή σε κατάσταση"
+
+#: libs/ardour/crossfade.cc:145
+msgid "Crossfade: no \"out\" region %1 found in playlist %2"
+msgstr "Crossfade: no \"out\" πεÏιοχή %1 δεν ευÏέθη στη λίστα αναπαÏ/γής %2"
+
+#: libs/ardour/crossfade.cc:492
+msgid "active changed"
+msgstr "αλλαγή ενεÏγοÏ"
+
+#: libs/ardour/crossfade.cc:741
+msgid "old-style crossfade information - no position information"
+msgstr "παλαιοÏ-Ï„Ïπου crossfade πληÏοφοÏία - καμία πληÏοφοÏία θέσεως"
+
+#: libs/ardour/curve.cc:112
+#: libs/ardour/globals.cc:340
+#: libs/ardour/insert.cc:454
+#: libs/ardour/session.cc:2466
+#: libs/ardour/session.cc:2518
+msgid "programming error: "
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: "
+
+#: libs/ardour/cycle_timer.cc:37
+msgid "CycleTimer::get_mhz(): can't open /proc/cpuinfo"
+msgstr "CycleTimer::get_mhz(): δεν ανοίγει το /proc/cpuinfo"
+
+#: libs/ardour/cycle_timer.cc:49
+msgid "CycleTimer::get_mhz(): cannot locate cpu MHz in /proc/cpuinfo"
+msgstr "CycleTimer::get_mhz(): δεν ευÏέθη το cpu MHz στο /proc/cpuinfo"
+
+#: libs/ardour/cycle_timer.cc:72
+msgid "cannot locate cpu MHz in /proc/cpuinfo"
+msgstr "δεν ευÏέθη το cpu MHz στο /proc/cpuinfo"
+
+#: libs/ardour/destructive_filesource.cc:188
+msgid "DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"
+msgstr "ΚαταστÏεπτικήΠηγήΑÏχείου: \"%1\" κακή ανάγνωση retval: %2 of %5 (%3: %4)"
+
+#: libs/ardour/destructive_filesource.cc:201
+#: libs/ardour/destructive_filesource.cc:243
+#: libs/ardour/destructive_filesource.cc:250
+msgid "DestructiveFileSource: \"%1\" bad write (%2)"
+msgstr "DestructiveFileSource: \"%1\" κακή εγγÏαφή (%2)"
+
+#: libs/ardour/globals.cc:109
+msgid "no MIDI ports specified: no MMC or MTC control possible"
+msgstr "καμία MIDI θÏÏα δεν επελέχθη: έλεγχος MMC ή MTC αδÏνατος"
+
+#: libs/ardour/globals.cc:124
+msgid "MIDI port specifications for \"%1\" are not understandable."
+msgstr "Οι Ï€ÏοδιαγÏαφές της θÏÏας MIDI για το \"%1\" δεν είναι κατανοητές."
+
+#: libs/ardour/globals.cc:137
+#: libs/ardour/globals.cc:141
+#: libs/ardour/globals.cc:145
+msgid "default"
+msgstr "Ï€ÏοκαθοÏισμένο"
+
+#: libs/ardour/globals.cc:173
+msgid "No MMC control (MIDI port \"%1\" not available)"
+msgstr "ΧωÏίς έλεγχο MMC (θÏÏα MIDI \"%1\" μη διαθέσιμη)"
+
+#: libs/ardour/globals.cc:179
+msgid "No MTC support (MIDI port \"%1\" not available)"
+msgstr "ΧωÏίς έλεγχο MTC (θÏÏα MIDI \"%1\" μη διαθέσιμη)"
+
+#: libs/ardour/globals.cc:184
+msgid "No MIDI parameter support (MIDI port \"%1\" not available)"
+msgstr "ΧωÏίς υποστήÏιξη MIDI παÏαμέτÏων (θÏÏα MIDI \"%1\" μη διαθέσιμη)"
+
+#: libs/ardour/import.cc:75
+msgid "Import: cannot open input sound file \"%1\""
+msgstr "Εισαγωγή: δεν μποÏÏŽ να ανοίξω το εισαγμένο αÏχείο ήχου \"%1\""
+
+#: libs/ardour/import.cc:80
+msgid "resampling audio"
+msgstr "επανά-ληψη(resampling) ήχου"
+
+#: libs/ardour/import.cc:84
+msgid "Import: cannot open converted sound file \"%1\""
+msgstr "Εισαγωγή: δεν μποÏÏŽ να ανοίξω το Ï„Ïοποποιημένο αÏχείο ήχου \"%1\""
+
+#: libs/ardour/import.cc:89
+msgid "Import: error while resampling sound file \"%1\""
+msgstr "Εισαγωγή: σφάλμα κατά την Ï„Ïοποποίηση αÏχείου \"%1\""
+
+#: libs/ardour/import.cc:148
+msgid "Session::import_audiofile: cannot open new file source for channel %1"
+msgstr "ΣυνεδÏία::εισαγωγή_αÏχείου: δεν μποÏÏŽ να ανοίξω νέα πηγή αÏχείου για το κανάλι %1"
+
+#: libs/ardour/import.cc:167
+msgid "converting audio"
+msgstr "μετατÏοπή ήχου"
+
+#: libs/ardour/import.cc:199
+msgid "building region"
+msgstr "χτίσιμο πεÏιοχής"
+
+#: libs/ardour/import.cc:201
+msgid "building regions"
+msgstr "χτίσιμο πεÏιοχών"
+
+#: libs/ardour/import.cc:325
+msgid "Import: could not open temp file: %1"
+msgstr "Εισαγωγή: δεν μπόÏεσα να ανοίξω το temp αÏχείο: %1"
+
+#: libs/ardour/import.cc:334
+msgid "Import: src_new() failed : %1"
+msgstr "Εισαγωγή: src_new() απέτυχε : %1"
+
+#: libs/ardour/import.cc:362
+msgid "Import: %1"
+msgstr "Εισαγωγή: %1"
+
+#: libs/ardour/insert.cc:644
+#: libs/ardour/insert.cc:936
+msgid "XML node describing insert is missing the `type' field"
+msgstr "Στον κόμβο XML που πεÏιγÏάφει το insert λείπει το πεδίο `type'"
+
+#: libs/ardour/insert.cc:653
+msgid "unknown plugin type %1 in plugin insert state"
+msgstr "άγνωστος Ï„Ïπος plugin %1 στην κατάσταση εισαχθέντων plugins"
+
+#: libs/ardour/insert.cc:665
+msgid "XML node describing insert is missing the `id' field"
+msgstr "Στον κόμβο XML που πεÏιγÏάφει το insert λείπει το πεδίο `id'"
+
+#: libs/ardour/insert.cc:678
+msgid ""
+"Found a reference to a plugin (\"%1\") that is unknown.\n"
+"Perhaps it was removed or moved since it was last used."
+msgstr ""
+"ΕυÏέθη μια αναφοÏά σε plugin (\"%1\") που είναι άγνωστο.\n"
+"Ίσως έχει διαγÏαφεί ή μετακινηθεί από την τελευταία του χÏήση."
+
+#: libs/ardour/insert.cc:716
+#: libs/ardour/insert.cc:953
+msgid "XML node describing insert is missing a Redirect node"
+msgstr "Στον κόμβο XML που πεÏιγÏάφει το insert λείπει ένας κόμβος Redirect"
+
+#: libs/ardour/insert.cc:721
+msgid "XML node describing a plugin insert is missing the `%1' information"
+msgstr "Στον κόμβο XML που πεÏιγÏάφει ένα plugin insert λείπουν οι πληÏοφοÏίες `%1' "
+
+#: libs/ardour/insert.cc:745
+msgid "PluginInsert: Auto: no ladspa port number"
+msgstr "PluginInsert: Auto: χωÏίς αÏιθμό θÏÏας ladspa"
+
+#: libs/ardour/insert.cc:752
+msgid "PluginInsert: Auto: port id out of range"
+msgstr "PluginInsert: Auto: το id θÏÏας είναι εκτός πεδίου"
+
+#: libs/ardour/insert.cc:768
+msgid "XML node describing a port automation is missing the `%1' information"
+msgstr "Στον κόμβο XML που πεÏιγÏάφει έναν αυτοματισμό θÏÏας λείπουν οι πληÏοφοÏίες `%1' "
+
+#: libs/ardour/insert.cc:854
+msgid "PortInsert: cannot add input port"
+msgstr "PortInsert: δεν μποÏεί να Ï€Ïοστεθει θÏÏα εισόδου"
+
+#: libs/ardour/insert.cc:859
+msgid "PortInsert: cannot add output port"
+msgstr "PortInsert: δεν μποÏεί να Ï€Ïοστεθεί θÏÏα εξόδου"
+
+#: libs/ardour/insert.cc:941
+msgid "non-port insert XML used for port plugin insert"
+msgstr "εισαγωγή μη-θÏÏας XML για χÏήση σε εισαγωγή plugin θÏÏας"
+
+#: libs/ardour/io.cc:598
+msgid "IO: cannot disconnect input port %1 from %2"
+msgstr "IO: δεν μποÏει να αποσυνδεθεί η θÏÏα εισόδου %1 από %2"
+
+#: libs/ardour/io.cc:666
+msgid "IO: cannot disconnect output port %1 from %2"
+msgstr "IO: δεν μποÏεί να αποσυνδεθεί η θÏÏα εξόδου %1 από %2"
+
+#: libs/ardour/io.cc:807
+#: libs/ardour/io.cc:1151
+#: libs/ardour/io.cc:1277
+#, c-format
+msgid "%s/out"
+msgstr "%s/out"
+
+#: libs/ardour/io.cc:809
+#: libs/ardour/io.cc:1153
+#: libs/ardour/io.cc:1279
+#: libs/ardour/io.cc:2849
+#, c-format
+msgid "%s/out %u"
+msgstr "%s/out %u"
+
+#: libs/ardour/io.cc:813
+#: libs/ardour/io.cc:1158
+#: libs/ardour/io.cc:1283
+msgid "IO: cannot register output port %1"
+msgstr "IO: δεν μποÏει να καταχώÏηθεί η θÏÏα εξόδου %1"
+
+#: libs/ardour/io.cc:908
+#: libs/ardour/io.cc:1011
+#: libs/ardour/io.cc:1117
+#, c-format
+msgid "%s/in"
+msgstr "%s/in"
+
+#: libs/ardour/io.cc:910
+#: libs/ardour/io.cc:1014
+#: libs/ardour/io.cc:1120
+#: libs/ardour/io.cc:2819
+#, c-format
+msgid "%s/in %u"
+msgstr "%s/in %u"
+
+#: libs/ardour/io.cc:914
+#: libs/ardour/io.cc:1020
+#: libs/ardour/io.cc:1125
+msgid "IO: cannot register input port %1"
+msgstr "IO: δεν μποÏεί να καταχώÏηθεί η θÏÏα εισόδου %1"
+
+#: libs/ardour/io.cc:1541
+msgid "IO::connecting_became_legal() called without a pending state node"
+msgstr "IO::connecting_became_legal() εκλήθη χωÏίς κόμβο καταστάσεως εν αναμονή"
+
+#: libs/ardour/io.cc:1564
+msgid "IO::ports_became_legal() called without a pending state node"
+msgstr "IO::ports_became_legal() εκλήθη χωÏίς κόμβο καταστάσεως εν αναμονή"
+
+#: libs/ardour/io.cc:1594
+msgid "incorrect XML node \"%1\" passed to IO object"
+msgstr "μη-σωστός κόμβος XML \"%1\" πέÏασε στο IO αντικείμενο"
+
+#: libs/ardour/io.cc:1649
+msgid "MIDI gain control specification for %1 is incomplete, so it has been ignored"
+msgstr "Η Ï€ÏοδιαγÏαφή ελέγχου του MIDI gain για το %1 είναι ημιτελής, με αποτέλεσμα να αγνοηθεί"
+
+#: libs/ardour/io.cc:1739
+#: libs/ardour/io.cc:1851
+msgid "Unknown connection \"%1\" listed for output of %2"
+msgstr "Άγνωστη σÏνδεση \"%1\" καταχωÏήθη για την έξοδο του %2"
+
+#: libs/ardour/io.cc:1741
+#: libs/ardour/io.cc:1853
+msgid "out 1"
+msgstr ""
+
+#: libs/ardour/io.cc:1742
+#: libs/ardour/io.cc:1854
+msgid "No output connections available as a replacement"
+msgstr "Καμία σÏνδεση output διαθέσιμη σαν εναλλακτική"
+
+#: libs/ardour/io.cc:1746
+#: libs/ardour/io.cc:1858
+msgid "Connection %1 was not available - \"out 1\" used instead"
+msgstr "Η σÏνδεση %1 δεν ήταν διαθέσιμη - αντ'αυτής χÏησιμοποιήθηκε η \"out 1\" "
+
+#: libs/ardour/io.cc:1760
+msgid "%1: cannot create I/O ports"
+msgstr "%1: θÏÏες I/O δεν μποÏοÏν να δημιουÏγηθοÏν"
+
+#: libs/ardour/io.cc:1867
+msgid "improper output channel list in XML node (%1)"
+msgstr "ασαφής λίστα καναλιών εξόδου στον κόμβο XML (%1)"
+
+#: libs/ardour/io.cc:1952
+msgid "IO: badly formed string in XML node for outputs \"%1\""
+msgstr "IO: δÏσμοÏφη γÏαμμή στον κόμβο XML για τις εξόδους \"%1\""
+
+#: libs/ardour/io.cc:1957
+msgid "IO: bad output string in XML node \"%1\""
+msgstr "IO: κακή γÏαμμή εξόδουστον κόμβο XML \"%1\""
+
+#: libs/ardour/io.cc:2525
+msgid "%1: could not open automation event file \"%2\""
+msgstr "%1: δεν μπόÏεσα να ανοίξω το αÏχείο συμβάντων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï \"%2\""
+
+#: libs/ardour/io.cc:2564
+msgid "%1: cannot open automation event file \"%2\""
+msgstr "%1: δεν μποÏÏŽ να ανοίξω το αÏχείο συμβάντων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï \"%2\""
+
+#: libs/ardour/io.cc:2579
+msgid "badly formed version number in automation event file \"%1\""
+msgstr "δÏσμοÏφος αÏιθμός εκδόσεως στο αÏχείο συμβάντων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï \"%1\""
+
+#: libs/ardour/io.cc:2583
+msgid "no version information in automation event file \"%1\""
+msgstr "δεν υπάÏχουν πληÏοφοÏίες πεÏί εκδόσεων στο αÏχείο συμβάντων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï \"%1\""
+
+#: libs/ardour/io.cc:2588
+msgid "mismatched automation event file version (%1)"
+msgstr "αταίÏιαστη έκδοση αÏχείου συμβάντων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï (%1)"
+
+#: libs/ardour/io.cc:2596
+msgid "badly formatted automation event record at line %1 of %2 (ignored)"
+msgstr "κακώς φοÏμαÏισμένη καταγÏαφή συμβάντων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï ÏƒÏ„Î· γÏαμμή %1 of %2 (αγνοήθηκε)"
+
+#: libs/ardour/io.cc:2616
+msgid "dubious automation event found (and ignored)"
+msgstr "αβέβαιο συμβάν Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï ÎµÏ…Ïέθηκε (και αγνοήθηκε)"
+
+#: libs/ardour/io.cc:2620
+#: libs/ardour/panner.cc:438
+#: libs/ardour/redirect.cc:148
+msgid "loaded from disk"
+msgstr "φοÏτώθηκε από το δίσκο"
+
+#: libs/ardour/io.cc:2791
+msgid "automation write/touch"
+msgstr "αυτοματισμός εγγÏαφή/αφή(write/touch)"
+
+#: libs/ardour/ladspa_plugin.cc:87
+msgid "LADSPA: module has no descriptor function."
+msgstr "LADSPA: το module δεν έχει ενδεικτική λειτουÏγία."
+
+#: libs/ardour/ladspa_plugin.cc:92
+msgid "LADSPA: plugin has gone away since discovery!"
+msgstr "LADSPA: το plugin την 'κοπάνισε' μετά την ανακάλυψη του!"
+
+#: libs/ardour/ladspa_plugin.cc:99
+msgid "LADSPA: \"%1\" cannot be used, since it cannot do inplace processing"
+msgstr "LADSPA: \"%1\" δεν μποÏεί να χÏησιμοποιηθεί, εφ'όσον δεν μποÏεί να κάνει επι τόπου επεξεÏγασία"
+
+#: libs/ardour/ladspa_plugin.cc:329
+msgid "illegal parameter number used with plugin \"%1\". This mayindicate a change in the plugin design, and presets may beinvalid"
+msgstr "παÏάνομος αÏιθμός παÏαμέτÏου σε χÏήση με το plugin \"%1\". Ίσως να ενδείκνυται αλλαγή στο σχεδιασμό του plugin, και οι Ïυθμίσεις ίσως να είναι άκυÏες"
+
+#: libs/ardour/ladspa_plugin.cc:430
+msgid "Bad node sent to LadspaPlugin::set_state"
+msgstr "Κακός κόμβος εστάλη στο LadspaPlugin::set_state"
+
+#: libs/ardour/ladspa_plugin.cc:443
+msgid "LADSPA: no ladspa port number"
+msgstr "LADSPA: κανείς αÏιθμός θÏÏας ladspa"
+
+#: libs/ardour/ladspa_plugin.cc:449
+msgid "LADSPA: no ladspa port data"
+msgstr "LADSPA: κανένα δεδομένο θÏÏας ladspa"
+
+#: libs/ardour/ladspa_plugin.cc:498
+msgid "LADSPA LadspaPlugin MIDI control specification for port %1 is incomplete, so it has been ignored"
+msgstr "Ο καθοÏισμός ελέγχου LADSPA LadspaPlugin για MIDI για την θÏÏα %1 είναι ημιτελής, έτσι αγνοήθηκε."
+
+#: libs/ardour/location.cc:269
+msgid "incorrect XML node passed to Location::set_state"
+msgstr "λανθασμένος κόμβος XML πέÏασε στην Τοποθεσία::set_state"
+
+#: libs/ardour/location.cc:276
+msgid "XML node for Location has no name information"
+msgstr "Ο κόμβος XML για την Τοποθεσία δεν έχει πληÏοφοÏίες ονόματος"
+
+#: libs/ardour/location.cc:283
+msgid "XML node for Location has no start information"
+msgstr "Ο κόμβος XML για την Τοποθεσία δεν έχει πληÏοφοÏίες ενάÏξεως"
+
+#: libs/ardour/location.cc:294
+msgid "XML node for Location has no end information"
+msgstr "Ο κόμβος XML για την Τοποθεσία δεν έχει πληÏοφοÏίες τέλους"
+
+#: libs/ardour/location.cc:303
+msgid "XML node for Location has no flags information"
+msgstr "Ο κόμβος XML για την Τοποθεσία δεν έχει πληÏοφοÏίες για σημαίες(flags)"
+
+#: libs/ardour/location.cc:391
+msgid "Locations: attempt to use unknown location as selected location"
+msgstr "Τοποθεσίες: απόπειÏα να χÏησιμοποιηθεί άγνωστη τοποθεσία σαν επιλεγμένη τοποθεσία"
+
+#: libs/ardour/location.cc:418
+#: libs/ardour/playlist.cc:1187
+msgid "clear"
+msgstr "εκκαθάÏιση"
+
+#: libs/ardour/location.cc:443
+msgid "clear markers"
+msgstr "εκκαθάÏιση στιγμάτων"
+
+#: libs/ardour/location.cc:471
+msgid "clear ranges"
+msgstr "εκκαθάÏιση διαστημάτων"
+
+#: libs/ardour/location.cc:489
+msgid "add"
+msgstr "Ï€Ïόσθεση"
+
+#: libs/ardour/location.cc:527
+msgid "remove"
+msgstr "απαλοιφή"
+
+#: libs/ardour/location.cc:567
+msgid "incorrect XML mode passed to Locations::set_state"
+msgstr "λανθασμένο XML mode πέÏασε στις Τοποθεσίες::set_state"
+
+#: libs/ardour/mtc_slave.cc:196
+msgid "MTC Slave: atomic read of current time failed, sleeping!"
+msgstr "MTC Slave: ατομική ανάγνωση του Ï„Ïέχοντος χÏόνου απέτυχε, πίσω για Ïπνο!"
+
+#: libs/ardour/named_selection.cc:77
+msgid "Chunk %1 uses an unknown playlist \"%2\""
+msgstr "Το κομμάτι %1 χÏησιμοποιεί άγνωστη λίστα αναπαÏ/γής \"%2\""
+
+#: libs/ardour/named_selection.cc:80
+msgid "Chunk %1 contains misformed playlist information"
+msgstr "Το κομμάτι %1 πεÏιέχει δÏσμοÏφες πληÏοφοÏίες για τη λίστα αναπαÏ/γής"
+
+#: libs/ardour/panner.cc:256
+msgid "MIDI pan control specification is incomplete, so it has been ignored"
+msgstr "Η Ï€ÏοδιαγÏαφή ελέγχου του MIDI pan είναι ημιτελής, με αποτέλεσμα να αγνοηθεί"
+
+#: libs/ardour/panner.cc:361
+msgid "automation write pass"
+msgstr "πέÏασμα εγγÏαφής αυτοματισμοÏ"
+
+#: libs/ardour/panner.cc:401
+#, c-format
+msgid "error writing pan automation file (%s)"
+msgstr "σφάλμα στην εγγÏαφή του pan στο αÏχείο Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï (%s)"
+
+#: libs/ardour/panner.cc:429
+msgid "badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"
+msgstr "δÏσμοÏφη εγγÏαφή συμβάντος pan Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï ÏƒÏ„Î· γÏαμμή %1 of %2 (αγνοήθηκε) [%3]"
+
+#: libs/ardour/panner.cc:944
+msgid "badly-formed positional data for Multi2dPanner - ignored"
+msgstr "δÏσμοÏφα δεδομένα θέσεως για το Multi2dPanner - αγνοήθηκε"
+
+#: libs/ardour/panner.cc:1237
+msgid "cannot open pan automation file \"%1\" for saving (%s)"
+msgstr "δεν μποÏÏŽ να ανοίξω αÏχείο Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï Ï„Î¿Ï… pan \"%1\" για αποθήκευση (%s)"
+
+#: libs/ardour/panner.cc:1273
+msgid "cannot open pan automation file %1 (%2)"
+msgstr "δεν μποÏÏŽ να ανοίξω αÏχείο Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï Ï„Î¿Ï… pan %1 (%2)"
+
+#: libs/ardour/panner.cc:1286
+msgid "badly formed version number in pan automation event file \"%1\""
+msgstr "δÏσμοÏφος αÏιθμός εκδόσεως στο αÏχείο συμβάντων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï Ï„Î¿Ï… pan \"%1\""
+
+#: libs/ardour/panner.cc:1290
+msgid "no version information in pan automation event file \"%1\" (first line = %2)"
+msgstr "καμία πληÏοφοÏία εκδόσεως στο αÏχείο συμβάντων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï Ï„Î¿Ï… pan \"%1\" (Ï€Ïώτη γÏαμμή = %2)"
+
+#: libs/ardour/panner.cc:1296
+msgid "mismatched pan automation event file version (%1)"
+msgstr "αταίÏιαστη έκδοση αÏχείου συμβάντων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï Î³Î¹Î± το pan (%1)"
+
+#: libs/ardour/panner.cc:1310
+msgid "too many panner states found in pan automation file %1"
+msgstr "πάÏα πολλές καταστάσεις panner ευÏέθησαν στο αÏχείο των αυτοματισμών pan %1"
+
+#: libs/ardour/panner.cc:1451
+msgid "Unknown panner plugin \"%1\" found in pan state - ignored"
+msgstr "Άγνωστο plugin για panner \"%1\" ευÏέθη στην κατάσταση pan - αγνοήθηκε"
+
+#: libs/ardour/panner.cc:1457
+msgid "panner plugin node has no type information!"
+msgstr "ο κόμβος για τα plugin του panner δεν έχει πληÏοφοÏίες Ï„Ïπου!"
+
+#: libs/ardour/playlist.cc:253
+msgid "playlist const copy constructor called"
+msgstr "λίστα αναπαÏ/γής const εκλήθη από κατασκευαστή αντιγÏάφου"
+
+#: libs/ardour/playlist.cc:259
+msgid "playlist non-const copy constructor called"
+msgstr "λίστα αναπαÏ/γής non-const εκλήθη από κατασκευαστή αντιγÏάφου"
+
+#: libs/ardour/playlist.cc:499
+msgid "add region"
+msgstr "Ï€Ïόσθεση πεÏιοχής"
+
+#: libs/ardour/playlist.cc:554
+msgid "replace region"
+msgstr "αντικατάσταση πεÏιοχής"
+
+#: libs/ardour/playlist.cc:567
+msgid "remove region"
+msgstr "απαλοιφή πεÏιοχής"
+
+#: libs/ardour/playlist.cc:614
+msgid "separate"
+msgstr "ξεχωÏιστό"
+
+#: libs/ardour/playlist.cc:878
+msgid "cut"
+msgstr "κοπή"
+
+#: libs/ardour/playlist.cc:968
+msgid "duplicate"
+msgstr "αντιγÏαφή"
+
+#: libs/ardour/playlist.cc:1023
+msgid "split"
+msgstr "διαχωÏισμός"
+
+#: libs/ardour/playlist.cc:1100
+msgid "%1: bounds changed received for region (%2)not in playlist"
+msgstr "%1: τα αλλαγμένα ÏŒÏια που ελήφθησαν για την πεÏιοχή (%2)δεν είναι στη λίστα αναπαÏ/γής"
+
+#: libs/ardour/playlist.cc:1361
+msgid "Playlist: cannot create region from state file"
+msgstr "Playlist: αδÏνατη η δημιουÏγία ΠεÏιοχής από αÏχείο καταστάσεως"
+
+#: libs/ardour/playlist.cc:1721
+msgid "nudged"
+msgstr "νυχθέν"
+
+#: libs/ardour/playlist_factory.cc:49
+#: libs/ardour/playlist_factory.cc:64
+msgid "programming error: Playlist::createRegion called with unknown Region type"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: Playlist::createRegion εκλήθη με άγνωστο Ï„Ïπο Πε"
+
+#: libs/ardour/playlist_factory.cc:86
+msgid "programming error: Playlist::copyPlaylist called with unknown Playlist type"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: Playlist::copyPlaylist με άγνωστο Ï„Ïπο Playlist"
+
+#: libs/ardour/plugin.cc:328
+msgid "Could not locate HOME. Preset not saved."
+msgstr "Δεν μπόÏεσα να βÏÏŽ το HOME. ΠÏο-ÏÏθμιση δεν αποθηκεÏθηκε."
+
+#: libs/ardour/plugin.cc:338
+#: libs/ardour/plugin.cc:344
+msgid "Could not create %1. Preset not saved. (%2)"
+msgstr "Δεν μπόÏεσα να δημιουÏγήσω το %1. ΠÏο-ÏÏθμιση δεν αποθηκεÏθηκε. (%2)"
+
+#: libs/ardour/plugin.cc:349
+msgid "Error saving presets file %1."
+msgstr "Σφάλμα στην αποθήκευση αÏχείου Ï€Ïο-Ïυθμίσεων %1."
+
+#: libs/ardour/plugin_manager.cc:194
+msgid "Could not parse rdf file: %1"
+msgstr "Δεν μπόÏεσα να αναλÏσω το αÏχείο rdf: %1"
+
+#: libs/ardour/plugin_manager.cc:235
+msgid "LADSPA: cannot load module \"%1\" (%2)"
+msgstr "LADSPA: δεν μποÏÏŽ να φοÏτώσω το module \"%1\" (%2)"
+
+#: libs/ardour/plugin_manager.cc:242
+msgid "LADSPA: module \"%1\" has no descriptor function."
+msgstr "LADSPA: το module \"%1\" δεν έχει λειτουÏγία πεÏιγÏαφής."
+
+#: libs/ardour/plugin_manager.cc:297
+msgid "VST: cannot load module from \"%1\""
+msgstr "VST: δεν μποÏÏŽ να φοÏτώσω module από \"%1\""
+
+#: libs/ardour/plugin_manager.cc:302
+msgid "You asked ardour to not use any VST plugins"
+msgstr "Ζητήσατε απ'το Ardour να μή χÏησιμοποιήσει VST plugins"
+
+#: libs/ardour/plugin_manager.cc:305
+msgid "This version of ardour has no support for VST plugins"
+msgstr "Η παÏοÏσα έκδοση του ardour δεν υποστηÏίζει VST plugins"
+
+#: libs/ardour/plugin_manager.cc:312
+msgid "LADSPA: cannot load module from \"%1\""
+msgstr "LADSPA: δεν μποÏÏŽ να φοÏτώσω module από \"%1\""
+
+#: libs/ardour/plugin_manager.cc:374
+#: libs/ardour/plugin_manager.cc:386
+msgid "Unknown"
+msgstr "Άγνωστο"
+
+#: libs/ardour/plugin_manager.cc:464
+msgid "VST plugin %1 does not support processReplacing, and so cannot be used in ardour at this time"
+msgstr "Το VST plugin %1 δεν υποστηÏίζει processReplacing, και έτσι δεν μποÏεί να χÏησιμοποιηθεί στον ardour αυτή τη φοÏά"
+
+#: libs/ardour/recent_sessions.cc:44
+msgid "cannot open recent session file %1 (%2)"
+msgstr "δεν μποÏÏŽ να ανοίξω το Ï€Ïόσφατο αÏχείο συνεδÏίας %1 (%2)"
+
+#: libs/ardour/redirect.cc:77
+msgid "programming error: unknown Redirect type in Redirect::Clone!\n"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: άγνωστος Ï„Ïπος Redirect στο Redirect::Clone!\n"
+
+#: libs/ardour/redirect.cc:102
+#: libs/ardour/utils.cc:203
+msgid "pre"
+msgstr "pre"
+
+#: libs/ardour/redirect.cc:104
+#: libs/ardour/utils.cc:206
+msgid "post"
+msgstr "post"
+
+#: libs/ardour/redirect.cc:107
+msgid "Redirect: unknown placement string \"%1\" (ignored)"
+msgstr "Redirect: άγνωστη γÏαμμή τοποθετήσεως \"%1\" (αγνοήθηκε)"
+
+#: libs/ardour/redirect.cc:125
+msgid "%1: cannot open %2 to load automation data (%3)"
+msgstr "%1: δεν μποÏÏŽ να ανοίξω %2 για ανάκληση δεδομένων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï (%3)"
+
+#: libs/ardour/redirect.cc:154
+msgid "%1: cannot load automation data from %2"
+msgstr "%1: δεν μποÏοÏν να ανακληθοÏν δεδομένα Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï Î±Ï€ÏŒ %2"
+
+#: libs/ardour/redirect.cc:175
+msgid "%1: cannot open %2 to store automation data (%3)"
+msgstr "%1: δεν μποÏÏŽ να ανοίξω %2 για αποθήκευση δεδομένων Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï (%3)"
+
+#: libs/ardour/redirect.cc:194
+#: libs/ardour/redirect.cc:201
+msgid "%1: could not save automation state to %2"
+msgstr "%1: δεν μπόÏεσα να αποθηκεÏσω την κατάσταση Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï ÏƒÏ„Î¿ %2"
+
+#: libs/ardour/redirect.cc:246
+msgid "Could not get state from Redirect (%1). Problem with save_automation"
+msgstr "Δεν μπόÏεσα να πάÏω κατάσταση από το Redirect (%1). ΠÏόβλημα με την αποθήκευση_αυτοματισμοÏ"
+
+#: libs/ardour/redirect.cc:296
+msgid "incorrect XML node \"%1\" passed to Redirect object"
+msgstr "λανθασμένος κόμβος XML \"%1\" πέÏασε στο αντικείμενο Redirect"
+
+#: libs/ardour/redirect.cc:318
+msgid "%1: Automation node has no path property"
+msgstr "%1: Ο Κόμβος Î±Ï…Ï„Î¿Î¼Î±Ï„Î¹ÏƒÎ¼Î¿Ï Î´ÎµÎ½ έχει οÏισμένο path"
+
+#: libs/ardour/redirect.cc:343
+msgid "XML node describing an IO is missing an IO node"
+msgstr "Στον κόμβο XML που πεÏιγÏάφει I/O λείπει ένας κόμβος IO"
+
+#: libs/ardour/redirect.cc:348
+msgid "XML node describing a redirect is missing the `active' field"
+msgstr "Στον κόμβο XML που πεÏιγÏάφει ένα redirect λείπει το πεδίο `ενεÏγό'(active)"
+
+#: libs/ardour/redirect.cc:358
+msgid "XML node describing a redirect is missing the `placement' field"
+msgstr "Στον κόμβο XML που πεÏιγÏάφει ένα redirect λείπει το πεδίο `τοποθέτηση'(placement)"
+
+#: libs/ardour/redirect.cc:467
+msgid "active_changed"
+msgstr "αλλαγή_ενεÏγοÏ"
+
+#: libs/ardour/region.cc:885
+msgid "Session: XMLNode describing a Region is incomplete (no id)"
+msgstr "ΣυνεδÏία: XMLΚόμβος που πεÏιγÏάφει ΠεÏιοχή είναι ημιτελής (χωÏίς id)"
+
+#: libs/ardour/region.cc:892
+msgid "Session: XMLNode describing a Region is incomplete (no name)"
+msgstr "ΣυνεδÏία: XMLΚόμβος που πεÏιγÏάφει ΠεÏιοχή είναι ημιτελής (χωÏίς όνομα)"
+
+#: libs/ardour/route.cc:79
+#: libs/ardour/session.cc:1554
+#: libs/ardour/session.cc:1560
+#: libs/ardour/session.cc:3093
+msgid "signal"
+msgstr "σήμα"
+
+#: libs/ardour/route.cc:1430
+msgid "Could not get state of route. Problem with save_automation"
+msgstr "Δεν μπόÏεσα να πάÏω κατάσταση διαδÏομής. ΠÏόβλημα με αποθήκευση_αυτοματισμοÏ"
+
+#: libs/ardour/route.cc:1482
+msgid "Send construction failed"
+msgstr "Αποστολή κατασκευής απέτυχε"
+
+#: libs/ardour/route.cc:1504
+msgid "unknown Insert type \"%1\"; ignored"
+msgstr "άγνωστος Ï„Ïπος Λήψης(Insert) \"%1\"... αγνοήθηκε"
+
+#: libs/ardour/route.cc:1510
+msgid "Insert XML node has no type property"
+msgstr "Εισηχθέντας κόμβος XML δεν έχει στοιχεία Ï„Ïπου"
+
+#: libs/ardour/route.cc:1515
+msgid "insert could not be created. Ignored."
+msgstr "εισαγωγή δεν μπόÏεσε να δημιουÏγηθεί. Αγνοήθηκε."
+
+#: libs/ardour/route.cc:1533
+msgid "Bad node sent to Route::set_state() [%1]"
+msgstr "Κακός κόμβος εστάλη στο Route::set_state() [%1]"
+
+#: libs/ardour/route.cc:1592
+msgid "Route %1: unknown edit group \"%2 in saved state (ignored)"
+msgstr "Route %1: άγνωστη ομάδα επεξεÏγασίας \"%2 στην αποθηκευμένη κατάσταση (αγνοήθηκε)"
+
+#: libs/ardour/route.cc:1608
+#: libs/ardour/route.cc:1612
+msgid "badly formed order key string in state file! [%1] ... ignored."
+msgstr "δÏσμοÏφη γÏαμμή ÎºÎ»ÎµÎ¹Î´Î¹Î¿Ï Ï„Î±Î¾Î¹Î½Î¿Î¼Î®ÏƒÎµÏ‰Ï‚ στο αÏχείο καταστάσεως ! [%1] ... αγνοήθηκε."
+
+#: libs/ardour/route.cc:1693
+#: libs/ardour/route.cc:1820
+msgid "[control]"
+msgstr "[έλεγχος]"
+
+#: libs/ardour/route.cc:1713
+msgid "Route %1: unknown mix group \"%2 in saved state (ignored)"
+msgstr "Route %1: άγνωστη ομάδα μίξεως \"%2 στην αποθηκευμένη κατάσταση (αγνοήθηκε)"
+
+#: libs/ardour/route.cc:1742
+#: libs/ardour/route.cc:1750
+msgid "MIDI mute control specification for %1 is incomplete, so it has been ignored"
+msgstr "Η Ï€ÏοδιαγÏαφή ελέγχου του MIDI mute για το %1 είναι ημιτελής, με αποτέλεσμα να αγνοηθεί"
+
+#: libs/ardour/send.cc:99
+msgid "XML node describing a send is missing a Redirect node"
+msgstr "Στον κόμβο XML όπου πεÏιγÏάφεται ένα send λείπει ένας κόμβος Redirect"
+
+#: libs/ardour/session.cc:103
+msgid "Could not resolve path: %1 (%2)"
+msgstr "ΑδÏνατη η εÏÏεση path: %1 (%2)"
+
+#: libs/ardour/session.cc:115
+msgid "cannot check session path %1 (%2)"
+msgstr "δεν μποÏÏŽ να ελέγξω το μονοπάτι συνεδÏίας %1 (%2)"
+
+#: libs/ardour/session.cc:145
+msgid "cannot check statefile %1 (%2)"
+msgstr "δεν μποÏÏŽ να ελέγξω το αÏχείο κατάστασεως %1 (%2)"
+
+#: libs/ardour/session.cc:181
+msgid "%1 is not an Ardour snapshot file"
+msgstr "Το %1 δεν έιναι αÏχείο-στιγμιότυπο του Ardour"
+
+#: libs/ardour/session.cc:198
+msgid "cannot determine current working directory (%1)"
+msgstr "δεν μποÏÏŽ να καθοÏίσω τον Ï„Ïέχοντα ενεÏγό φάκελο(directory) (%1)"
+
+#: libs/ardour/session.cc:215
+msgid "unknown file type for session %1"
+msgstr "άγνωστος Ï„Ïπος αÏχείου για την συνεδÏία %1"
+
+#: libs/ardour/session.cc:320
+msgid "monitor"
+msgstr "monitor"
+
+#: libs/ardour/session.cc:327
+msgid "master"
+msgstr "master"
+
+#: libs/ardour/session.cc:611
+msgid "could not setup Click I/O"
+msgstr "Δεν μπόÏεσα να διαμοÏφώσω το I/O του ΜετÏονόμου(click)"
+
+#: libs/ardour/session.cc:632
+msgid "cannot setup Click I/O"
+msgstr "Δεν μποÏÏŽ να διαμοÏφώσω το I/O του ΜετÏονόμου(click)"
+
+#: libs/ardour/session.cc:654
+msgid "cannot create Auditioner: no auditioning of regions possible"
+msgstr "δεν μποÏÏŽ να δημιουÏγήσω τον ΑκÏοατή: καμία ακÏόαση πεÏιοχών δυνατή"
+
+#: libs/ardour/session.cc:666
+#, c-format
+msgid "out %<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:678
+#, c-format
+msgid "in %<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:692
+#, c-format
+msgid "out %<PRIu32>+%<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:706
+#, c-format
+msgid "in %<PRIu32>+%<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:739
+msgid "cannot setup master inputs"
+msgstr "δεν μποÏÏŽ να διαμοÏφώσω τις master εισόδους"
+
+#: libs/ardour/session.cc:747
+msgid "cannot setup master outputs"
+msgstr "δεν μποÏÏŽ να διαμοÏφώσω τις master εξόδους"
+
+#: libs/ardour/session.cc:758
+msgid "Master Out"
+msgstr ""
+
+#: libs/ardour/session.cc:830
+msgid "cannot setup control inputs"
+msgstr "δεν μποÏÏŽ να διαμοÏφώσω τις εισόδους ελέγχου"
+
+#: libs/ardour/session.cc:838
+msgid "cannot set up master outputs"
+msgstr "δεν μποÏÏŽ να διαμοÏφώσω τις εξόδους ελέγχου"
+
+#: libs/ardour/session.cc:1110
+msgid "Session: you can't use that location for auto punch (start <= end)"
+msgstr "ΣυνεδÏία: δεν μποÏείτε να χÏησιμοποιήσετε αυτήν την τοποθεσία για auto punch (αÏχή <= τέλος)"
+
+#: libs/ardour/session.cc:1189
+msgid "Session: you can't use a mark for auto loop"
+msgstr "ΣυνεδÏία: δεν μποÏείτε να χÏησιμοποιήσετε στίγμα για auto loop"
+
+#: libs/ardour/session.cc:1572
+msgid "feedback loop setup between %1 and %2"
+msgstr "διαμόÏφωση feedback loop ανάμεσα σε %1 και %2"
+
+#: libs/ardour/session.cc:1724
+#: libs/ardour/session.cc:1821
+msgid "cannot configure %1 in/%2 out configuration for new audio track"
+msgstr "δεν μποÏÏŽ να διαμοÏφώσω %1 in/%2 out διάταξη για νέο κανάλι ήχου"
+
+#: libs/ardour/session.cc:1780
+msgid "Session: could not create new audio track."
+msgstr "ΣυνεδÏία: δεν μπόÏεσα να δημιουÏγήσω νέο κανάλι ήχου."
+
+#: libs/ardour/session.cc:1870
+msgid "Session: could not create new route."
+msgstr "ΣυνεδÏία: δεν μπόÏεσα να δημιουÏγήσω διαδÏομή."
+
+#: libs/ardour/session.cc:2354
+msgid "cannot create new name for region \"%1\""
+msgstr "δεν μποÏÏŽ να δημιουÏγήσω νέο όνομα για την πεÏιοχή \"%1\""
+
+#: libs/ardour/session.cc:2418
+msgid "too many regions with names like %1"
+msgstr "πάÏα πολλές πεÏιοχές με ονόματα σαν %1"
+
+#: libs/ardour/session.cc:2883
+msgid "There are already %1 recordings for %2, which I consider too many."
+msgstr "ΥπάÏχουν ήδη %1 εγγÏαφές για %2, τις οποίες θεωÏÏŽ πάÏα πολλές."
+
+#: libs/ardour/session.cc:3258
+msgid "programming error: unknown type of Insert created!"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: άγνωστος Ï„Ïπος Εισόδου εδημιουÏγήθη!"
+
+#: libs/ardour/session.cc:3264
+msgid "programming error: unknown type of Redirect created!"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: άγνωστος Ï„Ïπος Redirect εδημιουÏγήθη!"
+
+#: libs/ardour/session.cc:3287
+msgid "programming error: unknown type of Insert deleted!"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: άγνωστος Ï„Ïπος of Insert διεγÏάφη!"
+
+#: libs/ardour/session.cc:3293
+msgid "programming error: unknown type of Redirect deleted!"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: άγνωστος Ï„Ïπος of Redirect διεγÏάφη!"
+
+#: libs/ardour/session.cc:3636
+msgid "too many bounced versions of playlist \"%1\""
+msgstr "πάÏα πολλές bounced εκδόσεις της Playlist \"%1\""
+
+#: libs/ardour/session.cc:3649
+msgid "cannot create new audio file \"%1\" for %2"
+msgstr "δεν μποÏÏŽ να δημιουÏγήσω νέο αÏχείο ήχου \"%1\" για %2"
+
+#: libs/ardour/session_butler.cc:85
+#: libs/ardour/session_butler.cc:90
+msgid "UI: cannot set O_NONBLOCK on butler request pipe (%1)"
+msgstr "UI: δεν μποÏÏŽ να θέσω O_NONBLOCK στο butler request pipe (%1)"
+
+#: libs/ardour/session_butler.cc:95
+msgid "Session: could not create butler thread"
+msgstr "ΣυνεδÏία: δεν μπόÏεσα να δημιουÏγήσω δέσμη με τον butler"
+
+#: libs/ardour/session_butler.cc:189
+msgid "poll on butler request pipe failed (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:196
+msgid "Error on butler thread request pipe"
+msgstr "Σφάλμα στο butler thread request pipe"
+
+#: libs/ardour/session_butler.cc:238
+msgid "Error reading from butler request pipe"
+msgstr "Σφάλμα στην ανάγνωση από butler request pipe"
+
+#: libs/ardour/session_butler.cc:275
+msgid "Butler read ahead failure on dstream %1"
+msgstr "Αποτυχία Ï€Ïοανάγνωσης Butler στο dstream %1"
+
+#: libs/ardour/session_butler.cc:319
+msgid "Butler write-behind failure on dstream %1"
+msgstr "Αποτυχία οπισθεγγÏαφής Butler στο dstream %1"
+
+#: libs/ardour/session_click.cc:158
+msgid "cannot open click soundfile %1 (%2)"
+msgstr "δεν μποÏÏŽ να ανοίξω τοsoundfile μετÏονόμου%1 (%2)"
+
+#: libs/ardour/session_click.cc:167
+msgid "cannot read data from click soundfile"
+msgstr "δεν μποÏÏŽ να διαβάσω δεδομένα από το soundfile μετÏονόμου"
+
+#: libs/ardour/session_click.cc:192
+msgid "cannot open click emphasis soundfile %1 (%2)"
+msgstr "δεν μποÏÏŽ να ανοίξω το soundfile εμφάσεως μετÏονόμου %1 (%2)"
+
+#: libs/ardour/session_click.cc:200
+msgid "cannot read data from click emphasis soundfile"
+msgstr "δεν μποÏÏŽ να διαβάσω δεδομένα από το soundfile εμφάσεως μετÏονόμου"
+
+#: libs/ardour/session_events.cc:161
+msgid "Session: cannot have two events of type %1 at the same frame (%2)."
+msgstr "ΣυνεδÏία: δεν γίνεται να υπάÏχουν δÏο συμβάντα του Ï„Ïπου %1 στο ίδιο frame (%2)."
+
+#: libs/ardour/session_events.cc:422
+msgid "Programming error: illegal event type in process_event (%1)"
+msgstr "Σφάλμα Ï€ÏογÏαμματισμοÏ: παÏάνομος Ï„Ïπος συμβάντος στο process_event (%1)"
+
+#: libs/ardour/session_export.cc:63
+msgid "Export: no output file specified"
+msgstr "Εξαγωγή: κανένα αÏχείο εξόδου δεν Ï€ÏοσδιοÏίστηκε"
+
+#: libs/ardour/session_export.cc:164
+#: libs/ardour/session_export.cc:169
+msgid "illegal frame range in export specification"
+msgstr "παÏάνομο διάστημα frame στον Ï€ÏοσδιοÏισμό εξαγωγής"
+
+#: libs/ardour/session_export.cc:174
+msgid "Bad data width size. Report me!"
+msgstr "Κακό μέγεθος εÏÏους δεδομένων. ΑνάφεÏέ με!"
+
+#: libs/ardour/session_export.cc:204
+msgid "Export: cannot open output file \"%1\" (%2)"
+msgstr "Εξαγωγή: δεν μποÏÏŽ να ανοίξω αÏχείο εξόδου \"%1\" (%2)"
+
+#: libs/ardour/session_export.cc:214
+msgid "cannot initialize sample rate conversion: %1"
+msgstr "δεν μποÏÏŽ να καλέσω την μετατÏοπή του ÏÏ…Î¸Î¼Î¿Ï Î´ÎµÎ¹Î³Î¼Î±Ï„Î¿Î»Î·ÏˆÎ¯Î±Ï‚: %1"
+
+#: libs/ardour/session_export.cc:316
+msgid "an error occured during sample rate conversion: %1"
+msgstr "παÏουσιάστηκε σφάλμα κάτα την μετατÏοπή του ÏÏ…Î¸Î¼Î¿Ï Î´ÎµÎ¹Î³Î¼Î±Ï„Î¿Î»Î·ÏˆÎ¯Î±Ï‚: %1"
+
+#: libs/ardour/session_export.cc:327
+msgid "warning, leftover frames overflowed, glitches might occur in output"
+msgstr "Ï€Ïοσοχή, παÏατημένα frames διέÏÏευσαν, πιθανόν να παÏουσιαστοÏν ατέλειες στην έξοδο"
+
+#: libs/ardour/session_export.cc:418
+msgid "Export: could not write data to output file (%1)"
+msgstr "Εξαγωγή: δεν μπόÏεσα να γÏάψω δεδομένα στο αÏχείο εξόδου (%1)"
+
+#: libs/ardour/session_export.cc:500
+msgid "%1: cannot seek to %2 for export"
+msgstr "%1: δεν μποÏÏŽ να αναζητήσω στο %2 για εξαγωγή"
+
+#: libs/ardour/session_midi.cc:200
+msgid "Ardour is slaved to MTC - port cannot be reset"
+msgstr "Το Ardour υπακοÏει το MTC - η θÏÏα δεν γίνεται reset"
+
+#: libs/ardour/session_midi.cc:215
+msgid "unknown port %1 requested for MTC"
+msgstr "Εζητήθη άγνωστη θÏÏα %1 για το MTC"
+
+#: libs/ardour/session_midi.cc:541
+msgid "Error reading from MIDI port %1"
+msgstr "Σφάλμα στην ανάγνωση της θÏÏας MIDI %1"
+
+#: libs/ardour/session_midi.cc:914
+msgid "Session: could not send full MIDI time code"
+msgstr "ΣυνεδÏία: δεν μπόÏεσα να στείλω ολόκληÏο MIDI time code"
+
+#: libs/ardour/session_midi.cc:973
+msgid "Session: cannot send quarter-frame MTC message (%1)"
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να στείλω τέταÏτο-frame MTC μήνυμα (%1)"
+
+#: libs/ardour/session_midi.cc:1081
+msgid "MMC: cannot send command %1%2%3"
+msgstr "MMC: δεν μποÏÏŽ να στείλω την εντολή %1%2%3"
+
+#: libs/ardour/session_midi.cc:1188
+msgid "UI: cannot set O_NONBLOCK on signal read pipe (%1)"
+msgstr "UI: δεν μποÏÏŽ να θέσω O_NONBLOCK στο signal read pipe (%1)"
+
+#: libs/ardour/session_midi.cc:1193
+msgid "UI: cannot set O_NONBLOCK on signal write pipe (%1)"
+msgstr "UI: δεν μποÏÏŽ να θέσω O_NONBLOCK στο signal write pipe (%1)"
+
+#: libs/ardour/session_midi.cc:1198
+msgid "Session: could not create transport thread"
+msgstr "ΣυνεδÏία: δεν μπόÏεσα να δημιουÏγήσω δέσμη με transport"
+
+#: libs/ardour/session_midi.cc:1227
+msgid "cannot send signal to midi thread! (%1)"
+msgstr "δεν μποÏÏŽ να στείλω σήμα στη δέσμη midi! (%1)"
+
+#: libs/ardour/session_midi.cc:1322
+msgid "MIDI thread poll failed (%1)"
+msgstr "Αίτηση δέσμης MIDI απέτυχε (%1)"
+
+#: libs/ardour/session_midi.cc:1334
+msgid "Error on transport thread request pipe"
+msgstr "Σφάλμα στο transport thread request pipe"
+
+#: libs/ardour/session_midi.cc:1361
+msgid "Error reading from transport request pipe"
+msgstr "Σφάλμα στην ανάγνωση από transport request pipe"
+
+#: libs/ardour/session_process.cc:104
+msgid "Session: error in no roll for %1"
+msgstr "ΣυνεδÏία: σφάλμα στο no roll για %1"
+
+#: libs/ardour/session_state.cc:101
+msgid "Could not use path %1 (%s)"
+msgstr "ΑδÏνατη η χÏήση του path %1 (%s)"
+
+#: libs/ardour/session_state.cc:129
+msgid "end"
+msgstr "τέλος"
+
+#: libs/ardour/session_state.cc:130
+msgid "start"
+msgstr ""
+
+#: libs/ardour/session_state.cc:502
+msgid "Session: cannot create session dir \"%1\" (%2)"
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω φάκελο συνεδÏίας \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:513
+msgid "Session: cannot create session peakfile dir \"%1\" (%2)"
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω peakfile dir συνεδÏίας \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:522
+msgid "Session: cannot create session sounds dir \"%1\" (%2)"
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω φάκελο ήχων συνεδÏίας \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:531
+msgid "Session: cannot create session tape dir \"%1\" (%2)"
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω tape dir συνεδÏίας \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:540
+msgid "Session: cannot create session dead sounds dir \"%1\" (%2)"
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω φάκελο 'νεκÏών' ήχων συνεδÏίας \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:549
+msgid "Session: cannot create session automation dir \"%1\" (%2)"
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω φάκελο αυτοματισμών της συνεδÏίας \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:580
+msgid "Could not open %1 for writing mix template"
+msgstr "Δεν μπόÏεσα να ανοίξω %1 για γÏάψιμο του Ï€Ïοσχεδίου μίξεως"
+
+#: libs/ardour/session_state.cc:586
+msgid "Could not open mix template %1 for reading"
+msgstr "Δεν μπόÏεσα να ανοίξω Ï€Ïοσχέδιο μίξεως %1 για ανάγνωση"
+
+#: libs/ardour/session_state.cc:593
+msgid "Session already exists. Not overwriting"
+msgstr "Η ΣυνεδÏία ήδη υπάÏχει. ΑκÏÏωση overwriting"
+
+#: libs/ardour/session_state.cc:636
+msgid "Session: could not load diskstream via XML state"
+msgstr "ΣυνεδÏία: δεν μπόÏεσα να φοÏτώσω diskstream μέσω καταστάσεως XML"
+
+#: libs/ardour/session_state.cc:685
+msgid "could not backup old state file, current state not saved."
+msgstr "δεν μπόÏεσα να διασώσω το παλαιό αÏχείο καταστάσεως, η Ï„Ïέχουσα κατάσταση δεν αποθηκεÏτηκε."
+
+#: libs/ardour/session_state.cc:698
+msgid "state could not be saved to %1"
+msgstr "η κατάσταση δεν μποÏοÏσε να σωθεί στο %1"
+
+#: libs/ardour/session_state.cc:705
+msgid "could not remove corrupt state file %1"
+msgstr "αδÏνατη η διαγÏαφή αÏχείου διεφθαÏμένης state %1"
+
+#: libs/ardour/session_state.cc:709
+msgid "could not restore state file from backup %1"
+msgstr "αδÏνατη η επαναφοÏά του state file από backup %1"
+
+#: libs/ardour/session_state.cc:778
+msgid "%1: session state information file \"%2\" doesn't exist!"
+msgstr "%1: το αÏχείο πληÏοφοÏιών καταστάσεως συνεδÏίας \"%2\" δεν υπάÏχει!"
+
+#: libs/ardour/session_state.cc:789
+msgid "Could not understand ardour file %1"
+msgstr "Δεν μπόÏεσα να κατανοήσω το ardour αÏχείο %1"
+
+#: libs/ardour/session_state.cc:1493
+msgid "programming error: Session: incorrect XML node sent to set_state()"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: ΣυνεδÏία: λανθασμένος κόμβος XML εστάλη στην set_state()"
+
+#: libs/ardour/session_state.cc:1539
+msgid "Session: XML state has no options section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα επιλογών(options)"
+
+#: libs/ardour/session_state.cc:1544
+msgid "Session: XML state has no sources section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα πηγών"
+
+#: libs/ardour/session_state.cc:1551
+msgid "Session: XML state has no Regions section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα ΠεÏιοχών"
+
+#: libs/ardour/session_state.cc:1558
+msgid "Session: XML state has no playlists section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα playlists"
+
+#: libs/ardour/session_state.cc:1577
+msgid "Session: XML state has no diskstreams section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα diskstreams"
+
+#: libs/ardour/session_state.cc:1584
+msgid "Session: XML state has no connections section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα συνδέσεων"
+
+#: libs/ardour/session_state.cc:1591
+msgid "Session: XML state has no locations section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα τοποθεσιών"
+
+#: libs/ardour/session_state.cc:1624
+msgid "Session: XML state has no edit groups section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα επεξεÏ/σίας ομάδων"
+
+#: libs/ardour/session_state.cc:1631
+msgid "Session: XML state has no mix groups section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα μίξεως ομάδων"
+
+#: libs/ardour/session_state.cc:1638
+msgid "Session: XML state has no Tempo Map section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα Tempo Map"
+
+#: libs/ardour/session_state.cc:1645
+msgid "Session: XML state has no routes section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα διαδÏομών"
+
+#: libs/ardour/session_state.cc:1652
+msgid "Session: XML state has no click section"
+msgstr "ΣυνεδÏία: η XML κατάσταση δεν έχει τομέα μετÏονόμου"
+
+#: libs/ardour/session_state.cc:1697
+msgid "Session: cannot create Route from XML description."
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω ΔιαδÏομή από XML πεÏιγÏαφή."
+
+#: libs/ardour/session_state.cc:1735
+msgid "Session: cannot create Region from XML description."
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω ΠεÏιοχή από XML πεÏιγÏαφή."
+
+#: libs/ardour/session_state.cc:1764
+msgid "Session: XMLNode describing a AudioRegion is incomplete (no source)"
+msgstr "ΣυνεδÏία: Ο XMLΚόμβος που πεÏιγÏάφει AudioΠεÏιοχή είναι ημιτελής (δίχως πηγή)"
+
+#: libs/ardour/session_state.cc:1772
+#: libs/ardour/session_state.cc:1792
+msgid "Session: XMLNode describing a AudioRegion references an unknown source id =%1"
+msgstr "ΣυνεδÏία: Ο XMLΚόμβος που πεÏιγÏάφει AudioΠεÏιοχή αναφέÏει άγνωστο id πηγής =%1"
+
+#: libs/ardour/session_state.cc:1778
+#: libs/ardour/session_state.cc:1798
+msgid "Session: XMLNode describing a AudioRegion references a non-audio source id =%1"
+msgstr "ΣυνεδÏία: Ο XMLNode που πεÏιγÏάφει AudioRegion αναφέÏει μη-ηχητική πηγή με id =%1"
+
+#: libs/ardour/session_state.cc:1868
+msgid "Session: cannot create Source from XML description."
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω Πηγή από XML πεÏιγÏαφή."
+
+#: libs/ardour/session_state.cc:1889
+msgid "Found a sound file that cannot be used by Ardour. Talk to the progammers."
+msgstr "ΕυÏέθη sound file που δεν μποÏεί να χÏησιμοποιηθεί από τον Ardour. Επικοινωνήστε με τους Ï€ÏογÏαμματιστές."
+
+#: libs/ardour/session_state.cc:1913
+msgid "Could not create mix templates directory \"%1\" (%2)"
+msgstr "Δεν μπόÏεσα να δημιουÏγήσω φάκελο Ï€Ïοσχεδίων μίξεως \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:1927
+msgid "Template \"%1\" already exists - new version not created"
+msgstr "Το Ï€Ïοσχέδιο \"%1\" ήδη υπάÏχει - νέα έκδοση δεν δημιουÏγήθηκε"
+
+#: libs/ardour/session_state.cc:1934
+msgid "mix template not saved"
+msgstr "Ï€Ïοσχέδιο μίξεως δεν αποθηκεÏτηκε"
+
+#: libs/ardour/session_state.cc:1994
+msgid "cannot create session directory \"%1\"; ignored"
+msgstr "δεν μποÏÏŽ να δημιουÏγήσω φάκελο συνεδÏίας \"%1\"; αγνοήθηκε"
+
+#: libs/ardour/session_state.cc:2007
+msgid "cannot create sounds directory \"%1\"; ignored"
+msgstr "δεν μποÏÏŽ να δημιουÏγήσω τον φάκελο 'sounds' \"%1\"; αγνοήθηκε"
+
+#: libs/ardour/session_state.cc:2018
+msgid "cannot create dead sounds directory \"%1\"; ignored"
+msgstr "δεν μποÏÏŽ να δημιουÏγήσω τον φάκελο 'dead sounds' \"%1\"; αγνοήθηκε"
+
+#: libs/ardour/session_state.cc:2029
+msgid "cannot create peak file directory \"%1\"; ignored"
+msgstr "αδÏνατη η δημιουÏγία φακέλου peak file \"%1\"; αγνοήθηκε"
+
+#: libs/ardour/session_state.cc:2168
+#: libs/ardour/session_state.cc:2189
+msgid "Session: cannot create Playlist from XML description."
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω την Playlist από την XML πεÏιγÏαφή."
+
+#: libs/ardour/session_state.cc:2228
+msgid "Session: cannot create Named Selection from XML description."
+msgstr "ΣυνεδÏία: δεν μποÏÏŽ να δημιουÏγήσω την ονομασμένη επιλογή από την XML πεÏιγÏαφή."
+
+#: libs/ardour/session_state.cc:2360
+msgid "Unknown node \"%1\" found in Connections list from state file"
+msgstr "Άγνωστος κόμβος \"%1\" ευÏέθη στη λίστα 'Συνδέσεις' από το αÏχείο καταστάσεως"
+
+#: libs/ardour/session_state.cc:3197
+msgid "cannot remove dead sound file %1 (%2)"
+msgstr "δεν μποÏÏŽ να απαλοίψω το 'νεκÏο' ηχο-αÏχείο %1 (%2)"
+
+#: libs/ardour/session_time.cc:374
+msgid "Unknown JACK transport state %1 in sync callback"
+msgstr "Άγνωστη κατάσταση του JACK transport %1 στην ανάκληση sync"
+
+#: libs/ardour/session_timefx.cc:77
+msgid "tempoize: error creating name for new audio file based on %1"
+msgstr "tempoize: σφάλμα στη δημιουÏγία ονόματος για νέο αÏχείο ήχου βασισμένο σε %1"
+
+#: libs/ardour/session_timefx.cc:88
+msgid "tempoize: error creating new audio file %1 (%2)"
+msgstr "tempoize: σφάλμα στη δημιουÏγία νέου αÏχείου ήχου %1 (%2)"
+
+#: libs/ardour/session_timefx.cc:114
+msgid "tempoize: error reading data from %1"
+msgstr "tempoize: σφάλμα στην ανάγνωση δεδομένων από %1"
+
+#: libs/ardour/session_timefx.cc:127
+#: libs/ardour/session_timefx.cc:139
+msgid "error writing tempo-adjusted data to %1"
+msgstr "σφάλμα στην εγγÏαφή χÏονο-Ïυθμισμένων δεδομένων στο %1"
+
+#: libs/ardour/session_timefx.cc:145
+msgid "timefx code failure. please notify ardour-developers."
+msgstr "αποτυχία κώδικα timefx. παÏακαλώ ειδοποιήστε τους Ï€ÏογÏαμματιστές του ardour."
+
+#: libs/ardour/session_transport.cc:117
+msgid "Cannot loop - no loop range defined"
+msgstr "Δεν γίνεται loop - κανένα διάστημα loop δεν Ï€ÏοσδιοÏίστηκε"
+
+#: libs/ardour/session_transport.cc:479
+msgid ""
+"Seamless looping cannot be supported while Ardour is using JACK transport.\n"
+"Recommend changing the configured options"
+msgstr ""
+"Μονοκόμματο looping δεν υποστηÏίζεται ενώ ο Ardour χÏησιμοποιεί το JACK transport.\n"
+"ΣυνιστοÏμε την αλλαγή των διαμοÏφωμένων Ïυθμίσεων"
+
+#: libs/ardour/session_transport.cc:755
+msgid "Global varispeed cannot be supported while Ardour is connected to JACK transport control"
+msgstr "Η Global varispeed δεν μποÏεί να υποστηÏιχθεί ενώ ο Ardour είναι συνδεδεμένος με τον JACK transport control"
+
+#: libs/ardour/session_transport.cc:955
+msgid "please stop the transport before adjusting slave settings"
+msgstr "παÏακαλώ σταματήστε το transport Ï€Ïιν την ÏÏθμιση των επιλογων εξαÏτήσεως"
+
+#: libs/ardour/session_transport.cc:991
+msgid "No MTC port defined: MTC slaving is impossible."
+msgstr "Καμμία θÏÏα MTC δεν Ï€ÏοσδιοÏίστηκε: η εξάÏτηση του MTC (slaving) είναι αδÏνατη."
+
+#: libs/ardour/sndfile_helpers.cc:15
+msgid "WAV"
+msgstr "WAV"
+
+#: libs/ardour/sndfile_helpers.cc:16
+msgid "AIFF"
+msgstr "AIFF"
+
+#: libs/ardour/sndfile_helpers.cc:17
+msgid "raw (no header)"
+msgstr "raw (no header)"
+
+#: libs/ardour/sndfile_helpers.cc:18
+msgid "PAF (Ensoniq Paris)"
+msgstr "PAF (Ensoniq Paris)"
+
+#: libs/ardour/sndfile_helpers.cc:19
+msgid "AU (Sun/NeXT)"
+msgstr "AU (Sun/NeXT)"
+
+#: libs/ardour/sndfile_helpers.cc:20
+msgid "IRCAM"
+msgstr "IRCAM"
+
+#: libs/ardour/sndfile_helpers.cc:21
+msgid "W64 (64 bit WAV)"
+msgstr "W64 (64 bit WAV)"
+
+#: libs/ardour/sndfile_helpers.cc:26
+msgid ".wav"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:27
+msgid ".aiff"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:28
+msgid ".raw"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:29
+msgid ".paf"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:30
+msgid ".au"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:31
+msgid ".ircam"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:32
+msgid ".w64"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:47
+msgid "16 bit"
+msgstr "16 bit"
+
+#: libs/ardour/sndfile_helpers.cc:48
+msgid "24 bit"
+msgstr "24 bit"
+
+#: libs/ardour/sndfile_helpers.cc:49
+msgid "32 bit"
+msgstr "32 bit"
+
+#: libs/ardour/sndfile_helpers.cc:50
+msgid "8 bit"
+msgstr "8 bit"
+
+#: libs/ardour/sndfile_helpers.cc:51
+msgid "float"
+msgstr "float"
+
+#: libs/ardour/sndfile_helpers.cc:64
+msgid "Little-endian (Intel)"
+msgstr "Little-endian (Intel)"
+
+#: libs/ardour/sndfile_helpers.cc:65
+msgid "Big-endian (Mac)"
+msgstr "Big-endian (Mac)"
+
+#: libs/ardour/sndfilesource.cc:147
+msgid "FileSource: cannot get host information for BWF header (%1)"
+msgstr "FileSource: δεν μποÏÏŽ να βÏÏŽ πληÏοφοÏίες οικοδεσπότη(host) για επικεφαλίδα BWF (%1)"
+
+#: libs/ardour/sndfilesource.cc:169
+msgid "cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"
+msgstr "Δεν ετέθησαν broadcast info για το audio file %1 (%2); απόÏÏιψη broadcast info για αυτό το αÏχείο"
+
+#: libs/ardour/sndfilesource.cc:220
+msgid "SndFileSource: cannot open file \"%1\" for %2 (%3)"
+msgstr "SndFileSource: δεν μποÏÏŽ να ανοίξω το αÏχείο \"%1\" για %2 (%3)"
+
+#: libs/ardour/sndfilesource.cc:226
+msgid "SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"
+msgstr "SndFileSource: το αÏχείο πεÏιέχει μόνο %1 κανάλια; %2 δεν έχει αξία σαν κανάλι number"
+
+#: libs/ardour/sndfilesource.cc:327
+msgid "SndFileSource: could not seek to frame %1 within %2 (%3)"
+msgstr "SndFileSource: δεν μποÏοÏσα να αναζητήσω στο frame %1 μέσα στο %2 (%3)"
+
+#: libs/ardour/sndfilesource.cc:378
+msgid "programming error: %1 %2"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: %1 %2"
+
+#: libs/ardour/sndfilesource.cc:487
+#: libs/ardour/sndfilesource.cc:533
+msgid "cannot set broadcast info for audio file %1; Dropping broadcast info for this file"
+msgstr "Δεν ετέθησαν broadcast info για το audio file %1; ΑπόÏÏιψη broadcast info για αυτό το αÏχείο"
+
+#: libs/ardour/sndfilesource.cc:544
+msgid "%1: cannot seek to %2"
+msgstr "%1: αδÏνατη η αναζήτηση στο %2"
+
+#: libs/ardour/state_manager.cc:47
+msgid "cleared history"
+msgstr "εκκαθάÏιση ιστοÏικοÏ"
+
+#: libs/ardour/state_manager.cc:60
+msgid "programming error: illegal state ID (%1) passed to StateManager::set_state() (range = 0-%2)"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: αθέμιτη κατάσταση ID (%1) πέÏασε στον StateManager::set_state() (range = 0-%2)"
+
+#: libs/ardour/stateful.cc:102
+msgid "Error: could not write %1"
+msgstr "Σφάλμα: δεν μπόÏεσα να γÏάψω %1"
+
+#: libs/ardour/stateful.cc:116
+msgid "Could not understand XML file %1"
+msgstr "Δεν μπόÏεσα να κατανοήσω το XML αÏχείο %1"
+
+#: libs/ardour/tempo.cc:67
+msgid "TempoSection XML node has no \"start\" property"
+msgstr "Κόμβος του TempoSection XML δεν έχει ιδιότητα \"έναÏξη\""
+
+#: libs/ardour/tempo.cc:75
+msgid "TempoSection XML node has an illegal \"start\" value"
+msgstr "Κόμβος του TempoSection XML έχει αθέμιτη αξία \"έναÏξη\""
+
+#: libs/ardour/tempo.cc:82
+msgid "TempoSection XML node has no \"beats-per-minute\" property"
+msgstr "Κόμβος του TempoSection XML δεν έχει \"κτÏπων-ανά-λεπτό\" ιδιότητα"
+
+#: libs/ardour/tempo.cc:87
+msgid "TempoSection XML node has an illegal \"beats_per_minute\" value"
+msgstr "Κόμβος του TempoSection XML έχει αθέμιτη \"κτÏπων_ανά_λεπτό\" αξία"
+
+#: libs/ardour/tempo.cc:92
+msgid "TempoSection XML node has no \"movable\" property"
+msgstr "Κόμβος του TempoSection XML δεν έχει \"κινητή\" ιδιότητα"
+
+#: libs/ardour/tempo.cc:131
+msgid "MeterSection XML node has no \"start\" property"
+msgstr "Κόμβος του MeterSection XML δεν έχει \"έναÏξη\" ιδιότητα"
+
+#: libs/ardour/tempo.cc:139
+msgid "MeterSection XML node has an illegal \"start\" value"
+msgstr "Κόμβος του MeterSection XML έχει αθέμιτη \"έναÏξη\" αξία"
+
+#: libs/ardour/tempo.cc:146
+msgid "MeterSection XML node has no \"beats-per-bar\" property"
+msgstr "Κόμβος του MeterSection XML δεν έχει \"κτÏπων-ανά-μπάÏα\" ιδιότητα"
+
+#: libs/ardour/tempo.cc:151
+msgid "MeterSection XML node has an illegal \"beats-per-bar\" value"
+msgstr "Κόμβος του MeterSection XML έχει αθέμιτη \"κτÏπων-ανά-μπάÏα\" αξία"
+
+#: libs/ardour/tempo.cc:156
+msgid "MeterSection XML node has no \"note-type\" property"
+msgstr "Κόμβος του MeterSection XML δεν έχει \"Ï„Ïπος-νότας\" ιδιότητα"
+
+#: libs/ardour/tempo.cc:161
+msgid "MeterSection XML node has an illegal \"note-type\" value"
+msgstr "Κόμβος του MeterSection XML έχει αθέμιτη \"Ï„Ïπος-νότας\" αξία"
+
+#: libs/ardour/tempo.cc:166
+msgid "MeterSection XML node has no \"movable\" property"
+msgstr "Κόμβος του MeterSection XML δεν έχει \"κινητή\" ιδιότητα"
+
+#: libs/ardour/tempo.cc:259
+msgid "move metric"
+msgstr "μετακίνηση μετÏικοÏ"
+
+#: libs/ardour/tempo.cc:330
+msgid "metric removed"
+msgstr "μετÏικό απεσÏÏθη"
+
+#: libs/ardour/tempo.cc:373
+msgid "add tempo"
+msgstr "Ï€Ïόσθεση ÏυθμοÏ"
+
+#: libs/ardour/tempo.cc:402
+msgid "replace tempo"
+msgstr "αντικατάσταση ÏυθμοÏ"
+
+#: libs/ardour/tempo.cc:435
+msgid "add meter"
+msgstr "Ï€Ïόσθεση μετÏητή"
+
+#: libs/ardour/tempo.cc:463
+msgid "replaced meter"
+msgstr "αντικατεστημένος μετÏητής"
+
+#: libs/ardour/tempo.cc:483
+#: libs/ardour/tempo.cc:499
+msgid "programming error: no tempo section in tempo map!"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: κανένας τομέας ÏÏ…Î¸Î¼Î¿Ï ÏƒÏ„Î¿ χάÏτη Ïυθμών!"
+
+#: libs/ardour/tempo.cc:538
+msgid "programming error: unhandled MetricSection type"
+msgstr "σφάλμα Ï€ÏογÏαμματισμοÏ: αχείÏιστος Ï„Ïπος MetricSection"
+
+#: libs/ardour/tempo.cc:1226
+#: libs/ardour/tempo.cc:1238
+msgid "Tempo map: could not set new state, restoring old one."
+msgstr "ΧάÏτης Ïυθμών: δεν μπόÏεσα να θέσω νέα κατάσταση, επιστÏοφή Ï€ÏοηγοÏμενης."
+
+#: libs/ardour/tempo.cc:1262
+msgid "load XML data"
+msgstr "ΦόÏτωμα δεδομένων XML"
+
+#: libs/ardour/utils.cc:246
+msgid "illegal or badly-formed string used for path (%1)"
+msgstr "αθέμιτη ή κακοσχηματισμένη γÏαμμή για το μονοπάτι (%1)"
+
+#: libs/ardour/utils.cc:251
+msgid "path (%1) is ambiguous"
+msgstr "Το μονοπάτι (%1) είναι αμφίβολο"
+
+#: libs/ardour/vst_plugin.cc:187
+msgid "cannot create VST chunk directory: %1"
+msgstr "δεν μποÏÏŽ να δημιουÏγήσω φάκελο κομματιών VST: %1"
+
+#: libs/ardour/vst_plugin.cc:195
+msgid "cannot check VST chunk directory: %1"
+msgstr "δεν μποÏÏŽ να ελέγξω το φάκελο κομματιών VST: %1"
+
+#: libs/ardour/vst_plugin.cc:202
+msgid "%1 exists but is not a directory"
+msgstr "%1 υπάÏχει αλλά δεν είναι φάκελος"
+
+#: libs/ardour/vst_plugin.cc:240
+msgid "Bad node sent to VSTPlugin::set_state"
+msgstr "Κακός κόμβος εστάλη στο VSTPlugin::set_state"
+
+#: libs/ardour/vst_plugin.cc:343
+#: libs/ardour/vst_plugin.cc:354
+msgid "no support for presets using chunks at this time"
+msgstr "καμμία υποστήÏιξη αυτή τη στιγμή για Ïυθμίσεις που χÏησιμοποιοÏν κομμάτια"
+
+#: libs/ardour/coreaudiosource.cc:97
+msgid "CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel number"
+msgstr "CoreAudioSource: το αÏχείο πεÏιέχει μόνο %1 κανάλια; το %2 δεν έχει αξία σαν αÏιθμός καναλιών"
+
+#: libs/ardour/coreaudiosource.cc:162
+msgid "CoreAudioSource: could not seek to frame %1 within %2 (%3)"
+msgstr "CoreAudioSource: δεν μποÏοÏσα να αναζητήσω στο frame %1 μέσα στο %2 (%3)"
+
+#~ msgid "FileSource: \"%1\" not found when searching %2 using %3"
+#~ msgstr "FileSource: \"%1\" δεν ευÏέθη όταν αναζητείτο %2 χÏησιμοποιώντας %3"
+#~ msgid "FileSource: could not open \"%1\": (%2)"
+#~ msgstr "FileSource: δεν μπόÏεσα να ανοίξω \"%1\": (%2)"
+#~ msgid "FileSource: cannot write header in %1"
+#~ msgstr "FileSource: δεν μποÏÏŽ να γÏάψω επικεφαλίδα στο %1"
+#~ msgid "FileSource: cannot locate chunks in %1"
+#~ msgstr "FileSource: δεν μποÏÏŽ να ανιχνεÏσω κομμάτια στο %1"
+#~ msgid "FileSource: cannot read header in %1"
+#~ msgstr "FileSource: δεν μποÏÏŽ να διαβάσω επικεφαλίδα στο %1"
+#~ msgid "FileSource: cannot check header in %1"
+#~ msgstr "FileSource: δεν μποÏÏŽ να ελέγξω επικεφαλίδα στο %1"
+
+#, fuzzy
+#~ msgid "FileSource: cannot initialize peakfile for %1 as %2"
+#~ msgstr "FileSource: δεν μποÏÏŽ να εκκινήσω το peakfile για %1"
+#~ msgid "FileSource: cannot seek to end of file"
+#~ msgstr "FileSource: δεν μποÏÏŽ να ανιχνεÏσω το τέλος του αÏχείου"
+#~ msgid "FileSource: cannot read RIFF/WAVE chunk from file"
+#~ msgstr "FileSource: δεν μποÏÏŽ να διαβάσω κομμάτι RIFF/WAVE από το αÏχείο"
+#~ msgid "FileSource %1: not a RIFF/WAVE file"
+#~ msgstr "FileSource %1: δεν είναι RIFF/WAVE αÏχείο"
+#~ msgid "FileSource: can't read a chunk"
+#~ msgstr "FileSource: δεν μποÏÏŽ να διαβάσω κομμάτι"
+#~ msgid "FileSource: cannot get user information for BWF header (%1)"
+#~ msgstr ""
+#~ "FileSource: δεν μποÏÏŽ να βÏÏŽ πληÏοφοÏίες χÏήστη για επικεφαλίδα BWF (%1)"
+#~ msgid "FileSource[%1]: cannot update data size: %2"
+#~ msgstr "FileSource[%1]: δεν μποÏÏŽ να ανανεώσω το μέγεθος δεδομένων: %2"
+#~ msgid "FileSource: can't find RIFF chunk info"
+#~ msgstr "FileSource: δεν ευÏέθησαν πληÏοφοÏίες για RIFF κομμάτι"
+
+#, fuzzy
+#~ msgid "FileSource: can't find RIFX chunk info"
+#~ msgstr "FileSource: δεν ευÏέθησαν πληÏοφοÏίες για RIFF κομμάτι"
+#~ msgid "FileSource: can't read RIFF chunk"
+#~ msgstr "FileSource: δεν μποÏÏŽ να διαβάσω RIFF κομμάτι"
+#~ msgid "FileSource: can't find format chunk info"
+#~ msgstr "FileSource: δεν ευÏέθησαν πληÏοφοÏίες για το κομμάτι φοÏμαÏίσματος"
+#~ msgid "FileSource: can't read format chunk"
+#~ msgstr "FileSource: δεν μποÏÏŽ να διαβάσω το κομμάτι φοÏμαÏίσματος"
+#~ msgid "FileSource: can't find data chunk info"
+#~ msgstr "FileSource: δεν μποÏÏŽ να βÏÏŽ πληÏοφοÏίες για το κομμάτι δεδομένων"
+#~ msgid "FileSource: can't read data chunk"
+#~ msgstr "FileSource: δεν μποÏÏŽ να διαβάσω το κομμάτι δεδομένων"
+#~ msgid ""
+#~ "FileSource: cannot read Broadcast Wave data from existing audio file \"%1"
+#~ "\" (%2)"
+#~ msgstr ""
+#~ "FileSource: δεν μποÏÏŽ να διαβάσω δεδομένα Broadcast Wave από το υπάÏχον "
+#~ "αÏχείο \"%1\" (%2)"
+#~ msgid ""
+#~ "FileSource: cannot read Broadcast Wave coding history from audio file \"%1"
+#~ "\" (%2)"
+#~ msgstr ""
+#~ "FileSource: δεν μποÏÏŽ να διαβάσω το ιστοÏικό του κώδικα Broadcast Wave "
+#~ "από το αÏχείο \"%1\" (%2)"
+
+#, fuzzy
+#~ msgid ""
+#~ "FileSource \"%1\" does not use valid sample format.\n"
+#~ "This is probably a programming error."
+#~ msgstr ""
+#~ "FileSource \"%1\" δεν χÏησιμοποιεί format μεταβλητής υποδιαστολής.\n"
+#~ "Αυτό πιθανόν να είναι σφάλμα Ï€ÏογÏαμματισμοÏ."
+#~ msgid "FileSource \"%1\" has no \"data\" chunk"
+#~ msgstr "FileSource \"%1\" δεν έχει \"data\" κομμάτι"
+#~ msgid ""
+#~ "%1: data length in header (%2) differs from implicit size in file (%3)"
+#~ msgstr ""
+#~ "%1: το μέγεθος δεδομένων της επικεφαλίδας (%2) διαφέÏει από το δεδηλωμένο "
+#~ "μέγεθος στο αÏχείο (%3)"
+#~ msgid "\"%1\" has a sample rate of %2 instead of %3 as used by this session"
+#~ msgstr ""
+#~ "\"%1\" έχει Ïυθμό δειγματοληψίας %2 αντί του %3 όπως στην παÏοÏσα συνεδÏία"
+#~ msgid "FileSource: cannot write WAVE chunk: %1"
+#~ msgstr "FileSource: δεν μποÏÏŽ να γÏάψω WAVE κομμάτι: %1"
+#~ msgid "FileSource: cannot write format chunk: %1"
+#~ msgstr "FileSource: δεν μποÏÏŽ να γÏάψω format κομμάτι: %1"
+#~ msgid "FileSource: cannot data chunk: %1"
+#~ msgstr "FileSource: cδεν μποÏÏŽ να γÏάψω κομμάτι δεδομένων: %1"
+#~ msgid "FileSource: \"%1\" bad write (%2)"
+#~ msgstr "FileSource: \"%1\" κακή εγγÏαφή (%2)"
+
+#, fuzzy
+#~ msgid "cannot create feedback request pipe (%1)"
+#~ msgstr "Δεν μποÏÏŽ να δημιουÏγήσω transport request signal pipe (%1)"
+
+#, fuzzy
+#~ msgid "Session: could not create feedback thread"
+#~ msgstr "ΣυνεδÏία: δεν μπόÏεσα να δημιουÏγήσω δέσμη με τον butler"
+
+#, fuzzy
+#~ msgid "Feedback thread poll failed (%1)"
+#~ msgstr "Αίτηση δέσμης MIDI απέτυχε (%1)"
+
+#, fuzzy
+#~ msgid "Error on feedback thread request pipe"
+#~ msgstr "Σφάλμα στο transport thread request pipe"
+
+#, fuzzy
+#~ msgid "Error reading from feedback request pipe"
+#~ msgstr "Σφάλμα στην ανάγνωση από transport request pipe"
+#~ msgid "Could not find member filename"
+#~ msgstr "Δεν ευÏέθη το αÏχείο μέλους"
+#~ msgid "could not create crossfade object in playlist %1"
+#~ msgstr "δεν έγινε δημιουÏγία αντικειμένου crossfade στη λίστα αναπαÏ/γής%1"
+#~ msgid ""
+#~ "There are too many frozen versions of playlist \"%1\" to create another "
+#~ "one"
+#~ msgstr ""
+#~ "ΠάÏα πολλές εκδόσεις λίστας αναπαÏ/γής \"%1\" για τη δημιουÏγία άλλης"
+#~ msgid "alsa_pcm:playback_1"
+#~ msgstr "alsa_pcm:playback_1"
+#~ msgid "alsa_pcm:playback_2"
+#~ msgstr "alsa_pcm:playback_2"
+
+#, fuzzy
+#~ msgid "Could not find a template called %1 in %2"
+#~ msgstr "Δεν μπόÏεσα να ανοίξω Ï€Ïοσχέδιο μίξεως %1 για ανάγνωση"
+
+#, fuzzy
+#~ msgid "Source: cannot stat peakfile \"%1\" or \"%2\""
+#~ msgstr "Source: δεν μποÏÏŽ να μετÏήσω το αυτοστιγμεί peakfile \"%1\""
+#~ msgid "Transport: error polling extra MIDI port #1 (revents =%1%2%3"
+#~ msgstr ""
+#~ "Transport: σφάλμα στην αίτηση επιπλέον θÏÏας MIDI #1 (revents =%1%2%3"
+#~ msgid "Transport: error polling extra MIDI port #2 (revents =%1%2%3"
+#~ msgstr ""
+#~ "Transport: σφάλμα στην αίτηση επιπλέον θÏÏας MIDI #2 (revents =%1%2%3"
+#~ msgid "Source: cannot seek to frame %1 in peakfile!"
+#~ msgstr "Source: δεν μποÏÏŽ να αναζητήσω το frame %1 στο peakfile!"
+#~ msgid "Source[%1]: cannot seek to frame %2 in peakfile!"
+#~ msgstr "Source[%1]: δεν μποÏÏŽ να αναζητήσω το frame %2 στο peakfile!"
+#~ msgid "%1: could not seek to byte %2 in peakfile (%3"
+#~ msgstr "%1: δεν μποÏÏŽ να αναζητήσω σε byte %2 στο peakfile (%3"
+#~ msgid "could not register an input port called \"%1\""
+#~ msgstr "δεν μπόÏεσα να register μία θÏÏα εισόδου με όνομα \"%1\""
+#~ msgid "could not register an output port called \"%1\""
+#~ msgstr "δεν μπόÏεσα να register μία θÏÏα εξόδου με όνομα \"%1\""
+#~ msgid "%1: disk stream error at frame %2 (%3)"
+#~ msgstr "%1: σφάλμα disk stream στο frame %2 (%3)"
+#~ msgid "IO: cannot connect input port %1 to %2"
+#~ msgstr "IO: δεν μποÏεί να συνδεθεί η θÏÏα εισόδου %1 στο %2"
+#~ msgid "IO: cannot connect output port %1 to %2"
+#~ msgstr "IO: δεν μποÏεί να συνδεθεί η θÏÏα εξόδου %1 στο %2"
+#~ msgid "Playlist: cannot create from state."
+#~ msgstr "Playlist: δεν μποÏÏŽ να δημιουÏγήσω από κατάσταση."
+#~ msgid ""
+#~ "for various reasons, it is no longer valid to use one of the plugins "
+#~ "listed for this session. it will be ignored"
+#~ msgstr ""
+#~ "για διάφοÏους λόγους, δεν είναι πλέον χÏήσιμο να χÏησιμοποιηθεί ένα από "
+#~ "τα καταχωÏημένα pluginsγια αυτή τη συνεδÏία. Θα αγνοηθεί."
+#~ msgid "cannot connect click track to %1"
+#~ msgstr "δεν μποÏÏŽ να διασυνδέσω το κανάλι MετÏονόμου(click track) στο %1"
+#~ msgid "out %lu+%lu"
+#~ msgstr "out %lu+%lu"
+#~ msgid "in %lu+%lu"
+#~ msgstr "in %lu+%lu"
+#~ msgid "Session: error for %1 at frame %2 (%3)"
+#~ msgstr "ΣυνεδÏία: σφάλμα για %1 στο frame %2 (%3)"
+
diff --git a/libs/ardour/po/it_IT.po b/libs/ardour/po/it_IT.po
new file mode 100644
index 0000000000..2ce02827bd
--- /dev/null
+++ b/libs/ardour/po/it_IT.po
@@ -0,0 +1,2236 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR Paul Davis
+# This file is distributed under the same license as the PACKAGE package.
+# Filippo Pappalardo <filippo@email.it>, 2003.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libardour 0.664.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-06-29 21:03-0400\n"
+"PO-Revision-Date: 2003-05-21 12:50+0500\n"
+"Last-Translator: Filippo Pappalardo <filippo@email.it>\n"
+"Language-Team: Italian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=ISO-8859-1\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: libs/ardour/audio_diskstream.cc:337
+#, fuzzy
+msgid "AudioDiskstream: Session doesn't know about a Playlist called \"%1\""
+msgstr "DiskStream: La sessione non riconosce la Playlist chiamata \"%1\""
+
+#: libs/ardour/audio_diskstream.cc:342
+msgid "AudioDiskstream: Playlist \"%1\" isn't an audio playlist"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:433
+#, fuzzy
+msgid "AudioDiskstream %1: there is no existing playlist to make a copy of!"
+msgstr "DiskStream %1: non esiste alcuna playlist di cui fare una copia!"
+
+#: libs/ardour/audio_diskstream.cc:1114 libs/ardour/audio_diskstream.cc:1125
+#, fuzzy
+msgid ""
+"AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"
+msgstr "DiskStream %1: impossibile leggere %2 dalla playlista al frame %3"
+
+#: libs/ardour/audio_diskstream.cc:1254
+#, fuzzy
+msgid "AudioDiskstream %1: cannot read %2 from playlist at frame %3"
+msgstr "DiskStream %1: impossibile leggere %2 dalla playlista al frame %3"
+
+#: libs/ardour/audio_diskstream.cc:1621 libs/ardour/audio_diskstream.cc:1638
+#, fuzzy
+msgid "AudioDiskstream %1: cannot write to disk"
+msgstr "DiskStream %1: impossibile scrivere sul disco"
+
+#: libs/ardour/audio_diskstream.cc:1698
+#, fuzzy
+msgid "AudioDiskstream \"%1\": cannot flush captured data to disk!"
+msgstr "DiskStream \"%1\": impossibile scaricare i dati acquisiti sul disco!"
+
+#: libs/ardour/audio_diskstream.cc:1796
+msgid "%1: could not create region for complete audio file"
+msgstr "%1: impossibile creare una regione per il file audio completo"
+
+#: libs/ardour/audio_diskstream.cc:1819
+#, fuzzy
+msgid "AudioDiskstream: could not create region for captured audio!"
+msgstr "DiskStream: impossibile creare una regione per l'audio registrato!"
+
+#: libs/ardour/audio_diskstream.cc:1874
+#, fuzzy
+msgid "programmer error: %1"
+msgstr "errore di programmazione: %1"
+
+#: libs/ardour/audio_diskstream.cc:2146
+#, fuzzy
+msgid "AudioDiskstream: channel %1 out of range"
+msgstr "DiskStream: canale fuori margine"
+
+#: libs/ardour/audio_diskstream.cc:2171
+msgid "%1:%2 new capture file not initialized correctly"
+msgstr "%1:%2 nuovo file di registrazione non è stato avviato correttamente"
+
+#: libs/ardour/audio_diskstream.cc:2404
+msgid "Location \"%1\" not valid for track loop (start >= end)"
+msgstr "La Location \"%1\" non valida per il loop (inizio >= fine)"
+
+#: libs/ardour/audio_diskstream.cc:2485
+#, fuzzy
+msgid "%1: cannot restore pending capture source file %2"
+msgstr "Import: impossibile aprire il file audio di input \"%1\""
+
+#: libs/ardour/audio_diskstream.cc:2507
+msgid "%1: incorrect number of pending sources listed - ignoring them all"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2523
+#, fuzzy
+msgid "%1: cannot create whole-file region from pending capture sources"
+msgstr "Playlist: impossibile creare la Regione dal file di stato"
+
+#: libs/ardour/audio_diskstream.cc:2535
+#, fuzzy
+msgid "%1: cannot create region from pending capture sources"
+msgstr "Playlist: impossibile creare la Regione dal file di stato"
+
+#: libs/ardour/audio_library.cc:92
+msgid "channels"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:93
+#, fuzzy
+msgid "samplerate"
+msgstr "separa"
+
+#: libs/ardour/audio_library.cc:94
+msgid "resolution"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:95
+msgid "format"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:102
+msgid "Could not open %1. Audio Library not saved"
+msgstr "Impossibile accedere a %1. Libreria Audio non salvata"
+
+#: libs/ardour/audio_playlist.cc:53 libs/ardour/audio_playlist.cc:63
+#: libs/ardour/audio_playlist.cc:74 libs/ardour/audio_playlist.cc:121
+#: libs/ardour/insert.cc:76 libs/ardour/insert.cc:95 libs/ardour/insert.cc:120
+#: libs/ardour/insert.cc:838 libs/ardour/insert.cc:846 libs/ardour/send.cc:39
+#: libs/ardour/send.cc:53 libs/ardour/send.cc:62
+#: libs/ardour/session_state.cc:1621 libs/ardour/session_state.cc:1667
+msgid "initial state"
+msgstr "stato iniziale"
+
+#: libs/ardour/audio_playlist.cc:275 libs/ardour/audio_playlist.cc:769
+msgid ""
+"programming error: non-audio Region passed to remove_overlap in audio "
+"playlist"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:402
+msgid ""
+"programming error: non-audio Region tested for overlap in audio playlist"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:878
+msgid "xfade change"
+msgstr "cambio dello smorzamento incrociato"
+
+#: libs/ardour/audio_playlist.cc:933
+msgid "region modified"
+msgstr "regione modificata"
+
+#: libs/ardour/audio_track.cc:125 libs/ardour/io.cc:1716
+#: libs/ardour/io.cc:1826
+msgid "Unknown connection \"%1\" listed for input of %2"
+msgstr "Connessione sconosciuta \"%1\" come input di %2"
+
+#: libs/ardour/audio_track.cc:127 libs/ardour/io.cc:1718
+#: libs/ardour/io.cc:1828
+msgid "in 1"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:128 libs/ardour/io.cc:1719
+#: libs/ardour/io.cc:1829
+msgid "No input connections available as a replacement"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:132 libs/ardour/io.cc:1723
+#: libs/ardour/io.cc:1833
+msgid "Connection %1 was not available - \"in 1\" used instead"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:141 libs/ardour/io.cc:1842
+msgid "improper input channel list in XML node (%1)"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:186 libs/ardour/audio_track.cc:199
+msgid "AudioTrack: diskstream \"%1\" not known by session"
+msgstr "AudioTrack: diskstream \"%1\" non riconosciuto dalla sessione"
+
+#: libs/ardour/audio_track.cc:297
+msgid ""
+"MIDI rec_enable control specification for %1 is incomplete, so it has been "
+"ignored"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:309
+msgid "programming error: AudioTrack given state without diskstream!"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:144
+msgid "cannot activate JACK client"
+msgstr "impossibile attivare il client JACK"
+
+#: libs/ardour/audioengine.cc:395
+msgid "register audio input port called before engine was started"
+msgstr ""
+"la richiesta di registrazione di una porta di entrata avvenuta prima "
+"dell'avvio dell'applicazione"
+
+#: libs/ardour/audioengine.cc:426
+msgid "register audio output port called before engine was started"
+msgstr ""
+"la richiesta di registrazione di una porta di uscita avvenuta prima "
+"dell'avvio dell'applicazione"
+
+#: libs/ardour/audioengine.cc:487
+msgid "connect called before engine was started"
+msgstr "richiesta di connessione avvenuta prima dell'avvio dell'applicazione"
+
+#: libs/ardour/audioengine.cc:503
+msgid "AudioEngine: cannot connect %1 (%2) to %3 (%4)"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:516 libs/ardour/audioengine.cc:545
+msgid "disconnect called before engine was started"
+msgstr ""
+"richiesta di disconnessione avvenuta prima dell'avvio dell'applicazione"
+
+#: libs/ardour/audioengine.cc:603
+msgid "get_port_by_name() called before engine was started"
+msgstr ""
+"richiesta get_port_by_name() avvenuta prima dell'avvio dell'applicazione"
+
+#: libs/ardour/audioengine.cc:636
+msgid "get_ports called before engine was started"
+msgstr "richiesta di get_ports avvenuta prima dell'avvio dell'applicazione"
+
+#: libs/ardour/audioengine.cc:711
+msgid "get_nth_physical called before engine was started"
+msgstr ""
+"richiesta di get_nth_physical avvenuta prima dell'avvio dell'applicazione"
+
+#: libs/ardour/audioengine.cc:739
+#, fuzzy
+msgid "get_port_total_latency() called with no JACK client connection"
+msgstr ""
+"richiesta di get_port_total_latency() avvenuta prima dell'avvio "
+"dell'applicazione"
+
+#: libs/ardour/audioengine.cc:745
+msgid "get_port_total_latency() called before engine was started"
+msgstr ""
+"richiesta di get_port_total_latency() avvenuta prima dell'avvio "
+"dell'applicazione"
+
+#: libs/ardour/audioengine.cc:869
+msgid "Unable to connect to JACK server"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:872
+msgid "Could not connect to JACK server as \"%1\""
+msgstr ""
+
+#: libs/ardour/audioengine.cc:877
+msgid "JACK server started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:911
+msgid "cannot shutdown connection to JACK"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:936
+msgid "failed to connect to JACK"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:952
+#, fuzzy
+msgid "could not reregister %1"
+msgstr "Esportazione: impossibile scrivere dati sul file di output (%1)"
+
+#: libs/ardour/audioengine.cc:1009
+msgid "could not reconnect %1 and %2 (err = %3)"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:444 libs/ardour/session_state.cc:3095
+msgid ""
+"there are already 1000 files with names like %1; versioning discontinued"
+msgstr ""
+"ci sono gia' 1000 file con nomi come %1; tracciamento di versione interrotto"
+
+#: libs/ardour/audiofilesource.cc:458 libs/ardour/session_state.cc:3109
+msgid "cannot rename audio file source from %1 to %2 (%3)"
+msgstr "impossibile rinominare file audio sorgente da %1 a %2 (%3)"
+
+#: libs/ardour/audiofilesource.cc:465 libs/ardour/session_state.cc:3124
+msgid "cannot remove peakfile %1 for %2 (%3)"
+msgstr "impossibile eliminare il peakfile %1 per %2 (%3)"
+
+#: libs/ardour/audiofilesource.cc:509
+msgid "FileSource: search path not set"
+msgstr "FileSource: percorso di ricerca non specificato"
+
+#: libs/ardour/audiofilesource.cc:533
+msgid ""
+"FileSource: \"%1\" is ambigous when searching %2\n"
+"\t"
+msgstr ""
+"FileSource: \"%1\" è risultato ambiguo nel cercare %2\n"
+"\t"
+
+#: libs/ardour/audiofilesource.cc:539
+#, fuzzy
+msgid "Filesource: cannot find required file (%1): while searching %2"
+msgstr "FileSource: impossibile trovare il file richiesto (%1): %2"
+
+#: libs/ardour/audiofilesource.cc:562
+msgid "Filesource: cannot find required file (%1): %2"
+msgstr "FileSource: impossibile trovare il file richiesto (%1): %2"
+
+#: libs/ardour/audiofilesource.cc:567
+msgid "Filesource: cannot check for existing file (%1): %2"
+msgstr "FileSource: impossibile controllare il file esistente (%1): %2"
+
+#: libs/ardour/audiofilesource.cc:636 libs/ardour/insert.cc:525
+#: libs/ardour/sndfilesource.cc:113
+msgid "programming error: %1"
+msgstr "errore di programmazione: %1"
+
+#: libs/ardour/audiofilesource.cc:641
+#, fuzzy
+msgid "cannot rename audio file for %1 to %2"
+msgstr "impossibile rinominare file audio sorgente da %1 a %2 (%3)"
+
+#: libs/ardour/audiofilter.cc:45
+msgid "audiofilter: error creating name for new audio file based on %1"
+msgstr ""
+"audiofilter: errore nel creare il nome per il nuovo file audio basato su %1"
+
+#: libs/ardour/audiofilter.cc:58
+msgid "audiofilter: error creating new audio file %1 (%2)"
+msgstr "audiofilter: errore nel creare un nuovo file audio %1 (%2)"
+
+#: libs/ardour/audioregion.cc:857 libs/ardour/audioregion.cc:919
+#, fuzzy
+msgid "fade in change"
+msgstr "cambio dello smorzamento incrociato"
+
+#: libs/ardour/audioregion.cc:1349
+#, c-format
+msgid "normalized to %.2fdB"
+msgstr "normalizzato a %.2fdB"
+
+#: libs/ardour/audioregion.cc:1367
+#, fuzzy
+msgid "envelope change"
+msgstr "livello cambiato"
+
+#: libs/ardour/audiosource.cc:143
+msgid "poll on peak request pipe failed (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:150
+msgid "Error on peak thread request pipe"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:183
+#, fuzzy
+msgid "Error reading from peak request pipe"
+msgstr "Errore nel leggere dalla porta MIDI %1"
+
+#: libs/ardour/audiosource.cc:215 libs/ardour/session_butler.cc:80
+#: libs/ardour/session_midi.cc:1183
+msgid "Cannot create transport request signal pipe (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:220 libs/ardour/audiosource.cc:225
+msgid "UI: cannot set O_NONBLOCK on peak request pipe (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:230
+#, fuzzy
+msgid "AudioSource: could not create peak thread"
+msgstr "Sessione: impossibile creare un nuovo route"
+
+#: libs/ardour/audiosource.cc:308
+#, fuzzy
+msgid "cannot rename peakfile for %1 from %2 to %3 (%4)"
+msgstr "impossibile eliminare il peakfile %1 per %2 (%3)"
+
+#: libs/ardour/audiosource.cc:350
+#, fuzzy
+msgid "AudioSource: cannot stat peakfile \"%1\""
+msgstr "FileSource: impossibile avviare il peakfile per %1"
+
+#: libs/ardour/audiosource.cc:451
+msgid "cannot read sample data for unscaled peak computation"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:472 libs/ardour/audiosource.cc:543
+#: libs/ardour/audiosource.cc:787 libs/ardour/audiosource.cc:878
+#, fuzzy
+msgid "AudioSource: cannot open peakpath \"%1\" (%2)"
+msgstr "SndFileSource: impossibile accedere al file \"%1\" (%2)"
+
+#: libs/ardour/audiosource.cc:644
+#, fuzzy
+msgid "AudioSource[%1]: peak read - cannot read %2 samples at offset %3"
+msgstr "DiskStream %1: impossibile leggere %2 dalla playlista al frame %3"
+
+#: libs/ardour/audiosource.cc:798
+msgid "%1: could not write read raw data for peak computation (%2)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:823
+msgid "%1: could not write peak file data (%2)"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:65 libs/ardour/location.cc:345
+#: libs/ardour/tempo.cc:226
+msgid "initial"
+msgstr "iniziale"
+
+#: libs/ardour/automation_event.cc:232
+msgid "cleared"
+msgstr "pulito"
+
+#: libs/ardour/automation_event.cc:404
+msgid "added event"
+msgstr "aggiunto evento"
+
+#: libs/ardour/automation_event.cc:421
+msgid "removed event"
+msgstr "rimosso evento"
+
+#: libs/ardour/automation_event.cc:436
+msgid "removed multiple events"
+msgstr "rimossi molteplici eventi"
+
+#: libs/ardour/automation_event.cc:467 libs/ardour/automation_event.cc:498
+msgid "removed range"
+msgstr "rimosso intervallo"
+
+#: libs/ardour/automation_event.cc:528
+msgid "event range adjusted"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:550
+msgid "event adjusted"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:665 libs/ardour/automation_event.cc:770
+#: libs/ardour/panner.cc:1041
+msgid "programming error:"
+msgstr "errore di programmazione:"
+
+#: libs/ardour/automation_event.cc:1079
+msgid "cut/copy/clear"
+msgstr "taglia/copia/pulisci"
+
+#: libs/ardour/automation_event.cc:1112
+msgid "copy"
+msgstr "copia"
+
+#: libs/ardour/automation_event.cc:1180 libs/ardour/playlist.cc:939
+msgid "paste"
+msgstr "incolla"
+
+#: libs/ardour/automation_event.cc:1235
+msgid ""
+"automation list: no x-coordinate stored for control point (point ignored)"
+msgstr ""
+"lista automazione: nessuna coordinata X salvata per punto di controllo "
+"(ignorato)"
+
+#: libs/ardour/automation_event.cc:1241
+msgid ""
+"automation list: no y-coordinate stored for control point (point ignored)"
+msgstr ""
+"lista automazione: nessuna coordinata Y salvata per punto di controllo "
+"(ignorato)"
+
+#: libs/ardour/configuration.cc:80
+#, fuzzy
+msgid "loading system configuration file %1"
+msgstr ""
+"Ardour: impossibile leggere il file di configurazione di sistema \"%1\""
+
+#: libs/ardour/configuration.cc:83
+msgid "Ardour: cannot read system configuration file \"%1\""
+msgstr ""
+"Ardour: impossibile leggere il file di configurazione di sistema \"%1\""
+
+#: libs/ardour/configuration.cc:88
+msgid "Ardour: system configuration file \"%1\" not loaded successfully."
+msgstr ""
+"Ardour: il file di configurazione di sistema \"%1\" non stato caricato con "
+"successo"
+
+#: libs/ardour/configuration.cc:105
+#, fuzzy
+msgid "loading user configuration file %1"
+msgstr "Ardour: impossibile la lettura del file di configurazione \"%1\""
+
+#: libs/ardour/configuration.cc:108
+msgid "Ardour: cannot read configuration file \"%1\""
+msgstr "Ardour: impossibile la lettura del file di configurazione \"%1\""
+
+#: libs/ardour/configuration.cc:113
+#, fuzzy
+msgid "Ardour: user configuration file \"%1\" not loaded successfully."
+msgstr ""
+"Ardour: il file di configurazione \"%1\" non stato caricato con successo"
+
+#: libs/ardour/configuration.cc:137
+#, fuzzy
+msgid "Config file %1 not saved"
+msgstr "File di configurazione non salvato"
+
+#: libs/ardour/configuration.cc:210
+msgid "ill-formed MIDI port specification in ardour rcfile (ignored)"
+msgstr ""
+"porta MIDI mal configurata nel file di configurazione di ardour (ignorato)"
+
+#: libs/ardour/connection.cc:183
+msgid "Node for Connection has no \"name\" property"
+msgstr "Il nodo per la connessione non possiede l'attributo \"nome\""
+
+#: libs/ardour/connection.cc:191
+msgid "Node for Connection has no \"connections\" property"
+msgstr "Il nodo per la connessione non ha l'attributo \"connessioni\""
+
+#: libs/ardour/connection.cc:227 libs/ardour/io.cc:1902
+msgid "IO: badly formed string in XML node for inputs \"%1\""
+msgstr "IO: stringa malformata nel nodo XML per le entrate \"%1\""
+
+#: libs/ardour/connection.cc:232 libs/ardour/io.cc:1907
+msgid "bad input string in XML node \"%1\""
+msgstr "stringa malformata nel nodo XML \"%1\""
+
+#: libs/ardour/control_protocol_manager.cc:80
+msgid "control protocol name \"%1\" has no descriptor"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:85
+msgid "control protocol name \"%1\" could not be initialized"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:141
+msgid "Instantiating mandatory control protocol %1"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:175
+#, fuzzy
+msgid "Control protocol %1 not usable"
+msgstr "La porta MIDI \"%1\" non disponibile: nessun controllo MTC possibile"
+
+#: libs/ardour/control_protocol_manager.cc:187
+msgid "Control surface protocol discovered: \"%1\""
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:205
+#, fuzzy
+msgid "ControlProtocolManager: cannot load module \"%1\" (%2)"
+msgstr "LADSPA: impossibile caricare il modulo \"%1\" (%2)"
+
+#: libs/ardour/control_protocol_manager.cc:213
+#, fuzzy
+msgid "ControlProtocolManager: module \"%1\" has no descriptor function."
+msgstr "LADSPA: il modulo \"%1\" non ha alcuna funzione descriptor."
+
+#: libs/ardour/crossfade.cc:121
+msgid "Crossfade: no \"in\" region in state"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:128
+msgid "Crossfade: no \"in\" region %1 found in playlist %2"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:138
+msgid "Crossfade: no \"out\" region in state"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:145
+msgid "Crossfade: no \"out\" region %1 found in playlist %2"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:492
+#, fuzzy
+msgid "active changed"
+msgstr "livello cambiato"
+
+#: libs/ardour/crossfade.cc:741
+#, fuzzy
+msgid "old-style crossfade information - no position information"
+msgstr "il nodo XML per la Location non ha informazioni sull'inizio"
+
+#: libs/ardour/curve.cc:112 libs/ardour/globals.cc:340
+#: libs/ardour/insert.cc:454 libs/ardour/session.cc:2466
+#: libs/ardour/session.cc:2518
+msgid "programming error: "
+msgstr "errore di programmazione: "
+
+#: libs/ardour/cycle_timer.cc:37
+msgid "CycleTimer::get_mhz(): can't open /proc/cpuinfo"
+msgstr "CycleTimer::get_mhz(): impossibile accedere a /proc/cpuinfo"
+
+#: libs/ardour/cycle_timer.cc:49
+msgid "CycleTimer::get_mhz(): cannot locate cpu MHz in /proc/cpuinfo"
+msgstr ""
+"CycleTimer::get_mhz(): impossibile localizzare \"cpu MHz\" in /proc/cpuinfo"
+
+#: libs/ardour/cycle_timer.cc:72
+msgid "cannot locate cpu MHz in /proc/cpuinfo"
+msgstr "impossibile localizzare \"cpu MHz\" in /proc/cpuinfo"
+
+#: libs/ardour/destructive_filesource.cc:188
+msgid "DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"
+msgstr ""
+
+#: libs/ardour/destructive_filesource.cc:201
+#: libs/ardour/destructive_filesource.cc:243
+#: libs/ardour/destructive_filesource.cc:250
+msgid "DestructiveFileSource: \"%1\" bad write (%2)"
+msgstr ""
+
+#: libs/ardour/globals.cc:109
+msgid "no MIDI ports specified: no MMC or MTC control possible"
+msgstr "Nessuna porta MIDI specificata: impossibile alcun controllo MMC o MTC"
+
+#: libs/ardour/globals.cc:124
+msgid "MIDI port specifications for \"%1\" are not understandable."
+msgstr "Le specifiche per la porta MIDI \"%1\" non sono state capite"
+
+#: libs/ardour/globals.cc:137 libs/ardour/globals.cc:141
+#: libs/ardour/globals.cc:145
+msgid "default"
+msgstr ""
+
+#: libs/ardour/globals.cc:173
+msgid "No MMC control (MIDI port \"%1\" not available)"
+msgstr "La porta MIDI \"%1\" non disponibile: nessun controllo MTC possibile"
+
+#: libs/ardour/globals.cc:179
+msgid "No MTC support (MIDI port \"%1\" not available)"
+msgstr "La porta MIDI \"%1\" non disponibile: nessun controllo MTC possibile"
+
+#: libs/ardour/globals.cc:184
+#, fuzzy
+msgid "No MIDI parameter support (MIDI port \"%1\" not available)"
+msgstr "La porta MIDI \"%1\" non disponibile: nessun controllo MTC possibile"
+
+#: libs/ardour/import.cc:75
+msgid "Import: cannot open input sound file \"%1\""
+msgstr "Import: impossibile aprire il file audio di input \"%1\""
+
+#: libs/ardour/import.cc:80
+msgid "resampling audio"
+msgstr ""
+
+#: libs/ardour/import.cc:84
+msgid "Import: cannot open converted sound file \"%1\""
+msgstr "Import: impossibile aprire il file audio convertito \"%1\""
+
+#: libs/ardour/import.cc:89
+msgid "Import: error while resampling sound file \"%1\""
+msgstr "Import: errore nel resampling deil file audio \"%1\""
+
+#: libs/ardour/import.cc:148
+msgid "Session::import_audiofile: cannot open new file source for channel %1"
+msgstr ""
+"Session::import_audiofile: impossibile aprire il nuovo file per il canale %1"
+
+#: libs/ardour/import.cc:167
+msgid "converting audio"
+msgstr "conversione dell'audio"
+
+#: libs/ardour/import.cc:199
+msgid "building region"
+msgstr "costruzione della regione"
+
+#: libs/ardour/import.cc:201
+msgid "building regions"
+msgstr "costruzione delle regioni"
+
+#: libs/ardour/import.cc:325
+msgid "Import: could not open temp file: %1"
+msgstr "Import: impossibile aprire il file audio temporaneo \"%1\""
+
+#: libs/ardour/import.cc:334
+msgid "Import: src_new() failed : %1"
+msgstr ""
+
+#: libs/ardour/import.cc:362
+msgid "Import: %1"
+msgstr ""
+
+#: libs/ardour/insert.cc:644 libs/ardour/insert.cc:936
+msgid "XML node describing insert is missing the `type' field"
+msgstr "Il nodo XML descrivente l'insert manca del campo `type'"
+
+#: libs/ardour/insert.cc:653
+msgid "unknown plugin type %1 in plugin insert state"
+msgstr ""
+
+#: libs/ardour/insert.cc:665
+msgid "XML node describing insert is missing the `id' field"
+msgstr "Il nodo XML descrivente l'insert manca del campo `id'"
+
+#: libs/ardour/insert.cc:678
+msgid ""
+"Found a reference to a plugin (\"%1\") that is unknown.\n"
+"Perhaps it was removed or moved since it was last used."
+msgstr ""
+"Trovato un riferimento ad un plugin (\"%1\") sconosciuto.\n"
+"Forse stato rimosso o spostato dall'ultima volta che lo si e' usato"
+
+#: libs/ardour/insert.cc:716 libs/ardour/insert.cc:953
+msgid "XML node describing insert is missing a Redirect node"
+msgstr "Il nodo XML descrivente l'insert manca di un nodo Redirect"
+
+#: libs/ardour/insert.cc:721
+msgid "XML node describing a plugin insert is missing the `%1' information"
+msgstr "Il nodo XML descrivente un insert plugin manca della informazione `%1'"
+
+#: libs/ardour/insert.cc:745
+msgid "PluginInsert: Auto: no ladspa port number"
+msgstr ""
+
+#: libs/ardour/insert.cc:752
+msgid "PluginInsert: Auto: port id out of range"
+msgstr ""
+
+#: libs/ardour/insert.cc:768
+#, fuzzy
+msgid "XML node describing a port automation is missing the `%1' information"
+msgstr "Il nodo XML descrivente un insert LADSPA manca della informazione `%1'"
+
+#: libs/ardour/insert.cc:854
+msgid "PortInsert: cannot add input port"
+msgstr "PortInsert: impossibile aggiungere una porta d'entrata"
+
+#: libs/ardour/insert.cc:859
+msgid "PortInsert: cannot add output port"
+msgstr "PortInsert: impossibile aggiungere una porta d'uscita"
+
+#: libs/ardour/insert.cc:941
+msgid "non-port insert XML used for port plugin insert"
+msgstr "insert non-port XML usato per insert di plugin di porta"
+
+#: libs/ardour/io.cc:598
+msgid "IO: cannot disconnect input port %1 from %2"
+msgstr "IO: impossibile disconnettere la porta d'entrata %1 da %2"
+
+#: libs/ardour/io.cc:666
+msgid "IO: cannot disconnect output port %1 from %2"
+msgstr "IO: impossibile disconnettere la porta d'uscita %1 da %2"
+
+#: libs/ardour/io.cc:807 libs/ardour/io.cc:1151 libs/ardour/io.cc:1277
+#, c-format
+msgid "%s/out"
+msgstr ""
+
+#: libs/ardour/io.cc:809 libs/ardour/io.cc:1153 libs/ardour/io.cc:1279
+#: libs/ardour/io.cc:2849
+#, c-format
+msgid "%s/out %u"
+msgstr ""
+
+#: libs/ardour/io.cc:813 libs/ardour/io.cc:1158 libs/ardour/io.cc:1283
+msgid "IO: cannot register output port %1"
+msgstr "IO: impossibile registrare la porta %1"
+
+#: libs/ardour/io.cc:908 libs/ardour/io.cc:1011 libs/ardour/io.cc:1117
+#, c-format
+msgid "%s/in"
+msgstr ""
+
+#: libs/ardour/io.cc:910 libs/ardour/io.cc:1014 libs/ardour/io.cc:1120
+#: libs/ardour/io.cc:2819
+#, c-format
+msgid "%s/in %u"
+msgstr ""
+
+#: libs/ardour/io.cc:914 libs/ardour/io.cc:1020 libs/ardour/io.cc:1125
+msgid "IO: cannot register input port %1"
+msgstr "IO: impossibile registrare la porta %1"
+
+#: libs/ardour/io.cc:1541
+msgid "IO::connecting_became_legal() called without a pending state node"
+msgstr ""
+
+#: libs/ardour/io.cc:1564
+msgid "IO::ports_became_legal() called without a pending state node"
+msgstr ""
+
+#: libs/ardour/io.cc:1594
+msgid "incorrect XML node \"%1\" passed to IO object"
+msgstr ""
+
+#: libs/ardour/io.cc:1649
+msgid ""
+"MIDI gain control specification for %1 is incomplete, so it has been ignored"
+msgstr ""
+
+#: libs/ardour/io.cc:1739 libs/ardour/io.cc:1851
+msgid "Unknown connection \"%1\" listed for output of %2"
+msgstr ""
+
+#: libs/ardour/io.cc:1741 libs/ardour/io.cc:1853
+msgid "out 1"
+msgstr ""
+
+#: libs/ardour/io.cc:1742 libs/ardour/io.cc:1854
+msgid "No output connections available as a replacement"
+msgstr ""
+
+#: libs/ardour/io.cc:1746 libs/ardour/io.cc:1858
+msgid "Connection %1 was not available - \"out 1\" used instead"
+msgstr ""
+
+#: libs/ardour/io.cc:1760
+msgid "%1: cannot create I/O ports"
+msgstr ""
+
+#: libs/ardour/io.cc:1867
+msgid "improper output channel list in XML node (%1)"
+msgstr ""
+
+#: libs/ardour/io.cc:1952
+msgid "IO: badly formed string in XML node for outputs \"%1\""
+msgstr "IO: stringa mal formata nel nodo XML per le uscite \"%1\""
+
+#: libs/ardour/io.cc:1957
+msgid "IO: bad output string in XML node \"%1\""
+msgstr "IO: stringa mal formata nel nodo XML \"%1\""
+
+#: libs/ardour/io.cc:2525
+msgid "%1: could not open automation event file \"%2\""
+msgstr ""
+
+#: libs/ardour/io.cc:2564
+msgid "%1: cannot open automation event file \"%2\""
+msgstr ""
+
+#: libs/ardour/io.cc:2579
+msgid "badly formed version number in automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:2583
+msgid "no version information in automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:2588
+msgid "mismatched automation event file version (%1)"
+msgstr ""
+
+#: libs/ardour/io.cc:2596
+msgid "badly formatted automation event record at line %1 of %2 (ignored)"
+msgstr ""
+
+#: libs/ardour/io.cc:2616
+msgid "dubious automation event found (and ignored)"
+msgstr ""
+
+#: libs/ardour/io.cc:2620 libs/ardour/panner.cc:438
+#: libs/ardour/redirect.cc:148
+msgid "loaded from disk"
+msgstr ""
+
+#: libs/ardour/io.cc:2791
+msgid "automation write/touch"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:87
+msgid "LADSPA: module has no descriptor function."
+msgstr "LADSPA: il modulo non ha alcuna funzione descriptor."
+
+#: libs/ardour/ladspa_plugin.cc:92
+msgid "LADSPA: plugin has gone away since discovery!"
+msgstr "LADSPA: il plugin è stato rimosso"
+
+#: libs/ardour/ladspa_plugin.cc:99
+msgid "LADSPA: \"%1\" cannot be used, since it cannot do inplace processing"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:329
+msgid ""
+"illegal parameter number used with plugin \"%1\". This mayindicate a change "
+"in the plugin design, and presets may beinvalid"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:430
+msgid "Bad node sent to LadspaPlugin::set_state"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:443
+msgid "LADSPA: no ladspa port number"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:449
+msgid "LADSPA: no ladspa port data"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:498
+msgid ""
+"LADSPA LadspaPlugin MIDI control specification for port %1 is incomplete, so "
+"it has been ignored"
+msgstr ""
+
+#: libs/ardour/location.cc:269
+msgid "incorrect XML node passed to Location::set_state"
+msgstr ""
+
+#: libs/ardour/location.cc:276
+msgid "XML node for Location has no name information"
+msgstr "il nodo XML per la Location non ha informazioni sul nome"
+
+#: libs/ardour/location.cc:283
+msgid "XML node for Location has no start information"
+msgstr "il nodo XML per la Location non ha informazioni sull'inizio"
+
+#: libs/ardour/location.cc:294
+msgid "XML node for Location has no end information"
+msgstr "il nodo XML per la Location non ha informazioni sulla fine"
+
+#: libs/ardour/location.cc:303
+msgid "XML node for Location has no flags information"
+msgstr "il nodo XML per la Location non ha informazioni sui flags"
+
+#: libs/ardour/location.cc:391
+msgid "Locations: attempt to use unknown location as selected location"
+msgstr ""
+
+#: libs/ardour/location.cc:418 libs/ardour/playlist.cc:1187
+msgid "clear"
+msgstr "pulisci"
+
+#: libs/ardour/location.cc:443
+msgid "clear markers"
+msgstr ""
+
+#: libs/ardour/location.cc:471
+msgid "clear ranges"
+msgstr ""
+
+#: libs/ardour/location.cc:489
+msgid "add"
+msgstr ""
+
+#: libs/ardour/location.cc:527
+msgid "remove"
+msgstr "rimuovi"
+
+#: libs/ardour/location.cc:567
+msgid "incorrect XML mode passed to Locations::set_state"
+msgstr ""
+
+#: libs/ardour/mtc_slave.cc:196
+msgid "MTC Slave: atomic read of current time failed, sleeping!"
+msgstr ""
+
+#: libs/ardour/named_selection.cc:77
+msgid "Chunk %1 uses an unknown playlist \"%2\""
+msgstr "Lo spezzone %1 usa una playlist sconosciuta \"%2\""
+
+#: libs/ardour/named_selection.cc:80
+msgid "Chunk %1 contains misformed playlist information"
+msgstr "Lo spezzone %1 contiene informazioni sulla playlist mal formate"
+
+#: libs/ardour/panner.cc:256
+msgid "MIDI pan control specification is incomplete, so it has been ignored"
+msgstr ""
+
+#: libs/ardour/panner.cc:361
+msgid "automation write pass"
+msgstr ""
+
+#: libs/ardour/panner.cc:401
+#, c-format
+msgid "error writing pan automation file (%s)"
+msgstr "errore nello scrivere il file per l'automazione pan (%s)"
+
+#: libs/ardour/panner.cc:429
+msgid ""
+"badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"
+msgstr ""
+
+#: libs/ardour/panner.cc:944
+msgid "badly-formed positional data for Multi2dPanner - ignored"
+msgstr ""
+
+#: libs/ardour/panner.cc:1237
+msgid "cannot open pan automation file \"%1\" for saving (%s)"
+msgstr ""
+"impossibile aprire il file dell'automazione pan \"%1\" per salvare (%s)"
+
+#: libs/ardour/panner.cc:1273
+msgid "cannot open pan automation file %1 (%2)"
+msgstr "impossibile accedere al file dell'automazione pan %1 (%2)"
+
+#: libs/ardour/panner.cc:1286
+msgid "badly formed version number in pan automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/panner.cc:1290
+msgid ""
+"no version information in pan automation event file \"%1\" (first line = %2)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1296
+msgid "mismatched pan automation event file version (%1)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1310
+msgid "too many panner states found in pan automation file %1"
+msgstr ""
+
+#: libs/ardour/panner.cc:1451
+#, fuzzy
+msgid "Unknown panner plugin \"%1\" found in pan state - ignored"
+msgstr "Nodo sconosciuto \"%1\" trovato in Connections list dal file di stato"
+
+#: libs/ardour/panner.cc:1457
+#, fuzzy
+msgid "panner plugin node has no type information!"
+msgstr "il nodo XML per la Location non ha informazioni sulla fine"
+
+#: libs/ardour/playlist.cc:253
+msgid "playlist const copy constructor called"
+msgstr ""
+
+#: libs/ardour/playlist.cc:259
+msgid "playlist non-const copy constructor called"
+msgstr ""
+
+#: libs/ardour/playlist.cc:499
+msgid "add region"
+msgstr "aggiungi regione"
+
+#: libs/ardour/playlist.cc:554
+msgid "replace region"
+msgstr "sostituisci la regione"
+
+#: libs/ardour/playlist.cc:567
+msgid "remove region"
+msgstr "rimuovi la regione"
+
+#: libs/ardour/playlist.cc:614
+msgid "separate"
+msgstr "separa"
+
+#: libs/ardour/playlist.cc:878
+msgid "cut"
+msgstr "taglia"
+
+#: libs/ardour/playlist.cc:968
+msgid "duplicate"
+msgstr "duplica"
+
+#: libs/ardour/playlist.cc:1023
+msgid "split"
+msgstr "spezza"
+
+#: libs/ardour/playlist.cc:1100
+msgid "%1: bounds changed received for region (%2)not in playlist"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1361
+#, fuzzy
+msgid "Playlist: cannot create region from state file"
+msgstr "Playlist: impossibile creare la Regione dal file di stato"
+
+#: libs/ardour/playlist.cc:1721
+msgid "nudged"
+msgstr "spostato"
+
+#: libs/ardour/playlist_factory.cc:49 libs/ardour/playlist_factory.cc:64
+msgid ""
+"programming error: Playlist::createRegion called with unknown Region type"
+msgstr ""
+
+#: libs/ardour/playlist_factory.cc:86
+msgid ""
+"programming error: Playlist::copyPlaylist called with unknown Playlist type"
+msgstr ""
+
+#: libs/ardour/plugin.cc:328
+msgid "Could not locate HOME. Preset not saved."
+msgstr "impossibile localizzare HOME. Preset non salvato."
+
+#: libs/ardour/plugin.cc:338 libs/ardour/plugin.cc:344
+msgid "Could not create %1. Preset not saved. (%2)"
+msgstr "Impossibile creare %1 . Preset non salvato. (%2)"
+
+#: libs/ardour/plugin.cc:349
+msgid "Error saving presets file %1."
+msgstr "Errore nel salvare il file di preset %1."
+
+#: libs/ardour/plugin_manager.cc:194
+#, fuzzy
+msgid "Could not parse rdf file: %1"
+msgstr "IO: impossibile registrare la porta %1"
+
+#: libs/ardour/plugin_manager.cc:235
+msgid "LADSPA: cannot load module \"%1\" (%2)"
+msgstr "LADSPA: impossibile caricare il modulo \"%1\" (%2)"
+
+#: libs/ardour/plugin_manager.cc:242
+msgid "LADSPA: module \"%1\" has no descriptor function."
+msgstr "LADSPA: il modulo \"%1\" non ha alcuna funzione descriptor."
+
+#: libs/ardour/plugin_manager.cc:297
+#, fuzzy
+msgid "VST: cannot load module from \"%1\""
+msgstr "LADPSA: impossibile caricare il modulo da \"%1\""
+
+#: libs/ardour/plugin_manager.cc:302
+msgid "You asked ardour to not use any VST plugins"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:305
+msgid "This version of ardour has no support for VST plugins"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:312
+msgid "LADSPA: cannot load module from \"%1\""
+msgstr "LADPSA: impossibile caricare il modulo da \"%1\""
+
+#: libs/ardour/plugin_manager.cc:374 libs/ardour/plugin_manager.cc:386
+msgid "Unknown"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:464
+msgid ""
+"VST plugin %1 does not support processReplacing, and so cannot be used in "
+"ardour at this time"
+msgstr ""
+
+#: libs/ardour/recent_sessions.cc:44
+#, fuzzy
+msgid "cannot open recent session file %1 (%2)"
+msgstr "impossibile accedere al file di sessione recente %1 (%2)"
+
+#: libs/ardour/redirect.cc:77
+msgid "programming error: unknown Redirect type in Redirect::Clone!\n"
+msgstr ""
+
+#: libs/ardour/redirect.cc:102 libs/ardour/utils.cc:203
+msgid "pre"
+msgstr ""
+
+#: libs/ardour/redirect.cc:104 libs/ardour/utils.cc:206
+#, fuzzy
+msgid "post"
+msgstr "incolla"
+
+#: libs/ardour/redirect.cc:107
+msgid "Redirect: unknown placement string \"%1\" (ignored)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:125
+msgid "%1: cannot open %2 to load automation data (%3)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:154
+msgid "%1: cannot load automation data from %2"
+msgstr ""
+
+#: libs/ardour/redirect.cc:175
+msgid "%1: cannot open %2 to store automation data (%3)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:194 libs/ardour/redirect.cc:201
+msgid "%1: could not save automation state to %2"
+msgstr ""
+
+#: libs/ardour/redirect.cc:246
+msgid "Could not get state from Redirect (%1). Problem with save_automation"
+msgstr ""
+
+#: libs/ardour/redirect.cc:296
+msgid "incorrect XML node \"%1\" passed to Redirect object"
+msgstr ""
+
+#: libs/ardour/redirect.cc:318
+msgid "%1: Automation node has no path property"
+msgstr ""
+
+#: libs/ardour/redirect.cc:343
+msgid "XML node describing an IO is missing an IO node"
+msgstr ""
+
+#: libs/ardour/redirect.cc:348
+#, fuzzy
+msgid "XML node describing a redirect is missing the `active' field"
+msgstr "Il nodo XML descrivente l'insert manca del campo `type'"
+
+#: libs/ardour/redirect.cc:358
+msgid "XML node describing a redirect is missing the `placement' field"
+msgstr ""
+
+#: libs/ardour/redirect.cc:467
+#, fuzzy
+msgid "active_changed"
+msgstr "livello cambiato"
+
+#: libs/ardour/region.cc:885
+msgid "Session: XMLNode describing a Region is incomplete (no id)"
+msgstr ""
+
+#: libs/ardour/region.cc:892
+msgid "Session: XMLNode describing a Region is incomplete (no name)"
+msgstr ""
+
+#: libs/ardour/route.cc:79 libs/ardour/session.cc:1554
+#: libs/ardour/session.cc:1560 libs/ardour/session.cc:3093
+msgid "signal"
+msgstr "segnale"
+
+#: libs/ardour/route.cc:1430
+msgid "Could not get state of route. Problem with save_automation"
+msgstr ""
+
+#: libs/ardour/route.cc:1482
+msgid "Send construction failed"
+msgstr ""
+
+#: libs/ardour/route.cc:1504
+msgid "unknown Insert type \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/route.cc:1510
+msgid "Insert XML node has no type property"
+msgstr ""
+
+#: libs/ardour/route.cc:1515
+msgid "insert could not be created. Ignored."
+msgstr ""
+
+#: libs/ardour/route.cc:1533
+msgid "Bad node sent to Route::set_state() [%1]"
+msgstr ""
+
+#: libs/ardour/route.cc:1592
+msgid "Route %1: unknown edit group \"%2 in saved state (ignored)"
+msgstr ""
+
+#: libs/ardour/route.cc:1608 libs/ardour/route.cc:1612
+msgid "badly formed order key string in state file! [%1] ... ignored."
+msgstr ""
+
+#: libs/ardour/route.cc:1693 libs/ardour/route.cc:1820
+msgid "[control]"
+msgstr ""
+
+#: libs/ardour/route.cc:1713
+msgid "Route %1: unknown mix group \"%2 in saved state (ignored)"
+msgstr ""
+
+#: libs/ardour/route.cc:1742 libs/ardour/route.cc:1750
+msgid ""
+"MIDI mute control specification for %1 is incomplete, so it has been ignored"
+msgstr ""
+
+#: libs/ardour/send.cc:99
+msgid "XML node describing a send is missing a Redirect node"
+msgstr ""
+
+#: libs/ardour/session.cc:103
+#, fuzzy
+msgid "Could not resolve path: %1 (%2)"
+msgstr "impossibile controllare il percorso %1 (%2)"
+
+#: libs/ardour/session.cc:115
+msgid "cannot check session path %1 (%2)"
+msgstr "impossibile controllare il percorso %1 (%2)"
+
+#: libs/ardour/session.cc:145
+msgid "cannot check statefile %1 (%2)"
+msgstr "impossibile controllare il file di stato %1 (%2)"
+
+#: libs/ardour/session.cc:181
+msgid "%1 is not an Ardour snapshot file"
+msgstr "%1 non è un file di istantanea di Ardour"
+
+#: libs/ardour/session.cc:198
+msgid "cannot determine current working directory (%1)"
+msgstr "impossibile determinare la cartella di lavoro corrente (%1)"
+
+#: libs/ardour/session.cc:215
+msgid "unknown file type for session %1"
+msgstr "tipo di fle sconosciuto per la sessione %1"
+
+#: libs/ardour/session.cc:320
+msgid "monitor"
+msgstr ""
+
+#: libs/ardour/session.cc:327
+msgid "master"
+msgstr ""
+
+#: libs/ardour/session.cc:611
+msgid "could not setup Click I/O"
+msgstr "impossibile impostare entrata/uscita del click"
+
+#: libs/ardour/session.cc:632
+msgid "cannot setup Click I/O"
+msgstr "impossibile impostare entrata/uscita del click"
+
+#: libs/ardour/session.cc:654
+msgid "cannot create Auditioner: no auditioning of regions possible"
+msgstr "impossibile creare l'Auditioner"
+
+#: libs/ardour/session.cc:666
+#, c-format
+msgid "out %<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:678
+#, c-format
+msgid "in %<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:692
+#, c-format
+msgid "out %<PRIu32>+%<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:706
+#, c-format
+msgid "in %<PRIu32>+%<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:739
+#, fuzzy
+msgid "cannot setup master inputs"
+msgstr "IO: impossibile registrare la porta %1"
+
+#: libs/ardour/session.cc:747
+#, fuzzy
+msgid "cannot setup master outputs"
+msgstr "IO: impossibile registrare la porta %1"
+
+#: libs/ardour/session.cc:758
+msgid "Master Out"
+msgstr ""
+
+#: libs/ardour/session.cc:830
+#, fuzzy
+msgid "cannot setup control inputs"
+msgstr "impossibile impostare entrata/uscita del click"
+
+#: libs/ardour/session.cc:838
+#, fuzzy
+msgid "cannot set up master outputs"
+msgstr "IO: impossibile registrare la porta %1"
+
+#: libs/ardour/session.cc:1110
+msgid "Session: you can't use that location for auto punch (start <= end)"
+msgstr ""
+"Sessione: non si può usare quella location per l'auto punch (inizio <= fine)"
+
+#: libs/ardour/session.cc:1189
+msgid "Session: you can't use a mark for auto loop"
+msgstr "Sessione: non si può usare un marcatore per l'auto loop"
+
+#: libs/ardour/session.cc:1572
+msgid "feedback loop setup between %1 and %2"
+msgstr ""
+
+#: libs/ardour/session.cc:1724 libs/ardour/session.cc:1821
+msgid "cannot configure %1 in/%2 out configuration for new audio track"
+msgstr ""
+
+#: libs/ardour/session.cc:1780
+msgid "Session: could not create new audio track."
+msgstr "Sessione: impossibile creare una nuova traccia audio"
+
+#: libs/ardour/session.cc:1870
+msgid "Session: could not create new route."
+msgstr "Sessione: impossibile creare un nuovo route"
+
+#: libs/ardour/session.cc:2354
+msgid "cannot create new name for region \"%1\""
+msgstr "impossibile creare un nuovo nome per la regione \"%1\""
+
+#: libs/ardour/session.cc:2418
+msgid "too many regions with names like %1"
+msgstr "troppe regioni con nomi come %1"
+
+#: libs/ardour/session.cc:2883
+msgid "There are already %1 recordings for %2, which I consider too many."
+msgstr "Ci sono già %1 registrazioni per %2, che io considero troppe"
+
+#: libs/ardour/session.cc:3258
+msgid "programming error: unknown type of Insert created!"
+msgstr ""
+
+#: libs/ardour/session.cc:3264
+msgid "programming error: unknown type of Redirect created!"
+msgstr ""
+
+#: libs/ardour/session.cc:3287
+msgid "programming error: unknown type of Insert deleted!"
+msgstr ""
+
+#: libs/ardour/session.cc:3293
+msgid "programming error: unknown type of Redirect deleted!"
+msgstr ""
+
+#: libs/ardour/session.cc:3636
+msgid "too many bounced versions of playlist \"%1\""
+msgstr ""
+
+#: libs/ardour/session.cc:3649
+msgid "cannot create new audio file \"%1\" for %2"
+msgstr "impossibile creare un nuovo file audio \"%1\" per %2"
+
+#: libs/ardour/session_butler.cc:85 libs/ardour/session_butler.cc:90
+msgid "UI: cannot set O_NONBLOCK on butler request pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:95
+msgid "Session: could not create butler thread"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:189
+msgid "poll on butler request pipe failed (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:196
+msgid "Error on butler thread request pipe"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:238
+#, fuzzy
+msgid "Error reading from butler request pipe"
+msgstr "Errore nel leggere dalla porta MIDI %1"
+
+#: libs/ardour/session_butler.cc:275
+msgid "Butler read ahead failure on dstream %1"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:319
+msgid "Butler write-behind failure on dstream %1"
+msgstr ""
+
+#: libs/ardour/session_click.cc:158
+msgid "cannot open click soundfile %1 (%2)"
+msgstr "impossibile accedere al file audio per il click %1 (%2)"
+
+#: libs/ardour/session_click.cc:167
+msgid "cannot read data from click soundfile"
+msgstr "impossibile leggere dati dal file audio per il click"
+
+#: libs/ardour/session_click.cc:192
+msgid "cannot open click emphasis soundfile %1 (%2)"
+msgstr "impossibile accedere al file audio di enfasi per il click %1 (%2)"
+
+#: libs/ardour/session_click.cc:200
+msgid "cannot read data from click emphasis soundfile"
+msgstr "impossibile leggere dati dal file audio di enfasi per il click"
+
+#: libs/ardour/session_events.cc:161
+msgid "Session: cannot have two events of type %1 at the same frame (%2)."
+msgstr ""
+
+#: libs/ardour/session_events.cc:422
+msgid "Programming error: illegal event type in process_event (%1)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:63
+msgid "Export: no output file specified"
+msgstr "Esportazione: nessun file di output scpecificato"
+
+#: libs/ardour/session_export.cc:164 libs/ardour/session_export.cc:169
+msgid "illegal frame range in export specification"
+msgstr ""
+
+#: libs/ardour/session_export.cc:174
+msgid "Bad data width size. Report me!"
+msgstr ""
+
+#: libs/ardour/session_export.cc:204
+msgid "Export: cannot open output file \"%1\" (%2)"
+msgstr "Esportazione: impossibile accedere al fil di output \"%1\" (%2)"
+
+#: libs/ardour/session_export.cc:214
+msgid "cannot initialize sample rate conversion: %1"
+msgstr "impossibile avviare la conversione di campionatura: %1"
+
+#: libs/ardour/session_export.cc:316
+msgid "an error occured during sample rate conversion: %1"
+msgstr "c'è stato un errore durante la conversione di campionatura: %1"
+
+#: libs/ardour/session_export.cc:327
+msgid "warning, leftover frames overflowed, glitches might occur in output"
+msgstr ""
+
+#: libs/ardour/session_export.cc:418
+msgid "Export: could not write data to output file (%1)"
+msgstr "Esportazione: impossibile scrivere dati sul file di output (%1)"
+
+#: libs/ardour/session_export.cc:500
+msgid "%1: cannot seek to %2 for export"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:200
+msgid "Ardour is slaved to MTC - port cannot be reset"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:215
+msgid "unknown port %1 requested for MTC"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:541
+msgid "Error reading from MIDI port %1"
+msgstr "Errore nel leggere dalla porta MIDI %1"
+
+#: libs/ardour/session_midi.cc:914
+msgid "Session: could not send full MIDI time code"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:973
+msgid "Session: cannot send quarter-frame MTC message (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1081
+msgid "MMC: cannot send command %1%2%3"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1188
+msgid "UI: cannot set O_NONBLOCK on signal read pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1193
+msgid "UI: cannot set O_NONBLOCK on signal write pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1198
+msgid "Session: could not create transport thread"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1227
+msgid "cannot send signal to midi thread! (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1322
+msgid "MIDI thread poll failed (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1334
+msgid "Error on transport thread request pipe"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1361
+msgid "Error reading from transport request pipe"
+msgstr ""
+
+#: libs/ardour/session_process.cc:104
+msgid "Session: error in no roll for %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:101
+#, fuzzy
+msgid "Could not use path %1 (%s)"
+msgstr "impossibile controllare il percorso %1 (%2)"
+
+#: libs/ardour/session_state.cc:129
+msgid "end"
+msgstr ""
+
+#: libs/ardour/session_state.cc:130
+#, fuzzy
+msgid "start"
+msgstr "separa"
+
+#: libs/ardour/session_state.cc:502
+msgid "Session: cannot create session dir \"%1\" (%2)"
+msgstr "Sessione: impossibile creare la cartella per la sessione \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:513
+#, fuzzy
+msgid "Session: cannot create session peakfile dir \"%1\" (%2)"
+msgstr "Sessione: impossibile creare la cartella per la sessione \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:522
+msgid "Session: cannot create session sounds dir \"%1\" (%2)"
+msgstr ""
+"Sessione: impossibile creare la cartella sounds per la sessione \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:531
+#, fuzzy
+msgid "Session: cannot create session tape dir \"%1\" (%2)"
+msgstr "Sessione: impossibile creare la cartella per la sessione \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:540
+#, fuzzy
+msgid "Session: cannot create session dead sounds dir \"%1\" (%2)"
+msgstr ""
+"Sessione: impossibile creare la cartella sounds per la sessione \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:549
+msgid "Session: cannot create session automation dir \"%1\" (%2)"
+msgstr ""
+"Sessione: impossibile creare la cartella automation per la sessione \"%1\" (%"
+"2)"
+
+#: libs/ardour/session_state.cc:580
+#, fuzzy
+msgid "Could not open %1 for writing mix template"
+msgstr "Impossibile accedere a %1 per scrivere il modello di mixaggio"
+
+#: libs/ardour/session_state.cc:586
+#, fuzzy
+msgid "Could not open mix template %1 for reading"
+msgstr "Impossibile aprire il modello di mixaggio %1 per leggere"
+
+#: libs/ardour/session_state.cc:593
+msgid "Session already exists. Not overwriting"
+msgstr "La sessione esiste già. Non sovrascrivo"
+
+#: libs/ardour/session_state.cc:636
+msgid "Session: could not load diskstream via XML state"
+msgstr ""
+
+#: libs/ardour/session_state.cc:685
+msgid "could not backup old state file, current state not saved."
+msgstr ""
+"impossibile fare copia di sicurezza del file di stato, stato attuale non "
+"salvato"
+
+#: libs/ardour/session_state.cc:698
+#, fuzzy
+msgid "state could not be saved to %1"
+msgstr "stato non salvato"
+
+#: libs/ardour/session_state.cc:705
+#, fuzzy
+msgid "could not remove corrupt state file %1"
+msgstr "IO: impossibile registrare la porta %1"
+
+#: libs/ardour/session_state.cc:709
+#, fuzzy
+msgid "could not restore state file from backup %1"
+msgstr "Esportazione: impossibile scrivere dati sul file di output (%1)"
+
+#: libs/ardour/session_state.cc:778
+msgid "%1: session state information file \"%2\" doesn't exist!"
+msgstr ""
+"%1: il file di informazioni sullo stato della sessione \"%2\" non esiste!"
+
+#: libs/ardour/session_state.cc:789
+#, fuzzy
+msgid "Could not understand ardour file %1"
+msgstr "IO: impossibile registrare la porta %1"
+
+#: libs/ardour/session_state.cc:1493
+msgid "programming error: Session: incorrect XML node sent to set_state()"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1539
+msgid "Session: XML state has no options section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione option"
+
+#: libs/ardour/session_state.cc:1544
+msgid "Session: XML state has no sources section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione sources"
+
+#: libs/ardour/session_state.cc:1551
+msgid "Session: XML state has no Regions section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione Regions"
+
+#: libs/ardour/session_state.cc:1558
+msgid "Session: XML state has no playlists section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione playlist"
+
+#: libs/ardour/session_state.cc:1577
+msgid "Session: XML state has no diskstreams section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione diskstream"
+
+#: libs/ardour/session_state.cc:1584
+msgid "Session: XML state has no connections section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione connections"
+
+#: libs/ardour/session_state.cc:1591
+msgid "Session: XML state has no locations section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione locations"
+
+#: libs/ardour/session_state.cc:1624
+msgid "Session: XML state has no edit groups section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione edit groups"
+
+#: libs/ardour/session_state.cc:1631
+msgid "Session: XML state has no mix groups section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione mix groups"
+
+#: libs/ardour/session_state.cc:1638
+msgid "Session: XML state has no Tempo Map section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione Tempo Map"
+
+#: libs/ardour/session_state.cc:1645
+msgid "Session: XML state has no routes section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione routes"
+
+#: libs/ardour/session_state.cc:1652
+#, fuzzy
+msgid "Session: XML state has no click section"
+msgstr "Sessione: il file di stato XML non ha alcuna sezione click"
+
+#: libs/ardour/session_state.cc:1697
+msgid "Session: cannot create Route from XML description."
+msgstr "Sessione: impossibile creare Route dalla descrizione XML"
+
+#: libs/ardour/session_state.cc:1735
+msgid "Session: cannot create Region from XML description."
+msgstr "Sessione: impossibile creare regione dalla descrizione XML"
+
+#: libs/ardour/session_state.cc:1764
+#, fuzzy
+msgid "Session: XMLNode describing a AudioRegion is incomplete (no source)"
+msgstr ""
+"Sessione: il nodo XML descrivente una Regione è incompleto (nessun source)"
+
+#: libs/ardour/session_state.cc:1772 libs/ardour/session_state.cc:1792
+#, fuzzy
+msgid ""
+"Session: XMLNode describing a AudioRegion references an unknown source id =%1"
+msgstr ""
+"Sessione: il nodo XML descrivente una Regione fa riferimento ad un source "
+"con id sconosciuto =%1"
+
+#: libs/ardour/session_state.cc:1778 libs/ardour/session_state.cc:1798
+#, fuzzy
+msgid ""
+"Session: XMLNode describing a AudioRegion references a non-audio source id =%"
+"1"
+msgstr ""
+"Sessione: il nodo XML descrivente una Regione fa riferimento ad un source "
+"con id sconosciuto =%1"
+
+#: libs/ardour/session_state.cc:1868
+msgid "Session: cannot create Source from XML description."
+msgstr "Sessione: impossibile creare Source dalla descrizione XML"
+
+#: libs/ardour/session_state.cc:1889
+#, fuzzy
+msgid ""
+"Found a sound file that cannot be used by Ardour. Talk to the progammers."
+msgstr "E' stato trovato un file audio che non può essere usato da Ardour."
+
+#: libs/ardour/session_state.cc:1913
+msgid "Could not create mix templates directory \"%1\" (%2)"
+msgstr "Impossibile creare la cartella per i modelli di mixaggio \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:1927
+msgid "Template \"%1\" already exists - new version not created"
+msgstr "Il modello \"%1\" esiste già - non è stata creata una nuova versione"
+
+#: libs/ardour/session_state.cc:1934
+msgid "mix template not saved"
+msgstr "modello di mixaggio non salvato"
+
+#: libs/ardour/session_state.cc:1994
+msgid "cannot create session directory \"%1\"; ignored"
+msgstr "impossibile creare la cartella per la sessione %1; ignorato"
+
+#: libs/ardour/session_state.cc:2007
+msgid "cannot create sounds directory \"%1\"; ignored"
+msgstr "impossibile creare la cartella sounds \"%1\"; ignorato"
+
+#: libs/ardour/session_state.cc:2018
+#, fuzzy
+msgid "cannot create dead sounds directory \"%1\"; ignored"
+msgstr "impossibile creare la cartella sounds \"%1\"; ignorato"
+
+#: libs/ardour/session_state.cc:2029
+#, fuzzy
+msgid "cannot create peak file directory \"%1\"; ignored"
+msgstr "impossibile creare la cartella per la sessione %1; ignorato"
+
+#: libs/ardour/session_state.cc:2168 libs/ardour/session_state.cc:2189
+msgid "Session: cannot create Playlist from XML description."
+msgstr "Sessione: impossibile creare Playlist dalla descrizione XML"
+
+#: libs/ardour/session_state.cc:2228
+msgid "Session: cannot create Named Selection from XML description."
+msgstr "Sessione: impossibile creare Named Selection dalla descizione XML"
+
+#: libs/ardour/session_state.cc:2360
+msgid "Unknown node \"%1\" found in Connections list from state file"
+msgstr "Nodo sconosciuto \"%1\" trovato in Connections list dal file di stato"
+
+#: libs/ardour/session_state.cc:3197
+#, fuzzy
+msgid "cannot remove dead sound file %1 (%2)"
+msgstr "impossibile accedere al file audio per il click %1 (%2)"
+
+#: libs/ardour/session_time.cc:374
+msgid "Unknown JACK transport state %1 in sync callback"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:77
+msgid "tempoize: error creating name for new audio file based on %1"
+msgstr ""
+"tempoize: errore nel creare il nome per il nuovo file audio basato su %1"
+
+#: libs/ardour/session_timefx.cc:88
+msgid "tempoize: error creating new audio file %1 (%2)"
+msgstr "tempoize: errore nel creare un nuovo file audio %1 (%2)"
+
+#: libs/ardour/session_timefx.cc:114
+msgid "tempoize: error reading data from %1"
+msgstr "tempoize: errore nel leggere dati da %1"
+
+#: libs/ardour/session_timefx.cc:127 libs/ardour/session_timefx.cc:139
+msgid "error writing tempo-adjusted data to %1"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:145
+msgid "timefx code failure. please notify ardour-developers."
+msgstr ""
+
+#: libs/ardour/session_transport.cc:117
+msgid "Cannot loop - no loop range defined"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:479
+msgid ""
+"Seamless looping cannot be supported while Ardour is using JACK transport.\n"
+"Recommend changing the configured options"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:755
+msgid ""
+"Global varispeed cannot be supported while Ardour is connected to JACK "
+"transport control"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:955
+msgid "please stop the transport before adjusting slave settings"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:991
+msgid "No MTC port defined: MTC slaving is impossible."
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:15
+msgid "WAV"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:16
+msgid "AIFF"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:17
+msgid "raw (no header)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:18
+msgid "PAF (Ensoniq Paris)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:19
+msgid "AU (Sun/NeXT)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:20
+msgid "IRCAM"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:21
+msgid "W64 (64 bit WAV)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:26
+msgid ".wav"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:27
+msgid ".aiff"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:28
+msgid ".raw"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:29
+msgid ".paf"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:30
+msgid ".au"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:31
+msgid ".ircam"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:32
+msgid ".w64"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:47
+msgid "16 bit"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:48
+msgid "24 bit"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:49
+msgid "32 bit"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:50
+msgid "8 bit"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:51
+msgid "float"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:64
+msgid "Little-endian (Intel)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:65
+msgid "Big-endian (Mac)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:147
+msgid "FileSource: cannot get host information for BWF header (%1)"
+msgstr "FileSource: impossibile ottenere info sull'host dall'header BWF (%1)"
+
+#: libs/ardour/sndfilesource.cc:169
+msgid ""
+"cannot set broadcast info for audio file %1 (%2); dropping broadcast info "
+"for this file"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:220
+#, fuzzy
+msgid "SndFileSource: cannot open file \"%1\" for %2 (%3)"
+msgstr "SndFileSource: impossibile accedere al file \"%1\" (%2)"
+
+#: libs/ardour/sndfilesource.cc:226
+msgid ""
+"SndFileSource: file only contains %1 channels; %2 is invalid as a channel "
+"number"
+msgstr ""
+"SndFileSource: il file contiene solo %1 canali; %2 non è valido come numero "
+"di canale"
+
+#: libs/ardour/sndfilesource.cc:327
+msgid "SndFileSource: could not seek to frame %1 within %2 (%3)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:378
+#, fuzzy
+msgid "programming error: %1 %2"
+msgstr "errore di programmazione: %1"
+
+#: libs/ardour/sndfilesource.cc:487 libs/ardour/sndfilesource.cc:533
+msgid ""
+"cannot set broadcast info for audio file %1; Dropping broadcast info for "
+"this file"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:544
+msgid "%1: cannot seek to %2"
+msgstr ""
+
+#: libs/ardour/state_manager.cc:47
+#, fuzzy
+msgid "cleared history"
+msgstr "pulito"
+
+#: libs/ardour/state_manager.cc:60
+msgid ""
+"programming error: illegal state ID (%1) passed to StateManager::set_state() "
+"(range = 0-%2)"
+msgstr ""
+
+#: libs/ardour/stateful.cc:102
+#, fuzzy
+msgid "Error: could not write %1"
+msgstr "Esportazione: impossibile scrivere dati sul file di output (%1)"
+
+#: libs/ardour/stateful.cc:116
+msgid "Could not understand XML file %1"
+msgstr ""
+
+#: libs/ardour/tempo.cc:67
+msgid "TempoSection XML node has no \"start\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:75
+msgid "TempoSection XML node has an illegal \"start\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:82
+msgid "TempoSection XML node has no \"beats-per-minute\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:87
+msgid "TempoSection XML node has an illegal \"beats_per_minute\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:92
+msgid "TempoSection XML node has no \"movable\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:131
+msgid "MeterSection XML node has no \"start\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:139
+msgid "MeterSection XML node has an illegal \"start\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:146
+msgid "MeterSection XML node has no \"beats-per-bar\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:151
+msgid "MeterSection XML node has an illegal \"beats-per-bar\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:156
+msgid "MeterSection XML node has no \"note-type\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:161
+msgid "MeterSection XML node has an illegal \"note-type\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:166
+msgid "MeterSection XML node has no \"movable\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:259
+msgid "move metric"
+msgstr ""
+
+#: libs/ardour/tempo.cc:330
+msgid "metric removed"
+msgstr ""
+
+#: libs/ardour/tempo.cc:373
+#, fuzzy
+msgid "add tempo"
+msgstr "aggiungi regione"
+
+#: libs/ardour/tempo.cc:402
+#, fuzzy
+msgid "replace tempo"
+msgstr "sostituisci la regione"
+
+#: libs/ardour/tempo.cc:435
+msgid "add meter"
+msgstr ""
+
+#: libs/ardour/tempo.cc:463
+#, fuzzy
+msgid "replaced meter"
+msgstr "sostituisci la regione"
+
+#: libs/ardour/tempo.cc:483 libs/ardour/tempo.cc:499
+msgid "programming error: no tempo section in tempo map!"
+msgstr ""
+
+#: libs/ardour/tempo.cc:538
+msgid "programming error: unhandled MetricSection type"
+msgstr ""
+
+#: libs/ardour/tempo.cc:1226 libs/ardour/tempo.cc:1238
+msgid "Tempo map: could not set new state, restoring old one."
+msgstr ""
+
+#: libs/ardour/tempo.cc:1262
+msgid "load XML data"
+msgstr ""
+
+#: libs/ardour/utils.cc:246
+#, fuzzy
+msgid "illegal or badly-formed string used for path (%1)"
+msgstr "il percorso indicato per il RAID è non valido o malformato"
+
+#: libs/ardour/utils.cc:251
+#, fuzzy
+msgid "path (%1) is ambiguous"
+msgstr "il percorso indicato per la ricerca RAID è ambiguo"
+
+#: libs/ardour/vst_plugin.cc:187
+#, fuzzy
+msgid "cannot create VST chunk directory: %1"
+msgstr "impossibile creare la cartella sounds \"%1\"; ignorato"
+
+#: libs/ardour/vst_plugin.cc:195
+#, fuzzy
+msgid "cannot check VST chunk directory: %1"
+msgstr "impossibile determinare la cartella di lavoro corrente (%1)"
+
+#: libs/ardour/vst_plugin.cc:202
+msgid "%1 exists but is not a directory"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:240
+msgid "Bad node sent to VSTPlugin::set_state"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:343 libs/ardour/vst_plugin.cc:354
+msgid "no support for presets using chunks at this time"
+msgstr ""
+
+#: libs/ardour/coreaudiosource.cc:97
+#, fuzzy
+msgid ""
+"CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel "
+"number"
+msgstr ""
+"SndFileSource: il file contiene solo %1 canali; %2 non è valido come numero "
+"di canale"
+
+#: libs/ardour/coreaudiosource.cc:162
+msgid "CoreAudioSource: could not seek to frame %1 within %2 (%3)"
+msgstr ""
+
+#~ msgid "FileSource: \"%1\" not found when searching %2 using %3"
+#~ msgstr "FileSource: \"%1\" non trovato nel cercare %2 utilizzando %3"
+
+#~ msgid "FileSource: could not open \"%1\": (%2)"
+#~ msgstr "FileSource: impossibile aprire \"%1\": (%2)"
+
+#~ msgid "FileSource: cannot write header in %1"
+#~ msgstr "FileSource: impossibile scrivere header in %1"
+
+#~ msgid "FileSource: cannot locate chunks in %1"
+#~ msgstr "FileSource: impossibile trovare spezzoni in %1"
+
+#~ msgid "FileSource: cannot read header in %1"
+#~ msgstr "FileSource: impossibile leggere header in %1"
+
+#~ msgid "FileSource: cannot check header in %1"
+#~ msgstr "FileSource: impossibile controllare header in %1"
+
+#, fuzzy
+#~ msgid "FileSource: cannot initialize peakfile for %1 as %2"
+#~ msgstr "FileSource: impossibile avviare il peakfile per %1"
+
+#~ msgid "FileSource: cannot seek to end of file"
+#~ msgstr "FileSource: impossibile cercare fino alla fine del file"
+
+#~ msgid "FileSource: cannot read RIFF/WAVE chunk from file"
+#~ msgstr "FileSource: impossibile accedere allo spezzone RIFF/WAVE dal file"
+
+#~ msgid "FileSource %1: not a RIFF/WAVE file"
+#~ msgstr "FileSource %1: il file non un file RIFF/WAVE"
+
+#~ msgid "FileSource: can't read a chunk"
+#~ msgstr "FileSource: impossibile accedere ad uno spezzone"
+
+#~ msgid "FileSource: cannot get user information for BWF header (%1)"
+#~ msgstr "FileSource: impossibile otterene info utente dall'header BWF (%1)"
+
+#~ msgid "FileSource[%1]: cannot update data size: %2"
+#~ msgstr "FileSource[%1]: impossibile aggiornare la dimensione dei dati: %2"
+
+#~ msgid "FileSource: can't find RIFF chunk info"
+#~ msgstr "FileSource: impossibile trovare info sullo spezzone RIFF"
+
+#, fuzzy
+#~ msgid "FileSource: can't find RIFX chunk info"
+#~ msgstr "FileSource: impossibile trovare info sullo spezzone RIFF"
+
+#~ msgid "FileSource: can't read RIFF chunk"
+#~ msgstr "FileSource: impossibile accedere allo spezzone RIFF"
+
+#~ msgid "FileSource: can't find format chunk info"
+#~ msgstr "FileSource: impossibile trovare info sul formato dello spezzone"
+
+#~ msgid "FileSource: can't read format chunk"
+#~ msgstr "FileSource: impossibile leggere il formato dello spezzone"
+
+#~ msgid "FileSource: can't find data chunk info"
+#~ msgstr "FileSource: impossibile trovare info sui dati dello spezzone"
+
+#~ msgid "FileSource: can't read data chunk"
+#~ msgstr "FileSource: impossibile leggere dati dello spezzone"
+
+#~ msgid ""
+#~ "FileSource: cannot read Broadcast Wave data from existing audio file \"%1"
+#~ "\" (%2)"
+#~ msgstr ""
+#~ "FileSource: impossibile leggere dati Broadcast Wave dal file audio "
+#~ "esistente \"%1\" (%2) "
+
+#~ msgid ""
+#~ "FileSource: cannot read Broadcast Wave coding history from audio file \"%1"
+#~ "\" (%2)"
+#~ msgstr ""
+#~ "FileSource: impossibile leggere lo storico del Broadcast Wave dal file "
+#~ "audio \"%1\" (%2)"
+
+#, fuzzy
+#~ msgid ""
+#~ "FileSource \"%1\" does not use valid sample format.\n"
+#~ "This is probably a programming error."
+#~ msgstr ""
+#~ "FileSource \"%1\" non usa il formato floating point.\n"
+#~ "Questo probabilmente un errore di programmazione."
+
+#~ msgid "FileSource \"%1\" has no \"data\" chunk"
+#~ msgstr "FileSource \"%1\" non ha uno spezzone di \"dati\""
+
+#~ msgid ""
+#~ "%1: data length in header (%2) differs from implicit size in file (%3)"
+#~ msgstr ""
+#~ "%1: la lunghezza dei dati nell'header (%2) diversa dalla dimensione "
+#~ "implicita nel file (%3)"
+
+#~ msgid "\"%1\" has a sample rate of %2 instead of %3 as used by this session"
+#~ msgstr ""
+#~ "\"%1\" ha una sample rate di %2 anzicch di %3 come il resto della sessione"
+
+#~ msgid "FileSource: cannot write WAVE chunk: %1"
+#~ msgstr "FileSource: impossibile scrivere lo spezzone WAVE: %1"
+
+#~ msgid "FileSource: cannot write format chunk: %1"
+#~ msgstr "FileSource: impossibile scrivere il formato dello spezzone: %1"
+
+#, fuzzy
+#~ msgid "cannot create feedback request pipe (%1)"
+#~ msgstr "Errore nel leggere dalla porta MIDI %1"
+
+#, fuzzy
+#~ msgid "Error on feedback thread request pipe"
+#~ msgstr "Errore nel leggere dalla porta MIDI %1"
+
+#, fuzzy
+#~ msgid "Error reading from feedback request pipe"
+#~ msgstr "Errore nel leggere dalla porta MIDI %1"
+
+#~ msgid "could not create crossfade object in playlist %1"
+#~ msgstr "impossibile creare smorzamento incrociato nella playlist %1"
+
+#, fuzzy
+#~ msgid "Could not find a template called %1 in %2"
+#~ msgstr "Impossibile aprire il modello di mixaggio %1 per leggere"
diff --git a/libs/ardour/po/pl_PL.po b/libs/ardour/po/pl_PL.po
new file mode 100644
index 0000000000..3a39989f02
--- /dev/null
+++ b/libs/ardour/po/pl_PL.po
@@ -0,0 +1,2063 @@
+# translation of libardour2.po to Polish
+# Copyright (C) YEAR "Paul Davis"
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Piotr Zaryk <pzaryk@gmail.com>, 2008.
+msgid ""
+msgstr ""
+"Project-Id-Version: libardour2\n"
+"Report-Msgid-Bugs-To: Piotr Zaryk <pzaryk@gmail.com>\n"
+"POT-Creation-Date: 2008-04-03 16:16+0200\n"
+"PO-Revision-Date: 2008-04-10 10:51+0100\n"
+"Last-Translator: Piotr Zaryk <pzaryk@gmail.com>\n"
+"Language-Team: Polish <pl@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: libs/ardour/audioanalyser.cc:26
+msgid "cannot load VAMP plugin \"%1\""
+msgstr ""
+
+#: libs/ardour/audioanalyser.cc:46
+msgid "VAMP Plugin \"%1\" could not be loaded"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:249
+msgid "AudioDiskstream: Playlist \"%1\" isn't an audio playlist"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:302
+msgid "AudioDiskstream %1: there is no existing playlist to make a copy of!"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:937
+#: libs/ardour/audio_diskstream.cc:948
+msgid "AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1096
+msgid "AudioDiskstream %1: cannot read %2 from playlist at frame %3"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1138
+msgid "AudioDiskstream %1: cannot read xfade samples %2 from playlist at frame %3"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1491
+#: libs/ardour/audio_diskstream.cc:1508
+msgid "AudioDiskstream %1: cannot write to disk"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1553
+msgid "AudioDiskstream \"%1\": cannot flush captured data to disk!"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1641
+msgid "%1: could not create region for complete audio file"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1666
+msgid "AudioDiskstream: could not create region for captured audio!"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1774
+msgid "programmer error: %1"
+msgstr "błąd programisty: %1"
+
+#: libs/ardour/audio_diskstream.cc:2051
+msgid "AudioDiskstream: channel %1 out of range"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2070
+msgid "%1:%2 new capture file not initialized correctly"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2322
+msgid "%1: cannot restore pending capture source file %2"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2344
+msgid "%1: incorrect number of pending sources listed - ignoring them all"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2359
+msgid "%1: cannot create whole-file region from pending capture sources"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2371
+msgid "%1: cannot create region from pending capture sources"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:114
+msgid "Connect session to engine"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:498
+msgid "a port with this name already exists: check for duplicated track/bus names"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:500
+msgid "unknown error"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:503
+msgid "AudioEngine: cannot register port \"%1\": %2"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:511
+msgid "register input port called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:545
+msgid "register output port called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:625
+msgid "connect called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:641
+msgid "AudioEngine: connection already exists: %1 (%2) to %3 (%4)"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:645
+msgid "AudioEngine: cannot connect %1 (%2) to %3 (%4)"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:658
+#: libs/ardour/audioengine.cc:687
+msgid "disconnect called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:745
+msgid "get_port_by_name() called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:789
+msgid "get_ports called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:911
+msgid "get_nth_physical called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:939
+msgid "get_port_total_latency() called with no JACK client connection"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:945
+msgid "get_port_total_latency() called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:1134
+msgid "failed to connect to JACK"
+msgstr "nie udało się połączyć z JACK"
+
+#: libs/ardour/audioengine.cc:1152
+msgid "could not reregister %1"
+msgstr "nie można było zarejestrować %1"
+
+#: libs/ardour/audioengine.cc:1211
+msgid "could not reconnect %1 and %2 (err = %3)"
+msgstr "nie można było połączyć ponownie %1 i %2 (błąd = %3)"
+
+#: libs/ardour/audiofilesource.cc:408
+#: libs/ardour/session_state.cc:2881
+msgid "there are already 1000 files with names like %1; versioning discontinued"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:422
+#: libs/ardour/session_state.cc:2895
+msgid "cannot rename audio file source from %1 to %2 (%3)"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:429
+#: libs/ardour/session_state.cc:2909
+msgid "cannot remove peakfile %1 for %2 (%3)"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:465
+msgid "FileSource: search path not set"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:537
+msgid ""
+"FileSource: \"%1\" is ambigous when searching %2\n"
+"\t"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:543
+msgid "Filesource: cannot find required file (%1): while searching %2"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:583
+msgid "Filesource: cannot find required file (%1): %2"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:588
+msgid "Filesource: cannot check for existing file (%1): %2"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:651
+#: libs/ardour/audiosource.cc:876
+#: libs/ardour/automation_event.cc:636
+#: libs/ardour/insert.cc:547
+#: libs/ardour/session.cc:2172
+#: libs/ardour/session.cc:3576
+#: libs/ardour/session.cc:3615
+#: libs/ardour/session_command.cc:432
+#: libs/ardour/sndfilesource.cc:111
+msgid "programming error: %1"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:657
+msgid "Programming error! Ardour tried to rename a file over another file! It's safe to continue working, but please report this to the developers."
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:662
+msgid "cannot rename audio file %1 to %2"
+msgstr ""
+
+#: libs/ardour/audiofilter.cc:43
+msgid ""
+"This is an old Ardour session that does not have\n"
+"sufficient information for rendered FX"
+msgstr ""
+
+#: libs/ardour/audiofilter.cc:66
+msgid "audiofilter: error creating name for new audio file based on %1"
+msgstr ""
+
+#: libs/ardour/audiofilter.cc:77
+msgid "audiofilter: error creating new audio file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:57
+msgid "Could not open %1. Audio Library not saved"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:226
+#: libs/ardour/audio_playlist.cc:615
+msgid "programming error: non-audio Region passed to remove_overlap in audio playlist"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:356
+msgid "programming error: non-audio Region tested for overlap in audio playlist"
+msgstr ""
+
+#: libs/ardour/audioregion.cc:1596
+msgid ""
+"You have requested an operation that requires audio analysis.\n"
+"\n"
+"You currently have \"auto-analyse-audio\" disabled, which means\n"
+"that transient data must be generated every time it is required.\n"
+"\n"
+"If you are doing work that will require transient data on a\n"
+"regular basis, you should probably enable \"auto-analyse-audio\"\n"
+"then quit ardour and restart."
+msgstr ""
+
+#: libs/ardour/audiosource.cc:178
+msgid "cannot rename peakfile for %1 from %2 to %3 (%4)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:205
+msgid "AudioSource: cannot stat peakfile \"%1\""
+msgstr ""
+
+#: libs/ardour/audiosource.cc:329
+msgid "cannot read sample data for unscaled peak computation"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:349
+msgid "AudioSource: cannot open peakpath (a) \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:423
+msgid "AudioSource: cannot open peakpath (b) \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:537
+msgid "AudioSource[%1]: peak read - cannot read %2 samples at offset %3 of %4 (%5)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:624
+msgid "%1: could not write read raw data for peak computation (%2)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:670
+msgid "AudioSource: cannot open peakpath (c) \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:736
+#: libs/ardour/audiosource.cc:848
+msgid "%1: could not write peak file data (%2)"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:140
+#: libs/ardour/io.cc:1797
+#: libs/ardour/io.cc:1863
+msgid "Unknown connection \"%1\" listed for input of %2"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:145
+msgid "in 1+2"
+msgstr "wejście 1+2"
+
+#: libs/ardour/audio_track.cc:147
+#: libs/ardour/io.cc:1799
+#: libs/ardour/io.cc:1865
+msgid "in 1"
+msgstr "wejście 1"
+
+#: libs/ardour/audio_track.cc:151
+#: libs/ardour/io.cc:1800
+#: libs/ardour/io.cc:1866
+msgid "No input connections available as a replacement"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:155
+msgid "Connection %1 was not available - \"%2\" used instead"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:164
+#: libs/ardour/io.cc:1879
+msgid "improper input channel list in XML node (%1)"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:205
+#: libs/ardour/audio_track.cc:218
+msgid "AudioTrack: audio diskstream \"%1\" not known by session"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:260
+msgid "programming error: AudioTrack given state without diskstream!"
+msgstr ""
+
+#: libs/ardour/auditioner.cc:56
+msgid "no outputs available for auditioner - manual connection required"
+msgstr ""
+
+#: libs/ardour/auditioner.cc:127
+msgid "Auditioning of non-audio regions not yet supported"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:782
+#: libs/ardour/panner.cc:833
+msgid "programming error:"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1305
+msgid "automation list: cannot load coordinates from XML, all points ignored"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1351
+msgid "automation list: no x-coordinate stored for control point (point ignored)"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1357
+msgid "automation list: no y-coordinate stored for control point (point ignored)"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1371
+msgid "AutomationList: passed XML node called %1, not \"AutomationList\" - ignored"
+msgstr ""
+
+#: libs/ardour/configuration.cc:97
+msgid "loading system configuration file %1"
+msgstr ""
+
+#: libs/ardour/configuration.cc:100
+msgid "Ardour: cannot read system configuration file \"%1\""
+msgstr ""
+
+#: libs/ardour/configuration.cc:107
+msgid "Ardour: system configuration file \"%1\" not loaded successfully."
+msgstr ""
+
+#: libs/ardour/configuration.cc:111
+msgid "your system Ardour configuration file is empty. This probably means that there as an error installing Ardour"
+msgstr ""
+
+#: libs/ardour/configuration.cc:131
+msgid "loading user configuration file %1"
+msgstr ""
+
+#: libs/ardour/configuration.cc:134
+msgid "Ardour: cannot read configuration file \"%1\""
+msgstr ""
+
+#: libs/ardour/configuration.cc:141
+msgid "Ardour: user configuration file \"%1\" not loaded successfully."
+msgstr ""
+
+#: libs/ardour/configuration.cc:145
+msgid "your Ardour configuration file is empty. This is not normal."
+msgstr ""
+
+#: libs/ardour/configuration.cc:164
+msgid "Config file %1 not saved"
+msgstr "Plik konfiguracji %1 nie zapisany"
+
+#: libs/ardour/configuration.cc:253
+msgid "ill-formed MIDI port specification in ardour rcfile (ignored)"
+msgstr ""
+
+#: libs/ardour/connection.cc:182
+msgid "Node for Connection has no \"name\" property"
+msgstr ""
+
+#: libs/ardour/connection.cc:190
+msgid "Node for Connection has no \"connections\" property"
+msgstr ""
+
+#: libs/ardour/connection.cc:226
+#: libs/ardour/io.cc:1939
+msgid "IO: badly formed string in XML node for inputs \"%1\""
+msgstr ""
+
+#: libs/ardour/connection.cc:231
+#: libs/ardour/io.cc:1944
+msgid "bad input string in XML node \"%1\""
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:117
+msgid "control protocol name \"%1\" has no descriptor"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:122
+msgid "control protocol name \"%1\" could not be initialized"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:189
+msgid "Instantiating mandatory control protocol %1"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:201
+msgid "looking for control protocols in %1"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:223
+msgid "Control protocol %1 not usable"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:237
+msgid "Control surface protocol discovered: \"%1\""
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:255
+msgid "ControlProtocolManager: cannot load module \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:263
+msgid "ControlProtocolManager: module \"%1\" has no descriptor function."
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:380
+msgid "control protocol XML node has no name property. Ignored."
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:387
+msgid "control protocol \"%1\" is not known. Ignored"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:124
+msgid "Crossfade: no \"in\" region in state"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:131
+msgid "Crossfade: no \"in\" region %1 found in playlist %2"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:141
+msgid "Crossfade: no \"out\" region in state"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:148
+msgid "Crossfade: no \"out\" region %1 found in playlist %2"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:684
+msgid "old-style crossfade information - no position information"
+msgstr ""
+
+#: libs/ardour/curve.cc:116
+#: libs/ardour/globals.cc:394
+#: libs/ardour/insert.cc:461
+#: libs/ardour/session.cc:2655
+#: libs/ardour/session.cc:2741
+msgid "programming error: "
+msgstr ""
+
+#: libs/ardour/cycle_timer.cc:36
+msgid "CycleTimer::get_mhz(): can't open /proc/cpuinfo"
+msgstr ""
+
+#: libs/ardour/cycle_timer.cc:48
+msgid "CycleTimer::get_mhz(): cannot locate cpu MHz in /proc/cpuinfo"
+msgstr ""
+
+#: libs/ardour/cycle_timer.cc:71
+msgid "cannot locate cpu MHz in /proc/cpuinfo"
+msgstr "nie można zlokalizować taktowania CPU w /proc/cpuinfo"
+
+#: libs/ardour/diskstream.cc:253
+msgid "Location \"%1\" not valid for track loop (start >= end)"
+msgstr ""
+
+#: libs/ardour/globals.cc:112
+msgid "Starting OSC"
+msgstr ""
+
+#: libs/ardour/globals.cc:124
+msgid "no MIDI ports specified: no MMC or MTC control possible"
+msgstr ""
+
+#: libs/ardour/globals.cc:128
+msgid "Configuring MIDI ports"
+msgstr ""
+
+#: libs/ardour/globals.cc:143
+#: libs/ardour/globals.cc:147
+#: libs/ardour/globals.cc:151
+msgid "default"
+msgstr "domyślnie"
+
+#: libs/ardour/globals.cc:181
+msgid "No MMC control (MIDI port \"%1\" not available)"
+msgstr ""
+
+#: libs/ardour/globals.cc:187
+msgid "No MTC support (MIDI port \"%1\" not available)"
+msgstr ""
+
+#: libs/ardour/globals.cc:192
+msgid "No MIDI parameter support (MIDI port \"%1\" not available)"
+msgstr ""
+
+#: libs/ardour/globals.cc:273
+msgid "Could not set system open files limit to \"unlimited\""
+msgstr ""
+
+#: libs/ardour/globals.cc:275
+msgid "Could not set system open files limit to %1"
+msgstr ""
+
+#: libs/ardour/globals.cc:279
+msgid "Removed open file count limit. Excellent!"
+msgstr ""
+
+#: libs/ardour/globals.cc:281
+msgid "Ardour will be limited to %1 open files"
+msgstr ""
+
+#: libs/ardour/globals.cc:285
+msgid "Could not get system open files limit (%1)"
+msgstr ""
+
+#: libs/ardour/globals.cc:304
+msgid "Loading configuration"
+msgstr ""
+
+#: libs/ardour/import.cc:179
+msgid "Could not find a source for %1 even though we are updating this file!"
+msgstr ""
+
+#: libs/ardour/import.cc:208
+msgid "Unable to create file %1 during import"
+msgstr ""
+
+#: libs/ardour/import.cc:225
+msgid ""
+"converting %1\n"
+"(resample from %2KHz to %3KHz)\n"
+"(%4 of %5)"
+msgstr ""
+
+#: libs/ardour/import.cc:232
+msgid ""
+"converting %1\n"
+"(%2 of %3)"
+msgstr ""
+
+#: libs/ardour/import.cc:318
+msgid "Import: cannot open input sound file \"%1\""
+msgstr ""
+
+#: libs/ardour/insert.cc:680
+#: libs/ardour/insert.cc:1005
+msgid "XML node describing insert is missing the `type' field"
+msgstr ""
+
+#: libs/ardour/insert.cc:693
+msgid "unknown plugin type %1 in plugin insert state"
+msgstr ""
+
+#: libs/ardour/insert.cc:713
+msgid "Plugin has no unique ID field"
+msgstr ""
+
+#: libs/ardour/insert.cc:723
+msgid ""
+"Found a reference to a plugin (\"%1\") that is unknown.\n"
+"Perhaps it was removed or moved since it was last used."
+msgstr ""
+
+#: libs/ardour/insert.cc:754
+msgid "XML node describing a plugin insert is missing the `%1' information"
+msgstr ""
+
+#: libs/ardour/insert.cc:766
+#: libs/ardour/insert.cc:1033
+msgid "XML node describing insert is missing a Redirect node"
+msgstr ""
+
+#: libs/ardour/insert.cc:818
+msgid "PluginInsert: Auto: no ladspa port number"
+msgstr ""
+
+#: libs/ardour/insert.cc:825
+msgid "PluginInsert: Auto: port id out of range"
+msgstr ""
+
+#: libs/ardour/insert.cc:856
+msgid "XML node describing a port automation is missing the `%1' information"
+msgstr ""
+
+#: libs/ardour/insert.cc:911
+#: libs/ardour/insert.cc:919
+msgid "insert %1"
+msgstr ""
+
+#: libs/ardour/insert.cc:1010
+msgid "non-port insert XML used for port plugin insert"
+msgstr ""
+
+#: libs/ardour/io.cc:638
+msgid "IO: cannot disconnect input port %1 from %2"
+msgstr ""
+
+#: libs/ardour/io.cc:706
+msgid "IO: cannot disconnect output port %1 from %2"
+msgstr ""
+
+#: libs/ardour/io.cc:860
+#: libs/ardour/io.cc:1189
+#: libs/ardour/io.cc:1308
+msgid "IO: cannot register output port %1"
+msgstr ""
+
+#: libs/ardour/io.cc:967
+#: libs/ardour/io.cc:1066
+#: libs/ardour/io.cc:1164
+msgid "IO: cannot register input port %1"
+msgstr ""
+
+#: libs/ardour/io.cc:1558
+msgid "incorrect XML node \"%1\" passed to IO object"
+msgstr ""
+
+#: libs/ardour/io.cc:1686
+msgid "%1: cannot open automation event file \"%2\""
+msgstr ""
+
+#: libs/ardour/io.cc:1701
+msgid "badly formed version number in automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:1705
+msgid "no version information in automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:1713
+msgid "badly formatted automation event record at line %1 of %2 (ignored)"
+msgstr ""
+
+#: libs/ardour/io.cc:1733
+msgid "dubious automation event found (and ignored)"
+msgstr ""
+
+#: libs/ardour/io.cc:1746
+msgid "IO::connecting_became_legal() called without a pending state node"
+msgstr ""
+
+#: libs/ardour/io.cc:1768
+msgid "IO::ports_became_legal() called without a pending state node"
+msgstr ""
+
+#: libs/ardour/io.cc:1804
+#: libs/ardour/io.cc:1870
+msgid "Connection %1 was not available - \"in 1\" used instead"
+msgstr ""
+
+#: libs/ardour/io.cc:1820
+#: libs/ardour/io.cc:1888
+msgid "Unknown connection \"%1\" listed for output of %2"
+msgstr ""
+
+#: libs/ardour/io.cc:1822
+#: libs/ardour/io.cc:1890
+msgid "out 1"
+msgstr ""
+
+#: libs/ardour/io.cc:1823
+#: libs/ardour/io.cc:1891
+msgid "No output connections available as a replacement"
+msgstr ""
+
+#: libs/ardour/io.cc:1827
+#: libs/ardour/io.cc:1895
+msgid "Connection %1 was not available - \"out 1\" used instead"
+msgstr ""
+
+#: libs/ardour/io.cc:1841
+msgid "%1: cannot create I/O ports"
+msgstr ""
+
+#: libs/ardour/io.cc:1904
+msgid "improper output channel list in XML node (%1)"
+msgstr ""
+
+#: libs/ardour/io.cc:1989
+msgid "IO: badly formed string in XML node for outputs \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:1994
+msgid "IO: bad output string in XML node \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:2078
+msgid "you cannot use colons to name objects with I/O connections"
+msgstr ""
+
+#: libs/ardour/io.cc:2642
+msgid "in"
+msgstr "wejście"
+
+#: libs/ardour/io.cc:2645
+msgid "out"
+msgstr "wyjście"
+
+#: libs/ardour/io.cc:2697
+#: libs/ardour/io.cc:2730
+#, c-format
+msgid "%s %u"
+msgstr "%s %u"
+
+#: libs/ardour/ladspa_plugin.cc:86
+msgid "LADSPA: module has no descriptor function."
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:91
+msgid "LADSPA: plugin has gone away since discovery!"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:98
+msgid "LADSPA: \"%1\" cannot be used, since it cannot do inplace processing"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:306
+msgid "illegal parameter number used with plugin \"%1\". This mayindicate a change in the plugin design, and presets may beinvalid"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:389
+msgid "Bad node sent to LadspaPlugin::set_state"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:402
+msgid "LADSPA: no ladspa port number"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:409
+msgid "LADSPA: no ladspa port data"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:648
+msgid "LADSPA: cannot load module from \"%1\""
+msgstr ""
+
+#: libs/ardour/location.cc:213
+msgid "You cannot put a CD marker at this position"
+msgstr ""
+
+#: libs/ardour/location.cc:342
+msgid "incorrect XML node passed to Location::set_state"
+msgstr ""
+
+#: libs/ardour/location.cc:347
+msgid "XML node for Location has no ID information"
+msgstr ""
+
+#: libs/ardour/location.cc:353
+msgid "XML node for Location has no name information"
+msgstr ""
+
+#: libs/ardour/location.cc:360
+msgid "XML node for Location has no start information"
+msgstr ""
+
+#: libs/ardour/location.cc:371
+msgid "XML node for Location has no end information"
+msgstr ""
+
+#: libs/ardour/location.cc:378
+msgid "XML node for Location has no flags information"
+msgstr ""
+
+#: libs/ardour/location.cc:495
+msgid "Locations: attempt to use unknown location as selected location"
+msgstr ""
+
+#: libs/ardour/location.cc:663
+msgid "incorrect XML mode passed to Locations::set_state"
+msgstr ""
+
+#: libs/ardour/location.cc:684
+msgid "could not load location from session file - ignored"
+msgstr ""
+
+#: libs/ardour/mtc_slave.cc:123
+msgid "Unknown rate/drop value in incoming MTC stream, session values used instead"
+msgstr ""
+
+#: libs/ardour/mtc_slave.cc:229
+msgid "MTC Slave: atomic read of current time failed, sleeping!"
+msgstr ""
+
+#: libs/ardour/named_selection.cc:88
+msgid "Chunk %1 uses an unknown playlist \"%2\""
+msgstr ""
+
+#: libs/ardour/named_selection.cc:91
+msgid "Chunk %1 contains misformed playlist information"
+msgstr ""
+
+#: libs/ardour/panner.cc:253
+msgid "badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"
+msgstr ""
+
+#: libs/ardour/panner.cc:749
+msgid "badly-formed positional data for Multi2dPanner - ignored"
+msgstr ""
+
+#: libs/ardour/panner.cc:1138
+msgid "Unknown panner plugin \"%1\" found in pan state - ignored"
+msgstr ""
+
+#: libs/ardour/panner.cc:1144
+msgid "panner plugin node has no type information!"
+msgstr ""
+
+#: libs/ardour/panner.cc:1354
+msgid "cannot open pan automation file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1367
+msgid "badly formed version number in pan automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/panner.cc:1371
+msgid "no version information in pan automation event file \"%1\" (first line = %2)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1386
+msgid "too many panner states found in pan automation file %1"
+msgstr ""
+
+#: libs/ardour/playlist.cc:250
+msgid "playlist const copy constructor called"
+msgstr ""
+
+#: libs/ardour/playlist.cc:256
+msgid "playlist non-const copy constructor called"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1217
+msgid "%1: bounds changed received for region (%2)not in playlist"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1750
+msgid "region state node has no ID, ignored"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1761
+msgid "Playlist: cannot reset region state from XML"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1766
+msgid "Playlist: cannot create region from XML"
+msgstr ""
+
+#: libs/ardour/plugin.cc:281
+msgid "Could not locate HOME. Preset not saved."
+msgstr ""
+
+#: libs/ardour/plugin.cc:291
+#: libs/ardour/plugin.cc:297
+msgid "Could not create %1. Preset not saved. (%2)"
+msgstr ""
+
+#: libs/ardour/plugin.cc:302
+msgid "Error saving presets file %1."
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:223
+msgid "Could not parse rdf file: %1"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:263
+msgid "LADSPA: cannot load module \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:270
+msgid "LADSPA: module \"%1\" has no descriptor function."
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:454
+msgid "VST plugin %1 does not support processReplacing, and so cannot be used in ardour at this time"
+msgstr ""
+
+#: libs/ardour/recent_sessions.cc:44
+msgid "cannot open recent session file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:78
+msgid "programming error: unknown Redirect type in Redirect::Clone!\n"
+msgstr ""
+
+#: libs/ardour/redirect.cc:128
+msgid "%2: badly formatted node name in XML automation state, ignored"
+msgstr ""
+
+#: libs/ardour/redirect.cc:141
+msgid "%1: cannot load automation data from XML"
+msgstr ""
+
+#: libs/ardour/redirect.cc:219
+msgid "incorrect XML node \"%1\" passed to Redirect object"
+msgstr ""
+
+#: libs/ardour/redirect.cc:267
+msgid "XML node describing an IO is missing an IO node"
+msgstr ""
+
+#: libs/ardour/redirect.cc:272
+msgid "XML node describing a redirect is missing the `active' field"
+msgstr ""
+
+#: libs/ardour/redirect.cc:285
+msgid "XML node describing a redirect is missing the `placement' field"
+msgstr ""
+
+#: libs/ardour/redirect.cc:315
+msgid "%1: Automation node has no path property"
+msgstr ""
+
+#: libs/ardour/redirect.cc:351
+msgid "%1: cannot open %2 to load automation data (%3)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:376
+msgid "%1: cannot load automation data from %2"
+msgstr ""
+
+#: libs/ardour/region.cc:923
+msgid "XMLNode describing a Region is incomplete (no name)"
+msgstr ""
+
+#: libs/ardour/region.cc:1068
+msgid "Session: XMLNode describing a Region is incomplete (no id)"
+msgstr ""
+
+#: libs/ardour/region_factory.cc:52
+#: libs/ardour/region_factory.cc:69
+msgid "programming error: RegionFactory::create() called with unknown Region type"
+msgstr ""
+
+#: libs/ardour/resampled_source.cc:61
+msgid "Import: src_new() failed : %1"
+msgstr ""
+
+#: libs/ardour/resampled_source.cc:113
+msgid "Import: %1"
+msgstr ""
+
+#: libs/ardour/route.cc:83
+#: libs/ardour/session.cc:1560
+#: libs/ardour/session.cc:1566
+#: libs/ardour/session.cc:1871
+#: libs/ardour/session.cc:3369
+msgid "signal"
+msgstr ""
+
+#: libs/ardour/route.cc:1573
+msgid "Send construction failed"
+msgstr ""
+
+#: libs/ardour/route.cc:1601
+msgid "unknown Insert type \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/route.cc:1609
+msgid "Insert XML node has no type property"
+msgstr ""
+
+#: libs/ardour/route.cc:1614
+msgid "insert could not be created. Ignored."
+msgstr ""
+
+#: libs/ardour/route.cc:1636
+msgid "Bad node sent to Route::set_state() [%1]"
+msgstr ""
+
+#: libs/ardour/route.cc:1698
+msgid "Route %1: unknown edit group \"%2 in saved state (ignored)"
+msgstr ""
+
+#: libs/ardour/route.cc:1714
+#: libs/ardour/route.cc:1718
+msgid "badly formed order key string in state file! [%1] ... ignored."
+msgstr ""
+
+#: libs/ardour/route.cc:1784
+#: libs/ardour/route.cc:2001
+msgid "[control]"
+msgstr ""
+
+#: libs/ardour/route.cc:1823
+msgid "Route %1: unknown mix group \"%2 in saved state (ignored)"
+msgstr ""
+
+#: libs/ardour/route.cc:2019
+msgid "could not connect %1 to %2"
+msgstr ""
+
+#: libs/ardour/send.cc:34
+#: libs/ardour/send.cc:57
+msgid "send %1"
+msgstr ""
+
+#: libs/ardour/send.cc:117
+msgid "XML node describing a send is missing a Redirect node"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:80
+#: libs/ardour/session_midi.cc:1085
+msgid "Cannot create transport request signal pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:85
+#: libs/ardour/session_butler.cc:90
+msgid "UI: cannot set O_NONBLOCK on butler request pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:95
+msgid "Session: could not create butler thread"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:184
+msgid "poll on butler request pipe failed (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:191
+msgid "Error on butler thread request pipe: fd=%1 err=%2"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:232
+msgid "Error reading from butler request pipe"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:277
+msgid "Butler read ahead failure on dstream %1"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:323
+msgid "Butler write-behind failure on dstream %1"
+msgstr ""
+
+#: libs/ardour/session.cc:130
+msgid "Could not resolve path: %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session.cc:142
+msgid "cannot check session path %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session.cc:172
+msgid "cannot check statefile %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session.cc:208
+msgid "%1 is not an Ardour snapshot file"
+msgstr ""
+
+#: libs/ardour/session.cc:225
+msgid "cannot determine current working directory (%1)"
+msgstr ""
+
+#: libs/ardour/session.cc:242
+msgid "unknown file type for session %1"
+msgstr ""
+
+#: libs/ardour/session.cc:387
+msgid "monitor"
+msgstr "monitor"
+
+#: libs/ardour/session.cc:394
+#: libs/ardour/session.cc:1900
+msgid "master"
+msgstr "główna"
+
+#: libs/ardour/session.cc:674
+msgid "Set block size and sample rate"
+msgstr ""
+
+#: libs/ardour/session.cc:679
+msgid "Using configuration"
+msgstr ""
+
+#: libs/ardour/session.cc:712
+msgid "could not setup Click I/O"
+msgstr "nie można było ustawić I/O metronomu"
+
+#: libs/ardour/session.cc:733
+msgid "cannot setup Click I/O"
+msgstr "nie można ustawić I/O metronomu"
+
+#: libs/ardour/session.cc:736
+msgid "Compute I/O Latencies"
+msgstr ""
+
+#: libs/ardour/session.cc:748
+msgid "Set up standard connections"
+msgstr ""
+
+#: libs/ardour/session.cc:754
+#, c-format
+msgid "out %<PRIu32>"
+msgstr "wyjście %<PRIu32>"
+
+#: libs/ardour/session.cc:766
+#, c-format
+msgid "in %<PRIu32>"
+msgstr "wejście %<PRIu32>"
+
+#: libs/ardour/session.cc:780
+#, c-format
+msgid "out %<PRIu32>+%<PRIu32>"
+msgstr "wyjście %<PRIu32>+%<PRIu32>"
+
+#: libs/ardour/session.cc:794
+#, c-format
+msgid "in %<PRIu32>+%<PRIu32>"
+msgstr "wejście %<PRIu32>+%<PRIu32>"
+
+#: libs/ardour/session.cc:827
+msgid "cannot setup master inputs"
+msgstr "nie można ustawić głównych wejść"
+
+#: libs/ardour/session.cc:835
+msgid "cannot setup master outputs"
+msgstr "nie można ustawić głównych wyjść"
+
+#: libs/ardour/session.cc:846
+msgid "Master Out"
+msgstr "Główne wyjście"
+
+#: libs/ardour/session.cc:855
+msgid "Setup signal flow and plugins"
+msgstr ""
+
+#: libs/ardour/session.cc:861
+msgid "Catch up with send/insert state"
+msgstr ""
+
+#: libs/ardour/session.cc:892
+msgid "Connect to engine"
+msgstr ""
+
+#: libs/ardour/session.cc:899
+msgid "OSC startup"
+msgstr "poczÄ…tek OSC"
+
+#: libs/ardour/session.cc:928
+msgid "cannot create Auditioner: no auditioning of regions possible"
+msgstr ""
+
+#: libs/ardour/session.cc:942
+msgid "cannot setup control inputs"
+msgstr ""
+
+#: libs/ardour/session.cc:950
+msgid "cannot set up master outputs"
+msgstr ""
+
+#: libs/ardour/session.cc:1153
+msgid "Session: you can't use that location for auto punch (start <= end)"
+msgstr ""
+
+#: libs/ardour/session.cc:1194
+msgid "Session: you can't use a mark for auto loop"
+msgstr ""
+
+#: libs/ardour/session.cc:1578
+msgid "feedback loop setup between %1 and %2"
+msgstr ""
+
+#: libs/ardour/session.cc:1765
+#: libs/ardour/session.cc:1931
+msgid "cannot configure %1 in/%2 out configuration for new audio track"
+msgstr ""
+
+#: libs/ardour/session.cc:1817
+msgid "Session: could not create new audio track."
+msgstr ""
+
+#: libs/ardour/session.cc:1835
+#: libs/ardour/session.cc:1980
+msgid "No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks."
+msgstr ""
+
+#: libs/ardour/session.cc:1874
+msgid "editor"
+msgstr ""
+
+#: libs/ardour/session.cc:1975
+msgid "Session: could not create new audio route."
+msgstr ""
+
+#: libs/ardour/session.cc:2526
+msgid "cannot create new name for region \"%1\""
+msgstr ""
+
+#: libs/ardour/session.cc:2590
+msgid "too many regions with names like %1"
+msgstr ""
+
+#: libs/ardour/session.cc:2621
+#: libs/ardour/session.cc:2682
+msgid "Session::add_region() ignored a null region. Warning: you might have lost a region."
+msgstr ""
+
+#: libs/ardour/session.cc:3159
+msgid "There are already %1 recordings for %2, which I consider too many."
+msgstr ""
+
+#: libs/ardour/session.cc:3390
+msgid "Cannot compile tape track regexp for use (%1)"
+msgstr ""
+
+#: libs/ardour/session.cc:3543
+msgid "programming error: unknown type of Insert created!"
+msgstr ""
+
+#: libs/ardour/session.cc:3549
+msgid "programming error: unknown type of Redirect created!"
+msgstr ""
+
+#: libs/ardour/session.cc:3588
+msgid "programming error: unknown type of Redirect deleted!"
+msgstr ""
+
+#: libs/ardour/session.cc:3708
+#: libs/ardour/session.cc:3722
+#: libs/ardour/session.cc:4122
+msgid "Memory allocation error: posix_memalign (%1 * %2) failed (%3)"
+msgstr ""
+
+#: libs/ardour/session.cc:3792
+msgid "send ID %1 appears to be in use already"
+msgstr ""
+
+#: libs/ardour/session.cc:3804
+msgid "insert ID %1 appears to be in use already"
+msgstr ""
+
+#: libs/ardour/session.cc:3986
+msgid "too many bounced versions of playlist \"%1\""
+msgstr ""
+
+#: libs/ardour/session.cc:3995
+msgid "cannot create new audio file \"%1\" for %2"
+msgstr ""
+
+#: libs/ardour/session.cc:4107
+msgid "Programming error: get_silent_buffers() called for %1 buffers but only %2 exist"
+msgstr ""
+
+#: libs/ardour/session_click.cc:160
+msgid "cannot open click soundfile %1 (%2)"
+msgstr "nie można otworzyć pliku dźwiękowego metronomu %1 (%2)"
+
+#: libs/ardour/session_click.cc:169
+msgid "cannot read data from click soundfile"
+msgstr "nie można odczytać danych z pliku dźwiękowego metronomu"
+
+#: libs/ardour/session_click.cc:196
+msgid "cannot open click emphasis soundfile %1 (%2)"
+msgstr "nie można otworzyć pliku dźwiękowego akcentowanego uderzenia metronomu %1 (%2)"
+
+#: libs/ardour/session_click.cc:204
+msgid "cannot read data from click emphasis soundfile"
+msgstr "nie można odczytać danych z pliku dźwiękowego akcentowanego uderzenia metronomu"
+
+#: libs/ardour/session_command.cc:75
+msgid "Tried to reconstitute a MementoCommand with no contents, failing. id="
+msgstr ""
+
+#: libs/ardour/session_command.cc:111
+msgid "could not reconstitute MementoCommand from XMLNode. object type = %1 id = %2"
+msgstr ""
+
+#: libs/ardour/session_command.cc:123
+msgid "GlobalRouteStateCommand has no \"type\" node, ignoring"
+msgstr ""
+
+#: libs/ardour/session_command.cc:138
+msgid "unknown type of GlobalRouteStateCommand (%1), ignored"
+msgstr ""
+
+#: libs/ardour/session_command.cc:186
+msgid "global route state command has no \"%1\" node, ignoring entire command"
+msgstr ""
+
+#: libs/ardour/session_command.cc:202
+#: libs/ardour/session_command.cc:515
+msgid "cannot find track/bus \"%1\" while rebuilding a global route state command, ignored"
+msgstr ""
+
+#: libs/ardour/session_command.cc:499
+msgid "global route meter state command has no \"%1\" node, ignoring entire command"
+msgstr ""
+
+#: libs/ardour/session_events.cc:161
+msgid "Session: cannot have two events of type %1 at the same frame (%2)."
+msgstr ""
+
+#: libs/ardour/session_events.cc:437
+msgid "Programming error: illegal event type in process_event (%1)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:62
+msgid "Export: no output file specified"
+msgstr ""
+
+#: libs/ardour/session_export.cc:163
+#: libs/ardour/session_export.cc:168
+msgid "illegal frame range in export specification"
+msgstr ""
+
+#: libs/ardour/session_export.cc:173
+msgid "Bad data width size. Report me!"
+msgstr ""
+
+#: libs/ardour/session_export.cc:203
+msgid "Export: cannot open output file \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:213
+msgid "cannot initialize sample rate conversion: %1"
+msgstr ""
+
+#: libs/ardour/session_export.cc:315
+msgid "an error occured during sample rate conversion: %1"
+msgstr ""
+
+#: libs/ardour/session_export.cc:326
+msgid "warning, leftover frames overflowed, glitches might occur in output"
+msgstr ""
+
+#: libs/ardour/session_export.cc:417
+msgid "Export: could not write data to output file (%1)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:484
+msgid "%1: cannot seek to %2 for export"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:94
+msgid "Ardour is slaved to MTC - port cannot be reset"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:109
+msgid "unknown port %1 requested for MTC"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:446
+msgid "Error reading from MIDI port %1"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:816
+msgid "Session: could not send full MIDI time code"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:875
+msgid "Session: cannot send quarter-frame MTC message (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:983
+msgid "MMC: cannot send command %1%2%3"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1090
+msgid "UI: cannot set O_NONBLOCK on signal read pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1095
+msgid "UI: cannot set O_NONBLOCK on signal write pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1100
+msgid "Session: could not create transport thread"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1131
+msgid "cannot send signal to midi thread! (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1226
+msgid "MIDI thread poll failed (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1238
+msgid "Error on transport thread request pipe"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1265
+msgid "Error reading from transport request pipe"
+msgstr ""
+
+#: libs/ardour/session_process.cc:105
+msgid "Session: error in no roll for %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:109
+msgid "Could not use path %1 (%s)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:145
+#: libs/ardour/session_state.cc:1050
+msgid "end"
+msgstr "koniec"
+
+#: libs/ardour/session_state.cc:146
+#: libs/ardour/session_state.cc:1049
+msgid "start"
+msgstr "poczÄ…tek"
+
+#: libs/ardour/session_state.cc:338
+msgid "Reset Remote Controls"
+msgstr ""
+
+#: libs/ardour/session_state.cc:345
+msgid "Reset Control Protocols"
+msgstr ""
+
+#: libs/ardour/session_state.cc:365
+msgid "Session loading complete"
+msgstr ""
+
+#: libs/ardour/session_state.cc:484
+#: libs/ardour/session_state.cc:2857
+msgid "Session: cannot create session peakfile folder \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:495
+msgid "Session: cannot create session sounds folder \"%1\" (%2)"
+msgstr "Nie można utworzyć folderu dźwięków sesji \"%1\" (%2)"
+
+#: libs/ardour/session_state.cc:503
+msgid "Session: cannot create session dead sounds folder \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:510
+msgid "Session: cannot create session export folder \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:517
+msgid "Session: cannot create session analysis folder \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:529
+msgid "Session: cannot create session folder \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:561
+msgid "Could not open %1 for writing mix template"
+msgstr ""
+
+#: libs/ardour/session_state.cc:567
+msgid "Could not open mix template %1 for reading"
+msgstr ""
+
+#: libs/ardour/session_state.cc:606
+msgid "Session: could not load diskstream via XML state"
+msgstr ""
+
+#: libs/ardour/session_state.cc:649
+msgid "could not rename snapshot %1 to %2"
+msgstr ""
+
+#: libs/ardour/session_state.cc:688
+msgid "Ardour's audio engine is not connected and state saving would lose all I/O connections. Session not saved"
+msgstr ""
+
+#: libs/ardour/session_state.cc:732
+msgid "state could not be saved to %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:739
+msgid "could not rename temporary session file %1 to %2"
+msgstr ""
+
+#: libs/ardour/session_state.cc:809
+msgid "%1: session state information file \"%2\" doesn't exist!"
+msgstr ""
+
+#: libs/ardour/session_state.cc:818
+msgid "Could not understand ardour file %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:827
+msgid "Session file %1 is not an Ardour session"
+msgstr ""
+
+#: libs/ardour/session_state.cc:858
+msgid ""
+"Copying old session file %1 to %2\n"
+"Use %2 with Ardour versions before 2.0 from now on"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1168
+msgid "programming error: Session: incorrect XML node sent to set_state()"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1237
+msgid "Session: XML state has no options section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1241
+msgid "Session: XML state has no locations section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1274
+msgid "Session: XML state has no sources section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1281
+msgid "Session: XML state has no Regions section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1288
+msgid "Session: XML state has no playlists section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1307
+msgid "Session: XML state has no diskstreams section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1314
+msgid "Session: XML state has no connections section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1321
+msgid "Session: XML state has no edit groups section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1328
+msgid "Session: XML state has no mix groups section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1335
+msgid "Session: XML state has no Tempo Map section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1342
+msgid "Session: XML state has no routes section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1349
+msgid "Session: XML state has no click section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1384
+msgid "Session: cannot create Route from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1388
+msgid "Loaded track/bus %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1427
+msgid "Session: cannot create Region from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1431
+msgid "Can not load state for region '%1'"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1468
+msgid "Session: XMLNode describing a AudioRegion is incomplete (no source)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1476
+#: libs/ardour/session_state.cc:1497
+#: libs/ardour/session_state.cc:1517
+msgid "Session: XMLNode describing a AudioRegion references an unknown source id =%1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1482
+#: libs/ardour/session_state.cc:1503
+#: libs/ardour/session_state.cc:1523
+msgid "Session: XMLNode describing a AudioRegion references a non-audio source id =%1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1546
+msgid "Session: XMLNode describing an AudioRegion is missing some master sources; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1597
+msgid "cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1620
+msgid "Session: cannot create Source from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1625
+msgid "A sound file is missing. It will be replaced by silence."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1646
+msgid "Found a sound file that cannot be used by Ardour. Talk to the progammers."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1668
+msgid "Could not create mix templates directory \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1682
+msgid "Template \"%1\" already exists - new version not created"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1689
+msgid "mix template not saved"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1748
+msgid "cannot create session directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1759
+msgid "cannot create sounds directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1768
+msgid "cannot create dead sounds directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1777
+msgid "cannot create peak file directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1909
+#: libs/ardour/session_state.cc:1930
+msgid "Session: cannot create Playlist from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1969
+msgid "Session: cannot create Named Selection from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:2171
+msgid "Unknown node \"%1\" found in Connections list from state file"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2982
+msgid "cannot remove dead sound file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:3098
+msgid "could not backup old history file, current history not saved."
+msgstr ""
+
+#: libs/ardour/session_state.cc:3110
+msgid "history could not be saved to %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:3117
+msgid "could not remove corrupt history file %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:3121
+msgid "could not restore history file from backup %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:3143
+msgid "Loading history from '%1'."
+msgstr ""
+
+#: libs/ardour/session_state.cc:3150
+msgid "Could not understand session history file \"%1\""
+msgstr ""
+
+#: libs/ardour/session_state.cc:3193
+msgid "Couldn't figure out how to make a Command out of a %1 XMLNode."
+msgstr ""
+
+#: libs/ardour/session_time.cc:483
+msgid "Unknown JACK transport state %1 in sync callback"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:119
+msgid "Cannot loop - no loop range defined"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:537
+msgid ""
+"Seamless looping cannot be supported while Ardour is using JACK transport.\n"
+"Recommend changing the configured options"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:822
+msgid "Global varispeed cannot be supported while Ardour is connected to JACK transport control"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:1015
+msgid "please stop the transport before adjusting slave settings"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:1048
+msgid "No MTC port defined: MTC slaving is impossible."
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:34
+msgid "WAV"
+msgstr "WAV"
+
+#: libs/ardour/sndfile_helpers.cc:35
+msgid "AIFF"
+msgstr "AIFF"
+
+#: libs/ardour/sndfile_helpers.cc:36
+msgid "CAF"
+msgstr "CAF"
+
+#: libs/ardour/sndfile_helpers.cc:37
+msgid "W64 (64 bit WAV)"
+msgstr "W64 (64 bit WAV)"
+
+#: libs/ardour/sndfile_helpers.cc:38
+msgid "raw (no header)"
+msgstr "raw (brak nagłówka)"
+
+#: libs/ardour/sndfile_helpers.cc:43
+msgid ".wav"
+msgstr ".wav"
+
+#: libs/ardour/sndfile_helpers.cc:44
+msgid ".aiff"
+msgstr ".aiff"
+
+#: libs/ardour/sndfile_helpers.cc:45
+msgid ".caf"
+msgstr ".caf"
+
+#: libs/ardour/sndfile_helpers.cc:46
+msgid ".w64"
+msgstr ".w64"
+
+#: libs/ardour/sndfile_helpers.cc:47
+msgid ".raw"
+msgstr ".raw"
+
+#: libs/ardour/sndfile_helpers.cc:60
+msgid "16 bit"
+msgstr "16 bit"
+
+#: libs/ardour/sndfile_helpers.cc:61
+msgid "24 bit"
+msgstr "24 bit"
+
+#: libs/ardour/sndfile_helpers.cc:62
+msgid "32 bit"
+msgstr "32 bit"
+
+#: libs/ardour/sndfile_helpers.cc:63
+msgid "8 bit"
+msgstr "8 bit"
+
+#: libs/ardour/sndfile_helpers.cc:64
+msgid "float"
+msgstr "float"
+
+#: libs/ardour/sndfile_helpers.cc:77
+msgid "Little-endian (Intel)"
+msgstr "Little-endian (Intel)"
+
+#: libs/ardour/sndfile_helpers.cc:78
+msgid "Big-endian (Mac)"
+msgstr "Big-endian (Mac)"
+
+#: libs/ardour/sndfilesource.cc:149
+msgid "FileSource: cannot get host information for BWF header (%1)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:173
+msgid "cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:226
+msgid "SndFileSource: cannot open file \"%1\" for %2 (%3)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:234
+msgid "SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:332
+msgid "SndFileSource: could not seek to frame %1 within %2 (%3)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:377
+#: libs/ardour/sndfilesource.cc:412
+msgid "attempt to write a non-writable audio file source (%1)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:382
+#: libs/ardour/utils.cc:523
+#: libs/ardour/utils.cc:547
+#: libs/ardour/utils.cc:561
+#: libs/ardour/utils.cc:580
+msgid "programming error: %1 %2"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:516
+msgid "attempt to flush a non-writable audio file source (%1)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:526
+msgid "attempt to store broadcast info in a non-writable audio file source (%1)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:562
+#: libs/ardour/sndfilesource.cc:583
+msgid "cannot set broadcast info for audio file %1; Dropping broadcast info for this file"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:596
+msgid "%1: cannot seek to %2 (libsndfile error: %3"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:705
+msgid "SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:718
+#: libs/ardour/sndfilesource.cc:768
+#: libs/ardour/sndfilesource.cc:775
+msgid "SndFileSource: \"%1\" bad write (%2)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:798
+msgid "Filesource: start time is already set for existing file (%1): Cannot change start time."
+msgstr ""
+
+#: libs/ardour/tempo.cc:71
+msgid "TempoSection XML node has no \"start\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:79
+msgid "TempoSection XML node has an illegal \"start\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:86
+msgid "TempoSection XML node has no \"beats-per-minute\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:91
+msgid "TempoSection XML node has an illegal \"beats_per_minute\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:100
+msgid "TempoSection XML node has an illegal \"note-type\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:106
+msgid "TempoSection XML node has no \"movable\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:147
+msgid "MeterSection XML node has no \"start\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:155
+msgid "MeterSection XML node has an illegal \"start\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:162
+msgid "MeterSection XML node has no \"beats-per-bar\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:167
+msgid "MeterSection XML node has an illegal \"beats-per-bar\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:172
+msgid "MeterSection XML node has no \"note-type\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:177
+msgid "MeterSection XML node has an illegal \"note-type\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:182
+msgid "MeterSection XML node has no \"movable\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:561
+msgid "no tempo sections defined in tempo map - cannot change tempo @ %1"
+msgstr ""
+
+#: libs/ardour/tempo.cc:585
+#: libs/ardour/tempo.cc:601
+msgid "programming error: no tempo section in tempo map!"
+msgstr ""
+
+#: libs/ardour/tempo.cc:644
+#: libs/ardour/tempo.cc:702
+msgid "programming error: unhandled MetricSection type"
+msgstr ""
+
+#: libs/ardour/tempo.cc:1461
+#: libs/ardour/tempo.cc:1473
+msgid "Tempo map: could not set new state, restoring old one."
+msgstr ""
+
+#: libs/ardour/utils.cc:300
+msgid "illegal or badly-formed string used for path (%1)"
+msgstr ""
+
+#: libs/ardour/utils.cc:305
+msgid "path (%1) is ambiguous"
+msgstr ""
+
+#: libs/ardour/utils.cc:367
+#: libs/ardour/utils.cc:391
+msgid "Splice Edit"
+msgstr "Edycja klockowa"
+
+#: libs/ardour/utils.cc:369
+#: libs/ardour/utils.cc:384
+msgid "Slide Edit"
+msgstr "Edycja ślizgowa"
+
+#: libs/ardour/utils.cc:371
+#: libs/ardour/utils.cc:387
+msgid "Lock Edit"
+msgstr "Edycja zablokowana"
+
+#: libs/ardour/utils.cc:374
+msgid "programming error: unknown edit mode string \"%1\""
+msgstr ""
+
+#: libs/ardour/utils.cc:398
+#: libs/ardour/utils.cc:427
+msgid "Internal"
+msgstr "Wew."
+
+#: libs/ardour/utils.cc:402
+#: libs/ardour/utils.cc:423
+msgid "MTC"
+msgstr "MTC"
+
+#: libs/ardour/utils.cc:406
+#: libs/ardour/utils.cc:420
+msgid "JACK"
+msgstr "JACK"
+
+#: libs/ardour/utils.cc:410
+msgid "programming error: unknown slave source string \"%1\""
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:167
+msgid "cannot create VST chunk directory: %1"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:175
+msgid "cannot check VST chunk directory: %1"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:181
+msgid "%1 exists but is not a directory"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:219
+msgid "Bad node sent to VSTPlugin::set_state"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:323
+#: libs/ardour/vst_plugin.cc:334
+msgid "no support for presets using chunks at this time"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:486
+msgid "VST: cannot load module from \"%1\""
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:491
+msgid "You asked ardour to not use any VST plugins"
+msgstr ""
+
+#: libs/ardour/coreaudiosource.cc:80
+msgid "CoreAudioSource: cannot open file \"%1\" for %2"
+msgstr ""
+
+#: libs/ardour/rb_effect.cc:155
+#: libs/ardour/rb_effect.cc:193
+msgid "tempoize: error reading data from %1 at %2 (wanted %3, got %4)"
+msgstr ""
+
+#: libs/ardour/rb_effect.cc:218
+#: libs/ardour/rb_effect.cc:235
+msgid "error writing tempo-adjusted data to %1"
+msgstr ""
+
+#: libs/ardour/rb_effect.cc:242
+msgid "timefx code failure. please notify ardour-developers."
+msgstr ""
+
+#: libs/ardour/audio_unit.cc:104
+msgid "AudioUnit: Could not convert CAComponent to CAAudioUnit"
+msgstr ""
+
+#: libs/ardour/audio_unit.cc:147
+msgid "AUPlugin: cannot set processing block size"
+msgstr ""
+
+#: libs/ardour/audio_unit.cc:351
+msgid "AUPlugin: %1 cannot initialize plugin (err = %2)"
+msgstr ""
+
+#: libs/ardour/audio_unit.cc:431
+msgid "AUPlugin: could not set stream format for %1/%2 (err = %3)"
+msgstr ""
+
+#: libs/ardour/audio_unit.cc:476
+msgid "AUPlugin: %1 output_streams() called without any format set!"
+msgstr ""
+
+#: libs/ardour/audio_unit.cc:488
+msgid "AUPlugin: input_streams() called without any format set!"
+msgstr ""
+
+#: libs/ardour/audio_unit.cc:504
+msgid "AUPlugin: render callback called illegally!"
+msgstr ""
+
+#~ msgid "%s/out %u"
+#~ msgstr "%s/wyjście %u"
+#~ msgid "%s/in"
+#~ msgstr "%s/wejście"
+
diff --git a/libs/ardour/po/ru_RU.po b/libs/ardour/po/ru_RU.po
new file mode 100644
index 0000000000..aeeb1bf547
--- /dev/null
+++ b/libs/ardour/po/ru_RU.po
@@ -0,0 +1,2000 @@
+# Copyright (C) 2004 Paul Davis
+# This file is distributed under the same license as the libardour package.
+# Igor Blinov pitstop@nm.ru, 2004.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: libardour 0.716.1\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-06-29 21:03-0400\n"
+"PO-Revision-Date: 2004-03-31 00:55+0300\n"
+"Last-Translator: Igor Blinov pitstop@nm.ru\n"
+"Language-Team: Russian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=koi8-r\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: libs/ardour/audio_diskstream.cc:337
+msgid "AudioDiskstream: Session doesn't know about a Playlist called \"%1\""
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:342
+msgid "AudioDiskstream: Playlist \"%1\" isn't an audio playlist"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:433
+msgid "AudioDiskstream %1: there is no existing playlist to make a copy of!"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1114 libs/ardour/audio_diskstream.cc:1125
+msgid ""
+"AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1254
+msgid "AudioDiskstream %1: cannot read %2 from playlist at frame %3"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1621 libs/ardour/audio_diskstream.cc:1638
+msgid "AudioDiskstream %1: cannot write to disk"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1698
+msgid "AudioDiskstream \"%1\": cannot flush captured data to disk!"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1796
+msgid "%1: could not create region for complete audio file"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1819
+msgid "AudioDiskstream: could not create region for captured audio!"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1874
+#, fuzzy
+msgid "programmer error: %1"
+msgstr "ÏÛÉÂËÁ ÐÒÏÇÒÁÍÍÙ: "
+
+#: libs/ardour/audio_diskstream.cc:2146
+msgid "AudioDiskstream: channel %1 out of range"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2171
+msgid "%1:%2 new capture file not initialized correctly"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2404
+msgid "Location \"%1\" not valid for track loop (start >= end)"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2485
+msgid "%1: cannot restore pending capture source file %2"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2507
+msgid "%1: incorrect number of pending sources listed - ignoring them all"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2523
+msgid "%1: cannot create whole-file region from pending capture sources"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2535
+msgid "%1: cannot create region from pending capture sources"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:92
+msgid "channels"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:93
+#, fuzzy
+msgid "samplerate"
+msgstr "ÒÁÚÄÅÌÉÔØ"
+
+#: libs/ardour/audio_library.cc:94
+msgid "resolution"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:95
+msgid "format"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:102
+msgid "Could not open %1. Audio Library not saved"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:53 libs/ardour/audio_playlist.cc:63
+#: libs/ardour/audio_playlist.cc:74 libs/ardour/audio_playlist.cc:121
+#: libs/ardour/insert.cc:76 libs/ardour/insert.cc:95 libs/ardour/insert.cc:120
+#: libs/ardour/insert.cc:838 libs/ardour/insert.cc:846 libs/ardour/send.cc:39
+#: libs/ardour/send.cc:53 libs/ardour/send.cc:62
+#: libs/ardour/session_state.cc:1621 libs/ardour/session_state.cc:1667
+msgid "initial state"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:275 libs/ardour/audio_playlist.cc:769
+msgid ""
+"programming error: non-audio Region passed to remove_overlap in audio "
+"playlist"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:402
+msgid ""
+"programming error: non-audio Region tested for overlap in audio playlist"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:878
+msgid "xfade change"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:933
+msgid "region modified"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:125 libs/ardour/io.cc:1716
+#: libs/ardour/io.cc:1826
+msgid "Unknown connection \"%1\" listed for input of %2"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:127 libs/ardour/io.cc:1718
+#: libs/ardour/io.cc:1828
+msgid "in 1"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:128 libs/ardour/io.cc:1719
+#: libs/ardour/io.cc:1829
+msgid "No input connections available as a replacement"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:132 libs/ardour/io.cc:1723
+#: libs/ardour/io.cc:1833
+msgid "Connection %1 was not available - \"in 1\" used instead"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:141 libs/ardour/io.cc:1842
+msgid "improper input channel list in XML node (%1)"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:186 libs/ardour/audio_track.cc:199
+msgid "AudioTrack: diskstream \"%1\" not known by session"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:297
+msgid ""
+"MIDI rec_enable control specification for %1 is incomplete, so it has been "
+"ignored"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:309
+msgid "programming error: AudioTrack given state without diskstream!"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:144
+msgid "cannot activate JACK client"
+msgstr "ÎÅ ÕÄÁÌÏÓØ ÁËÔÉ×ÉÒÏ×ÁÔØ ËÌÉÅÎÔÁ JACK ÓÅÒ×ÅÒÁ"
+
+#: libs/ardour/audioengine.cc:395
+msgid "register audio input port called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:426
+msgid "register audio output port called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:487
+msgid "connect called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:503
+msgid "AudioEngine: cannot connect %1 (%2) to %3 (%4)"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:516 libs/ardour/audioengine.cc:545
+msgid "disconnect called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:603
+msgid "get_port_by_name() called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:636
+msgid "get_ports called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:711
+msgid "get_nth_physical called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:739
+msgid "get_port_total_latency() called with no JACK client connection"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:745
+msgid "get_port_total_latency() called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:869
+msgid "Unable to connect to JACK server"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:872
+msgid "Could not connect to JACK server as \"%1\""
+msgstr ""
+
+#: libs/ardour/audioengine.cc:877
+msgid "JACK server started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:911
+msgid "cannot shutdown connection to JACK"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:936
+msgid "failed to connect to JACK"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:952
+msgid "could not reregister %1"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:1009
+msgid "could not reconnect %1 and %2 (err = %3)"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:444 libs/ardour/session_state.cc:3095
+msgid ""
+"there are already 1000 files with names like %1; versioning discontinued"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:458 libs/ardour/session_state.cc:3109
+msgid "cannot rename audio file source from %1 to %2 (%3)"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:465 libs/ardour/session_state.cc:3124
+msgid "cannot remove peakfile %1 for %2 (%3)"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:509
+msgid "FileSource: search path not set"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:533
+msgid ""
+"FileSource: \"%1\" is ambigous when searching %2\n"
+"\t"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:539
+msgid "Filesource: cannot find required file (%1): while searching %2"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:562
+msgid "Filesource: cannot find required file (%1): %2"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:567
+msgid "Filesource: cannot check for existing file (%1): %2"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:636 libs/ardour/insert.cc:525
+#: libs/ardour/sndfilesource.cc:113
+#, fuzzy
+msgid "programming error: %1"
+msgstr "ÏÛÉÂËÁ ÐÒÏÇÒÁÍÍÙ: "
+
+#: libs/ardour/audiofilesource.cc:641
+msgid "cannot rename audio file for %1 to %2"
+msgstr ""
+
+#: libs/ardour/audiofilter.cc:45
+msgid "audiofilter: error creating name for new audio file based on %1"
+msgstr ""
+
+#: libs/ardour/audiofilter.cc:58
+msgid "audiofilter: error creating new audio file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/audioregion.cc:857 libs/ardour/audioregion.cc:919
+msgid "fade in change"
+msgstr ""
+
+#: libs/ardour/audioregion.cc:1349
+#, c-format
+msgid "normalized to %.2fdB"
+msgstr ""
+
+#: libs/ardour/audioregion.cc:1367
+msgid "envelope change"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:143
+msgid "poll on peak request pipe failed (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:150
+msgid "Error on peak thread request pipe"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:183
+msgid "Error reading from peak request pipe"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:215 libs/ardour/session_butler.cc:80
+#: libs/ardour/session_midi.cc:1183
+msgid "Cannot create transport request signal pipe (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:220 libs/ardour/audiosource.cc:225
+msgid "UI: cannot set O_NONBLOCK on peak request pipe (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:230
+msgid "AudioSource: could not create peak thread"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:308
+msgid "cannot rename peakfile for %1 from %2 to %3 (%4)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:350
+#, fuzzy
+msgid "AudioSource: cannot stat peakfile \"%1\""
+msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÐÒÏÞÉÔÁÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÏÌØÚÏ×ÁÔÅÌÑ \"%1\""
+
+#: libs/ardour/audiosource.cc:451
+msgid "cannot read sample data for unscaled peak computation"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:472 libs/ardour/audiosource.cc:543
+#: libs/ardour/audiosource.cc:787 libs/ardour/audiosource.cc:878
+#, fuzzy
+msgid "AudioSource: cannot open peakpath \"%1\" (%2)"
+msgstr "LADSPA: ÎÅ ÕÄÁÌÏÓØ ÚÁÇÒÕÚÉÔØ ÍÏÄÕÌØ \"%1\" (%2)"
+
+#: libs/ardour/audiosource.cc:644
+msgid "AudioSource[%1]: peak read - cannot read %2 samples at offset %3"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:798
+msgid "%1: could not write read raw data for peak computation (%2)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:823
+msgid "%1: could not write peak file data (%2)"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:65 libs/ardour/location.cc:345
+#: libs/ardour/tempo.cc:226
+msgid "initial"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:232
+msgid "cleared"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:404
+msgid "added event"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:421
+#, fuzzy
+msgid "removed event"
+msgstr "ÕÄÁÌÉÔØ ÏÂÌÁÓÔØ"
+
+#: libs/ardour/automation_event.cc:436
+msgid "removed multiple events"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:467 libs/ardour/automation_event.cc:498
+#, fuzzy
+msgid "removed range"
+msgstr "ÕÄÁÌÉÔØ ÏÂÌÁÓÔØ"
+
+#: libs/ardour/automation_event.cc:528
+msgid "event range adjusted"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:550
+msgid "event adjusted"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:665 libs/ardour/automation_event.cc:770
+#: libs/ardour/panner.cc:1041
+#, fuzzy
+msgid "programming error:"
+msgstr "ÏÛÉÂËÁ ÐÒÏÇÒÁÍÍÙ: "
+
+#: libs/ardour/automation_event.cc:1079
+msgid "cut/copy/clear"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1112
+msgid "copy"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1180 libs/ardour/playlist.cc:939
+msgid "paste"
+msgstr "×ÓÔÁ×ÉÔØ"
+
+#: libs/ardour/automation_event.cc:1235
+msgid ""
+"automation list: no x-coordinate stored for control point (point ignored)"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1241
+msgid ""
+"automation list: no y-coordinate stored for control point (point ignored)"
+msgstr ""
+
+#: libs/ardour/configuration.cc:80
+#, fuzzy
+msgid "loading system configuration file %1"
+msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÐÒÏÞÉÔÁÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÒÏÇÒÁÍÍÙ \"%1\""
+
+#: libs/ardour/configuration.cc:83
+msgid "Ardour: cannot read system configuration file \"%1\""
+msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÐÒÏÞÉÔÁÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÒÏÇÒÁÍÍÙ \"%1\""
+
+#: libs/ardour/configuration.cc:88
+msgid "Ardour: system configuration file \"%1\" not loaded successfully."
+msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÚÁÇÒÕÚÉÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÒÏÇÒÁÍÍÙ \"%1\"."
+
+#: libs/ardour/configuration.cc:105
+#, fuzzy
+msgid "loading user configuration file %1"
+msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÐÒÏÞÉÔÁÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÏÌØÚÏ×ÁÔÅÌÑ \"%1\""
+
+#: libs/ardour/configuration.cc:108
+msgid "Ardour: cannot read configuration file \"%1\""
+msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÐÒÏÞÉÔÁÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÏÌØÚÏ×ÁÔÅÌÑ \"%1\""
+
+#: libs/ardour/configuration.cc:113
+#, fuzzy
+msgid "Ardour: user configuration file \"%1\" not loaded successfully."
+msgstr "Ardour: ÎÅ ÕÄÁÌÏÓØ ÚÁÇÒÕÚÉÔØ ÆÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÐÏÌØÚÏ×ÁÔÅÌÑ \"%1\"."
+
+#: libs/ardour/configuration.cc:137
+#, fuzzy
+msgid "Config file %1 not saved"
+msgstr "æÁÊÌ ËÏÎÆÉÇÕÒÁÃÉÉ ÎÅ ÓÏÈÒÁΣÎ"
+
+#: libs/ardour/configuration.cc:210
+msgid "ill-formed MIDI port specification in ardour rcfile (ignored)"
+msgstr ""
+
+#: libs/ardour/connection.cc:183
+msgid "Node for Connection has no \"name\" property"
+msgstr ""
+
+#: libs/ardour/connection.cc:191
+msgid "Node for Connection has no \"connections\" property"
+msgstr ""
+
+#: libs/ardour/connection.cc:227 libs/ardour/io.cc:1902
+msgid "IO: badly formed string in XML node for inputs \"%1\""
+msgstr ""
+
+#: libs/ardour/connection.cc:232 libs/ardour/io.cc:1907
+msgid "bad input string in XML node \"%1\""
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:80
+msgid "control protocol name \"%1\" has no descriptor"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:85
+msgid "control protocol name \"%1\" could not be initialized"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:141
+msgid "Instantiating mandatory control protocol %1"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:175
+msgid "Control protocol %1 not usable"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:187
+msgid "Control surface protocol discovered: \"%1\""
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:205
+#, fuzzy
+msgid "ControlProtocolManager: cannot load module \"%1\" (%2)"
+msgstr "LADSPA: ÎÅ ÕÄÁÌÏÓØ ÚÁÇÒÕÚÉÔØ ÍÏÄÕÌØ \"%1\" (%2)"
+
+#: libs/ardour/control_protocol_manager.cc:213
+msgid "ControlProtocolManager: module \"%1\" has no descriptor function."
+msgstr ""
+
+#: libs/ardour/crossfade.cc:121
+msgid "Crossfade: no \"in\" region in state"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:128
+msgid "Crossfade: no \"in\" region %1 found in playlist %2"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:138
+msgid "Crossfade: no \"out\" region in state"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:145
+msgid "Crossfade: no \"out\" region %1 found in playlist %2"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:492
+msgid "active changed"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:741
+msgid "old-style crossfade information - no position information"
+msgstr ""
+
+#: libs/ardour/curve.cc:112 libs/ardour/globals.cc:340
+#: libs/ardour/insert.cc:454 libs/ardour/session.cc:2466
+#: libs/ardour/session.cc:2518
+msgid "programming error: "
+msgstr "ÏÛÉÂËÁ ÐÒÏÇÒÁÍÍÙ: "
+
+#: libs/ardour/cycle_timer.cc:37
+msgid "CycleTimer::get_mhz(): can't open /proc/cpuinfo"
+msgstr ""
+
+#: libs/ardour/cycle_timer.cc:49
+msgid "CycleTimer::get_mhz(): cannot locate cpu MHz in /proc/cpuinfo"
+msgstr ""
+
+#: libs/ardour/cycle_timer.cc:72
+msgid "cannot locate cpu MHz in /proc/cpuinfo"
+msgstr ""
+
+#: libs/ardour/destructive_filesource.cc:188
+msgid "DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"
+msgstr ""
+
+#: libs/ardour/destructive_filesource.cc:201
+#: libs/ardour/destructive_filesource.cc:243
+#: libs/ardour/destructive_filesource.cc:250
+msgid "DestructiveFileSource: \"%1\" bad write (%2)"
+msgstr ""
+
+#: libs/ardour/globals.cc:109
+msgid "no MIDI ports specified: no MMC or MTC control possible"
+msgstr ""
+
+#: libs/ardour/globals.cc:124
+msgid "MIDI port specifications for \"%1\" are not understandable."
+msgstr ""
+
+#: libs/ardour/globals.cc:137 libs/ardour/globals.cc:141
+#: libs/ardour/globals.cc:145
+msgid "default"
+msgstr ""
+
+#: libs/ardour/globals.cc:173
+msgid "No MMC control (MIDI port \"%1\" not available)"
+msgstr ""
+
+#: libs/ardour/globals.cc:179
+msgid "No MTC support (MIDI port \"%1\" not available)"
+msgstr ""
+
+#: libs/ardour/globals.cc:184
+msgid "No MIDI parameter support (MIDI port \"%1\" not available)"
+msgstr ""
+
+#: libs/ardour/import.cc:75
+msgid "Import: cannot open input sound file \"%1\""
+msgstr ""
+
+#: libs/ardour/import.cc:80
+msgid "resampling audio"
+msgstr ""
+
+#: libs/ardour/import.cc:84
+msgid "Import: cannot open converted sound file \"%1\""
+msgstr ""
+
+#: libs/ardour/import.cc:89
+msgid "Import: error while resampling sound file \"%1\""
+msgstr ""
+
+#: libs/ardour/import.cc:148
+msgid "Session::import_audiofile: cannot open new file source for channel %1"
+msgstr ""
+
+#: libs/ardour/import.cc:167
+msgid "converting audio"
+msgstr ""
+
+#: libs/ardour/import.cc:199
+msgid "building region"
+msgstr ""
+
+#: libs/ardour/import.cc:201
+msgid "building regions"
+msgstr ""
+
+#: libs/ardour/import.cc:325
+msgid "Import: could not open temp file: %1"
+msgstr ""
+
+#: libs/ardour/import.cc:334
+msgid "Import: src_new() failed : %1"
+msgstr ""
+
+#: libs/ardour/import.cc:362
+msgid "Import: %1"
+msgstr "éÍÐÏÒÔ: %1"
+
+#: libs/ardour/insert.cc:644 libs/ardour/insert.cc:936
+msgid "XML node describing insert is missing the `type' field"
+msgstr ""
+
+#: libs/ardour/insert.cc:653
+msgid "unknown plugin type %1 in plugin insert state"
+msgstr ""
+
+#: libs/ardour/insert.cc:665
+msgid "XML node describing insert is missing the `id' field"
+msgstr ""
+
+#: libs/ardour/insert.cc:678
+msgid ""
+"Found a reference to a plugin (\"%1\") that is unknown.\n"
+"Perhaps it was removed or moved since it was last used."
+msgstr ""
+
+#: libs/ardour/insert.cc:716 libs/ardour/insert.cc:953
+msgid "XML node describing insert is missing a Redirect node"
+msgstr ""
+
+#: libs/ardour/insert.cc:721
+msgid "XML node describing a plugin insert is missing the `%1' information"
+msgstr ""
+
+#: libs/ardour/insert.cc:745
+msgid "PluginInsert: Auto: no ladspa port number"
+msgstr ""
+
+#: libs/ardour/insert.cc:752
+msgid "PluginInsert: Auto: port id out of range"
+msgstr ""
+
+#: libs/ardour/insert.cc:768
+msgid "XML node describing a port automation is missing the `%1' information"
+msgstr ""
+
+#: libs/ardour/insert.cc:854
+msgid "PortInsert: cannot add input port"
+msgstr ""
+
+#: libs/ardour/insert.cc:859
+msgid "PortInsert: cannot add output port"
+msgstr ""
+
+#: libs/ardour/insert.cc:941
+msgid "non-port insert XML used for port plugin insert"
+msgstr ""
+
+#: libs/ardour/io.cc:598
+msgid "IO: cannot disconnect input port %1 from %2"
+msgstr ""
+
+#: libs/ardour/io.cc:666
+msgid "IO: cannot disconnect output port %1 from %2"
+msgstr ""
+
+#: libs/ardour/io.cc:807 libs/ardour/io.cc:1151 libs/ardour/io.cc:1277
+#, c-format
+msgid "%s/out"
+msgstr ""
+
+#: libs/ardour/io.cc:809 libs/ardour/io.cc:1153 libs/ardour/io.cc:1279
+#: libs/ardour/io.cc:2849
+#, c-format
+msgid "%s/out %u"
+msgstr ""
+
+#: libs/ardour/io.cc:813 libs/ardour/io.cc:1158 libs/ardour/io.cc:1283
+msgid "IO: cannot register output port %1"
+msgstr ""
+
+#: libs/ardour/io.cc:908 libs/ardour/io.cc:1011 libs/ardour/io.cc:1117
+#, c-format
+msgid "%s/in"
+msgstr ""
+
+#: libs/ardour/io.cc:910 libs/ardour/io.cc:1014 libs/ardour/io.cc:1120
+#: libs/ardour/io.cc:2819
+#, c-format
+msgid "%s/in %u"
+msgstr ""
+
+#: libs/ardour/io.cc:914 libs/ardour/io.cc:1020 libs/ardour/io.cc:1125
+msgid "IO: cannot register input port %1"
+msgstr ""
+
+#: libs/ardour/io.cc:1541
+msgid "IO::connecting_became_legal() called without a pending state node"
+msgstr ""
+
+#: libs/ardour/io.cc:1564
+msgid "IO::ports_became_legal() called without a pending state node"
+msgstr ""
+
+#: libs/ardour/io.cc:1594
+msgid "incorrect XML node \"%1\" passed to IO object"
+msgstr ""
+
+#: libs/ardour/io.cc:1649
+msgid ""
+"MIDI gain control specification for %1 is incomplete, so it has been ignored"
+msgstr ""
+
+#: libs/ardour/io.cc:1739 libs/ardour/io.cc:1851
+msgid "Unknown connection \"%1\" listed for output of %2"
+msgstr ""
+
+#: libs/ardour/io.cc:1741 libs/ardour/io.cc:1853
+msgid "out 1"
+msgstr ""
+
+#: libs/ardour/io.cc:1742 libs/ardour/io.cc:1854
+msgid "No output connections available as a replacement"
+msgstr ""
+
+#: libs/ardour/io.cc:1746 libs/ardour/io.cc:1858
+msgid "Connection %1 was not available - \"out 1\" used instead"
+msgstr ""
+
+#: libs/ardour/io.cc:1760
+msgid "%1: cannot create I/O ports"
+msgstr ""
+
+#: libs/ardour/io.cc:1867
+msgid "improper output channel list in XML node (%1)"
+msgstr ""
+
+#: libs/ardour/io.cc:1952
+msgid "IO: badly formed string in XML node for outputs \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:1957
+msgid "IO: bad output string in XML node \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:2525
+msgid "%1: could not open automation event file \"%2\""
+msgstr ""
+
+#: libs/ardour/io.cc:2564
+msgid "%1: cannot open automation event file \"%2\""
+msgstr ""
+
+#: libs/ardour/io.cc:2579
+msgid "badly formed version number in automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:2583
+msgid "no version information in automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:2588
+msgid "mismatched automation event file version (%1)"
+msgstr ""
+
+#: libs/ardour/io.cc:2596
+msgid "badly formatted automation event record at line %1 of %2 (ignored)"
+msgstr ""
+
+#: libs/ardour/io.cc:2616
+msgid "dubious automation event found (and ignored)"
+msgstr ""
+
+#: libs/ardour/io.cc:2620 libs/ardour/panner.cc:438
+#: libs/ardour/redirect.cc:148
+msgid "loaded from disk"
+msgstr ""
+
+#: libs/ardour/io.cc:2791
+msgid "automation write/touch"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:87
+msgid "LADSPA: module has no descriptor function."
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:92
+msgid "LADSPA: plugin has gone away since discovery!"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:99
+msgid "LADSPA: \"%1\" cannot be used, since it cannot do inplace processing"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:329
+msgid ""
+"illegal parameter number used with plugin \"%1\". This mayindicate a change "
+"in the plugin design, and presets may beinvalid"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:430
+msgid "Bad node sent to LadspaPlugin::set_state"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:443
+msgid "LADSPA: no ladspa port number"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:449
+msgid "LADSPA: no ladspa port data"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:498
+msgid ""
+"LADSPA LadspaPlugin MIDI control specification for port %1 is incomplete, so "
+"it has been ignored"
+msgstr ""
+
+#: libs/ardour/location.cc:269
+msgid "incorrect XML node passed to Location::set_state"
+msgstr ""
+
+#: libs/ardour/location.cc:276
+msgid "XML node for Location has no name information"
+msgstr ""
+
+#: libs/ardour/location.cc:283
+msgid "XML node for Location has no start information"
+msgstr ""
+
+#: libs/ardour/location.cc:294
+msgid "XML node for Location has no end information"
+msgstr ""
+
+#: libs/ardour/location.cc:303
+msgid "XML node for Location has no flags information"
+msgstr ""
+
+#: libs/ardour/location.cc:391
+msgid "Locations: attempt to use unknown location as selected location"
+msgstr ""
+
+#: libs/ardour/location.cc:418 libs/ardour/playlist.cc:1187
+msgid "clear"
+msgstr ""
+
+#: libs/ardour/location.cc:443
+msgid "clear markers"
+msgstr ""
+
+#: libs/ardour/location.cc:471
+msgid "clear ranges"
+msgstr ""
+
+#: libs/ardour/location.cc:489
+msgid "add"
+msgstr "ÄÏÂÁ×ÉÔØ"
+
+#: libs/ardour/location.cc:527
+msgid "remove"
+msgstr "ÕÄÁÌÉÔØ"
+
+#: libs/ardour/location.cc:567
+msgid "incorrect XML mode passed to Locations::set_state"
+msgstr ""
+
+#: libs/ardour/mtc_slave.cc:196
+msgid "MTC Slave: atomic read of current time failed, sleeping!"
+msgstr ""
+
+#: libs/ardour/named_selection.cc:77
+msgid "Chunk %1 uses an unknown playlist \"%2\""
+msgstr ""
+
+#: libs/ardour/named_selection.cc:80
+msgid "Chunk %1 contains misformed playlist information"
+msgstr ""
+
+#: libs/ardour/panner.cc:256
+msgid "MIDI pan control specification is incomplete, so it has been ignored"
+msgstr ""
+
+#: libs/ardour/panner.cc:361
+msgid "automation write pass"
+msgstr ""
+
+#: libs/ardour/panner.cc:401
+#, c-format
+msgid "error writing pan automation file (%s)"
+msgstr ""
+
+#: libs/ardour/panner.cc:429
+msgid ""
+"badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"
+msgstr ""
+
+#: libs/ardour/panner.cc:944
+msgid "badly-formed positional data for Multi2dPanner - ignored"
+msgstr ""
+
+#: libs/ardour/panner.cc:1237
+msgid "cannot open pan automation file \"%1\" for saving (%s)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1273
+msgid "cannot open pan automation file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1286
+msgid "badly formed version number in pan automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/panner.cc:1290
+msgid ""
+"no version information in pan automation event file \"%1\" (first line = %2)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1296
+msgid "mismatched pan automation event file version (%1)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1310
+msgid "too many panner states found in pan automation file %1"
+msgstr ""
+
+#: libs/ardour/panner.cc:1451
+msgid "Unknown panner plugin \"%1\" found in pan state - ignored"
+msgstr ""
+
+#: libs/ardour/panner.cc:1457
+msgid "panner plugin node has no type information!"
+msgstr ""
+
+#: libs/ardour/playlist.cc:253
+msgid "playlist const copy constructor called"
+msgstr ""
+
+#: libs/ardour/playlist.cc:259
+msgid "playlist non-const copy constructor called"
+msgstr ""
+
+#: libs/ardour/playlist.cc:499
+msgid "add region"
+msgstr "ÄÏÂÁ×ÉÔØ ÏÂÌÁÓÔØ"
+
+#: libs/ardour/playlist.cc:554
+msgid "replace region"
+msgstr "ÚÁÍÅÎÉÔØ ÏÂÌÁÓÔØ"
+
+#: libs/ardour/playlist.cc:567
+msgid "remove region"
+msgstr "ÕÄÁÌÉÔØ ÏÂÌÁÓÔØ"
+
+#: libs/ardour/playlist.cc:614
+msgid "separate"
+msgstr "ÒÁÚÄÅÌÉÔØ"
+
+#: libs/ardour/playlist.cc:878
+msgid "cut"
+msgstr "×ÙÒÅÚÁÔØ"
+
+#: libs/ardour/playlist.cc:968
+msgid "duplicate"
+msgstr "ÒÁÚÍÎÏÖÉÔØ"
+
+#: libs/ardour/playlist.cc:1023
+msgid "split"
+msgstr "ÓËÌÅÉÔØ"
+
+#: libs/ardour/playlist.cc:1100
+msgid "%1: bounds changed received for region (%2)not in playlist"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1361
+msgid "Playlist: cannot create region from state file"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1721
+msgid "nudged"
+msgstr ""
+
+#: libs/ardour/playlist_factory.cc:49 libs/ardour/playlist_factory.cc:64
+msgid ""
+"programming error: Playlist::createRegion called with unknown Region type"
+msgstr ""
+
+#: libs/ardour/playlist_factory.cc:86
+msgid ""
+"programming error: Playlist::copyPlaylist called with unknown Playlist type"
+msgstr ""
+
+#: libs/ardour/plugin.cc:328
+msgid "Could not locate HOME. Preset not saved."
+msgstr ""
+
+#: libs/ardour/plugin.cc:338 libs/ardour/plugin.cc:344
+msgid "Could not create %1. Preset not saved. (%2)"
+msgstr ""
+
+#: libs/ardour/plugin.cc:349
+msgid "Error saving presets file %1."
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:194
+msgid "Could not parse rdf file: %1"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:235
+msgid "LADSPA: cannot load module \"%1\" (%2)"
+msgstr "LADSPA: ÎÅ ÕÄÁÌÏÓØ ÚÁÇÒÕÚÉÔØ ÍÏÄÕÌØ \"%1\" (%2)"
+
+#: libs/ardour/plugin_manager.cc:242
+msgid "LADSPA: module \"%1\" has no descriptor function."
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:297
+#, fuzzy
+msgid "VST: cannot load module from \"%1\""
+msgstr "LADSPA: ÎÅ ÕÄÁÌÏÓØ ÚÁÇÒÕÚÉÔØ ÍÏÄÕÌØ \"%1\" (%2)"
+
+#: libs/ardour/plugin_manager.cc:302
+msgid "You asked ardour to not use any VST plugins"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:305
+msgid "This version of ardour has no support for VST plugins"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:312
+msgid "LADSPA: cannot load module from \"%1\""
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:374 libs/ardour/plugin_manager.cc:386
+msgid "Unknown"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:464
+msgid ""
+"VST plugin %1 does not support processReplacing, and so cannot be used in "
+"ardour at this time"
+msgstr ""
+
+#: libs/ardour/recent_sessions.cc:44
+msgid "cannot open recent session file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:77
+msgid "programming error: unknown Redirect type in Redirect::Clone!\n"
+msgstr ""
+
+#: libs/ardour/redirect.cc:102 libs/ardour/utils.cc:203
+msgid "pre"
+msgstr "ÐÒÅ"
+
+#: libs/ardour/redirect.cc:104 libs/ardour/utils.cc:206
+msgid "post"
+msgstr "ÐÏÓÔ"
+
+#: libs/ardour/redirect.cc:107
+msgid "Redirect: unknown placement string \"%1\" (ignored)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:125
+msgid "%1: cannot open %2 to load automation data (%3)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:154
+msgid "%1: cannot load automation data from %2"
+msgstr ""
+
+#: libs/ardour/redirect.cc:175
+msgid "%1: cannot open %2 to store automation data (%3)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:194 libs/ardour/redirect.cc:201
+msgid "%1: could not save automation state to %2"
+msgstr ""
+
+#: libs/ardour/redirect.cc:246
+msgid "Could not get state from Redirect (%1). Problem with save_automation"
+msgstr ""
+
+#: libs/ardour/redirect.cc:296
+msgid "incorrect XML node \"%1\" passed to Redirect object"
+msgstr ""
+
+#: libs/ardour/redirect.cc:318
+msgid "%1: Automation node has no path property"
+msgstr ""
+
+#: libs/ardour/redirect.cc:343
+msgid "XML node describing an IO is missing an IO node"
+msgstr ""
+
+#: libs/ardour/redirect.cc:348
+msgid "XML node describing a redirect is missing the `active' field"
+msgstr ""
+
+#: libs/ardour/redirect.cc:358
+msgid "XML node describing a redirect is missing the `placement' field"
+msgstr ""
+
+#: libs/ardour/redirect.cc:467
+msgid "active_changed"
+msgstr ""
+
+#: libs/ardour/region.cc:885
+msgid "Session: XMLNode describing a Region is incomplete (no id)"
+msgstr ""
+
+#: libs/ardour/region.cc:892
+msgid "Session: XMLNode describing a Region is incomplete (no name)"
+msgstr ""
+
+#: libs/ardour/route.cc:79 libs/ardour/session.cc:1554
+#: libs/ardour/session.cc:1560 libs/ardour/session.cc:3093
+msgid "signal"
+msgstr ""
+
+#: libs/ardour/route.cc:1430
+msgid "Could not get state of route. Problem with save_automation"
+msgstr ""
+
+#: libs/ardour/route.cc:1482
+msgid "Send construction failed"
+msgstr ""
+
+#: libs/ardour/route.cc:1504
+msgid "unknown Insert type \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/route.cc:1510
+msgid "Insert XML node has no type property"
+msgstr ""
+
+#: libs/ardour/route.cc:1515
+msgid "insert could not be created. Ignored."
+msgstr ""
+
+#: libs/ardour/route.cc:1533
+msgid "Bad node sent to Route::set_state() [%1]"
+msgstr ""
+
+#: libs/ardour/route.cc:1592
+msgid "Route %1: unknown edit group \"%2 in saved state (ignored)"
+msgstr ""
+
+#: libs/ardour/route.cc:1608 libs/ardour/route.cc:1612
+msgid "badly formed order key string in state file! [%1] ... ignored."
+msgstr ""
+
+#: libs/ardour/route.cc:1693 libs/ardour/route.cc:1820
+msgid "[control]"
+msgstr ""
+
+#: libs/ardour/route.cc:1713
+msgid "Route %1: unknown mix group \"%2 in saved state (ignored)"
+msgstr ""
+
+#: libs/ardour/route.cc:1742 libs/ardour/route.cc:1750
+msgid ""
+"MIDI mute control specification for %1 is incomplete, so it has been ignored"
+msgstr ""
+
+#: libs/ardour/send.cc:99
+msgid "XML node describing a send is missing a Redirect node"
+msgstr ""
+
+#: libs/ardour/session.cc:103
+msgid "Could not resolve path: %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session.cc:115
+msgid "cannot check session path %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session.cc:145
+msgid "cannot check statefile %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session.cc:181
+msgid "%1 is not an Ardour snapshot file"
+msgstr ""
+
+#: libs/ardour/session.cc:198
+msgid "cannot determine current working directory (%1)"
+msgstr ""
+
+#: libs/ardour/session.cc:215
+msgid "unknown file type for session %1"
+msgstr ""
+
+#: libs/ardour/session.cc:320
+msgid "monitor"
+msgstr "ÍÏÎÉÔÏÒ"
+
+#: libs/ardour/session.cc:327
+msgid "master"
+msgstr "ÍÁÓÔÅÒ"
+
+#: libs/ardour/session.cc:611
+msgid "could not setup Click I/O"
+msgstr ""
+
+#: libs/ardour/session.cc:632
+msgid "cannot setup Click I/O"
+msgstr ""
+
+#: libs/ardour/session.cc:654
+msgid "cannot create Auditioner: no auditioning of regions possible"
+msgstr ""
+
+#: libs/ardour/session.cc:666
+#, c-format
+msgid "out %<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:678
+#, c-format
+msgid "in %<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:692
+#, c-format
+msgid "out %<PRIu32>+%<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:706
+#, c-format
+msgid "in %<PRIu32>+%<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:739
+msgid "cannot setup master inputs"
+msgstr ""
+
+#: libs/ardour/session.cc:747
+msgid "cannot setup master outputs"
+msgstr ""
+
+#: libs/ardour/session.cc:758
+#, fuzzy
+msgid "Master Out"
+msgstr "ÍÁÓÔÅÒ"
+
+#: libs/ardour/session.cc:830
+msgid "cannot setup control inputs"
+msgstr ""
+
+#: libs/ardour/session.cc:838
+msgid "cannot set up master outputs"
+msgstr ""
+
+#: libs/ardour/session.cc:1110
+msgid "Session: you can't use that location for auto punch (start <= end)"
+msgstr ""
+
+#: libs/ardour/session.cc:1189
+msgid "Session: you can't use a mark for auto loop"
+msgstr ""
+
+#: libs/ardour/session.cc:1572
+msgid "feedback loop setup between %1 and %2"
+msgstr ""
+
+#: libs/ardour/session.cc:1724 libs/ardour/session.cc:1821
+msgid "cannot configure %1 in/%2 out configuration for new audio track"
+msgstr ""
+
+#: libs/ardour/session.cc:1780
+msgid "Session: could not create new audio track."
+msgstr ""
+
+#: libs/ardour/session.cc:1870
+msgid "Session: could not create new route."
+msgstr ""
+
+#: libs/ardour/session.cc:2354
+msgid "cannot create new name for region \"%1\""
+msgstr ""
+
+#: libs/ardour/session.cc:2418
+msgid "too many regions with names like %1"
+msgstr ""
+
+#: libs/ardour/session.cc:2883
+msgid "There are already %1 recordings for %2, which I consider too many."
+msgstr ""
+
+#: libs/ardour/session.cc:3258
+msgid "programming error: unknown type of Insert created!"
+msgstr ""
+
+#: libs/ardour/session.cc:3264
+msgid "programming error: unknown type of Redirect created!"
+msgstr ""
+
+#: libs/ardour/session.cc:3287
+msgid "programming error: unknown type of Insert deleted!"
+msgstr ""
+
+#: libs/ardour/session.cc:3293
+msgid "programming error: unknown type of Redirect deleted!"
+msgstr ""
+
+#: libs/ardour/session.cc:3636
+msgid "too many bounced versions of playlist \"%1\""
+msgstr ""
+
+#: libs/ardour/session.cc:3649
+msgid "cannot create new audio file \"%1\" for %2"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:85 libs/ardour/session_butler.cc:90
+msgid "UI: cannot set O_NONBLOCK on butler request pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:95
+msgid "Session: could not create butler thread"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:189
+msgid "poll on butler request pipe failed (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:196
+msgid "Error on butler thread request pipe"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:238
+msgid "Error reading from butler request pipe"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:275
+msgid "Butler read ahead failure on dstream %1"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:319
+msgid "Butler write-behind failure on dstream %1"
+msgstr ""
+
+#: libs/ardour/session_click.cc:158
+msgid "cannot open click soundfile %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session_click.cc:167
+msgid "cannot read data from click soundfile"
+msgstr ""
+
+#: libs/ardour/session_click.cc:192
+msgid "cannot open click emphasis soundfile %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session_click.cc:200
+msgid "cannot read data from click emphasis soundfile"
+msgstr ""
+
+#: libs/ardour/session_events.cc:161
+msgid "Session: cannot have two events of type %1 at the same frame (%2)."
+msgstr ""
+
+#: libs/ardour/session_events.cc:422
+msgid "Programming error: illegal event type in process_event (%1)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:63
+msgid "Export: no output file specified"
+msgstr ""
+
+#: libs/ardour/session_export.cc:164 libs/ardour/session_export.cc:169
+msgid "illegal frame range in export specification"
+msgstr ""
+
+#: libs/ardour/session_export.cc:174
+msgid "Bad data width size. Report me!"
+msgstr ""
+
+#: libs/ardour/session_export.cc:204
+msgid "Export: cannot open output file \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:214
+msgid "cannot initialize sample rate conversion: %1"
+msgstr ""
+
+#: libs/ardour/session_export.cc:316
+msgid "an error occured during sample rate conversion: %1"
+msgstr ""
+
+#: libs/ardour/session_export.cc:327
+msgid "warning, leftover frames overflowed, glitches might occur in output"
+msgstr ""
+
+#: libs/ardour/session_export.cc:418
+msgid "Export: could not write data to output file (%1)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:500
+msgid "%1: cannot seek to %2 for export"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:200
+msgid "Ardour is slaved to MTC - port cannot be reset"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:215
+msgid "unknown port %1 requested for MTC"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:541
+msgid "Error reading from MIDI port %1"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:914
+msgid "Session: could not send full MIDI time code"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:973
+msgid "Session: cannot send quarter-frame MTC message (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1081
+msgid "MMC: cannot send command %1%2%3"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1188
+msgid "UI: cannot set O_NONBLOCK on signal read pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1193
+msgid "UI: cannot set O_NONBLOCK on signal write pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1198
+msgid "Session: could not create transport thread"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1227
+msgid "cannot send signal to midi thread! (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1322
+msgid "MIDI thread poll failed (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1334
+msgid "Error on transport thread request pipe"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1361
+msgid "Error reading from transport request pipe"
+msgstr ""
+
+#: libs/ardour/session_process.cc:104
+msgid "Session: error in no roll for %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:101
+msgid "Could not use path %1 (%s)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:129
+msgid "end"
+msgstr ""
+
+#: libs/ardour/session_state.cc:130
+#, fuzzy
+msgid "start"
+msgstr "ÒÁÚÄÅÌÉÔØ"
+
+#: libs/ardour/session_state.cc:502
+msgid "Session: cannot create session dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:513
+msgid "Session: cannot create session peakfile dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:522
+msgid "Session: cannot create session sounds dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:531
+msgid "Session: cannot create session tape dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:540
+msgid "Session: cannot create session dead sounds dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:549
+msgid "Session: cannot create session automation dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:580
+msgid "Could not open %1 for writing mix template"
+msgstr ""
+
+#: libs/ardour/session_state.cc:586
+msgid "Could not open mix template %1 for reading"
+msgstr ""
+
+#: libs/ardour/session_state.cc:593
+msgid "Session already exists. Not overwriting"
+msgstr ""
+
+#: libs/ardour/session_state.cc:636
+msgid "Session: could not load diskstream via XML state"
+msgstr ""
+
+#: libs/ardour/session_state.cc:685
+msgid "could not backup old state file, current state not saved."
+msgstr ""
+
+#: libs/ardour/session_state.cc:698
+msgid "state could not be saved to %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:705
+msgid "could not remove corrupt state file %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:709
+msgid "could not restore state file from backup %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:778
+msgid "%1: session state information file \"%2\" doesn't exist!"
+msgstr ""
+
+#: libs/ardour/session_state.cc:789
+msgid "Could not understand ardour file %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1493
+msgid "programming error: Session: incorrect XML node sent to set_state()"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1539
+msgid "Session: XML state has no options section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1544
+msgid "Session: XML state has no sources section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1551
+msgid "Session: XML state has no Regions section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1558
+msgid "Session: XML state has no playlists section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1577
+msgid "Session: XML state has no diskstreams section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1584
+msgid "Session: XML state has no connections section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1591
+msgid "Session: XML state has no locations section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1624
+msgid "Session: XML state has no edit groups section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1631
+msgid "Session: XML state has no mix groups section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1638
+msgid "Session: XML state has no Tempo Map section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1645
+msgid "Session: XML state has no routes section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1652
+msgid "Session: XML state has no click section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1697
+msgid "Session: cannot create Route from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1735
+msgid "Session: cannot create Region from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1764
+msgid "Session: XMLNode describing a AudioRegion is incomplete (no source)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1772 libs/ardour/session_state.cc:1792
+msgid ""
+"Session: XMLNode describing a AudioRegion references an unknown source id =%1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1778 libs/ardour/session_state.cc:1798
+msgid ""
+"Session: XMLNode describing a AudioRegion references a non-audio source id =%"
+"1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1868
+msgid "Session: cannot create Source from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1889
+msgid ""
+"Found a sound file that cannot be used by Ardour. Talk to the progammers."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1913
+msgid "Could not create mix templates directory \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1927
+msgid "Template \"%1\" already exists - new version not created"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1934
+msgid "mix template not saved"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1994
+msgid "cannot create session directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2007
+msgid "cannot create sounds directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2018
+msgid "cannot create dead sounds directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2029
+msgid "cannot create peak file directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2168 libs/ardour/session_state.cc:2189
+msgid "Session: cannot create Playlist from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:2228
+msgid "Session: cannot create Named Selection from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:2360
+msgid "Unknown node \"%1\" found in Connections list from state file"
+msgstr ""
+
+#: libs/ardour/session_state.cc:3197
+msgid "cannot remove dead sound file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session_time.cc:374
+msgid "Unknown JACK transport state %1 in sync callback"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:77
+msgid "tempoize: error creating name for new audio file based on %1"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:88
+msgid "tempoize: error creating new audio file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:114
+msgid "tempoize: error reading data from %1"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:127 libs/ardour/session_timefx.cc:139
+msgid "error writing tempo-adjusted data to %1"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:145
+msgid "timefx code failure. please notify ardour-developers."
+msgstr ""
+
+#: libs/ardour/session_transport.cc:117
+msgid "Cannot loop - no loop range defined"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:479
+msgid ""
+"Seamless looping cannot be supported while Ardour is using JACK transport.\n"
+"Recommend changing the configured options"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:755
+msgid ""
+"Global varispeed cannot be supported while Ardour is connected to JACK "
+"transport control"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:955
+msgid "please stop the transport before adjusting slave settings"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:991
+msgid "No MTC port defined: MTC slaving is impossible."
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:15
+msgid "WAV"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:16
+msgid "AIFF"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:17
+msgid "raw (no header)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:18
+msgid "PAF (Ensoniq Paris)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:19
+msgid "AU (Sun/NeXT)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:20
+msgid "IRCAM"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:21
+msgid "W64 (64 bit WAV)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:26
+msgid ".wav"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:27
+msgid ".aiff"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:28
+msgid ".raw"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:29
+msgid ".paf"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:30
+msgid ".au"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:31
+msgid ".ircam"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:32
+msgid ".w64"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:47
+msgid "16 bit"
+msgstr "16 ÂÉÔ"
+
+#: libs/ardour/sndfile_helpers.cc:48
+msgid "24 bit"
+msgstr "24 ÂÉÔÁ"
+
+#: libs/ardour/sndfile_helpers.cc:49
+msgid "32 bit"
+msgstr "32 ÂÉÔÁ"
+
+#: libs/ardour/sndfile_helpers.cc:50
+msgid "8 bit"
+msgstr "8 ÂÉÔ"
+
+#: libs/ardour/sndfile_helpers.cc:51
+msgid "float"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:64
+msgid "Little-endian (Intel)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:65
+msgid "Big-endian (Mac)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:147
+msgid "FileSource: cannot get host information for BWF header (%1)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:169
+msgid ""
+"cannot set broadcast info for audio file %1 (%2); dropping broadcast info "
+"for this file"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:220
+msgid "SndFileSource: cannot open file \"%1\" for %2 (%3)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:226
+msgid ""
+"SndFileSource: file only contains %1 channels; %2 is invalid as a channel "
+"number"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:327
+msgid "SndFileSource: could not seek to frame %1 within %2 (%3)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:378
+#, fuzzy
+msgid "programming error: %1 %2"
+msgstr "ÏÛÉÂËÁ ÐÒÏÇÒÁÍÍÙ: "
+
+#: libs/ardour/sndfilesource.cc:487 libs/ardour/sndfilesource.cc:533
+msgid ""
+"cannot set broadcast info for audio file %1; Dropping broadcast info for "
+"this file"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:544
+msgid "%1: cannot seek to %2"
+msgstr ""
+
+#: libs/ardour/state_manager.cc:47
+msgid "cleared history"
+msgstr ""
+
+#: libs/ardour/state_manager.cc:60
+msgid ""
+"programming error: illegal state ID (%1) passed to StateManager::set_state() "
+"(range = 0-%2)"
+msgstr ""
+
+#: libs/ardour/stateful.cc:102
+msgid "Error: could not write %1"
+msgstr ""
+
+#: libs/ardour/stateful.cc:116
+msgid "Could not understand XML file %1"
+msgstr ""
+
+#: libs/ardour/tempo.cc:67
+msgid "TempoSection XML node has no \"start\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:75
+msgid "TempoSection XML node has an illegal \"start\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:82
+msgid "TempoSection XML node has no \"beats-per-minute\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:87
+msgid "TempoSection XML node has an illegal \"beats_per_minute\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:92
+msgid "TempoSection XML node has no \"movable\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:131
+msgid "MeterSection XML node has no \"start\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:139
+msgid "MeterSection XML node has an illegal \"start\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:146
+msgid "MeterSection XML node has no \"beats-per-bar\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:151
+msgid "MeterSection XML node has an illegal \"beats-per-bar\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:156
+msgid "MeterSection XML node has no \"note-type\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:161
+msgid "MeterSection XML node has an illegal \"note-type\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:166
+msgid "MeterSection XML node has no \"movable\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:259
+msgid "move metric"
+msgstr ""
+
+#: libs/ardour/tempo.cc:330
+msgid "metric removed"
+msgstr ""
+
+#: libs/ardour/tempo.cc:373
+msgid "add tempo"
+msgstr ""
+
+#: libs/ardour/tempo.cc:402
+msgid "replace tempo"
+msgstr ""
+
+#: libs/ardour/tempo.cc:435
+msgid "add meter"
+msgstr ""
+
+#: libs/ardour/tempo.cc:463
+msgid "replaced meter"
+msgstr ""
+
+#: libs/ardour/tempo.cc:483 libs/ardour/tempo.cc:499
+msgid "programming error: no tempo section in tempo map!"
+msgstr ""
+
+#: libs/ardour/tempo.cc:538
+msgid "programming error: unhandled MetricSection type"
+msgstr ""
+
+#: libs/ardour/tempo.cc:1226 libs/ardour/tempo.cc:1238
+msgid "Tempo map: could not set new state, restoring old one."
+msgstr ""
+
+#: libs/ardour/tempo.cc:1262
+msgid "load XML data"
+msgstr ""
+
+#: libs/ardour/utils.cc:246
+msgid "illegal or badly-formed string used for path (%1)"
+msgstr ""
+
+#: libs/ardour/utils.cc:251
+msgid "path (%1) is ambiguous"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:187
+msgid "cannot create VST chunk directory: %1"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:195
+msgid "cannot check VST chunk directory: %1"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:202
+msgid "%1 exists but is not a directory"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:240
+msgid "Bad node sent to VSTPlugin::set_state"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:343 libs/ardour/vst_plugin.cc:354
+msgid "no support for presets using chunks at this time"
+msgstr ""
+
+#: libs/ardour/coreaudiosource.cc:97
+msgid ""
+"CoreAudioSource: file only contains %1 channels; %2 is invalid as a channel "
+"number"
+msgstr ""
+
+#: libs/ardour/coreaudiosource.cc:162
+msgid "CoreAudioSource: could not seek to frame %1 within %2 (%3)"
+msgstr ""
diff --git a/libs/ardour/po/sv_SE.po b/libs/ardour/po/sv_SE.po
new file mode 100644
index 0000000000..ddc7f108bb
--- /dev/null
+++ b/libs/ardour/po/sv_SE.po
@@ -0,0 +1,2025 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR "Paul Davis"
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: ardour\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2006-10-03 00:39+0200\n"
+"PO-Revision-Date: 2006-10-03 01:09+GMT+1\n"
+"Last-Translator: Petter Sundlöf <petter.sundlof@findus.dhs.org>\n"
+"Language-Team: Swedish <sv@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: libs/ardour/diskstream.cc:258
+msgid "Location \"%1\" not valid for track loop (start >= end)"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:298
+msgid "AudioDiskstream: Playlist \"%1\" isn't an audio playlist"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:349
+msgid "AudioDiskstream %1: there is no existing playlist to make a copy of!"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:924 libs/ardour/audio_diskstream.cc:935
+msgid ""
+"AudioDiskstream %1: when refilling, cannot read %2 from playlist at frame %3"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1069
+msgid "AudioDiskstream %1: cannot read %2 from playlist at frame %3"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1412 libs/ardour/audio_diskstream.cc:1429
+msgid "AudioDiskstream %1: cannot write to disk"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1473
+msgid "AudioDiskstream \"%1\": cannot flush captured data to disk!"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1563
+msgid "%1: could not create region for complete audio file"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1587
+msgid "AudioDiskstream: could not create region for captured audio!"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1643
+msgid "programmer error: %1"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1929
+msgid "AudioDiskstream: channel %1 out of range"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:1952
+msgid "%1:%2 new capture file not initialized correctly"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2178
+msgid "%1: cannot restore pending capture source file %2"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2200
+msgid "%1: incorrect number of pending sources listed - ignoring them all"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2215
+msgid "%1: cannot create whole-file region from pending capture sources"
+msgstr ""
+
+#: libs/ardour/audio_diskstream.cc:2227
+msgid "%1: cannot create region from pending capture sources"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:92
+msgid "channels"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:93
+msgid "samplerate"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:94
+msgid "resolution"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:95
+msgid "format"
+msgstr ""
+
+#: libs/ardour/audio_library.cc:102
+msgid "Could not open %1. Audio Library not saved"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:53 libs/ardour/audio_playlist.cc:63
+#: libs/ardour/audio_playlist.cc:74 libs/ardour/audio_playlist.cc:121
+#: libs/ardour/insert.cc:84 libs/ardour/insert.cc:103
+#: libs/ardour/insert.cc:128 libs/ardour/insert.cc:862
+#: libs/ardour/insert.cc:870 libs/ardour/send.cc:39 libs/ardour/send.cc:53
+#: libs/ardour/send.cc:62 libs/ardour/session_state.cc:1128
+#: libs/ardour/session_state.cc:1170
+msgid "initial state"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:261 libs/ardour/audio_playlist.cc:743
+msgid ""
+"programming error: non-audio Region passed to remove_overlap in audio "
+"playlist"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:388
+msgid ""
+"programming error: non-audio Region tested for overlap in audio playlist"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:851
+msgid "xfade change"
+msgstr ""
+
+#: libs/ardour/audio_playlist.cc:874
+msgid "region modified"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:105 libs/ardour/io.cc:1696
+#: libs/ardour/io.cc:1762
+msgid "Unknown connection \"%1\" listed for input of %2"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:107 libs/ardour/io.cc:1698
+#: libs/ardour/io.cc:1764
+msgid "in 1"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:108 libs/ardour/io.cc:1699
+#: libs/ardour/io.cc:1765
+msgid "No input connections available as a replacement"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:112 libs/ardour/io.cc:1703
+#: libs/ardour/io.cc:1769
+msgid "Connection %1 was not available - \"in 1\" used instead"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:121 libs/ardour/io.cc:1778
+msgid "improper input channel list in XML node (%1)"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:162 libs/ardour/audio_track.cc:175
+msgid "AudioTrack: audio diskstream \"%1\" not known by session"
+msgstr ""
+
+#: libs/ardour/audio_track.cc:216
+msgid "programming error: AudioTrack given state without diskstream!"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:146
+msgid "cannot activate JACK client"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:421
+msgid "register input port called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:457
+msgid "register output port called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:539
+msgid "connect called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:555
+msgid "AudioEngine: cannot connect %1 (%2) to %3 (%4)"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:568 libs/ardour/audioengine.cc:597
+msgid "disconnect called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:655
+msgid "get_port_by_name() called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:699
+msgid "get_ports called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:817
+msgid "get_nth_physical called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:845
+msgid "get_port_total_latency() called with no JACK client connection"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:851
+msgid "get_port_total_latency() called before engine was started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:982
+msgid "Unable to connect to JACK server"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:985
+msgid "Could not connect to JACK server as \"%1\""
+msgstr ""
+
+#: libs/ardour/audioengine.cc:990
+msgid "JACK server started"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:1024
+msgid "cannot shutdown connection to JACK"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:1049
+msgid "failed to connect to JACK"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:1067
+msgid "could not reregister %1"
+msgstr ""
+
+#: libs/ardour/audioengine.cc:1125
+msgid "could not reconnect %1 and %2 (err = %3)"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:355 libs/ardour/session_state.cc:2575
+msgid ""
+"there are already 1000 files with names like %1; versioning discontinued"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:369 libs/ardour/session_state.cc:2589
+msgid "cannot rename audio file source from %1 to %2 (%3)"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:376 libs/ardour/session_state.cc:2604
+msgid "cannot remove peakfile %1 for %2 (%3)"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:420
+msgid "FileSource: search path not set"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:444
+msgid ""
+"FileSource: \"%1\" is ambigous when searching %2\n"
+"\t"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:450
+msgid "Filesource: cannot find required file (%1): while searching %2"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:473
+msgid "Filesource: cannot find required file (%1): %2"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:478
+msgid "Filesource: cannot check for existing file (%1): %2"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:534 libs/ardour/insert.cc:532
+#: libs/ardour/session.cc:1967 libs/ardour/sndfilesource.cc:109
+msgid "programming error: %1"
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:540
+msgid ""
+"Programming error! Ardour tried to rename a file over another file! It's "
+"safe to continue working, but please report this to the developers."
+msgstr ""
+
+#: libs/ardour/audiofilesource.cc:545
+msgid "cannot rename audio file for %1 to %2"
+msgstr ""
+
+#: libs/ardour/audiofilter.cc:47
+msgid "audiofilter: error creating name for new audio file based on %1"
+msgstr ""
+
+#: libs/ardour/audiofilter.cc:57
+msgid "audiofilter: error creating new audio file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/audioregion.cc:888 libs/ardour/audioregion.cc:950
+msgid "fade in change"
+msgstr ""
+
+#: libs/ardour/audioregion.cc:1321
+#, c-format
+msgid "normalized to %.2fdB"
+msgstr ""
+
+#: libs/ardour/audioregion.cc:1339
+msgid "envelope change"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:144
+msgid "poll on peak request pipe failed (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:151
+msgid "Error on peak thread request pipe"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:184
+msgid "Error reading from peak request pipe"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:216 libs/ardour/session_butler.cc:80
+#: libs/ardour/session_midi.cc:1073
+msgid "Cannot create transport request signal pipe (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:221 libs/ardour/audiosource.cc:226
+msgid "UI: cannot set O_NONBLOCK on peak request pipe (%1)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:231
+msgid "AudioSource: could not create peak thread"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:326
+msgid "cannot rename peakfile for %1 from %2 to %3 (%4)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:368
+msgid "AudioSource: cannot stat peakfile \"%1\""
+msgstr ""
+
+#: libs/ardour/audiosource.cc:466
+msgid "cannot read sample data for unscaled peak computation"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:486 libs/ardour/audiosource.cc:557
+#: libs/ardour/audiosource.cc:793 libs/ardour/audiosource.cc:882
+msgid "AudioSource: cannot open peakpath \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:657
+msgid "AudioSource[%1]: peak read - cannot read %2 samples at offset %3"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:804
+msgid "%1: could not write read raw data for peak computation (%2)"
+msgstr ""
+
+#: libs/ardour/audiosource.cc:829
+msgid "%1: could not write peak file data (%2)"
+msgstr ""
+
+#: libs/ardour/auditioner.cc:118
+msgid "Auditioning of non-audio regions not yet supported"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:67 libs/ardour/location.cc:375
+#: libs/ardour/tempo.cc:226
+msgid "initial"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:240
+msgid "cleared"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:412
+msgid "added event"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:429
+msgid "removed event"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:444
+msgid "removed multiple events"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:475 libs/ardour/automation_event.cc:506
+msgid "removed range"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:536
+msgid "event range adjusted"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:558
+msgid "event adjusted"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:673 libs/ardour/automation_event.cc:778
+#: libs/ardour/panner.cc:889
+msgid "programming error:"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1087
+msgid "cut/copy/clear"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1120
+msgid "copy"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1188 libs/ardour/playlist.cc:960
+msgid "paste"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1243
+msgid ""
+"automation list: no x-coordinate stored for control point (point ignored)"
+msgstr ""
+
+#: libs/ardour/automation_event.cc:1249
+msgid ""
+"automation list: no y-coordinate stored for control point (point ignored)"
+msgstr ""
+
+#: libs/ardour/configuration.cc:87
+msgid "loading system configuration file %1"
+msgstr ""
+
+#: libs/ardour/configuration.cc:90
+msgid "Ardour: cannot read system configuration file \"%1\""
+msgstr ""
+
+#: libs/ardour/configuration.cc:97
+msgid "Ardour: system configuration file \"%1\" not loaded successfully."
+msgstr ""
+
+#: libs/ardour/configuration.cc:111
+msgid "loading user configuration file %1"
+msgstr ""
+
+#: libs/ardour/configuration.cc:114
+msgid "Ardour: cannot read configuration file \"%1\""
+msgstr ""
+
+#: libs/ardour/configuration.cc:121
+msgid "Ardour: user configuration file \"%1\" not loaded successfully."
+msgstr ""
+
+#: libs/ardour/configuration.cc:141
+msgid "Config file %1 not saved"
+msgstr ""
+
+#: libs/ardour/configuration.cc:226
+msgid "ill-formed MIDI port specification in ardour rcfile (ignored)"
+msgstr ""
+
+#: libs/ardour/connection.cc:183
+msgid "Node for Connection has no \"name\" property"
+msgstr ""
+
+#: libs/ardour/connection.cc:191
+msgid "Node for Connection has no \"connections\" property"
+msgstr ""
+
+#: libs/ardour/connection.cc:227 libs/ardour/io.cc:1838
+msgid "IO: badly formed string in XML node for inputs \"%1\""
+msgstr ""
+
+#: libs/ardour/connection.cc:232 libs/ardour/io.cc:1843
+msgid "bad input string in XML node \"%1\""
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:84
+msgid "control protocol name \"%1\" has no descriptor"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:89
+msgid "control protocol name \"%1\" could not be initialized"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:145
+msgid "Instantiating mandatory control protocol %1"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:179
+msgid "Control protocol %1 not usable"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:192
+msgid "Control surface protocol discovered: \"%1\""
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:210
+msgid "ControlProtocolManager: cannot load module \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/control_protocol_manager.cc:218
+msgid "ControlProtocolManager: module \"%1\" has no descriptor function."
+msgstr ""
+
+#: libs/ardour/crossfade.cc:120
+msgid "Crossfade: no \"in\" region in state"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:127
+msgid "Crossfade: no \"in\" region %1 found in playlist %2"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:137
+msgid "Crossfade: no \"out\" region in state"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:144
+msgid "Crossfade: no \"out\" region %1 found in playlist %2"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:491
+msgid "active changed"
+msgstr ""
+
+#: libs/ardour/crossfade.cc:740
+msgid "old-style crossfade information - no position information"
+msgstr ""
+
+#: libs/ardour/curve.cc:117 libs/ardour/globals.cc:348
+#: libs/ardour/insert.cc:454 libs/ardour/session.cc:2432
+#: libs/ardour/session.cc:2486
+msgid "programming error: "
+msgstr ""
+
+#: libs/ardour/cycle_timer.cc:37
+msgid "CycleTimer::get_mhz(): can't open /proc/cpuinfo"
+msgstr ""
+
+#: libs/ardour/cycle_timer.cc:49
+msgid "CycleTimer::get_mhz(): cannot locate cpu MHz in /proc/cpuinfo"
+msgstr ""
+
+#: libs/ardour/cycle_timer.cc:72
+msgid "cannot locate cpu MHz in /proc/cpuinfo"
+msgstr ""
+
+#: libs/ardour/destructive_filesource.cc:200
+msgid "DestructiveFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"
+msgstr ""
+
+#: libs/ardour/destructive_filesource.cc:213
+#: libs/ardour/destructive_filesource.cc:258
+#: libs/ardour/destructive_filesource.cc:265
+msgid "DestructiveFileSource: \"%1\" bad write (%2)"
+msgstr ""
+
+#: libs/ardour/destructive_filesource.cc:403
+msgid ""
+"Filesource: start time is already set for existing file (%1): Cannot change "
+"start time."
+msgstr ""
+
+#: libs/ardour/globals.cc:110
+msgid "no MIDI ports specified: no MMC or MTC control possible"
+msgstr ""
+
+#: libs/ardour/globals.cc:125
+msgid "MIDI port specifications for \"%1\" are not understandable."
+msgstr ""
+
+#: libs/ardour/globals.cc:138 libs/ardour/globals.cc:142
+#: libs/ardour/globals.cc:146
+msgid "default"
+msgstr ""
+
+#: libs/ardour/globals.cc:174
+msgid "No MMC control (MIDI port \"%1\" not available)"
+msgstr ""
+
+#: libs/ardour/globals.cc:180
+msgid "No MTC support (MIDI port \"%1\" not available)"
+msgstr ""
+
+#: libs/ardour/globals.cc:185
+msgid "No MIDI parameter support (MIDI port \"%1\" not available)"
+msgstr ""
+
+#: libs/ardour/import.cc:77
+msgid "Import: cannot open input sound file \"%1\""
+msgstr ""
+
+#: libs/ardour/import.cc:82
+msgid "resampling audio"
+msgstr ""
+
+#: libs/ardour/import.cc:86
+msgid "Import: cannot open converted sound file \"%1\""
+msgstr ""
+
+#: libs/ardour/import.cc:91
+msgid "Import: error while resampling sound file \"%1\""
+msgstr ""
+
+#: libs/ardour/import.cc:145
+msgid "Session::import_audiofile: cannot open new file source for channel %1"
+msgstr ""
+
+#: libs/ardour/import.cc:163
+msgid "converting audio"
+msgstr ""
+
+#: libs/ardour/import.cc:195
+msgid "building region"
+msgstr ""
+
+#: libs/ardour/import.cc:197
+msgid "building regions"
+msgstr ""
+
+#: libs/ardour/import.cc:309
+msgid "Import/SRC: could not open input file: %1"
+msgstr ""
+
+#: libs/ardour/import.cc:317
+msgid "Import/SRC: could not open output file: %1"
+msgstr ""
+
+#: libs/ardour/import.cc:326
+msgid "Import: src_new() failed : %1"
+msgstr ""
+
+#: libs/ardour/import.cc:354
+msgid "Import: %1"
+msgstr ""
+
+#: libs/ardour/insert.cc:651 libs/ardour/insert.cc:960
+msgid "XML node describing insert is missing the `type' field"
+msgstr ""
+
+#: libs/ardour/insert.cc:660
+msgid "unknown plugin type %1 in plugin insert state"
+msgstr ""
+
+#: libs/ardour/insert.cc:672
+msgid "XML node describing insert is missing the `id' field"
+msgstr ""
+
+#: libs/ardour/insert.cc:685
+msgid ""
+"Found a reference to a plugin (\"%1\") that is unknown.\n"
+"Perhaps it was removed or moved since it was last used."
+msgstr ""
+
+#: libs/ardour/insert.cc:723 libs/ardour/insert.cc:977
+msgid "XML node describing insert is missing a Redirect node"
+msgstr ""
+
+#: libs/ardour/insert.cc:728
+msgid "XML node describing a plugin insert is missing the `%1' information"
+msgstr ""
+
+#: libs/ardour/insert.cc:752
+msgid "PluginInsert: Auto: no ladspa port number"
+msgstr ""
+
+#: libs/ardour/insert.cc:759
+msgid "PluginInsert: Auto: port id out of range"
+msgstr ""
+
+#: libs/ardour/insert.cc:775
+msgid "XML node describing a port automation is missing the `%1' information"
+msgstr ""
+
+#: libs/ardour/insert.cc:878
+msgid "PortInsert: cannot add input port"
+msgstr ""
+
+#: libs/ardour/insert.cc:883
+msgid "PortInsert: cannot add output port"
+msgstr ""
+
+#: libs/ardour/insert.cc:965
+msgid "non-port insert XML used for port plugin insert"
+msgstr ""
+
+#: libs/ardour/io.cc:603
+msgid "IO: cannot disconnect input port %1 from %2"
+msgstr ""
+
+#: libs/ardour/io.cc:671
+msgid "IO: cannot disconnect output port %1 from %2"
+msgstr ""
+
+#: libs/ardour/io.cc:822 libs/ardour/io.cc:1177 libs/ardour/io.cc:1303
+#, c-format
+msgid "%s/out"
+msgstr ""
+
+#: libs/ardour/io.cc:824 libs/ardour/io.cc:1179 libs/ardour/io.cc:1305
+#: libs/ardour/io.cc:2688
+#, c-format
+msgid "%s/out %u"
+msgstr ""
+
+#: libs/ardour/io.cc:828 libs/ardour/io.cc:1184 libs/ardour/io.cc:1309
+msgid "IO: cannot register output port %1"
+msgstr ""
+
+#: libs/ardour/io.cc:934 libs/ardour/io.cc:1037 libs/ardour/io.cc:1143
+#, c-format
+msgid "%s/in"
+msgstr ""
+
+#: libs/ardour/io.cc:936 libs/ardour/io.cc:1040 libs/ardour/io.cc:1146
+#: libs/ardour/io.cc:2658
+#, c-format
+msgid "%s/in %u"
+msgstr ""
+
+#: libs/ardour/io.cc:940 libs/ardour/io.cc:1046 libs/ardour/io.cc:1151
+msgid "IO: cannot register input port %1"
+msgstr ""
+
+#: libs/ardour/io.cc:1551
+msgid "IO::connecting_became_legal() called without a pending state node"
+msgstr ""
+
+#: libs/ardour/io.cc:1574
+msgid "IO::ports_became_legal() called without a pending state node"
+msgstr ""
+
+#: libs/ardour/io.cc:1603
+msgid "incorrect XML node \"%1\" passed to IO object"
+msgstr ""
+
+#: libs/ardour/io.cc:1719 libs/ardour/io.cc:1787
+msgid "Unknown connection \"%1\" listed for output of %2"
+msgstr ""
+
+#: libs/ardour/io.cc:1721 libs/ardour/io.cc:1789
+msgid "out 1"
+msgstr ""
+
+#: libs/ardour/io.cc:1722 libs/ardour/io.cc:1790
+msgid "No output connections available as a replacement"
+msgstr ""
+
+#: libs/ardour/io.cc:1726 libs/ardour/io.cc:1794
+msgid "Connection %1 was not available - \"out 1\" used instead"
+msgstr ""
+
+#: libs/ardour/io.cc:1740
+msgid "%1: cannot create I/O ports"
+msgstr ""
+
+#: libs/ardour/io.cc:1803
+msgid "improper output channel list in XML node (%1)"
+msgstr ""
+
+#: libs/ardour/io.cc:1888
+msgid "IO: badly formed string in XML node for outputs \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:1893
+msgid "IO: bad output string in XML node \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:2391
+msgid "%1: could not open automation event file \"%2\""
+msgstr ""
+
+#: libs/ardour/io.cc:2430
+msgid "%1: cannot open automation event file \"%2\" (%2)"
+msgstr ""
+
+#: libs/ardour/io.cc:2445
+msgid "badly formed version number in automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:2449
+msgid "no version information in automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/io.cc:2454
+msgid "mismatched automation event file version (%1)"
+msgstr ""
+
+#: libs/ardour/io.cc:2462
+msgid "badly formatted automation event record at line %1 of %2 (ignored)"
+msgstr ""
+
+#: libs/ardour/io.cc:2482
+msgid "dubious automation event found (and ignored)"
+msgstr ""
+
+#: libs/ardour/io.cc:2486 libs/ardour/panner.cc:288
+#: libs/ardour/redirect.cc:148
+msgid "loaded from disk"
+msgstr ""
+
+#: libs/ardour/io.cc:2630
+msgid "automation write/touch"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:87
+msgid "LADSPA: module has no descriptor function."
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:92
+msgid "LADSPA: plugin has gone away since discovery!"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:99
+msgid "LADSPA: \"%1\" cannot be used, since it cannot do inplace processing"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:315
+msgid ""
+"illegal parameter number used with plugin \"%1\". This mayindicate a change "
+"in the plugin design, and presets may beinvalid"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:394
+msgid "Bad node sent to LadspaPlugin::set_state"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:407
+msgid "LADSPA: no ladspa port number"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:413
+msgid "LADSPA: no ladspa port data"
+msgstr ""
+
+#: libs/ardour/ladspa_plugin.cc:653
+msgid "LADSPA: cannot load module from \"%1\""
+msgstr ""
+
+#: libs/ardour/location.cc:295
+msgid "incorrect XML node passed to Location::set_state"
+msgstr ""
+
+#: libs/ardour/location.cc:300
+msgid "XML node for Location has no ID information"
+msgstr ""
+
+#: libs/ardour/location.cc:306
+msgid "XML node for Location has no name information"
+msgstr ""
+
+#: libs/ardour/location.cc:313
+msgid "XML node for Location has no start information"
+msgstr ""
+
+#: libs/ardour/location.cc:324
+msgid "XML node for Location has no end information"
+msgstr ""
+
+#: libs/ardour/location.cc:333
+msgid "XML node for Location has no flags information"
+msgstr ""
+
+#: libs/ardour/location.cc:421
+msgid "Locations: attempt to use unknown location as selected location"
+msgstr ""
+
+#: libs/ardour/location.cc:448 libs/ardour/playlist.cc:1204
+msgid "clear"
+msgstr ""
+
+#: libs/ardour/location.cc:473
+msgid "clear markers"
+msgstr ""
+
+#: libs/ardour/location.cc:501
+msgid "clear ranges"
+msgstr ""
+
+#: libs/ardour/location.cc:519
+msgid "add"
+msgstr ""
+
+#: libs/ardour/location.cc:557
+msgid "remove"
+msgstr ""
+
+#: libs/ardour/location.cc:597
+msgid "incorrect XML mode passed to Locations::set_state"
+msgstr ""
+
+#: libs/ardour/location.cc:615
+msgid "could not load location from session file - ignored"
+msgstr ""
+
+#: libs/ardour/mtc_slave.cc:196
+msgid "MTC Slave: atomic read of current time failed, sleeping!"
+msgstr ""
+
+#: libs/ardour/named_selection.cc:77
+msgid "Chunk %1 uses an unknown playlist \"%2\""
+msgstr ""
+
+#: libs/ardour/named_selection.cc:80
+msgid "Chunk %1 contains misformed playlist information"
+msgstr ""
+
+#: libs/ardour/panner.cc:211
+msgid "automation write pass"
+msgstr ""
+
+#: libs/ardour/panner.cc:251
+#, c-format
+msgid "error writing pan automation file (%s)"
+msgstr ""
+
+#: libs/ardour/panner.cc:279
+msgid ""
+"badly formatted pan automation event record at line %1 of %2 (ignored) [%3]"
+msgstr ""
+
+#: libs/ardour/panner.cc:794
+msgid "badly-formed positional data for Multi2dPanner - ignored"
+msgstr ""
+
+#: libs/ardour/panner.cc:1083
+msgid "cannot open pan automation file \"%1\" for saving (%2)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1119
+msgid "cannot open pan automation file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1132
+msgid "badly formed version number in pan automation event file \"%1\""
+msgstr ""
+
+#: libs/ardour/panner.cc:1136
+msgid ""
+"no version information in pan automation event file \"%1\" (first line = %2)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1142
+msgid "mismatched pan automation event file version (%1)"
+msgstr ""
+
+#: libs/ardour/panner.cc:1156
+msgid "too many panner states found in pan automation file %1"
+msgstr ""
+
+#: libs/ardour/panner.cc:1297
+msgid "Unknown panner plugin \"%1\" found in pan state - ignored"
+msgstr ""
+
+#: libs/ardour/panner.cc:1303
+msgid "panner plugin node has no type information!"
+msgstr ""
+
+#: libs/ardour/playlist.cc:251
+msgid "playlist const copy constructor called"
+msgstr ""
+
+#: libs/ardour/playlist.cc:257
+msgid "playlist non-const copy constructor called"
+msgstr ""
+
+#: libs/ardour/playlist.cc:498
+msgid "add region"
+msgstr ""
+
+#: libs/ardour/playlist.cc:550
+msgid "replace region"
+msgstr ""
+
+#: libs/ardour/playlist.cc:563
+msgid "remove region"
+msgstr ""
+
+#: libs/ardour/playlist.cc:635
+msgid "separate"
+msgstr ""
+
+#: libs/ardour/playlist.cc:899
+msgid "cut"
+msgstr ""
+
+#: libs/ardour/playlist.cc:989
+msgid "duplicate"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1044
+msgid "split"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1121
+msgid "%1: bounds changed received for region (%2)not in playlist"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1377
+msgid "Playlist: cannot create region from state file"
+msgstr ""
+
+#: libs/ardour/playlist.cc:1737
+msgid "nudged"
+msgstr ""
+
+#: libs/ardour/playlist_factory.cc:40
+msgid ""
+"programming error: Playlist::copyPlaylist called with unknown Playlist type"
+msgstr ""
+
+#: libs/ardour/plugin.cc:218
+msgid "Could not locate HOME. Preset not saved."
+msgstr ""
+
+#: libs/ardour/plugin.cc:228 libs/ardour/plugin.cc:234
+msgid "Could not create %1. Preset not saved. (%2)"
+msgstr ""
+
+#: libs/ardour/plugin.cc:239
+msgid "Error saving presets file %1."
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:192
+msgid "Could not parse rdf file: %1"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:232
+msgid "LADSPA: cannot load module \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:239
+msgid "LADSPA: module \"%1\" has no descriptor function."
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:295 libs/ardour/plugin_manager.cc:307
+msgid "Unknown"
+msgstr ""
+
+#: libs/ardour/plugin_manager.cc:380
+msgid ""
+"VST plugin %1 does not support processReplacing, and so cannot be used in "
+"ardour at this time"
+msgstr ""
+
+#: libs/ardour/recent_sessions.cc:44
+msgid "cannot open recent session file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:77
+msgid "programming error: unknown Redirect type in Redirect::Clone!\n"
+msgstr ""
+
+#: libs/ardour/redirect.cc:102 libs/ardour/utils.cc:194
+msgid "pre"
+msgstr ""
+
+#: libs/ardour/redirect.cc:104 libs/ardour/utils.cc:197
+msgid "post"
+msgstr ""
+
+#: libs/ardour/redirect.cc:107
+msgid "Redirect: unknown placement string \"%1\" (ignored)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:125
+msgid "%1: cannot open %2 to load automation data (%3)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:154
+msgid "%1: cannot load automation data from %2"
+msgstr ""
+
+#: libs/ardour/redirect.cc:175
+msgid "%1: cannot open %2 to store automation data (%3)"
+msgstr ""
+
+#: libs/ardour/redirect.cc:194 libs/ardour/redirect.cc:201
+msgid "%1: could not save automation state to %2"
+msgstr ""
+
+#: libs/ardour/redirect.cc:246
+msgid "Could not get state from Redirect (%1). Problem with save_automation"
+msgstr ""
+
+#: libs/ardour/redirect.cc:296
+msgid "incorrect XML node \"%1\" passed to Redirect object"
+msgstr ""
+
+#: libs/ardour/redirect.cc:318
+msgid "%1: Automation node has no path property"
+msgstr ""
+
+#: libs/ardour/redirect.cc:343
+msgid "XML node describing an IO is missing an IO node"
+msgstr ""
+
+#: libs/ardour/redirect.cc:348
+msgid "XML node describing a redirect is missing the `active' field"
+msgstr ""
+
+#: libs/ardour/redirect.cc:358
+msgid "XML node describing a redirect is missing the `placement' field"
+msgstr ""
+
+#: libs/ardour/redirect.cc:467
+msgid "active_changed"
+msgstr ""
+
+#: libs/ardour/region.cc:901
+msgid "Session: XMLNode describing a Region is incomplete (no id)"
+msgstr ""
+
+#: libs/ardour/region.cc:908
+msgid "Session: XMLNode describing a Region is incomplete (no name)"
+msgstr ""
+
+#: libs/ardour/region_factory.cc:53 libs/ardour/region_factory.cc:70
+msgid ""
+"programming error: RegionFactory::create() called with unknown Region type"
+msgstr ""
+
+#: libs/ardour/route.cc:81 libs/ardour/session.cc:1434
+#: libs/ardour/session.cc:1440 libs/ardour/session.cc:3064
+msgid "signal"
+msgstr ""
+
+#: libs/ardour/route.cc:1407
+msgid "Could not get state of route. Problem with save_automation"
+msgstr ""
+
+#: libs/ardour/route.cc:1459
+msgid "Send construction failed"
+msgstr ""
+
+#: libs/ardour/route.cc:1481
+msgid "unknown Insert type \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/route.cc:1487
+msgid "Insert XML node has no type property"
+msgstr ""
+
+#: libs/ardour/route.cc:1492
+msgid "insert could not be created. Ignored."
+msgstr ""
+
+#: libs/ardour/route.cc:1508
+msgid "Bad node sent to Route::set_state() [%1]"
+msgstr ""
+
+#: libs/ardour/route.cc:1572
+msgid "Route %1: unknown edit group \"%2 in saved state (ignored)"
+msgstr ""
+
+#: libs/ardour/route.cc:1588 libs/ardour/route.cc:1592
+msgid "badly formed order key string in state file! [%1] ... ignored."
+msgstr ""
+
+#: libs/ardour/route.cc:1673 libs/ardour/route.cc:1761
+msgid "[control]"
+msgstr ""
+
+#: libs/ardour/route.cc:1693
+msgid "Route %1: unknown mix group \"%2 in saved state (ignored)"
+msgstr ""
+
+#: libs/ardour/send.cc:99
+msgid "XML node describing a send is missing a Redirect node"
+msgstr ""
+
+#: libs/ardour/session.cc:111
+msgid "Could not resolve path: %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session.cc:123
+msgid "cannot check session path %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session.cc:153
+msgid "cannot check statefile %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session.cc:189
+msgid "%1 is not an Ardour snapshot file"
+msgstr ""
+
+#: libs/ardour/session.cc:206
+msgid "cannot determine current working directory (%1)"
+msgstr ""
+
+#: libs/ardour/session.cc:223
+msgid "unknown file type for session %1"
+msgstr ""
+
+#: libs/ardour/session.cc:343
+msgid "monitor"
+msgstr ""
+
+#: libs/ardour/session.cc:351
+msgid "master"
+msgstr ""
+
+#: libs/ardour/session.cc:633
+msgid "could not setup Click I/O"
+msgstr ""
+
+#: libs/ardour/session.cc:654
+msgid "cannot setup Click I/O"
+msgstr ""
+
+#: libs/ardour/session.cc:676
+msgid "cannot create Auditioner: no auditioning of regions possible"
+msgstr ""
+
+#: libs/ardour/session.cc:688
+#, c-format
+msgid "out %<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:700
+#, c-format
+msgid "in %<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:714
+#, c-format
+msgid "out %<PRIu32>+%<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:728
+#, c-format
+msgid "in %<PRIu32>+%<PRIu32>"
+msgstr ""
+
+#: libs/ardour/session.cc:761
+msgid "cannot setup master inputs"
+msgstr ""
+
+#: libs/ardour/session.cc:769
+msgid "cannot setup master outputs"
+msgstr ""
+
+#: libs/ardour/session.cc:780
+msgid "Master Out"
+msgstr ""
+
+#: libs/ardour/session.cc:852
+msgid "cannot setup control inputs"
+msgstr ""
+
+#: libs/ardour/session.cc:860
+msgid "cannot set up master outputs"
+msgstr ""
+
+#: libs/ardour/session.cc:1043
+msgid "Session: you can't use that location for auto punch (start <= end)"
+msgstr ""
+
+#: libs/ardour/session.cc:1080
+msgid "Session: you can't use a mark for auto loop"
+msgstr ""
+
+#: libs/ardour/session.cc:1452
+msgid "feedback loop setup between %1 and %2"
+msgstr ""
+
+#: libs/ardour/session.cc:1629 libs/ardour/session.cc:1750
+msgid "cannot configure %1 in/%2 out configuration for new audio track"
+msgstr ""
+
+#: libs/ardour/session.cc:1687
+msgid "Session: could not create new audio track."
+msgstr ""
+
+#: libs/ardour/session.cc:1800
+msgid "Session: could not create new audio route."
+msgstr ""
+
+#: libs/ardour/session.cc:2319
+msgid "cannot create new name for region \"%1\""
+msgstr ""
+
+#: libs/ardour/session.cc:2383
+msgid "too many regions with names like %1"
+msgstr ""
+
+#: libs/ardour/session.cc:2883
+msgid "There are already %1 recordings for %2, which I consider too many."
+msgstr ""
+
+#: libs/ardour/session.cc:3085
+msgid "Cannot compile tape track regexp for use (%1)"
+msgstr ""
+
+#: libs/ardour/session.cc:3232
+msgid "programming error: unknown type of Insert created!"
+msgstr ""
+
+#: libs/ardour/session.cc:3238
+msgid "programming error: unknown type of Redirect created!"
+msgstr ""
+
+#: libs/ardour/session.cc:3261
+msgid "programming error: unknown type of Insert deleted!"
+msgstr ""
+
+#: libs/ardour/session.cc:3267
+msgid "programming error: unknown type of Redirect deleted!"
+msgstr ""
+
+#: libs/ardour/session.cc:3573
+msgid "too many bounced versions of playlist \"%1\""
+msgstr ""
+
+#: libs/ardour/session.cc:3582
+msgid "cannot create new audio file \"%1\" for %2"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:85 libs/ardour/session_butler.cc:90
+msgid "UI: cannot set O_NONBLOCK on butler request pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:95
+msgid "Session: could not create butler thread"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:183
+msgid "poll on butler request pipe failed (%1)"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:190
+msgid "Error on butler thread request pipe: fd=%1 err=%2"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:231
+msgid "Error reading from butler request pipe"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:268
+msgid "Butler read ahead failure on dstream %1"
+msgstr ""
+
+#: libs/ardour/session_butler.cc:311
+msgid "Butler write-behind failure on dstream %1"
+msgstr ""
+
+#: libs/ardour/session_click.cc:160
+msgid "cannot open click soundfile %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session_click.cc:169
+msgid "cannot read data from click soundfile"
+msgstr ""
+
+#: libs/ardour/session_click.cc:196
+msgid "cannot open click emphasis soundfile %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session_click.cc:204
+msgid "cannot read data from click emphasis soundfile"
+msgstr ""
+
+#: libs/ardour/session_command.cc:49
+msgid "Tried to reconstitute a MementoCommand with no contents, failing. id="
+msgstr ""
+
+#: libs/ardour/session_command.cc:95
+msgid "could not reconstitute MementoCommand from XMLNode. id="
+msgstr ""
+
+#: libs/ardour/session_events.cc:161
+msgid "Session: cannot have two events of type %1 at the same frame (%2)."
+msgstr ""
+
+#: libs/ardour/session_events.cc:422
+msgid "Programming error: illegal event type in process_event (%1)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:63
+msgid "Export: no output file specified"
+msgstr ""
+
+#: libs/ardour/session_export.cc:164 libs/ardour/session_export.cc:169
+msgid "illegal frame range in export specification"
+msgstr ""
+
+#: libs/ardour/session_export.cc:174
+msgid "Bad data width size. Report me!"
+msgstr ""
+
+#: libs/ardour/session_export.cc:204
+msgid "Export: cannot open output file \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:214
+msgid "cannot initialize sample rate conversion: %1"
+msgstr ""
+
+#: libs/ardour/session_export.cc:316
+msgid "an error occured during sample rate conversion: %1"
+msgstr ""
+
+#: libs/ardour/session_export.cc:327
+msgid "warning, leftover frames overflowed, glitches might occur in output"
+msgstr ""
+
+#: libs/ardour/session_export.cc:418
+msgid "Export: could not write data to output file (%1)"
+msgstr ""
+
+#: libs/ardour/session_export.cc:502
+msgid "%1: cannot seek to %2 for export"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:95
+msgid "Ardour is slaved to MTC - port cannot be reset"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:110
+msgid "unknown port %1 requested for MTC"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:435
+msgid "Error reading from MIDI port %1"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:804
+msgid "Session: could not send full MIDI time code"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:863
+msgid "Session: cannot send quarter-frame MTC message (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:971
+msgid "MMC: cannot send command %1%2%3"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1078
+msgid "UI: cannot set O_NONBLOCK on signal read pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1083
+msgid "UI: cannot set O_NONBLOCK on signal write pipe (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1088
+msgid "Session: could not create transport thread"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1117
+msgid "cannot send signal to midi thread! (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1212
+msgid "MIDI thread poll failed (%1)"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1224
+msgid "Error on transport thread request pipe"
+msgstr ""
+
+#: libs/ardour/session_midi.cc:1251
+msgid "Error reading from transport request pipe"
+msgstr ""
+
+#: libs/ardour/session_process.cc:104
+msgid "Session: error in no roll for %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:103
+msgid "Could not use path %1 (%s)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:131
+msgid "end"
+msgstr ""
+
+#: libs/ardour/session_state.cc:132
+msgid "start"
+msgstr ""
+
+#: libs/ardour/session_state.cc:443
+msgid "Session: cannot create session dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:450
+msgid "Session: cannot create session peakfile dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:457
+msgid "Session: cannot create session sounds dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:464
+msgid "Session: cannot create session dead sounds dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:471
+msgid "Session: cannot create session automation dir \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:500
+msgid "Could not open %1 for writing mix template"
+msgstr ""
+
+#: libs/ardour/session_state.cc:506
+msgid "Could not open mix template %1 for reading"
+msgstr ""
+
+#: libs/ardour/session_state.cc:548
+msgid "Session: could not load diskstream via XML state"
+msgstr ""
+
+#: libs/ardour/session_state.cc:597
+msgid "could not backup old state file, current state not saved."
+msgstr ""
+
+#: libs/ardour/session_state.cc:612
+msgid "state could not be saved to %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:619
+msgid "could not remove corrupt state file %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:623
+msgid "could not restore state file from backup %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:693
+msgid "%1: session state information file \"%2\" doesn't exist!"
+msgstr ""
+
+#: libs/ardour/session_state.cc:704 libs/ardour/session_state.cc:2824
+msgid "Could not understand ardour file %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:988
+msgid "programming error: Session: incorrect XML node sent to set_state()"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1047
+msgid "Session: XML state has no options section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1051
+msgid "Session: XML state has no sources section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1058
+msgid "Session: XML state has no Regions section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1065
+msgid "Session: XML state has no playlists section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1084
+msgid "Session: XML state has no diskstreams section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1091
+msgid "Session: XML state has no connections section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1098
+msgid "Session: XML state has no locations section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1131
+msgid "Session: XML state has no edit groups section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1138
+msgid "Session: XML state has no mix groups section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1145
+msgid "Session: XML state has no Tempo Map section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1152
+msgid "Session: XML state has no routes section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1159
+msgid "Session: XML state has no click section"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1202
+msgid "Session: cannot create Route from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1243
+msgid "Session: cannot create Region from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1271
+msgid "Session: XMLNode describing a AudioRegion is incomplete (no source)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1279 libs/ardour/session_state.cc:1300
+msgid ""
+"Session: XMLNode describing a AudioRegion references an unknown source id =%1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1285 libs/ardour/session_state.cc:1306
+msgid ""
+"Session: XMLNode describing a AudioRegion references a non-audio source id =%"
+"1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1377
+msgid "Session: cannot create Source from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1396
+msgid ""
+"Found a sound file that cannot be used by Ardour. Talk to the progammers."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1418
+msgid "Could not create mix templates directory \"%1\" (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1432
+msgid "Template \"%1\" already exists - new version not created"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1439
+msgid "mix template not saved"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1498
+msgid "cannot create session directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1509
+msgid "cannot create sounds directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1518
+msgid "cannot create dead sounds directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1527
+msgid "cannot create peak file directory \"%1\"; ignored"
+msgstr ""
+
+#: libs/ardour/session_state.cc:1659 libs/ardour/session_state.cc:1680
+msgid "Session: cannot create Playlist from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1719
+msgid "Session: cannot create Named Selection from XML description."
+msgstr ""
+
+#: libs/ardour/session_state.cc:1872
+msgid "Unknown node \"%1\" found in Connections list from state file"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2677
+msgid "cannot remove dead sound file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2778
+msgid "could not backup old history file, current history not saved."
+msgstr ""
+
+#: libs/ardour/session_state.cc:2786
+msgid "history could not be saved to %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2794
+msgid "could not remove corrupt history file %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2798
+msgid "could not restore history file from backup %1"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2816
+msgid "Loading history from '%1'."
+msgstr ""
+
+#: libs/ardour/session_state.cc:2819
+msgid "%1: session history file \"%2\" doesn't exist!"
+msgstr ""
+
+#: libs/ardour/session_state.cc:2861
+msgid "Couldn't figure out how to make a Command out of a %1 XMLNode."
+msgstr ""
+
+#: libs/ardour/session_time.cc:370
+msgid "Unknown JACK transport state %1 in sync callback"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:79
+msgid "tempoize: error creating name for new audio file based on %1"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:88
+msgid "tempoize: error creating new audio file %1 (%2)"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:113
+msgid "tempoize: error reading data from %1"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:126 libs/ardour/session_timefx.cc:138
+msgid "error writing tempo-adjusted data to %1"
+msgstr ""
+
+#: libs/ardour/session_timefx.cc:144
+msgid "timefx code failure. please notify ardour-developers."
+msgstr ""
+
+#: libs/ardour/session_transport.cc:117
+msgid "Cannot loop - no loop range defined"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:474
+msgid ""
+"Seamless looping cannot be supported while Ardour is using JACK transport.\n"
+"Recommend changing the configured options"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:743
+msgid ""
+"Global varispeed cannot be supported while Ardour is connected to JACK "
+"transport control"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:933
+msgid "please stop the transport before adjusting slave settings"
+msgstr ""
+
+#: libs/ardour/session_transport.cc:966
+msgid "No MTC port defined: MTC slaving is impossible."
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:15
+msgid "WAV"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:16
+msgid "AIFF"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:17
+msgid "raw (no header)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:18
+msgid "PAF (Ensoniq Paris)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:19
+msgid "AU (Sun/NeXT)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:20
+msgid "IRCAM"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:21
+msgid "W64 (64 bit WAV)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:26
+msgid ".wav"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:27
+msgid ".aiff"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:28
+msgid ".raw"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:29
+msgid ".paf"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:30
+msgid ".au"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:31
+msgid ".ircam"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:32
+msgid ".w64"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:47
+msgid "16 bit"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:48
+msgid "24 bit"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:49
+msgid "32 bit"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:50
+msgid "8 bit"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:51
+msgid "float"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:64
+msgid "Little-endian (Intel)"
+msgstr ""
+
+#: libs/ardour/sndfile_helpers.cc:65
+msgid "Big-endian (Mac)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:143
+msgid "FileSource: cannot get host information for BWF header (%1)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:167
+msgid ""
+"cannot set broadcast info for audio file %1 (%2); dropping broadcast info "
+"for this file"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:216
+msgid "SndFileSource: cannot open file \"%1\" for %2 (%3)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:222
+msgid ""
+"SndFileSource: file only contains %1 channels; %2 is invalid as a channel "
+"number"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:327
+msgid "SndFileSource: could not seek to frame %1 within %2 (%3)"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:378
+msgid "programming error: %1 %2"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:486 libs/ardour/sndfilesource.cc:507
+msgid ""
+"cannot set broadcast info for audio file %1; Dropping broadcast info for "
+"this file"
+msgstr ""
+
+#: libs/ardour/sndfilesource.cc:521
+msgid "%1: cannot seek to %2"
+msgstr ""
+
+#: libs/ardour/state_manager.cc:47
+msgid "cleared history"
+msgstr ""
+
+#: libs/ardour/state_manager.cc:60
+msgid ""
+"programming error: illegal state ID (%1) passed to StateManager::set_state() "
+"(range = 0-%2)"
+msgstr ""
+
+#: libs/ardour/tempo.cc:67
+msgid "TempoSection XML node has no \"start\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:75
+msgid "TempoSection XML node has an illegal \"start\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:82
+msgid "TempoSection XML node has no \"beats-per-minute\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:87
+msgid "TempoSection XML node has an illegal \"beats_per_minute\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:92
+msgid "TempoSection XML node has no \"movable\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:131
+msgid "MeterSection XML node has no \"start\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:139
+msgid "MeterSection XML node has an illegal \"start\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:146
+msgid "MeterSection XML node has no \"beats-per-bar\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:151
+msgid "MeterSection XML node has an illegal \"beats-per-bar\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:156
+msgid "MeterSection XML node has no \"note-type\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:161
+msgid "MeterSection XML node has an illegal \"note-type\" value"
+msgstr ""
+
+#: libs/ardour/tempo.cc:166
+msgid "MeterSection XML node has no \"movable\" property"
+msgstr ""
+
+#: libs/ardour/tempo.cc:259
+msgid "move metric"
+msgstr ""
+
+#: libs/ardour/tempo.cc:330
+msgid "metric removed"
+msgstr ""
+
+#: libs/ardour/tempo.cc:373
+msgid "add tempo"
+msgstr ""
+
+#: libs/ardour/tempo.cc:402
+msgid "replace tempo"
+msgstr ""
+
+#: libs/ardour/tempo.cc:435
+msgid "add meter"
+msgstr ""
+
+#: libs/ardour/tempo.cc:463
+msgid "replaced meter"
+msgstr ""
+
+#: libs/ardour/tempo.cc:483 libs/ardour/tempo.cc:499
+msgid "programming error: no tempo section in tempo map!"
+msgstr ""
+
+#: libs/ardour/tempo.cc:538
+msgid "programming error: unhandled MetricSection type"
+msgstr ""
+
+#: libs/ardour/tempo.cc:1265 libs/ardour/tempo.cc:1277
+msgid "Tempo map: could not set new state, restoring old one."
+msgstr ""
+
+#: libs/ardour/tempo.cc:1301
+msgid "load XML data"
+msgstr ""
+
+#: libs/ardour/utils.cc:237
+msgid "illegal or badly-formed string used for path (%1)"
+msgstr ""
+
+#: libs/ardour/utils.cc:242
+msgid "path (%1) is ambiguous"
+msgstr ""
+
+#: libs/ardour/utils.cc:304 libs/ardour/utils.cc:323
+msgid "Splice Edit"
+msgstr "Fogredigering"
+
+#: libs/ardour/utils.cc:306 libs/ardour/utils.cc:319
+msgid "Slide Edit"
+msgstr "Glidredigering"
+
+#: libs/ardour/utils.cc:309
+msgid "programming error: unknown edit mode string \"%1\""
+msgstr ""
+
+#: libs/ardour/utils.cc:330 libs/ardour/utils.cc:359
+msgid "Internal"
+msgstr "Intern"
+
+#: libs/ardour/utils.cc:334 libs/ardour/utils.cc:355
+msgid "MTC"
+msgstr ""
+
+#: libs/ardour/utils.cc:338 libs/ardour/utils.cc:352
+msgid "JACK"
+msgstr ""
+
+#: libs/ardour/utils.cc:342
+msgid "programming error: unknown slave source string \"%1\""
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:178
+msgid "cannot create VST chunk directory: %1"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:186
+msgid "cannot check VST chunk directory: %1"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:193
+msgid "%1 exists but is not a directory"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:231
+msgid "Bad node sent to VSTPlugin::set_state"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:334 libs/ardour/vst_plugin.cc:345
+msgid "no support for presets using chunks at this time"
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:495
+msgid "VST: cannot load module from \"%1\""
+msgstr ""
+
+#: libs/ardour/vst_plugin.cc:500
+msgid "You asked ardour to not use any VST plugins"
+msgstr ""
+
+#: libs/ardour/audio_unit.cc:48
+msgid "AudioUnit: Could not convert CAComponent to CAAudioUnit"
+msgstr ""
diff --git a/libs/ardour/port.cc b/libs/ardour/port.cc
new file mode 100644
index 0000000000..7aadb9183f
--- /dev/null
+++ b/libs/ardour/port.cc
@@ -0,0 +1,379 @@
+/*
+ Copyright (C) 2002-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/port.h>
+
+using namespace ARDOUR;
+using namespace std;
+
+AudioEngine* Port::engine = 0;
+
+Port::Port (const std::string& name, Flags flgs)
+ : _flags (flgs)
+ , _name (name)
+ , _metering (0)
+ , _last_monitor (false)
+{
+}
+
+Port::~Port ()
+{
+ disconnect_all ();
+}
+
+void
+Port::reset ()
+{
+ _last_monitor = false;
+}
+
+void
+Port::set_engine (AudioEngine* e)
+{
+ engine = e;
+}
+
+int
+Port::connect (Port& other)
+{
+ /* caller must hold process lock */
+
+ pair<set<Port*>::iterator,bool> result;
+
+ result = _connections.insert (&other);
+
+ if (result.second) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+int
+Port::disconnect (Port& other)
+{
+ /* caller must hold process lock */
+
+ for (set<Port*>::iterator i = _connections.begin(); i != _connections.end(); ++i) {
+ if ((*i) == &other) {
+ _connections.erase (i);
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+int
+Port::disconnect_all ()
+{
+ /* caller must hold process lock */
+
+ _connections.clear ();
+ return 0;
+}
+
+void
+Port::set_latency (nframes_t val)
+{
+ _latency = val;
+}
+
+bool
+Port::connected() const
+{
+ /* caller must hold process lock */
+ return !_connections.empty();
+}
+
+bool
+Port::connected_to (const string& portname) const
+{
+ /* caller must hold process lock */
+
+ for (set<Port*>::const_iterator p = _connections.begin(); p != _connections.end(); ++p) {
+ if ((*p)->name() == portname) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int
+Port::get_connections (vector<string>& names) const
+{
+ /* caller must hold process lock */
+ int i = 0;
+ set<Port*>::const_iterator p;
+
+ for (i = 0, p = _connections.begin(); p != _connections.end(); ++p, ++i) {
+ names.push_back ((*p)->name());
+ }
+
+ return i;
+}
+
+
+//-------------------------------------
+
+int
+PortFacade::set_name (const std::string& str)
+{
+ int ret;
+
+ if (_ext_port) {
+ if ((ret = _ext_port->set_name (str)) == 0) {
+ _name = _ext_port->name();
+ }
+ } else {
+ _name = str;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+string
+PortFacade::short_name () const
+{
+ if (_ext_port) {
+ return _ext_port->short_name();
+ } else {
+ return _name;
+ }
+}
+
+
+int
+PortFacade::reestablish ()
+{
+ if (_ext_port) {
+ return _ext_port->reestablish ();
+ } else {
+ return 0;
+ }
+}
+
+
+int
+PortFacade::reconnect()
+{
+ if (_ext_port) {
+ return _ext_port->reconnect ();
+ } else {
+ return 0;
+ }
+}
+
+void
+PortFacade::set_latency (nframes_t val)
+{
+ if (_ext_port) {
+ _ext_port->set_latency (val);
+ } else {
+ _latency = val;
+ }
+}
+
+nframes_t
+PortFacade::latency() const
+{
+ if (_ext_port) {
+ return _ext_port->latency();
+ } else {
+ return _latency;
+ }
+}
+
+nframes_t
+PortFacade::total_latency() const
+{
+ if (_ext_port) {
+ return _ext_port->total_latency();
+ } else {
+ return _latency;
+ }
+}
+
+bool
+PortFacade::monitoring_input() const
+{
+ if (_ext_port) {
+ return _ext_port->monitoring_input ();
+ } else {
+ return false;
+ }
+}
+
+void
+PortFacade::ensure_monitor_input (bool yn)
+{
+ if (_ext_port) {
+ _ext_port->ensure_monitor_input (yn);
+ }
+}
+
+void
+PortFacade::request_monitor_input (bool yn)
+{
+ if (_ext_port) {
+ _ext_port->request_monitor_input (yn);
+ }
+}
+
+int
+PortFacade::connect (Port& other)
+{
+ int ret;
+
+ if (_ext_port) {
+ ret = _ext_port->connect (other);
+ } else {
+ ret = 0;
+ }
+
+ if (ret == 0) {
+ ret = Port::connect (other);
+ }
+
+ return ret;
+}
+
+int
+PortFacade::connect (const std::string& other)
+{
+ PortConnectableByName* pcn;
+
+ if (!_ext_port) {
+ return -1;
+ }
+
+ pcn = dynamic_cast<PortConnectableByName*>(_ext_port);
+
+ if (pcn) {
+ return pcn->connect (other);
+ } else {
+ return -1;
+ }
+}
+
+
+int
+PortFacade::disconnect (Port& other)
+{
+ int reta;
+ int retb;
+
+ if (_ext_port) {
+ reta = _ext_port->disconnect (other);
+ } else {
+ reta = 0;
+ }
+
+ retb = Port::disconnect (other);
+
+ return reta || retb;
+}
+
+int
+PortFacade::disconnect_all ()
+{
+ int reta = 0;
+ int retb = 0;
+
+ if (_ext_port) {
+ reta = _ext_port->disconnect_all ();
+ }
+
+ retb = Port::disconnect_all ();
+
+ return reta || retb;
+}
+
+int
+PortFacade::disconnect (const std::string& other)
+{
+ PortConnectableByName* pcn;
+
+ if (!_ext_port) {
+ return -1;
+ }
+
+ pcn = dynamic_cast<PortConnectableByName*>(_ext_port);
+
+ if (pcn) {
+ return pcn->disconnect (other);
+ } else {
+ return -1;
+ }
+}
+
+bool
+PortFacade::connected () const
+{
+ if (Port::connected()) {
+ return true;
+ }
+
+ if (_ext_port) {
+ return _ext_port->connected();
+ }
+
+ return false;
+}
+bool
+PortFacade::connected_to (const std::string& portname) const
+{
+ if (Port::connected_to (portname)) {
+ return true;
+ }
+
+ if (_ext_port) {
+ return _ext_port->connected_to (portname);
+ }
+
+ return false;
+
+}
+
+int
+PortFacade::get_connections (vector<string>& names) const
+{
+ int i = 0;
+
+ if (_ext_port) {
+ i = _ext_port->get_connections (names);
+ }
+
+ i += Port::get_connections (names);
+
+ return i;
+}
+
+void
+PortFacade::reset ()
+{
+ Port::reset ();
+
+ if (_ext_port) {
+ _ext_port->reset ();
+ }
+}
diff --git a/libs/ardour/port_insert.cc b/libs/ardour/port_insert.cc
new file mode 100644
index 0000000000..e14835b083
--- /dev/null
+++ b/libs/ardour/port_insert.cc
@@ -0,0 +1,246 @@
+/*
+ Copyright (C) 2000,2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+
+#include <sigc++/bind.h>
+
+#include <pbd/failed_constructor.h>
+#include <pbd/xml++.h>
+
+#include <ardour/port_insert.h>
+#include <ardour/plugin.h>
+#include <ardour/port.h>
+#include <ardour/route.h>
+#include <ardour/buffer_set.h>
+
+#include <ardour/audioengine.h>
+#include <ardour/session.h>
+#include <ardour/types.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+PortInsert::PortInsert (Session& s, Placement p)
+ : IOProcessor (s, string_compose (_("insert %1"), (bitslot = s.next_insert_id()) + 1), p, 1, -1, 1, -1)
+{
+ init ();
+ ProcessorCreated (this); /* EMIT SIGNAL */
+}
+
+PortInsert::PortInsert (const PortInsert& other)
+ : IOProcessor (other._session, string_compose (_("insert %1"), (bitslot = other._session.next_insert_id()) + 1), other.placement(), 1, -1, 1, -1)
+{
+ init ();
+ ProcessorCreated (this); /* EMIT SIGNAL */
+}
+
+void
+PortInsert::init ()
+{
+ if (_io->add_input_port ("", this)) {
+ error << _("PortInsert: cannot add input port") << endmsg;
+ throw failed_constructor();
+ }
+
+ if (_io->add_output_port ("", this)) {
+ error << _("PortInsert: cannot add output port") << endmsg;
+ throw failed_constructor();
+ }
+}
+
+PortInsert::PortInsert (Session& s, const XMLNode& node)
+ : IOProcessor (s, "unnamed port insert", PreFader)
+{
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+
+ ProcessorCreated (this); /* EMIT SIGNAL */
+}
+
+PortInsert::~PortInsert ()
+{
+ GoingAway ();
+}
+
+void
+PortInsert::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
+{
+ if (_io->n_outputs().n_total() == 0) {
+ return;
+ }
+
+ if (!active()) {
+ /* deliver silence */
+ _io->silence (nframes, offset);
+ return;
+ }
+
+ _io->deliver_output(bufs, start_frame, end_frame, nframes, offset);
+
+ _io->collect_input(bufs, nframes, offset);
+}
+
+XMLNode&
+PortInsert::get_state(void)
+{
+ return state (true);
+}
+
+XMLNode&
+PortInsert::state (bool full)
+{
+ XMLNode& node = IOProcessor::state(full);
+ char buf[32];
+ node.add_property ("type", "port");
+ snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
+ node.add_property ("bitslot", buf);
+
+ return node;
+}
+
+int
+PortInsert::set_state(const XMLNode& node)
+{
+ XMLNodeList nlist = node.children();
+ XMLNodeIterator niter;
+ XMLPropertyList plist;
+ const XMLProperty *prop;
+
+ if ((prop = node.property ("type")) == 0) {
+ error << _("XML node describing port insert is missing the `type' field") << endmsg;
+ return -1;
+ }
+
+ if (prop->value() != "port") {
+ error << _("non-port insert XML used for port plugin insert") << endmsg;
+ return -1;
+ }
+
+ if ((prop = node.property ("bitslot")) == 0) {
+ bitslot = _session.next_insert_id();
+ } else {
+ sscanf (prop->value().c_str(), "%" PRIu32, &bitslot);
+ _session.mark_insert_id (bitslot);
+ }
+
+ const XMLNode* insert_node = &node;
+
+ // legacy sessions: search for child IOProcessor node
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == "IOProcessor") {
+ insert_node = *niter;
+ break;
+ }
+ }
+
+ IOProcessor::set_state (*insert_node);
+
+ return 0;
+}
+
+ARDOUR::nframes_t
+PortInsert::signal_latency() const
+{
+ /* because we deliver and collect within the same cycle,
+ all I/O is necessarily delayed by at least frames_per_cycle().
+
+ if the return port for insert has its own latency, we
+ need to take that into account too.
+ */
+
+ return _session.engine().frames_per_cycle() + _io->input_latency();
+}
+
+bool
+PortInsert::can_support_input_configuration (ChanCount in) const
+{
+ if (_io->input_maximum() == ChanCount::INFINITE && _io->output_maximum() == ChanCount::INFINITE) {
+
+ /* not configured yet */
+
+ return true; /* we can support anything the first time we're asked */
+
+ } else {
+
+ /* the "input" config for a port insert corresponds to how
+ many output ports it will have.
+ */
+
+ if (_io->output_maximum() == in) {
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+ChanCount
+PortInsert::output_for_input_configuration (ChanCount in) const
+{
+ return in;
+}
+
+bool
+PortInsert::configure_io (ChanCount in, ChanCount out)
+{
+ /* do not allow configuration to be changed outside the range of
+ the last request config. or something like that.
+ */
+
+
+ /* this is a bit odd:
+
+ the number of inputs we are required to handle corresponds
+ to the number of output ports we need.
+
+ the number of outputs we are required to have corresponds
+ to the number of input ports we need.
+ */
+
+ _io->set_output_maximum (in);
+ _io->set_output_minimum (in);
+ _io->set_input_maximum (out);
+ _io->set_input_minimum (out);
+
+ bool success = (_io->ensure_io (out, in, false, this) == 0);
+
+ if (success)
+ return Processor::configure_io(in, out);
+ else
+ return false;
+}
+
+ChanCount
+PortInsert::output_streams() const
+{
+ return _io->n_inputs ();
+}
+
+ChanCount
+PortInsert::input_streams() const
+{
+ return _io->n_outputs ();
+}
+
diff --git a/libs/ardour/port_set.cc b/libs/ardour/port_set.cc
new file mode 100644
index 0000000000..3182c2b959
--- /dev/null
+++ b/libs/ardour/port_set.cc
@@ -0,0 +1,126 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <ardour/port_set.h>
+
+namespace ARDOUR {
+
+PortSet::PortSet()
+{
+ for (size_t i=0; i < DataType::num_types; ++i)
+ _ports.push_back( PortVec() );
+}
+
+static bool sort_ports_by_name (Port* a, Port* b)
+{
+ return (a->name() < b->name());
+}
+
+void
+PortSet::add(Port* port)
+{
+ PortVec& v = _ports[port->type()];
+
+ v.push_back(port);
+ sort(v.begin(), v.end(), sort_ports_by_name);
+
+ _count.set(port->type(), _count.get(port->type()) + 1);
+
+ assert(_count.get(port->type()) == _ports[port->type()].size());
+}
+
+bool
+PortSet::remove(Port* port)
+{
+ for (std::vector<PortVec>::iterator l = _ports.begin(); l != _ports.end(); ++l) {
+ PortVec::iterator i = find(l->begin(), l->end(), port);
+ if (i != l->end()) {
+ l->erase(i);
+ _count.set(port->type(), _count.get(port->type()) - 1);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/** Get the total number of ports (of all types) in the PortSet
+ */
+size_t
+PortSet::num_ports() const
+{
+ size_t ret = 0;
+
+ for (std::vector<PortVec>::const_iterator l = _ports.begin(); l != _ports.end(); ++l)
+ ret += (*l).size();
+
+ return ret;
+}
+
+bool
+PortSet::contains(const Port* port) const
+{
+ for (std::vector<PortVec>::const_iterator l = _ports.begin(); l != _ports.end(); ++l)
+ if (find((*l).begin(), (*l).end(), port) != (*l).end())
+ return true;
+
+ return false;
+}
+
+Port*
+PortSet::port(size_t n) const
+{
+ // This is awesome. Awesomely slow.
+
+ size_t size_so_far = 0;
+
+ for (std::vector<PortVec>::const_iterator l = _ports.begin(); l != _ports.end(); ++l) {
+ if (n < size_so_far + (*l).size())
+ return (*l)[n - size_so_far];
+ else
+ size_so_far += (*l).size();
+ }
+
+ return NULL; // n out of range
+}
+
+Port*
+PortSet::port(DataType type, size_t n) const
+{
+ if (type == DataType::NIL) {
+ return port(n);
+ } else {
+ const PortVec& v = _ports[type];
+ assert(n < v.size());
+ return v[n];
+ }
+}
+
+AudioPort*
+PortSet::nth_audio_port(size_t n) const
+{
+ return dynamic_cast<AudioPort*>(port(DataType::AUDIO, n));
+}
+
+MidiPort*
+PortSet::nth_midi_port(size_t n) const
+{
+ return dynamic_cast<MidiPort*>(port(DataType::MIDI, n));
+}
+
+} // namepace ARDOUR
diff --git a/libs/ardour/processor.cc b/libs/ardour/processor.cc
new file mode 100644
index 0000000000..cbbbb374fb
--- /dev/null
+++ b/libs/ardour/processor.cc
@@ -0,0 +1,255 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+
+#include <sigc++/bind.h>
+
+#include <pbd/failed_constructor.h>
+#include <pbd/enumwriter.h>
+#include <pbd/xml++.h>
+
+#include <ardour/processor.h>
+#include <ardour/plugin.h>
+#include <ardour/port.h>
+#include <ardour/route.h>
+#include <ardour/ladspa_plugin.h>
+#include <ardour/buffer_set.h>
+#include <ardour/send.h>
+#include <ardour/port_insert.h>
+#include <ardour/plugin_insert.h>
+
+#ifdef VST_SUPPORT
+#include <ardour/vst_plugin.h>
+#endif
+
+#ifdef HAVE_AUDIOUNITS
+#include <ardour/audio_unit.h>
+#endif
+
+#include <ardour/audioengine.h>
+#include <ardour/session.h>
+#include <ardour/types.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+sigc::signal<void,Processor*> Processor::ProcessorCreated;
+
+// Always saved as Processor, but may be IOProcessor or Send in legacy sessions
+const string Processor::state_node_name = "Processor";
+
+Processor::Processor(Session& session, const string& name, Placement p)
+ : Automatable(session, name)
+ , _active(false)
+ , _next_ab_is_active(false)
+ , _configured(false)
+ , _placement(p)
+ , _gui(0)
+{
+}
+
+boost::shared_ptr<Processor>
+Processor::clone (boost::shared_ptr<const Processor> other)
+{
+ boost::shared_ptr<const Send> send;
+ boost::shared_ptr<const PortInsert> port_insert;
+ boost::shared_ptr<const PluginInsert> plugin_insert;
+
+ if ((send = boost::dynamic_pointer_cast<const Send>(other)) != 0) {
+ return boost::shared_ptr<Processor> (new Send (*send));
+ } else if ((port_insert = boost::dynamic_pointer_cast<const PortInsert>(other)) != 0) {
+ return boost::shared_ptr<Processor> (new PortInsert (*port_insert));
+ } else if ((plugin_insert = boost::dynamic_pointer_cast<const PluginInsert>(other)) != 0) {
+ return boost::shared_ptr<Processor> (new PluginInsert (*plugin_insert));
+ } else {
+ fatal << _("programming error: unknown Processor type in Processor::Clone!\n")
+ << endmsg;
+ /*NOTREACHED*/
+ }
+ return boost::shared_ptr<Processor>();
+}
+
+void
+Processor::set_sort_key (uint32_t key)
+{
+ _sort_key = key;
+}
+
+void
+Processor::set_placement (Placement p)
+{
+ if (_placement != p) {
+ _placement = p;
+ PlacementChanged (); /* EMIT SIGNAL */
+ }
+}
+
+void
+Processor::set_active (bool yn)
+{
+ _active = yn;
+ ActiveChanged ();
+}
+
+XMLNode&
+Processor::get_state (void)
+{
+ return state (true);
+}
+
+/* NODE STRUCTURE
+
+ <Automation [optionally with visible="...." ]>
+ <parameter-N>
+ <AutomationList id=N>
+ <events>
+ X1 Y1
+ X2 Y2
+ ....
+ </events>
+ </parameter-N>
+ <Automation>
+*/
+
+XMLNode&
+Processor::state (bool full_state)
+{
+ XMLNode* node = new XMLNode (state_node_name);
+ stringstream sstr;
+
+ // FIXME: This conflicts with "id" used by plugin for name in legacy sessions (ugh).
+ // Do we need to serialize this?
+ /*
+ char buf[64];
+ id().print (buf, sizeof (buf));
+ node->add_property("id", buf);
+ */
+
+ node->add_property("name", _name);
+ node->add_property("active", active() ? "yes" : "no");
+ node->add_property("placement", enum_2_string (_placement));
+
+ if (_extra_xml){
+ node->add_child_copy (*_extra_xml);
+ }
+
+ if (full_state) {
+
+ XMLNode& automation = Automatable::get_automation_state();
+
+ for (set<Parameter>::iterator x = _visible_controls.begin(); x != _visible_controls.end(); ++x) {
+ if (x != _visible_controls.begin()) {
+ sstr << ' ';
+ }
+ sstr << *x;
+ }
+
+ automation.add_property ("visible", sstr.str());
+
+ node->add_child_nocopy (automation);
+ }
+
+ return *node;
+}
+
+int
+Processor::set_state (const XMLNode& node)
+{
+ const XMLProperty *prop;
+
+ // may not exist for legacy sessions
+ if ((prop = node.property ("name")) != 0) {
+ set_name(prop->value());
+ }
+
+ XMLNodeList nlist = node.children();
+ XMLNodeIterator niter;
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ if ((*niter)->name() == X_("Automation")) {
+
+
+ XMLProperty *prop;
+
+ if ((prop = (*niter)->property ("path")) != 0) {
+ old_set_automation_state (*(*niter));
+ } else {
+ set_automation_state (*(*niter), Parameter(PluginAutomation));
+ }
+
+ if ((prop = (*niter)->property ("visible")) != 0) {
+ uint32_t what;
+ stringstream sstr;
+
+ _visible_controls.clear ();
+
+ sstr << prop->value();
+ while (1) {
+ sstr >> what;
+ if (sstr.fail()) {
+ break;
+ }
+ // FIXME: other automation types?
+ mark_automation_visible (Parameter(PluginAutomation, what), true);
+ }
+ }
+
+ } else if ((*niter)->name() == "extra") {
+ _extra_xml = new XMLNode (*(*niter));
+ }
+ }
+
+ if ((prop = node.property ("active")) == 0) {
+ error << _("XML node describing a processor is missing the `active' field") << endmsg;
+ return -1;
+ }
+
+ if (_active != (prop->value() == "yes")) {
+ _active = !_active;
+ ActiveChanged (); /* EMIT_SIGNAL */
+ }
+
+ if ((prop = node.property ("placement")) == 0) {
+ error << _("XML node describing a processor is missing the `placement' field") << endmsg;
+ return -1;
+ }
+
+ /* hack to handle older sessions before we only used EnumWriter */
+
+ string pstr;
+
+ if (prop->value() == "pre") {
+ pstr = "PreFader";
+ } else if (prop->value() == "post") {
+ pstr = "PostFader";
+ } else {
+ pstr = prop->value();
+ }
+
+ Placement p = Placement (string_2_enum (pstr, p));
+ set_placement (p);
+
+ return 0;
+}
+
diff --git a/libs/ardour/quantize.cc b/libs/ardour/quantize.cc
new file mode 100644
index 0000000000..ccbda9711a
--- /dev/null
+++ b/libs/ardour/quantize.cc
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/basename.h>
+
+#include <ardour/types.h>
+#include <ardour/quantize.h>
+#include <ardour/session.h>
+#include <ardour/smf_source.h>
+#include <ardour/midi_region.h>
+#include <ardour/tempo.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+
+
+/** Quantize notes, valid for MIDI regions only.
+ *
+ * Q is the quantize value in beats, ie 1.0 = quantize to beats,
+ * 0.25 = quantize to beats/4, etc.
+ */
+Quantize::Quantize (Session& s, double q)
+ : Filter (s)
+ , _q(q)
+{
+}
+
+Quantize::~Quantize ()
+{
+}
+
+int
+Quantize::run (boost::shared_ptr<Region> r)
+{
+ boost::shared_ptr<MidiRegion> region = boost::dynamic_pointer_cast<MidiRegion>(r);
+ if (!region)
+ return -1;
+
+ // FIXME: how to make a whole file region if it isn't?
+ //assert(region->whole_file());
+
+ boost::shared_ptr<MidiSource> src = region->midi_source(0);
+ src->load_model();
+
+ boost::shared_ptr<MidiModel> model = src->model();
+
+ // FIXME: Model really needs to be switched to beat time (double) ASAP
+
+ const Tempo& t = session.tempo_map().tempo_at(r->start());
+ const Meter& m = session.tempo_map().meter_at(r->start());
+
+ double q_frames = _q * (m.frames_per_bar(t, session.frame_rate()) / (double)m.beats_per_bar());
+
+ for (MidiModel::Notes::iterator i = model->notes().begin(); i != model->notes().end(); ++i) {
+ const double new_time = lrint((*i)->time() / 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);
+ }
+
+ model->set_edited(true);
+
+ return 0;
+}
diff --git a/libs/ardour/rb_effect.cc b/libs/ardour/rb_effect.cc
new file mode 100644
index 0000000000..4daf5cb33a
--- /dev/null
+++ b/libs/ardour/rb_effect.cc
@@ -0,0 +1,298 @@
+/*
+ Copyright (C) 2004-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+#include <cmath>
+
+#include <pbd/error.h>
+#include <rubberband/RubberBandStretcher.h>
+
+#include <ardour/types.h>
+#include <ardour/stretch.h>
+#include <ardour/pitch.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/session.h>
+#include <ardour/audioregion.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+using namespace RubberBand;
+
+Pitch::Pitch (Session& s, TimeFXRequest& req)
+ : RBEffect (s, req)
+{
+}
+
+Stretch::Stretch (Session& s, TimeFXRequest& req)
+ : RBEffect (s, req)
+{
+}
+
+RBEffect::RBEffect (Session& s, TimeFXRequest& req)
+ : Filter (s)
+ , tsr (req)
+
+{
+ tsr.progress = 0.0f;
+}
+
+RBEffect::~RBEffect ()
+{
+}
+
+int
+RBEffect::run (boost::shared_ptr<AudioRegion> region)
+{
+ SourceList nsrcs;
+ nframes_t done;
+ int ret = -1;
+ const nframes_t bufsize = 256;
+ gain_t* gain_buffer = 0;
+ Sample** buffers = 0;
+ char suffix[32];
+ string new_name;
+ string::size_type at;
+ nframes_t pos = 0;
+ int avail = 0;
+
+ // note: this_time_fraction is a ratio of original length. 1.0 = no change,
+ // 0.5 is half as long, 2.0 is twice as long, etc.
+
+ double this_time_fraction = tsr.time_fraction * region->stretch ();
+ double this_pitch_fraction = tsr.pitch_fraction * region->shift ();
+
+ RubberBandStretcher stretcher (session.frame_rate(), region->n_channels(),
+ (RubberBandStretcher::Options) tsr.opts,
+ this_time_fraction, this_pitch_fraction);
+
+ tsr.progress = 0.0f;
+ tsr.done = false;
+
+ uint32_t channels = region->n_channels();
+ nframes_t duration = region->ancestral_length();
+
+ stretcher.setExpectedInputDuration(duration);
+ stretcher.setDebugLevel(1);
+
+ /* the name doesn't need to be super-precise, but allow for 2 fractional
+ digits just to disambiguate close but not identical FX
+ */
+
+ if (this_time_fraction == 1.0) {
+ snprintf (suffix, sizeof (suffix), "@%d", (int) floor (this_pitch_fraction * 100.0f));
+ } else if (this_pitch_fraction == 1.0) {
+ snprintf (suffix, sizeof (suffix), "@%d", (int) floor (this_time_fraction * 100.0f));
+ } else {
+ snprintf (suffix, sizeof (suffix), "@%d-%d",
+ (int) floor (this_time_fraction * 100.0f),
+ (int) floor (this_pitch_fraction * 100.0f));
+ }
+
+ /* create new sources */
+
+ if (make_new_sources (region, nsrcs, suffix)) {
+ goto out;
+ }
+
+ gain_buffer = new gain_t[bufsize];
+ buffers = new float *[channels];
+
+ for (uint32_t i = 0; i < channels; ++i) {
+ buffers[i] = new float[bufsize];
+ }
+
+ /* we read from the master (original) sources for the region,
+ not the ones currently in use, in case it's already been
+ subject to timefx. */
+
+ /* study first, process afterwards. */
+
+ pos = 0;
+ avail = 0;
+ done = 0;
+
+ try {
+ while (pos < duration && !tsr.cancel) {
+
+ nframes_t this_read = 0;
+
+ for (uint32_t i = 0; i < channels; ++i) {
+
+ this_read = 0;
+ nframes_t this_time;
+
+ this_time = min(bufsize, duration - pos);
+
+ this_read = region->master_read_at
+ (buffers[i],
+ buffers[i],
+ gain_buffer,
+ pos + region->position(),
+ this_time,
+ i);
+
+ if (this_read != this_time) {
+ error << string_compose
+ (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
+ region->name(), pos + region->position(), this_time, this_read) << endmsg;
+ goto out;
+ }
+ }
+
+ pos += this_read;
+ done += this_read;
+
+ tsr.progress = ((float) done / duration) * 0.25;
+
+ stretcher.study(buffers, this_read, pos == duration);
+ }
+
+ done = 0;
+ pos = 0;
+
+ while (pos < duration && !tsr.cancel) {
+
+ nframes_t this_read = 0;
+
+ for (uint32_t i = 0; i < channels; ++i) {
+
+ this_read = 0;
+ nframes_t this_time;
+
+ this_time = min(bufsize, duration - pos);
+
+ this_read = region->master_read_at
+ (buffers[i],
+ buffers[i],
+ gain_buffer,
+ pos + region->position(),
+ this_time,
+ i);
+
+ if (this_read != this_time) {
+ error << string_compose
+ (_("tempoize: error reading data from %1 at %2 (wanted %3, got %4)"),
+ region->name(), pos + region->position(), this_time, this_read) << endmsg;
+ goto out;
+ }
+ }
+
+ pos += this_read;
+ done += this_read;
+
+ tsr.progress = 0.25 + ((float) done / duration) * 0.75;
+
+ stretcher.process(buffers, this_read, pos == duration);
+
+ int avail = 0;
+
+ while ((avail = stretcher.available()) > 0) {
+
+ this_read = min(bufsize, uint32_t(avail));
+
+ stretcher.retrieve(buffers, this_read);
+
+ for (uint32_t i = 0; i < nsrcs.size(); ++i) {
+
+ if (nsrcs[i]->write(buffers[i], this_read) !=
+ this_read) {
+ error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
+ goto out;
+ }
+ }
+ }
+ }
+
+ while ((avail = stretcher.available()) >= 0) {
+
+ uint32_t this_read = min(bufsize, uint32_t(avail));
+
+ stretcher.retrieve(buffers, this_read);
+
+ for (uint32_t i = 0; i < nsrcs.size(); ++i) {
+
+ if (nsrcs[i]->write(buffers[i], this_read) !=
+ this_read) {
+ error << string_compose (_("error writing tempo-adjusted data to %1"), nsrcs[i]->name()) << endmsg;
+ goto out;
+ }
+ }
+ }
+
+ } catch (runtime_error& err) {
+ error << _("timefx code failure. please notify ardour-developers.") << endmsg;
+ error << err.what() << endmsg;
+ goto out;
+ }
+
+ new_name = region->name();
+ at = new_name.find ('@');
+
+ // remove any existing stretch indicator
+
+ if (at != string::npos && at > 2) {
+ new_name = new_name.substr (0, at - 1);
+ }
+
+ new_name += suffix;
+
+ ret = finish (region, nsrcs, new_name);
+
+ /* now reset ancestral data for each new region */
+
+ for (vector<boost::shared_ptr<AudioRegion> >::iterator x = results.begin(); x != results.end(); ++x) {
+
+ (*x)->set_ancestral_data (region->ancestral_start(),
+ region->ancestral_length(),
+ this_time_fraction,
+ this_pitch_fraction );
+ (*x)->set_master_sources (region->get_master_sources());
+ }
+
+ out:
+
+ if (gain_buffer) {
+ delete [] gain_buffer;
+ }
+
+ if (buffers) {
+ for (uint32_t i = 0; i < channels; ++i) {
+ delete buffers[i];
+ }
+ delete [] buffers;
+ }
+
+ if (ret || tsr.cancel) {
+ for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
+ (*si)->mark_for_remove ();
+ }
+ }
+
+ tsr.done = true;
+
+ return ret;
+}
+
+
+
+
+
diff --git a/libs/ardour/recent_sessions.cc b/libs/ardour/recent_sessions.cc
new file mode 100644
index 0000000000..9b8668dd88
--- /dev/null
+++ b/libs/ardour/recent_sessions.cc
@@ -0,0 +1,135 @@
+/*
+ Copyright (C) 2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cstring>
+#include <cerrno>
+#include <unistd.h>
+#include <fstream>
+#include <algorithm>
+#include <pbd/error.h>
+#include <ardour/configuration.h>
+#include <ardour/filesystem_paths.h>
+#include <ardour/recent_sessions.h>
+#include <ardour/utils.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+namespace {
+
+ const char * const recent_file_name = "recent";
+
+} // anonymous
+
+int
+ARDOUR::read_recent_sessions (RecentSessions& rs)
+{
+ sys::path recent_file_path(user_config_directory());
+
+ recent_file_path /= recent_file_name;
+
+ const string path = recent_file_path.to_string();
+
+ ifstream recent (path.c_str());
+
+ if (!recent) {
+ if (errno != ENOENT) {
+ error << string_compose (_("cannot open recent session file %1 (%2)"), path, strerror (errno)) << endmsg;
+ return -1;
+ } else {
+ return 1;
+ }
+ }
+
+ while (true) {
+
+ pair<string,string> newpair;
+
+ getline(recent, newpair.first);
+
+ if (!recent.good()) {
+ break;
+ }
+
+ getline(recent, newpair.second);
+
+ if (!recent.good()) {
+ break;
+ }
+
+ rs.push_back (newpair);
+ }
+
+ /* display sorting should be done in the GUI, otherwise the
+ * natural order will be broken
+ */
+
+ return 0;
+}
+
+int
+ARDOUR::write_recent_sessions (RecentSessions& rs)
+{
+ sys::path recent_file_path(user_config_directory());
+
+ recent_file_path /= recent_file_name;
+
+ const string path = recent_file_path.to_string();
+
+ ofstream recent (path.c_str());
+
+ if (!recent) {
+ return -1;
+ }
+
+ for (RecentSessions::iterator i = rs.begin(); i != rs.end(); ++i) {
+ recent << (*i).first << '\n' << (*i).second << endl;
+ }
+
+ return 0;
+}
+
+int
+ARDOUR::store_recent_sessions (string name, string path)
+{
+ RecentSessions rs;
+
+ if (ARDOUR::read_recent_sessions (rs) < 0) {
+ return -1;
+ }
+
+ pair<string,string> newpair;
+
+ newpair.first = name;
+ newpair.second = path;
+
+ rs.erase(remove(rs.begin(), rs.end(), newpair), rs.end());
+
+ rs.push_front (newpair);
+
+ if (rs.size() > 10) {
+ rs.erase(rs.begin()+10, rs.end());
+ }
+
+ return ARDOUR::write_recent_sessions (rs);
+}
+
diff --git a/libs/ardour/region.cc b/libs/ardour/region.cc
new file mode 100644
index 0000000000..a6d153f359
--- /dev/null
+++ b/libs/ardour/region.cc
@@ -0,0 +1,1532 @@
+/*
+ Copyright (C) 2000-2003 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <cmath>
+#include <climits>
+#include <algorithm>
+#include <sstream>
+
+#include <sigc++/bind.h>
+#include <sigc++/class_slot.h>
+
+#include <glibmm/thread.h>
+#include <pbd/xml++.h>
+#include <pbd/stacktrace.h>
+#include <pbd/enumwriter.h>
+
+#include <ardour/region.h>
+#include <ardour/playlist.h>
+#include <ardour/session.h>
+#include <ardour/source.h>
+#include <ardour/tempo.h>
+#include <ardour/region_factory.h>
+#include <ardour/filter.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+Change Region::FadeChanged = ARDOUR::new_change ();
+Change Region::SyncOffsetChanged = ARDOUR::new_change ();
+Change Region::MuteChanged = ARDOUR::new_change ();
+Change Region::OpacityChanged = ARDOUR::new_change ();
+Change Region::LockChanged = ARDOUR::new_change ();
+Change Region::LayerChanged = ARDOUR::new_change ();
+Change Region::HiddenChanged = ARDOUR::new_change ();
+
+
+/* derived-from-derived constructor (no sources in constructor) */
+Region::Region (Session& s, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
+ : Automatable(s, name)
+ , _type(type)
+ , _flags(flags)
+ , _start(start)
+ , _length(length)
+ , _position(0)
+ , _last_position(0)
+ , _positional_lock_style(AudioTime)
+ , _sync_position(_start)
+ , _layer(layer)
+ , _first_edit(EditChangesNothing)
+ , _frozen(0)
+ , _stretch(1.0)
+ , _read_data_count(0)
+ , _pending_changed(Change (0))
+ , _last_layer_op(0)
+{
+ /* no sources at this point */
+}
+
+/** Basic Region constructor (single source) */
+Region::Region (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, DataType type, layer_t layer, Region::Flag flags)
+ : Automatable(src->session(), name)
+ , _type(type)
+ , _flags(flags)
+ , _start(start)
+ , _length(length)
+ , _position(0)
+ , _last_position(0)
+ , _positional_lock_style(AudioTime)
+ , _sync_position(_start)
+ , _layer(layer)
+ , _first_edit(EditChangesNothing)
+ , _frozen(0)
+ , _ancestral_start (start)
+ , _ancestral_length (length)
+ , _stretch (1.0)
+ , _shift (0.0)
+ , _valid_transients(false)
+ , _read_data_count(0)
+ , _pending_changed(Change (0))
+ , _last_layer_op(0)
+
+{
+ _sources.push_back (src);
+ _master_sources.push_back (src);
+
+ src->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), src));
+
+ assert(_sources.size() > 0);
+ _positional_lock_style = AudioTime;
+}
+
+/** Basic Region constructor (many sources) */
+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)
+ , _start(start)
+ , _length(length)
+ , _position(0)
+ , _last_position(0)
+ , _positional_lock_style(AudioTime)
+ , _sync_position(_start)
+ , _layer(layer)
+ , _first_edit(EditChangesNothing)
+ , _frozen(0)
+ , _stretch(1.0)
+ , _read_data_count(0)
+ , _pending_changed(Change (0))
+ , _last_layer_op(0)
+{
+
+ set<boost::shared_ptr<Source> > unique_srcs;
+
+ 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::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)));
+ }
+ }
+
+ assert(_sources.size() > 0);
+}
+
+/** Create a new Region from part of an existing one */
+Region::Region (boost::shared_ptr<const Region> other, nframes_t offset, nframes_t length, const string& name, layer_t layer, Flag flags)
+ : Automatable(other->session(), name)
+ , _type(other->data_type())
+ , _flags(Flag(flags & ~(Locked|PositionLocked|WholeFile|Hidden)))
+ , _start(other->_start + offset)
+ , _length(length)
+ , _position(0)
+ , _last_position(0)
+ , _positional_lock_style(other->_positional_lock_style)
+ , _sync_position(_start)
+ , _layer(layer)
+ , _first_edit(EditChangesNothing)
+ , _frozen(0)
+ , _ancestral_start (other->_ancestral_start + offset)
+ , _ancestral_length (length)
+ , _stretch (1.0)
+ , _shift (0.0)
+ , _valid_transients(false)
+ , _read_data_count(0)
+ , _pending_changed(Change (0))
+ , _last_layer_op(0)
+{
+ if (other->_sync_position < offset)
+ _sync_position = other->_sync_position;
+
+ set<boost::shared_ptr<Source> > unique_srcs;
+
+ for (SourceList::const_iterator i= other->_sources.begin(); i != other->_sources.end(); ++i) {
+ _sources.push_back (*i);
+ (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
+ unique_srcs.insert (*i);
+ }
+
+ if (other->_sync_position < offset) {
+ _sync_position = other->_sync_position;
+ }
+
+ for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.end(); ++i) {
+ if (unique_srcs.find (*i) == unique_srcs.end()) {
+ (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
+ }
+ _master_sources.push_back (*i);
+ }
+
+ assert(_sources.size() > 0);
+}
+
+/** Pure copy constructor */
+Region::Region (boost::shared_ptr<const Region> other)
+ : Automatable(other->session(), other->name())
+ , _type(other->data_type())
+ , _flags(Flag(other->_flags & ~(Locked|PositionLocked)))
+ , _start(other->_start)
+ , _length(other->_length)
+ , _position(other->_position)
+ , _last_position(other->_last_position)
+ , _positional_lock_style(other->_positional_lock_style)
+ , _sync_position(other->_sync_position)
+ , _layer(other->_layer)
+ , _first_edit(EditChangesID)
+ , _frozen(0)
+ , _ancestral_start (_start)
+ , _ancestral_length (_length)
+ , _stretch (1.0)
+ , _shift (0.0)
+ , _valid_transients(false)
+ , _read_data_count(0)
+ , _pending_changed(Change(0))
+ , _last_layer_op(other->_last_layer_op)
+{
+ other->_first_edit = EditChangesName;
+
+ if (other->_extra_xml) {
+ _extra_xml = new XMLNode (*other->_extra_xml);
+ } else {
+ _extra_xml = 0;
+ }
+
+ set<boost::shared_ptr<Source> > unique_srcs;
+
+ for (SourceList::const_iterator i = other->_sources.begin(); i != other->_sources.end(); ++i) {
+ _sources.push_back (*i);
+ (*i)->GoingAway.connect (bind (mem_fun (*this, &Region::source_deleted), (*i)));
+ unique_srcs.insert (*i);
+ }
+
+ for (SourceList::const_iterator i = other->_master_sources.begin(); i != other->_master_sources.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)));
+ }
+ }
+
+ assert(_sources.size() > 0);
+}
+
+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))
+ , _start(0)
+ , _length(0)
+ , _position(0)
+ , _last_position(0)
+ , _positional_lock_style(AudioTime)
+ , _sync_position(_start)
+ , _layer(0)
+ , _first_edit(EditChangesNothing)
+ , _frozen(0)
+ , _stretch(1.0)
+ , _read_data_count(0)
+ , _pending_changed(Change(0))
+ , _last_layer_op(0)
+{
+ set<boost::shared_ptr<Source> > unique_srcs;
+
+ 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::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)));
+ }
+ }
+
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+
+ assert(_type != DataType::NIL);
+ assert(_sources.size() > 0);
+}
+
+Region::Region (boost::shared_ptr<Source> src, const XMLNode& node)
+ : Automatable(src->session(), X_("error: XML did not reset this"))
+ , _type(DataType::NIL)
+ , _flags(Flag(0))
+ , _start(0)
+ , _length(0)
+ , _position(0)
+ , _last_position(0)
+ , _positional_lock_style(AudioTime)
+ , _sync_position(_start)
+ , _layer(0)
+ , _first_edit(EditChangesNothing)
+ , _frozen(0)
+ , _stretch(1.0)
+ , _read_data_count(0)
+ , _pending_changed(Change(0))
+ , _last_layer_op(0)
+{
+ _sources.push_back (src);
+
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+
+ assert(_type != DataType::NIL);
+ assert(_sources.size() > 0);
+}
+
+Region::~Region ()
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (pl) {
+ for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+ (*i)->remove_playlist (pl);
+ }
+ for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
+ (*i)->remove_playlist (pl);
+ }
+ }
+
+ notify_callbacks ();
+ GoingAway (); /* EMIT SIGNAL */
+}
+
+void
+Region::set_playlist (boost::weak_ptr<Playlist> wpl)
+{
+ boost::shared_ptr<Playlist> old_playlist = (_playlist.lock());
+
+ boost::shared_ptr<Playlist> pl (wpl.lock());
+
+ if (old_playlist == pl) {
+ return;
+ }
+
+ _playlist = pl;
+
+ if (pl) {
+ if (old_playlist) {
+ for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+ (*i)->remove_playlist (_playlist);
+ (*i)->add_playlist (pl);
+ }
+ for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
+ (*i)->remove_playlist (_playlist);
+ (*i)->add_playlist (pl);
+ }
+ } else {
+ for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+ (*i)->add_playlist (pl);
+ }
+ for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
+ (*i)->add_playlist (pl);
+ }
+ }
+ } else {
+ if (old_playlist) {
+ for (SourceList::const_iterator i = _sources.begin(); i != _sources.end(); ++i) {
+ (*i)->remove_playlist (old_playlist);
+ }
+ for (SourceList::const_iterator i = _master_sources.begin(); i != _master_sources.end(); ++i) {
+ (*i)->remove_playlist (old_playlist);
+ }
+ }
+ }
+}
+
+bool
+Region::set_name (const std::string& str)
+{
+ if (_name != str) {
+ SessionObject::set_name(str); // EMIT SIGNAL NameChanged()
+ assert(_name == str);
+ send_change (ARDOUR::NameChanged);
+ }
+
+ return true;
+}
+
+void
+Region::set_length (nframes_t len, void *src)
+{
+ if (_flags & Locked) {
+ return;
+ }
+
+ if (_length != len && len != 0) {
+
+ /* check that the current _position wouldn't make the new
+ length impossible.
+ */
+
+ if (max_frames - len < _position) {
+ return;
+ }
+
+ if (!verify_length (len)) {
+ return;
+ }
+
+
+ _last_length = _length;
+ _length = len;
+
+ _flags = Region::Flag (_flags & ~WholeFile);
+
+ first_edit ();
+ maybe_uncopy ();
+ invalidate_transients ();
+
+ if (!_frozen) {
+ recompute_at_end ();
+ }
+
+ send_change (LengthChanged);
+ }
+}
+
+void
+Region::maybe_uncopy ()
+{
+}
+
+void
+Region::first_edit ()
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (_first_edit != EditChangesNothing && pl) {
+
+ _name = pl->session().new_region_name (_name);
+ _first_edit = EditChangesNothing;
+
+ send_change (ARDOUR::NameChanged);
+ RegionFactory::CheckNewRegion (shared_from_this());
+ }
+}
+
+bool
+Region::at_natural_position () const
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (!pl) {
+ return false;
+ }
+
+ boost::shared_ptr<Region> whole_file_region = get_parent();
+
+ if (whole_file_region) {
+ if (_position == whole_file_region->position() + _start) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+Region::move_to_natural_position (void *src)
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (!pl) {
+ return;
+ }
+
+ boost::shared_ptr<Region> whole_file_region = get_parent();
+
+ if (whole_file_region) {
+ set_position (whole_file_region->position() + _start, src);
+ }
+}
+
+void
+Region::special_set_position (nframes_t pos)
+{
+ /* this is used when creating a whole file region as
+ a way to store its "natural" or "captured" position.
+ */
+
+ _position = _position;
+ _position = pos;
+}
+
+void
+Region::set_position_lock_style (PositionLockStyle ps)
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (!pl) {
+ return;
+ }
+
+ _positional_lock_style = ps;
+
+ if (_positional_lock_style == MusicTime) {
+ pl->session().tempo_map().bbt_time (_position, _bbt_time);
+ }
+
+}
+
+void
+Region::update_position_after_tempo_map_change ()
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (!pl || _positional_lock_style != MusicTime) {
+ return;
+ }
+
+ TempoMap& map (pl->session().tempo_map());
+ nframes_t pos = map.frame_time (_bbt_time);
+ set_position_internal (pos, false);
+}
+
+void
+Region::set_position (nframes_t pos, void *src)
+{
+ if (!can_move()) {
+ return;
+ }
+
+ set_position_internal (pos, true);
+}
+
+void
+Region::set_position_internal (nframes_t pos, bool allow_bbt_recompute)
+{
+ if (_position != pos) {
+ _last_position = _position;
+ _position = pos;
+
+ /* check that the new _position wouldn't make the current
+ length impossible - if so, change the length.
+
+ XXX is this the right thing to do?
+ */
+
+ if (max_frames - _length < _position) {
+ _last_length = _length;
+ _length = max_frames - _position;
+ }
+
+ if (allow_bbt_recompute) {
+ recompute_position_from_lock_style ();
+ }
+
+ invalidate_transients ();
+ }
+
+ /* do this even if the position is the same. this helps out
+ a GUI that has moved its representation already.
+ */
+
+ send_change (PositionChanged);
+}
+
+void
+Region::set_position_on_top (nframes_t pos, void *src)
+{
+ if (_flags & Locked) {
+ return;
+ }
+
+ if (_position != pos) {
+ _last_position = _position;
+ _position = pos;
+ }
+
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (pl) {
+ pl->raise_region_to_top (shared_from_this ());
+ }
+
+ /* do this even if the position is the same. this helps out
+ a GUI that has moved its representation already.
+ */
+
+ send_change (PositionChanged);
+}
+
+void
+Region::recompute_position_from_lock_style ()
+{
+ if (_positional_lock_style == MusicTime) {
+ boost::shared_ptr<Playlist> pl (playlist());
+ if (pl) {
+ pl->session().tempo_map().bbt_time (_position, _bbt_time);
+ }
+ }
+}
+
+void
+Region::nudge_position (nframes64_t n, void *src)
+{
+ if (_flags & Locked) {
+ return;
+ }
+
+ if (n == 0) {
+ return;
+ }
+
+ _last_position = _position;
+
+ if (n > 0) {
+ if (_position > max_frames - n) {
+ _position = max_frames;
+ } else {
+ _position += n;
+ }
+ } else {
+ if (_position < (nframes_t) -n) {
+ _position = 0;
+ } else {
+ _position += n;
+ }
+ }
+
+ send_change (PositionChanged);
+}
+
+void
+Region::set_ancestral_data (nframes64_t s, nframes64_t l, float st, float sh)
+{
+ _ancestral_length = l;
+ _ancestral_start = s;
+ _stretch = st;
+ _shift = sh;
+}
+
+void
+Region::set_start (nframes_t pos, void *src)
+{
+ if (_flags & (Locked|PositionLocked)) {
+ return;
+ }
+ /* This just sets the start, nothing else. It effectively shifts
+ the contents of the Region within the overall extent of the Source,
+ without changing the Region's position or length
+ */
+
+ if (_start != pos) {
+
+ if (!verify_start (pos)) {
+ return;
+ }
+
+ _start = pos;
+ _flags = Region::Flag (_flags & ~WholeFile);
+ first_edit ();
+ invalidate_transients ();
+
+ send_change (StartChanged);
+ }
+}
+
+void
+Region::trim_start (nframes_t new_position, void *src)
+{
+ if (_flags & (Locked|PositionLocked)) {
+ return;
+ }
+ nframes_t new_start;
+ int32_t start_shift;
+
+ if (new_position > _position) {
+ start_shift = new_position - _position;
+ } else {
+ start_shift = -(_position - new_position);
+ }
+
+ if (start_shift > 0) {
+
+ if (_start > max_frames - start_shift) {
+ new_start = max_frames;
+ } else {
+ new_start = _start + start_shift;
+ }
+
+ if (!verify_start (new_start)) {
+ return;
+ }
+
+ } else if (start_shift < 0) {
+
+ if (_start < (nframes_t) -start_shift) {
+ new_start = 0;
+ } else {
+ new_start = _start + start_shift;
+ }
+ } else {
+ return;
+ }
+
+ if (new_start == _start) {
+ return;
+ }
+
+ _start = new_start;
+ _flags = Region::Flag (_flags & ~WholeFile);
+ first_edit ();
+
+ send_change (StartChanged);
+}
+
+void
+Region::trim_front (nframes_t new_position, void *src)
+{
+ if (_flags & Locked) {
+ return;
+ }
+
+ nframes_t end = last_frame();
+ nframes_t source_zero;
+
+ if (_position > _start) {
+ source_zero = _position - _start;
+ } else {
+ source_zero = 0; // its actually negative, but this will work for us
+ }
+
+ if (new_position < end) { /* can't trim it zero or negative length */
+
+ nframes_t newlen;
+
+ /* can't trim it back passed where source position zero is located */
+
+ new_position = max (new_position, source_zero);
+
+
+ if (new_position > _position) {
+ newlen = _length - (new_position - _position);
+ } else {
+ newlen = _length + (_position - new_position);
+ }
+
+ trim_to_internal (new_position, newlen, src);
+ if (!_frozen) {
+ recompute_at_start ();
+ }
+ }
+}
+
+void
+Region::trim_end (nframes_t new_endpoint, void *src)
+{
+ if (_flags & Locked) {
+ return;
+ }
+
+ if (new_endpoint > _position) {
+ trim_to_internal (_position, new_endpoint - _position, this);
+ if (!_frozen) {
+ recompute_at_end ();
+ }
+ }
+}
+
+void
+Region::trim_to (nframes_t position, nframes_t length, void *src)
+{
+ if (_flags & Locked) {
+ return;
+ }
+
+ trim_to_internal (position, length, src);
+
+ if (!_frozen) {
+ recompute_at_start ();
+ recompute_at_end ();
+ }
+}
+
+void
+Region::trim_to_internal (nframes_t position, nframes_t length, void *src)
+{
+ int32_t start_shift;
+ nframes_t new_start;
+
+ if (_flags & Locked) {
+ return;
+ }
+
+ if (position > _position) {
+ start_shift = position - _position;
+ } else {
+ start_shift = -(_position - position);
+ }
+
+ if (start_shift > 0) {
+
+ if (_start > max_frames - start_shift) {
+ new_start = max_frames;
+ } else {
+ new_start = _start + start_shift;
+ }
+
+
+ } else if (start_shift < 0) {
+
+ if (_start < (nframes_t) -start_shift) {
+ new_start = 0;
+ } else {
+ new_start = _start + start_shift;
+ }
+ } else {
+ new_start = _start;
+ }
+
+ if (!verify_start_and_length (new_start, length)) {
+ return;
+ }
+
+ Change what_changed = Change (0);
+
+ if (_start != new_start) {
+ _start = new_start;
+ what_changed = Change (what_changed|StartChanged);
+ }
+ if (_length != length) {
+ if (!_frozen) {
+ _last_length = _length;
+ }
+ _length = length;
+ what_changed = Change (what_changed|LengthChanged);
+ }
+ if (_position != position) {
+ if (!_frozen) {
+ _last_position = _position;
+ }
+ _position = position;
+ what_changed = Change (what_changed|PositionChanged);
+ }
+
+ _flags = Region::Flag (_flags & ~WholeFile);
+
+ if (what_changed & (StartChanged|LengthChanged)) {
+ first_edit ();
+ }
+
+ if (what_changed) {
+ send_change (what_changed);
+ }
+}
+
+void
+Region::set_hidden (bool yn)
+{
+ if (hidden() != yn) {
+
+ if (yn) {
+ _flags = Flag (_flags|Hidden);
+ } else {
+ _flags = Flag (_flags & ~Hidden);
+ }
+
+ send_change (HiddenChanged);
+ }
+}
+
+void
+Region::set_muted (bool yn)
+{
+ if (muted() != yn) {
+
+ if (yn) {
+ _flags = Flag (_flags|Muted);
+ } else {
+ _flags = Flag (_flags & ~Muted);
+ }
+
+ send_change (MuteChanged);
+ }
+}
+
+void
+Region::set_opaque (bool yn)
+{
+ if (opaque() != yn) {
+ if (yn) {
+ _flags = Flag (_flags|Opaque);
+ } else {
+ _flags = Flag (_flags & ~Opaque);
+ }
+ send_change (OpacityChanged);
+ }
+}
+
+void
+Region::set_locked (bool yn)
+{
+ if (locked() != yn) {
+ if (yn) {
+ _flags = Flag (_flags|Locked);
+ } else {
+ _flags = Flag (_flags & ~Locked);
+ }
+ send_change (LockChanged);
+ }
+}
+
+void
+Region::set_position_locked (bool yn)
+{
+ if (position_locked() != yn) {
+ if (yn) {
+ _flags = Flag (_flags|PositionLocked);
+ } else {
+ _flags = Flag (_flags & ~PositionLocked);
+ }
+ send_change (LockChanged);
+ }
+}
+
+void
+Region::set_sync_position (nframes_t absolute_pos)
+{
+ nframes_t file_pos;
+
+ file_pos = _start + (absolute_pos - _position);
+
+ if (file_pos != _sync_position) {
+
+ _sync_position = file_pos;
+ _flags = Flag (_flags|SyncMarked);
+
+ if (!_frozen) {
+ maybe_uncopy ();
+ }
+ send_change (SyncOffsetChanged);
+ }
+}
+
+void
+Region::clear_sync_position ()
+{
+ if (_flags & SyncMarked) {
+ _flags = Flag (_flags & ~SyncMarked);
+
+ if (!_frozen) {
+ maybe_uncopy ();
+ }
+ send_change (SyncOffsetChanged);
+ }
+}
+
+nframes_t
+Region::sync_offset (int& dir) const
+{
+ /* returns the sync point relative the first frame of the region */
+
+ if (_flags & SyncMarked) {
+ if (_sync_position > _start) {
+ dir = 1;
+ return _sync_position - _start;
+ } else {
+ dir = -1;
+ return _start - _sync_position;
+ }
+ } else {
+ dir = 0;
+ return 0;
+ }
+}
+
+nframes_t
+Region::adjust_to_sync (nframes_t pos)
+{
+ int sync_dir;
+ nframes_t offset = sync_offset (sync_dir);
+
+ // cerr << "adjusting pos = " << pos << " to sync at " << _sync_position << " offset = " << offset << " with dir = " << sync_dir << endl;
+
+ if (sync_dir > 0) {
+ if (pos > offset) {
+ pos -= offset;
+ } else {
+ pos = 0;
+ }
+ } else {
+ if (max_frames - pos > offset) {
+ pos += offset;
+ }
+ }
+
+ return pos;
+}
+
+nframes_t
+Region::sync_position() const
+{
+ if (_flags & SyncMarked) {
+ return _sync_position;
+ } else {
+ return _start;
+ }
+}
+
+void
+Region::raise ()
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+ if (pl) {
+ pl->raise_region (shared_from_this ());
+ }
+}
+
+void
+Region::lower ()
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+ if (pl) {
+ pl->lower_region (shared_from_this ());
+ }
+}
+
+
+void
+Region::raise_to_top ()
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+ if (pl) {
+ pl->raise_region_to_top (shared_from_this());
+ }
+}
+
+void
+Region::lower_to_bottom ()
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+ if (pl) {
+ pl->lower_region_to_bottom (shared_from_this());
+ }
+}
+
+void
+Region::set_layer (layer_t l)
+{
+ if (_layer != l) {
+ _layer = l;
+
+ send_change (LayerChanged);
+ }
+}
+
+XMLNode&
+Region::state (bool full_state)
+{
+ XMLNode *node = new XMLNode ("Region");
+ char buf[64];
+ const char* fe = NULL;
+
+ _id.print (buf, sizeof (buf));
+ node->add_property ("id", buf);
+ node->add_property ("name", _name);
+ node->add_property ("type", _type.to_string());
+ snprintf (buf, sizeof (buf), "%u", _start);
+ node->add_property ("start", buf);
+ snprintf (buf, sizeof (buf), "%u", _length);
+ node->add_property ("length", buf);
+ snprintf (buf, sizeof (buf), "%u", _position);
+ node->add_property ("position", buf);
+ snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_start);
+ node->add_property ("ancestral-start", buf);
+ snprintf (buf, sizeof (buf), "%" PRIi64, _ancestral_length);
+ node->add_property ("ancestral-length", buf);
+ snprintf (buf, sizeof (buf), "%.12g", _stretch);
+ node->add_property ("stretch", buf);
+ snprintf (buf, sizeof (buf), "%.12g", _shift);
+ node->add_property ("shift", buf);
+
+ switch (_first_edit) {
+ case EditChangesNothing:
+ fe = X_("nothing");
+ break;
+ case EditChangesName:
+ fe = X_("name");
+ break;
+ case EditChangesID:
+ fe = X_("id");
+ break;
+ default: /* should be unreachable but makes g++ happy */
+ fe = X_("nothing");
+ break;
+ }
+
+ node->add_property ("first_edit", fe);
+
+ /* note: flags are stored by derived classes */
+
+ snprintf (buf, sizeof (buf), "%d", (int) _layer);
+ node->add_property ("layer", buf);
+ snprintf (buf, sizeof (buf), "%" PRIu32, _sync_position);
+ node->add_property ("sync-position", buf);
+
+ if (_positional_lock_style != AudioTime) {
+ node->add_property ("positional-lock-style", enum_2_string (_positional_lock_style));
+ stringstream str;
+ str << _bbt_time;
+ node->add_property ("bbt-position", str.str());
+ }
+
+ return *node;
+}
+
+XMLNode&
+Region::get_state ()
+{
+ return state (true);
+}
+
+int
+Region::set_live_state (const XMLNode& node, Change& what_changed, bool send)
+{
+ const XMLNodeList& nlist = node.children();
+ const XMLProperty *prop;
+ nframes_t val;
+
+ /* this is responsible for setting those aspects of Region state
+ that are mutable after construction.
+ */
+
+ if ((prop = node.property ("name")) == 0) {
+ error << _("XMLNode describing a Region is incomplete (no name)") << endmsg;
+ return -1;
+ }
+
+ _name = prop->value();
+
+ if ((prop = node.property ("type")) == 0) {
+ _type = DataType::AUDIO;
+ } else {
+ _type = DataType(prop->value());
+ }
+
+ if ((prop = node.property ("start")) != 0) {
+ sscanf (prop->value().c_str(), "%" PRIu32, &val);
+ if (val != _start) {
+ what_changed = Change (what_changed|StartChanged);
+ _start = val;
+ }
+ } else {
+ _start = 0;
+ }
+
+ if ((prop = node.property ("length")) != 0) {
+ sscanf (prop->value().c_str(), "%" PRIu32, &val);
+ if (val != _length) {
+ what_changed = Change (what_changed|LengthChanged);
+ _last_length = _length;
+ _length = val;
+ }
+ } else {
+ _last_length = _length;
+ _length = 1;
+ }
+
+ if ((prop = node.property ("position")) != 0) {
+ sscanf (prop->value().c_str(), "%" PRIu32, &val);
+ if (val != _position) {
+ what_changed = Change (what_changed|PositionChanged);
+ _last_position = _position;
+ _position = val;
+ }
+ } else {
+ _last_position = _position;
+ _position = 0;
+ }
+
+ if ((prop = node.property ("layer")) != 0) {
+ layer_t x;
+ x = (layer_t) atoi (prop->value().c_str());
+ if (x != _layer) {
+ what_changed = Change (what_changed|LayerChanged);
+ _layer = x;
+ }
+ } else {
+ _layer = 0;
+ }
+
+ if ((prop = node.property ("sync-position")) != 0) {
+ sscanf (prop->value().c_str(), "%" PRIu32, &val);
+ if (val != _sync_position) {
+ what_changed = Change (what_changed|SyncOffsetChanged);
+ _sync_position = val;
+ }
+ } else {
+ _sync_position = _start;
+ }
+
+ if ((prop = node.property ("positional-lock-style")) != 0) {
+ _positional_lock_style = PositionLockStyle (string_2_enum (prop->value(), _positional_lock_style));
+
+ if (_positional_lock_style == MusicTime) {
+ if ((prop = node.property ("bbt-position")) == 0) {
+ /* missing BBT info, revert to audio time locking */
+ _positional_lock_style = AudioTime;
+ } else {
+ if (sscanf (prop->value().c_str(), "%d|%d|%d",
+ &_bbt_time.bars,
+ &_bbt_time.beats,
+ &_bbt_time.ticks) != 3) {
+ _positional_lock_style = AudioTime;
+ }
+ }
+ }
+
+ } else {
+ _positional_lock_style = AudioTime;
+ }
+
+ /* XXX FIRST EDIT !!! */
+
+ /* these 3 properties never change as a result of any editing */
+
+ if ((prop = node.property ("ancestral-start")) != 0) {
+ _ancestral_start = atoi (prop->value());
+ } else {
+ _ancestral_start = _start;
+ }
+
+ if ((prop = node.property ("ancestral-length")) != 0) {
+ _ancestral_length = atoi (prop->value());
+ } else {
+ _ancestral_length = _length;
+ }
+
+ if ((prop = node.property ("stretch")) != 0) {
+ _stretch = atof (prop->value());
+ } else {
+ _stretch = 1.0;
+ }
+
+ if ((prop = node.property ("shift")) != 0) {
+ _shift = atof (prop->value());
+ } else {
+ _shift = 1.0;
+ }
+
+ /* note: derived classes set flags */
+
+ if (_extra_xml) {
+ delete _extra_xml;
+ _extra_xml = 0;
+ }
+
+ for (XMLNodeConstIterator niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ XMLNode *child;
+
+ child = (*niter);
+
+ if (child->name () == "extra") {
+ _extra_xml = new XMLNode (*child);
+ break;
+ }
+ }
+
+ if (send) {
+ send_change (what_changed);
+ }
+
+ return 0;
+}
+
+int
+Region::set_state (const XMLNode& node)
+{
+ const XMLProperty *prop;
+ Change what_changed = Change (0);
+
+ /* ID is not allowed to change, ever */
+
+ if ((prop = node.property ("id")) == 0) {
+ error << _("Session: XMLNode describing a Region is incomplete (no id)") << endmsg;
+ return -1;
+ }
+
+ _id = prop->value();
+
+ _first_edit = EditChangesNothing;
+
+ set_live_state (node, what_changed, true);
+
+ return 0;
+}
+
+void
+Region::freeze ()
+{
+ _frozen++;
+ _last_length = _length;
+ _last_position = _position;
+}
+
+void
+Region::thaw (const string& why)
+{
+ Change what_changed = Change (0);
+
+ {
+ Glib::Mutex::Lock lm (_lock);
+
+ if (_frozen && --_frozen > 0) {
+ return;
+ }
+
+ if (_pending_changed) {
+ what_changed = _pending_changed;
+ _pending_changed = Change (0);
+ }
+ }
+
+ if (what_changed == Change (0)) {
+ return;
+ }
+
+ if (what_changed & LengthChanged) {
+ if (what_changed & PositionChanged) {
+ recompute_at_start ();
+ }
+ recompute_at_end ();
+ }
+
+ StateChanged (what_changed);
+}
+
+void
+Region::send_change (Change what_changed)
+{
+ {
+ Glib::Mutex::Lock lm (_lock);
+ if (_frozen) {
+ _pending_changed = Change (_pending_changed|what_changed);
+ return;
+ }
+ }
+
+ StateChanged (what_changed);
+}
+
+void
+Region::set_last_layer_op (uint64_t when)
+{
+ _last_layer_op = when;
+}
+
+bool
+Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
+{
+ return coverage (other->first_frame(), other->last_frame()) != OverlapNone;
+}
+
+bool
+Region::equivalent (boost::shared_ptr<const Region> other) const
+{
+ return _start == other->_start &&
+ _position == other->_position &&
+ _length == other->_length;
+}
+
+bool
+Region::size_equivalent (boost::shared_ptr<const Region> other) const
+{
+ return _start == other->_start &&
+ _length == other->_length;
+}
+
+bool
+Region::region_list_equivalent (boost::shared_ptr<const Region> other) const
+{
+ return size_equivalent (other) && source_equivalent (other) && _name == other->_name;
+}
+
+void
+Region::source_deleted (boost::shared_ptr<Source>)
+{
+ _sources.clear ();
+ drop_references ();
+}
+
+vector<string>
+Region::master_source_names ()
+{
+ SourceList::iterator i;
+
+ vector<string> names;
+ for (i = _master_sources.begin(); i != _master_sources.end(); ++i) {
+ names.push_back((*i)->name());
+ }
+
+ return names;
+}
+
+void
+Region::set_master_sources (SourceList& srcs)
+{
+ _master_sources = srcs;
+}
+
+bool
+Region::source_equivalent (boost::shared_ptr<const Region> other) const
+{
+ if (!other)
+ return false;
+
+ SourceList::const_iterator i;
+ SourceList::const_iterator io;
+
+ for (i = _sources.begin(), io = other->_sources.begin(); i != _sources.end() && io != other->_sources.end(); ++i, ++io) {
+ if ((*i)->id() != (*io)->id()) {
+ return false;
+ }
+ }
+
+ for (i = _master_sources.begin(), io = other->_master_sources.begin(); i != _master_sources.end() && io != other->_master_sources.end(); ++i, ++io) {
+ if ((*i)->id() != (*io)->id()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool
+Region::verify_length (nframes_t len)
+{
+ if (source() && (source()->destructive() || source()->length_mutable())) {
+ return true;
+ }
+
+ nframes_t maxlen = 0;
+
+ for (uint32_t n=0; n < _sources.size(); ++n) {
+ maxlen = max (maxlen, _sources[n]->length() - _start);
+ }
+
+ len = min (len, maxlen);
+
+ return true;
+}
+
+bool
+Region::verify_start_and_length (nframes_t new_start, nframes_t& new_length)
+{
+ if (source() && (source()->destructive() || source()->length_mutable())) {
+ return true;
+ }
+
+ nframes_t maxlen = 0;
+
+ for (uint32_t n=0; n < _sources.size(); ++n) {
+ maxlen = max (maxlen, _sources[n]->length() - new_start);
+ }
+
+ new_length = min (new_length, maxlen);
+
+ return true;
+}
+
+bool
+Region::verify_start (nframes_t pos)
+{
+ if (source() && (source()->destructive() || source()->length_mutable())) {
+ return true;
+ }
+
+ for (uint32_t n=0; n < _sources.size(); ++n) {
+ if (pos > _sources[n]->length() - _length) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool
+Region::verify_start_mutable (nframes_t& new_start)
+{
+ if (source() && (source()->destructive() || source()->length_mutable())) {
+ return true;
+ }
+
+ for (uint32_t n=0; n < _sources.size(); ++n) {
+ if (new_start > _sources[n]->length() - _length) {
+ new_start = _sources[n]->length() - _length;
+ }
+ }
+ return true;
+}
+
+boost::shared_ptr<Region>
+Region::get_parent() const
+{
+ boost::shared_ptr<Playlist> pl (playlist());
+
+ if (pl) {
+ boost::shared_ptr<Region> r;
+ boost::shared_ptr<Region const> grrr2 = boost::dynamic_pointer_cast<Region const> (shared_from_this());
+
+ if (grrr2 && (r = pl->session().find_whole_file_parent (grrr2))) {
+ return boost::static_pointer_cast<Region> (r);
+ }
+ }
+
+ return boost::shared_ptr<Region>();
+}
+
+int
+Region::apply (Filter& filter)
+{
+ return filter.run (shared_from_this());
+}
+
+
+void
+Region::invalidate_transients ()
+{
+ _valid_transients = false;
+ _transients.clear ();
+}
+
diff --git a/libs/ardour/region_factory.cc b/libs/ardour/region_factory.cc
new file mode 100644
index 0000000000..84d8167240
--- /dev/null
+++ b/libs/ardour/region_factory.cc
@@ -0,0 +1,181 @@
+/*
+ Copyright (C) 2000-2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/error.h>
+
+#include <ardour/session.h>
+
+#include <ardour/region_factory.h>
+#include <ardour/region.h>
+#include <ardour/audioregion.h>
+#include <ardour/audiosource.h>
+#include <ardour/midi_source.h>
+#include <ardour/midi_region.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+sigc::signal<void,boost::shared_ptr<Region> > RegionFactory::CheckNewRegion;
+
+boost::shared_ptr<Region>
+RegionFactory::create (boost::shared_ptr<Region> region, nframes_t start,
+ nframes_t length, std::string name,
+ layer_t layer, Region::Flag flags, bool announce)
+{
+ boost::shared_ptr<const AudioRegion> other_a;
+ boost::shared_ptr<const MidiRegion> other_m;
+
+ if ((other_a = boost::dynamic_pointer_cast<AudioRegion>(region)) != 0) {
+ AudioRegion* ar = new AudioRegion (other_a, start, length, name, layer, flags);
+ boost::shared_ptr<AudioRegion> arp (ar);
+ boost::shared_ptr<Region> ret (boost::static_pointer_cast<Region> (arp));
+ if (announce) {
+ CheckNewRegion (ret);
+ }
+ return ret;
+ } else if ((other_m = boost::dynamic_pointer_cast<MidiRegion>(region)) != 0) {
+ MidiRegion* ar = new MidiRegion (other_m, start, length, name, layer, flags);
+ boost::shared_ptr<MidiRegion> arp (ar);
+ boost::shared_ptr<Region> ret (boost::static_pointer_cast<Region> (arp));
+ if (announce) {
+ CheckNewRegion (ret);
+ }
+ return ret;
+ } else {
+ fatal << _("programming error: RegionFactory::create() called with unknown Region type")
+ << endmsg;
+ /*NOTREACHED*/
+ return boost::shared_ptr<Region>();
+ }
+}
+
+boost::shared_ptr<Region>
+RegionFactory::create (boost::shared_ptr<const Region> region)
+{
+ boost::shared_ptr<const AudioRegion> ar;
+ boost::shared_ptr<const MidiRegion> mr;
+
+ if ((ar = boost::dynamic_pointer_cast<const AudioRegion>(region)) != 0) {
+ boost::shared_ptr<Region> ret (new AudioRegion (ar));
+ /* pure copy constructor - no CheckNewRegion emitted */
+ return ret;
+ } else if ((mr = boost::dynamic_pointer_cast<const MidiRegion>(region)) != 0) {
+ boost::shared_ptr<Region> ret (new MidiRegion (mr));
+ /* pure copy constructor - no CheckNewRegion emitted */
+ return ret;
+ } else {
+ fatal << _("programming error: RegionFactory::create() called with unknown Region type")
+ << endmsg;
+ /*NOTREACHED*/
+ return boost::shared_ptr<Region>();
+ }
+}
+
+boost::shared_ptr<Region>
+RegionFactory::create (boost::shared_ptr<AudioRegion> region, nframes_t start,
+ nframes_t length, std::string name,
+ layer_t layer, Region::Flag flags, bool announce)
+{
+ return create (boost::static_pointer_cast<Region> (region), start, length, name, layer, flags, announce);
+}
+
+boost::shared_ptr<Region>
+RegionFactory::create (Session& session, XMLNode& node, bool yn)
+{
+ boost::shared_ptr<Region> r = session.XMLRegionFactory (node, yn);
+ CheckNewRegion (r);
+ return r;
+}
+
+boost::shared_ptr<Region>
+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>();
+ }
+
+ if (srcs[0]->type() == DataType::AUDIO) {
+
+ AudioRegion* ar = new AudioRegion (srcs, start, length, name, layer, flags);
+ boost::shared_ptr<AudioRegion> arp (ar);
+ boost::shared_ptr<Region> ret (boost::static_pointer_cast<Region> (arp));
+ if (announce) {
+ CheckNewRegion (ret);
+ }
+ return ret;
+
+ } else if (srcs[0]->type() == DataType::MIDI) {
+
+ MidiRegion* ar = new MidiRegion (srcs, start, length, name, layer, flags);
+ boost::shared_ptr<MidiRegion> mrp (ar);
+ boost::shared_ptr<Region> ret (boost::static_pointer_cast<Region> (mrp));
+ if (announce) {
+ CheckNewRegion (ret);
+ }
+ return ret;
+
+ }
+
+ return boost::shared_ptr<Region> ();
+}
+
+boost::shared_ptr<Region>
+RegionFactory::create (SourceList& srcs, const XMLNode& node)
+{
+ if (srcs.empty()) {
+ return boost::shared_ptr<Region>();
+ }
+
+ if (srcs[0]->type() == DataType::AUDIO) {
+ boost::shared_ptr<Region> ret (new AudioRegion (srcs, node));
+ CheckNewRegion (ret);
+ return ret;
+ } else if (srcs[0]->type() == DataType::MIDI) {
+ boost::shared_ptr<Region> ret (new MidiRegion (srcs, node));
+ CheckNewRegion (ret);
+ return ret;
+ }
+
+ return boost::shared_ptr<Region> ();
+}
+
+boost::shared_ptr<Region>
+RegionFactory::create (boost::shared_ptr<Source> src, nframes_t start, nframes_t length, const string& name, layer_t layer, Region::Flag flags, bool announce)
+{
+ boost::shared_ptr<AudioSource> as;
+ boost::shared_ptr<MidiSource> ms;
+
+ if ((as = boost::dynamic_pointer_cast<AudioSource>(src)) != 0) {
+ boost::shared_ptr<Region> ret (new AudioRegion (as, start, length, name, layer, flags));
+ if (announce) {
+ CheckNewRegion (ret);
+ }
+ return ret;
+ } else if ((ms = boost::dynamic_pointer_cast<MidiSource>(src)) != 0) {
+ boost::shared_ptr<Region> ret (new MidiRegion (ms, start, length, name, layer, flags));
+ if (announce) {
+ CheckNewRegion (ret);
+ }
+ return ret;
+ }
+
+ return boost::shared_ptr<Region>();
+}
diff --git a/libs/ardour/resampled_source.cc b/libs/ardour/resampled_source.cc
new file mode 100644
index 0000000000..083fde95a1
--- /dev/null
+++ b/libs/ardour/resampled_source.cc
@@ -0,0 +1,128 @@
+/*
+ Copyright (C) 2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <pbd/error.h>
+#include <ardour/resampled_source.h>
+#include <pbd/failed_constructor.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+const uint32_t ResampledImportableSource::blocksize = 16384U;
+
+ResampledImportableSource::ResampledImportableSource (boost::shared_ptr<ImportableSource> src, nframes_t rate, SrcQuality srcq)
+ : source (src)
+{
+ int err;
+
+ source->seek (0);
+
+ /* Initialize the sample rate converter. */
+
+ int src_type = SRC_LINEAR;
+
+ switch (srcq) {
+ case SrcBest:
+ src_type = SRC_SINC_BEST_QUALITY;
+ break;
+ case SrcGood:
+ src_type = SRC_SINC_MEDIUM_QUALITY;
+ break;
+ case SrcQuick:
+ src_type = SRC_SINC_FASTEST;
+ break;
+ case SrcFast:
+ src_type = SRC_ZERO_ORDER_HOLD;
+ break;
+ case SrcFastest:
+ src_type = SRC_LINEAR;
+ break;
+ }
+
+ if ((src_state = src_new (src_type, source->channels(), &err)) == 0) {
+ error << string_compose(_("Import: src_new() failed : %1"), src_strerror (err)) << endmsg ;
+ throw failed_constructor ();
+ }
+
+ src_data.end_of_input = 0 ; /* Set this later. */
+
+ /* Start with zero to force load in while loop. */
+
+ src_data.input_frames = 0 ;
+ src_data.data_in = input ;
+
+ src_data.src_ratio = ((float) rate) / source->samplerate();
+
+ input = new float[blocksize];
+}
+
+ResampledImportableSource::~ResampledImportableSource ()
+{
+ src_state = src_delete (src_state) ;
+ delete [] input;
+}
+
+nframes_t
+ResampledImportableSource::read (Sample* output, nframes_t nframes)
+{
+ int err;
+
+ /* If the input buffer is empty, refill it. */
+
+ if (src_data.input_frames == 0) {
+
+ src_data.input_frames = source->read (input, blocksize);
+
+ /* The last read will not be a full buffer, so set end_of_input. */
+
+ if ((nframes_t) src_data.input_frames < blocksize) {
+ src_data.end_of_input = true;
+ }
+
+ src_data.input_frames /= source->channels();
+ src_data.data_in = input;
+ }
+
+ src_data.data_out = output;
+
+ if (!src_data.end_of_input) {
+ src_data.output_frames = nframes / source->channels();
+ } else {
+ src_data.output_frames = src_data.input_frames;
+ }
+
+ if ((err = src_process (src_state, &src_data))) {
+ error << string_compose(_("Import: %1"), src_strerror (err)) << endmsg ;
+ return 0 ;
+ }
+
+ /* Terminate if at end */
+
+ if (src_data.end_of_input && src_data.output_frames_gen == 0) {
+ return 0;
+ }
+
+ src_data.data_in += src_data.input_frames_used * source->channels();
+ src_data.input_frames -= src_data.input_frames_used ;
+
+ return src_data.output_frames_gen * source->channels();
+}
+
diff --git a/libs/ardour/reverse.cc b/libs/ardour/reverse.cc
new file mode 100644
index 0000000000..02ec2924b0
--- /dev/null
+++ b/libs/ardour/reverse.cc
@@ -0,0 +1,130 @@
+/*
+ Copyright (C) 2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+
+#include <pbd/basename.h>
+
+#include <ardour/types.h>
+#include <ardour/reverse.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/session.h>
+#include <ardour/audioregion.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+
+Reverse::Reverse (Session& s)
+ : Filter (s)
+{
+}
+
+Reverse::~Reverse ()
+{
+}
+
+int
+Reverse::run (boost::shared_ptr<Region> r)
+{
+ SourceList nsrcs;
+ SourceList::iterator si;
+ nframes_t blocksize = 256 * 1024;
+ Sample* buf = 0;
+ nframes_t fpos;
+ nframes_t fstart;
+ nframes_t to_read;
+ int ret = -1;
+
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion>(r);
+ if (!region)
+ return ret;
+
+ /* create new sources */
+
+ if (make_new_sources (region, nsrcs)) {
+ goto out;
+ }
+
+ fstart = region->start();
+
+ if (blocksize > region->length()) {
+ blocksize = region->length();
+ }
+
+ fpos = max (fstart, (fstart + region->length() - blocksize));
+ buf = new Sample[blocksize];
+ to_read = blocksize;
+
+ /* now read it backwards */
+
+ while (to_read) {
+
+ uint32_t n;
+
+ for (n = 0, si = nsrcs.begin(); n < region->n_channels(); ++n, ++si) {
+
+ /* read it in */
+
+ if (region->audio_source (n)->read (buf, fpos, to_read) != to_read) {
+ goto out;
+ }
+
+ /* swap memory order */
+
+ for (nframes_t i = 0; i < to_read/2; ++i) {
+ swap (buf[i],buf[to_read-1-i]);
+ }
+
+ /* write it out */
+
+ boost::shared_ptr<AudioSource> asrc(boost::dynamic_pointer_cast<AudioSource>(*si));
+
+ if (asrc && asrc->write (buf, to_read) != to_read) {
+ goto out;
+ }
+ }
+
+ if (fpos > fstart + blocksize) {
+ fpos -= to_read;
+ to_read = blocksize;
+ } else {
+ to_read = fpos - fstart;
+ fpos = fstart;
+ }
+ };
+
+ ret = finish (region, nsrcs);
+
+ out:
+
+ if (buf) {
+ delete [] buf;
+ }
+
+ if (ret) {
+ for (si = nsrcs.begin(); si != nsrcs.end(); ++si) {
+ boost::shared_ptr<AudioSource> asrc(boost::dynamic_pointer_cast<AudioSource>(*si));
+ asrc->mark_for_remove ();
+ }
+ }
+
+ return ret;
+}
diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc
new file mode 100644
index 0000000000..e655efedb2
--- /dev/null
+++ b/libs/ardour/route.cc
@@ -0,0 +1,2676 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cmath>
+#include <fstream>
+#include <cassert>
+
+#include <sigc++/bind.h>
+#include <pbd/xml++.h>
+#include <pbd/enumwriter.h>
+#include <pbd/stacktrace.h>
+
+#include <ardour/timestamps.h>
+#include <ardour/audioengine.h>
+#include <ardour/route.h>
+#include <ardour/buffer.h>
+#include <ardour/processor.h>
+#include <ardour/plugin_insert.h>
+#include <ardour/port_insert.h>
+#include <ardour/send.h>
+#include <ardour/session.h>
+#include <ardour/utils.h>
+#include <ardour/configuration.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/route_group.h>
+#include <ardour/port.h>
+#include <ardour/audio_port.h>
+#include <ardour/ladspa_plugin.h>
+#include <ardour/panner.h>
+#include <ardour/dB.h>
+#include <ardour/amp.h>
+#include <ardour/meter.h>
+#include <ardour/buffer_set.h>
+#include <ardour/mix.h>
+#include <ardour/profile.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+uint32_t Route::order_key_cnt = 0;
+sigc::signal<void> Route::SyncOrderKeys;
+
+Route::Route (Session& sess, string name, int input_min, int input_max, int output_min, int output_max, Flag flg, DataType default_type)
+ : IO (sess, name, input_min, input_max, output_min, output_max, default_type),
+ _flags (flg),
+ _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)),
+ _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl))
+{
+ init ();
+}
+
+Route::Route (Session& sess, const XMLNode& node, DataType default_type)
+ : IO (sess, *node.child ("IO"), default_type),
+ _solo_control (new ToggleControllable (X_("solo"), *this, ToggleControllable::SoloControl)),
+ _mute_control (new ToggleControllable (X_("mute"), *this, ToggleControllable::MuteControl))
+{
+ init ();
+ _set_state (node, false);
+}
+
+void
+Route::init ()
+{
+ processor_max_outs.reset();
+ _muted = false;
+ _soloed = false;
+ _solo_safe = false;
+ _recordable = true;
+ _active = true;
+ _phase_invert = false;
+ _denormal_protection = false;
+ order_keys[strdup (N_("signal"))] = order_key_cnt++;
+ _silent = false;
+ _meter_point = MeterPostFader;
+ _initial_delay = 0;
+ _roll_delay = 0;
+ _own_latency = 0;
+ _user_latency = 0;
+ _have_internal_generator = false;
+ _declickable = false;
+ _pending_declick = true;
+ _remote_control_id = 0;
+
+ _edit_group = 0;
+ _mix_group = 0;
+
+ _mute_affects_pre_fader = Config->get_mute_affects_pre_fader();
+ _mute_affects_post_fader = Config->get_mute_affects_post_fader();
+ _mute_affects_control_outs = Config->get_mute_affects_control_outs();
+ _mute_affects_main_outs = Config->get_mute_affects_main_outs();
+
+ solo_gain = 1.0;
+ desired_solo_gain = 1.0;
+ mute_gain = 1.0;
+ desired_mute_gain = 1.0;
+
+ _control_outs = 0;
+
+ input_changed.connect (mem_fun (this, &Route::input_change_handler));
+ output_changed.connect (mem_fun (this, &Route::output_change_handler));
+}
+
+Route::~Route ()
+{
+ clear_processors (PreFader);
+ clear_processors (PostFader);
+
+ for (OrderKeys::iterator i = order_keys.begin(); i != order_keys.end(); ++i) {
+ free ((void*)(i->first));
+ }
+
+ if (_control_outs) {
+ delete _control_outs;
+ }
+}
+
+void
+Route::set_remote_control_id (uint32_t id)
+{
+ if (id != _remote_control_id) {
+ _remote_control_id = id;
+ RemoteControlIDChanged ();
+ }
+}
+
+uint32_t
+Route::remote_control_id() const
+{
+ return _remote_control_id;
+}
+
+long
+Route::order_key (const char* name) const
+{
+ OrderKeys::const_iterator i;
+
+ for (i = order_keys.begin(); i != order_keys.end(); ++i) {
+ if (!strcmp (name, i->first)) {
+ return i->second;
+ }
+ }
+
+ return -1;
+}
+
+void
+Route::set_order_key (const char* name, long n)
+{
+ order_keys[strdup(name)] = n;
+
+ if (Config->get_sync_all_route_ordering()) {
+ for (OrderKeys::iterator x = order_keys.begin(); x != order_keys.end(); ++x) {
+ x->second = n;
+ }
+ }
+
+ _session.set_dirty ();
+}
+
+void
+Route::sync_order_keys ()
+{
+ uint32_t key;
+
+ if (order_keys.empty()) {
+ return;
+ }
+
+ OrderKeys::iterator x = order_keys.begin();
+ key = x->second;
+ ++x;
+
+ for (; x != order_keys.end(); ++x) {
+ x->second = key;
+ }
+}
+
+string
+Route::ensure_track_or_route_name(string name, Session &session)
+{
+ string newname = name;
+
+ while (session.route_by_name (newname)!=NULL)
+ {
+ newname = bump_name_once (newname);
+ }
+
+ return newname;
+}
+
+
+void
+Route::inc_gain (gain_t fraction, void *src)
+{
+ IO::inc_gain (fraction, src);
+}
+
+void
+Route::set_gain (gain_t val, void *src)
+{
+ if (src != 0 && _mix_group && src != _mix_group && _mix_group->is_active()) {
+
+ if (_mix_group->is_relative()) {
+
+ gain_t usable_gain = gain();
+ if (usable_gain < 0.000001f) {
+ usable_gain = 0.000001f;
+ }
+
+ gain_t delta = val;
+ if (delta < 0.000001f) {
+ delta = 0.000001f;
+ }
+
+ delta -= usable_gain;
+
+ if (delta == 0.0f)
+ return;
+
+ gain_t factor = delta / usable_gain;
+
+ if (factor > 0.0f) {
+ factor = _mix_group->get_max_factor(factor);
+ if (factor == 0.0f) {
+ _gain_control->Changed(); /* EMIT SIGNAL */
+ return;
+ }
+ } else {
+ factor = _mix_group->get_min_factor(factor);
+ if (factor == 0.0f) {
+ _gain_control->Changed(); /* EMIT SIGNAL */
+ return;
+ }
+ }
+
+ _mix_group->apply (&Route::inc_gain, factor, _mix_group);
+
+ } else {
+
+ _mix_group->apply (&Route::set_gain, val, _mix_group);
+ }
+
+ return;
+ }
+
+ if (val == gain()) {
+ return;
+ }
+
+ IO::set_gain (val, src);
+}
+
+/** Process this route for one (sub) cycle (process thread)
+ *
+ * @param bufs Scratch buffers to use for the signal path
+ * @param start_frame Initial transport frame
+ * @param end_frame Final transport frame
+ * @param nframes Number of frames to output (to ports)
+ * @param offset Output offset (of port buffers, for split cycles)
+ *
+ * Note that (end_frame - start_frame) may not be equal to nframes when the
+ * transport speed isn't 1.0 (eg varispeed).
+ */
+void
+Route::process_output_buffers (BufferSet& bufs,
+ nframes_t start_frame, nframes_t end_frame,
+ nframes_t nframes, nframes_t offset, bool with_processors, int declick,
+ bool meter)
+{
+ // This is definitely very audio-only for now
+ assert(_default_type == DataType::AUDIO);
+
+ ProcessorList::iterator i;
+ bool post_fader_work = false;
+ bool mute_declick_applied = false;
+ gain_t dmg, dsg, dg;
+ IO *co;
+ bool mute_audible;
+ bool solo_audible;
+ bool no_monitor;
+ gain_t* gab = _session.gain_automation_buffer();
+
+ switch (Config->get_monitoring_model()) {
+ case HardwareMonitoring:
+ case ExternalMonitoring:
+ no_monitor = true;
+ break;
+ default:
+ no_monitor = false;
+ }
+
+ declick = _pending_declick;
+
+ {
+ Glib::Mutex::Lock cm (_control_outs_lock, Glib::TRY_LOCK);
+
+ if (cm.locked()) {
+ co = _control_outs;
+ } else {
+ co = 0;
+ }
+ }
+
+ {
+ Glib::Mutex::Lock dm (declick_lock, Glib::TRY_LOCK);
+
+ if (dm.locked()) {
+ dmg = desired_mute_gain;
+ dsg = desired_solo_gain;
+ dg = _desired_gain;
+ } else {
+ dmg = mute_gain;
+ dsg = solo_gain;
+ dg = _gain;
+ }
+ }
+
+ /* ----------------------------------------------------------------------------------------------------
+ GLOBAL DECLICK (for transport changes etc.)
+ -------------------------------------------------------------------------------------------------- */
+
+ if (declick > 0) {
+ Amp::run_in_place (bufs, nframes, 0.0, 1.0, false);
+ _pending_declick = 0;
+ } else if (declick < 0) {
+ Amp::run_in_place (bufs, nframes, 1.0, 0.0, false);
+ _pending_declick = 0;
+ } else {
+
+ /* no global declick */
+
+ if (solo_gain != dsg) {
+ Amp::run_in_place (bufs, nframes, solo_gain, dsg, false);
+ solo_gain = dsg;
+ }
+ }
+
+
+ /* ----------------------------------------------------------------------------------------------------
+ INPUT METERING & MONITORING
+ -------------------------------------------------------------------------------------------------- */
+
+ if (meter && (_meter_point == MeterInput)) {
+ _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
+ }
+
+ if (!_soloed && _mute_affects_pre_fader && (mute_gain != dmg)) {
+ Amp::run_in_place (bufs, nframes, mute_gain, dmg, false);
+ mute_gain = dmg;
+ mute_declick_applied = true;
+ }
+
+ if ((_meter_point == MeterInput) && co) {
+
+ solo_audible = dsg > 0;
+ mute_audible = dmg > 0;// || !_mute_affects_pre_fader;
+
+ if ( // muted by solo of another track
+
+ !solo_audible ||
+
+ // muted by mute of this track
+
+ !mute_audible ||
+
+ // rec-enabled but not s/w monitoring
+
+ // TODO: this is probably wrong
+
+ (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording()))
+
+ ) {
+
+ co->silence (nframes, offset);
+
+ } else {
+
+ co->deliver_output (bufs, start_frame, end_frame, nframes, offset);
+
+ }
+ }
+
+ /* -----------------------------------------------------------------------------------------------------
+ DENORMAL CONTROL
+ -------------------------------------------------------------------------------------------------- */
+
+ if (_denormal_protection || Config->get_denormal_protection()) {
+
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+ Sample* const sp = i->data();
+
+ for (nframes_t nx = offset; nx < nframes + offset; ++nx) {
+ sp[nx] += 1.0e-27f;
+ }
+ }
+ }
+
+ /* ----------------------------------------------------------------------------------------------------
+ PRE-FADER REDIRECTS
+ -------------------------------------------------------------------------------------------------- */
+
+ if (with_processors) {
+ Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+ if (rm.locked()) {
+ if (mute_gain > 0 || !_mute_affects_pre_fader) {
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ switch ((*i)->placement()) {
+ case PreFader:
+ (*i)->run_in_place (bufs, start_frame, end_frame, nframes, offset);
+ break;
+ case PostFader:
+ post_fader_work = true;
+ break;
+ }
+ }
+ } else {
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ switch ((*i)->placement()) {
+ case PreFader:
+ (*i)->silence (nframes, offset);
+ break;
+ case PostFader:
+ post_fader_work = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // This really should already be true...
+ bufs.set_count(pre_fader_streams());
+
+ if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_post_fader) {
+ Amp::run_in_place (bufs, nframes, mute_gain, dmg, false);
+ mute_gain = dmg;
+ mute_declick_applied = true;
+ }
+
+ /* ----------------------------------------------------------------------------------------------------
+ PRE-FADER METERING & MONITORING
+ -------------------------------------------------------------------------------------------------- */
+
+ if (meter && (_meter_point == MeterPreFader)) {
+ _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
+ }
+
+
+ if ((_meter_point == MeterPreFader) && co) {
+
+ solo_audible = dsg > 0;
+ mute_audible = dmg > 0 || !_mute_affects_pre_fader;
+
+ if ( // muted by solo of another track
+
+ !solo_audible ||
+
+ // muted by mute of this track
+
+ !mute_audible ||
+
+ // rec-enabled but not s/w monitoring
+
+ (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording()))
+
+ ) {
+
+ co->silence (nframes, offset);
+
+ } else {
+
+ co->deliver_output (bufs, start_frame, end_frame, nframes, offset);
+ }
+ }
+
+ /* ----------------------------------------------------------------------------------------------------
+ GAIN STAGE
+ -------------------------------------------------------------------------------------------------- */
+
+ /* if not recording or recording and requiring any monitor signal, then apply gain */
+
+ if ( // not recording
+
+ !(record_enabled() && _session.actively_recording()) ||
+
+ // OR recording
+
+ // AND software monitoring required
+
+ Config->get_monitoring_model() == SoftwareMonitoring) {
+
+ if (apply_gain_automation) {
+
+ if (_phase_invert) {
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+ Sample* const sp = i->data();
+
+ for (nframes_t nx = 0; nx < nframes; ++nx) {
+ sp[nx] *= -gab[nx];
+ }
+ }
+ } else {
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+ Sample* const sp = i->data();
+
+ for (nframes_t nx = 0; nx < nframes; ++nx) {
+ sp[nx] *= gab[nx];
+ }
+ }
+ }
+
+ if (apply_gain_automation && _session.transport_rolling() && nframes > 0) {
+ _effective_gain = gab[nframes-1];
+ }
+
+ } else {
+
+ /* manual (scalar) gain */
+
+ if (_gain != dg) {
+
+ Amp::run_in_place (bufs, nframes, _gain, dg, _phase_invert);
+ _gain = dg;
+
+ } else if (_gain != 0 && (_phase_invert || _gain != 1.0)) {
+
+ /* no need to interpolate current gain value,
+ but its non-unity, so apply it. if the gain
+ is zero, do nothing because we'll ship silence
+ below.
+ */
+
+ gain_t this_gain;
+
+ if (_phase_invert) {
+ this_gain = -_gain;
+ } else {
+ this_gain = _gain;
+ }
+
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+ Sample* const sp = i->data();
+ apply_gain_to_buffer(sp,nframes,this_gain);
+ }
+
+ } else if (_gain == 0) {
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i) {
+ i->clear();
+ }
+ }
+ }
+
+ } else {
+
+ /* actively recording, no monitoring required; leave buffers as-is to save CPU cycles */
+
+ }
+
+ /* ----------------------------------------------------------------------------------------------------
+ POST-FADER REDIRECTS
+ -------------------------------------------------------------------------------------------------- */
+
+ /* note that post_fader_work cannot be true unless with_processors was also true, so don't test both */
+
+ if (post_fader_work) {
+
+ Glib::RWLock::ReaderLock rm (_processor_lock, Glib::TRY_LOCK);
+ if (rm.locked()) {
+ if (mute_gain > 0 || !_mute_affects_post_fader) {
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ switch ((*i)->placement()) {
+ case PreFader:
+ break;
+ case PostFader:
+ (*i)->run_in_place (bufs, start_frame, end_frame, nframes, offset);
+ break;
+ }
+ }
+ } else {
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ switch ((*i)->placement()) {
+ case PreFader:
+ break;
+ case PostFader:
+ (*i)->silence (nframes, offset);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_control_outs) {
+ Amp::run_in_place (bufs, nframes, mute_gain, dmg, false);
+ mute_gain = dmg;
+ mute_declick_applied = true;
+ }
+
+ /* ----------------------------------------------------------------------------------------------------
+ CONTROL OUTPUT STAGE
+ -------------------------------------------------------------------------------------------------- */
+
+ if ((_meter_point == MeterPostFader) && co) {
+
+ solo_audible = solo_gain > 0;
+ mute_audible = dmg > 0 || !_mute_affects_control_outs;
+
+ if ( // silent anyway
+
+ (_gain == 0 && !apply_gain_automation) ||
+
+ // muted by solo of another track
+
+ !solo_audible ||
+
+ // muted by mute of this track
+
+ !mute_audible ||
+
+ // recording but not s/w monitoring
+
+ (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording()))
+
+ ) {
+
+ co->silence (nframes, offset);
+
+ } else {
+
+ co->deliver_output (bufs, start_frame, end_frame, nframes, offset);
+ }
+ }
+
+ /* ----------------------------------------------------------------------
+ GLOBAL MUTE
+ ----------------------------------------------------------------------*/
+
+ if (!_soloed && (mute_gain != dmg) && !mute_declick_applied && _mute_affects_main_outs) {
+ Amp::run_in_place (bufs, nframes, mute_gain, dmg, false);
+ mute_gain = dmg;
+ mute_declick_applied = true;
+ }
+
+ /* ----------------------------------------------------------------------------------------------------
+ MAIN OUTPUT STAGE
+ -------------------------------------------------------------------------------------------------- */
+
+ solo_audible = dsg > 0;
+ mute_audible = dmg > 0 || !_mute_affects_main_outs;
+
+ if (n_outputs().get(_default_type) == 0) {
+
+ /* relax */
+
+ } else if (no_monitor && record_enabled() && (!Config->get_auto_input() || _session.actively_recording())) {
+
+ IO::silence (nframes, offset);
+
+ } else {
+
+ if ( // silent anyway
+
+ (_gain == 0 && !apply_gain_automation) ||
+
+ // muted by solo of another track, but not using control outs for solo
+
+ (!solo_audible && (Config->get_solo_model() != SoloBus)) ||
+
+ // muted by mute of this track
+
+ !mute_audible
+
+ ) {
+
+ /* don't use Route::silence() here, because that causes
+ all outputs (sends, port processors, etc. to be silent).
+ */
+
+ if (_meter_point == MeterPostFader) {
+ peak_meter().reset();
+ }
+
+ IO::silence (nframes, offset);
+
+ } else {
+
+ deliver_output(bufs, start_frame, end_frame, nframes, offset);
+
+ }
+
+ }
+
+ /* ----------------------------------------------------------------------------------------------------
+ POST-FADER METERING
+ -------------------------------------------------------------------------------------------------- */
+
+ if (meter && (_meter_point == MeterPostFader)) {
+ if ((_gain == 0 && !apply_gain_automation) || dmg == 0) {
+ _meter->reset();
+ } else {
+ _meter->run_in_place(output_buffers(), start_frame, end_frame, nframes, offset);
+ }
+ }
+}
+
+ChanCount
+Route::n_process_buffers ()
+{
+ return max (n_inputs(), processor_max_outs);
+}
+
+void
+Route::passthru (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset, int declick, bool meter_first)
+{
+ BufferSet& bufs = _session.get_scratch_buffers(n_process_buffers());
+
+ _silent = false;
+
+ collect_input (bufs, nframes, offset);
+
+ if (meter_first) {
+ _meter->run_in_place(bufs, start_frame, end_frame, nframes, offset);
+ meter_first = false;
+ }
+
+ process_output_buffers (bufs, start_frame, end_frame, nframes, offset, true, declick, meter_first);
+}
+
+void
+Route::passthru_silence (nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset, int declick, bool meter)
+{
+ process_output_buffers (_session.get_silent_buffers (n_process_buffers()), start_frame, end_frame, nframes, offset, true, declick, meter);
+}
+
+void
+Route::set_solo (bool yn, void *src)
+{
+ if (_solo_safe) {
+ return;
+ }
+
+ if (_mix_group && src != _mix_group && _mix_group->is_active()) {
+ _mix_group->apply (&Route::set_solo, yn, _mix_group);
+ return;
+ }
+
+ if (_soloed != yn) {
+ _soloed = yn;
+ solo_changed (src); /* EMIT SIGNAL */
+ _solo_control->Changed (); /* EMIT SIGNAL */
+ }
+}
+
+void
+Route::set_solo_mute (bool yn)
+{
+ Glib::Mutex::Lock lm (declick_lock);
+
+ /* Called by Session in response to another Route being soloed.
+ */
+
+ desired_solo_gain = (yn?0.0:1.0);
+}
+
+void
+Route::set_solo_safe (bool yn, void *src)
+{
+ if (_solo_safe != yn) {
+ _solo_safe = yn;
+ solo_safe_changed (src); /* EMIT SIGNAL */
+ }
+}
+
+void
+Route::set_mute (bool yn, void *src)
+
+{
+ if (_mix_group && src != _mix_group && _mix_group->is_active()) {
+ _mix_group->apply (&Route::set_mute, yn, _mix_group);
+ return;
+ }
+
+ if (_muted != yn) {
+ _muted = yn;
+ mute_changed (src); /* EMIT SIGNAL */
+
+ _mute_control->Changed (); /* EMIT SIGNAL */
+
+ Glib::Mutex::Lock lm (declick_lock);
+ desired_mute_gain = (yn?0.0f:1.0f);
+ }
+}
+
+int
+Route::add_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err)
+{
+ ChanCount old_rmo = processor_max_outs;
+
+ if (!_session.engine().connected()) {
+ return 1;
+ }
+
+ {
+ Glib::RWLock::WriterLock lm (_processor_lock);
+
+ boost::shared_ptr<PluginInsert> pi;
+ boost::shared_ptr<PortInsert> porti;
+
+ //processor->set_default_type(_default_type);
+
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(processor)) != 0) {
+ pi->set_count (1);
+
+ if (pi->natural_input_streams() == ChanCount::ZERO) {
+ /* generator plugin */
+ _have_internal_generator = true;
+ }
+
+ }
+
+ _processors.push_back (processor);
+
+ // Set up processor list channels. This will set processor->[input|output]_streams(),
+ // configure redirect ports properly, etc.
+ if (_reset_plugin_counts (err)) {
+ _processors.pop_back ();
+ _reset_plugin_counts (0); // it worked before we tried to add it ...
+ return -1;
+ }
+
+ // Ensure peak vector sizes before the plugin is activated
+ ChanCount potential_max_streams = max(processor->input_streams(), processor->output_streams());
+ _meter->configure_io(potential_max_streams, potential_max_streams);
+
+ processor->activate ();
+ processor->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+
+ _user_latency = 0;
+ }
+
+ if (processor_max_outs != old_rmo || old_rmo == ChanCount::ZERO) {
+ reset_panner ();
+ }
+
+ processors_changed (); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+int
+Route::add_processors (const ProcessorList& others, ProcessorStreams* err)
+{
+ ChanCount old_rmo = processor_max_outs;
+
+ if (!_session.engine().connected()) {
+ return 1;
+ }
+
+ {
+ Glib::RWLock::WriterLock lm (_processor_lock);
+
+ ProcessorList::iterator existing_end = _processors.end();
+ --existing_end;
+
+ ChanCount potential_max_streams;
+
+ for (ProcessorList::const_iterator i = others.begin(); i != others.end(); ++i) {
+
+ boost::shared_ptr<PluginInsert> pi;
+
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+ pi->set_count (1);
+
+ ChanCount m = max(pi->input_streams(), pi->output_streams());
+ if (m > potential_max_streams)
+ potential_max_streams = m;
+ }
+
+ // Ensure peak vector sizes before the plugin is activated
+ _meter->configure_io(potential_max_streams, potential_max_streams);
+
+ _processors.push_back (*i);
+
+ if (_reset_plugin_counts (err)) {
+ ++existing_end;
+ _processors.erase (existing_end, _processors.end());
+ _reset_plugin_counts (0); // it worked before we tried to add it ...
+ return -1;
+ }
+
+ (*i)->activate ();
+ (*i)->ActiveChanged.connect (bind (mem_fun (_session, &Session::update_latency_compensation), false, false));
+ }
+
+ _user_latency = 0;
+ }
+
+ if (processor_max_outs != old_rmo || old_rmo == ChanCount::ZERO) {
+ reset_panner ();
+ }
+
+ processors_changed (); /* EMIT SIGNAL */
+ return 0;
+}
+
+/** Turn off all processors with a given placement
+ * @param p Placement of processors to disable
+ */
+
+void
+Route::disable_processors (Placement p)
+{
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->placement() == p) {
+ (*i)->set_active (false);
+ }
+ }
+
+ _session.set_dirty ();
+}
+
+/** Turn off all redirects
+ */
+
+void
+Route::disable_processors ()
+{
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ (*i)->set_active (false);
+ }
+
+ _session.set_dirty ();
+}
+
+/** Turn off all redirects with a given placement
+ * @param p Placement of redirects to disable
+ */
+
+void
+Route::disable_plugins (Placement p)
+{
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (boost::dynamic_pointer_cast<PluginInsert> (*i) && (*i)->placement() == p) {
+ (*i)->set_active (false);
+ }
+ }
+
+ _session.set_dirty ();
+}
+
+/** Turn off all plugins
+ */
+
+void
+Route::disable_plugins ()
+{
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (boost::dynamic_pointer_cast<PluginInsert> (*i)) {
+ (*i)->set_active (false);
+ }
+ }
+
+ _session.set_dirty ();
+}
+
+
+void
+Route::ab_plugins (bool forward)
+{
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ if (forward) {
+
+ /* forward = turn off all active redirects, and mark them so that the next time
+ we go the other way, we will revert them
+ */
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (!boost::dynamic_pointer_cast<PluginInsert> (*i)) {
+ continue;
+ }
+
+ if ((*i)->active()) {
+ (*i)->set_active (false);
+ (*i)->set_next_ab_is_active (true);
+ } else {
+ (*i)->set_next_ab_is_active (false);
+ }
+ }
+
+ } else {
+
+ /* backward = if the redirect was marked to go active on the next ab, do so */
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+ if (!boost::dynamic_pointer_cast<PluginInsert> (*i)) {
+ continue;
+ }
+
+ if ((*i)->get_next_ab_is_active()) {
+ (*i)->set_active (true);
+ } else {
+ (*i)->set_active (false);
+ }
+ }
+ }
+
+ _session.set_dirty ();
+}
+
+
+/* Figure out the streams that will feed into PreFader */
+ChanCount
+Route::pre_fader_streams() const
+{
+ boost::shared_ptr<Processor> processor;
+
+ // Find the last pre-fader redirect
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->placement() == PreFader) {
+ processor = *i;
+ }
+ }
+
+ if (processor) {
+ return processor->output_streams();
+ } else {
+ return n_inputs ();
+ }
+}
+
+
+/** Remove processors with a given placement.
+ * @param p Placement of processors to remove.
+ */
+void
+Route::clear_processors (Placement p)
+{
+ const ChanCount old_rmo = processor_max_outs;
+
+ if (!_session.engine().connected()) {
+ return;
+ }
+
+ {
+ Glib::RWLock::WriterLock lm (_processor_lock);
+ ProcessorList new_list;
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->placement() == p) {
+ /* it's the placement we want to get rid of */
+ (*i)->drop_references ();
+ } else {
+ /* it's a different placement, so keep it */
+ new_list.push_back (*i);
+ }
+ }
+
+ _processors = new_list;
+ }
+
+ /* FIXME: can't see how this test can ever fire */
+ if (processor_max_outs != old_rmo) {
+ reset_panner ();
+ }
+
+ processor_max_outs.reset();
+ _have_internal_generator = false;
+ processors_changed (); /* EMIT SIGNAL */
+}
+
+int
+Route::remove_processor (boost::shared_ptr<Processor> processor, ProcessorStreams* err)
+{
+ ChanCount old_rmo = processor_max_outs;
+
+ if (!_session.engine().connected()) {
+ return 1;
+ }
+
+ processor_max_outs.reset();
+
+ {
+ Glib::RWLock::WriterLock lm (_processor_lock);
+ ProcessorList::iterator i;
+ bool removed = false;
+
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ if (*i == processor) {
+
+ ProcessorList::iterator tmp;
+
+ /* move along, see failure case for reset_plugin_counts()
+ where we may need to reprocessor the processor.
+ */
+
+ tmp = i;
+ ++tmp;
+
+ /* stop redirects that send signals to JACK ports
+ from causing noise as a result of no longer being
+ run.
+ */
+
+ boost::shared_ptr<IOProcessor> redirect;
+
+ if ((redirect = boost::dynamic_pointer_cast<IOProcessor> (*i)) != 0) {
+ redirect->io()->disconnect_inputs (this);
+ redirect->io()->disconnect_outputs (this);
+ }
+
+ _processors.erase (i);
+
+ i = tmp;
+ removed = true;
+ break;
+ }
+
+ _user_latency = 0;
+ }
+
+ if (!removed) {
+ /* what? */
+ return 1;
+ }
+
+ if (_reset_plugin_counts (err)) {
+ /* get back to where we where */
+ _processors.insert (i, processor);
+ /* we know this will work, because it worked before :) */
+ _reset_plugin_counts (0);
+ return -1;
+ }
+
+ bool foo = false;
+
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<PluginInsert> pi;
+
+ if ((pi = boost::dynamic_pointer_cast<PluginInsert>(*i)) != 0) {
+ if (pi->is_generator()) {
+ foo = true;
+ }
+ }
+ }
+
+ _have_internal_generator = foo;
+ }
+
+ if (old_rmo != processor_max_outs) {
+ reset_panner ();
+ }
+
+ processor->drop_references ();
+
+ processors_changed (); /* EMIT SIGNAL */
+ return 0;
+}
+
+int
+Route::reset_plugin_counts (ProcessorStreams* err)
+{
+ Glib::RWLock::WriterLock lm (_processor_lock);
+ return _reset_plugin_counts (err);
+}
+
+
+int
+Route::_reset_plugin_counts (ProcessorStreams* err)
+{
+ ProcessorList::iterator r;
+ map<Placement,list<ProcessorCount> > processor_map;
+ ChanCount initial_streams;
+ ChanCount post_fader_input;
+ int ret = -1;
+
+ /* Process each placement in order, checking to see if we
+ can really do what has been requested.
+ */
+
+ /* divide processors up by placement so we get the signal flow
+ properly modelled. we need to do this because the _processors
+ list is not sorted by placement, and because other reasons may
+ exist now or in the future for this separate treatment.
+ */
+
+ /* ... but it should/will be... */
+
+ for (r = _processors.begin(); r != _processors.end(); ++r) {
+
+ boost::shared_ptr<Processor> processor;
+
+ if ((processor = boost::dynamic_pointer_cast<Processor>(*r)) != 0) {
+ processor_map[processor->placement()].push_back (ProcessorCount (processor));
+ }
+ }
+
+ /* A: PreFader */
+
+ if ( ! check_some_plugin_counts (processor_map[PreFader], n_inputs (), err)) {
+ goto streamcount;
+ }
+
+ post_fader_input = (err ? err->count : n_inputs());
+
+ /* B: PostFader */
+
+ if ( ! check_some_plugin_counts (processor_map[PostFader], post_fader_input, err)) {
+ goto streamcount;
+ }
+
+ /* OK, everything can be set up correctly, so lets do it */
+
+ apply_some_plugin_counts (processor_map[PreFader]);
+ apply_some_plugin_counts (processor_map[PostFader]);
+
+ /* recompute max outs of any processor */
+
+ ret = 0;
+
+ streamcount:
+ processor_max_outs.reset();
+
+ for (r = _processors.begin(); r != _processors.end(); ++r) {
+ processor_max_outs = max ((*r)->output_streams (), processor_max_outs);
+ }
+
+ return 0;
+}
+
+int32_t
+Route::apply_some_plugin_counts (list<ProcessorCount>& iclist)
+{
+ list<ProcessorCount>::iterator i;
+
+ for (i = iclist.begin(); i != iclist.end(); ++i) {
+
+ cerr << "now applying for " << (*i).processor->name() << " in = " << (*i).in.n_audio() << " out = " << (*i).out.n_audio() << endl;
+
+ if ((*i).processor->configure_io ((*i).in, (*i).out)) {
+ return -1;
+ }
+ /* make sure that however many we have, they are all active */
+ (*i).processor->activate ();
+ }
+
+ return 0;
+}
+
+/** Returns whether \a iclist can be configured and run starting with
+ * \a required_inputs at the first processor's inputs.
+ * If false is returned, \a iclist can not be run with \a required_inputs, and \a err is set.
+ * Otherwise, \a err is set to the output of the list.
+ */
+bool
+Route::check_some_plugin_counts (list<ProcessorCount>& iclist, ChanCount required_inputs, ProcessorStreams* err)
+{
+ list<ProcessorCount>::iterator i;
+ size_t index = 0;
+
+ if (err) {
+ err->index = 0;
+ err->count = required_inputs;
+ }
+
+ for (i = iclist.begin(); i != iclist.end(); ++i) {
+
+
+ cerr << "Checking whether " << (*i).processor->name() << " can support " << required_inputs.n_audio() << " inputs\n";
+
+ if ((*i).processor->can_support_input_configuration (required_inputs) < 0) {
+ if (err) {
+ err->index = index;
+ err->count = required_inputs;
+ }
+ return false;
+ }
+
+ (*i).in = required_inputs;
+ (*i).out = (*i).processor->output_for_input_configuration (required_inputs);
+
+ cerr << "config looks like " << (*i).processor->name() << " in = " << (*i).in.n_audio() << " out = " << (*i).out.n_audio() << endl;
+
+ required_inputs = (*i).out;
+
+ ++index;
+ }
+
+ if (err) {
+ if (!iclist.empty()) {
+ err->index = index;
+ err->count = iclist.back().processor->output_for_input_configuration(required_inputs);
+ }
+ }
+
+ return true;
+}
+
+int
+Route::copy_processors (const Route& other, Placement placement, ProcessorStreams* err)
+{
+ ChanCount old_rmo = processor_max_outs;
+
+ ProcessorList to_be_deleted;
+
+ {
+ Glib::RWLock::WriterLock lm (_processor_lock);
+ ProcessorList::iterator tmp;
+ ProcessorList the_copy;
+
+ the_copy = _processors;
+
+ /* remove all relevant processors */
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ) {
+ tmp = i;
+ ++tmp;
+
+ if ((*i)->placement() == placement) {
+ to_be_deleted.push_back (*i);
+ _processors.erase (i);
+ }
+
+ i = tmp;
+ }
+
+ /* now copy the relevant ones from "other" */
+
+ for (ProcessorList::const_iterator i = other._processors.begin(); i != other._processors.end(); ++i) {
+ if ((*i)->placement() == placement) {
+ _processors.push_back (IOProcessor::clone (*i));
+ }
+ }
+
+ /* reset plugin stream handling */
+
+ if (_reset_plugin_counts (err)) {
+
+ /* FAILED COPY ATTEMPT: we have to restore order */
+
+ /* delete all cloned processors */
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ) {
+
+ tmp = i;
+ ++tmp;
+
+ if ((*i)->placement() == placement) {
+ _processors.erase (i);
+ }
+
+ i = tmp;
+ }
+
+ /* restore the natural order */
+
+ _processors = the_copy;
+ processor_max_outs = old_rmo;
+
+ /* we failed, even though things are OK again */
+
+ return -1;
+
+ } else {
+
+ /* SUCCESSFUL COPY ATTEMPT: delete the processors we removed pre-copy */
+ to_be_deleted.clear ();
+ _user_latency = 0;
+ }
+ }
+
+ if (processor_max_outs != old_rmo || old_rmo == ChanCount::ZERO) {
+ reset_panner ();
+ }
+
+ processors_changed (); /* EMIT SIGNAL */
+ return 0;
+}
+
+void
+Route::all_processors_flip ()
+{
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ if (_processors.empty()) {
+ return;
+ }
+
+ bool first_is_on = _processors.front()->active();
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ (*i)->set_active (!first_is_on);
+ }
+
+ _session.set_dirty ();
+}
+
+/** Set all processors with a given placement to a given active state.
+ * @param p Placement of processors to change.
+ * @param state New active state for those processors.
+ */
+void
+Route::all_processors_active (Placement p, bool state)
+{
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ if (_processors.empty()) {
+ return;
+ }
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->placement() == p) {
+ (*i)->set_active (state);
+ }
+ }
+
+ _session.set_dirty ();
+}
+
+struct ProcessorSorter {
+ bool operator() (boost::shared_ptr<const Processor> a, boost::shared_ptr<const Processor> b) {
+ return a->sort_key() < b->sort_key();
+ }
+};
+
+int
+Route::sort_processors (ProcessorStreams* err)
+{
+ {
+ ProcessorSorter comparator;
+ Glib::RWLock::WriterLock lm (_processor_lock);
+ ChanCount old_rmo = processor_max_outs;
+
+ /* the sweet power of C++ ... */
+
+ ProcessorList as_it_was_before = _processors;
+
+ _processors.sort (comparator);
+
+ if (_reset_plugin_counts (err)) {
+ _processors = as_it_was_before;
+ processor_max_outs = old_rmo;
+ return -1;
+ }
+ }
+
+ reset_panner ();
+ processors_changed (); /* EMIT SIGNAL */
+
+ return 0;
+}
+
+XMLNode&
+Route::get_state()
+{
+ return state(true);
+}
+
+XMLNode&
+Route::get_template()
+{
+ return state(false);
+}
+
+XMLNode&
+Route::state(bool full_state)
+{
+ XMLNode *node = new XMLNode("Route");
+ ProcessorList::iterator i;
+ char buf[32];
+
+ if (_flags) {
+ node->add_property("flags", enum_2_string (_flags));
+ }
+
+ node->add_property("default-type", _default_type.to_string());
+
+ node->add_property("active", _active?"yes":"no");
+ node->add_property("muted", _muted?"yes":"no");
+ node->add_property("soloed", _soloed?"yes":"no");
+ node->add_property("phase-invert", _phase_invert?"yes":"no");
+ node->add_property("denormal-protection", _denormal_protection?"yes":"no");
+ node->add_property("mute-affects-pre-fader", _mute_affects_pre_fader?"yes":"no");
+ node->add_property("mute-affects-post-fader", _mute_affects_post_fader?"yes":"no");
+ node->add_property("mute-affects-control-outs", _mute_affects_control_outs?"yes":"no");
+ node->add_property("mute-affects-main-outs", _mute_affects_main_outs?"yes":"no");
+
+ if (_edit_group) {
+ node->add_property("edit-group", _edit_group->name());
+ }
+ if (_mix_group) {
+ node->add_property("mix-group", _mix_group->name());
+ }
+
+ string order_string;
+ OrderKeys::iterator x = order_keys.begin();
+
+ while (x != order_keys.end()) {
+ order_string += string ((*x).first);
+ order_string += '=';
+ snprintf (buf, sizeof(buf), "%ld", (*x).second);
+ order_string += buf;
+
+ ++x;
+
+ if (x == order_keys.end()) {
+ break;
+ }
+
+ order_string += ':';
+ }
+ node->add_property ("order-keys", order_string);
+
+ node->add_child_nocopy (IO::state (full_state));
+ node->add_child_nocopy (_solo_control->get_state ());
+ node->add_child_nocopy (_mute_control->get_state ());
+
+ XMLNode* remote_control_node = new XMLNode (X_("remote_control"));
+ snprintf (buf, sizeof (buf), "%d", _remote_control_id);
+ remote_control_node->add_property (X_("id"), buf);
+ node->add_child_nocopy (*remote_control_node);
+
+ if (_control_outs) {
+ XMLNode* cnode = new XMLNode (X_("ControlOuts"));
+ cnode->add_child_nocopy (_control_outs->state (full_state));
+ node->add_child_nocopy (*cnode);
+ }
+
+ if (_comment.length()) {
+ XMLNode *cmt = node->add_child ("Comment");
+ cmt->add_content (_comment);
+ }
+
+ for (i = _processors.begin(); i != _processors.end(); ++i) {
+ node->add_child_nocopy((*i)->state (full_state));
+ }
+
+ if (_extra_xml){
+ node->add_child_copy (*_extra_xml);
+ }
+
+ return *node;
+}
+
+XMLNode&
+Route::get_processor_state ()
+{
+ XMLNode* root = new XMLNode (X_("redirects"));
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ root->add_child_nocopy ((*i)->state (true));
+ }
+
+ return *root;
+}
+
+int
+Route::set_processor_state (const XMLNode& root)
+{
+ if (root.name() != X_("redirects")) {
+ return -1;
+ }
+
+ XMLNodeList nlist;
+ XMLNodeList nnlist;
+ XMLNodeConstIterator iter;
+ XMLNodeConstIterator niter;
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ nlist = root.children();
+
+ for (iter = nlist.begin(); iter != nlist.end(); ++iter){
+
+ /* iter now points to a IOProcessor state node */
+
+ nnlist = (*iter)->children ();
+
+ for (niter = nnlist.begin(); niter != nnlist.end(); ++niter) {
+
+ /* find the IO child node, since it contains the ID we need */
+
+ /* XXX OOP encapsulation violation, ugh */
+
+ if ((*niter)->name() == IO::state_node_name) {
+
+ XMLProperty* prop = (*niter)->property (X_("id"));
+
+ if (!prop) {
+ warning << _("IOProcessor node has no ID, ignored") << endmsg;
+ break;
+ }
+
+ ID id = prop->value ();
+
+ /* now look for a processor with that ID */
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->id() == id) {
+ (*i)->set_state (**iter);
+ break;
+ }
+ }
+
+ break;
+
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+void
+Route::set_deferred_state ()
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+
+ if (!deferred_state) {
+ return;
+ }
+
+ nlist = deferred_state->children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+ add_processor_from_xml (**niter);
+ }
+
+ delete deferred_state;
+ deferred_state = 0;
+}
+
+void
+Route::add_processor_from_xml (const XMLNode& node)
+{
+ const XMLProperty *prop;
+
+ // legacy sessions use a different node name for sends
+ if (node.name() == "Send") {
+
+ try {
+ boost::shared_ptr<Send> send (new Send (_session, node));
+ add_processor (send);
+ }
+
+ catch (failed_constructor &err) {
+ error << _("Send construction failed") << endmsg;
+ return;
+ }
+
+ // use "Processor" in XML?
+ } else if (node.name() == "Processor") {
+
+ try {
+ if ((prop = node.property ("type")) != 0) {
+
+ boost::shared_ptr<Processor> processor;
+ bool have_insert = false;
+
+ if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
+ prop->value() == "lv2" ||
+ prop->value() == "vst" ||
+ prop->value() == "audiounit") {
+
+ processor.reset (new PluginInsert(_session, node));
+ have_insert = true;
+
+ } else if (prop->value() == "port") {
+
+ processor.reset (new PortInsert (_session, node));
+
+ } else if (prop->value() == "send") {
+
+ processor.reset (new Send (_session, node));
+ have_insert = true;
+
+ } else {
+
+ error << string_compose(_("unknown Processor type \"%1\"; ignored"), prop->value()) << endmsg;
+ }
+
+ add_processor (processor);
+
+ } else {
+ error << _("Processor XML node has no type property") << endmsg;
+ }
+ }
+
+ catch (failed_constructor &err) {
+ warning << _("processor could not be created. Ignored.") << endmsg;
+ return;
+ }
+ }
+}
+
+int
+Route::set_state (const XMLNode& node)
+{
+ return _set_state (node, true);
+}
+
+int
+Route::_set_state (const XMLNode& node, bool call_base)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLNode *child;
+ XMLPropertyList plist;
+ const XMLProperty *prop;
+
+ if (node.name() != "Route"){
+ error << string_compose(_("Bad node sent to Route::set_state() [%1]"), node.name()) << endmsg;
+ return -1;
+ }
+
+ if ((prop = node.property (X_("flags"))) != 0) {
+ _flags = Flag (string_2_enum (prop->value(), _flags));
+ } else {
+ _flags = Flag (0);
+ }
+
+ if ((prop = node.property (X_("default-type"))) != 0) {
+ _default_type = DataType(prop->value());
+ assert(_default_type != DataType::NIL);
+ }
+
+ if ((prop = node.property (X_("phase-invert"))) != 0) {
+ set_phase_invert (prop->value()=="yes"?true:false, this);
+ }
+
+ if ((prop = node.property (X_("denormal-protection"))) != 0) {
+ set_denormal_protection (prop->value()=="yes"?true:false, this);
+ }
+
+ _active = true;
+ if ((prop = node.property (X_("active"))) != 0) {
+ set_active (prop->value() == "yes");
+ }
+
+ if ((prop = node.property (X_("muted"))) != 0) {
+ bool yn = prop->value()=="yes"?true:false;
+
+ /* force reset of mute status */
+
+ _muted = !yn;
+ set_mute(yn, this);
+ mute_gain = desired_mute_gain;
+ }
+
+ if ((prop = node.property (X_("soloed"))) != 0) {
+ bool yn = prop->value()=="yes"?true:false;
+
+ /* force reset of solo status */
+
+ _soloed = !yn;
+ set_solo (yn, this);
+ solo_gain = desired_solo_gain;
+ }
+
+ if ((prop = node.property (X_("mute-affects-pre-fader"))) != 0) {
+ _mute_affects_pre_fader = (prop->value()=="yes")?true:false;
+ }
+
+ if ((prop = node.property (X_("mute-affects-post-fader"))) != 0) {
+ _mute_affects_post_fader = (prop->value()=="yes")?true:false;
+ }
+
+ if ((prop = node.property (X_("mute-affects-control-outs"))) != 0) {
+ _mute_affects_control_outs = (prop->value()=="yes")?true:false;
+ }
+
+ if ((prop = node.property (X_("mute-affects-main-outs"))) != 0) {
+ _mute_affects_main_outs = (prop->value()=="yes")?true:false;
+ }
+
+ if ((prop = node.property (X_("edit-group"))) != 0) {
+ RouteGroup* edit_group = _session.edit_group_by_name(prop->value());
+ if(edit_group == 0) {
+ error << string_compose(_("Route %1: unknown edit group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
+ } else {
+ set_edit_group(edit_group, this);
+ }
+ }
+
+ if ((prop = node.property (X_("order-keys"))) != 0) {
+
+ long n;
+
+ string::size_type colon, equal;
+ string remaining = prop->value();
+
+ while (remaining.length()) {
+
+ if ((equal = remaining.find_first_of ('=')) == string::npos || equal == remaining.length()) {
+ error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
+ << endmsg;
+ } else {
+ if (sscanf (remaining.substr (equal+1).c_str(), "%ld", &n) != 1) {
+ error << string_compose (_("badly formed order key string in state file! [%1] ... ignored."), remaining)
+ << endmsg;
+ } else {
+ set_order_key (remaining.substr (0, equal).c_str(), n);
+ }
+ }
+
+ colon = remaining.find_first_of (':');
+
+ if (colon != string::npos) {
+ remaining = remaining.substr (colon+1);
+ } else {
+ break;
+ }
+ }
+ }
+
+ nlist = node.children();
+
+ if (deferred_state) {
+ delete deferred_state;
+ }
+
+ deferred_state = new XMLNode(X_("deferred state"));
+
+ /* set parent class properties before anything else */
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+
+ child = *niter;
+
+ if (child->name() == IO::state_node_name && call_base) {
+
+ IO::set_state (*child);
+ break;
+ }
+ }
+
+ XMLNodeList processor_nodes;
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+
+ child = *niter;
+
+ if (child->name() == X_("Send") || child->name() == X_("Processor")) {
+ processor_nodes.push_back(child);
+ }
+
+ }
+
+ _set_processor_states(processor_nodes);
+
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter){
+ child = *niter;
+ // All processors have been applied already
+
+ if (child->name() == X_("Automation")) {
+
+ if ((prop = child->property (X_("path"))) != 0) {
+ load_automation (prop->value());
+ }
+
+ } else if (child->name() == X_("ControlOuts")) {
+
+ string coutname = _name;
+ coutname += _("[control]");
+
+ _control_outs = new IO (_session, coutname);
+ _control_outs->set_state (**(child->children().begin()));
+
+ } else if (child->name() == X_("Comment")) {
+
+ /* XXX this is a terrible API design in libxml++ */
+
+ XMLNode *cmt = *(child->children().begin());
+ _comment = cmt->content();
+
+ } else if (child->name() == X_("extra")) {
+
+ _extra_xml = new XMLNode (*child);
+
+ } else if (child->name() == X_("controllable") && (prop = child->property("name")) != 0) {
+
+ if (prop->value() == "solo") {
+ _solo_control->set_state (*child);
+ _session.add_controllable (_solo_control);
+ }
+ else if (prop->value() == "mute") {
+ _mute_control->set_state (*child);
+ _session.add_controllable (_mute_control);
+ }
+ }
+ else if (child->name() == X_("remote_control")) {
+ if ((prop = child->property (X_("id"))) != 0) {
+ int32_t x;
+ sscanf (prop->value().c_str(), "%d", &x);
+ set_remote_control_id (x);
+ }
+ }
+ }
+
+ if ((prop = node.property (X_("mix-group"))) != 0) {
+ RouteGroup* mix_group = _session.mix_group_by_name(prop->value());
+ if (mix_group == 0) {
+ error << string_compose(_("Route %1: unknown mix group \"%2 in saved state (ignored)"), _name, prop->value()) << endmsg;
+ } else {
+ set_mix_group(mix_group, this);
+ }
+ }
+
+ return 0;
+}
+
+void
+Route::_set_processor_states(const XMLNodeList &nlist)
+{
+ XMLNodeConstIterator niter;
+ char buf[64];
+
+ ProcessorList::iterator i, o;
+
+ // Iterate through existing processors, remove those which are not in the state list
+ for (i = _processors.begin(); i != _processors.end(); ) {
+ ProcessorList::iterator tmp = i;
+ ++tmp;
+
+ bool processorInStateList = false;
+
+ (*i)->id().print (buf, sizeof (buf));
+
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ // legacy sessions (IOProcessor as a child of Processor, both is-a IO)
+ if (strncmp(buf,(*niter)->child(X_("IOProcessor"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) {
+ processorInStateList = true;
+ break;
+ } else if (strncmp(buf,(*niter)->property(X_("id"))->value().c_str(), sizeof(buf)) == 0) {
+ processorInStateList = true;
+ break;
+ }
+ }
+
+ if (!processorInStateList) {
+ remove_processor (*i);
+ }
+
+
+ i = tmp;
+ }
+
+
+ // Iterate through state list and make sure all processors are on the track and in the correct order,
+ // set the state of existing processors according to the new state on the same go
+ i = _processors.begin();
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter, ++i) {
+
+ // Check whether the next processor in the list
+ o = i;
+
+ while (o != _processors.end()) {
+ (*o)->id().print (buf, sizeof (buf));
+ if ( strncmp(buf, (*niter)->child(X_("IOProcessor"))->child(X_("IO"))->property(X_("id"))->value().c_str(), sizeof(buf)) == 0)
+ break;
+ else if (strncmp(buf,(*niter)->property(X_("id"))->value().c_str(), sizeof(buf)) == 0)
+ break;
+
+ ++o;
+ }
+
+ if (o == _processors.end()) {
+ // If the processor (*niter) is not on the route, we need to create it
+ // and move it to the correct location
+
+ ProcessorList::iterator prev_last = _processors.end();
+ --prev_last; // We need this to check whether adding succeeded
+
+ add_processor_from_xml (**niter);
+
+ ProcessorList::iterator last = _processors.end();
+ --last;
+
+ if (prev_last == last) {
+ cerr << "Could not fully restore state as some processors were not possible to create" << endl;
+ continue;
+
+ }
+
+ boost::shared_ptr<Processor> tmp = (*last);
+ // remove the processor from the wrong location
+ _processors.erase(last);
+ // processor the new processor at the current location
+ _processors.insert(i, tmp);
+
+ --i; // move pointer to the newly processored processor
+ continue;
+ }
+
+ // We found the processor (*niter) on the route, first we must make sure the processor
+ // is at the location provided in the XML state
+ if (i != o) {
+ boost::shared_ptr<Processor> tmp = (*o);
+ // remove the old copy
+ _processors.erase(o);
+ // processor the processor at the correct location
+ _processors.insert(i, tmp);
+
+ --i; // move pointer so it points to the right processor
+ }
+
+ (*i)->set_state( (**niter) );
+ }
+
+ processors_changed ();
+}
+
+void
+Route::curve_reallocate ()
+{
+// _gain_automation_curve.finish_resize ();
+// _pan_automation_curve.finish_resize ();
+}
+
+void
+Route::silence (nframes_t nframes, nframes_t offset)
+{
+ if (!_silent) {
+
+ IO::silence (nframes, offset);
+
+ if (_control_outs) {
+ _control_outs->silence (nframes, offset);
+ }
+
+ {
+ Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+
+ if (lm.locked()) {
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<PluginInsert> pi;
+ if (!_active && (pi = boost::dynamic_pointer_cast<PluginInsert> (*i)) != 0) {
+ // skip plugins, they don't need anything when we're not active
+ continue;
+ }
+
+ (*i)->silence (nframes, offset);
+ }
+
+ if (nframes == _session.get_block_size() && offset == 0) {
+ // _silent = true;
+ }
+ }
+ }
+
+ }
+}
+
+int
+Route::set_control_outs (const vector<string>& ports)
+{
+ Glib::Mutex::Lock lm (_control_outs_lock);
+ vector<string>::const_iterator i;
+ size_t limit;
+
+ if (_control_outs) {
+ delete _control_outs;
+ _control_outs = 0;
+ }
+
+ if (is_control() || is_master()) {
+ /* no control outs for these two special busses */
+ return 0;
+ }
+
+ if (ports.empty()) {
+ return 0;
+ }
+
+ string coutname = _name;
+ coutname += _("[control]");
+
+ _control_outs = new IO (_session, coutname);
+
+ /* our control outs need as many outputs as we
+ have audio outputs. we track the changes in ::output_change_handler().
+ */
+
+ // XXX its stupid that we have to get this value twice
+
+ limit = n_outputs().n_audio();
+
+ if (_control_outs->ensure_io (ChanCount::ZERO, ChanCount (DataType::AUDIO, n_outputs().get (DataType::AUDIO)), true, this)) {
+ return -1;
+ }
+
+ /* now connect to the named ports */
+
+ for (size_t n = 0; n < limit; ++n) {
+ if (_control_outs->connect_output (_control_outs->output (n), ports[n % ports.size()], this)) {
+ error << string_compose (_("could not connect %1 to %2"), _control_outs->output(n)->name(), ports[n]) << endmsg;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void
+Route::set_edit_group (RouteGroup *eg, void *src)
+
+{
+ if (eg == _edit_group) {
+ return;
+ }
+
+ if (_edit_group) {
+ _edit_group->remove (this);
+ }
+
+ if ((_edit_group = eg) != 0) {
+ _edit_group->add (this);
+ }
+
+ _session.set_dirty ();
+ edit_group_changed (src); /* EMIT SIGNAL */
+}
+
+void
+Route::drop_edit_group (void *src)
+{
+ _edit_group = 0;
+ _session.set_dirty ();
+ edit_group_changed (src); /* EMIT SIGNAL */
+}
+
+void
+Route::set_mix_group (RouteGroup *mg, void *src)
+
+{
+ if (mg == _mix_group) {
+ return;
+ }
+
+ if (_mix_group) {
+ _mix_group->remove (this);
+ }
+
+ if ((_mix_group = mg) != 0) {
+ _mix_group->add (this);
+ }
+
+ _session.set_dirty ();
+ mix_group_changed (src); /* EMIT SIGNAL */
+}
+
+void
+Route::drop_mix_group (void *src)
+{
+ _mix_group = 0;
+ _session.set_dirty ();
+ mix_group_changed (src); /* EMIT SIGNAL */
+}
+
+void
+Route::set_comment (string cmt, void *src)
+{
+ _comment = cmt;
+ comment_changed (src);
+ _session.set_dirty ();
+}
+
+bool
+Route::feeds (boost::shared_ptr<Route> other)
+{
+ uint32_t i, j;
+
+ IO& self = *this;
+ uint32_t no = self.n_outputs().n_total();
+ uint32_t ni = other->n_inputs ().n_total();
+
+ for (i = 0; i < no; ++i) {
+ for (j = 0; j < ni; ++j) {
+ if (self.output(i)->connected_to (other->input(j)->name())) {
+ return true;
+ }
+ }
+ }
+
+ /* check IOProcessors which may also interconnect Routes */
+
+ for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); r++) {
+
+ boost::shared_ptr<IOProcessor> redirect = boost::dynamic_pointer_cast<IOProcessor>(*r);
+
+ if ( ! redirect)
+ continue;
+
+ // TODO: support internal redirects here
+
+ no = redirect->io()->n_outputs().n_total();
+
+ for (i = 0; i < no; ++i) {
+ for (j = 0; j < ni; ++j) {
+ if (redirect->io()->output(i)->connected_to (other->input (j)->name())) {
+ return true;
+ }
+ }
+ }
+ }
+
+ /* check for control room outputs which may also interconnect Routes */
+
+ if (_control_outs) {
+
+ no = _control_outs->n_outputs().n_total();
+
+ for (i = 0; i < no; ++i) {
+ for (j = 0; j < ni; ++j) {
+ if (_control_outs->output(i)->connected_to (other->input (j)->name())) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void
+Route::set_mute_config (mute_type t, bool onoff, void *src)
+{
+ switch (t) {
+ case PRE_FADER:
+ _mute_affects_pre_fader = onoff;
+ pre_fader_changed(src); /* EMIT SIGNAL */
+ break;
+
+ case POST_FADER:
+ _mute_affects_post_fader = onoff;
+ post_fader_changed(src); /* EMIT SIGNAL */
+ break;
+
+ case CONTROL_OUTS:
+ _mute_affects_control_outs = onoff;
+ control_outs_changed(src); /* EMIT SIGNAL */
+ break;
+
+ case MAIN_OUTS:
+ _mute_affects_main_outs = onoff;
+ main_outs_changed(src); /* EMIT SIGNAL */
+ break;
+ }
+}
+
+bool
+Route::get_mute_config (mute_type t)
+{
+ bool onoff = false;
+
+ switch (t){
+ case PRE_FADER:
+ onoff = _mute_affects_pre_fader;
+ break;
+ case POST_FADER:
+ onoff = _mute_affects_post_fader;
+ break;
+ case CONTROL_OUTS:
+ onoff = _mute_affects_control_outs;
+ break;
+ case MAIN_OUTS:
+ onoff = _mute_affects_main_outs;
+ break;
+ }
+
+ return onoff;
+}
+
+void
+Route::handle_transport_stopped (bool abort_ignored, bool did_locate, bool can_flush_processors)
+{
+ nframes_t now = _session.transport_frame();
+
+ {
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ if (!did_locate) {
+ automation_snapshot (now, true);
+ }
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+ if (Config->get_plugins_stop_with_transport() && can_flush_processors) {
+ (*i)->deactivate ();
+ (*i)->activate ();
+ }
+
+ (*i)->transport_stopped (now);
+ }
+ }
+
+ IO::transport_stopped (now);
+
+ _roll_delay = _initial_delay;
+}
+
+void
+Route::input_change_handler (IOChange change, void *ignored)
+{
+ if (change & ConfigurationChanged) {
+ reset_plugin_counts (0);
+ }
+}
+
+void
+Route::output_change_handler (IOChange change, void *ignored)
+{
+ if (change & ConfigurationChanged) {
+ if (_control_outs) {
+ _control_outs->ensure_io (ChanCount::ZERO, ChanCount(DataType::AUDIO, n_outputs().n_audio()), true, this);
+ }
+
+ reset_plugin_counts (0);
+ }
+}
+
+uint32_t
+Route::pans_required () const
+{
+ if (n_outputs().n_audio() < 2) {
+ return 0;
+ }
+
+ return max (n_inputs ().n_audio(), processor_max_outs.n_audio());
+}
+
+int
+Route::no_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset,
+ bool session_state_changing, bool can_record, bool rec_monitors_input)
+{
+ if (n_outputs().n_total() == 0) {
+ return 0;
+ }
+
+ if (session_state_changing || !_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ apply_gain_automation = false;
+
+ if (n_inputs().n_total()) {
+ passthru (start_frame, end_frame, nframes, offset, 0, false);
+ } else {
+ silence (nframes, offset);
+ }
+
+ return 0;
+}
+
+nframes_t
+Route::check_initial_delay (nframes_t nframes, nframes_t& offset, nframes_t& transport_frame)
+{
+ if (_roll_delay > nframes) {
+
+ _roll_delay -= nframes;
+ silence (nframes, offset);
+ /* transport frame is not legal for caller to use */
+ return 0;
+
+ } else if (_roll_delay > 0) {
+
+ nframes -= _roll_delay;
+
+ silence (_roll_delay, offset);
+
+ offset += _roll_delay;
+ transport_frame += _roll_delay;
+
+ _roll_delay = 0;
+ }
+
+ return nframes;
+}
+
+int
+Route::roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset, int declick,
+ bool can_record, bool rec_monitors_input)
+{
+ {
+ Glib::RWLock::ReaderLock lm (_processor_lock, Glib::TRY_LOCK);
+ if (lm.locked()) {
+ // automation snapshot can also be called from the non-rt context
+ // and it uses the processor list, so we take the lock out here
+ automation_snapshot (_session.transport_frame(), false);
+ }
+ }
+
+ if ((n_outputs().n_total() == 0 && _processors.empty()) || n_inputs().n_total() == 0 || !_active) {
+ silence (nframes, offset);
+ return 0;
+ }
+
+ nframes_t unused = 0;
+
+ if ((nframes = check_initial_delay (nframes, offset, unused)) == 0) {
+ return 0;
+ }
+
+ _silent = false;
+
+ apply_gain_automation = false;
+
+ {
+ Glib::Mutex::Lock am (_automation_lock, Glib::TRY_LOCK);
+
+ if (am.locked() && _session.transport_rolling()) {
+
+ if (_gain_control->list()->automation_playback()) {
+ apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
+ start_frame, end_frame, _session.gain_automation_buffer(), nframes);
+ }
+ }
+ }
+
+ passthru (start_frame, end_frame, nframes, offset, declick, false);
+
+ return 0;
+}
+
+int
+Route::silent_roll (nframes_t nframes, nframes_t start_frame, nframes_t end_frame, nframes_t offset,
+ bool can_record, bool rec_monitors_input)
+{
+ silence (nframes, offset);
+ return 0;
+}
+
+void
+Route::toggle_monitor_input ()
+{
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ i->ensure_monitor_input( ! i->monitoring_input());
+ }
+}
+
+bool
+Route::has_external_redirects () const
+{
+ // FIXME: what about sends?
+
+ boost::shared_ptr<const PortInsert> pi;
+
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((pi = boost::dynamic_pointer_cast<const PortInsert>(*i)) != 0) {
+
+ for (PortSet::const_iterator port = pi->io()->outputs().begin();
+ port != pi->io()->outputs().end(); ++port) {
+
+ string port_name = port->name();
+ string client_name = port_name.substr (0, port_name.find(':'));
+
+ /* only say "yes" if the redirect is actually in use */
+
+ if (client_name != "ardour" && pi->active()) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void
+Route::flush_processors ()
+{
+ /* XXX shouldn't really try to take this lock, since
+ this is called from the RT audio thread.
+ */
+
+ Glib::RWLock::ReaderLock lm (_processor_lock);
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ (*i)->deactivate ();
+ (*i)->activate ();
+ }
+}
+
+void
+Route::set_meter_point (MeterPoint p, void *src)
+{
+ if (_meter_point != p) {
+ _meter_point = p;
+ meter_change (src); /* EMIT SIGNAL */
+ _session.set_dirty ();
+ }
+}
+
+nframes_t
+Route::update_total_latency ()
+{
+ nframes_t old = _own_latency;
+
+ if (_user_latency) {
+ _own_latency = _user_latency;
+ } else {
+ _own_latency = 0;
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->active ()) {
+ _own_latency += (*i)->signal_latency ();
+ }
+ }
+ }
+
+ set_port_latency (_own_latency);
+
+ if (!_user_latency) {
+ /* this (virtual) function is used for pure Routes,
+ not derived classes like AudioTrack. this means
+ that the data processed here comes from an input
+ port, not prerecorded material, and therefore we
+ have to take into account any input latency.
+ */
+
+
+ _own_latency += input_latency ();
+ }
+
+ if (old != _own_latency) {
+ signal_latency_changed (); /* EMIT SIGNAL */
+ }
+
+ return _own_latency;
+}
+
+void
+Route::set_user_latency (nframes_t nframes)
+{
+ Latent::set_user_latency (nframes);
+ _session.update_latency_compensation (false, false);
+}
+
+void
+Route::set_latency_delay (nframes_t longest_session_latency)
+{
+ nframes_t old = _initial_delay;
+
+ if (_own_latency < longest_session_latency) {
+ _initial_delay = longest_session_latency - _own_latency;
+ } else {
+ _initial_delay = 0;
+ }
+
+ if (_initial_delay != old) {
+ initial_delay_changed (); /* EMIT SIGNAL */
+ }
+
+ if (_session.transport_stopped()) {
+ _roll_delay = _initial_delay;
+ }
+}
+
+void
+Route::automation_snapshot (nframes_t now, bool force)
+{
+ if (!force && !should_snapshot(now)) {
+ return;
+ }
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ // IO::automation_snapshot (now, force); ?
+ (*i)->automation_snapshot (now, force);
+ }
+}
+
+Route::ToggleControllable::ToggleControllable (std::string name, Route& s, ToggleType tp)
+ : Controllable (name), route (s), type(tp)
+{
+
+}
+
+void
+Route::ToggleControllable::set_value (float val)
+{
+ bool bval = ((val >= 0.5f) ? true: false);
+
+ switch (type) {
+ case MuteControl:
+ route.set_mute (bval, this);
+ break;
+ case SoloControl:
+ route.set_solo (bval, this);
+ break;
+ default:
+ break;
+ }
+}
+
+float
+Route::ToggleControllable::get_value (void) const
+{
+ float val = 0.0f;
+
+ switch (type) {
+ case MuteControl:
+ val = route.muted() ? 1.0f : 0.0f;
+ break;
+ case SoloControl:
+ val = route.soloed() ? 1.0f : 0.0f;
+ break;
+ default:
+ break;
+ }
+
+ return val;
+}
+
+void
+Route::set_block_size (nframes_t nframes)
+{
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ (*i)->set_block_size (nframes);
+ }
+}
+
+void
+Route::protect_automation ()
+{
+ Automatable::protect_automation();
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i)
+ (*i)->protect_automation();
+}
+
+void
+Route::set_pending_declick (int declick)
+{
+ if (_declickable) {
+ /* this call is not allowed to turn off a pending declick unless "force" is true */
+ if (declick) {
+ _pending_declick = declick;
+ }
+ // cerr << _name << ": after setting to " << declick << " pending declick = " << _pending_declick << endl;
+ } else {
+ _pending_declick = 0;
+ }
+
+}
diff --git a/libs/ardour/route_group.cc b/libs/ardour/route_group.cc
new file mode 100644
index 0000000000..f22deb5dbf
--- /dev/null
+++ b/libs/ardour/route_group.cc
@@ -0,0 +1,213 @@
+/*
+ Copyright (C) 2000-2002 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.
+
+*/
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include <algorithm>
+
+#include <sigc++/bind.h>
+
+#include <pbd/error.h>
+#include <pbd/enumwriter.h>
+
+#include <ardour/route_group.h>
+#include <ardour/audio_track.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/configuration.h>
+
+using namespace ARDOUR;
+using namespace sigc;
+using namespace std;
+
+RouteGroup::RouteGroup (Session& s, const string &n, Flag f)
+ : _session (s), _name (n), _flags (f)
+{
+}
+
+void
+RouteGroup::set_name (string str)
+{
+ _name = str;
+ _session.set_dirty ();
+ FlagsChanged (0); /* EMIT SIGNAL */
+}
+
+int
+RouteGroup::add (Route *r)
+{
+ routes.push_back (r);
+ r->GoingAway.connect (sigc::bind (mem_fun (*this, &RouteGroup::remove_when_going_away), r));
+ _session.set_dirty ();
+ changed (); /* EMIT SIGNAL */
+ return 0;
+}
+
+void
+RouteGroup::remove_when_going_away (Route *r)
+{
+ remove (r);
+}
+
+int
+RouteGroup::remove (Route *r)
+{
+ list<Route *>::iterator i;
+
+ if ((i = find (routes.begin(), routes.end(), r)) != routes.end()) {
+ routes.erase (i);
+ _session.set_dirty ();
+ changed (); /* EMIT SIGNAL */
+ return 0;
+ }
+ return -1;
+}
+
+
+gain_t
+RouteGroup::get_min_factor(gain_t factor)
+{
+ gain_t g;
+
+ for (list<Route *>::iterator i = routes.begin(); i != routes.end(); i++) {
+ g = (*i)->gain();
+
+ if ( (g+g*factor) >= 0.0f)
+ continue;
+
+ if ( g <= 0.0000003f )
+ return 0.0f;
+
+ factor = 0.0000003f/g - 1.0f;
+ }
+ return factor;
+}
+
+gain_t
+RouteGroup::get_max_factor(gain_t factor)
+{
+ gain_t g;
+
+ for (list<Route *>::iterator i = routes.begin(); i != routes.end(); i++) {
+ g = (*i)->gain();
+
+ // if the current factor woulnd't raise this route above maximum
+ if ( (g+g*factor) <= 1.99526231f)
+ continue;
+
+ // if route gain is already at peak, return 0.0f factor
+ if (g>=1.99526231f)
+ return 0.0f;
+
+ // factor is calculated so that it would raise current route to max
+ factor = 1.99526231f/g - 1.0f;
+ }
+
+ return factor;
+}
+
+XMLNode&
+RouteGroup::get_state (void)
+{
+ XMLNode *node = new XMLNode ("RouteGroup");
+ node->add_property ("name", _name);
+ node->add_property ("flags", enum_2_string (_flags));
+ return *node;
+}
+
+int
+RouteGroup::set_state (const XMLNode& node)
+{
+ const XMLProperty *prop;
+
+ if ((prop = node.property ("name")) != 0) {
+ _name = prop->value();
+ }
+
+ if ((prop = node.property ("flags")) != 0) {
+ _flags = Flag (string_2_enum (prop->value(), _flags));
+ }
+
+ return 0;
+}
+
+void
+RouteGroup::set_active (bool yn, void *src)
+{
+ if (is_active() == yn) {
+ return;
+ }
+ if (yn) {
+ _flags = Flag (_flags | Active);
+ } else {
+ _flags = Flag (_flags & ~Active);
+ }
+ _session.set_dirty ();
+ FlagsChanged (src); /* EMIT SIGNAL */
+}
+
+void
+RouteGroup::set_relative (bool yn, void *src)
+
+{
+ if (is_relative() == yn) {
+ return;
+ }
+ if (yn) {
+ _flags = Flag (_flags | Relative);
+ } else {
+ _flags = Flag (_flags & ~Relative);
+ }
+ _session.set_dirty ();
+ FlagsChanged (src); /* EMIT SIGNAL */
+}
+
+void
+RouteGroup::set_hidden (bool yn, void *src)
+
+{
+ if (is_hidden() == yn) {
+ return;
+ }
+ if (yn) {
+ _flags = Flag (_flags | Hidden);
+ if (Config->get_hiding_groups_deactivates_groups()) {
+ _flags = Flag (_flags & ~Active);
+ }
+ } else {
+ _flags = Flag (_flags & ~Hidden);
+ if (Config->get_hiding_groups_deactivates_groups()) {
+ _flags = Flag (_flags | Active);
+ }
+ }
+ _session.set_dirty ();
+ FlagsChanged (src); /* EMIT SIGNAL */
+}
+
+void
+RouteGroup::audio_track_group (set<AudioTrack*>& ats)
+{
+ for (list<Route*>::iterator i = routes.begin(); i != routes.end(); ++i) {
+ AudioTrack* at = dynamic_cast<AudioTrack*>(*i);
+ if (at) {
+ ats.insert (at);
+ }
+ }
+}
+
diff --git a/libs/ardour/send.cc b/libs/ardour/send.cc
new file mode 100644
index 0000000000..736a443c72
--- /dev/null
+++ b/libs/ardour/send.cc
@@ -0,0 +1,228 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+
+#include <pbd/xml++.h>
+
+#include <ardour/send.h>
+#include <ardour/session.h>
+#include <ardour/port.h>
+#include <ardour/audio_port.h>
+#include <ardour/buffer_set.h>
+#include <ardour/meter.h>
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+Send::Send (Session& s, Placement p)
+ : IOProcessor (s, string_compose (_("send %1"), (bitslot = s.next_send_id()) + 1), p)
+{
+ _metering = false;
+ ProcessorCreated (this); /* EMIT SIGNAL */
+}
+
+Send::Send (Session& s, const XMLNode& node)
+ : IOProcessor (s, "send", PreFader)
+{
+ _metering = false;
+
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+
+ ProcessorCreated (this); /* EMIT SIGNAL */
+}
+
+Send::Send (const Send& other)
+ : IOProcessor (other._session, string_compose (_("send %1"), (bitslot = other._session.next_send_id()) + 1), other.placement())
+{
+ _metering = false;
+ ProcessorCreated (this); /* EMIT SIGNAL */
+}
+
+Send::~Send ()
+{
+ GoingAway ();
+}
+
+XMLNode&
+Send::get_state(void)
+{
+ return state (true);
+}
+
+XMLNode&
+Send::state(bool full)
+{
+ XMLNode& node = IOProcessor::state(full);
+ char buf[32];
+ node.add_property ("type", "send");
+ snprintf (buf, sizeof (buf), "%" PRIu32, bitslot);
+ node.add_property ("bitslot", buf);
+
+ return node;
+}
+
+int
+Send::set_state(const XMLNode& node)
+{
+ XMLNodeList nlist = node.children();
+ XMLNodeIterator niter;
+ const XMLProperty* prop;
+
+ if ((prop = node.property ("bitslot")) == 0) {
+ bitslot = _session.next_send_id();
+ } else {
+ sscanf (prop->value().c_str(), "%" PRIu32, &bitslot);
+ _session.mark_send_id (bitslot);
+ }
+
+ const XMLNode* insert_node = &node;
+
+ /* Send has regular IO automation (gain, pan) */
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == "IOProcessor") {
+ insert_node = *niter;
+ } else if ((*niter)->name() == X_("Automation")) {
+ _io->set_automation_state (*(*niter), Parameter(GainAutomation));
+ }
+ }
+
+ IOProcessor::set_state (*insert_node);
+
+ return 0;
+}
+
+void
+Send::run_in_place (BufferSet& bufs, nframes_t start_frame, nframes_t end_frame, nframes_t nframes, nframes_t offset)
+{
+ if (active()) {
+
+ // we have to copy the input, because IO::deliver_output may alter the buffers
+ // in-place, which a send must never do.
+
+ BufferSet& sendbufs = _session.get_mix_buffers(bufs.count());
+
+ sendbufs.read_from(bufs, nframes);
+ assert(sendbufs.count() == bufs.count());
+
+ _io->deliver_output (sendbufs, start_frame, end_frame, nframes, offset);
+
+ if (_metering) {
+ if (_io->_gain == 0) {
+ _io->_meter->reset();
+ } else {
+ _io->_meter->run_in_place(_io->output_buffers(), start_frame, end_frame, nframes, offset);
+ }
+ }
+
+ } else {
+ _io->silence (nframes, offset);
+
+ if (_metering) {
+ _io->_meter->reset();
+ }
+ }
+}
+
+void
+Send::set_metering (bool yn)
+{
+ _metering = yn;
+
+ if (!_metering) {
+ /* XXX possible thread hazard here */
+ _io->peak_meter().reset();
+ }
+}
+
+bool
+Send::can_support_input_configuration (ChanCount in) const
+{
+ if (_io->input_maximum() == ChanCount::INFINITE && _io->output_maximum() == ChanCount::INFINITE) {
+
+ /* not configured yet */
+
+ return true; /* we can support anything the first time we're asked */
+
+ } else {
+
+ /* the "input" config for a port insert corresponds to how
+ many output ports it will have.
+ */
+
+ if (_io->output_maximum() == in) {
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+ChanCount
+Send::output_for_input_configuration (ChanCount in) const
+{
+ // from the internal (Insert) perspective a Send does not modify its input whatsoever
+ return in;
+}
+
+bool
+Send::configure_io (ChanCount in, ChanCount out)
+{
+ /* we're transparent no matter what. fight the power. */
+ if (out != in)
+ return false;
+
+ _io->set_output_maximum (in);
+ _io->set_output_minimum (in);
+ _io->set_input_maximum (ChanCount::ZERO);
+ _io->set_input_minimum (ChanCount::ZERO);
+
+ bool success = _io->ensure_io (ChanCount::ZERO, in, false, this) == 0;
+
+ if (success) {
+ Processor::configure_io(in, out);
+ _io->reset_panner();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+ChanCount
+Send::output_streams() const
+{
+ // this method reflects the idea that from the perspective of the Route's ProcessorList,
+ // a send is just a passthrough. that doesn't match what the Send actually does with its
+ // data, but since what it does is invisible to the Route, it appears to be a passthrough.
+
+ return _configured_input;
+}
+
+ChanCount
+Send::input_streams() const
+{
+ return _configured_input;
+}
+
+
diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc
new file mode 100644
index 0000000000..ff269cc931
--- /dev/null
+++ b/libs/ardour/session.cc
@@ -0,0 +1,4273 @@
+/*
+ Copyright (C) 1999-2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+#include <string>
+#include <vector>
+#include <sstream>
+#include <fstream>
+#include <cstdio> /* sprintf(3) ... grrr */
+#include <cmath>
+#include <cerrno>
+#include <unistd.h>
+#include <limits.h>
+
+#include <sigc++/bind.h>
+#include <sigc++/retype.h>
+
+#include <glibmm/thread.h>
+#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
+
+#include <pbd/error.h>
+#include <glibmm/thread.h>
+#include <pbd/pathscanner.h>
+#include <pbd/stl_delete.h>
+#include <pbd/basename.h>
+#include <pbd/stacktrace.h>
+#include <pbd/file_utils.h>
+
+#include <ardour/audioengine.h>
+#include <ardour/configuration.h>
+#include <ardour/session.h>
+#include <ardour/session_directory.h>
+#include <ardour/utils.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/audioplaylist.h>
+#include <ardour/audioregion.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/midi_diskstream.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/midi_region.h>
+#include <ardour/smf_source.h>
+#include <ardour/auditioner.h>
+#include <ardour/recent_sessions.h>
+#include <ardour/io_processor.h>
+#include <ardour/send.h>
+#include <ardour/processor.h>
+#include <ardour/plugin_insert.h>
+#include <ardour/port_insert.h>
+#include <ardour/auto_bundle.h>
+#include <ardour/slave.h>
+#include <ardour/tempo.h>
+#include <ardour/audio_track.h>
+#include <ardour/midi_track.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/named_selection.h>
+#include <ardour/crossfade.h>
+#include <ardour/playlist.h>
+#include <ardour/click.h>
+#include <ardour/data_type.h>
+#include <ardour/buffer_set.h>
+#include <ardour/source_factory.h>
+#include <ardour/region_factory.h>
+#include <ardour/filename_extensions.h>
+#include <ardour/session_directory.h>
+#include <ardour/tape_file_matcher.h>
+#include <ardour/analyser.h>
+
+#ifdef HAVE_LIBLO
+#include <ardour/osc.h>
+#endif
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+using boost::shared_ptr;
+
+#ifdef __x86_64__
+static const int CPU_CACHE_ALIGN = 64;
+#else
+static const int CPU_CACHE_ALIGN = 16; /* arguably 32 on most arches, but it matters less */
+#endif
+
+bool Session::_disable_all_loaded_plugins = false;
+
+Session::compute_peak_t Session::compute_peak = 0;
+Session::find_peaks_t Session::find_peaks = 0;
+Session::apply_gain_to_buffer_t Session::apply_gain_to_buffer = 0;
+Session::mix_buffers_with_gain_t Session::mix_buffers_with_gain = 0;
+Session::mix_buffers_no_gain_t Session::mix_buffers_no_gain = 0;
+
+sigc::signal<void,std::string> Session::Dialog;
+sigc::signal<int> Session::AskAboutPendingState;
+sigc::signal<int,nframes_t,nframes_t> Session::AskAboutSampleRateMismatch;
+sigc::signal<void> Session::SendFeedback;
+
+sigc::signal<void> Session::SMPTEOffsetChanged;
+sigc::signal<void> Session::StartTimeChanged;
+sigc::signal<void> Session::EndTimeChanged;
+
+sigc::signal<void> Session::AutoBindingOn;
+sigc::signal<void> Session::AutoBindingOff;
+
+Session::Session (AudioEngine &eng,
+ const string& fullpath,
+ const string& snapshot_name,
+ string mix_template)
+
+ : _engine (eng),
+ _scratch_buffers(new BufferSet()),
+ _silent_buffers(new BufferSet()),
+ _mix_buffers(new BufferSet()),
+ _mmc_port (default_mmc_port),
+ _mtc_port (default_mtc_port),
+ _midi_port (default_midi_port),
+ _session_dir (new SessionDirectory(fullpath)),
+ pending_events (2048),
+ post_transport_work((PostTransportWork)0),
+ _send_smpte_update (false),
+ midi_requests (128),
+ diskstreams (new DiskstreamList),
+ routes (new RouteList),
+ auditioner ((Auditioner*) 0),
+ _bundle_xml_node (0),
+ _click_io ((IO*) 0),
+ main_outs (0)
+{
+ bool new_session;
+
+ if (!eng.connected()) {
+ throw failed_constructor();
+ }
+
+ cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl;
+
+ n_physical_outputs = _engine.n_physical_outputs();
+ n_physical_inputs = _engine.n_physical_inputs();
+
+ first_stage_init (fullpath, snapshot_name);
+
+ 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 ();
+ throw failed_constructor ();
+ }
+ }
+
+ if (second_stage_init (new_session)) {
+ destroy ();
+ throw failed_constructor ();
+ }
+
+ store_recent_sessions(_name, _path);
+
+ bool was_dirty = dirty();
+
+ _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
+
+ Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed));
+
+ if (was_dirty) {
+ DirtyChanged (); /* EMIT SIGNAL */
+ }
+}
+
+Session::Session (AudioEngine &eng,
+ string fullpath,
+ string snapshot_name,
+ AutoConnectOption input_ac,
+ AutoConnectOption output_ac,
+ uint32_t control_out_channels,
+ uint32_t master_out_channels,
+ uint32_t requested_physical_in,
+ uint32_t requested_physical_out,
+ nframes_t initial_length)
+
+ : _engine (eng),
+ _scratch_buffers(new BufferSet()),
+ _silent_buffers(new BufferSet()),
+ _mix_buffers(new BufferSet()),
+ _mmc_port (default_mmc_port),
+ _mtc_port (default_mtc_port),
+ _midi_port (default_midi_port),
+ _session_dir ( new SessionDirectory(fullpath)),
+ pending_events (2048),
+ post_transport_work((PostTransportWork)0),
+ _send_smpte_update (false),
+ midi_requests (16),
+ diskstreams (new DiskstreamList),
+ routes (new RouteList),
+ _bundle_xml_node (0),
+ main_outs (0)
+
+{
+ bool new_session;
+
+ if (!eng.connected()) {
+ throw failed_constructor();
+ }
+
+ cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl;
+
+ n_physical_outputs = _engine.n_physical_outputs();
+ n_physical_inputs = _engine.n_physical_inputs();
+
+ if (n_physical_inputs) {
+ n_physical_inputs = max (requested_physical_in, n_physical_inputs);
+ }
+
+ if (n_physical_outputs) {
+ n_physical_outputs = max (requested_physical_out, n_physical_outputs);
+ }
+
+ first_stage_init (fullpath, snapshot_name);
+
+ new_session = !g_file_test (_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR));
+
+ if (new_session) {
+ if (create (new_session, string(), initial_length)) {
+ destroy ();
+ throw failed_constructor ();
+ }
+ }
+
+ {
+ /* set up Master Out and Control Out if necessary */
+
+ RouteList rl;
+ int control_id = 1;
+
+ if (control_out_channels) {
+ shared_ptr<Route> r (new Route (*this, _("monitor"), -1, control_out_channels, -1, control_out_channels, Route::ControlOut));
+ r->set_remote_control_id (control_id++);
+
+ rl.push_back (r);
+ }
+
+ if (master_out_channels) {
+ shared_ptr<Route> r (new Route (*this, _("master"), -1, master_out_channels, -1, master_out_channels, Route::MasterOut));
+ r->set_remote_control_id (control_id);
+
+ rl.push_back (r);
+ } else {
+ /* prohibit auto-connect to master, because there isn't one */
+ output_ac = AutoConnectOption (output_ac & ~AutoConnectMaster);
+ }
+
+ if (!rl.empty()) {
+ add_routes (rl, false);
+ }
+
+ }
+
+ Config->set_input_auto_connect (input_ac);
+ Config->set_output_auto_connect (output_ac);
+
+ if (second_stage_init (new_session)) {
+ destroy ();
+ throw failed_constructor ();
+ }
+
+ store_recent_sessions (_name, _path);
+
+ _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
+
+ Config->ParameterChanged.connect (mem_fun (*this, &Session::config_changed));
+}
+
+Session::~Session ()
+{
+ destroy ();
+}
+
+void
+Session::destroy ()
+{
+ /* if we got to here, leaving pending capture state around
+ is a mistake.
+ */
+
+ remove_pending_capture_state ();
+
+ _state_of_the_state = StateOfTheState (CannotSave|Deletion);
+
+ _engine.remove_session ();
+
+ GoingAway (); /* EMIT SIGNAL */
+
+ /* do this */
+
+ notify_callbacks ();
+
+ /* clear history so that no references to objects are held any more */
+
+ _history.clear ();
+
+ /* clear state tree so that no references to objects are held any more */
+
+ if (state_tree) {
+ delete state_tree;
+ }
+
+ terminate_butler_thread ();
+ //terminate_midi_thread ();
+
+ if (click_data && click_data != default_click) {
+ delete [] click_data;
+ }
+
+ if (click_emphasis_data && click_emphasis_data != default_click_emphasis) {
+ delete [] click_emphasis_data;
+ }
+
+ clear_clicks ();
+
+ delete _scratch_buffers;
+ delete _silent_buffers;
+ delete _mix_buffers;
+
+ AudioDiskstream::free_working_buffers();
+
+ Route::SyncOrderKeys.clear();
+
+#undef TRACK_DESTRUCTION
+#ifdef TRACK_DESTRUCTION
+ cerr << "delete named selections\n";
+#endif /* TRACK_DESTRUCTION */
+ for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ) {
+ NamedSelectionList::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ delete *i;
+ i = tmp;
+ }
+
+#ifdef TRACK_DESTRUCTION
+ cerr << "delete playlists\n";
+#endif /* TRACK_DESTRUCTION */
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ) {
+ PlaylistList::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ (*i)->drop_references ();
+
+ i = tmp;
+ }
+
+ for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ) {
+ PlaylistList::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ (*i)->drop_references ();
+
+ i = tmp;
+ }
+
+ playlists.clear ();
+ unused_playlists.clear ();
+
+#ifdef TRACK_DESTRUCTION
+ cerr << "delete regions\n";
+#endif /* TRACK_DESTRUCTION */
+
+ for (RegionList::iterator i = regions.begin(); i != regions.end(); ) {
+ RegionList::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ i->second->drop_references ();
+
+ i = tmp;
+ }
+
+ regions.clear ();
+
+#ifdef TRACK_DESTRUCTION
+ cerr << "delete routes\n";
+#endif /* TRACK_DESTRUCTION */
+ {
+ RCUWriter<RouteList> writer (routes);
+ boost::shared_ptr<RouteList> r = writer.get_copy ();
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->drop_references ();
+ }
+ r->clear ();
+ /* writer goes out of scope and updates master */
+ }
+
+ routes.flush ();
+
+#ifdef TRACK_DESTRUCTION
+ cerr << "delete diskstreams\n";
+#endif /* TRACK_DESTRUCTION */
+ {
+ RCUWriter<DiskstreamList> dwriter (diskstreams);
+ boost::shared_ptr<DiskstreamList> dsl = dwriter.get_copy();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->drop_references ();
+ }
+ dsl->clear ();
+ }
+ diskstreams.flush ();
+
+#ifdef TRACK_DESTRUCTION
+ cerr << "delete audio sources\n";
+#endif /* TRACK_DESTRUCTION */
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
+ SourceMap::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ i->second->drop_references ();
+
+ i = tmp;
+ }
+ sources.clear ();
+
+#ifdef TRACK_DESTRUCTION
+ cerr << "delete mix groups\n";
+#endif /* TRACK_DESTRUCTION */
+ for (list<RouteGroup *>::iterator i = mix_groups.begin(); i != mix_groups.end(); ) {
+ list<RouteGroup*>::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ delete *i;
+
+ i = tmp;
+ }
+
+#ifdef TRACK_DESTRUCTION
+ cerr << "delete edit groups\n";
+#endif /* TRACK_DESTRUCTION */
+ for (list<RouteGroup *>::iterator i = edit_groups.begin(); i != edit_groups.end(); ) {
+ list<RouteGroup*>::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ delete *i;
+
+ i = tmp;
+ }
+
+ if (butler_mixdown_buffer) {
+ delete [] butler_mixdown_buffer;
+ }
+
+ if (butler_gain_buffer) {
+ delete [] butler_gain_buffer;
+ }
+
+ Crossfade::set_buffer_size (0);
+
+ if (mmc) {
+ delete mmc;
+ }
+}
+
+void
+Session::set_worst_io_latencies ()
+{
+ _worst_output_latency = 0;
+ _worst_input_latency = 0;
+
+ if (!_engine.connected()) {
+ return;
+ }
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ _worst_output_latency = max (_worst_output_latency, (*i)->output_latency());
+ _worst_input_latency = max (_worst_input_latency, (*i)->input_latency());
+ }
+}
+
+void
+Session::when_engine_running ()
+{
+ string first_physical_output;
+
+ /* we don't want to run execute this again */
+
+ BootMessage (_("Set block size and sample rate"));
+
+ set_block_size (_engine.frames_per_cycle());
+ set_frame_rate (_engine.frame_rate());
+
+ BootMessage (_("Using configuration"));
+
+ Config->map_parameters (mem_fun (*this, &Session::config_changed));
+
+ /* every time we reconnect, recompute worst case output latencies */
+
+ _engine.Running.connect (mem_fun (*this, &Session::set_worst_io_latencies));
+
+ if (synced_to_jack()) {
+ _engine.transport_stop ();
+ }
+
+ if (Config->get_jack_time_master()) {
+ _engine.transport_locate (_transport_frame);
+ }
+
+ _clicking = false;
+
+ try {
+ XMLNode* child = 0;
+
+ _click_io.reset (new ClickIO (*this, "click", 0, 0, -1, -1));
+
+ if (state_tree && (child = find_named_node (*state_tree->root(), "Click")) != 0) {
+
+ /* existing state for Click */
+
+ if (_click_io->set_state (*child->children().front()) == 0) {
+
+ _clicking = Config->get_clicking ();
+
+ } else {
+
+ error << _("could not setup Click I/O") << endmsg;
+ _clicking = false;
+ }
+
+ } else {
+
+ /* default state for Click */
+
+ first_physical_output = _engine.get_nth_physical_output (DataType::AUDIO, 0);
+
+ if (first_physical_output.length()) {
+ if (_click_io->add_output_port (first_physical_output, this)) {
+ // relax, even though its an error
+ } else {
+ _clicking = Config->get_clicking ();
+ }
+ }
+ }
+ }
+
+ catch (failed_constructor& err) {
+ error << _("cannot setup Click I/O") << endmsg;
+ }
+
+ BootMessage (_("Compute I/O Latencies"));
+
+ set_worst_io_latencies ();
+
+ if (_clicking) {
+ // XXX HOW TO ALERT UI TO THIS ? DO WE NEED TO?
+ }
+
+ /* Create a set of Bundle objects that map
+ to the physical outputs currently available
+ */
+
+ BootMessage (_("Set up standard connections"));
+
+ /* ONE: MONO */
+
+ for (uint32_t np = 0; np < n_physical_outputs; ++np) {
+ char buf[32];
+ snprintf (buf, sizeof (buf), _("out %" PRIu32), np+1);
+
+ shared_ptr<AutoBundle> c (new AutoBundle (buf, true));
+ c->set_channels (1);
+ c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+
+ add_bundle (c);
+ }
+
+ for (uint32_t np = 0; np < n_physical_inputs; ++np) {
+ char buf[32];
+ snprintf (buf, sizeof (buf), _("in %" PRIu32), np+1);
+
+ shared_ptr<AutoBundle> c (new AutoBundle (buf, false));
+ c->set_channels (1);
+ c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+
+ add_bundle (c);
+ }
+
+ /* TWO: STEREO */
+
+ for (uint32_t np = 0; np < n_physical_outputs; np +=2) {
+ char buf[32];
+ snprintf (buf, sizeof (buf), _("out %" PRIu32 "+%" PRIu32), np+1, np+2);
+
+ shared_ptr<AutoBundle> c (new AutoBundle (buf, true));
+ c->set_channels (2);
+ c->set_port (0, _engine.get_nth_physical_output (DataType::AUDIO, np));
+ c->set_port (1, _engine.get_nth_physical_output (DataType::AUDIO, np + 1));
+
+ add_bundle (c);
+ }
+
+ for (uint32_t np = 0; np < n_physical_inputs; np +=2) {
+ char buf[32];
+ snprintf (buf, sizeof (buf), _("in %" PRIu32 "+%" PRIu32), np+1, np+2);
+
+ shared_ptr<AutoBundle> c (new AutoBundle (buf, false));
+ c->set_channels (2);
+ c->set_port (0, _engine.get_nth_physical_input (DataType::AUDIO, np));
+ c->set_port (1, _engine.get_nth_physical_input (DataType::AUDIO, np + 1));
+
+ add_bundle (c);
+ }
+
+ /* THREE MASTER */
+
+ if (_master_out) {
+
+ /* create master/control ports */
+
+ if (_master_out) {
+ uint32_t n;
+
+ /* force the master to ignore any later call to this */
+
+ if (_master_out->pending_state_node) {
+ _master_out->ports_became_legal();
+ }
+
+ /* no panner resets till we are through */
+
+ _master_out->defer_pan_reset ();
+
+ while (_master_out->n_inputs().n_audio()
+ < _master_out->input_maximum().n_audio()) {
+ if (_master_out->add_input_port ("", this, DataType::AUDIO)) {
+ error << _("cannot setup master inputs")
+ << endmsg;
+ break;
+ }
+ }
+ n = 0;
+ while (_master_out->n_outputs().n_audio()
+ < _master_out->output_maximum().n_audio()) {
+ if (_master_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this, DataType::AUDIO)) {
+ error << _("cannot setup master outputs")
+ << endmsg;
+ break;
+ }
+ n++;
+ }
+
+ _master_out->allow_pan_reset ();
+
+ }
+
+ shared_ptr<AutoBundle> c (new AutoBundle (_("Master Out"), true));
+
+ c->set_channels (_master_out->n_inputs().n_total());
+ for (uint32_t n = 0; n < _master_out->n_inputs ().n_total(); ++n) {
+ c->set_port (n, _master_out->input(n)->name());
+ }
+ add_bundle (c);
+ }
+
+ BootMessage (_("Setup signal flow and plugins"));
+
+ hookup_io ();
+
+ /* catch up on send+insert cnts */
+
+ BootMessage (_("Catch up with send/insert state"));
+
+ insert_cnt = 0;
+
+ for (list<PortInsert*>::iterator i = _port_inserts.begin(); i != _port_inserts.end(); ++i) {
+ uint32_t id;
+
+ if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) {
+ if (id > insert_cnt) {
+ insert_cnt = id;
+ }
+ }
+ }
+
+ send_cnt = 0;
+
+ for (list<Send*>::iterator i = _sends.begin(); i != _sends.end(); ++i) {
+ uint32_t id;
+
+ if (sscanf ((*i)->name().c_str(), "%*s %u", &id) == 1) {
+ if (id > send_cnt) {
+ send_cnt = id;
+ }
+ }
+ }
+
+
+ _state_of_the_state = StateOfTheState (_state_of_the_state & ~(CannotSave|Dirty));
+
+ /* hook us up to the engine */
+
+ BootMessage (_("Connect to engine"));
+
+ _engine.set_session (this);
+
+#ifdef HAVE_LIBLO
+ /* and to OSC */
+
+ BootMessage (_("OSC startup"));
+
+ osc->set_session (*this);
+#endif
+
+}
+
+void
+Session::hookup_io ()
+{
+ /* stop graph reordering notifications from
+ causing resorts, etc.
+ */
+
+ _state_of_the_state = StateOfTheState (_state_of_the_state | InitialConnecting);
+
+
+ if (auditioner == 0) {
+
+ /* we delay creating the auditioner till now because
+ it makes its own connections to ports.
+ the engine has to be running for this to work.
+ */
+
+ try {
+ auditioner.reset (new Auditioner (*this));
+ }
+
+ catch (failed_constructor& err) {
+ warning << _("cannot create Auditioner: no auditioning of regions possible") << endmsg;
+ }
+ }
+
+ /* Tell all IO objects to create their ports */
+
+ IO::enable_ports ();
+
+ if (_control_out) {
+ uint32_t n;
+ vector<string> cports;
+
+ while (_control_out->n_inputs().n_audio() < _control_out->input_maximum().n_audio()) {
+ if (_control_out->add_input_port ("", this)) {
+ error << _("cannot setup control inputs")
+ << endmsg;
+ break;
+ }
+ }
+ n = 0;
+ while (_control_out->n_outputs().n_audio() < _control_out->output_maximum().n_audio()) {
+ if (_control_out->add_output_port (_engine.get_nth_physical_output (DataType::AUDIO, n), this)) {
+ error << _("cannot set up master outputs")
+ << endmsg;
+ break;
+ }
+ n++;
+ }
+
+
+ uint32_t ni = _control_out->n_inputs().get (DataType::AUDIO);
+
+ for (n = 0; n < ni; ++n) {
+ cports.push_back (_control_out->input(n)->name());
+ }
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator x = r->begin(); x != r->end(); ++x) {
+ (*x)->set_control_outs (cports);
+ }
+ }
+
+ /* load bundles, which we may have postponed earlier on */
+ if (_bundle_xml_node) {
+ load_bundles (*_bundle_xml_node);
+ delete _bundle_xml_node;
+ }
+
+ /* Tell all IO objects to connect themselves together */
+
+ IO::enable_connecting ();
+
+ /* Now reset all panners */
+
+ IO::reset_panners ();
+
+ /* Anyone who cares about input state, wake up and do something */
+
+ IOConnectionsComplete (); /* EMIT SIGNAL */
+
+ _state_of_the_state = StateOfTheState (_state_of_the_state & ~InitialConnecting);
+
+
+ /* now handle the whole enchilada as if it was one
+ graph reorder event.
+ */
+
+ graph_reordered ();
+
+ /* update mixer solo state */
+
+ catch_up_on_solo();
+}
+
+void
+Session::playlist_length_changed ()
+{
+ /* we can't just increase end_location->end() if pl->get_maximum_extent()
+ if larger. if the playlist used to be the longest playlist,
+ and its now shorter, we have to decrease end_location->end(). hence,
+ we have to iterate over all diskstreams and check the
+ playlists currently in use.
+ */
+ find_current_end ();
+}
+
+void
+Session::diskstream_playlist_changed (boost::shared_ptr<Diskstream> dstream)
+{
+ boost::shared_ptr<Playlist> playlist;
+
+ if ((playlist = dstream->playlist()) != 0) {
+ playlist->LengthChanged.connect (mem_fun (this, &Session::playlist_length_changed));
+ }
+
+ /* see comment in playlist_length_changed () */
+ find_current_end ();
+}
+
+bool
+Session::record_enabling_legal () const
+{
+ /* this used to be in here, but survey says.... we don't need to restrict it */
+ // if (record_status() == Recording) {
+ // return false;
+ // }
+
+ if (Config->get_all_safe()) {
+ return false;
+ }
+ return true;
+}
+
+void
+Session::reset_input_monitor_state ()
+{
+ if (transport_rolling()) {
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching to input = " << !auto_input << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring && !Config->get_auto_input());
+ }
+ }
+ } else {
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching to input = " << !Config->get_auto_input() << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (Config->get_monitoring_model() == HardwareMonitoring);
+ }
+ }
+ }
+}
+
+void
+Session::auto_punch_start_changed (Location* location)
+{
+ replace_event (Event::PunchIn, location->start());
+
+ if (get_record_enabled() && Config->get_punch_in()) {
+ /* capture start has been changed, so save new pending state */
+ save_state ("", true);
+ }
+}
+
+void
+Session::auto_punch_end_changed (Location* location)
+{
+ nframes_t when_to_stop = location->end();
+ // when_to_stop += _worst_output_latency + _worst_input_latency;
+ replace_event (Event::PunchOut, when_to_stop);
+}
+
+void
+Session::auto_punch_changed (Location* location)
+{
+ nframes_t when_to_stop = location->end();
+
+ replace_event (Event::PunchIn, location->start());
+ //when_to_stop += _worst_output_latency + _worst_input_latency;
+ replace_event (Event::PunchOut, when_to_stop);
+}
+
+void
+Session::auto_loop_changed (Location* location)
+{
+ replace_event (Event::AutoLoop, location->end(), location->start());
+
+ if (transport_rolling() && play_loop) {
+
+ //if (_transport_frame < location->start() || _transport_frame > location->end()) {
+
+ if (_transport_frame > location->end()) {
+ // relocate to beginning of loop
+ clear_events (Event::LocateRoll);
+
+ request_locate (location->start(), true);
+
+ }
+ else if (Config->get_seamless_loop() && !loop_changing) {
+
+ // schedule a locate-roll to refill the diskstreams at the
+ // previous loop end
+ loop_changing = true;
+
+ if (location->end() > last_loopend) {
+ clear_events (Event::LocateRoll);
+ Event *ev = new Event (Event::LocateRoll, Event::Add, last_loopend, last_loopend, 0, true);
+ queue_event (ev);
+ }
+
+ }
+ }
+
+ last_loopend = location->end();
+}
+
+void
+Session::set_auto_punch_location (Location* location)
+{
+ Location* existing;
+
+ if ((existing = _locations.auto_punch_location()) != 0 && existing != location) {
+ auto_punch_start_changed_connection.disconnect();
+ auto_punch_end_changed_connection.disconnect();
+ auto_punch_changed_connection.disconnect();
+ existing->set_auto_punch (false, this);
+ remove_event (existing->start(), Event::PunchIn);
+ clear_events (Event::PunchOut);
+ auto_punch_location_changed (0);
+ }
+
+ set_dirty();
+
+ if (location == 0) {
+ return;
+ }
+
+ if (location->end() <= location->start()) {
+ error << _("Session: you can't use that location for auto punch (start <= end)") << endmsg;
+ return;
+ }
+
+ auto_punch_start_changed_connection.disconnect();
+ auto_punch_end_changed_connection.disconnect();
+ auto_punch_changed_connection.disconnect();
+
+ auto_punch_start_changed_connection = location->start_changed.connect (mem_fun (this, &Session::auto_punch_start_changed));
+ auto_punch_end_changed_connection = location->end_changed.connect (mem_fun (this, &Session::auto_punch_end_changed));
+ auto_punch_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_punch_changed));
+
+ location->set_auto_punch (true, this);
+
+
+ auto_punch_changed (location);
+
+ auto_punch_location_changed (location);
+}
+
+void
+Session::set_auto_loop_location (Location* location)
+{
+ Location* existing;
+
+ if ((existing = _locations.auto_loop_location()) != 0 && existing != location) {
+ auto_loop_start_changed_connection.disconnect();
+ auto_loop_end_changed_connection.disconnect();
+ auto_loop_changed_connection.disconnect();
+ existing->set_auto_loop (false, this);
+ remove_event (existing->end(), Event::AutoLoop);
+ auto_loop_location_changed (0);
+ }
+
+ set_dirty();
+
+ if (location == 0) {
+ return;
+ }
+
+ if (location->end() <= location->start()) {
+ error << _("Session: you can't use a mark for auto loop") << endmsg;
+ return;
+ }
+
+ last_loopend = location->end();
+
+ auto_loop_start_changed_connection.disconnect();
+ auto_loop_end_changed_connection.disconnect();
+ auto_loop_changed_connection.disconnect();
+
+ auto_loop_start_changed_connection = location->start_changed.connect (mem_fun (this, &Session::auto_loop_changed));
+ auto_loop_end_changed_connection = location->end_changed.connect (mem_fun (this, &Session::auto_loop_changed));
+ auto_loop_changed_connection = location->changed.connect (mem_fun (this, &Session::auto_loop_changed));
+
+ location->set_auto_loop (true, this);
+
+ /* take care of our stuff first */
+
+ auto_loop_changed (location);
+
+ /* now tell everyone else */
+
+ auto_loop_location_changed (location);
+}
+
+void
+Session::locations_added (Location* ignored)
+{
+ set_dirty ();
+}
+
+void
+Session::locations_changed ()
+{
+ _locations.apply (*this, &Session::handle_locations_changed);
+}
+
+void
+Session::handle_locations_changed (Locations::LocationList& locations)
+{
+ Locations::LocationList::iterator i;
+ Location* location;
+ bool set_loop = false;
+ bool set_punch = false;
+
+ for (i = locations.begin(); i != locations.end(); ++i) {
+
+ location =* i;
+
+ if (location->is_auto_punch()) {
+ set_auto_punch_location (location);
+ set_punch = true;
+ }
+ if (location->is_auto_loop()) {
+ set_auto_loop_location (location);
+ set_loop = true;
+ }
+
+ }
+
+ if (!set_loop) {
+ set_auto_loop_location (0);
+ }
+ if (!set_punch) {
+ set_auto_punch_location (0);
+ }
+
+ set_dirty();
+}
+
+void
+Session::enable_record ()
+{
+ /* XXX really atomic compare+swap here */
+ if (g_atomic_int_get (&_record_status) != Recording) {
+ g_atomic_int_set (&_record_status, Recording);
+ _last_record_location = _transport_frame;
+ deliver_mmc(MIDI::MachineControl::cmdRecordStrobe, _last_record_location);
+
+ if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) {
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ (*i)->monitor_input (true);
+ }
+ }
+ }
+
+ RecordStateChanged ();
+ }
+}
+
+void
+Session::disable_record (bool rt_context, bool force)
+{
+ RecordState rs;
+
+ if ((rs = (RecordState) g_atomic_int_get (&_record_status)) != Disabled) {
+
+ if ((!Config->get_latched_record_enable () && !play_loop) || force) {
+ g_atomic_int_set (&_record_status, Disabled);
+ } else {
+ if (rs == Recording) {
+ g_atomic_int_set (&_record_status, Enabled);
+ }
+ }
+
+ // FIXME: timestamp correct? [DR]
+ // FIXME FIXME FIXME: rt_context? this must be called in the process thread.
+ // does this /need/ to be sent in all cases?
+ if (rt_context)
+ deliver_mmc (MIDI::MachineControl::cmdRecordExit, _transport_frame);
+
+ if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) {
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ (*i)->monitor_input (false);
+ }
+ }
+ }
+
+ RecordStateChanged (); /* emit signal */
+
+ if (!rt_context) {
+ remove_pending_capture_state ();
+ }
+ }
+}
+
+void
+Session::step_back_from_record ()
+{
+ /* XXX really atomic compare+swap here */
+ if (g_atomic_int_get (&_record_status) == Recording) {
+ g_atomic_int_set (&_record_status, Enabled);
+
+ if (Config->get_monitoring_model() == HardwareMonitoring && Config->get_auto_input()) {
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (false);
+ }
+ }
+ }
+ }
+}
+
+void
+Session::maybe_enable_record ()
+{
+ g_atomic_int_set (&_record_status, Enabled);
+
+ /* this function is currently called from somewhere other than an RT thread.
+ this save_state() call therefore doesn't impact anything.
+ */
+
+ save_state ("", true);
+
+ if (_transport_speed) {
+ if (!Config->get_punch_in()) {
+ enable_record ();
+ }
+ } else {
+ deliver_mmc (MIDI::MachineControl::cmdRecordPause, _transport_frame);
+ RecordStateChanged (); /* EMIT SIGNAL */
+ }
+
+ set_dirty();
+}
+
+nframes_t
+Session::audible_frame () const
+{
+ nframes_t ret;
+ nframes_t offset;
+ nframes_t tf;
+
+ /* the first of these two possible settings for "offset"
+ mean that the audible frame is stationary until
+ audio emerges from the latency compensation
+ "pseudo-pipeline".
+
+ the second means that the audible frame is stationary
+ until audio would emerge from a physical port
+ in the absence of any plugin latency compensation
+ */
+
+ offset = _worst_output_latency;
+
+ if (offset > current_block_size) {
+ offset -= current_block_size;
+ } else {
+ /* XXX is this correct? if we have no external
+ physical connections and everything is internal
+ then surely this is zero? still, how
+ likely is that anyway?
+ */
+ offset = current_block_size;
+ }
+
+ if (synced_to_jack()) {
+ tf = _engine.transport_frame();
+ } else {
+ tf = _transport_frame;
+ }
+
+ if (_transport_speed == 0) {
+ return tf;
+ }
+
+ if (tf < offset) {
+ return 0;
+ }
+
+ ret = tf;
+
+ if (!non_realtime_work_pending()) {
+
+ /* MOVING */
+
+ /* take latency into account */
+
+ ret -= offset;
+ }
+
+ return ret;
+}
+
+void
+Session::set_frame_rate (nframes_t frames_per_second)
+{
+ /** \fn void Session::set_frame_size(nframes_t)
+ the AudioEngine object that calls this guarantees
+ that it will not be called while we are also in
+ ::process(). Its fine to do things that block
+ here.
+ */
+
+ _base_frame_rate = frames_per_second;
+
+ sync_time_vars();
+
+ Automatable::set_automation_interval ((jack_nframes_t) ceil ((double) frames_per_second * (0.001 * Config->get_automation_interval())));
+
+ clear_clicks ();
+
+ // XXX we need some equivalent to this, somehow
+ // SndFileSource::setup_standard_crossfades (frames_per_second);
+
+ set_dirty();
+
+ /* XXX need to reset/reinstantiate all LADSPA plugins */
+}
+
+void
+Session::set_block_size (nframes_t nframes)
+{
+ /* the AudioEngine guarantees
+ that it will not be called while we are also in
+ ::process(). It is therefore fine to do things that block
+ here.
+ */
+
+ {
+
+ current_block_size = nframes;
+
+ ensure_buffers(_scratch_buffers->available());
+
+ if (_gain_automation_buffer) {
+ delete [] _gain_automation_buffer;
+ }
+ _gain_automation_buffer = new gain_t[nframes];
+
+ allocate_pan_automation_buffers (nframes, _npan_buffers, true);
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->set_block_size (nframes);
+ }
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->set_block_size (nframes);
+ }
+
+ set_worst_io_latencies ();
+ }
+}
+
+void
+Session::set_default_fade (float steepness, float fade_msecs)
+{
+#if 0
+ nframes_t fade_frames;
+
+ /* Don't allow fade of less 1 frame */
+
+ if (fade_msecs < (1000.0 * (1.0/_current_frame_rate))) {
+
+ fade_msecs = 0;
+ fade_frames = 0;
+
+ } else {
+
+ fade_frames = (nframes_t) floor (fade_msecs * _current_frame_rate * 0.001);
+
+ }
+
+ default_fade_msecs = fade_msecs;
+ default_fade_steepness = steepness;
+
+ {
+ // jlc, WTF is this!
+ Glib::RWLock::ReaderLock lm (route_lock);
+ AudioRegion::set_default_fade (steepness, fade_frames);
+ }
+
+ set_dirty();
+
+ /* XXX have to do this at some point */
+ /* foreach region using default fade, reset, then
+ refill_all_diskstream_buffers ();
+ */
+#endif
+}
+
+struct RouteSorter {
+ bool operator() (boost::shared_ptr<Route> r1, boost::shared_ptr<Route> r2) {
+ if (r1->fed_by.find (r2) != r1->fed_by.end()) {
+ return false;
+ } else if (r2->fed_by.find (r1) != r2->fed_by.end()) {
+ return true;
+ } else {
+ if (r1->fed_by.empty()) {
+ if (r2->fed_by.empty()) {
+ /* no ardour-based connections inbound to either route. just use signal order */
+ return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
+ } else {
+ /* r2 has connections, r1 does not; run r1 early */
+ return true;
+ }
+ } else {
+ return r1->order_key(N_("signal")) < r2->order_key(N_("signal"));
+ }
+ }
+ }
+};
+
+static void
+trace_terminal (shared_ptr<Route> r1, shared_ptr<Route> rbase)
+{
+ shared_ptr<Route> r2;
+
+ if ((r1->fed_by.find (rbase) != r1->fed_by.end()) && (rbase->fed_by.find (r1) != rbase->fed_by.end())) {
+ info << string_compose(_("feedback loop setup between %1 and %2"), r1->name(), rbase->name()) << endmsg;
+ return;
+ }
+
+ /* make a copy of the existing list of routes that feed r1 */
+
+ set<shared_ptr<Route> > existing = r1->fed_by;
+
+ /* for each route that feeds r1, recurse, marking it as feeding
+ rbase as well.
+ */
+
+ for (set<shared_ptr<Route> >::iterator i = existing.begin(); i != existing.end(); ++i) {
+ r2 =* i;
+
+ /* r2 is a route that feeds r1 which somehow feeds base. mark
+ base as being fed by r2
+ */
+
+ rbase->fed_by.insert (r2);
+
+ if (r2 != rbase) {
+
+ /* 2nd level feedback loop detection. if r1 feeds or is fed by r2,
+ stop here.
+ */
+
+ if ((r1->fed_by.find (r2) != r1->fed_by.end()) && (r2->fed_by.find (r1) != r2->fed_by.end())) {
+ continue;
+ }
+
+ /* now recurse, so that we can mark base as being fed by
+ all routes that feed r2
+ */
+
+ trace_terminal (r2, rbase);
+ }
+
+ }
+}
+
+void
+Session::resort_routes ()
+{
+ /* don't do anything here with signals emitted
+ by Routes while we are being destroyed.
+ */
+
+ if (_state_of_the_state & Deletion) {
+ return;
+ }
+
+
+ {
+
+ RCUWriter<RouteList> writer (routes);
+ shared_ptr<RouteList> r = writer.get_copy ();
+ resort_routes_using (r);
+ /* writer goes out of scope and forces update */
+ }
+
+}
+void
+Session::resort_routes_using (shared_ptr<RouteList> r)
+{
+ RouteList::iterator i, j;
+
+ for (i = r->begin(); i != r->end(); ++i) {
+
+ (*i)->fed_by.clear ();
+
+ for (j = r->begin(); j != r->end(); ++j) {
+
+ /* although routes can feed themselves, it will
+ cause an endless recursive descent if we
+ detect it. so don't bother checking for
+ self-feeding.
+ */
+
+ if (*j == *i) {
+ continue;
+ }
+
+ if ((*j)->feeds (*i)) {
+ (*i)->fed_by.insert (*j);
+ }
+ }
+ }
+
+ for (i = r->begin(); i != r->end(); ++i) {
+ trace_terminal (*i, *i);
+ }
+
+ RouteSorter cmp;
+ r->sort (cmp);
+
+#if 0
+ cerr << "finished route resort\n";
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ cerr << " " << (*i)->name() << " signal order = " << (*i)->order_key ("signal") << endl;
+ }
+ cerr << endl;
+#endif
+
+}
+
+list<boost::shared_ptr<MidiTrack> >
+Session::new_midi_track (TrackMode mode, uint32_t how_many)
+{
+ char track_name[32];
+ uint32_t track_id = 0;
+ uint32_t n = 0;
+ string port;
+ RouteList new_routes;
+ list<boost::shared_ptr<MidiTrack> > ret;
+ //uint32_t control_id;
+
+ // FIXME: need physical I/O and autoconnect stuff for MIDI
+
+ /* count existing midi tracks */
+
+ {
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (dynamic_cast<MidiTrack*>((*i).get()) != 0) {
+ if (!(*i)->is_hidden()) {
+ n++;
+ //channels_used += (*i)->n_inputs().n_midi();
+ }
+ }
+ }
+ }
+
+ /*
+ vector<string> physinputs;
+ vector<string> physoutputs;
+ uint32_t nphysical_in;
+ uint32_t nphysical_out;
+
+ _engine.get_physical_outputs (physoutputs);
+ _engine.get_physical_inputs (physinputs);
+ control_id = ntracks() + nbusses() + 1;
+ */
+
+ while (how_many) {
+
+ /* check for duplicate route names, since we might have pre-existing
+ routes with this name (e.g. create Audio1, Audio2, delete Audio1,
+ save, close,restart,add new route - first named route is now
+ Audio2)
+ */
+
+
+ do {
+ ++track_id;
+
+ snprintf (track_name, sizeof(track_name), "Midi %" PRIu32, track_id);
+
+ if (route_by_name (track_name) == 0) {
+ break;
+ }
+
+ } while (track_id < (UINT_MAX-1));
+
+ /*
+ if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+ nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size());
+ } else {
+ nphysical_in = 0;
+ }
+
+ if (Config->get_output_auto_connect() & AutoConnectPhysical) {
+ nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size());
+ } else {
+ nphysical_out = 0;
+ }
+ */
+
+ shared_ptr<MidiTrack> track;
+
+ try {
+ track = boost::shared_ptr<MidiTrack>((new MidiTrack (*this, track_name, Route::Flag (0), mode)));
+
+ if (track->ensure_io (ChanCount(DataType::MIDI, 1), ChanCount(DataType::AUDIO, 1), false, this)) {
+ error << "cannot configure 1 in/1 out configuration for new midi track" << endmsg;
+ goto failed;
+ }
+
+ /*
+ if (nphysical_in) {
+ for (uint32_t x = 0; x < track->n_inputs().n_midi() && x < nphysical_in; ++x) {
+
+ port = "";
+
+ if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+ port = physinputs[(channels_used+x)%nphysical_in];
+ }
+
+ if (port.length() && track->connect_input (track->input (x), port, this)) {
+ break;
+ }
+ }
+ }
+
+ for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) {
+
+ port = "";
+
+ if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
+ port = physoutputs[(channels_used+x)%nphysical_out];
+ } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
+ if (_master_out) {
+ port = _master_out->input (x%_master_out->n_inputs().n_midi())->name();
+ }
+ }
+
+ if (port.length() && track->connect_output (track->output (x), port, this)) {
+ break;
+ }
+ }
+
+ channels_used += track->n_inputs ().n_midi();
+
+ */
+
+ track->midi_diskstream()->non_realtime_input_change();
+
+ track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes));
+ //track->set_remote_control_id (control_id);
+
+ new_routes.push_back (track);
+ ret.push_back (track);
+ }
+
+ catch (failed_constructor &err) {
+ error << _("Session: could not create new midi track.") << endmsg;
+
+ if (track) {
+ /* we need to get rid of this, since the track failed to be created */
+ /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
+
+ {
+ RCUWriter<DiskstreamList> writer (diskstreams);
+ boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
+ ds->remove (track->midi_diskstream());
+ }
+ }
+
+ goto failed;
+ }
+
+ catch (AudioEngine::PortRegistrationFailure& pfe) {
+
+ error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg;
+
+ if (track) {
+ /* we need to get rid of this, since the track failed to be created */
+ /* XXX arguably, MidiTrack::MidiTrack should not do the Session::add_diskstream() */
+
+ {
+ RCUWriter<DiskstreamList> writer (diskstreams);
+ boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
+ ds->remove (track->midi_diskstream());
+ }
+ }
+
+ goto failed;
+ }
+
+ --how_many;
+ }
+
+ failed:
+ if (!new_routes.empty()) {
+ add_routes (new_routes, false);
+ save_state (_current_snapshot_name);
+ }
+
+ return ret;
+}
+
+list<boost::shared_ptr<AudioTrack> >
+Session::new_audio_track (int input_channels, int output_channels, TrackMode mode, uint32_t how_many)
+{
+ char track_name[32];
+ uint32_t track_id = 0;
+ uint32_t n = 0;
+ uint32_t channels_used = 0;
+ string port;
+ RouteList new_routes;
+ list<boost::shared_ptr<AudioTrack> > ret;
+ uint32_t control_id;
+
+ /* count existing audio tracks */
+
+ {
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (dynamic_cast<AudioTrack*>((*i).get()) != 0) {
+ if (!(*i)->is_hidden()) {
+ n++;
+ channels_used += (*i)->n_inputs().n_audio();
+ }
+ }
+ }
+ }
+
+ vector<string> physinputs;
+ vector<string> physoutputs;
+ uint32_t nphysical_in;
+ uint32_t nphysical_out;
+
+ _engine.get_physical_outputs (physoutputs);
+ _engine.get_physical_inputs (physinputs);
+ control_id = ntracks() + nbusses() + 1;
+
+ while (how_many) {
+
+ /* check for duplicate route names, since we might have pre-existing
+ routes with this name (e.g. create Audio1, Audio2, delete Audio1,
+ save, close,restart,add new route - first named route is now
+ Audio2)
+ */
+
+
+ do {
+ ++track_id;
+
+ snprintf (track_name, sizeof(track_name), "Audio %" PRIu32, track_id);
+
+ if (route_by_name (track_name) == 0) {
+ break;
+ }
+
+ } while (track_id < (UINT_MAX-1));
+
+ if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+ nphysical_in = min (n_physical_inputs, (uint32_t) physinputs.size());
+ } else {
+ nphysical_in = 0;
+ }
+
+ if (Config->get_output_auto_connect() & AutoConnectPhysical) {
+ nphysical_out = min (n_physical_outputs, (uint32_t) physinputs.size());
+ } else {
+ nphysical_out = 0;
+ }
+
+ shared_ptr<AudioTrack> track;
+
+ try {
+ track = boost::shared_ptr<AudioTrack>((new AudioTrack (*this, track_name, Route::Flag (0), mode)));
+
+ if (track->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) {
+ error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
+ input_channels, output_channels)
+ << endmsg;
+ goto failed;
+ }
+
+ if (nphysical_in) {
+ for (uint32_t x = 0; x < track->n_inputs().n_audio() && x < nphysical_in; ++x) {
+
+ port = "";
+
+ if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+ port = physinputs[(channels_used+x)%nphysical_in];
+ }
+
+ if (port.length() && track->connect_input (track->input (x), port, this)) {
+ break;
+ }
+ }
+ }
+
+ for (uint32_t x = 0; x < track->n_outputs().n_midi(); ++x) {
+
+ port = "";
+
+ if (nphysical_out && (Config->get_output_auto_connect() & AutoConnectPhysical)) {
+ port = physoutputs[(channels_used+x)%nphysical_out];
+ } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
+ if (_master_out) {
+ port = _master_out->input (x%_master_out->n_inputs().n_audio())->name();
+ }
+ }
+
+ if (port.length() && track->connect_output (track->output (x), port, this)) {
+ break;
+ }
+ }
+
+ channels_used += track->n_inputs ().n_audio();
+
+ track->audio_diskstream()->non_realtime_input_change();
+
+ track->DiskstreamChanged.connect (mem_fun (this, &Session::resort_routes));
+ track->set_remote_control_id (control_id);
+ ++control_id;
+
+ new_routes.push_back (track);
+ ret.push_back (track);
+ }
+
+ catch (failed_constructor &err) {
+ error << _("Session: could not create new audio track.") << endmsg;
+
+ if (track) {
+ /* we need to get rid of this, since the track failed to be created */
+ /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
+
+ {
+ RCUWriter<DiskstreamList> writer (diskstreams);
+ boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
+ ds->remove (track->audio_diskstream());
+ }
+ }
+
+ goto failed;
+ }
+
+ catch (AudioEngine::PortRegistrationFailure& pfe) {
+
+ error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg;
+
+ if (track) {
+ /* we need to get rid of this, since the track failed to be created */
+ /* XXX arguably, AudioTrack::AudioTrack should not do the Session::add_diskstream() */
+
+ {
+ RCUWriter<DiskstreamList> writer (diskstreams);
+ boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
+ ds->remove (track->audio_diskstream());
+ }
+ }
+
+ goto failed;
+ }
+
+ --how_many;
+ }
+
+ failed:
+ if (!new_routes.empty()) {
+ add_routes (new_routes, true);
+ }
+
+ return ret;
+}
+
+void
+Session::set_remote_control_ids ()
+{
+ RemoteModel m = Config->get_remote_model();
+
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ( MixerOrdered == m) {
+ long order = (*i)->order_key(N_("signal"));
+ (*i)->set_remote_control_id( order+1 );
+ } else if ( EditorOrdered == m) {
+ long order = (*i)->order_key(N_("editor"));
+ (*i)->set_remote_control_id( order+1 );
+ } else if ( UserOrdered == m) {
+ //do nothing ... only changes to remote id's are initiated by user
+ }
+ }
+}
+
+
+Session::RouteList
+Session::new_audio_route (int input_channels, int output_channels, uint32_t how_many)
+{
+ char bus_name[32];
+ uint32_t bus_id = 1;
+ uint32_t n = 0;
+ string port;
+ RouteList ret;
+ uint32_t control_id;
+
+ /* count existing audio busses */
+
+ {
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (dynamic_cast<AudioTrack*>((*i).get()) == 0) {
+ if (!(*i)->is_hidden() && (*i)->name() != _("master")) {
+ bus_id++;
+ }
+ }
+ }
+ }
+
+ vector<string> physinputs;
+ vector<string> physoutputs;
+
+ _engine.get_physical_outputs (physoutputs);
+ _engine.get_physical_inputs (physinputs);
+ control_id = ntracks() + nbusses() + 1;
+
+ while (how_many) {
+
+ do {
+ snprintf (bus_name, sizeof(bus_name), "Bus %" PRIu32, bus_id);
+
+ bus_id++;
+
+ if (route_by_name (bus_name) == 0) {
+ break;
+ }
+
+ } while (bus_id < (UINT_MAX-1));
+
+ try {
+ shared_ptr<Route> bus (new Route (*this, bus_name, -1, -1, -1, -1, Route::Flag(0), DataType::AUDIO));
+
+ if (bus->ensure_io (ChanCount(DataType::AUDIO, input_channels), ChanCount(DataType::AUDIO, output_channels), false, this)) {
+ error << string_compose (_("cannot configure %1 in/%2 out configuration for new audio track"),
+ input_channels, output_channels)
+ << endmsg;
+ goto failure;
+ }
+
+ for (uint32_t x = 0; n_physical_inputs && x < bus->n_inputs().n_audio(); ++x) {
+
+ port = "";
+
+ if (Config->get_input_auto_connect() & AutoConnectPhysical) {
+ port = physinputs[((n+x)%n_physical_inputs)];
+ }
+
+ if (port.length() && bus->connect_input (bus->input (x), port, this)) {
+ break;
+ }
+ }
+
+ for (uint32_t x = 0; n_physical_outputs && x < bus->n_outputs().n_audio(); ++x) {
+
+ port = "";
+
+ if (Config->get_output_auto_connect() & AutoConnectPhysical) {
+ port = physoutputs[((n+x)%n_physical_outputs)];
+ } else if (Config->get_output_auto_connect() & AutoConnectMaster) {
+ if (_master_out) {
+ port = _master_out->input (x%_master_out->n_inputs().n_audio())->name();
+ }
+ }
+
+ if (port.length() && bus->connect_output (bus->output (x), port, this)) {
+ break;
+ }
+ }
+
+ bus->set_remote_control_id (control_id);
+ ++control_id;
+
+ ret.push_back (bus);
+ }
+
+
+ catch (failed_constructor &err) {
+ error << _("Session: could not create new audio route.") << endmsg;
+ goto failure;
+ }
+
+ catch (AudioEngine::PortRegistrationFailure& pfe) {
+ error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg;
+ goto failure;
+ }
+
+
+ --how_many;
+ }
+
+ failure:
+ if (!ret.empty()) {
+ add_routes (ret, true);
+ }
+
+ return ret;
+
+}
+
+void
+Session::add_routes (RouteList& new_routes, bool save)
+{
+ {
+ RCUWriter<RouteList> writer (routes);
+ shared_ptr<RouteList> r = writer.get_copy ();
+ r->insert (r->end(), new_routes.begin(), new_routes.end());
+ resort_routes_using (r);
+ }
+
+ for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) {
+
+ boost::weak_ptr<Route> wpr (*x);
+
+ (*x)->solo_changed.connect (sigc::bind (mem_fun (*this, &Session::route_solo_changed), wpr));
+ (*x)->mute_changed.connect (mem_fun (*this, &Session::route_mute_changed));
+ (*x)->output_changed.connect (mem_fun (*this, &Session::set_worst_io_latencies_x));
+ (*x)->processors_changed.connect (bind (mem_fun (*this, &Session::update_latency_compensation), false, false));
+
+ if ((*x)->is_master()) {
+ _master_out = (*x);
+ }
+
+ if ((*x)->is_control()) {
+ _control_out = (*x);
+ }
+
+ add_bundle ((*x)->bundle_for_inputs());
+ add_bundle ((*x)->bundle_for_outputs());
+ }
+
+ if (_control_out && IO::connecting_legal) {
+
+ vector<string> cports;
+ uint32_t ni = _control_out->n_inputs().n_audio();
+
+ for (uint32_t n = 0; n < ni; ++n) {
+ cports.push_back (_control_out->input(n)->name());
+ }
+
+ for (RouteList::iterator x = new_routes.begin(); x != new_routes.end(); ++x) {
+ (*x)->set_control_outs (cports);
+ }
+ }
+
+ set_dirty();
+
+ if (save) {
+ save_state (_current_snapshot_name);
+ }
+
+ RouteAdded (new_routes); /* EMIT SIGNAL */
+}
+
+void
+Session::add_diskstream (boost::shared_ptr<Diskstream> dstream)
+{
+ /* need to do this in case we're rolling at the time, to prevent false underruns */
+ dstream->do_refill_with_alloc ();
+
+ dstream->set_block_size (current_block_size);
+
+ {
+ RCUWriter<DiskstreamList> writer (diskstreams);
+ boost::shared_ptr<DiskstreamList> ds = writer.get_copy();
+ ds->push_back (dstream);
+ /* writer goes out of scope, copies ds back to main */
+ }
+
+ dstream->PlaylistChanged.connect (sigc::bind (mem_fun (*this, &Session::diskstream_playlist_changed), dstream));
+ /* this will connect to future changes, and check the current length */
+ diskstream_playlist_changed (dstream);
+
+ dstream->prepare ();
+
+}
+
+void
+Session::remove_route (shared_ptr<Route> route)
+{
+ {
+ RCUWriter<RouteList> writer (routes);
+ shared_ptr<RouteList> rs = writer.get_copy ();
+
+ rs->remove (route);
+
+ /* deleting the master out seems like a dumb
+ idea, but its more of a UI policy issue
+ than our concern.
+ */
+
+ if (route == _master_out) {
+ _master_out = shared_ptr<Route> ();
+ }
+
+ if (route == _control_out) {
+ _control_out = shared_ptr<Route> ();
+
+ /* cancel control outs for all routes */
+
+ vector<string> empty;
+
+ for (RouteList::iterator r = rs->begin(); r != rs->end(); ++r) {
+ (*r)->set_control_outs (empty);
+ }
+ }
+
+ update_route_solo_state ();
+
+ /* writer goes out of scope, forces route list update */
+ }
+
+ Track* t;
+ boost::shared_ptr<Diskstream> ds;
+
+ if ((t = dynamic_cast<Track*>(route.get())) != 0) {
+ ds = t->diskstream();
+ }
+
+ if (ds) {
+
+ {
+ RCUWriter<DiskstreamList> dsl (diskstreams);
+ boost::shared_ptr<DiskstreamList> d = dsl.get_copy();
+ d->remove (ds);
+ }
+ }
+
+ find_current_end ();
+
+ // We need to disconnect the routes inputs and outputs
+
+ route->disconnect_inputs (0);
+ route->disconnect_outputs (0);
+
+ update_latency_compensation (false, false);
+ set_dirty();
+
+ /* get rid of it from the dead wood collection in the route list manager */
+
+ /* XXX i think this is unsafe as it currently stands, but i am not sure. (pd, october 2nd, 2006) */
+
+ routes.flush ();
+
+ /* try to cause everyone to drop their references */
+
+ route->drop_references ();
+
+ /* save the new state of the world */
+
+ if (save_state (_current_snapshot_name)) {
+ save_history (_current_snapshot_name);
+ }
+}
+
+void
+Session::route_mute_changed (void* src)
+{
+ set_dirty ();
+}
+
+void
+Session::route_solo_changed (void* src, boost::weak_ptr<Route> wpr)
+{
+ if (solo_update_disabled) {
+ // We know already
+ return;
+ }
+
+ bool is_track;
+ boost::shared_ptr<Route> route = wpr.lock ();
+
+ if (!route) {
+ /* should not happen */
+ error << string_compose (_("programming error: %1"), X_("invalid route weak ptr passed to route_solo_changed")) << endmsg;
+ return;
+ }
+
+ is_track = (boost::dynamic_pointer_cast<AudioTrack>(route) != 0);
+
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+
+ /* soloing a track mutes all other tracks, soloing a bus mutes all other busses */
+
+ if (is_track) {
+
+ /* don't mess with busses */
+
+ if (dynamic_cast<Track*>((*i).get()) == 0) {
+ continue;
+ }
+
+ } else {
+
+ /* don't mess with tracks */
+
+ if (dynamic_cast<Track*>((*i).get()) != 0) {
+ continue;
+ }
+ }
+
+ if ((*i) != route &&
+ ((*i)->mix_group () == 0 ||
+ (*i)->mix_group () != route->mix_group () ||
+ !route->mix_group ()->is_active())) {
+
+ if ((*i)->soloed()) {
+
+ /* if its already soloed, and solo latching is enabled,
+ then leave it as it is.
+ */
+
+ if (Config->get_solo_latched()) {
+ continue;
+ }
+ }
+
+ /* do it */
+
+ solo_update_disabled = true;
+ (*i)->set_solo (false, src);
+ solo_update_disabled = false;
+ }
+ }
+
+ bool something_soloed = false;
+ bool same_thing_soloed = false;
+ bool signal = false;
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->soloed()) {
+ something_soloed = true;
+ if (dynamic_cast<Track*>((*i).get())) {
+ if (is_track) {
+ same_thing_soloed = true;
+ break;
+ }
+ } else {
+ if (!is_track) {
+ same_thing_soloed = true;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if (something_soloed != currently_soloing) {
+ signal = true;
+ currently_soloing = something_soloed;
+ }
+
+ modify_solo_mute (is_track, same_thing_soloed);
+
+ if (signal) {
+ SoloActive (currently_soloing); /* EMIT SIGNAL */
+ }
+
+ SoloChanged (); /* EMIT SIGNAL */
+
+ set_dirty();
+}
+
+void
+Session::update_route_solo_state ()
+{
+ bool mute = false;
+ bool is_track = false;
+ bool signal = false;
+
+ /* caller must hold RouteLock */
+
+ /* this is where we actually implement solo by changing
+ the solo mute setting of each track.
+ */
+
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->soloed()) {
+ mute = true;
+ if (dynamic_cast<Track*>((*i).get())) {
+ is_track = true;
+ }
+ break;
+ }
+ }
+
+ if (mute != currently_soloing) {
+ signal = true;
+ currently_soloing = mute;
+ }
+
+ if (!is_track && !mute) {
+
+ /* nothing is soloed */
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->set_solo_mute (false);
+ }
+
+ if (signal) {
+ SoloActive (false);
+ }
+
+ return;
+ }
+
+ modify_solo_mute (is_track, mute);
+
+ if (signal) {
+ SoloActive (currently_soloing);
+ }
+}
+
+void
+Session::modify_solo_mute (bool is_track, bool mute)
+{
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+
+ if (is_track) {
+
+ /* only alter track solo mute */
+
+ if (dynamic_cast<Track*>((*i).get())) {
+ if ((*i)->soloed()) {
+ (*i)->set_solo_mute (!mute);
+ } else {
+ (*i)->set_solo_mute (mute);
+ }
+ }
+
+ } else {
+
+ /* only alter bus solo mute */
+
+ if (!dynamic_cast<Track*>((*i).get())) {
+
+ if ((*i)->soloed()) {
+
+ (*i)->set_solo_mute (false);
+
+ } else {
+
+ /* don't mute master or control outs
+ in response to another bus solo
+ */
+
+ if ((*i) != _master_out &&
+ (*i) != _control_out) {
+ (*i)->set_solo_mute (mute);
+ }
+ }
+ }
+
+ }
+ }
+}
+
+
+void
+Session::catch_up_on_solo ()
+{
+ /* this is called after set_state() to catch the full solo
+ state, which can't be correctly determined on a per-route
+ basis, but needs the global overview that only the session
+ has.
+ */
+ update_route_solo_state();
+}
+
+shared_ptr<Route>
+Session::route_by_name (string name)
+{
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->name() == name) {
+ return *i;
+ }
+ }
+
+ return shared_ptr<Route> ((Route*) 0);
+}
+
+shared_ptr<Route>
+Session::route_by_id (PBD::ID id)
+{
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->id() == id) {
+ return *i;
+ }
+ }
+
+ return shared_ptr<Route> ((Route*) 0);
+}
+
+shared_ptr<Route>
+Session::route_by_remote_id (uint32_t id)
+{
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->remote_control_id() == id) {
+ return *i;
+ }
+ }
+
+ return shared_ptr<Route> ((Route*) 0);
+}
+
+void
+Session::find_current_end ()
+{
+ if (_state_of_the_state & Loading) {
+ return;
+ }
+
+ nframes_t max = get_maximum_extent ();
+
+ if (max > end_location->end()) {
+ end_location->set_end (max);
+ set_dirty();
+ DurationChanged(); /* EMIT SIGNAL */
+ }
+}
+
+nframes_t
+Session::get_maximum_extent () const
+{
+ nframes_t max = 0;
+ nframes_t me;
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ boost::shared_ptr<Playlist> pl = (*i)->playlist();
+ if ((me = pl->get_maximum_extent()) > max) {
+ max = me;
+ }
+ }
+
+ return max;
+}
+
+boost::shared_ptr<Diskstream>
+Session::diskstream_by_name (string name)
+{
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->name() == name) {
+ return *i;
+ }
+ }
+
+ return boost::shared_ptr<Diskstream>((Diskstream*) 0);
+}
+
+boost::shared_ptr<Diskstream>
+Session::diskstream_by_id (const PBD::ID& id)
+{
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->id() == id) {
+ return *i;
+ }
+ }
+
+ return boost::shared_ptr<Diskstream>((Diskstream*) 0);
+}
+
+/* Region management */
+
+string
+Session::new_region_name (string old)
+{
+ string::size_type last_period;
+ uint32_t number;
+ string::size_type len = old.length() + 64;
+ char buf[len];
+
+ if ((last_period = old.find_last_of ('.')) == string::npos) {
+
+ /* no period present - add one explicitly */
+
+ old += '.';
+ last_period = old.length() - 1;
+ number = 0;
+
+ } else {
+
+ number = atoi (old.substr (last_period+1).c_str());
+
+ }
+
+ while (number < (UINT_MAX-1)) {
+
+ RegionList::const_iterator i;
+ string sbuf;
+
+ number++;
+
+ snprintf (buf, len, "%s%" PRIu32, old.substr (0, last_period + 1).c_str(), number);
+ sbuf = buf;
+
+ for (i = regions.begin(); i != regions.end(); ++i) {
+ if (i->second->name() == sbuf) {
+ break;
+ }
+ }
+
+ if (i == regions.end()) {
+ break;
+ }
+ }
+
+ if (number != (UINT_MAX-1)) {
+ return buf;
+ }
+
+ error << string_compose (_("cannot create new name for region \"%1\""), old) << endmsg;
+ return old;
+}
+
+int
+Session::region_name (string& result, string base, bool newlevel) const
+{
+ char buf[16];
+ string subbase;
+
+ assert(base.find("/") == string::npos);
+
+ if (base == "") {
+
+ Glib::Mutex::Lock lm (region_lock);
+
+ snprintf (buf, sizeof (buf), "%d", (int)regions.size() + 1);
+
+
+ result = "region.";
+ result += buf;
+
+ } else {
+
+ /* XXX this is going to be slow. optimize me later */
+
+ if (newlevel) {
+ subbase = base;
+ } else {
+ string::size_type pos;
+
+ pos = base.find_last_of ('.');
+
+ /* pos may be npos, but then we just use entire base */
+
+ subbase = base.substr (0, pos);
+
+ }
+
+ bool name_taken = true;
+
+ {
+ Glib::Mutex::Lock lm (region_lock);
+
+ for (int n = 1; n < 5000; ++n) {
+
+ result = subbase;
+ snprintf (buf, sizeof (buf), ".%d", n);
+ result += buf;
+
+ name_taken = false;
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+ if (i->second->name() == result) {
+ name_taken = true;
+ break;
+ }
+ }
+
+ if (!name_taken) {
+ break;
+ }
+ }
+ }
+
+ if (name_taken) {
+ fatal << string_compose(_("too many regions with names like %1"), base) << endmsg;
+ /*NOTREACHED*/
+ }
+ }
+ return 0;
+}
+
+void
+Session::add_region (boost::shared_ptr<Region> region)
+{
+ vector<boost::shared_ptr<Region> > v;
+ v.push_back (region);
+ add_regions (v);
+}
+
+void
+Session::add_regions (vector<boost::shared_ptr<Region> >& new_regions)
+{
+ bool added = false;
+
+ {
+ Glib::Mutex::Lock lm (region_lock);
+
+ for (vector<boost::shared_ptr<Region> >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) {
+
+ boost::shared_ptr<Region> region = *ii;
+
+ if (region == 0) {
+
+ error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg;
+
+ } else {
+
+ RegionList::iterator x;
+
+ for (x = regions.begin(); x != regions.end(); ++x) {
+
+ if (region->region_list_equivalent (x->second)) {
+ break;
+ }
+ }
+
+ if (x == regions.end()) {
+
+ pair<RegionList::key_type,RegionList::mapped_type> entry;
+
+ entry.first = region->id();
+ entry.second = region;
+
+ pair<RegionList::iterator,bool> x = regions.insert (entry);
+
+ if (!x.second) {
+ return;
+ }
+
+ added = true;
+ }
+ }
+ }
+ }
+
+ /* mark dirty because something has changed even if we didn't
+ add the region to the region list.
+ */
+
+ set_dirty();
+
+ if (added) {
+
+ vector<boost::weak_ptr<Region> > v;
+ boost::shared_ptr<Region> first_r;
+
+ for (vector<boost::shared_ptr<Region> >::iterator ii = new_regions.begin(); ii != new_regions.end(); ++ii) {
+
+ boost::shared_ptr<Region> region = *ii;
+
+ if (region == 0) {
+
+ error << _("Session::add_region() ignored a null region. Warning: you might have lost a region.") << endmsg;
+
+ } else {
+ v.push_back (region);
+
+ if (!first_r) {
+ first_r = region;
+ }
+ }
+
+ region->StateChanged.connect (sigc::bind (mem_fun (*this, &Session::region_changed), boost::weak_ptr<Region>(region)));
+ region->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_region), boost::weak_ptr<Region>(region)));
+ }
+
+ if (!v.empty()) {
+ RegionsAdded (v); /* EMIT SIGNAL */
+ }
+ }
+}
+
+void
+Session::region_changed (Change what_changed, boost::weak_ptr<Region> weak_region)
+{
+ boost::shared_ptr<Region> region (weak_region.lock ());
+
+ if (!region) {
+ return;
+ }
+
+ if (what_changed & Region::HiddenChanged) {
+ /* relay hidden changes */
+ RegionHiddenChange (region);
+ }
+}
+
+void
+Session::remove_region (boost::weak_ptr<Region> weak_region)
+{
+ RegionList::iterator i;
+ boost::shared_ptr<Region> region (weak_region.lock ());
+
+ if (!region) {
+ return;
+ }
+
+ bool removed = false;
+
+ {
+ Glib::Mutex::Lock lm (region_lock);
+
+ if ((i = regions.find (region->id())) != regions.end()) {
+ regions.erase (i);
+ removed = true;
+ }
+ }
+
+ /* mark dirty because something has changed even if we didn't
+ remove the region from the region list.
+ */
+
+ set_dirty();
+
+ if (removed) {
+ RegionRemoved(region); /* EMIT SIGNAL */
+ }
+}
+
+boost::shared_ptr<Region>
+Session::find_whole_file_parent (boost::shared_ptr<Region const> child)
+{
+ RegionList::iterator i;
+ boost::shared_ptr<Region> region;
+
+ Glib::Mutex::Lock lm (region_lock);
+
+ for (i = regions.begin(); i != regions.end(); ++i) {
+
+ region = i->second;
+
+ if (region->whole_file()) {
+
+ if (child->source_equivalent (region)) {
+ return region;
+ }
+ }
+ }
+
+ return boost::shared_ptr<Region> ();
+}
+
+void
+Session::find_equivalent_playlist_regions (boost::shared_ptr<Region> region, vector<boost::shared_ptr<Region> >& result)
+{
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i)
+ (*i)->get_region_list_equivalent_regions (region, result);
+}
+
+int
+Session::destroy_region (boost::shared_ptr<Region> region)
+{
+ vector<boost::shared_ptr<Source> > srcs;
+
+ {
+ if (region->playlist()) {
+ region->playlist()->destroy_region (region);
+ }
+
+ for (uint32_t n = 0; n < region->n_channels(); ++n) {
+ srcs.push_back (region->source (n));
+ }
+ }
+
+ region->drop_references ();
+
+ for (vector<boost::shared_ptr<Source> >::iterator i = srcs.begin(); i != srcs.end(); ++i) {
+
+ (*i)->mark_for_remove ();
+ (*i)->drop_references ();
+
+ cerr << "source was not used by any playlist\n";
+ }
+
+ return 0;
+}
+
+int
+Session::destroy_regions (list<boost::shared_ptr<Region> > regions)
+{
+ for (list<boost::shared_ptr<Region> >::iterator i = regions.begin(); i != regions.end(); ++i) {
+ destroy_region (*i);
+ }
+ return 0;
+}
+
+int
+Session::remove_last_capture ()
+{
+ list<boost::shared_ptr<Region> > r;
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ list<boost::shared_ptr<Region> >& l = (*i)->last_capture_regions();
+
+ if (!l.empty()) {
+ r.insert (r.end(), l.begin(), l.end());
+ l.clear ();
+ }
+ }
+
+ destroy_regions (r);
+
+ save_state (_current_snapshot_name);
+
+ return 0;
+}
+
+int
+Session::remove_region_from_region_list (boost::shared_ptr<Region> r)
+{
+ remove_region (r);
+ return 0;
+}
+
+/* Source Management */
+void
+Session::add_source (boost::shared_ptr<Source> source)
+{
+ pair<SourceMap::key_type, SourceMap::mapped_type> entry;
+ pair<SourceMap::iterator,bool> result;
+
+ entry.first = source->id();
+ entry.second = source;
+
+ {
+ Glib::Mutex::Lock lm (source_lock);
+ result = sources.insert (entry);
+ }
+
+ if (result.second) {
+ source->GoingAway.connect (sigc::bind (mem_fun (this, &Session::remove_source), boost::weak_ptr<Source> (source)));
+ set_dirty();
+ }
+
+ boost::shared_ptr<AudioFileSource> afs;
+
+ if ((afs = boost::dynamic_pointer_cast<AudioFileSource>(source)) != 0) {
+ if (Config->get_auto_analyse_audio()) {
+ Analyser::queue_source_for_analysis (source, false);
+ }
+ }
+}
+
+void
+Session::remove_source (boost::weak_ptr<Source> src)
+{
+ SourceMap::iterator i;
+ boost::shared_ptr<Source> source = src.lock();
+
+ if (!source) {
+ return;
+ }
+
+ {
+ Glib::Mutex::Lock lm (source_lock);
+
+ if ((i = sources.find (source->id())) != sources.end()) {
+ sources.erase (i);
+ }
+ }
+
+ if (!_state_of_the_state & InCleanup) {
+
+ /* save state so we don't end up with a session file
+ referring to non-existent sources.
+ */
+
+ save_state (_current_snapshot_name);
+ }
+}
+
+boost::shared_ptr<Source>
+Session::source_by_id (const PBD::ID& id)
+{
+ Glib::Mutex::Lock lm (source_lock);
+ SourceMap::iterator i;
+ boost::shared_ptr<Source> source;
+
+ if ((i = sources.find (id)) != sources.end()) {
+ source = i->second;
+ }
+
+ return source;
+}
+
+
+boost::shared_ptr<Source>
+Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn)
+{
+ Glib::Mutex::Lock lm (source_lock);
+
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+ cerr << "comparing " << path << " with " << i->second->name() << endl;
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(i->second);
+
+ if (afs && afs->path() == path && chn == afs->channel()) {
+ return afs;
+ }
+
+ }
+ return boost::shared_ptr<Source>();
+}
+
+Glib::ustring
+Session::peak_path (Glib::ustring base) const
+{
+ sys::path peakfile_path(_session_dir->peak_path());
+ peakfile_path /= basename_nosuffix (base) + peakfile_suffix;
+ return peakfile_path.to_string();
+}
+
+string
+Session::change_audio_path_by_name (string path, string oldname, string newname, bool destructive)
+{
+ string look_for;
+ string old_basename = PBD::basename_nosuffix (oldname);
+ string new_legalized = legalize_for_path (newname);
+
+ /* note: we know (or assume) the old path is already valid */
+
+ if (destructive) {
+
+ /* destructive file sources have a name of the form:
+
+ /path/to/Tnnnn-NAME(%[LR])?.wav
+
+ the task here is to replace NAME with the new name.
+ */
+
+ /* find last slash */
+
+ string dir;
+ string prefix;
+ string::size_type slash;
+ string::size_type dash;
+
+ if ((slash = path.find_last_of ('/')) == string::npos) {
+ return "";
+ }
+
+ dir = path.substr (0, slash+1);
+
+ /* '-' is not a legal character for the NAME part of the path */
+
+ if ((dash = path.find_last_of ('-')) == string::npos) {
+ return "";
+ }
+
+ prefix = path.substr (slash+1, dash-(slash+1));
+
+ path = dir;
+ path += prefix;
+ path += '-';
+ path += new_legalized;
+ path += ".wav"; /* XXX gag me with a spoon */
+
+ } else {
+
+ /* non-destructive file sources have a name of the form:
+
+ /path/to/NAME-nnnnn(%[LR])?.wav
+
+ the task here is to replace NAME with the new name.
+ */
+
+ string dir;
+ string suffix;
+ string::size_type slash;
+ string::size_type dash;
+ string::size_type postfix;
+
+ /* find last slash */
+
+ if ((slash = path.find_last_of ('/')) == string::npos) {
+ return "";
+ }
+
+ dir = path.substr (0, slash+1);
+
+ /* '-' is not a legal character for the NAME part of the path */
+
+ if ((dash = path.find_last_of ('-')) == string::npos) {
+ return "";
+ }
+
+ suffix = path.substr (dash+1);
+
+ // Suffix is now everything after the dash. Now we need to eliminate
+ // the nnnnn part, which is done by either finding a '%' or a '.'
+
+ postfix = suffix.find_last_of ("%");
+ if (postfix == string::npos) {
+ postfix = suffix.find_last_of ('.');
+ }
+
+ if (postfix != string::npos) {
+ suffix = suffix.substr (postfix);
+ } else {
+ error << "Logic error in Session::change_audio_path_by_name(), please report to the developers" << endl;
+ return "";
+ }
+
+ const uint32_t limit = 10000;
+ char buf[PATH_MAX+1];
+
+ for (uint32_t cnt = 1; cnt <= limit; ++cnt) {
+
+ snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str());
+
+ if (access (buf, F_OK) != 0) {
+ path = buf;
+ break;
+ }
+ path = "";
+ }
+
+ if (path == "") {
+ error << "FATAL ERROR! Could not find a " << endl;
+ }
+
+ }
+
+ return path;
+}
+
+string
+Session::audio_path_from_name (string name, uint32_t nchan, uint32_t chan, bool destructive)
+{
+ string spath;
+ uint32_t cnt;
+ char buf[PATH_MAX+1];
+ const uint32_t limit = 10000;
+ string legalized;
+
+ buf[0] = '\0';
+ legalized = legalize_for_path (name);
+
+ /* find a "version" of the file name that doesn't exist in
+ any of the possible directories.
+ */
+
+ for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) {
+
+ vector<space_and_path>::iterator i;
+ uint32_t existing = 0;
+
+ for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+
+ SessionDirectory sdir((*i).path);
+
+ spath = sdir.sound_path().to_string();
+
+ if (destructive) {
+ if (nchan < 2) {
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str());
+ } else if (nchan == 2) {
+ if (chan == 0) {
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s%%L.wav", spath.c_str(), cnt, legalized.c_str());
+ } else {
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s%%R.wav", spath.c_str(), cnt, legalized.c_str());
+ }
+ } else if (nchan < 26) {
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s%%%c.wav", spath.c_str(), cnt, legalized.c_str(), 'a' + chan);
+ } else {
+ snprintf (buf, sizeof(buf), "%s/T%04d-%s.wav", spath.c_str(), cnt, legalized.c_str());
+ }
+
+ } else {
+
+ spath += '/';
+ spath += legalized;
+
+ if (nchan < 2) {
+ snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt);
+ } else if (nchan == 2) {
+ if (chan == 0) {
+ snprintf (buf, sizeof(buf), "%s-%u%%L.wav", spath.c_str(), cnt);
+ } else {
+ snprintf (buf, sizeof(buf), "%s-%u%%R.wav", spath.c_str(), cnt);
+ }
+ } else if (nchan < 26) {
+ snprintf (buf, sizeof(buf), "%s-%u%%%c.wav", spath.c_str(), cnt, 'a' + chan);
+ } else {
+ snprintf (buf, sizeof(buf), "%s-%u.wav", spath.c_str(), cnt);
+ }
+ }
+
+ if (sys::exists(buf)) {
+ existing++;
+ }
+
+ }
+
+ if (existing == 0) {
+ break;
+ }
+
+ if (cnt > limit) {
+ error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg;
+ destroy ();
+ throw failed_constructor();
+ }
+ }
+
+ /* we now have a unique name for the file, but figure out where to
+ actually put it.
+ */
+
+ string foo = buf;
+
+ SessionDirectory sdir(get_best_session_directory_for_new_source ());
+
+ spath = sdir.sound_path().to_string();
+ spath += '/';
+
+ string::size_type pos = foo.find_last_of ('/');
+
+ if (pos == string::npos) {
+ spath += foo;
+ } else {
+ spath += foo.substr (pos + 1);
+ }
+
+ return spath;
+}
+
+boost::shared_ptr<AudioFileSource>
+Session::create_audio_source_for_session (AudioDiskstream& ds, uint32_t chan, bool destructive)
+{
+ string spath = audio_path_from_name (ds.name(), ds.n_channels().n_audio(), chan, destructive);
+ return boost::dynamic_pointer_cast<AudioFileSource> (
+ SourceFactory::createWritable (DataType::AUDIO, *this, spath, destructive, frame_rate()));
+}
+
+// FIXME: _terrible_ code duplication
+string
+Session::change_midi_path_by_name (string path, string oldname, string newname, bool destructive)
+{
+ string look_for;
+ string old_basename = PBD::basename_nosuffix (oldname);
+ string new_legalized = legalize_for_path (newname);
+
+ /* note: we know (or assume) the old path is already valid */
+
+ if (destructive) {
+
+ /* destructive file sources have a name of the form:
+
+ /path/to/Tnnnn-NAME(%[LR])?.wav
+
+ the task here is to replace NAME with the new name.
+ */
+
+ /* find last slash */
+
+ string dir;
+ string prefix;
+ string::size_type slash;
+ string::size_type dash;
+
+ if ((slash = path.find_last_of ('/')) == string::npos) {
+ return "";
+ }
+
+ dir = path.substr (0, slash+1);
+
+ /* '-' is not a legal character for the NAME part of the path */
+
+ if ((dash = path.find_last_of ('-')) == string::npos) {
+ return "";
+ }
+
+ prefix = path.substr (slash+1, dash-(slash+1));
+
+ path = dir;
+ path += prefix;
+ path += '-';
+ path += new_legalized;
+ path += ".mid"; /* XXX gag me with a spoon */
+
+ } else {
+
+ /* non-destructive file sources have a name of the form:
+
+ /path/to/NAME-nnnnn(%[LR])?.wav
+
+ the task here is to replace NAME with the new name.
+ */
+
+ string dir;
+ string suffix;
+ string::size_type slash;
+ string::size_type dash;
+ string::size_type postfix;
+
+ /* find last slash */
+
+ if ((slash = path.find_last_of ('/')) == string::npos) {
+ return "";
+ }
+
+ dir = path.substr (0, slash+1);
+
+ /* '-' is not a legal character for the NAME part of the path */
+
+ if ((dash = path.find_last_of ('-')) == string::npos) {
+ return "";
+ }
+
+ suffix = path.substr (dash+1);
+
+ // Suffix is now everything after the dash. Now we need to eliminate
+ // the nnnnn part, which is done by either finding a '%' or a '.'
+
+ postfix = suffix.find_last_of ("%");
+ if (postfix == string::npos) {
+ postfix = suffix.find_last_of ('.');
+ }
+
+ if (postfix != string::npos) {
+ suffix = suffix.substr (postfix);
+ } else {
+ error << "Logic error in Session::change_midi_path_by_name(), please report to the developers" << endl;
+ return "";
+ }
+
+ const uint32_t limit = 10000;
+ char buf[PATH_MAX+1];
+
+ for (uint32_t cnt = 1; cnt <= limit; ++cnt) {
+
+ snprintf (buf, sizeof(buf), "%s%s-%u%s", dir.c_str(), newname.c_str(), cnt, suffix.c_str());
+
+ if (access (buf, F_OK) != 0) {
+ path = buf;
+ break;
+ }
+ path = "";
+ }
+
+ if (path == "") {
+ error << "FATAL ERROR! Could not find a " << endl;
+ }
+
+ }
+
+ return path;
+}
+
+string
+Session::midi_path_from_name (string name)
+{
+ string spath;
+ uint32_t cnt;
+ char buf[PATH_MAX+1];
+ const uint32_t limit = 10000;
+ string legalized;
+
+ buf[0] = '\0';
+ legalized = legalize_for_path (name);
+
+ /* find a "version" of the file name that doesn't exist in
+ any of the possible directories.
+ */
+
+ for (cnt = 1; cnt <= limit; ++cnt) {
+
+ vector<space_and_path>::iterator i;
+ uint32_t existing = 0;
+
+ for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+
+ SessionDirectory sdir((*i).path);
+
+ sys::path p = sdir.midi_path();
+
+ p /= legalized;
+
+ spath = p.to_string();
+
+ snprintf (buf, sizeof(buf), "%s-%u.mid", spath.c_str(), cnt);
+
+ if (sys::exists (buf)) {
+ existing++;
+ }
+ }
+
+ if (existing == 0) {
+ break;
+ }
+
+ if (cnt > limit) {
+ error << string_compose(_("There are already %1 recordings for %2, which I consider too many."), limit, name) << endmsg;
+ throw failed_constructor();
+ }
+ }
+
+ /* we now have a unique name for the file, but figure out where to
+ actually put it.
+ */
+
+ string foo = buf;
+
+ SessionDirectory sdir(get_best_session_directory_for_new_source ());
+
+ spath = sdir.midi_path().to_string();
+ spath += '/';
+
+ string::size_type pos = foo.find_last_of ('/');
+
+ if (pos == string::npos) {
+ spath += foo;
+ } else {
+ spath += foo.substr (pos + 1);
+ }
+
+ return spath;
+}
+
+boost::shared_ptr<MidiSource>
+Session::create_midi_source_for_session (MidiDiskstream& ds)
+{
+ string mpath = midi_path_from_name (ds.name());
+
+ return boost::dynamic_pointer_cast<SMFSource> (SourceFactory::createWritable (DataType::MIDI, *this, mpath, false, frame_rate()));
+}
+
+
+/* Playlist management */
+
+boost::shared_ptr<Playlist>
+Session::playlist_by_name (string name)
+{
+ Glib::Mutex::Lock lm (playlist_lock);
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ if ((*i)->name() == name) {
+ return* i;
+ }
+ }
+ for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+ if ((*i)->name() == name) {
+ return* i;
+ }
+ }
+
+ return boost::shared_ptr<Playlist>();
+}
+
+void
+Session::add_playlist (boost::shared_ptr<Playlist> playlist)
+{
+ if (playlist->hidden()) {
+ return;
+ }
+
+ {
+ Glib::Mutex::Lock lm (playlist_lock);
+ if (find (playlists.begin(), playlists.end(), playlist) == playlists.end()) {
+ playlists.insert (playlists.begin(), playlist);
+ playlist->InUse.connect (sigc::bind (mem_fun (*this, &Session::track_playlist), boost::weak_ptr<Playlist>(playlist)));
+ playlist->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_playlist), boost::weak_ptr<Playlist>(playlist)));
+ }
+ }
+
+ set_dirty();
+
+ PlaylistAdded (playlist); /* EMIT SIGNAL */
+}
+
+void
+Session::get_playlists (vector<boost::shared_ptr<Playlist> >& s)
+{
+ {
+ Glib::Mutex::Lock lm (playlist_lock);
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ s.push_back (*i);
+ }
+ for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+ s.push_back (*i);
+ }
+ }
+}
+
+void
+Session::track_playlist (bool inuse, boost::weak_ptr<Playlist> wpl)
+{
+ boost::shared_ptr<Playlist> pl(wpl.lock());
+
+ if (!pl) {
+ return;
+ }
+
+ PlaylistList::iterator x;
+
+ if (pl->hidden()) {
+ /* its not supposed to be visible */
+ return;
+ }
+
+ {
+ Glib::Mutex::Lock lm (playlist_lock);
+
+ if (!inuse) {
+
+ unused_playlists.insert (pl);
+
+ if ((x = playlists.find (pl)) != playlists.end()) {
+ playlists.erase (x);
+ }
+
+
+ } else {
+
+ playlists.insert (pl);
+
+ if ((x = unused_playlists.find (pl)) != unused_playlists.end()) {
+ unused_playlists.erase (x);
+ }
+ }
+ }
+}
+
+void
+Session::remove_playlist (boost::weak_ptr<Playlist> weak_playlist)
+{
+ if (_state_of_the_state & Deletion) {
+ return;
+ }
+
+ boost::shared_ptr<Playlist> playlist (weak_playlist.lock());
+
+ if (!playlist) {
+ return;
+ }
+
+ {
+ Glib::Mutex::Lock lm (playlist_lock);
+
+ PlaylistList::iterator i;
+
+ i = find (playlists.begin(), playlists.end(), playlist);
+ if (i != playlists.end()) {
+ playlists.erase (i);
+ }
+
+ i = find (unused_playlists.begin(), unused_playlists.end(), playlist);
+ if (i != unused_playlists.end()) {
+ unused_playlists.erase (i);
+ }
+
+ }
+
+ set_dirty();
+
+ PlaylistRemoved (playlist); /* EMIT SIGNAL */
+}
+
+void
+Session::set_audition (boost::shared_ptr<Region> r)
+{
+ pending_audition_region = r;
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportAudition);
+ schedule_butler_transport_work ();
+}
+
+void
+Session::audition_playlist ()
+{
+ Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0);
+ ev->region.reset ();
+ queue_event (ev);
+}
+
+void
+Session::non_realtime_set_audition ()
+{
+ if (!pending_audition_region) {
+ auditioner->audition_current_playlist ();
+ } else {
+ auditioner->audition_region (pending_audition_region);
+ pending_audition_region.reset ();
+ }
+ AuditionActive (true); /* EMIT SIGNAL */
+}
+
+void
+Session::audition_region (boost::shared_ptr<Region> r)
+{
+ Event* ev = new Event (Event::Audition, Event::Add, Event::Immediate, 0, 0.0);
+ ev->region = r;
+ queue_event (ev);
+}
+
+void
+Session::cancel_audition ()
+{
+ if (auditioner->active()) {
+ auditioner->cancel_audition ();
+ AuditionActive (false); /* EMIT SIGNAL */
+ }
+}
+
+bool
+Session::RoutePublicOrderSorter::operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b)
+{
+ return a->order_key(N_("signal")) < b->order_key(N_("signal"));
+}
+
+void
+Session::remove_empty_sounds ()
+{
+ vector<string> audio_filenames;
+
+ get_files_in_directory (_session_dir->sound_path(), audio_filenames);
+
+ Glib::Mutex::Lock lm (source_lock);
+
+ TapeFileMatcher tape_file_matcher;
+
+ remove_if (audio_filenames.begin(), audio_filenames.end(),
+ sigc::mem_fun (tape_file_matcher, &TapeFileMatcher::matches));
+
+ for (vector<string>::iterator i = audio_filenames.begin(); i != audio_filenames.end(); ++i) {
+
+ sys::path audio_file_path (_session_dir->sound_path());
+
+ audio_file_path /= *i;
+
+ if (AudioFileSource::is_empty (*this, audio_file_path.to_string())) {
+
+ try
+ {
+ sys::remove (audio_file_path);
+ const string peakfile = peak_path (audio_file_path.to_string());
+ sys::remove (peakfile);
+ }
+ catch (const sys::filesystem_error& err)
+ {
+ error << err.what() << endmsg;
+ }
+ }
+ }
+}
+
+bool
+Session::is_auditioning () const
+{
+ /* can be called before we have an auditioner object */
+ if (auditioner) {
+ return auditioner->active();
+ } else {
+ return false;
+ }
+}
+
+void
+Session::set_all_solo (bool yn)
+{
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (!(*i)->is_hidden()) {
+ (*i)->set_solo (yn, this);
+ }
+ }
+
+ set_dirty();
+}
+
+void
+Session::set_all_mute (bool yn)
+{
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (!(*i)->is_hidden()) {
+ (*i)->set_mute (yn, this);
+ }
+ }
+
+ set_dirty();
+}
+
+uint32_t
+Session::n_diskstreams () const
+{
+ uint32_t n = 0;
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::const_iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ n++;
+ }
+ }
+ return n;
+}
+
+void
+Session::graph_reordered ()
+{
+ /* don't do this stuff if we are setting up connections
+ from a set_state() call or creating new tracks.
+ */
+
+ if (_state_of_the_state & InitialConnecting) {
+ return;
+ }
+
+ /* every track/bus asked for this to be handled but it was deferred because
+ we were connecting. do it now.
+ */
+
+ request_input_change_handling ();
+
+ resort_routes ();
+
+ /* force all diskstreams to update their capture offset values to
+ reflect any changes in latencies within the graph.
+ */
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->set_capture_offset ();
+ }
+}
+
+void
+Session::record_disenable_all ()
+{
+ record_enable_change_all (false);
+}
+
+void
+Session::record_enable_all ()
+{
+ record_enable_change_all (true);
+}
+
+void
+Session::record_enable_change_all (bool yn)
+{
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ Track* at;
+
+ if ((at = dynamic_cast<Track*>((*i).get())) != 0) {
+ at->set_record_enable (yn, this);
+ }
+ }
+
+ /* since we don't keep rec-enable state, don't mark session dirty */
+}
+
+void
+Session::add_processor (Processor* processor)
+{
+ Send* send;
+ PortInsert* port_insert;
+ PluginInsert* plugin_insert;
+
+ if ((port_insert = dynamic_cast<PortInsert *> (processor)) != 0) {
+ _port_inserts.insert (_port_inserts.begin(), port_insert);
+ } else if ((plugin_insert = dynamic_cast<PluginInsert *> (processor)) != 0) {
+ _plugin_inserts.insert (_plugin_inserts.begin(), plugin_insert);
+ } else if ((send = dynamic_cast<Send *> (processor)) != 0) {
+ _sends.insert (_sends.begin(), send);
+ } else {
+ fatal << _("programming error: unknown type of Insert created!") << endmsg;
+ /*NOTREACHED*/
+ }
+
+ processor->GoingAway.connect (sigc::bind (mem_fun (*this, &Session::remove_processor), processor));
+
+ set_dirty();
+}
+
+void
+Session::remove_processor (Processor* processor)
+{
+ Send* send;
+ PortInsert* port_insert;
+ PluginInsert* plugin_insert;
+
+ if ((port_insert = dynamic_cast<PortInsert *> (processor)) != 0) {
+ list<PortInsert*>::iterator x = find (_port_inserts.begin(), _port_inserts.end(), port_insert);
+ if (x != _port_inserts.end()) {
+ insert_bitset[port_insert->bit_slot()] = false;
+ _port_inserts.erase (x);
+ }
+ } else if ((plugin_insert = dynamic_cast<PluginInsert *> (processor)) != 0) {
+ _plugin_inserts.remove (plugin_insert);
+ } else if ((send = dynamic_cast<Send *> (processor)) != 0) {
+ list<Send*>::iterator x = find (_sends.begin(), _sends.end(), send);
+ if (x != _sends.end()) {
+ send_bitset[send->bit_slot()] = false;
+ _sends.erase (x);
+ }
+ } else {
+ fatal << _("programming error: unknown type of Insert deleted!") << endmsg;
+ /*NOTREACHED*/
+ }
+
+ set_dirty();
+}
+
+nframes_t
+Session::available_capture_duration ()
+{
+ float sample_bytes_on_disk = 4.0; // keep gcc happy
+
+ switch (Config->get_native_file_data_format()) {
+ case FormatFloat:
+ sample_bytes_on_disk = 4.0;
+ break;
+
+ case FormatInt24:
+ sample_bytes_on_disk = 3.0;
+ break;
+
+ case FormatInt16:
+ sample_bytes_on_disk = 2.0;
+ break;
+
+ default:
+ /* impossible, but keep some gcc versions happy */
+ fatal << string_compose (_("programming error: %1"),
+ X_("illegal native file data format"))
+ << endmsg;
+ /*NOTREACHED*/
+ }
+
+ double scale = 4096.0 / sample_bytes_on_disk;
+
+ if (_total_free_4k_blocks * scale > (double) max_frames) {
+ return max_frames;
+ }
+
+ return (nframes_t) floor (_total_free_4k_blocks * scale);
+}
+
+void
+Session::add_bundle (shared_ptr<Bundle> bundle)
+{
+ {
+ Glib::Mutex::Lock guard (bundle_lock);
+ _bundles.push_back (bundle);
+ }
+
+ BundleAdded (bundle); /* EMIT SIGNAL */
+
+ set_dirty();
+}
+
+void
+Session::remove_bundle (shared_ptr<Bundle> bundle)
+{
+ bool removed = false;
+
+ {
+ Glib::Mutex::Lock guard (bundle_lock);
+ BundleList::iterator i = find (_bundles.begin(), _bundles.end(), bundle);
+
+ if (i != _bundles.end()) {
+ _bundles.erase (i);
+ removed = true;
+ }
+ }
+
+ if (removed) {
+ BundleRemoved (bundle); /* EMIT SIGNAL */
+ }
+
+ set_dirty();
+}
+
+shared_ptr<Bundle>
+Session::bundle_by_name (string name) const
+{
+ Glib::Mutex::Lock lm (bundle_lock);
+
+ for (BundleList::const_iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
+ if ((*i)->name() == name) {
+ return* i;
+ }
+ }
+
+ return boost::shared_ptr<Bundle> ();
+}
+
+void
+Session::tempo_map_changed (Change ignored)
+{
+ clear_clicks ();
+
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ (*i)->update_after_tempo_map_change ();
+ }
+
+ for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+ (*i)->update_after_tempo_map_change ();
+ }
+
+ set_dirty ();
+}
+
+/** Ensures that all buffers (scratch, send, silent, etc) are allocated for
+ * the given count with the current block size.
+ */
+void
+Session::ensure_buffers (ChanCount howmany)
+{
+ if (current_block_size == 0)
+ return; // too early? (is this ok?)
+
+ // We need at least 2 MIDI scratch buffers to mix/merge
+ if (howmany.n_midi() < 2)
+ howmany.set_midi(2);
+
+ // FIXME: JACK needs to tell us maximum MIDI buffer size
+ // Using nasty assumption (max # events == nframes) for now
+ _scratch_buffers->ensure_buffers(howmany, current_block_size);
+ _mix_buffers->ensure_buffers(howmany, current_block_size);
+ _silent_buffers->ensure_buffers(howmany, current_block_size);
+
+ allocate_pan_automation_buffers (current_block_size, howmany.n_audio(), false);
+}
+
+uint32_t
+Session::next_insert_id ()
+{
+ /* this doesn't really loop forever. just think about it */
+
+ while (true) {
+ for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < insert_bitset.size(); ++n) {
+ if (!insert_bitset[n]) {
+ insert_bitset[n] = true;
+ return n;
+
+ }
+ }
+
+ /* none available, so resize and try again */
+
+ insert_bitset.resize (insert_bitset.size() + 16, false);
+ }
+}
+
+uint32_t
+Session::next_send_id ()
+{
+ /* this doesn't really loop forever. just think about it */
+
+ while (true) {
+ for (boost::dynamic_bitset<uint32_t>::size_type n = 0; n < send_bitset.size(); ++n) {
+ if (!send_bitset[n]) {
+ send_bitset[n] = true;
+ return n;
+
+ }
+ }
+
+ /* none available, so resize and try again */
+
+ send_bitset.resize (send_bitset.size() + 16, false);
+ }
+}
+
+void
+Session::mark_send_id (uint32_t id)
+{
+ if (id >= send_bitset.size()) {
+ send_bitset.resize (id+16, false);
+ }
+ if (send_bitset[id]) {
+ warning << string_compose (_("send ID %1 appears to be in use already"), id) << endmsg;
+ }
+ send_bitset[id] = true;
+}
+
+void
+Session::mark_insert_id (uint32_t id)
+{
+ if (id >= insert_bitset.size()) {
+ insert_bitset.resize (id+16, false);
+ }
+ if (insert_bitset[id]) {
+ warning << string_compose (_("insert ID %1 appears to be in use already"), id) << endmsg;
+ }
+ insert_bitset[id] = true;
+}
+
+/* Named Selection management */
+
+NamedSelection *
+Session::named_selection_by_name (string name)
+{
+ Glib::Mutex::Lock lm (named_selection_lock);
+ for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ++i) {
+ if ((*i)->name == name) {
+ return* i;
+ }
+ }
+ return 0;
+}
+
+void
+Session::add_named_selection (NamedSelection* named_selection)
+{
+ {
+ Glib::Mutex::Lock lm (named_selection_lock);
+ named_selections.insert (named_selections.begin(), named_selection);
+ }
+
+ for (list<boost::shared_ptr<Playlist> >::iterator i = named_selection->playlists.begin(); i != named_selection->playlists.end(); ++i) {
+ add_playlist (*i);
+ }
+
+ set_dirty();
+
+ NamedSelectionAdded (); /* EMIT SIGNAL */
+}
+
+void
+Session::remove_named_selection (NamedSelection* named_selection)
+{
+ bool removed = false;
+
+ {
+ Glib::Mutex::Lock lm (named_selection_lock);
+
+ NamedSelectionList::iterator i = find (named_selections.begin(), named_selections.end(), named_selection);
+
+ if (i != named_selections.end()) {
+ delete (*i);
+ named_selections.erase (i);
+ set_dirty();
+ removed = true;
+ }
+ }
+
+ if (removed) {
+ NamedSelectionRemoved (); /* EMIT SIGNAL */
+ }
+}
+
+void
+Session::reset_native_file_format ()
+{
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->reset_write_sources (false);
+ }
+}
+
+bool
+Session::route_name_unique (string n) const
+{
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
+ if ((*i)->name() == n) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+uint32_t
+Session::n_playlists () const
+{
+ Glib::Mutex::Lock lm (playlist_lock);
+ return playlists.size();
+}
+
+void
+Session::allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, bool force)
+{
+ if (!force && howmany <= _npan_buffers) {
+ return;
+ }
+
+ if (_pan_automation_buffer) {
+
+ for (uint32_t i = 0; i < _npan_buffers; ++i) {
+ delete [] _pan_automation_buffer[i];
+ }
+
+ delete [] _pan_automation_buffer;
+ }
+
+ _pan_automation_buffer = new pan_t*[howmany];
+
+ for (uint32_t i = 0; i < howmany; ++i) {
+ _pan_automation_buffer[i] = new pan_t[nframes];
+ }
+
+ _npan_buffers = howmany;
+}
+
+int
+Session::freeze (InterThreadInfo& itt)
+{
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+
+ Track *at;
+
+ if ((at = dynamic_cast<Track*>((*i).get())) != 0) {
+ /* XXX this is wrong because itt.progress will keep returning to zero at the start
+ of every track.
+ */
+ at->freeze (itt);
+ }
+ }
+
+ return 0;
+}
+
+int
+Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t len,
+ bool overwrite, vector<boost::shared_ptr<Source> >& srcs, InterThreadInfo& itt)
+{
+ int ret = -1;
+ boost::shared_ptr<Playlist> playlist;
+ boost::shared_ptr<AudioFileSource> fsource;
+ uint32_t x;
+ char buf[PATH_MAX+1];
+ ChanCount nchans(track.audio_diskstream()->n_channels());
+ nframes_t position;
+ nframes_t this_chunk;
+ nframes_t to_do;
+ BufferSet buffers;
+ SessionDirectory sdir(get_best_session_directory_for_new_source ());
+ const string sound_dir = sdir.sound_path().to_string();
+
+ // any bigger than this seems to cause stack overflows in called functions
+ const nframes_t chunk_size = (128 * 1024)/4;
+
+ g_atomic_int_set (&processing_prohibited, 1);
+
+ /* call tree *MUST* hold route_lock */
+
+ if ((playlist = track.diskstream()->playlist()) == 0) {
+ goto out;
+ }
+
+ /* external redirects will be a problem */
+
+ if (track.has_external_redirects()) {
+ goto out;
+ }
+
+ for (uint32_t chan_n=0; chan_n < nchans.n_audio(); ++chan_n) {
+
+ for (x = 0; x < 99999; ++x) {
+ snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 ".wav", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1);
+ if (access (buf, F_OK) != 0) {
+ break;
+ }
+ }
+
+ if (x == 99999) {
+ error << string_compose (_("too many bounced versions of playlist \"%1\""), playlist->name()) << endmsg;
+ goto out;
+ }
+
+ try {
+ fsource = boost::dynamic_pointer_cast<AudioFileSource> (
+ SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate()));
+ }
+
+ catch (failed_constructor& err) {
+ error << string_compose (_("cannot create new audio file \"%1\" for %2"), buf, track.name()) << endmsg;
+ goto out;
+ }
+
+ srcs.push_back (fsource);
+ }
+
+ /* XXX need to flush all redirects */
+
+ position = start;
+ to_do = len;
+
+ /* create a set of reasonably-sized buffers */
+ buffers.ensure_buffers(nchans, chunk_size);
+ buffers.set_count(nchans);
+
+ for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+ if (afs)
+ afs->prepare_for_peakfile_writes ();
+ }
+
+ while (to_do && !itt.cancel) {
+
+ this_chunk = min (to_do, chunk_size);
+
+ if (track.export_stuff (buffers, start, this_chunk)) {
+ goto out;
+ }
+
+ uint32_t n = 0;
+ for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+
+ if (afs) {
+ if (afs->write (buffers.get_audio(n).data(), this_chunk) != this_chunk) {
+ goto out;
+ }
+ }
+ }
+
+ start += this_chunk;
+ to_do -= this_chunk;
+
+ itt.progress = (float) (1.0 - ((double) to_do / len));
+
+ }
+
+ if (!itt.cancel) {
+
+ time_t now;
+ struct tm* xnow;
+ time (&now);
+ xnow = localtime (&now);
+
+ for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+
+ if (afs) {
+ afs->update_header (position, *xnow, now);
+ afs->flush_header ();
+ }
+ }
+
+ /* construct a region to represent the bounced material */
+
+ boost::shared_ptr<Region> aregion = RegionFactory::create (srcs, 0, srcs.front()->length(),
+ region_name_from_path (srcs.front()->name(), true));
+
+ ret = 0;
+ }
+
+ out:
+ if (ret) {
+ for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+
+ if (afs) {
+ afs->mark_for_remove ();
+ }
+
+ (*src)->drop_references ();
+ }
+
+ } else {
+ for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+
+ if (afs)
+ afs->done_with_peakfile_writes ();
+ }
+ }
+
+ g_atomic_int_set (&processing_prohibited, 0);
+
+ return ret;
+}
+
+BufferSet&
+Session::get_silent_buffers (ChanCount count)
+{
+ assert(_silent_buffers->available() >= count);
+ _silent_buffers->set_count(count);
+
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ for (size_t i= 0; i < count.get(*t); ++i) {
+ _silent_buffers->get(*t, i).clear();
+ }
+ }
+
+ return *_silent_buffers;
+}
+
+BufferSet&
+Session::get_scratch_buffers (ChanCount count)
+{
+ assert(_scratch_buffers->available() >= count);
+ _scratch_buffers->set_count(count);
+ return *_scratch_buffers;
+}
+
+BufferSet&
+Session::get_mix_buffers (ChanCount count)
+{
+ assert(_mix_buffers->available() >= count);
+ _mix_buffers->set_count(count);
+ return *_mix_buffers;
+}
+
+uint32_t
+Session::ntracks () const
+{
+ uint32_t n = 0;
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
+ if (dynamic_cast<Track*> ((*i).get())) {
+ ++n;
+ }
+ }
+
+ return n;
+}
+
+uint32_t
+Session::nbusses () const
+{
+ uint32_t n = 0;
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::const_iterator i = r->begin(); i != r->end(); ++i) {
+ if (dynamic_cast<Track*> ((*i).get()) == 0) {
+ ++n;
+ }
+ }
+
+ return n;
+}
+
+void
+Session::add_automation_list(AutomationList *al)
+{
+ automation_lists[al->id()] = al;
+}
+
+nframes_t
+Session::compute_initial_length ()
+{
+ return _engine.frame_rate() * 60 * 5;
+}
+
+void
+Session::sync_order_keys ()
+{
+ if (!Config->get_sync_all_route_ordering()) {
+ /* leave order keys as they are */
+ return;
+ }
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->sync_order_keys ();
+ }
+
+ Route::SyncOrderKeys (); // EMIT SIGNAL
+}
+
+void
+Session::foreach_bundle (sigc::slot<void, boost::shared_ptr<Bundle> > sl)
+{
+ Glib::Mutex::Lock lm (bundle_lock);
+ for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
+ sl (*i);
+ }
+}
+
diff --git a/libs/ardour/session_butler.cc b/libs/ardour/session_butler.cc
new file mode 100644
index 0000000000..ec5de23caf
--- /dev/null
+++ b/libs/ardour/session_butler.cc
@@ -0,0 +1,471 @@
+/*
+ Copyright (C) 1999-2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+#include <string>
+#include <cmath>
+#include <cerrno>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/error.h>
+#include <pbd/pthread_utils.h>
+#include <pbd/stacktrace.h>
+
+#include <ardour/configuration.h>
+#include <ardour/audioengine.h>
+#include <ardour/session.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/crossfade.h>
+#include <ardour/timestamps.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+static float _read_data_rate;
+static float _write_data_rate;
+
+/* XXX put this in the right place */
+
+static inline uint32_t next_power_of_two (uint32_t n)
+{
+ --n;
+ n |= n >> 16;
+ n |= n >> 8;
+ n |= n >> 4;
+ n |= n >> 2;
+ n |= n >> 1;
+ ++n;
+ return n;
+}
+
+/*---------------------------------------------------------------------------
+ BUTLER THREAD
+ ---------------------------------------------------------------------------*/
+
+int
+Session::start_butler_thread ()
+{
+ /* size is in Samples, not bytes */
+ audio_dstream_buffer_size = (uint32_t) floor (Config->get_audio_track_buffer_seconds() * (float) frame_rate());
+
+ /* size is in bytes
+ * XXX: Jack needs to tell us the MIDI buffer size
+ * (i.e. how many MIDI bytes we might see in a cycle)
+ */
+ midi_dstream_buffer_size = (uint32_t) floor (Config->get_midi_track_buffer_seconds() * (float)frame_rate());
+
+ Crossfade::set_buffer_size (audio_dstream_buffer_size);
+
+ butler_should_run = false;
+
+ if (pipe (butler_request_pipe)) {
+ error << string_compose(_("Cannot create transport request signal pipe (%1)"), strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ if (fcntl (butler_request_pipe[0], F_SETFL, O_NONBLOCK)) {
+ error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"), strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ if (fcntl (butler_request_pipe[1], F_SETFL, O_NONBLOCK)) {
+ error << string_compose(_("UI: cannot set O_NONBLOCK on butler request pipe (%1)"), strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ if (pthread_create_and_store ("disk butler", &butler_thread, 0, _butler_thread_work, this)) {
+ error << _("Session: could not create butler thread") << endmsg;
+ return -1;
+ }
+
+ // pthread_detach (butler_thread);
+
+ return 0;
+}
+
+void
+Session::terminate_butler_thread ()
+{
+ if (butler_thread) {
+ void* status;
+ char c = ButlerRequest::Quit;
+ ::write (butler_request_pipe[1], &c, 1);
+ pthread_join (butler_thread, &status);
+ }
+}
+
+void
+Session::schedule_butler_transport_work ()
+{
+ g_atomic_int_inc (&butler_should_do_transport_work);
+ summon_butler ();
+}
+
+void
+Session::schedule_curve_reallocation ()
+{
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportCurveRealloc);
+ schedule_butler_transport_work ();
+}
+
+void
+Session::summon_butler ()
+{
+ char c = ButlerRequest::Run;
+ ::write (butler_request_pipe[1], &c, 1);
+ // PBD::stacktrace (cerr);
+}
+
+void
+Session::stop_butler ()
+{
+ Glib::Mutex::Lock lm (butler_request_lock);
+ char c = ButlerRequest::Pause;
+ ::write (butler_request_pipe[1], &c, 1);
+ butler_paused.wait(butler_request_lock);
+}
+
+void
+Session::wait_till_butler_finished ()
+{
+ Glib::Mutex::Lock lm (butler_request_lock);
+ char c = ButlerRequest::Wake;
+ ::write (butler_request_pipe[1], &c, 1);
+ butler_paused.wait(butler_request_lock);
+}
+
+void *
+Session::_butler_thread_work (void* arg)
+{
+ PBD::ThreadCreated (pthread_self(), X_("Butler"));
+ return ((Session *) arg)->butler_thread_work ();
+ return 0;
+}
+
+void *
+Session::butler_thread_work ()
+{
+ uint32_t err = 0;
+ int32_t bytes;
+ bool compute_io;
+ struct timeval begin, end;
+ struct pollfd pfd[1];
+ bool disk_work_outstanding = false;
+ DiskstreamList::iterator i;
+
+ while (true) {
+ pfd[0].fd = butler_request_pipe[0];
+ pfd[0].events = POLLIN|POLLERR|POLLHUP;
+
+ if (poll (pfd, 1, (disk_work_outstanding ? 0 : -1)) < 0) {
+
+ if (errno == EINTR) {
+ continue;
+ }
+
+ error << string_compose (_("poll on butler request pipe failed (%1)"),
+ strerror (errno))
+ << endmsg;
+ break;
+ }
+
+ if (pfd[0].revents & ~POLLIN) {
+ error << string_compose (_("Error on butler thread request pipe: fd=%1 err=%2"), pfd[0].fd, pfd[0].revents) << endmsg;
+ break;
+ }
+
+ if (pfd[0].revents & POLLIN) {
+
+ char req;
+
+ /* empty the pipe of all current requests */
+
+ while (1) {
+ size_t nread = ::read (butler_request_pipe[0], &req, sizeof (req));
+ if (nread == 1) {
+
+ switch ((ButlerRequest::Type) req) {
+
+ case ButlerRequest::Wake:
+ break;
+
+ case ButlerRequest::Run:
+ butler_should_run = true;
+ break;
+
+ case ButlerRequest::Pause:
+ butler_should_run = false;
+ break;
+
+ case ButlerRequest::Quit:
+ pthread_exit_pbd (0);
+ /*NOTREACHED*/
+ break;
+
+ default:
+ break;
+ }
+
+ } else if (nread == 0) {
+ break;
+ } else if (errno == EAGAIN) {
+ break;
+ } else {
+ fatal << _("Error reading from butler request pipe") << endmsg;
+ /*NOTREACHED*/
+ }
+ }
+ }
+
+ if (transport_work_requested()) {
+ butler_transport_work ();
+ }
+
+ disk_work_outstanding = false;
+ bytes = 0;
+ compute_io = true;
+
+ gettimeofday (&begin, 0);
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader ();
+
+// for (i = dsl->begin(); i != dsl->end(); ++i) {
+// cerr << "BEFORE " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
+// }
+
+ for (i = dsl->begin(); !transport_work_requested() && butler_should_run && i != dsl->end(); ++i) {
+
+ boost::shared_ptr<Diskstream> ds = *i;
+
+ /* don't read inactive tracks */
+
+ IO* io = ds->io();
+
+ if (io && !io->active()) {
+ continue;
+ }
+
+ switch (ds->do_refill ()) {
+ case 0:
+ bytes += ds->read_data_count();
+ break;
+ case 1:
+ bytes += ds->read_data_count();
+ disk_work_outstanding = true;
+ break;
+
+ default:
+ compute_io = false;
+ error << string_compose(_("Butler read ahead failure on dstream %1"), (*i)->name()) << endmsg;
+ break;
+ }
+
+ }
+
+ if (i != dsl->end()) {
+ /* we didn't get to all the streams */
+ disk_work_outstanding = true;
+ }
+
+ if (!err && transport_work_requested()) {
+ continue;
+ }
+
+ if (compute_io) {
+ gettimeofday (&end, 0);
+
+ double b = begin.tv_sec + (begin.tv_usec/1000000.0);
+ double e = end.tv_sec + (end.tv_usec / 1000000.0);
+
+ _read_data_rate = bytes / (e - b);
+ }
+
+ bytes = 0;
+ compute_io = true;
+ gettimeofday (&begin, 0);
+
+ for (i = dsl->begin(); !transport_work_requested() && butler_should_run && i != dsl->end(); ++i) {
+ // cerr << "write behind for " << (*i)->name () << endl;
+
+ /* note that we still try to flush diskstreams attached to inactive routes
+ */
+
+ switch ((*i)->do_flush (Session::ButlerContext)) {
+ case 0:
+ bytes += (*i)->write_data_count();
+ break;
+ case 1:
+ bytes += (*i)->write_data_count();
+ disk_work_outstanding = true;
+ break;
+
+ default:
+ err++;
+ compute_io = false;
+ error << string_compose(_("Butler write-behind failure on dstream %1"), (*i)->name()) << endmsg;
+ /* don't break - try to flush all streams in case they
+ are split across disks.
+ */
+ }
+ }
+
+ if (err && actively_recording()) {
+ /* stop the transport and try to catch as much possible
+ captured state as we can.
+ */
+ request_stop ();
+ }
+
+ if (i != dsl->end()) {
+ /* we didn't get to all the streams */
+ disk_work_outstanding = true;
+ }
+
+ if (!err && transport_work_requested()) {
+ continue;
+ }
+
+ if (compute_io) {
+ gettimeofday (&end, 0);
+
+ double b = begin.tv_sec + (begin.tv_usec/1000000.0);
+ double e = end.tv_sec + (end.tv_usec / 1000000.0);
+
+ _write_data_rate = bytes / (e - b);
+ }
+
+ if (!disk_work_outstanding) {
+ refresh_disk_space ();
+ }
+
+
+ {
+ Glib::Mutex::Lock lm (butler_request_lock);
+
+ if (butler_should_run && (disk_work_outstanding || transport_work_requested())) {
+// for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+// cerr << "AFTER " << (*i)->name() << ": pb = " << (*i)->playback_buffer_load() << " cp = " << (*i)->capture_buffer_load() << endl;
+// }
+
+ continue;
+ }
+
+ butler_paused.signal();
+ }
+ }
+
+ pthread_exit_pbd (0);
+ /*NOTREACHED*/
+ return (0);
+}
+
+
+void
+Session::request_overwrite_buffer (Diskstream* stream)
+{
+ Event *ev = new Event (Event::Overwrite, Event::Add, Event::Immediate, 0, 0, 0.0);
+ ev->set_ptr (stream);
+ queue_event (ev);
+}
+
+/** Process thread. */
+void
+Session::overwrite_some_buffers (Diskstream* ds)
+{
+ if (actively_recording()) {
+ return;
+ }
+
+ if (ds) {
+
+ ds->set_pending_overwrite (true);
+
+ } else {
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->set_pending_overwrite (true);
+ }
+ }
+
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportOverWrite);
+ schedule_butler_transport_work ();
+}
+
+float
+Session::read_data_rate () const
+{
+ /* disk i/o in excess of 10000MB/sec indicate the buffer cache
+ in action. ignore it.
+ */
+ return _read_data_rate > 10485760000.0f ? 0.0f : _read_data_rate;
+}
+
+float
+Session::write_data_rate () const
+{
+ /* disk i/o in excess of 10000MB/sec indicate the buffer cache
+ in action. ignore it.
+ */
+ return _write_data_rate > 10485760000.0f ? 0.0f : _write_data_rate;
+}
+
+uint32_t
+Session::playback_load ()
+{
+ return (uint32_t) g_atomic_int_get (&_playback_load);
+}
+
+uint32_t
+Session::capture_load ()
+{
+ return (uint32_t) g_atomic_int_get (&_capture_load);
+}
+
+uint32_t
+Session::playback_load_min ()
+{
+ return (uint32_t) g_atomic_int_get (&_playback_load_min);
+}
+
+uint32_t
+Session::capture_load_min ()
+{
+ return (uint32_t) g_atomic_int_get (&_capture_load_min);
+}
+
+void
+Session::reset_capture_load_min ()
+{
+ g_atomic_int_set (&_capture_load_min, 100);
+}
+
+
+void
+Session::reset_playback_load_min ()
+{
+ g_atomic_int_set (&_playback_load_min, 100);
+}
diff --git a/libs/ardour/session_click.cc b/libs/ardour/session_click.cc
new file mode 100644
index 0000000000..7161de6d78
--- /dev/null
+++ b/libs/ardour/session_click.cc
@@ -0,0 +1,223 @@
+/*
+ Copyright (C) 20002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <list>
+#include <cerrno>
+
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+#include <ardour/tempo.h>
+#include <ardour/io.h>
+#include <ardour/buffer_set.h>
+
+#include <sndfile.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+Pool Session::Click::pool ("click", sizeof (Click), 128);
+
+void
+Session::click (nframes_t start, nframes_t nframes, nframes_t offset)
+{
+ TempoMap::BBTPointList *points;
+ Sample *buf;
+
+ if (_click_io == 0) {
+ return;
+ }
+
+ Glib::RWLock::WriterLock clickm (click_lock, Glib::TRY_LOCK);
+
+ if (!clickm.locked() || _transport_speed != 1.0 || !_clicking || click_data == 0) {
+ _click_io->silence (nframes, offset);
+ return;
+ }
+
+ const nframes_t end = start + (nframes_t)floor(nframes * _transport_speed);
+
+ BufferSet& bufs = get_scratch_buffers(ChanCount(DataType::AUDIO, 1));
+ buf = bufs.get_audio(0).data();
+ points = _tempo_map->get_points (start, end);
+
+ if (points == 0) {
+ goto run_clicks;
+ }
+
+ if (points->empty()) {
+ delete points;
+ goto run_clicks;
+ }
+
+ for (TempoMap::BBTPointList::iterator i = points->begin(); i != points->end(); ++i) {
+ switch ((*i).type) {
+ case TempoMap::Beat:
+ if (click_emphasis_data == 0 || (click_emphasis_data && (*i).beat != 1)) {
+ clicks.push_back (new Click ((*i).frame, click_length, click_data));
+ }
+ break;
+
+ case TempoMap::Bar:
+ if (click_emphasis_data) {
+ clicks.push_back (new Click ((*i).frame, click_emphasis_length, click_emphasis_data));
+ }
+ break;
+ }
+ }
+
+ run_clicks:
+ memset (buf, 0, sizeof (Sample) * nframes);
+
+ for (list<Click*>::iterator i = clicks.begin(); i != clicks.end(); ) {
+
+ nframes_t copy;
+ nframes_t internal_offset;
+ Click *clk;
+ list<Click*>::iterator next;
+
+ clk = *i;
+ next = i;
+ ++next;
+
+ if (clk->start < start) {
+ internal_offset = 0;
+ } else {
+ internal_offset = clk->start - start;
+ }
+
+ if (nframes < internal_offset) {
+ /* we've just located or something..
+ effectively going backwards.
+ lets get the flock out of here */
+ break;
+ }
+
+ copy = min (clk->duration - clk->offset, nframes - internal_offset);
+
+ memcpy (buf + internal_offset, &clk->data[clk->offset], copy * sizeof (Sample));
+
+ clk->offset += copy;
+
+ if (clk->offset >= clk->duration) {
+ delete clk;
+ clicks.erase (i);
+ }
+
+
+ i = next;
+ }
+
+ _click_io->deliver_output (bufs, start, end, nframes, offset);
+}
+
+void
+Session::setup_click_sounds (int which)
+{
+ SNDFILE *sndfile;
+ SF_INFO info;
+
+ clear_clicks();
+
+ if ((which == 0 || which == 1)) {
+
+ if (click_data && click_data != default_click) {
+ delete [] click_data;
+ click_data = 0;
+ }
+
+ string path = Config->get_click_sound();
+
+ if (path.empty()) {
+
+ click_data = const_cast<Sample*> (default_click);
+ click_length = default_click_length;
+
+ } else {
+
+ if ((sndfile = sf_open (path.c_str(), SFM_READ, &info)) == 0) {
+ char errbuf[256];
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ warning << string_compose (_("cannot open click soundfile %1 (%2)"), path, errbuf) << endmsg;
+ _clicking = false;
+ return;
+ }
+
+ click_data = new Sample[info.frames];
+ click_length = info.frames;
+
+ if (sf_read_float (sndfile, click_data, info.frames) != info.frames) {
+ warning << _("cannot read data from click soundfile") << endmsg;
+ delete click_data;
+ click_data = 0;
+ _clicking = false;
+ }
+
+ sf_close (sndfile);
+
+ }
+ }
+
+ if ((which == 0 || which == -1)) {
+
+ if (click_emphasis_data && click_emphasis_data != default_click_emphasis) {
+ delete [] click_emphasis_data;
+ click_emphasis_data = 0;
+ }
+
+ string path = Config->get_click_emphasis_sound();
+
+ if (path.empty()) {
+ click_emphasis_data = const_cast<Sample*> (default_click_emphasis);
+ click_emphasis_length = default_click_emphasis_length;
+ } else {
+ if ((sndfile = sf_open (path.c_str(), SFM_READ, &info)) == 0) {
+ char errbuf[256];
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ warning << string_compose (_("cannot open click emphasis soundfile %1 (%2)"), path, errbuf) << endmsg;
+ return;
+ }
+
+ click_emphasis_data = new Sample[info.frames];
+ click_emphasis_length = info.frames;
+
+ if (sf_read_float (sndfile, click_emphasis_data, info.frames) != info.frames) {
+ warning << _("cannot read data from click emphasis soundfile") << endmsg;
+ delete click_emphasis_data;
+ click_emphasis_data = 0;
+ }
+
+ sf_close (sndfile);
+ }
+ }
+}
+
+void
+Session::clear_clicks ()
+{
+ Glib::RWLock::WriterLock lm (click_lock);
+
+ for (Clicks::iterator i = clicks.begin(); i != clicks.end(); ++i) {
+ delete *i;
+ }
+
+ clicks.clear ();
+}
diff --git a/libs/ardour/session_command.cc b/libs/ardour/session_command.cc
new file mode 100644
index 0000000000..e8670e7199
--- /dev/null
+++ b/libs/ardour/session_command.cc
@@ -0,0 +1,544 @@
+/*
+ Copyright (C) 2000-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/session.h>
+#include <ardour/route.h>
+#include <pbd/memento_command.h>
+#include <ardour/diskstream.h>
+#include <ardour/playlist.h>
+#include <ardour/audioplaylist.h>
+#include <ardour/audio_track.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/midi_track.h>
+#include <ardour/tempo.h>
+#include <ardour/audiosource.h>
+#include <ardour/audioregion.h>
+#include <ardour/midi_source.h>
+#include <ardour/midi_region.h>
+#include <pbd/error.h>
+#include <pbd/id.h>
+#include <pbd/statefuldestructible.h>
+#include <pbd/failed_constructor.h>
+
+using namespace PBD;
+using namespace ARDOUR;
+
+#include "i18n.h"
+
+void Session::register_with_memento_command_factory(PBD::ID id, PBD::StatefulThingWithGoingAway *ptr)
+{
+ registry[id] = ptr;
+}
+
+Command *
+Session::memento_command_factory(XMLNode *n)
+{
+ PBD::ID id;
+ XMLNode *before = 0, *after = 0;
+ XMLNode *child = 0;
+
+ /* get id */
+ id = PBD::ID(n->property("obj_id")->value());
+
+ /* get before/after */
+
+ if (n->name() == "MementoCommand") {
+ before = new XMLNode(*n->children().front());
+ after = new XMLNode(*n->children().back());
+ child = before;
+ } else if (n->name() == "MementoUndoCommand") {
+ before = new XMLNode(*n->children().front());
+ child = before;
+ } else if (n->name() == "MementoRedoCommand") {
+ after = new XMLNode(*n->children().front());
+ child = after;
+ } else if (n->name() == "PlaylistCommand") {
+ before = new XMLNode(*n->children().front());
+ after = new XMLNode(*n->children().back());
+ child = before;
+ }
+
+ if (!child)
+ {
+ error << _("Tried to reconstitute a MementoCommand with no contents, failing. id=") << id.to_s() << endmsg;
+ return 0;
+ }
+
+ /* create command */
+ string obj_T = n->property ("type_name")->value();
+ if (obj_T == typeid (AudioRegion).name() || obj_T == typeid (MidiRegion).name() || obj_T == typeid (Region).name()) {
+ if (regions.count(id)) {
+ return new MementoCommand<Region>(*regions[id], before, after);
+ }
+ } else if (obj_T == typeid (AudioSource).name() || obj_T == typeid (MidiSource).name()) {
+ if (sources.count(id))
+ return new MementoCommand<Source>(*sources[id], before, after);
+ } else if (obj_T == typeid (Location).name()) {
+ Location* loc = _locations.get_location_by_id(id);
+ if (loc) {
+ return new MementoCommand<Location>(*loc, before, after);
+ }
+ } else if (obj_T == typeid (Locations).name()) {
+ return new MementoCommand<Locations>(_locations, before, after);
+ } else if (obj_T == typeid (TempoMap).name()) {
+ return new MementoCommand<TempoMap>(*_tempo_map, before, after);
+ } else if (obj_T == typeid (Playlist).name() || obj_T == typeid (AudioPlaylist).name() || obj_T == typeid (MidiPlaylist).name()) {
+ if (boost::shared_ptr<Playlist> pl = playlist_by_name(child->property("name")->value())) {
+ return new MementoCommand<Playlist>(*(pl.get()), before, after);
+ }
+ } else if (obj_T == typeid (Route).name() || obj_T == typeid (AudioTrack).name() || obj_T == typeid(MidiTrack).name()) {
+ return new MementoCommand<Route>(*route_by_id(id), before, after);
+ } else if (obj_T == typeid (Curve).name() || obj_T == typeid (AutomationList).name()) {
+ if (automation_lists.count(id))
+ return new MementoCommand<AutomationList>(*automation_lists[id], before, after);
+ } else if (registry.count(id)) { // For Editor and AutomationLine which are off-limits here
+ return new MementoCommand<PBD::StatefulThingWithGoingAway>(*registry[id], before, after);
+ }
+
+ /* we failed */
+ error << string_compose (_("could not reconstitute MementoCommand from XMLNode. object type = %1 id = %2"), obj_T, id.to_s()) << endmsg;
+
+ return 0 ;
+}
+
+Command *
+Session::global_state_command_factory (const XMLNode& node)
+{
+ const XMLProperty* prop;
+ Command* command = 0;
+
+ if ((prop = node.property ("type")) == 0) {
+ error << _("GlobalRouteStateCommand has no \"type\" node, ignoring") << endmsg;
+ return 0;
+ }
+
+ try {
+
+ if (prop->value() == "solo") {
+ command = new GlobalSoloStateCommand (*this, node);
+ } else if (prop->value() == "mute") {
+ command = new GlobalMuteStateCommand (*this, node);
+ } else if (prop->value() == "rec-enable") {
+ command = new GlobalRecordEnableStateCommand (*this, node);
+ } else if (prop->value() == "metering") {
+ command = new GlobalMeteringStateCommand (*this, node);
+ } else {
+ error << string_compose (_("unknown type of GlobalRouteStateCommand (%1), ignored"), prop->value()) << endmsg;
+ }
+ }
+
+ catch (failed_constructor& err) {
+ return 0;
+ }
+
+ return command;
+}
+
+Session::GlobalRouteStateCommand::GlobalRouteStateCommand (Session& s, void* p)
+ : sess (s), src (p)
+{
+}
+
+Session::GlobalRouteStateCommand::GlobalRouteStateCommand (Session& s, const XMLNode& node)
+ : sess (s), src (this)
+{
+ if (set_state (node)) {
+ throw failed_constructor ();
+ }
+}
+
+int
+Session::GlobalRouteStateCommand::set_state (const XMLNode& node)
+{
+ GlobalRouteBooleanState states;
+ XMLNodeList nlist;
+ const XMLProperty* prop;
+ XMLNode* child;
+ XMLNodeConstIterator niter;
+ int loop;
+
+ before.clear ();
+ after.clear ();
+
+ for (loop = 0; loop < 2; ++loop) {
+
+ const char *str;
+
+ if (loop) {
+ str = "after";
+ } else {
+ str = "before";
+ }
+
+ if ((child = node.child (str)) == 0) {
+ warning << string_compose (_("global route state command has no \"%1\" node, ignoring entire command"), str) << endmsg;
+ return -1;
+ }
+
+ nlist = child->children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ RouteBooleanState rbs;
+ boost::shared_ptr<Route> route;
+ ID id;
+
+ prop = (*niter)->property ("id");
+ id = prop->value ();
+
+ if ((route = sess.route_by_id (id)) == 0) {
+ warning << string_compose (_("cannot find track/bus \"%1\" while rebuilding a global route state command, ignored"), id.to_s()) << endmsg;
+ continue;
+ }
+
+ rbs.first = boost::weak_ptr<Route> (route);
+
+ prop = (*niter)->property ("yn");
+ rbs.second = (prop->value() == "1");
+
+ if (loop) {
+ after.push_back (rbs);
+ } else {
+ before.push_back (rbs);
+ }
+ }
+ }
+
+ return 0;
+}
+
+XMLNode&
+Session::GlobalRouteStateCommand::get_state ()
+{
+ XMLNode* node = new XMLNode (X_("GlobalRouteStateCommand"));
+ XMLNode* nbefore = new XMLNode (X_("before"));
+ XMLNode* nafter = new XMLNode (X_("after"));
+
+ for (Session::GlobalRouteBooleanState::iterator x = before.begin(); x != before.end(); ++x) {
+ XMLNode* child = new XMLNode ("s");
+ boost::shared_ptr<Route> r = x->first.lock();
+
+ if (r) {
+ child->add_property (X_("id"), r->id().to_s());
+ child->add_property (X_("yn"), (x->second ? "1" : "0"));
+ nbefore->add_child_nocopy (*child);
+ }
+ }
+
+ for (Session::GlobalRouteBooleanState::iterator x = after.begin(); x != after.end(); ++x) {
+ XMLNode* child = new XMLNode ("s");
+ boost::shared_ptr<Route> r = x->first.lock();
+
+ if (r) {
+ child->add_property (X_("id"), r->id().to_s());
+ child->add_property (X_("yn"), (x->second ? "1" : "0"));
+ nafter->add_child_nocopy (*child);
+ }
+ }
+
+ node->add_child_nocopy (*nbefore);
+ node->add_child_nocopy (*nafter);
+
+ return *node;
+}
+
+// solo
+
+Session::GlobalSoloStateCommand::GlobalSoloStateCommand(Session &sess, void *src)
+ : GlobalRouteStateCommand (sess, src)
+{
+ after = before = sess.get_global_route_boolean(&Route::soloed);
+}
+
+Session::GlobalSoloStateCommand::GlobalSoloStateCommand (Session& sess, const XMLNode& node)
+ : Session::GlobalRouteStateCommand (sess, node)
+{
+}
+
+void
+Session::GlobalSoloStateCommand::mark()
+{
+ after = sess.get_global_route_boolean(&Route::soloed);
+}
+
+void
+Session::GlobalSoloStateCommand::operator()()
+{
+ sess.set_global_solo(after, src);
+}
+
+void
+Session::GlobalSoloStateCommand::undo()
+{
+ sess.set_global_solo(before, src);
+}
+
+XMLNode&
+Session::GlobalSoloStateCommand::get_state()
+{
+ XMLNode& node = GlobalRouteStateCommand::get_state();
+ node.add_property ("type", "solo");
+ return node;
+}
+
+// mute
+Session::GlobalMuteStateCommand::GlobalMuteStateCommand(Session &sess, void *src)
+ : GlobalRouteStateCommand (sess, src)
+{
+ after = before = sess.get_global_route_boolean(&Route::muted);
+}
+
+Session::GlobalMuteStateCommand::GlobalMuteStateCommand (Session& sess, const XMLNode& node)
+ : Session::GlobalRouteStateCommand (sess, node)
+{
+}
+
+void
+Session::GlobalMuteStateCommand::mark()
+{
+ after = sess.get_global_route_boolean(&Route::muted);
+}
+
+void
+Session::GlobalMuteStateCommand::operator()()
+{
+ sess.set_global_mute(after, src);
+}
+
+void
+Session::GlobalMuteStateCommand::undo()
+{
+ sess.set_global_mute(before, src);
+}
+
+XMLNode&
+Session::GlobalMuteStateCommand::get_state()
+{
+ XMLNode& node = GlobalRouteStateCommand::get_state();
+ node.add_property ("type", "mute");
+ return node;
+}
+
+// record enable
+Session::GlobalRecordEnableStateCommand::GlobalRecordEnableStateCommand(Session &sess, void *src)
+ : GlobalRouteStateCommand (sess, src)
+{
+ after = before = sess.get_global_route_boolean(&Route::record_enabled);
+}
+
+Session::GlobalRecordEnableStateCommand::GlobalRecordEnableStateCommand (Session& sess, const XMLNode& node)
+ : Session::GlobalRouteStateCommand (sess, node)
+{
+}
+
+void
+Session::GlobalRecordEnableStateCommand::mark()
+{
+ after = sess.get_global_route_boolean(&Route::record_enabled);
+}
+
+void
+Session::GlobalRecordEnableStateCommand::operator()()
+{
+ sess.set_global_record_enable(after, src);
+}
+
+void
+Session::GlobalRecordEnableStateCommand::undo()
+{
+ sess.set_global_record_enable(before, src);
+}
+
+XMLNode&
+Session::GlobalRecordEnableStateCommand::get_state()
+{
+ XMLNode& node = GlobalRouteStateCommand::get_state();
+ node.add_property ("type", "rec-enable");
+ return node;
+}
+
+// metering
+Session::GlobalMeteringStateCommand::GlobalMeteringStateCommand(Session &s, void *p)
+ : sess (s), src (p)
+{
+ after = before = sess.get_global_route_metering();
+}
+
+Session::GlobalMeteringStateCommand::GlobalMeteringStateCommand (Session& s, const XMLNode& node)
+ : sess (s), src (this)
+{
+ if (set_state (node)) {
+ throw failed_constructor();
+ }
+}
+
+void
+Session::GlobalMeteringStateCommand::mark()
+{
+ after = sess.get_global_route_metering();
+}
+
+void
+Session::GlobalMeteringStateCommand::operator()()
+{
+ sess.set_global_route_metering(after, src);
+}
+
+void
+Session::GlobalMeteringStateCommand::undo()
+{
+ sess.set_global_route_metering(before, src);
+}
+
+XMLNode&
+Session::GlobalMeteringStateCommand::get_state()
+{
+ XMLNode* node = new XMLNode (X_("GlobalRouteStateCommand"));
+ XMLNode* nbefore = new XMLNode (X_("before"));
+ XMLNode* nafter = new XMLNode (X_("after"));
+
+ for (Session::GlobalRouteMeterState::iterator x = before.begin(); x != before.end(); ++x) {
+ XMLNode* child = new XMLNode ("s");
+ boost::shared_ptr<Route> r = x->first.lock();
+
+ if (r) {
+ child->add_property (X_("id"), r->id().to_s());
+
+ const char* meterstr = 0;
+
+ switch (x->second) {
+ case MeterInput:
+ meterstr = X_("input");
+ break;
+ case MeterPreFader:
+ meterstr = X_("pre");
+ break;
+ case MeterPostFader:
+ meterstr = X_("post");
+ break;
+ default:
+ fatal << string_compose (_("programming error: %1") , "no meter state in Session::GlobalMeteringStateCommand::get_state") << endmsg;
+ }
+
+ child->add_property (X_("meter"), meterstr);
+ nbefore->add_child_nocopy (*child);
+ }
+ }
+
+ for (Session::GlobalRouteMeterState::iterator x = after.begin(); x != after.end(); ++x) {
+ XMLNode* child = new XMLNode ("s");
+ boost::shared_ptr<Route> r = x->first.lock();
+
+ if (r) {
+ child->add_property (X_("id"), r->id().to_s());
+
+ const char* meterstr;
+
+ switch (x->second) {
+ case MeterInput:
+ meterstr = X_("input");
+ break;
+ case MeterPreFader:
+ meterstr = X_("pre");
+ break;
+ case MeterPostFader:
+ meterstr = X_("post");
+ break;
+ default: meterstr = "";
+ }
+
+ child->add_property (X_("meter"), meterstr);
+ nafter->add_child_nocopy (*child);
+ }
+ }
+
+ node->add_child_nocopy (*nbefore);
+ node->add_child_nocopy (*nafter);
+
+ node->add_property ("type", "metering");
+
+ return *node;
+}
+
+int
+Session::GlobalMeteringStateCommand::set_state (const XMLNode& node)
+{
+ GlobalRouteBooleanState states;
+ XMLNodeList nlist;
+ const XMLProperty* prop;
+ XMLNode* child;
+ XMLNodeConstIterator niter;
+ int loop;
+
+ before.clear ();
+ after.clear ();
+
+ for (loop = 0; loop < 2; ++loop) {
+
+ const char *str;
+
+ if (loop) {
+ str = "after";
+ } else {
+ str = "before";
+ }
+
+ if ((child = node.child (str)) == 0) {
+ warning << string_compose (_("global route meter state command has no \"%1\" node, ignoring entire command"), str) << endmsg;
+ return -1;
+ }
+
+ nlist = child->children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ RouteMeterState rms;
+ boost::shared_ptr<Route> route;
+ ID id;
+
+ prop = (*niter)->property ("id");
+ id = prop->value ();
+
+ if ((route = sess.route_by_id (id)) == 0) {
+ warning << string_compose (_("cannot find track/bus \"%1\" while rebuilding a global route state command, ignored"), id.to_s()) << endmsg;
+ continue;
+ }
+
+ rms.first = boost::weak_ptr<Route> (route);
+
+ prop = (*niter)->property ("meter");
+
+ if (prop->value() == X_("pre")) {
+ rms.second = MeterPreFader;
+ } else if (prop->value() == X_("post")) {
+ rms.second = MeterPostFader;
+ } else {
+ rms.second = MeterInput;
+ }
+
+ if (loop) {
+ after.push_back (rms);
+ } else {
+ before.push_back (rms);
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/libs/ardour/session_directory.cc b/libs/ardour/session_directory.cc
new file mode 100644
index 0000000000..d5f2ddc1ba
--- /dev/null
+++ b/libs/ardour/session_directory.cc
@@ -0,0 +1,148 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <pbd/error.h>
+#include <pbd/compose.h>
+#include <pbd/filesystem.h>
+
+#include <ardour/directory_names.h>
+#include <ardour/session_directory.h>
+#include <ardour/utils.h>
+
+#include "i18n.h"
+
+namespace ARDOUR {
+
+using namespace PBD::sys;
+
+SessionDirectory::SessionDirectory (const path& session_path)
+ : m_root_path(session_path)
+{
+
+}
+
+bool
+SessionDirectory::create ()
+{
+ bool is_new = false;
+
+ vector<path> sub_dirs = sub_directories ();
+ for (vector<path>::const_iterator i = sub_dirs.begin(); i != sub_dirs.end(); ++i)
+ {
+ try
+ {
+ if(create_directories(*i)) is_new = true;
+ }
+ catch (PBD::sys::filesystem_error& ex)
+ {
+ // log the error
+ PBD::error << string_compose(_("Cannot create Session directory at path %1 Error: %2"), (*i).to_string(), ex.what()) << endmsg;
+
+ // and rethrow
+ throw ex;
+ }
+ }
+
+ return is_new;
+}
+
+bool
+SessionDirectory::is_valid () const
+{
+ if (!is_directory (m_root_path)) return false;
+
+ vector<path> sub_dirs = sub_directories ();
+
+ for (vector<path>::iterator i = sub_dirs.begin(); i != sub_dirs.end(); ++i) {
+ if (!is_directory (*i)) {
+ PBD::warning << string_compose(_("Session subdirectory does not exist at path %1"), (*i).to_string()) << endmsg;
+ return false;
+ }
+ }
+ return true;
+}
+
+const path
+SessionDirectory::old_sound_path () const
+{
+ return m_root_path / old_sound_dir_name;
+}
+
+const path
+SessionDirectory::sources_root () const
+{
+ const string legalized_root(legalize_for_path(m_root_path.leaf()));
+
+ return m_root_path / interchange_dir_name / legalized_root;
+}
+
+const path
+SessionDirectory::sound_path () const
+{
+ if(is_directory (old_sound_path ())) return old_sound_path();
+
+ // the new style sound directory
+ return sources_root() / sound_dir_name;
+}
+
+const path
+SessionDirectory::midi_path () const
+{
+ return sources_root() / midi_dir_name;
+}
+
+const path
+SessionDirectory::peak_path () const
+{
+ return m_root_path / peak_dir_name;
+}
+
+const path
+SessionDirectory::dead_sound_path () const
+{
+ return m_root_path / dead_sound_dir_name;
+}
+
+const path
+SessionDirectory::dead_midi_path () const
+{
+ return m_root_path / dead_midi_dir_name;
+}
+
+const path
+SessionDirectory::export_path () const
+{
+ return m_root_path / export_dir_name;
+}
+
+const vector<path>
+SessionDirectory::sub_directories () const
+{
+ vector<path> tmp_paths;
+
+ tmp_paths.push_back ( sound_path () );
+ tmp_paths.push_back ( midi_path () );
+ tmp_paths.push_back ( peak_path () );
+ tmp_paths.push_back ( dead_sound_path () );
+ tmp_paths.push_back ( dead_midi_path () );
+ tmp_paths.push_back ( export_path () );
+
+ return tmp_paths;
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc
new file mode 100644
index 0000000000..5fc8cd7535
--- /dev/null
+++ b/libs/ardour/session_events.cc
@@ -0,0 +1,453 @@
+/*
+ Copyright (C) 1999-2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cmath>
+#include <unistd.h>
+
+#include <ardour/timestamps.h>
+
+#include <pbd/error.h>
+#include <glibmm/thread.h>
+
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+#include <ardour/audio_diskstream.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+MultiAllocSingleReleasePool Session::Event::pool ("event", sizeof (Session::Event), 512);
+
+static const char* event_names[] = {
+ "SetTransportSpeed",
+ "SetDiskstreamSpeed",
+ "Locate",
+ "LocateRoll",
+ "LocateRollLocate",
+ "SetLoop",
+ "PunchIn",
+ "PunchOut",
+ "RangeStop",
+ "RangeLocate",
+ "Overwrite",
+ "SetSlaveSource",
+ "Audition",
+ "InputConfigurationChange",
+ "SetAudioRange",
+ "SetMusicRange",
+ "SetPlayRange",
+ "StopOnce",
+ "AutoLoop"
+};
+
+void
+Session::add_event (nframes_t frame, Event::Type type, nframes_t target_frame)
+{
+ Event* ev = new Event (type, Event::Add, frame, target_frame, 0);
+ queue_event (ev);
+}
+
+void
+Session::remove_event (nframes_t frame, Event::Type type)
+{
+ Event* ev = new Event (type, Event::Remove, frame, 0, 0);
+ queue_event (ev);
+}
+
+void
+Session::replace_event (Event::Type type, nframes_t frame, nframes_t target)
+{
+ Event* ev = new Event (type, Event::Replace, frame, target, 0);
+ queue_event (ev);
+}
+
+void
+Session::clear_events (Event::Type type)
+{
+ Event* ev = new Event (type, Event::Clear, 0, 0, 0);
+ queue_event (ev);
+}
+
+
+void
+Session::dump_events () const
+{
+ cerr << "EVENT DUMP" << endl;
+ for (Events::const_iterator i = events.begin(); i != events.end(); ++i) {
+ cerr << "\tat " << (*i)->action_frame << ' ' << (*i)->type << " target = " << (*i)->target_frame << endl;
+ }
+ cerr << "Next event: ";
+
+ if ((Events::const_iterator) next_event == events.end()) {
+ cerr << "none" << endl;
+ } else {
+ cerr << "at " << (*next_event)->action_frame << ' '
+ << (*next_event)->type << " target = "
+ << (*next_event)->target_frame << endl;
+ }
+ cerr << "Immediate events pending:\n";
+ for (Events::const_iterator i = immediate_events.begin(); i != immediate_events.end(); ++i) {
+ cerr << "\tat " << (*i)->action_frame << ' ' << (*i)->type << " target = " << (*i)->target_frame << endl;
+ }
+ cerr << "END EVENT_DUMP" << endl;
+}
+
+void
+Session::queue_event (Event* ev)
+{
+ if (_state_of_the_state & Loading) {
+ merge_event (ev);
+ } else {
+ pending_events.write (&ev, 1);
+ }
+}
+
+void
+Session::merge_event (Event* ev)
+{
+ switch (ev->action) {
+ case Event::Remove:
+ _remove_event (ev);
+ delete ev;
+ return;
+
+ case Event::Replace:
+ _replace_event (ev);
+ return;
+
+ case Event::Clear:
+ _clear_event_type (ev->type);
+ delete ev;
+ return;
+
+ case Event::Add:
+ break;
+ }
+
+ /* try to handle immediate events right here */
+
+ if (ev->action_frame == 0) {
+ process_event (ev);
+ return;
+ }
+
+ switch (ev->type) {
+ case Event::AutoLoop:
+ case Event::StopOnce:
+ _clear_event_type (ev->type);
+ break;
+
+ default:
+ for (Events::iterator i = events.begin(); i != events.end(); ++i) {
+ if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) {
+ error << string_compose(_("Session: cannot have two events of type %1 at the same frame (%2)."),
+ event_names[ev->type], ev->action_frame) << endmsg;
+ return;
+ }
+ }
+ }
+
+ events.insert (events.begin(), ev);
+ events.sort (Event::compare);
+ next_event = events.begin();
+ set_next_event ();
+}
+
+/** @return true when @a ev is deleted. */
+bool
+Session::_replace_event (Event* ev)
+{
+ bool ret = false;
+ Events::iterator i;
+
+ /* private, used only for events that can only exist once in the queue */
+
+ for (i = events.begin(); i != events.end(); ++i) {
+ if ((*i)->type == ev->type) {
+ (*i)->action_frame = ev->action_frame;
+ (*i)->target_frame = ev->target_frame;
+ if ((*i) == ev) {
+ ret = true;
+ }
+ delete ev;
+ break;
+ }
+ }
+
+ if (i == events.end()) {
+ events.insert (events.begin(), ev);
+ }
+
+ events.sort (Event::compare);
+ next_event = events.end();
+ set_next_event ();
+
+ return ret;
+}
+
+/** @return true when @a ev is deleted. */
+bool
+Session::_remove_event (Session::Event* ev)
+{
+ bool ret = false;
+ Events::iterator i;
+
+ for (i = events.begin(); i != events.end(); ++i) {
+ if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) {
+ if ((*i) == ev) {
+ ret = true;
+ }
+
+ delete *i;
+ if (i == next_event) {
+ ++next_event;
+ }
+ events.erase (i);
+ break;
+ }
+ }
+
+ if (i != events.end()) {
+ set_next_event ();
+ }
+
+ return ret;
+}
+
+void
+Session::_clear_event_type (Event::Type type)
+{
+ Events::iterator i, tmp;
+
+ for (i = events.begin(); i != events.end(); ) {
+
+ tmp = i;
+ ++tmp;
+
+ if ((*i)->type == type) {
+ delete *i;
+ if (i == next_event) {
+ ++next_event;
+ }
+ events.erase (i);
+ }
+
+ i = tmp;
+ }
+
+ for (i = immediate_events.begin(); i != immediate_events.end(); ) {
+
+ tmp = i;
+ ++tmp;
+
+ if ((*i)->type == type) {
+ delete *i;
+ immediate_events.erase (i);
+ }
+
+ i = tmp;
+ }
+
+ set_next_event ();
+}
+
+void
+Session::set_next_event ()
+{
+ if (events.empty()) {
+ next_event = events.end();
+ return;
+ }
+
+ if (next_event == events.end()) {
+ next_event = events.begin();
+ }
+
+ if ((*next_event)->action_frame > _transport_frame) {
+ next_event = events.begin();
+ }
+
+ for (; next_event != events.end(); ++next_event) {
+ if ((*next_event)->action_frame >= _transport_frame) {
+ break;
+ }
+ }
+}
+
+void
+Session::process_event (Event* ev)
+{
+ bool remove = true;
+ bool del = true;
+
+ /* if we're in the middle of a state change (i.e. waiting
+ for the butler thread to complete the non-realtime
+ part of the change), we'll just have to queue this
+ event for a time when the change is complete.
+ */
+
+ if (non_realtime_work_pending()) {
+
+ /* except locates, which we have the capability to handle */
+
+ if (ev->type != Event::Locate) {
+ immediate_events.insert (immediate_events.end(), ev);
+ _remove_event (ev);
+ return;
+ }
+ }
+
+ //printf("Processing event: %s\n", event_names[ev->type]);
+
+ switch (ev->type) {
+ case Event::SetLoop:
+ set_play_loop (ev->yes_or_no);
+ break;
+
+ case Event::AutoLoop:
+ if (play_loop) {
+ start_locate (ev->target_frame, true, false, Config->get_seamless_loop());
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case Event::Locate:
+ if (ev->yes_or_no) {
+ // cerr << "forced locate to " << ev->target_frame << endl;
+ locate (ev->target_frame, false, true, false);
+ } else {
+ // cerr << "soft locate to " << ev->target_frame << endl;
+ start_locate (ev->target_frame, false, true, false);
+ }
+ _send_smpte_update = true;
+ break;
+
+ case Event::LocateRoll:
+ if (ev->yes_or_no) {
+ // cerr << "forced locate to+roll " << ev->target_frame << endl;
+ locate (ev->target_frame, true, true, false);
+ } else {
+ // cerr << "soft locate to+roll " << ev->target_frame << endl;
+ start_locate (ev->target_frame, true, true, false);
+ }
+ _send_smpte_update = true;
+ break;
+
+ case Event::LocateRollLocate:
+ // locate is handled by ::request_roll_at_and_return()
+ _requested_return_frame = ev->target_frame;
+ cerr << "Set RRF " << ev->target_frame << endl;
+ request_locate (ev->target2_frame, true);
+ break;
+
+
+ case Event::SetTransportSpeed:
+ set_transport_speed (ev->speed, ev->yes_or_no);
+ break;
+
+ case Event::PunchIn:
+ // cerr << "PunchIN at " << transport_frame() << endl;
+ if (Config->get_punch_in() && record_status() == Enabled) {
+ enable_record ();
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case Event::PunchOut:
+ // cerr << "PunchOUT at " << transport_frame() << endl;
+ if (Config->get_punch_out()) {
+ step_back_from_record ();
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case Event::StopOnce:
+ if (!non_realtime_work_pending()) {
+ stop_transport (ev->yes_or_no);
+ _clear_event_type (Event::StopOnce);
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case Event::RangeStop:
+ if (!non_realtime_work_pending()) {
+ stop_transport (ev->yes_or_no);
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case Event::RangeLocate:
+ start_locate (ev->target_frame, true, true, false);
+ remove = false;
+ del = false;
+ break;
+
+ case Event::Overwrite:
+ overwrite_some_buffers (static_cast<Diskstream*>(ev->ptr));
+ break;
+
+ case Event::SetDiskstreamSpeed:
+ set_diskstream_speed (static_cast<Diskstream*> (ev->ptr), ev->speed);
+ break;
+
+ case Event::SetSlaveSource:
+ set_slave_source (ev->slave);
+ break;
+
+ case Event::Audition:
+ set_audition (ev->region);
+ // drop reference to region
+ ev->region.reset ();
+ break;
+
+ case Event::InputConfigurationChange:
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportInputChange);
+ schedule_butler_transport_work ();
+ break;
+
+ case Event::SetAudioRange:
+ current_audio_range = ev->audio_range;
+ setup_auto_play ();
+ break;
+
+ case Event::SetPlayRange:
+ set_play_range (ev->yes_or_no);
+ break;
+
+ default:
+ fatal << string_compose(_("Programming error: illegal event type in process_event (%1)"), ev->type) << endmsg;
+ /*NOTREACHED*/
+ break;
+ };
+
+ if (remove) {
+ del = del && !_remove_event (ev);
+ }
+
+ if (del) {
+ delete ev;
+ }
+}
diff --git a/libs/ardour/session_export.cc b/libs/ardour/session_export.cc
new file mode 100644
index 0000000000..49b6d9b150
--- /dev/null
+++ b/libs/ardour/session_export.cc
@@ -0,0 +1,652 @@
+/*
+ Copyright (C) 1999-2002 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.
+
+*/
+
+/* see gdither.cc for why we have to do this */
+
+#define _ISOC9X_SOURCE 1
+#define _ISOC99_SOURCE 1
+#include <cmath>
+#undef _ISOC99_SOURCE
+#undef _ISOC9X_SOURCE
+#undef __USE_SVID
+#define __USE_SVID 1
+#include <cstdlib>
+#undef __USE_SVID
+
+#include <unistd.h>
+#include <inttypes.h>
+#include <float.h>
+
+#include <sigc++/bind.h>
+
+#include <pbd/error.h>
+#include <glibmm/thread.h>
+
+#include <ardour/gdither.h>
+#include <ardour/timestamps.h>
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+#include <ardour/export.h>
+#include <ardour/sndfile_helpers.h>
+#include <ardour/port.h>
+#include <ardour/audio_port.h>
+#include <ardour/audioengine.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/panner.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+static int
+convert_spec_to_info (ExportSpecification& spec, SF_INFO& sfinfo)
+{
+ if (spec.path.length() == 0) {
+ error << _("Export: no output file specified") << endmsg;
+ return -1;
+ }
+
+ /* XXX add checks that the directory path exists, and also
+ check if we are overwriting an existing file...
+ */
+
+ sfinfo.format = spec.format;
+ sfinfo.samplerate = spec.sample_rate;
+ sfinfo.frames = spec.end_frame - spec.start_frame + 1;
+ sfinfo.channels = min (spec.channels, 2U);
+
+ return 0;
+}
+
+ExportSpecification::ExportSpecification ()
+{
+ init ();
+}
+
+ExportSpecification::~ExportSpecification ()
+{
+ clear ();
+}
+
+void
+ExportSpecification::init ()
+{
+ src_state = 0;
+ pos = 0;
+ total_frames = 0;
+ out = 0;
+ channels = 0;
+ running = false;
+ stop = false;
+ progress = 0.0;
+ status = 0;
+ dither = 0;
+ start_frame = 0;
+ end_frame = 0;
+ dataF = 0;
+ dataF2 = 0;
+ leftoverF = 0;
+ max_leftover_frames = 0;
+ leftover_frames = 0;
+ output_data = 0;
+ out_samples_max = 0;
+ data_width = 0;
+ do_freewheel = false;
+}
+
+void
+ExportSpecification::clear ()
+{
+ if (out) {
+ sf_close (out);
+ out = 0;
+ }
+
+ if (src_state) {
+ src_delete (src_state);
+ src_state = 0;
+ }
+
+ if (dither) {
+ gdither_free (dither);
+ dither = 0;
+ }
+
+ if (output_data) {
+ free (output_data);
+ output_data = 0;
+ }
+ if (dataF) {
+ delete [] dataF;
+ dataF = 0;
+ }
+ if (dataF2) {
+ delete [] dataF2;
+ dataF2 = 0;
+ }
+ if (leftoverF) {
+ delete [] leftoverF;
+ leftoverF = 0;
+ }
+
+ freewheel_connection.disconnect ();
+
+ init ();
+}
+
+int
+ExportSpecification::prepare (nframes_t blocksize, nframes_t frate)
+{
+ char errbuf[256];
+ GDitherSize dither_size;
+
+ frame_rate = frate;
+
+ if (channels == 0) {
+ error << _("illegal channel count in export specification") << endmsg;
+ return -1;
+ }
+
+ if (start_frame >= end_frame) {
+ error << _("illegal frame range in export specification") << endmsg;
+ return -1;
+ }
+
+ if ((data_width = sndfile_data_width(format)) == 0) {
+ error << _("Bad data width size. Report me!") << endmsg;
+ return -1;
+ }
+
+ switch (data_width) {
+ case 8:
+ dither_size = GDither8bit;
+ break;
+
+ case 16:
+ dither_size = GDither16bit;
+ break;
+
+ case 24:
+ dither_size = GDither32bit;
+ break;
+
+ default:
+ dither_size = GDitherFloat;
+ break;
+ }
+
+ if (convert_spec_to_info (*this, sfinfo)) {
+ return -1;
+ }
+
+ /* XXX make sure we have enough disk space for the output */
+
+ if ((out = sf_open (path.c_str(), SFM_WRITE, &sfinfo)) == 0) {
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ error << string_compose(_("Export: cannot open output file \"%1\" (%2)"), path, errbuf) << endmsg;
+ return -1;
+ }
+
+ dataF = new float[blocksize * channels];
+
+ if (sample_rate != frame_rate) {
+ int err;
+
+ if ((src_state = src_new (src_quality, channels, &err)) == 0) {
+ error << string_compose (_("cannot initialize sample rate conversion: %1"), src_strerror (err)) << endmsg;
+ return -1;
+ }
+
+ src_data.src_ratio = sample_rate / (double) frame_rate;
+ out_samples_max = (nframes_t) ceil (blocksize * src_data.src_ratio * channels);
+ dataF2 = new float[out_samples_max];
+
+ max_leftover_frames = 4 * blocksize;
+ leftoverF = new float[max_leftover_frames * channels];
+ leftover_frames = 0;
+
+ } else {
+ out_samples_max = blocksize * channels;
+ }
+
+ dither = gdither_new (dither_type, channels, dither_size, data_width);
+
+ /* allocate buffers where dithering and output will occur */
+
+ switch (data_width) {
+ case 8:
+ sample_bytes = 1;
+ break;
+
+ case 16:
+ sample_bytes = 2;
+ break;
+
+ case 24:
+ case 32:
+ sample_bytes = 4;
+ break;
+
+ default:
+ sample_bytes = 0; // float format
+ break;
+ }
+
+ if (sample_bytes) {
+ output_data = (void*) malloc (sample_bytes * out_samples_max);
+ }
+
+ return 0;
+}
+
+int
+ExportSpecification::process (nframes_t nframes)
+{
+ float* float_buffer = 0;
+ uint32_t chn;
+ uint32_t x;
+ uint32_t i;
+ sf_count_t written;
+ char errbuf[256];
+ nframes_t to_write = 0;
+ int cnt = 0;
+
+ do {
+
+ /* now do sample rate conversion */
+
+ if (sample_rate != frame_rate) {
+
+ int err;
+
+ src_data.output_frames = out_samples_max / channels;
+ src_data.end_of_input = ((pos + nframes) >= end_frame);
+ src_data.data_out = dataF2;
+
+ if (leftover_frames > 0) {
+
+ /* input data will be in leftoverF rather than dataF */
+
+ src_data.data_in = leftoverF;
+
+ if (cnt == 0) {
+
+ /* first time, append new data from dataF into the leftoverF buffer */
+
+ memcpy (leftoverF + (leftover_frames * channels), dataF, nframes * channels * sizeof(float));
+ src_data.input_frames = nframes + leftover_frames;
+ } else {
+
+ /* otherwise, just use whatever is still left in leftoverF; the contents
+ were adjusted using memmove() right after the last SRC call (see
+ below)
+ */
+
+ src_data.input_frames = leftover_frames;
+ }
+
+ } else {
+
+ src_data.data_in = dataF;
+ src_data.input_frames = nframes;
+
+ }
+
+ ++cnt;
+
+ if ((err = src_process (src_state, &src_data)) != 0) {
+ error << string_compose (_("an error occured during sample rate conversion: %1"),
+ src_strerror (err))
+ << endmsg;
+ return -1;
+ }
+
+ to_write = src_data.output_frames_gen;
+ leftover_frames = src_data.input_frames - src_data.input_frames_used;
+
+ if (leftover_frames > 0) {
+ if (leftover_frames > max_leftover_frames) {
+ error << _("warning, leftover frames overflowed, glitches might occur in output") << endmsg;
+ leftover_frames = max_leftover_frames;
+ }
+ memmove (leftoverF, (char *) (src_data.data_in + (src_data.input_frames_used * channels)),
+ leftover_frames * channels * sizeof(float));
+ }
+
+ float_buffer = dataF2;
+
+ } else {
+
+ /* no SRC, keep it simple */
+
+ to_write = nframes;
+ leftover_frames = 0;
+ float_buffer = dataF;
+ }
+
+ if (output_data) {
+ memset (output_data, 0, sample_bytes * to_write * channels);
+ }
+
+ switch (data_width) {
+ case 8:
+ case 16:
+ case 24:
+ for (chn = 0; chn < channels; ++chn) {
+ gdither_runf (dither, chn, to_write, float_buffer, output_data);
+ }
+ break;
+
+ case 32:
+ for (chn = 0; chn < channels; ++chn) {
+
+ int *ob = (int *) output_data;
+ const double int_max = (float) INT_MAX;
+ const double int_min = (float) INT_MIN;
+
+ for (x = 0; x < to_write; ++x) {
+ i = chn + (x * channels);
+
+ if (float_buffer[i] > 1.0f) {
+ ob[i] = INT_MAX;
+ } else if (float_buffer[i] < -1.0f) {
+ ob[i] = INT_MIN;
+ } else {
+ if (float_buffer[i] >= 0.0f) {
+ ob[i] = lrintf (int_max * float_buffer[i]);
+ } else {
+ ob[i] = - lrintf (int_min * float_buffer[i]);
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ for (x = 0; x < to_write * channels; ++x) {
+ if (float_buffer[x] > 1.0f) {
+ float_buffer[x] = 1.0f;
+ } else if (float_buffer[x] < -1.0f) {
+ float_buffer[x] = -1.0f;
+ }
+ }
+ break;
+ }
+
+ /* and export to disk */
+
+ switch (data_width) {
+ case 8:
+ /* XXXX no way to deliver 8 bit audio to libsndfile */
+ written = to_write;
+ break;
+
+ case 16:
+ written = sf_writef_short (out, (short*) output_data, to_write);
+ break;
+
+ case 24:
+ case 32:
+ written = sf_writef_int (out, (int*) output_data, to_write);
+ break;
+
+ default:
+ written = sf_writef_float (out, float_buffer, to_write);
+ break;
+ }
+
+ if ((nframes_t) written != to_write) {
+ sf_error_str (out, errbuf, sizeof (errbuf) - 1);
+ error << string_compose(_("Export: could not write data to output file (%1)"), errbuf) << endmsg;
+ return -1;
+ }
+
+
+ } while (leftover_frames >= nframes);
+
+ return 0;
+}
+
+int
+Session::start_export (ExportSpecification& spec)
+{
+ if (!_engine.connected()) {
+ return -1;
+ }
+
+ if (spec.prepare (current_block_size, frame_rate())) {
+ return -1;
+ }
+
+ spec.pos = spec.start_frame;
+ spec.end_frame = spec.end_frame;
+ spec.total_frames = spec.end_frame - spec.start_frame;
+ spec.running = true;
+ spec.do_freewheel = false; /* force a call to ::prepare_to_export() before proceeding to normal operation */
+
+ spec.freewheel_connection = _engine.Freewheel.connect (sigc::bind (mem_fun (*this, &Session::process_export), &spec));
+
+ return _engine.freewheel (true);
+}
+
+int
+Session::stop_export (ExportSpecification& spec)
+{
+ /* don't stop freewheeling but do stop paying attention to it for now */
+
+ spec.freewheel_connection.disconnect ();
+ spec.clear (); /* resets running/stop etc */
+
+ return 0;
+}
+
+int
+Session::prepare_to_export (ExportSpecification& spec)
+{
+ int ret = -1;
+
+ wait_till_butler_finished ();
+
+ /* take everyone out of awrite to avoid disasters */
+
+ {
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->protect_automation ();
+ }
+ }
+
+ /* get everyone to the right position */
+
+ {
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)-> seek (spec.start_frame, true)) {
+ error << string_compose (_("%1: cannot seek to %2 for export"),
+ (*i)->name(), spec.start_frame)
+ << endmsg;
+ goto out;
+ }
+ }
+ }
+
+ /* make sure we are actually rolling */
+
+ if (get_record_enabled()) {
+ disable_record (false);
+ }
+
+ _exporting = true;
+
+ /* no slaving */
+
+ post_export_slave = Config->get_slave_source ();
+ post_export_position = _transport_frame;
+
+ Config->set_slave_source (None);
+
+ /* get transport ready */
+
+ set_transport_speed (1.0, false);
+ butler_transport_work ();
+ g_atomic_int_set (&butler_should_do_transport_work, 0);
+ post_transport ();
+
+ /* we are ready to go ... */
+
+ ret = 0;
+
+ out:
+ return ret;
+}
+
+int
+Session::process_export (nframes_t nframes, ExportSpecification* spec)
+{
+ uint32_t chn;
+ uint32_t x;
+ int ret = -1;
+ nframes_t this_nframes;
+
+ /* This is not required to be RT-safe because we are running while freewheeling */
+
+ if (spec->do_freewheel == false) {
+
+ /* first time in export function: get set up */
+
+ if (prepare_to_export (*spec)) {
+ spec->running = false;
+ spec->status = -1;
+ return -1;
+ }
+
+ spec->do_freewheel = true;
+ }
+
+ if (!_exporting) {
+ /* finished, but still freewheeling */
+ process_without_events (nframes);
+ return 0;
+ }
+
+ if (!spec->running || spec->stop || (this_nframes = min ((spec->end_frame - spec->pos), nframes)) == 0) {
+ process_without_events (nframes);
+ return stop_export (*spec);
+ }
+
+ /* make sure we've caught up with disk i/o, since
+ we're running faster than realtime c/o JACK.
+ */
+
+ wait_till_butler_finished ();
+
+ /* do the usual stuff */
+
+ process_without_events (nframes);
+
+ /* and now export the results */
+
+ nframes = this_nframes;
+
+ memset (spec->dataF, 0, sizeof (spec->dataF[0]) * nframes * spec->channels);
+
+ /* foreach output channel ... */
+
+ for (chn = 0; chn < spec->channels; ++chn) {
+
+ ExportPortMap::iterator mi = spec->port_map.find (chn);
+
+ if (mi == spec->port_map.end()) {
+ /* no ports exported to this channel */
+ continue;
+ }
+
+ vector<PortChannelPair>& mapped_ports ((*mi).second);
+
+ for (vector<PortChannelPair>::iterator t = mapped_ports.begin(); t != mapped_ports.end(); ++t) {
+
+ /* OK, this port's output is supposed to appear on this channel
+ */
+
+ AudioPort* const aport = dynamic_cast<AudioPort*>((*t).first);
+ MidiPort* const mport = dynamic_cast<MidiPort*>((*t).first);
+ if (aport != 0) {
+ Sample* port_buffer = aport->get_audio_buffer().data();
+
+ /* now interleave the data from the channel into the float buffer */
+
+ for (x = 0; x < nframes; ++x) {
+ spec->dataF[chn+(x*spec->channels)] += (float) port_buffer[x];
+ }
+ } else if (mport != 0) {
+ cerr << "EXPORT MIDI PORT" << endl;
+ }
+ }
+ }
+
+ if (spec->process (nframes)) {
+ goto out;
+ }
+
+ spec->pos += nframes;
+ spec->progress = 1.0 - (((float) spec->end_frame - spec->pos) / spec->total_frames);
+
+ /* and we're good to go */
+
+ ret = 0;
+
+ out:
+ if (ret) {
+ sf_close (spec->out);
+ spec->out = 0;
+ unlink (spec->path.c_str());
+ spec->running = false;
+ spec->status = ret;
+ _exporting = false;
+ }
+
+ return ret;
+}
+
+void
+Session::finalize_audio_export ()
+{
+ _engine.freewheel (false);
+ _exporting = false;
+
+ /* can't use stop_transport() here because we need
+ an immediate halt and don't require all the declick
+ stuff that stop_transport() implements.
+ */
+
+ realtime_stop (true);
+ schedule_butler_transport_work ();
+
+ /* restart slaving */
+
+ if (post_export_slave != None) {
+ Config->set_slave_source (post_export_slave);
+ } else {
+ locate (post_export_position, false, false, false);
+ }
+}
diff --git a/libs/ardour/session_feedback.cc b/libs/ardour/session_feedback.cc
new file mode 100644
index 0000000000..2bb9886f50
--- /dev/null
+++ b/libs/ardour/session_feedback.cc
@@ -0,0 +1,49 @@
+/*
+ Copyright (C) 2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+#include <cmath>
+#include <cerrno>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#include <midi++/types.h>
+#include <midi++/port.h>
+#include <midi++/manager.h>
+
+#include <glibmm/thread.h>
+
+#include <pbd/pthread_utils.h>
+
+#include <ardour/configuration.h>
+#include <ardour/audioengine.h>
+#include <ardour/session.h>
+#include <ardour/audio_track.h>
+#include <ardour/audio_diskstream.h>
+#include <control_protocol/control_protocol.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+//using namespace sigc;
+
+
+
diff --git a/libs/ardour/session_midi.cc b/libs/ardour/session_midi.cc
new file mode 100644
index 0000000000..b4938636a2
--- /dev/null
+++ b/libs/ardour/session_midi.cc
@@ -0,0 +1,1211 @@
+
+/*
+ Copyright (C) 1999-2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+#include <cmath>
+#include <cerrno>
+#include <cassert>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include <midi++/mmc.h>
+#include <midi++/port.h>
+#include <midi++/manager.h>
+#include <pbd/error.h>
+#include <glibmm/thread.h>
+#include <pbd/pthread_utils.h>
+
+#include <ardour/configuration.h>
+#include <ardour/audioengine.h>
+#include <ardour/session.h>
+#include <ardour/audio_track.h>
+#include <ardour/midi_track.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/slave.h>
+#include <ardour/cycles.h>
+#include <ardour/smpte.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+using namespace MIDI;
+
+MachineControl::CommandSignature MMC_CommandSignature;
+MachineControl::ResponseSignature MMC_ResponseSignature;
+
+
+void
+Session::midi_panic()
+{
+ {
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ MidiTrack *track = dynamic_cast<MidiTrack*>((*i).get());
+ if (track != 0) {
+ track->midi_panic();
+ }
+ }
+ }
+}
+
+int
+Session::use_config_midi_ports ()
+{
+ string port_name;
+
+ if (default_mmc_port) {
+ set_mmc_port (default_mmc_port->name());
+ } else {
+ set_mmc_port ("");
+ }
+
+ if (default_mtc_port) {
+ set_mtc_port (default_mtc_port->name());
+ } else {
+ set_mtc_port ("");
+ }
+
+ if (default_midi_port) {
+ set_midi_port (default_midi_port->name());
+ } else {
+ set_midi_port ("");
+ }
+
+ return 0;
+}
+
+
+/***********************************************************************
+ MTC, MMC, etc.
+**********************************************************************/
+
+int
+Session::set_mtc_port (string port_tag)
+{
+ MTC_Slave *ms;
+
+ if (port_tag.length() == 0) {
+
+ if (_slave && ((ms = dynamic_cast<MTC_Slave*> (_slave)) != 0)) {
+ error << _("Ardour is slaved to MTC - port cannot be reset") << endmsg;
+ return -1;
+ }
+
+ if (_mtc_port == 0) {
+ return 0;
+ }
+
+ _mtc_port = 0;
+ goto out;
+ }
+
+ MIDI::Port* port;
+
+ if ((port = MIDI::Manager::instance()->port (port_tag)) == 0) {
+ error << string_compose (_("unknown port %1 requested for MTC"), port_tag) << endl;
+ return -1;
+ }
+
+ _mtc_port = port;
+
+ if (_slave && ((ms = dynamic_cast<MTC_Slave*> (_slave)) != 0)) {
+ ms->rebind (*port);
+ }
+
+ Config->set_mtc_port_name (port_tag);
+
+ out:
+ MTC_PortChanged(); /* EMIT SIGNAL */
+ change_midi_ports ();
+ set_dirty();
+ return 0;
+}
+
+void
+Session::set_mmc_receive_device_id (uint32_t device_id)
+{
+ if (mmc) {
+ mmc->set_receive_device_id (device_id);
+ }
+}
+
+void
+Session::set_mmc_send_device_id (uint32_t device_id)
+{
+ if (mmc) {
+ mmc->set_send_device_id (device_id);
+ }
+}
+
+int
+Session::set_mmc_port (string port_tag)
+{
+ MIDI::byte old_recv_device_id = 0;
+ MIDI::byte old_send_device_id = 0;
+ bool reset_id = false;
+
+ if (port_tag.length() == 0) {
+ if (_mmc_port == 0) {
+ return 0;
+ }
+ _mmc_port = 0;
+ goto out;
+ }
+
+ MIDI::Port* port;
+
+ if ((port = MIDI::Manager::instance()->port (port_tag)) == 0) {
+ return -1;
+ }
+
+ _mmc_port = port;
+
+ if (mmc) {
+ old_recv_device_id = mmc->receive_device_id();
+ old_recv_device_id = mmc->send_device_id();
+ reset_id = true;
+ delete mmc;
+ }
+
+ mmc = new MIDI::MachineControl (*_mmc_port, 1.0,
+ MMC_CommandSignature,
+ MMC_ResponseSignature);
+
+ if (reset_id) {
+ mmc->set_receive_device_id (old_recv_device_id);
+ mmc->set_send_device_id (old_send_device_id);
+ }
+
+ mmc->Play.connect
+ (mem_fun (*this, &Session::mmc_deferred_play));
+ mmc->DeferredPlay.connect
+ (mem_fun (*this, &Session::mmc_deferred_play));
+ mmc->Stop.connect
+ (mem_fun (*this, &Session::mmc_stop));
+ mmc->FastForward.connect
+ (mem_fun (*this, &Session::mmc_fast_forward));
+ mmc->Rewind.connect
+ (mem_fun (*this, &Session::mmc_rewind));
+ mmc->Pause.connect
+ (mem_fun (*this, &Session::mmc_pause));
+ mmc->RecordPause.connect
+ (mem_fun (*this, &Session::mmc_record_pause));
+ mmc->RecordStrobe.connect
+ (mem_fun (*this, &Session::mmc_record_strobe));
+ mmc->RecordExit.connect
+ (mem_fun (*this, &Session::mmc_record_exit));
+ mmc->Locate.connect
+ (mem_fun (*this, &Session::mmc_locate));
+ mmc->Step.connect
+ (mem_fun (*this, &Session::mmc_step));
+ mmc->Shuttle.connect
+ (mem_fun (*this, &Session::mmc_shuttle));
+ mmc->TrackRecordStatusChange.connect
+ (mem_fun (*this, &Session::mmc_record_enable));
+
+
+ /* also handle MIDI SPP because its so common */
+
+ _mmc_port->input()->start.connect (mem_fun (*this, &Session::spp_start));
+ _mmc_port->input()->contineu.connect (mem_fun (*this, &Session::spp_continue));
+ _mmc_port->input()->stop.connect (mem_fun (*this, &Session::spp_stop));
+
+ Config->set_mmc_port_name (port_tag);
+
+ out:
+ MMC_PortChanged(); /* EMIT SIGNAL */
+ change_midi_ports ();
+ set_dirty();
+ return 0;
+}
+
+int
+Session::set_midi_port (string port_tag)
+{
+#if 0
+ if (port_tag.length() == 0) {
+ if (_midi_port == 0) {
+ return 0;
+ }
+ _midi_port = 0;
+ goto out;
+ }
+
+ MIDI::Port* port;
+
+ if ((port = MIDI::Manager::instance()->port (port_tag)) == 0) {
+ return -1;
+ }
+
+ _midi_port = port;
+
+ /* XXX need something to forward this to control protocols ? or just
+ use the signal below
+ */
+
+ Config->set_midi_port_name (port_tag);
+
+ out:
+#endif
+ MIDI_PortChanged(); /* EMIT SIGNAL */
+ change_midi_ports ();
+ set_dirty();
+ return 0;
+}
+
+void
+Session::set_trace_midi_input (bool yn, MIDI::Port* port)
+{
+ MIDI::Parser* input_parser;
+
+ if (port) {
+ if ((input_parser = port->input()) != 0) {
+ input_parser->trace (yn, &cout, "input: ");
+ }
+ } else {
+
+ if (_mmc_port) {
+ if ((input_parser = _mmc_port->input()) != 0) {
+ input_parser->trace (yn, &cout, "input: ");
+ }
+ }
+
+ if (_mtc_port && _mtc_port != _mmc_port) {
+ if ((input_parser = _mtc_port->input()) != 0) {
+ input_parser->trace (yn, &cout, "input: ");
+ }
+ }
+
+ if (_midi_port && _midi_port != _mmc_port && _midi_port != _mtc_port ) {
+ if ((input_parser = _midi_port->input()) != 0) {
+ input_parser->trace (yn, &cout, "input: ");
+ }
+ }
+ }
+
+ Config->set_trace_midi_input (yn);
+}
+
+void
+Session::set_trace_midi_output (bool yn, MIDI::Port* port)
+{
+ MIDI::Parser* output_parser;
+
+ if (port) {
+ if ((output_parser = port->output()) != 0) {
+ output_parser->trace (yn, &cout, "output: ");
+ }
+ } else {
+ if (_mmc_port) {
+ if ((output_parser = _mmc_port->output()) != 0) {
+ output_parser->trace (yn, &cout, "output: ");
+ }
+ }
+
+ if (_mtc_port && _mtc_port != _mmc_port) {
+ if ((output_parser = _mtc_port->output()) != 0) {
+ output_parser->trace (yn, &cout, "output: ");
+ }
+ }
+
+ if (_midi_port && _midi_port != _mmc_port && _midi_port != _mtc_port ) {
+ if ((output_parser = _midi_port->output()) != 0) {
+ output_parser->trace (yn, &cout, "output: ");
+ }
+ }
+
+ }
+
+ Config->set_trace_midi_output (yn);
+}
+
+bool
+Session::get_trace_midi_input(MIDI::Port *port)
+{
+ MIDI::Parser* input_parser;
+ if (port) {
+ if ((input_parser = port->input()) != 0) {
+ return input_parser->tracing();
+ }
+ }
+ else {
+ if (_mmc_port) {
+ if ((input_parser = _mmc_port->input()) != 0) {
+ return input_parser->tracing();
+ }
+ }
+
+ if (_mtc_port) {
+ if ((input_parser = _mtc_port->input()) != 0) {
+ return input_parser->tracing();
+ }
+ }
+
+ if (_midi_port) {
+ if ((input_parser = _midi_port->input()) != 0) {
+ return input_parser->tracing();
+ }
+ }
+ }
+
+ return false;
+}
+
+bool
+Session::get_trace_midi_output(MIDI::Port *port)
+{
+ MIDI::Parser* output_parser;
+ if (port) {
+ if ((output_parser = port->output()) != 0) {
+ return output_parser->tracing();
+ }
+ }
+ else {
+ if (_mmc_port) {
+ if ((output_parser = _mmc_port->output()) != 0) {
+ return output_parser->tracing();
+ }
+ }
+
+ if (_mtc_port) {
+ if ((output_parser = _mtc_port->output()) != 0) {
+ return output_parser->tracing();
+ }
+ }
+
+ if (_midi_port) {
+ if ((output_parser = _midi_port->output()) != 0) {
+ return output_parser->tracing();
+ }
+ }
+ }
+
+ return false;
+
+}
+
+void
+Session::setup_midi_control ()
+{
+ outbound_mtc_smpte_frame = 0;
+ next_quarter_frame_to_send = 0;
+
+ /* setup the MMC buffer */
+
+ mmc_buffer[0] = 0xf0; // SysEx
+ mmc_buffer[1] = 0x7f; // Real Time SysEx ID for MMC
+ mmc_buffer[2] = (mmc ? mmc->send_device_id() : 0x7f);
+ mmc_buffer[3] = 0x6; // MCC
+
+ /* Set up the qtr frame message */
+
+ mtc_msg[0] = 0xf1;
+ mtc_msg[2] = 0xf1;
+ mtc_msg[4] = 0xf1;
+ mtc_msg[6] = 0xf1;
+ mtc_msg[8] = 0xf1;
+ mtc_msg[10] = 0xf1;
+ mtc_msg[12] = 0xf1;
+ mtc_msg[14] = 0xf1;
+}
+
+void
+Session::spp_start (Parser& ignored)
+{
+ if (Config->get_mmc_control() && (Config->get_slave_source() != MTC)) {
+ request_transport_speed (1.0);
+ }
+}
+
+void
+Session::spp_continue (Parser& ignored)
+{
+ spp_start (ignored);
+}
+
+void
+Session::spp_stop (Parser& ignored)
+{
+ if (Config->get_mmc_control()) {
+ request_stop ();
+ }
+}
+
+void
+Session::mmc_deferred_play (MIDI::MachineControl &mmc)
+{
+ if (Config->get_mmc_control() && (Config->get_slave_source() != MTC)) {
+ request_transport_speed (1.0);
+ }
+}
+
+void
+Session::mmc_record_pause (MIDI::MachineControl &mmc)
+{
+ if (Config->get_mmc_control()) {
+ maybe_enable_record();
+ }
+}
+
+void
+Session::mmc_record_strobe (MIDI::MachineControl &mmc)
+{
+ if (!Config->get_mmc_control())
+ return;
+
+ /* record strobe does an implicit "Play" command */
+
+ if (_transport_speed != 1.0) {
+
+ /* start_transport() will move from Enabled->Recording, so we
+ don't need to do anything here except enable recording.
+ its not the same as maybe_enable_record() though, because
+ that *can* switch to Recording, which we do not want.
+ */
+
+ save_state ("", true);
+ g_atomic_int_set (&_record_status, Enabled);
+ RecordStateChanged (); /* EMIT SIGNAL */
+
+ request_transport_speed (1.0);
+
+ } else {
+
+ enable_record ();
+ }
+}
+
+void
+Session::mmc_record_exit (MIDI::MachineControl &mmc)
+{
+ if (Config->get_mmc_control()) {
+ disable_record (false);
+ }
+}
+
+void
+Session::mmc_stop (MIDI::MachineControl &mmc)
+{
+ if (Config->get_mmc_control()) {
+ request_stop ();
+ }
+}
+
+void
+Session::mmc_pause (MIDI::MachineControl &mmc)
+{
+ if (Config->get_mmc_control()) {
+
+ /* We support RECORD_PAUSE, so the spec says that
+ we must interpret PAUSE like RECORD_PAUSE if
+ recording.
+ */
+
+ if (actively_recording()) {
+ maybe_enable_record ();
+ } else {
+ request_stop ();
+ }
+ }
+}
+
+static bool step_queued = false;
+
+void
+Session::mmc_step (MIDI::MachineControl &mmc, int steps)
+{
+ if (!Config->get_mmc_control()) {
+ return;
+ }
+
+ struct timeval now;
+ struct timeval diff = { 0, 0 };
+
+ gettimeofday (&now, 0);
+
+ timersub (&now, &last_mmc_step, &diff);
+
+ gettimeofday (&now, 0);
+ timersub (&now, &last_mmc_step, &diff);
+
+ if (last_mmc_step.tv_sec != 0 && (diff.tv_usec + (diff.tv_sec * 1000000)) < _engine.usecs_per_cycle()) {
+ return;
+ }
+
+ double diff_secs = diff.tv_sec + (diff.tv_usec / 1000000.0);
+ double cur_speed = (((steps * 0.5) * smpte_frames_per_second()) / diff_secs) / smpte_frames_per_second();
+
+ if (_transport_speed == 0 || cur_speed * _transport_speed < 0) {
+ /* change direction */
+ step_speed = cur_speed;
+ } else {
+ step_speed = (0.6 * step_speed) + (0.4 * cur_speed);
+ }
+
+ step_speed *= 0.25;
+
+#if 0
+ cerr << "delta = " << diff_secs
+ << " ct = " << _transport_speed
+ << " steps = " << steps
+ << " new speed = " << cur_speed
+ << " speed = " << step_speed
+ << endl;
+#endif
+
+ request_transport_speed (step_speed);
+ last_mmc_step = now;
+
+ if (!step_queued) {
+ midi_timeouts.push_back (mem_fun (*this, &Session::mmc_step_timeout));
+ step_queued = true;
+ }
+}
+
+void
+Session::mmc_rewind (MIDI::MachineControl &mmc)
+{
+ if (Config->get_mmc_control()) {
+ request_transport_speed(-8.0f);
+ }
+}
+
+void
+Session::mmc_fast_forward (MIDI::MachineControl &mmc)
+{
+ if (Config->get_mmc_control()) {
+ request_transport_speed(8.0f);
+ }
+}
+
+void
+Session::mmc_locate (MIDI::MachineControl &mmc, const MIDI::byte* mmc_tc)
+{
+ if (!Config->get_mmc_control()) {
+ return;
+ }
+
+ nframes_t target_frame;
+ SMPTE::Time smpte;
+
+ smpte.hours = mmc_tc[0] & 0xf;
+ smpte.minutes = mmc_tc[1];
+ smpte.seconds = mmc_tc[2];
+ smpte.frames = mmc_tc[3];
+ smpte.rate = smpte_frames_per_second();
+ smpte.drop = smpte_drop_frames();
+
+ // Also takes smpte offset into account:
+ smpte_to_sample( smpte, target_frame, true /* use_offset */, false /* use_subframes */ );
+
+ if (target_frame > max_frames) {
+ target_frame = max_frames;
+ }
+
+ /* Some (all?) MTC/MMC devices do not send a full MTC frame
+ at the end of a locate, instead sending only an MMC
+ locate command. This causes the current position
+ of an MTC slave to become out of date. Catch this.
+ */
+
+ MTC_Slave* mtcs = dynamic_cast<MTC_Slave*> (_slave);
+
+ if (mtcs != 0) {
+ // cerr << "Locate *with* MTC slave\n";
+ mtcs->handle_locate (mmc_tc);
+ } else {
+ // cerr << "Locate without MTC slave\n";
+ request_locate (target_frame, false);
+ }
+}
+
+void
+Session::mmc_shuttle (MIDI::MachineControl &mmc, float speed, bool forw)
+{
+ if (!Config->get_mmc_control()) {
+ return;
+ }
+
+ if (Config->get_shuttle_speed_threshold() >= 0 && speed > Config->get_shuttle_speed_threshold()) {
+ speed *= Config->get_shuttle_speed_factor();
+ }
+
+ if (forw) {
+ request_transport_speed (speed);
+ } else {
+ request_transport_speed (-speed);
+ }
+}
+
+void
+Session::mmc_record_enable (MIDI::MachineControl &mmc, size_t trk, bool enabled)
+{
+ if (Config->get_mmc_control()) {
+
+ RouteList::iterator i;
+ boost::shared_ptr<RouteList> r = routes.reader();
+
+ for (i = r->begin(); i != r->end(); ++i) {
+ AudioTrack *at;
+
+ if ((at = dynamic_cast<AudioTrack*>((*i).get())) != 0) {
+ if (trk == at->remote_control_id()) {
+ at->set_record_enable (enabled, &mmc);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void
+Session::change_midi_ports ()
+{
+ MIDIRequest* request = new MIDIRequest;
+
+ request->type = MIDIRequest::PortChange;
+ midi_requests.write (&request, 1);
+ poke_midi_thread ();
+}
+
+/** Send MTC Full Frame message (complete SMPTE time) for the start of this cycle.
+ * This resets the MTC code, the next quarter frame message that is sent will be
+ * the first one with the beginning of this cycle as the new start point.
+ */
+
+int
+Session::send_full_time_code(nframes_t nframes)
+{
+ /* This function could easily send at a given frame offset, but would
+ * that be useful? Does ardour do sub-block accurate locating? [DR] */
+
+ MIDI::byte msg[10];
+ SMPTE::Time smpte;
+
+ _send_smpte_update = false;
+
+ if (_mtc_port == 0 || !session_send_mtc) {
+ return 0;
+ }
+
+ // Get smpte time for this transport frame
+ sample_to_smpte(_transport_frame, smpte, true /* use_offset */, false /* no subframes */);
+
+ transmitting_smpte_time = smpte;
+ outbound_mtc_smpte_frame = _transport_frame;
+
+ // I don't understand this bit yet.. [DR]
+ if (((mtc_smpte_bits >> 5) != MIDI::MTC_25_FPS) && (transmitting_smpte_time.frames % 2)) {
+ // start MTC quarter frame transmission on an even frame
+ SMPTE::increment( transmitting_smpte_time );
+ outbound_mtc_smpte_frame += (nframes_t) _frames_per_smpte_frame;
+ }
+
+ // Compensate for audio latency
+ outbound_mtc_smpte_frame += _worst_output_latency;
+
+ next_quarter_frame_to_send = 0;
+
+ // Sync slave to the same SMPTE time as we are on
+ msg[0] = 0xf0;
+ msg[1] = 0x7f;
+ msg[2] = 0x7f;
+ msg[3] = 0x1;
+ msg[4] = 0x1;
+ msg[9] = 0xf7;
+
+ msg[5] = mtc_smpte_bits | smpte.hours;
+ msg[6] = smpte.minutes;
+ msg[7] = smpte.seconds;
+ msg[8] = smpte.frames;
+
+ cerr << "MTC: Sending full time code at " << outbound_mtc_smpte_frame << endl;
+
+ // Send message at offset 0, sent time is for the start of this cycle
+ if (_mtc_port->midimsg (msg, sizeof (msg), 0)) {
+ error << _("Session: could not send full MIDI time code") << endmsg;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/** Sends MTC (quarter-frame) messages for this cycle.
+ * Must be called exactly once per cycle from the audio thread. Realtime safe.
+ * This function assumes the state of full SMPTE is sane, eg. the slave is
+ * expecting quarter frame messages and has the right frame of reference (any
+ * full MTC SMPTE time messages that needed to be sent should have been sent
+ * earlier already this cycle by send_full_time_code)
+ */
+int
+Session::send_midi_time_code_for_cycle(nframes_t nframes)
+{
+ assert (next_quarter_frame_to_send >= 0);
+ assert (next_quarter_frame_to_send <= 7);
+
+ if (_mtc_port == 0 || !session_send_mtc || transmitting_smpte_time.negative
+ /*|| (next_quarter_frame_to_send < 0)*/ ) {
+ // cerr << "(MTC) Not sending MTC\n";
+ return 0;
+ }
+
+ /* Duration of one quarter frame */
+ nframes_t quarter_frame_duration = ((long) _frames_per_smpte_frame) >> 2;
+
+ // cerr << "(MTC) TR: " << _transport_frame << " - SF: " << outbound_mtc_smpte_frame
+ // << " - NQ: " << next_quarter_frame_to_send << " - FD" << quarter_frame_duration << endl;
+
+ // FIXME: this should always be true
+ //assert((outbound_mtc_smpte_frame + (next_quarter_frame_to_send * quarter_frame_duration))
+ // > _transport_frame);
+
+
+ // Send quarter frames for this cycle
+ while (_transport_frame + nframes > (outbound_mtc_smpte_frame +
+ (next_quarter_frame_to_send * quarter_frame_duration))) {
+
+ // cerr << "(MTC) Next frame to send: " << next_quarter_frame_to_send << endl;
+
+ switch (next_quarter_frame_to_send) {
+ case 0:
+ mtc_msg[1] = 0x00 | (transmitting_smpte_time.frames & 0xf);
+ break;
+ case 1:
+ mtc_msg[1] = 0x10 | ((transmitting_smpte_time.frames & 0xf0) >> 4);
+ break;
+ case 2:
+ mtc_msg[1] = 0x20 | (transmitting_smpte_time.seconds & 0xf);
+ break;
+ case 3:
+ mtc_msg[1] = 0x30 | ((transmitting_smpte_time.seconds & 0xf0) >> 4);
+ break;
+ case 4:
+ mtc_msg[1] = 0x40 | (transmitting_smpte_time.minutes & 0xf);
+ break;
+ case 5:
+ mtc_msg[1] = 0x50 | ((transmitting_smpte_time.minutes & 0xf0) >> 4);
+ break;
+ case 6:
+ mtc_msg[1] = 0x60 | ((mtc_smpte_bits|transmitting_smpte_time.hours) & 0xf);
+ break;
+ case 7:
+ mtc_msg[1] = 0x70 | (((mtc_smpte_bits|transmitting_smpte_time.hours) & 0xf0) >> 4);
+ break;
+ }
+
+ const nframes_t msg_time = (outbound_mtc_smpte_frame
+ + (quarter_frame_duration * next_quarter_frame_to_send));
+
+ // This message must fall within this block or something is broken
+ assert(msg_time >= _transport_frame);
+ assert(msg_time < _transport_frame + nframes);
+
+ nframes_t out_stamp = msg_time - _transport_frame;
+ assert(out_stamp < nframes);
+
+ if (_mtc_port->midimsg (mtc_msg, 2, out_stamp)) {
+ error << string_compose(_("Session: cannot send quarter-frame MTC message (%1)"), strerror (errno))
+ << endmsg;
+ return -1;
+ }
+
+ /*cerr << "(MTC) SMPTE: " << transmitting_smpte_time.hours
+ << ":" << transmitting_smpte_time.minutes
+ << ":" << transmitting_smpte_time.seconds
+ << ":" << transmitting_smpte_time.frames
+ << ", qfm = " << next_quarter_frame_to_send
+ << ", stamp = " << out_stamp
+ << ", delta = " << _transport_frame + out_stamp - last_time << endl;*/
+
+ // Increment quarter frame counter
+ next_quarter_frame_to_send++;
+
+ if (next_quarter_frame_to_send >= 8) {
+ // Wrap quarter frame counter
+ next_quarter_frame_to_send = 0;
+ // Increment smpte time twice
+ SMPTE::increment( transmitting_smpte_time );
+ SMPTE::increment( transmitting_smpte_time );
+ // Re-calculate timing of first quarter frame
+ //smpte_to_sample( transmitting_smpte_time, outbound_mtc_smpte_frame, true /* use_offset */, false );
+ outbound_mtc_smpte_frame += 8 * quarter_frame_duration;
+ // Compensate for audio latency
+ outbound_mtc_smpte_frame += _worst_output_latency;
+ }
+ }
+
+ return 0;
+}
+
+/***********************************************************************
+ OUTBOUND MMC STUFF
+**********************************************************************/
+
+void
+Session::deliver_mmc (MIDI::MachineControl::Command cmd, nframes_t where)
+{
+ using namespace MIDI;
+ int nbytes = 4;
+ SMPTE::Time smpte;
+
+ if (_mmc_port == 0 || !session_send_mmc) {
+ // cerr << "Not delivering MMC " << _mmc_port << " - " << session_send_mmc << endl;
+ return;
+ }
+
+ mmc_buffer[nbytes++] = cmd;
+
+ // cerr << "delivering MMC, cmd = " << hex << (int) cmd << dec << endl;
+
+ switch (cmd) {
+ case MachineControl::cmdLocate:
+ smpte_time_subframes (where, smpte);
+
+ mmc_buffer[nbytes++] = 0x6; // byte count
+ mmc_buffer[nbytes++] = 0x1; // "TARGET" subcommand
+ mmc_buffer[nbytes++] = smpte.hours;
+ mmc_buffer[nbytes++] = smpte.minutes;
+ mmc_buffer[nbytes++] = smpte.seconds;
+ mmc_buffer[nbytes++] = smpte.frames;
+ mmc_buffer[nbytes++] = smpte.subframes;
+ break;
+
+ case MachineControl::cmdStop:
+ break;
+
+ case MachineControl::cmdPlay:
+ /* always convert Play into Deferred Play */
+ /* Why? [DR] */
+ mmc_buffer[4] = MachineControl::cmdDeferredPlay;
+ break;
+
+ case MachineControl::cmdDeferredPlay:
+ break;
+
+ case MachineControl::cmdRecordStrobe:
+ break;
+
+ case MachineControl::cmdRecordExit:
+ break;
+
+ case MachineControl::cmdRecordPause:
+ break;
+
+ default:
+ nbytes = 0;
+ };
+
+ if (nbytes) {
+
+ mmc_buffer[nbytes++] = 0xf7; // terminate SysEx/MMC message
+
+ if (_mmc_port->midimsg (mmc_buffer, nbytes, 0)) {
+ error << string_compose(_("MMC: cannot send command %1%2%3"), &hex, cmd, &dec) << endmsg;
+ }
+ }
+}
+
+bool
+Session::mmc_step_timeout ()
+{
+ struct timeval now;
+ struct timeval diff;
+ double diff_usecs;
+ gettimeofday (&now, 0);
+
+ timersub (&now, &last_mmc_step, &diff);
+ diff_usecs = diff.tv_sec * 1000000 + diff.tv_usec;
+
+ if (diff_usecs > 1000000.0 || fabs (_transport_speed) < 0.0000001) {
+ /* too long or too slow, stop transport */
+ request_transport_speed (0.0);
+ step_queued = false;
+ return false;
+ }
+
+ if (diff_usecs < 250000.0) {
+ /* too short, just keep going */
+ return true;
+ }
+
+ /* slow it down */
+
+ request_transport_speed (_transport_speed * 0.75);
+ return true;
+}
+
+/*---------------------------------------------------------------------------
+ MIDI THREAD
+ ---------------------------------------------------------------------------*/
+
+int
+Session::start_midi_thread ()
+{
+ if (pipe (midi_request_pipe)) {
+ error << string_compose(_("Cannot create transport request signal pipe (%1)"), strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ if (fcntl (midi_request_pipe[0], F_SETFL, O_NONBLOCK)) {
+ error << string_compose(_("UI: cannot set O_NONBLOCK on " "signal read pipe (%1)"), strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ if (fcntl (midi_request_pipe[1], F_SETFL, O_NONBLOCK)) {
+ error << string_compose(_("UI: cannot set O_NONBLOCK on " "signal write pipe (%1)"), strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ if (pthread_create_and_store ("transport", &midi_thread, 0, _midi_thread_work, this)) {
+ error << _("Session: could not create transport thread") << endmsg;
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+Session::terminate_midi_thread ()
+{
+ if (midi_thread) {
+
+ MIDIRequest* request = new MIDIRequest;
+ void* status;
+
+ request->type = MIDIRequest::Quit;
+
+ midi_requests.write (&request, 1);
+ poke_midi_thread ();
+
+ pthread_join (midi_thread, &status);
+ }
+}
+
+void
+Session::poke_midi_thread ()
+{
+ static char c = 0;
+
+ if (write (midi_request_pipe[1], &c, 1) != 1) {
+ error << string_compose(_("cannot send signal to midi thread! (%1)"), strerror (errno)) << endmsg;
+ }
+}
+
+void *
+Session::_midi_thread_work (void* arg)
+{
+ pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, 0);
+ pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, 0);
+
+ ((Session *) arg)->midi_thread_work ();
+ return 0;
+}
+
+void
+Session::midi_thread_work ()
+{
+ MIDIRequest* request;
+ struct pollfd pfd[4];
+ int nfds = 0;
+ int timeout;
+ int fds_ready;
+ struct sched_param rtparam;
+ int x;
+ bool restart;
+ vector<MIDI::Port*> ports;
+
+ PBD::ThreadCreatedWithRequestSize (pthread_self(), X_("MIDI"), 2048);
+
+ memset (&rtparam, 0, sizeof (rtparam));
+ rtparam.sched_priority = 9; /* XXX should be relative to audio (JACK) thread */
+
+ if ((x = pthread_setschedparam (pthread_self(), SCHED_FIFO, &rtparam)) != 0) {
+ // do we care? not particularly.
+ }
+
+ /* set up the port vector; 4 is the largest possible size for now */
+
+ ports.assign (4, (MIDI::Port*) 0);
+
+ while (1) {
+
+ nfds = 0;
+
+ pfd[nfds].fd = midi_request_pipe[0];
+ pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
+ nfds++;
+
+ if (Config->get_mmc_control() && _mmc_port && _mmc_port->selectable() >= 0) {
+ pfd[nfds].fd = _mmc_port->selectable();
+ pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
+ ports[nfds] = _mmc_port;
+ nfds++;
+ }
+
+ /* if MTC is being handled on a different port from MMC
+ or we are not handling MMC at all, poll
+ the relevant port.
+ */
+
+ if (_mtc_port && (_mtc_port != _mmc_port || !Config->get_mmc_control()) && _mtc_port->selectable() >= 0) {
+ pfd[nfds].fd = _mtc_port->selectable();
+ pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
+ ports[nfds] = _mtc_port;
+ nfds++;
+ }
+
+ /* if we are using MMC control, we obviously have to listen
+ the relevant port.
+ */
+
+ if (_midi_port && (_midi_port != _mmc_port || !Config->get_mmc_control()) && (_midi_port != _mtc_port) && _midi_port->selectable() >= 0) {
+ pfd[nfds].fd = _midi_port->selectable();
+ pfd[nfds].events = POLLIN|POLLHUP|POLLERR;
+ ports[nfds] = _midi_port;
+ nfds++;
+ }
+
+ if (!midi_timeouts.empty()) {
+ timeout = 100; /* 10msecs */
+ } else {
+ timeout = -1; /* if there is no data, we don't care */
+ }
+
+ again:
+ // cerr << "MIDI poll on " << nfds << " for " << timeout << endl;
+ if (poll (pfd, nfds, timeout) < 0) {
+ if (errno == EINTR) {
+ /* gdb at work, perhaps */
+ goto again;
+ }
+
+ error << string_compose(_("MIDI thread poll failed (%1)"), strerror (errno)) << endmsg;
+
+ break;
+ }
+ // cerr << "MIDI thread wakes at " << get_cycles () << endl;
+
+ fds_ready = 0;
+
+ /* check the transport request pipe */
+
+ if (pfd[0].revents & ~POLLIN) {
+ error << _("Error on transport thread request pipe") << endmsg;
+ break;
+ }
+
+ if (pfd[0].revents & POLLIN) {
+
+ char foo[16];
+
+ // cerr << "MIDI request FIFO ready\n";
+ fds_ready++;
+
+ /* empty the pipe of all current requests */
+
+ while (1) {
+ size_t nread = read (midi_request_pipe[0], &foo, sizeof (foo));
+
+ if (nread > 0) {
+ if ((size_t) nread < sizeof (foo)) {
+ break;
+ } else {
+ continue;
+ }
+ } else if (nread == 0) {
+ break;
+ } else if (errno == EAGAIN) {
+ break;
+ } else {
+ fatal << _("Error reading from transport request pipe") << endmsg;
+ /*NOTREACHED*/
+ }
+ }
+
+ while (midi_requests.read (&request, 1) == 1) {
+
+ switch (request->type) {
+ case MIDIRequest::PortChange:
+ /* restart poll with new ports */
+ // cerr << "rebind\n";
+ restart = true;
+ break;
+
+ case MIDIRequest::Quit:
+ delete request;
+ pthread_exit_pbd (0);
+ /*NOTREACHED*/
+ break;
+
+ default:
+ break;
+ }
+
+
+ delete request;
+ }
+
+ }
+
+ if (restart) {
+ continue;
+ }
+
+ /* now read the rest of the ports */
+
+ for (int p = 1; p < nfds; ++p) {
+ if ((pfd[p].revents & ~POLLIN)) {
+ // error << string_compose(_("Transport: error polling MIDI port %1 (revents =%2%3%4"), p, &hex, pfd[p].revents, &dec) << endmsg;
+ break;
+ }
+
+ if (pfd[p].revents & POLLIN) {
+ fds_ready++;
+ ports[p]->parse ();
+ }
+ }
+
+ /* timeout driven */
+
+ if (fds_ready < 2 && timeout != -1) {
+
+ for (MidiTimeoutList::iterator i = midi_timeouts.begin(); i != midi_timeouts.end(); ) {
+
+ MidiTimeoutList::iterator tmp;
+ tmp = i;
+ ++tmp;
+
+ if (!(*i)()) {
+ midi_timeouts.erase (i);
+ }
+
+ i = tmp;
+ }
+ }
+ }
+}
+
diff --git a/libs/ardour/session_process.cc b/libs/ardour/session_process.cc
new file mode 100644
index 0000000000..d6890b31ae
--- /dev/null
+++ b/libs/ardour/session_process.cc
@@ -0,0 +1,886 @@
+/*
+ Copyright (C) 1999-2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cmath>
+#include <cerrno>
+#include <algorithm>
+#include <unistd.h>
+
+#include <pbd/error.h>
+
+#include <glibmm/thread.h>
+
+#include <ardour/ardour.h>
+#include <ardour/session.h>
+#include <ardour/timestamps.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/audioengine.h>
+#include <ardour/slave.h>
+#include <ardour/auditioner.h>
+#include <ardour/cycles.h>
+#include <ardour/cycle_timer.h>
+
+#include <midi++/manager.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+using namespace std;
+
+/** Called by the audio engine when there is work to be done with JACK.
+ * @param nframes Number of frames to process.
+ */
+void
+Session::process (nframes_t nframes)
+{
+ MIDI::Manager::instance()->cycle_start(nframes);
+
+ _silent = false;
+
+ if (synced_to_jack() && waiting_to_start) {
+ if ( _engine.transport_state() == AudioEngine::TransportRolling) {
+ actually_start_transport ();
+ }
+ }
+
+ if (non_realtime_work_pending()) {
+ if (!transport_work_requested ()) {
+ post_transport ();
+ }
+ }
+
+ (this->*process_function) (nframes);
+
+ MIDI::Manager::instance()->cycle_end();
+
+ SendFeedback (); /* EMIT SIGNAL */
+}
+
+void
+Session::prepare_diskstreams ()
+{
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->prepare ();
+ }
+}
+
+int
+Session::no_roll (nframes_t nframes, nframes_t offset)
+{
+ nframes_t end_frame = _transport_frame + nframes; // FIXME: varispeed + no_roll ??
+ int ret = 0;
+ bool declick = get_transport_declick_required();
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ if (_click_io) {
+ _click_io->silence (nframes, offset);
+ }
+
+ if (g_atomic_int_get (&processing_prohibited)) {
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->silence (nframes, offset);
+ }
+ return 0;
+ }
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+
+ if ((*i)->is_hidden()) {
+ continue;
+ }
+
+ (*i)->set_pending_declick (declick);
+
+ if ((*i)->no_roll (nframes, _transport_frame, end_frame, offset, non_realtime_work_pending(),
+ actively_recording(), declick)) {
+ error << string_compose(_("Session: error in no roll for %1"), (*i)->name()) << endmsg;
+ ret = -1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int
+Session::process_routes (nframes_t nframes, nframes_t offset)
+{
+ bool record_active;
+ int declick = get_transport_declick_required();
+ bool rec_monitors = get_rec_monitors_input();
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ if (transport_sub_state & StopPendingCapture) {
+ /* force a declick out */
+ declick = -1;
+ }
+
+ record_active = actively_recording(); // || (get_record_enabled() && get_punch_in());
+
+ const nframes_t start_frame = _transport_frame;
+ const nframes_t end_frame = _transport_frame + (nframes_t)floor(nframes * _transport_speed);
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+
+ int ret;
+
+ if ((*i)->is_hidden()) {
+ continue;
+ }
+
+ (*i)->set_pending_declick (declick);
+
+ if ((ret = (*i)->roll (nframes, start_frame, end_frame, offset, declick, record_active, rec_monitors)) < 0) {
+
+ /* we have to do this here. Route::roll() for an AudioTrack will have called AudioDiskstream::process(),
+ and the DS will expect AudioDiskstream::commit() to be called. but we're aborting from that
+ call path, so make sure we release any outstanding locks here before we return failure.
+ */
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator ids = dsl->begin(); ids != dsl->end(); ++ids) {
+ (*ids)->recover ();
+ }
+
+ stop_transport ();
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+Session::silent_process_routes (nframes_t nframes, nframes_t offset)
+{
+ bool record_active = actively_recording();
+ int declick = get_transport_declick_required();
+ bool rec_monitors = get_rec_monitors_input();
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ if (transport_sub_state & StopPendingCapture) {
+ /* force a declick out */
+ declick = -1;
+ }
+
+ const nframes_t start_frame = _transport_frame;
+ const nframes_t end_frame = _transport_frame + lrintf(nframes * _transport_speed);
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+
+ int ret;
+
+ if ((*i)->is_hidden()) {
+ continue;
+ }
+
+ if ((ret = (*i)->silent_roll (nframes, start_frame, end_frame, offset, record_active, rec_monitors)) < 0) {
+
+ /* we have to do this here. Route::roll() for an AudioTrack will have called AudioDiskstream::process(),
+ and the DS will expect AudioDiskstream::commit() to be called. but we're aborting from that
+ call path, so make sure we release any outstanding locks here before we return failure.
+ */
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator ids = dsl->begin(); ids != dsl->end(); ++ids) {
+ (*ids)->recover ();
+ }
+
+ stop_transport ();
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void
+Session::commit_diskstreams (nframes_t nframes, bool &needs_butler)
+{
+ int dret;
+ float pworst = 1.0f;
+ float cworst = 1.0f;
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+
+ if ((*i)->hidden()) {
+ continue;
+ }
+
+ /* force all diskstreams not handled by a Route to call do their stuff.
+ Note: the diskstreams that were handled by a route will just return zero
+ from this call, because they know they were processed. So in fact, this
+ also runs commit() for every diskstream.
+ */
+
+ if ((dret = (*i)->process (_transport_frame, nframes, 0, actively_recording(), get_rec_monitors_input())) == 0) {
+ if ((*i)->commit (nframes)) {
+ needs_butler = true;
+ }
+
+ } else if (dret < 0) {
+ (*i)->recover();
+ }
+
+ pworst = min (pworst, (*i)->playback_buffer_load());
+ cworst = min (cworst, (*i)->capture_buffer_load());
+ }
+
+ uint32_t pmin = g_atomic_int_get (&_playback_load);
+ uint32_t pminold = g_atomic_int_get (&_playback_load_min);
+ uint32_t cmin = g_atomic_int_get (&_capture_load);
+ uint32_t cminold = g_atomic_int_get (&_capture_load_min);
+
+ g_atomic_int_set (&_playback_load, (uint32_t) floor (pworst * 100.0f));
+ g_atomic_int_set (&_capture_load, (uint32_t) floor (cworst * 100.0f));
+ g_atomic_int_set (&_playback_load_min, min (pmin, pminold));
+ g_atomic_int_set (&_capture_load_min, min (cmin, cminold));
+
+ if (actively_recording()) {
+ set_dirty();
+ }
+}
+
+/** Process callback used when the auditioner is not active */
+void
+Session::process_with_events (nframes_t nframes)
+{
+ Event* ev;
+ nframes_t this_nframes;
+ nframes_t end_frame;
+ nframes_t offset;
+ bool session_needs_butler = false;
+ nframes_t stop_limit;
+ long frames_moved;
+
+ /* make sure the auditioner is silent */
+
+ if (auditioner) {
+ auditioner->silence (nframes, 0);
+ }
+
+ /* handle any pending events */
+
+ while (pending_events.read (&ev, 1) == 1) {
+ merge_event (ev);
+ }
+
+ /* if we are not in the middle of a state change,
+ and there are immediate events queued up,
+ process them.
+ */
+
+ while (!non_realtime_work_pending() && !immediate_events.empty()) {
+ Event *ev = immediate_events.front ();
+ immediate_events.pop_front ();
+ process_event (ev);
+ }
+
+ /* Events caused a transport change, send an MTC Full Frame (SMPTE) message.
+ * This is sent whether rolling or not, to give slaves an idea of ardour time
+ * on locates (and allow slow slaves to position and prepare for rolling)
+ */
+ if (_send_smpte_update) {
+ send_full_time_code(nframes);
+ }
+
+ if (!process_can_proceed()) {
+ _silent = true;
+ return;
+ }
+
+ if (events.empty() || next_event == events.end()) {
+ process_without_events (nframes);
+ return;
+ }
+
+ end_frame = _transport_frame + (nframes_t)abs(floor(nframes * _transport_speed));
+
+ {
+ Event* this_event;
+ Events::iterator the_next_one;
+
+ if (!process_can_proceed()) {
+ _silent = true;
+ return;
+ }
+
+ if (!_exporting && _slave) {
+ if (!follow_slave (nframes, 0)) {
+ return;
+ }
+ }
+
+ if (_transport_speed == 0) {
+ no_roll (nframes, 0);
+ return;
+ }
+
+ if (!_exporting) {
+ send_midi_time_code_for_cycle (nframes);
+ }
+
+ if (actively_recording()) {
+ stop_limit = max_frames;
+ } else {
+
+ if (Config->get_stop_at_session_end()) {
+ stop_limit = current_end_frame();
+ } else {
+ stop_limit = max_frames;
+ }
+ }
+
+ if (maybe_stop (stop_limit)) {
+ no_roll (nframes, 0);
+ return;
+ }
+
+ this_event = *next_event;
+ the_next_one = next_event;
+ ++the_next_one;
+
+ offset = 0;
+
+ /* yes folks, here it is, the actual loop where we really truly
+ process some audio */
+ while (nframes) {
+
+ this_nframes = nframes; /* real (jack) time relative */
+ frames_moved = (long) floor (_transport_speed * nframes); /* transport relative */
+
+ /* running an event, position transport precisely to its time */
+ if (this_event && this_event->action_frame <= end_frame && this_event->action_frame >= _transport_frame) {
+ /* this isn't quite right for reverse play */
+ frames_moved = (long) (this_event->action_frame - _transport_frame);
+ this_nframes = (nframes_t) abs( floor(frames_moved / _transport_speed) );
+ }
+
+ if (this_nframes) {
+
+ click (_transport_frame, nframes, offset);
+
+ /* now process frames between now and the first event in this block */
+ prepare_diskstreams ();
+
+ if (process_routes (this_nframes, offset)) {
+ no_roll (nframes, 0);
+ return;
+ }
+
+ commit_diskstreams (this_nframes, session_needs_butler);
+
+ nframes -= this_nframes;
+ offset += this_nframes;
+
+ if (frames_moved < 0) {
+ decrement_transport_position (-frames_moved);
+ } else {
+ increment_transport_position (frames_moved);
+ }
+
+ maybe_stop (stop_limit);
+ check_declick_out ();
+ }
+
+ /* now handle this event and all others scheduled for the same time */
+
+ while (this_event && this_event->action_frame == _transport_frame) {
+ process_event (this_event);
+
+ if (the_next_one == events.end()) {
+ this_event = 0;
+ } else {
+ this_event = *the_next_one;
+ ++the_next_one;
+ }
+ }
+
+ /* if an event left our state changing, do the right thing */
+
+ if (non_realtime_work_pending()) {
+ no_roll (nframes, offset);
+ break;
+ }
+
+ /* this is necessary to handle the case of seamless looping */
+ end_frame = _transport_frame + (nframes_t) floor (nframes * _transport_speed);
+
+ }
+
+ set_next_event ();
+
+ } /* implicit release of route lock */
+
+ if (session_needs_butler)
+ summon_butler ();
+}
+
+void
+Session::reset_slave_state ()
+{
+ average_slave_delta = 1800;
+ delta_accumulator_cnt = 0;
+ have_first_delta_accumulator = false;
+ slave_state = Stopped;
+}
+
+bool
+Session::transport_locked () const
+{
+ Slave* sl = _slave;
+
+ if (!locate_pending() && ((Config->get_slave_source() == None) || (sl && sl->ok() && sl->locked()))) {
+ return true;
+ }
+
+ return false;
+}
+
+bool
+Session::follow_slave (nframes_t nframes, nframes_t offset)
+{
+ float slave_speed;
+ nframes_t slave_transport_frame;
+ nframes_t this_delta;
+ int dir;
+ bool starting;
+
+ if (!_slave->ok()) {
+ stop_transport ();
+ Config->set_slave_source (None);
+ goto noroll;
+ }
+
+ _slave->speed_and_position (slave_speed, slave_transport_frame);
+
+ if (!_slave->locked()) {
+ goto noroll;
+ }
+
+ if (slave_transport_frame > _transport_frame) {
+ this_delta = slave_transport_frame - _transport_frame;
+ dir = 1;
+ } else {
+ this_delta = _transport_frame - slave_transport_frame;
+ dir = -1;
+ }
+
+ if ((starting = _slave->starting())) {
+ slave_speed = 0.0f;
+ }
+
+#if 0
+ cerr << "delta = " << (int) (dir * this_delta)
+ << " speed = " << slave_speed
+ << " ts = " << _transport_speed
+ << " M@ "<< slave_transport_frame << " S@ " << _transport_frame
+ << " avgdelta = " << average_slave_delta
+ << endl;
+#endif
+
+ if (_slave->is_always_synced() || Config->get_timecode_source_is_synced()) {
+
+ /* if the TC source is synced, then we assume that its
+ speed is binary: 0.0 or 1.0
+ */
+
+ if (slave_speed != 0.0f) {
+ slave_speed = 1.0f;
+ }
+
+ } else {
+
+ /* TC source is able to drift relative to us (slave)
+ so we need to keep track of the drift and adjust
+ our speed to remain locked.
+ */
+
+ if (delta_accumulator_cnt >= delta_accumulator_size) {
+ have_first_delta_accumulator = true;
+ delta_accumulator_cnt = 0;
+ }
+
+ if (delta_accumulator_cnt != 0 || this_delta < _current_frame_rate) {
+ delta_accumulator[delta_accumulator_cnt++] = dir*this_delta;
+ }
+
+ if (have_first_delta_accumulator) {
+ average_slave_delta = 0;
+ for (int i = 0; i < delta_accumulator_size; ++i) {
+ average_slave_delta += delta_accumulator[i];
+ }
+ average_slave_delta /= delta_accumulator_size;
+ if (average_slave_delta < 0) {
+ average_dir = -1;
+ average_slave_delta = -average_slave_delta;
+ } else {
+ average_dir = 1;
+ }
+ // cerr << "avgdelta = " << average_slave_delta*average_dir << endl;
+ }
+ }
+
+ if (slave_speed != 0.0f) {
+
+ /* slave is running */
+
+ switch (slave_state) {
+ case Stopped:
+ if (_slave->requires_seekahead()) {
+ slave_wait_end = slave_transport_frame + _current_frame_rate;
+ locate (slave_wait_end, false, false);
+ slave_state = Waiting;
+ starting = true;
+
+ } else {
+
+ slave_state = Running;
+
+ Location* al = _locations.auto_loop_location();
+
+ if (al && play_loop && (slave_transport_frame < al->start() || slave_transport_frame > al->end())) {
+ // cancel looping
+ request_play_loop(false);
+ }
+
+ if (slave_transport_frame != _transport_frame) {
+ locate (slave_transport_frame, false, false);
+ }
+ }
+ break;
+
+ case Waiting:
+ break;
+
+ default:
+ break;
+
+ }
+
+ if (slave_state == Waiting) {
+
+ // cerr << "waiting at " << slave_transport_frame << endl;
+ if (slave_transport_frame >= slave_wait_end) {
+ // cerr << "\tstart at " << _transport_frame << endl;
+
+ slave_state = Running;
+
+ bool ok = true;
+ nframes_t frame_delta = slave_transport_frame - _transport_frame;
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->can_internal_playback_seek (frame_delta)) {
+ ok = false;
+ break;
+ }
+ }
+
+ if (ok) {
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->internal_playback_seek (frame_delta);
+ }
+ _transport_frame += frame_delta;
+
+ } else {
+ // cerr << "cannot micro-seek\n";
+ /* XXX what? */
+ }
+
+ memset (delta_accumulator, 0, sizeof (nframes_t) * delta_accumulator_size);
+ average_slave_delta = 0;
+ this_delta = 0;
+ }
+ }
+
+ if (slave_state == Running && _transport_speed == 0.0f) {
+
+ // cerr << "slave starts transport\n";
+
+ start_transport ();
+ }
+
+ } else {
+
+ /* slave has stopped */
+
+ if (_transport_speed != 0.0f) {
+
+ // cerr << "slave stops transport: " << slave_speed << " frame: " << slave_transport_frame
+ // << " tf = " << _transport_frame
+ // << endl;
+
+ if (Config->get_slave_source() == JACK) {
+ last_stop_frame = _transport_frame;
+ }
+
+ stop_transport();
+ }
+
+ if (slave_transport_frame != _transport_frame) {
+ // cerr << "slave stopped, move to " << slave_transport_frame << endl;
+ force_locate (slave_transport_frame, false);
+ }
+
+ slave_state = Stopped;
+ }
+
+ if (slave_state == Running && !_slave->is_always_synced() && !Config->get_timecode_source_is_synced()) {
+
+
+ if (_transport_speed != 0.0f) {
+
+ /*
+ note that average_dir is +1 or -1
+ */
+
+ const float adjust_seconds = 1.0f;
+ float delta;
+
+ //if (average_slave_delta == 0) {
+ delta = this_delta;
+ delta *= dir;
+// } else {
+// delta = average_slave_delta;
+// delta *= average_dir;
+// }
+
+ float adjusted_speed = slave_speed +
+ (delta / (adjust_seconds * _current_frame_rate));
+
+ // cerr << "adjust using " << delta
+ // << " towards " << adjusted_speed
+ // << " ratio = " << adjusted_speed / slave_speed
+ // << " current = " << _transport_speed
+ // << " slave @ " << slave_speed
+ // << endl;
+
+ request_transport_speed (adjusted_speed);
+
+#if 1
+ if ((nframes_t) average_slave_delta > _slave->resolution()) {
+ // cerr << "not locked\n";
+ goto silent_motion;
+ }
+#endif
+ }
+ }
+
+ if (!starting && !non_realtime_work_pending()) {
+ /* speed is set, we're locked, and good to go */
+ return true;
+ }
+
+ silent_motion:
+
+ if (slave_speed && _transport_speed) {
+
+ /* something isn't right, but we should move with the master
+ for now.
+ */
+
+ bool need_butler;
+
+ prepare_diskstreams ();
+ silent_process_routes (nframes, offset);
+ commit_diskstreams (nframes, need_butler);
+
+ if (need_butler) {
+ summon_butler ();
+ }
+
+ int32_t frames_moved = (int32_t) floor (_transport_speed * nframes);
+
+ if (frames_moved < 0) {
+ decrement_transport_position (-frames_moved);
+ } else {
+ increment_transport_position (frames_moved);
+ }
+
+ nframes_t stop_limit;
+
+ if (actively_recording()) {
+ stop_limit = max_frames;
+ } else {
+ if (Config->get_stop_at_session_end()) {
+ stop_limit = current_end_frame();
+ } else {
+ stop_limit = max_frames;
+ }
+ }
+
+ maybe_stop (stop_limit);
+ }
+
+ noroll:
+ /* don't move at all */
+ no_roll (nframes, 0);
+ return false;
+}
+
+void
+Session::process_without_events (nframes_t nframes)
+{
+ bool session_needs_butler = false;
+ nframes_t stop_limit;
+ long frames_moved;
+ nframes_t offset = 0;
+
+ if (!process_can_proceed()) {
+ _silent = true;
+ return;
+ }
+
+ if (!_exporting && _slave) {
+ if (!follow_slave (nframes, 0)) {
+ return;
+ }
+ }
+
+ if (_transport_speed == 0) {
+ no_roll (nframes, 0);
+ return;
+ }
+
+ if (!_exporting) {
+ send_midi_time_code_for_cycle (nframes);
+ }
+
+ if (actively_recording()) {
+ stop_limit = max_frames;
+ } else {
+ if (Config->get_stop_at_session_end()) {
+ stop_limit = current_end_frame();
+ } else {
+ stop_limit = max_frames;
+ }
+ }
+
+ if (maybe_stop (stop_limit)) {
+ no_roll (nframes, 0);
+ return;
+ }
+
+ if (maybe_sync_start (nframes, offset)) {
+ return;
+ }
+
+ click (_transport_frame, nframes, offset);
+
+ prepare_diskstreams ();
+
+ frames_moved = (long) floor (_transport_speed * nframes);
+
+ if (process_routes (nframes, offset)) {
+ no_roll (nframes, offset);
+ return;
+ }
+
+ commit_diskstreams (nframes, session_needs_butler);
+
+ if (frames_moved < 0) {
+ decrement_transport_position (-frames_moved);
+ } else {
+ increment_transport_position (frames_moved);
+ }
+
+ maybe_stop (stop_limit);
+ check_declick_out ();
+
+ if (session_needs_butler)
+ summon_butler ();
+}
+
+/** Process callback used when the auditioner is active.
+ * @param nframes number of frames to process.
+ */
+void
+Session::process_audition (nframes_t nframes)
+{
+ Event* ev;
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (!(*i)->is_hidden()) {
+ (*i)->silence (nframes, 0);
+ }
+ }
+
+ /* run the auditioner, and if it says we need butler service, ask for it */
+
+ if (auditioner->play_audition (nframes) > 0) {
+ summon_butler ();
+ }
+
+ /* handle pending events */
+
+ while (pending_events.read (&ev, 1) == 1) {
+ merge_event (ev);
+ }
+
+ /* if we are not in the middle of a state change,
+ and there are immediate events queued up,
+ process them.
+ */
+
+ while (!non_realtime_work_pending() && !immediate_events.empty()) {
+ Event *ev = immediate_events.front ();
+ immediate_events.pop_front ();
+ process_event (ev);
+ }
+
+ if (!auditioner->active()) {
+ /* auditioner no longer active, so go back to the normal process callback */
+ process_function = &Session::process_with_events;
+ }
+}
+
+bool
+Session::maybe_sync_start (nframes_t& nframes, nframes_t& offset)
+{
+ nframes_t sync_offset;
+
+ if (!waiting_for_sync_offset) {
+ return false;
+ }
+
+ if (_engine.get_sync_offset (sync_offset) && sync_offset < nframes) {
+
+ no_roll (sync_offset, 0);
+ nframes -= sync_offset;
+ offset += sync_offset;
+ waiting_for_sync_offset = false;
+
+ if (nframes == 0) {
+ return true; // done
+ }
+
+ } else {
+ no_roll (nframes, 0);
+ return true; // done
+ }
+
+ return false;
+}
+
diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc
new file mode 100644
index 0000000000..c228f3c47b
--- /dev/null
+++ b/libs/ardour/session_state.cc
@@ -0,0 +1,3219 @@
+/*
+ Copyright (C) 1999-2002 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.
+
+*/
+
+#define __STDC_FORMAT_MACROS 1
+#include <stdint.h>
+
+#include <algorithm>
+#include <fstream>
+#include <string>
+#include <cerrno>
+
+#include <sigc++/bind.h>
+
+#include <cstdio> /* snprintf(3) ... grrr */
+#include <cmath>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <climits>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <dirent.h>
+
+#ifdef HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#else
+#include <sys/param.h>
+#include <sys/mount.h>
+#endif
+
+#include <glibmm.h>
+#include <glibmm/thread.h>
+
+#include <midi++/mmc.h>
+#include <midi++/port.h>
+
+#include <pbd/error.h>
+#include <pbd/pathscanner.h>
+#include <pbd/pthread_utils.h>
+#include <pbd/search_path.h>
+#include <pbd/stacktrace.h>
+
+#include <ardour/audioengine.h>
+#include <ardour/configuration.h>
+#include <ardour/session.h>
+#include <ardour/session_directory.h>
+#include <ardour/session_utils.h>
+#include <ardour/session_state_utils.h>
+#include <ardour/buffer.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/midi_diskstream.h>
+#include <ardour/utils.h>
+#include <ardour/audioplaylist.h>
+#include <ardour/midi_playlist.h>
+#include <ardour/smf_source.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/silentfilesource.h>
+#include <ardour/sndfilesource.h>
+#include <ardour/midi_source.h>
+#include <ardour/sndfile_helpers.h>
+#include <ardour/auditioner.h>
+#include <ardour/export.h>
+#include <ardour/io_processor.h>
+#include <ardour/send.h>
+#include <ardour/processor.h>
+#include <ardour/bundle.h>
+#include <ardour/slave.h>
+#include <ardour/tempo.h>
+#include <ardour/audio_track.h>
+#include <ardour/midi_track.h>
+#include <ardour/cycle_timer.h>
+#include <ardour/utils.h>
+#include <ardour/named_selection.h>
+#include <ardour/version.h>
+#include <ardour/location.h>
+#include <ardour/audioregion.h>
+#include <ardour/midi_region.h>
+#include <ardour/crossfade.h>
+#include <ardour/control_protocol_manager.h>
+#include <ardour/region_factory.h>
+#include <ardour/source_factory.h>
+#include <ardour/playlist_factory.h>
+#include <ardour/filename_extensions.h>
+#include <ardour/directory_names.h>
+#include <ardour/template_utils.h>
+
+#include <control_protocol/control_protocol.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+void
+Session::first_stage_init (string fullpath, string snapshot_name)
+{
+ if (fullpath.length() == 0) {
+ destroy ();
+ throw failed_constructor();
+ }
+
+ char buf[PATH_MAX+1];
+ if (!realpath (fullpath.c_str(), buf) && (errno != ENOENT)) {
+ error << string_compose(_("Could not use path %1 (%s)"), buf, strerror(errno)) << endmsg;
+ destroy ();
+ throw failed_constructor();
+ }
+
+ _path = string(buf);
+
+ if (_path[_path.length()-1] != '/') {
+ _path += '/';
+ }
+
+ /* these two are just provisional settings. set_state()
+ will likely override them.
+ */
+
+ _name = _current_snapshot_name = snapshot_name;
+
+ set_history_depth (Config->get_history_depth());
+
+ _current_frame_rate = _engine.frame_rate ();
+ _nominal_frame_rate = _current_frame_rate;
+ _base_frame_rate = _current_frame_rate;
+
+ _tempo_map = new TempoMap (_current_frame_rate);
+ _tempo_map->StateChanged.connect (mem_fun (*this, &Session::tempo_map_changed));
+
+
+
+ g_atomic_int_set (&processing_prohibited, 0);
+ insert_cnt = 0;
+ _transport_speed = 0;
+ _last_transport_speed = 0;
+ auto_play_legal = false;
+ transport_sub_state = 0;
+ _transport_frame = 0;
+ last_stop_frame = 0;
+ end_location = new Location (0, 0, _("end"), Location::Flags ((Location::IsMark|Location::IsEnd)));
+ start_location = new Location (0, 0, _("start"), Location::Flags ((Location::IsMark|Location::IsStart)));
+ _end_location_is_free = true;
+ g_atomic_int_set (&_record_status, Disabled);
+ loop_changing = false;
+ play_loop = false;
+ _last_roll_location = 0;
+ _last_record_location = 0;
+ pending_locate_frame = 0;
+ pending_locate_roll = false;
+ pending_locate_flush = false;
+ audio_dstream_buffer_size = 0;
+ midi_dstream_buffer_size = 0;
+ state_tree = 0;
+ state_was_pending = false;
+ set_next_event ();
+ outbound_mtc_smpte_frame = 0;
+ next_quarter_frame_to_send = -1;
+ current_block_size = 0;
+ solo_update_disabled = false;
+ currently_soloing = false;
+ _have_captured = false;
+ _worst_output_latency = 0;
+ _worst_input_latency = 0;
+ _worst_track_latency = 0;
+ _state_of_the_state = StateOfTheState(CannotSave|InitialConnecting|Loading);
+
+ _slave = 0;
+ butler_mixdown_buffer = 0;
+ butler_gain_buffer = 0;
+ mmc = 0;
+ session_send_mmc = false;
+ session_send_mtc = false;
+ post_transport_work = PostTransportWork (0);
+ g_atomic_int_set (&butler_should_do_transport_work, 0);
+ g_atomic_int_set (&butler_active, 0);
+ g_atomic_int_set (&_playback_load, 100);
+ g_atomic_int_set (&_capture_load, 100);
+ g_atomic_int_set (&_playback_load_min, 100);
+ g_atomic_int_set (&_capture_load_min, 100);
+ _play_range = false;
+ waiting_to_start = false;
+ _exporting = false;
+ _gain_automation_buffer = 0;
+ _pan_automation_buffer = 0;
+ _npan_buffers = 0;
+ pending_abort = false;
+ destructive_index = 0;
+ current_trans = 0;
+ first_file_data_format_reset = true;
+ first_file_header_format_reset = true;
+ butler_thread = (pthread_t) 0;
+ //midi_thread = (pthread_t) 0;
+
+ AudioDiskstream::allocate_working_buffers();
+
+ /* default short fade = 15ms */
+
+ Crossfade::set_short_xfade_length ((nframes_t) floor (Config->get_short_xfade_seconds() * frame_rate()));
+ SndFileSource::setup_standard_crossfades (frame_rate());
+
+ last_mmc_step.tv_sec = 0;
+ last_mmc_step.tv_usec = 0;
+ step_speed = 0.0;
+
+ /* click sounds are unset by default, which causes us to internal
+ waveforms for clicks.
+ */
+
+ click_data = 0;
+ click_emphasis_data = 0;
+ click_length = 0;
+ click_emphasis_length = 0;
+ _clicking = false;
+
+ process_function = &Session::process_with_events;
+
+ if (Config->get_use_video_sync()) {
+ waiting_for_sync_offset = true;
+ } else {
+ waiting_for_sync_offset = false;
+ }
+
+ last_smpte_when = 0;
+ _smpte_offset = 0;
+ _smpte_offset_negative = true;
+ last_smpte_valid = false;
+
+ sync_time_vars ();
+
+ last_rr_session_dir = session_dirs.begin();
+ refresh_disk_space ();
+
+ // set_default_fade (0.2, 5.0); /* steepness, millisecs */
+
+ /* slave stuff */
+
+ average_slave_delta = 1800;
+ have_first_delta_accumulator = false;
+ delta_accumulator_cnt = 0;
+ slave_state = Stopped;
+
+ _engine.GraphReordered.connect (mem_fun (*this, &Session::graph_reordered));
+
+ /* These are all static "per-class" signals */
+
+ RegionFactory::CheckNewRegion.connect (mem_fun (*this, &Session::add_region));
+ SourceFactory::SourceCreated.connect (mem_fun (*this, &Session::add_source));
+ PlaylistFactory::PlaylistCreated.connect (mem_fun (*this, &Session::add_playlist));
+ Processor::ProcessorCreated.connect (mem_fun (*this, &Session::add_processor));
+ NamedSelection::NamedSelectionCreated.connect (mem_fun (*this, &Session::add_named_selection));
+ AutomationList::AutomationListCreated.connect (mem_fun (*this, &Session::add_automation_list));
+
+ Controllable::Destroyed.connect (mem_fun (*this, &Session::remove_controllable));
+
+ IO::PortCountChanged.connect (mem_fun (*this, &Session::ensure_buffers));
+
+ /* stop IO objects from doing stuff until we're ready for them */
+
+ IO::disable_panners ();
+ IO::disable_ports ();
+ IO::disable_connecting ();
+}
+
+int
+Session::second_stage_init (bool new_session)
+{
+ AudioFileSource::set_peak_dir (_session_dir->peak_path().to_string());
+
+ if (!new_session) {
+ if (load_state (_current_snapshot_name)) {
+ return -1;
+ }
+ remove_empty_sounds ();
+ }
+
+ if (start_butler_thread()) {
+ return -1;
+ }
+
+ if (start_midi_thread ()) {
+ return -1;
+ }
+
+ // set_state() will call setup_raid_path(), but if it's a new session we need
+ // to call setup_raid_path() here.
+
+ if (state_tree) {
+ if (set_state (*state_tree->root())) {
+ return -1;
+ }
+ } else {
+ setup_raid_path(_path);
+ }
+
+ /* we can't save till after ::when_engine_running() is called,
+ because otherwise we save state with no connections made.
+ therefore, we reset _state_of_the_state because ::set_state()
+ will have cleared it.
+
+ we also have to include Loading so that any events that get
+ generated between here and the end of ::when_engine_running()
+ will be processed directly rather than queued.
+ */
+
+ _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave|Loading);
+
+
+ _locations.changed.connect (mem_fun (this, &Session::locations_changed));
+ _locations.added.connect (mem_fun (this, &Session::locations_added));
+ setup_click_sounds (0);
+ setup_midi_control ();
+
+ /* Pay attention ... */
+
+ _engine.Halted.connect (mem_fun (*this, &Session::engine_halted));
+ _engine.Xrun.connect (mem_fun (*this, &Session::xrun_recovery));
+
+ try {
+ when_engine_running();
+ }
+
+ /* handle this one in a different way than all others, so that its clear what happened */
+
+ catch (AudioEngine::PortRegistrationFailure& err) {
+ error << _("Unable to create all required ports")
+ << endmsg;
+ return -1;
+ }
+
+ catch (...) {
+ return -1;
+ }
+
+ BootMessage (_("Reset Remote Controls"));
+
+ send_full_time_code (0);
+ _engine.transport_locate (0);
+ deliver_mmc (MIDI::MachineControl::cmdMmcReset, 0);
+ deliver_mmc (MIDI::MachineControl::cmdLocate, 0);
+
+ BootMessage (_("Reset Control Protocols"));
+
+ ControlProtocolManager::instance().set_session (*this);
+
+ if (new_session) {
+ _end_location_is_free = true;
+ } else {
+ _end_location_is_free = false;
+ }
+
+ _state_of_the_state = Clean;
+
+ DirtyChanged (); /* EMIT SIGNAL */
+
+ if (state_was_pending) {
+ save_state (_current_snapshot_name);
+ remove_pending_capture_state ();
+ state_was_pending = false;
+ }
+
+ BootMessage (_("Session loading complete"));
+
+ return 0;
+}
+
+string
+Session::raid_path () const
+{
+ SearchPath raid_search_path;
+
+ for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+ raid_search_path += sys::path((*i).path);
+ }
+
+ return raid_search_path.to_string ();
+}
+
+void
+Session::setup_raid_path (string path)
+{
+ if (path.empty()) {
+ return;
+ }
+
+ space_and_path sp;
+ string fspath;
+
+ session_dirs.clear ();
+
+ SearchPath search_path(path);
+ SearchPath sound_search_path;
+ SearchPath midi_search_path;
+
+ for (
+ SearchPath::const_iterator i = search_path.begin();
+ i != search_path.end();
+ ++i
+ )
+ {
+ sp.path = (*i).to_string ();
+ sp.blocks = 0; // not needed
+ session_dirs.push_back (sp);
+
+ SessionDirectory sdir(sp.path);
+
+ sound_search_path += sdir.sound_path ();
+ midi_search_path += sdir.midi_path ();
+ }
+
+ // set the AudioFileSource and SMFSource search path
+
+ AudioFileSource::set_search_path (sound_search_path.to_string ());
+ SMFSource::set_search_path (midi_search_path.to_string ());
+
+ // reset the round-robin soundfile path thingie
+
+ last_rr_session_dir = session_dirs.begin();
+}
+
+int
+Session::ensure_subdirs ()
+{
+ string dir;
+
+ 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 folder \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ dir = session_directory().sound_path().to_string();
+
+ if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
+ error << string_compose(_("Session: cannot create session sounds dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ dir = session_directory().midi_path().to_string();
+
+ if (g_mkdir_with_parents (dir.c_str(), 0755) < 0) {
+ error << string_compose(_("Session: cannot create session midi dir \"%1\" (%2)"), dir, strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ 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 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 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 */
+
+ if (!mix_template.empty()) {
+ std::string in_path = mix_template;
+
+ ifstream in(in_path.c_str());
+
+ if (in){
+ string out_path = _path;
+ out_path += _name;
+ out_path += statefile_suffix;
+
+ ofstream out(out_path.c_str());
+
+ if (out){
+ out << in.rdbuf();
+
+ // okay, session is set up. Treat like normal saved
+ // session from now on.
+
+ new_session = false;
+ return 0;
+
+ } else {
+ error << string_compose (_("Could not open %1 for writing mix template"), out_path)
+ << endmsg;
+ return -1;
+ }
+
+ } else {
+ error << string_compose (_("Could not open mix template %1 for reading"), in_path)
+ << endmsg;
+ return -1;
+ }
+
+ }
+
+ /* set initial start + end point */
+
+ start_location->set_end (0);
+ _locations.add (start_location);
+
+ end_location->set_end (initial_length);
+ _locations.add (end_location);
+
+ _state_of_the_state = Clean;
+
+ save_state ("");
+
+ return 0;
+}
+
+
+int
+Session::load_diskstreams (const XMLNode& node)
+{
+ XMLNodeList clist;
+ XMLNodeConstIterator citer;
+
+ clist = node.children();
+
+ for (citer = clist.begin(); citer != clist.end(); ++citer) {
+
+ try {
+ /* diskstreams added automatically by DiskstreamCreated handler */
+ if ((*citer)->name() == "AudioDiskstream" || (*citer)->name() == "DiskStream") {
+ boost::shared_ptr<AudioDiskstream> dstream (new AudioDiskstream (*this, **citer));
+ add_diskstream (dstream);
+ } else if ((*citer)->name() == "MidiDiskstream") {
+ boost::shared_ptr<MidiDiskstream> dstream (new MidiDiskstream (*this, **citer));
+ add_diskstream (dstream);
+ } else {
+ error << _("Session: unknown diskstream type in XML") << endmsg;
+ }
+ }
+
+ catch (failed_constructor& err) {
+ error << _("Session: could not load diskstream via XML state") << endmsg;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void
+Session::maybe_write_autosave()
+{
+ if (dirty() && record_status() != Recording) {
+ save_state("", true);
+ }
+}
+
+void
+Session::remove_pending_capture_state ()
+{
+ sys::path pending_state_file_path(_session_dir->root_path());
+
+ pending_state_file_path /= _current_snapshot_name + pending_suffix;
+
+ try
+ {
+ sys::remove (pending_state_file_path);
+ }
+ catch(sys::filesystem_error& ex)
+ {
+ error << string_compose(_("Could remove pending capture state at path \"%1\" (%2)"),
+ pending_state_file_path.to_string(), ex.what()) << endmsg;
+ }
+}
+
+/** Rename a state file.
+ * @param snapshot_name Snapshot name.
+ */
+void
+Session::rename_state (string old_name, string new_name)
+{
+ if (old_name == _current_snapshot_name || old_name == _name) {
+ /* refuse to rename the current snapshot or the "main" one */
+ return;
+ }
+
+ const string old_xml_filename = old_name + statefile_suffix;
+ const string new_xml_filename = new_name + statefile_suffix;
+
+ const sys::path old_xml_path = _session_dir->root_path() / old_xml_filename;
+ const sys::path new_xml_path = _session_dir->root_path() / new_xml_filename;
+
+ try
+ {
+ sys::rename (old_xml_path, new_xml_path);
+ }
+ catch (const sys::filesystem_error& err)
+ {
+ error << string_compose(_("could not rename snapshot %1 to %2 (%3)"),
+ old_name, new_name, err.what()) << endmsg;
+ }
+}
+
+/** Remove a state file.
+ * @param snapshot_name Snapshot name.
+ */
+void
+Session::remove_state (string snapshot_name)
+{
+ if (snapshot_name == _current_snapshot_name || snapshot_name == _name) {
+ // refuse to remove the current snapshot or the "main" one
+ return;
+ }
+
+ sys::path xml_path(_session_dir->root_path());
+
+ xml_path /= snapshot_name + statefile_suffix;
+
+ if (!create_backup_file (xml_path)) {
+ // don't remove it if a backup can't be made
+ // create_backup_file will log the error.
+ return;
+ }
+
+ // and delete it
+ sys::remove (xml_path);
+}
+
+int
+Session::save_state (string snapshot_name, bool pending)
+{
+ XMLTree tree;
+ sys::path xml_path(_session_dir->root_path());
+
+ if (_state_of_the_state & CannotSave) {
+ return 1;
+ }
+
+ if (!_engine.connected ()) {
+ error << _("Ardour's audio engine is not connected and state saving would lose all I/O connections. Session not saved")
+ << endmsg;
+ return 1;
+ }
+
+ /* tell sources we're saving first, in case they write out to a new file
+ * which should be saved with the state rather than the old one */
+ for (SourceMap::const_iterator i = sources.begin(); i != sources.end(); ++i)
+ i->second->session_saved();
+
+ tree.set_root (&get_state());
+
+ if (snapshot_name.empty()) {
+ snapshot_name = _current_snapshot_name;
+ }
+
+ if (!pending) {
+
+ /* proper save: use statefile_suffix (.ardour in English) */
+
+ xml_path /= snapshot_name + statefile_suffix;
+
+ /* make a backup copy of the old file */
+
+ if (sys::exists(xml_path) && !create_backup_file (xml_path)) {
+ // create_backup_file will log the error
+ return -1;
+ }
+
+ } else {
+
+ /* pending save: use pending_suffix (.pending in English) */
+ xml_path /= snapshot_name + pending_suffix;
+ }
+
+ sys::path tmp_path(_session_dir->root_path());
+
+ tmp_path /= snapshot_name + temp_suffix;
+
+ // cerr << "actually writing state to " << xml_path.to_string() << endl;
+
+ if (!tree.write (tmp_path.to_string())) {
+ error << string_compose (_("state could not be saved to %1"), tmp_path.to_string()) << endmsg;
+ sys::remove (tmp_path);
+ return -1;
+
+ } else {
+
+ if (rename (tmp_path.to_string().c_str(), xml_path.to_string().c_str()) != 0) {
+ error << string_compose (_("could not rename temporary session file %1 to %2"),
+ tmp_path.to_string(), xml_path.to_string()) << endmsg;
+ sys::remove (tmp_path);
+ return -1;
+ }
+ }
+
+ if (!pending) {
+
+ save_history (snapshot_name);
+
+ bool was_dirty = dirty();
+
+ _state_of_the_state = StateOfTheState (_state_of_the_state & ~Dirty);
+
+ if (was_dirty) {
+ DirtyChanged (); /* EMIT SIGNAL */
+ }
+
+ StateSaved (snapshot_name); /* EMIT SIGNAL */
+ }
+
+ return 0;
+}
+
+int
+Session::restore_state (string snapshot_name)
+{
+ if (load_state (snapshot_name) == 0) {
+ set_state (*state_tree->root());
+ }
+
+ return 0;
+}
+
+int
+Session::load_state (string snapshot_name)
+{
+ if (state_tree) {
+ delete state_tree;
+ state_tree = 0;
+ }
+
+ state_was_pending = false;
+
+ /* check for leftover pending state from a crashed capture attempt */
+
+ sys::path xmlpath(_session_dir->root_path());
+ xmlpath /= snapshot_name + pending_suffix;
+
+ if (sys::exists (xmlpath)) {
+
+ /* there is pending state from a crashed capture attempt */
+
+ if (AskAboutPendingState()) {
+ state_was_pending = true;
+ }
+ }
+
+ if (!state_was_pending) {
+ xmlpath = _session_dir->root_path();
+ xmlpath /= snapshot_name + statefile_suffix;
+ }
+
+ if (!sys::exists (xmlpath)) {
+ error << string_compose(_("%1: session state information file \"%2\" doesn't exist!"), _name, xmlpath.to_string()) << endmsg;
+ return 1;
+ }
+
+ state_tree = new XMLTree;
+
+ set_dirty();
+
+ if (!state_tree->read (xmlpath.to_string())) {
+ error << string_compose(_("Could not understand ardour file %1"), xmlpath.to_string()) << endmsg;
+ delete state_tree;
+ state_tree = 0;
+ return -1;
+ }
+
+ XMLNode& root (*state_tree->root());
+
+ if (root.name() != X_("Session")) {
+ error << string_compose (_("Session file %1 is not an Ardour session"), xmlpath.to_string()) << endmsg;
+ delete state_tree;
+ state_tree = 0;
+ return -1;
+ }
+
+ const XMLProperty* prop;
+ bool is_old = false;
+
+ if ((prop = root.property ("version")) == 0) {
+ /* no version implies very old version of Ardour */
+ is_old = true;
+ } else {
+ int major_version;
+ major_version = atoi (prop->value().c_str()); // grab just the first number before the period
+ if (major_version < 2) {
+ is_old = true;
+ }
+ }
+
+ if (is_old) {
+
+ sys::path backup_path(_session_dir->root_path());
+
+ backup_path /= snapshot_name + "-1" + statefile_suffix;
+
+ // only create a backup once
+ if (sys::exists (backup_path)) {
+ return 0;
+ }
+
+ info << string_compose (_("Copying old session file %1 to %2\nUse %2 with Ardour versions before 2.0 from now on"),
+ xmlpath.to_string(), backup_path.to_string())
+ << endmsg;
+
+ try
+ {
+ sys::copy_file (xmlpath, backup_path);
+ }
+ catch(sys::filesystem_error& ex)
+ {
+ error << string_compose (_("Unable to make backup of state file %1 (%2)"),
+ xmlpath.to_string(), ex.what())
+ << endmsg;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+Session::load_options (const XMLNode& node)
+{
+ XMLNode* child;
+ XMLProperty* prop;
+ LocaleGuard lg (X_("POSIX"));
+
+ Config->set_variables (node, ConfigVariableBase::Session);
+
+ if ((child = find_named_node (node, "end-marker-is-free")) != 0) {
+ if ((prop = child->property ("val")) != 0) {
+ _end_location_is_free = (prop->value() == "yes");
+ }
+ }
+
+ return 0;
+}
+
+bool
+Session::save_config_options_predicate (ConfigVariableBase::Owner owner) const
+{
+ const ConfigVariableBase::Owner modified_by_session_or_user = (ConfigVariableBase::Owner)
+ (ConfigVariableBase::Session|ConfigVariableBase::Interface);
+
+ return owner & modified_by_session_or_user;
+}
+
+XMLNode&
+Session::get_options () const
+{
+ XMLNode* child;
+ LocaleGuard lg (X_("POSIX"));
+
+ XMLNode& option_root = Config->get_variables (mem_fun (*this, &Session::save_config_options_predicate));
+
+ child = option_root.add_child ("end-marker-is-free");
+ child->add_property ("val", _end_location_is_free ? "yes" : "no");
+
+ return option_root;
+}
+
+XMLNode&
+Session::get_state()
+{
+ return state(true);
+}
+
+XMLNode&
+Session::get_template()
+{
+ /* if we don't disable rec-enable, diskstreams
+ will believe they need to store their capture
+ sources in their state node.
+ */
+
+ disable_record (false);
+
+ return state(false);
+}
+
+XMLNode&
+Session::state(bool full_state)
+{
+ XMLNode* node = new XMLNode("Session");
+ XMLNode* child;
+
+ // store libardour version, just in case
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%d.%d.%d", libardour3_major_version, libardour3_minor_version, libardour3_micro_version);
+ node->add_property("version", string(buf));
+
+ /* store configuration settings */
+
+ if (full_state) {
+
+ node->add_property ("name", _name);
+ snprintf (buf, sizeof (buf), "%" PRId32, _nominal_frame_rate);
+ node->add_property ("sample-rate", buf);
+
+ if (session_dirs.size() > 1) {
+
+ string p;
+
+ vector<space_and_path>::iterator i = session_dirs.begin();
+ vector<space_and_path>::iterator next;
+
+ ++i; /* skip the first one */
+ next = i;
+ ++next;
+
+ while (i != session_dirs.end()) {
+
+ p += (*i).path;
+
+ if (next != session_dirs.end()) {
+ p += ':';
+ } else {
+ break;
+ }
+
+ ++next;
+ ++i;
+ }
+
+ child = node->add_child ("Path");
+ child->add_content (p);
+ }
+ }
+
+ /* save the ID counter */
+
+ snprintf (buf, sizeof (buf), "%" PRIu64, ID::counter());
+ node->add_property ("id-counter", buf);
+
+ /* various options */
+
+ node->add_child_nocopy (get_options());
+
+ child = node->add_child ("Sources");
+
+ if (full_state) {
+ Glib::Mutex::Lock sl (source_lock);
+
+ for (SourceMap::iterator siter = sources.begin(); siter != sources.end(); ++siter) {
+
+ /* Don't save information about AudioFileSources that are empty */
+
+ boost::shared_ptr<AudioFileSource> fs;
+
+ if ((fs = boost::dynamic_pointer_cast<AudioFileSource> (siter->second)) != 0) {
+
+ /* Don't save sources that are empty, unless they're destructive (which are OK
+ if they are empty, because we will re-use them every time.)
+ */
+
+ if (!fs->destructive()) {
+ if (fs->length() == 0) {
+ continue;
+ }
+ }
+ }
+
+ child->add_child_nocopy (siter->second->get_state());
+ }
+ }
+
+ child = node->add_child ("Regions");
+
+ if (full_state) {
+ Glib::Mutex::Lock rl (region_lock);
+
+ for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
+
+ /* only store regions not attached to playlists */
+
+ if (i->second->playlist() == 0) {
+ child->add_child_nocopy (i->second->state (true));
+ }
+ }
+ }
+
+ child = node->add_child ("DiskStreams");
+
+ {
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ child->add_child_nocopy ((*i)->get_state());
+ }
+ }
+ }
+
+ if (full_state) {
+ node->add_child_nocopy (_locations.get_state());
+ } else {
+ // for a template, just create a new Locations, populate it
+ // with the default start and end, and get the state for that.
+ Locations loc;
+ Location* start = new Location(0, 0, _("start"), Location::Flags ((Location::IsMark|Location::IsStart)));
+ Location* end = new Location(0, 0, _("end"), Location::Flags ((Location::IsMark|Location::IsEnd)));
+ start->set_end(0);
+ loc.add (start);
+ end->set_end(compute_initial_length());
+ loc.add (end);
+ node->add_child_nocopy (loc.get_state());
+ }
+
+ child = node->add_child ("Bundles");
+ {
+ Glib::Mutex::Lock lm (bundle_lock);
+ for (BundleList::iterator i = _bundles.begin(); i != _bundles.end(); ++i) {
+ boost::shared_ptr<UserBundle> b = boost::dynamic_pointer_cast<UserBundle> (*i);
+ if (b) {
+ child->add_child_nocopy (b->get_state());
+ }
+ }
+ }
+
+ child = node->add_child ("Routes");
+ {
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ RoutePublicOrderSorter cmp;
+ RouteList public_order (*r);
+ public_order.sort (cmp);
+
+ for (RouteList::iterator i = public_order.begin(); i != public_order.end(); ++i) {
+ if (!(*i)->is_hidden()) {
+ if (full_state) {
+ child->add_child_nocopy ((*i)->get_state());
+ } else {
+ child->add_child_nocopy ((*i)->get_template());
+ }
+ }
+ }
+ }
+
+
+ child = node->add_child ("EditGroups");
+ for (list<RouteGroup *>::iterator i = edit_groups.begin(); i != edit_groups.end(); ++i) {
+ child->add_child_nocopy ((*i)->get_state());
+ }
+
+ child = node->add_child ("MixGroups");
+ for (list<RouteGroup *>::iterator i = mix_groups.begin(); i != mix_groups.end(); ++i) {
+ child->add_child_nocopy ((*i)->get_state());
+ }
+
+ child = node->add_child ("Playlists");
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ if (!(*i)->hidden()) {
+ if (!(*i)->empty()) {
+ if (full_state) {
+ child->add_child_nocopy ((*i)->get_state());
+ } else {
+ child->add_child_nocopy ((*i)->get_template());
+ }
+ }
+ }
+ }
+
+ child = node->add_child ("UnusedPlaylists");
+ for (PlaylistList::iterator i = unused_playlists.begin(); i != unused_playlists.end(); ++i) {
+ if (!(*i)->hidden()) {
+ if (!(*i)->empty()) {
+ if (full_state) {
+ child->add_child_nocopy ((*i)->get_state());
+ } else {
+ child->add_child_nocopy ((*i)->get_template());
+ }
+ }
+ }
+ }
+
+
+ if (_click_io) {
+ child = node->add_child ("Click");
+ child->add_child_nocopy (_click_io->state (full_state));
+ }
+
+ if (full_state) {
+ child = node->add_child ("NamedSelections");
+ for (NamedSelectionList::iterator i = named_selections.begin(); i != named_selections.end(); ++i) {
+ if (full_state) {
+ child->add_child_nocopy ((*i)->get_state());
+ }
+ }
+ }
+
+ node->add_child_nocopy (_tempo_map->get_state());
+
+ node->add_child_nocopy (get_control_protocol_state());
+
+ if (_extra_xml) {
+ node->add_child_copy (*_extra_xml);
+ }
+
+ return *node;
+}
+
+XMLNode&
+Session::get_control_protocol_state ()
+{
+ ControlProtocolManager& cpm (ControlProtocolManager::instance());
+ return cpm.get_state();
+}
+
+int
+Session::set_state (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNode* child;
+ const XMLProperty* prop;
+ int ret = -1;
+
+ _state_of_the_state = StateOfTheState (_state_of_the_state|CannotSave);
+
+ if (node.name() != X_("Session")){
+ fatal << _("programming error: Session: incorrect XML node sent to set_state()") << endmsg;
+ return -1;
+ }
+
+ if ((prop = node.property ("name")) != 0) {
+ _name = prop->value ();
+ }
+
+ if ((prop = node.property (X_("sample-rate"))) != 0) {
+
+ _nominal_frame_rate = atoi (prop->value());
+
+ if (_nominal_frame_rate != _current_frame_rate) {
+ if (AskAboutSampleRateMismatch (_nominal_frame_rate, _current_frame_rate)) {
+ return -1;
+ }
+ }
+ }
+
+ setup_raid_path(_session_dir->root_path().to_string());
+
+ if ((prop = node.property (X_("id-counter"))) != 0) {
+ uint64_t x;
+ sscanf (prop->value().c_str(), "%" PRIu64, &x);
+ ID::init_counter (x);
+ } else {
+ /* old sessions used a timebased counter, so fake
+ the startup ID counter based on a standard
+ timestamp.
+ */
+ time_t now;
+ time (&now);
+ ID::init_counter (now);
+ }
+
+
+ IO::disable_ports ();
+ IO::disable_connecting ();
+
+ /* Object loading order:
+
+ MIDI Control
+ Path
+ extra
+ Options/Config
+ Locations
+ Sources
+ AudioRegions
+ AudioDiskstreams
+ Connections
+ Routes
+ EditGroups
+ MixGroups
+ Click
+ ControlProtocols
+ */
+
+ if (use_config_midi_ports ()) {
+ }
+
+ if ((child = find_named_node (node, "extra")) != 0) {
+ _extra_xml = new XMLNode (*child);
+ }
+
+ if (((child = find_named_node (node, "Options")) != 0)) { /* old style */
+ load_options (*child);
+ } else if ((child = find_named_node (node, "Config")) != 0) { /* new style */
+ load_options (*child);
+ } else {
+ error << _("Session: XML state has no options section") << endmsg;
+ }
+
+ if ((child = find_named_node (node, "Locations")) == 0) {
+ error << _("Session: XML state has no locations section") << endmsg;
+ goto out;
+ } else if (_locations.set_state (*child)) {
+ goto out;
+ }
+
+ Location* location;
+
+ if ((location = _locations.auto_loop_location()) != 0) {
+ set_auto_loop_location (location);
+ }
+
+ if ((location = _locations.auto_punch_location()) != 0) {
+ set_auto_punch_location (location);
+ }
+
+ if ((location = _locations.end_location()) == 0) {
+ _locations.add (end_location);
+ } else {
+ delete end_location;
+ end_location = location;
+ }
+
+ if ((location = _locations.start_location()) == 0) {
+ _locations.add (start_location);
+ } else {
+ delete start_location;
+ start_location = location;
+ }
+
+ AudioFileSource::set_header_position_offset (start_location->start());
+
+ if ((child = find_named_node (node, "Sources")) == 0) {
+ error << _("Session: XML state has no sources section") << endmsg;
+ goto out;
+ } else if (load_sources (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "Regions")) == 0) {
+ error << _("Session: XML state has no Regions section") << endmsg;
+ goto out;
+ } else if (load_regions (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "Playlists")) == 0) {
+ error << _("Session: XML state has no playlists section") << endmsg;
+ goto out;
+ } else if (load_playlists (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "UnusedPlaylists")) == 0) {
+ // this is OK
+ } else if (load_unused_playlists (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "NamedSelections")) != 0) {
+ if (load_named_selections (*child)) {
+ goto out;
+ }
+ }
+
+ if ((child = find_named_node (node, "DiskStreams")) == 0) {
+ error << _("Session: XML state has no diskstreams section") << endmsg;
+ goto out;
+ } else if (load_diskstreams (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "Bundles")) == 0) {
+ warning << _("Session: XML state has no bundles section (2.0 session?)") << endmsg;
+ //goto out;
+ } else {
+ /* We can't load Bundles yet as they need to be able
+ to convert from port names to Port objects, which can't happen until
+ later */
+ _bundle_xml_node = new XMLNode (*child);
+ }
+
+ if ((child = find_named_node (node, "EditGroups")) == 0) {
+ error << _("Session: XML state has no edit groups section") << endmsg;
+ goto out;
+ } else if (load_edit_groups (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "MixGroups")) == 0) {
+ error << _("Session: XML state has no mix groups section") << endmsg;
+ goto out;
+ } else if (load_mix_groups (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "TempoMap")) == 0) {
+ error << _("Session: XML state has no Tempo Map section") << endmsg;
+ goto out;
+ } else if (_tempo_map->set_state (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "Routes")) == 0) {
+ error << _("Session: XML state has no routes section") << endmsg;
+ goto out;
+ } else if (load_routes (*child)) {
+ goto out;
+ }
+
+ if ((child = find_named_node (node, "Click")) == 0) {
+ warning << _("Session: XML state has no click section") << endmsg;
+ } else if (_click_io) {
+ _click_io->set_state (*child);
+ }
+
+ if ((child = find_named_node (node, "ControlProtocols")) != 0) {
+ ControlProtocolManager::instance().set_protocol_states (*child);
+ }
+
+ /* here beginneth the second phase ... */
+
+ StateReady (); /* EMIT SIGNAL */
+
+ return 0;
+
+ out:
+ return ret;
+}
+
+int
+Session::load_routes (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ RouteList new_routes;
+
+ nlist = node.children();
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ boost::shared_ptr<Route> route (XMLRouteFactory (**niter));
+
+ if (route == 0) {
+ error << _("Session: cannot create Route from XML description.") << endmsg;
+ return -1;
+ }
+
+ BootMessage (string_compose (_("Loaded track/bus %1"), route->name()));
+
+ new_routes.push_back (route);
+ }
+
+ add_routes (new_routes, false);
+
+ return 0;
+}
+
+boost::shared_ptr<Route>
+Session::XMLRouteFactory (const XMLNode& node)
+{
+ if (node.name() != "Route") {
+ return boost::shared_ptr<Route> ((Route*) 0);
+ }
+
+ bool has_diskstream = (node.property ("diskstream") != 0 || node.property ("diskstream-id") != 0);
+
+ DataType type = DataType::AUDIO;
+ const XMLProperty* prop = node.property("default-type");
+ if (prop)
+ type = DataType(prop->value());
+
+ assert(type != DataType::NIL);
+
+ if (has_diskstream) {
+ if (type == DataType::AUDIO) {
+ boost::shared_ptr<Route> ret (new AudioTrack (*this, node));
+ return ret;
+ } else {
+ boost::shared_ptr<Route> ret (new MidiTrack (*this, node));
+ return ret;
+ }
+ } else {
+ boost::shared_ptr<Route> ret (new Route (*this, node));
+ return ret;
+ }
+}
+
+int
+Session::load_regions (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ boost::shared_ptr<Region> region;
+
+ nlist = node.children();
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((region = XMLRegionFactory (**niter, false)) == 0) {
+ error << _("Session: cannot create Region from XML description.");
+ const XMLProperty *name = (**niter).property("name");
+
+ if (name) {
+ error << " " << string_compose (_("Can not load state for region '%1'"), name->value());
+ }
+
+ error << endmsg;
+ }
+ }
+
+ return 0;
+}
+
+boost::shared_ptr<Region>
+Session::XMLRegionFactory (const XMLNode& node, bool full)
+{
+ const XMLProperty* type = node.property("type");
+
+ try {
+
+ if ( !type || type->value() == "audio" ) {
+
+ return boost::shared_ptr<Region>(XMLAudioRegionFactory (node, full));
+
+ } else if (type->value() == "midi") {
+
+ return boost::shared_ptr<Region>(XMLMidiRegionFactory (node, full));
+
+ }
+
+ } catch (failed_constructor& err) {
+ return boost::shared_ptr<Region> ();
+ }
+
+ return boost::shared_ptr<Region> ();
+}
+
+boost::shared_ptr<AudioRegion>
+Session::XMLAudioRegionFactory (const XMLNode& node, bool full)
+{
+ const XMLProperty* prop;
+ boost::shared_ptr<Source> source;
+ boost::shared_ptr<AudioSource> as;
+ SourceList sources;
+ SourceList master_sources;
+ uint32_t nchans = 1;
+ char buf[128];
+
+ if (node.name() != X_("Region")) {
+ return boost::shared_ptr<AudioRegion>();
+ }
+
+ if ((prop = node.property (X_("channels"))) != 0) {
+ nchans = atoi (prop->value().c_str());
+ }
+
+ if ((prop = node.property ("name")) == 0) {
+ cerr << "no name for this region\n";
+ abort ();
+ }
+
+ if ((prop = node.property (X_("source-0"))) == 0) {
+ if ((prop = node.property ("source")) == 0) {
+ error << _("Session: XMLNode describing a AudioRegion is incomplete (no source)") << endmsg;
+ return boost::shared_ptr<AudioRegion>();
+ }
+ }
+
+ PBD::ID s_id (prop->value());
+
+ if ((source = source_by_id (s_id)) == 0) {
+ error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), s_id) << endmsg;
+ return boost::shared_ptr<AudioRegion>();
+ }
+
+ as = boost::dynamic_pointer_cast<AudioSource>(source);
+ if (!as) {
+ error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), s_id) << endmsg;
+ return boost::shared_ptr<AudioRegion>();
+ }
+
+ sources.push_back (as);
+
+ /* pickup other channels */
+
+ for (uint32_t n=1; n < nchans; ++n) {
+ snprintf (buf, sizeof(buf), X_("source-%d"), n);
+ if ((prop = node.property (buf)) != 0) {
+
+ PBD::ID id2 (prop->value());
+
+ if ((source = source_by_id (id2)) == 0) {
+ error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg;
+ return boost::shared_ptr<AudioRegion>();
+ }
+
+ as = boost::dynamic_pointer_cast<AudioSource>(source);
+ if (!as) {
+ error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg;
+ return boost::shared_ptr<AudioRegion>();
+ }
+ sources.push_back (as);
+ }
+ }
+
+ for (uint32_t n=1; n < nchans; ++n) {
+ snprintf (buf, sizeof(buf), X_("master-source-%d"), n);
+ if ((prop = node.property (buf)) != 0) {
+
+ PBD::ID id2 (prop->value());
+
+ if ((source = source_by_id (id2)) == 0) {
+ error << string_compose(_("Session: XMLNode describing a AudioRegion references an unknown source id =%1"), id2) << endmsg;
+ return boost::shared_ptr<AudioRegion>();
+ }
+
+ as = boost::dynamic_pointer_cast<AudioSource>(source);
+ if (!as) {
+ error << string_compose(_("Session: XMLNode describing a AudioRegion references a non-audio source id =%1"), id2) << endmsg;
+ return boost::shared_ptr<AudioRegion>();
+ }
+ master_sources.push_back (as);
+ }
+ }
+
+ try {
+ boost::shared_ptr<AudioRegion> region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, node)));
+
+ /* a final detail: this is the one and only place that we know how long missing files are */
+
+ if (region->whole_file()) {
+ for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) {
+ boost::shared_ptr<SilentFileSource> sfp = boost::dynamic_pointer_cast<SilentFileSource> (*sx);
+ if (sfp) {
+ sfp->set_length (region->length());
+ }
+ }
+ }
+
+ if (!master_sources.empty()) {
+ if (master_sources.size() == nchans) {
+ error << _("Session: XMLNode describing an AudioRegion is missing some master sources; ignored") << endmsg;
+ } else {
+ region->set_master_sources (master_sources);
+ }
+ }
+
+ return region;
+
+ }
+
+ catch (failed_constructor& err) {
+ return boost::shared_ptr<AudioRegion>();
+ }
+}
+
+boost::shared_ptr<MidiRegion>
+Session::XMLMidiRegionFactory (const XMLNode& node, bool full)
+{
+ const XMLProperty* prop;
+ boost::shared_ptr<Source> source;
+ boost::shared_ptr<MidiSource> ms;
+ SourceList sources;
+ uint32_t nchans = 1;
+
+ if (node.name() != X_("Region")) {
+ return boost::shared_ptr<MidiRegion>();
+ }
+
+ if ((prop = node.property (X_("channels"))) != 0) {
+ nchans = atoi (prop->value().c_str());
+ }
+
+ if ((prop = node.property ("name")) == 0) {
+ cerr << "no name for this region\n";
+ abort ();
+ }
+
+ // Multiple midi channels? that's just crazy talk
+ assert(nchans == 1);
+
+ if ((prop = node.property (X_("source-0"))) == 0) {
+ if ((prop = node.property ("source")) == 0) {
+ error << _("Session: XMLNode describing a MidiRegion is incomplete (no source)") << endmsg;
+ return boost::shared_ptr<MidiRegion>();
+ }
+ }
+
+ PBD::ID s_id (prop->value());
+
+ if ((source = source_by_id (s_id)) == 0) {
+ error << string_compose(_("Session: XMLNode describing a MidiRegion references an unknown source id =%1"), s_id) << endmsg;
+ return boost::shared_ptr<MidiRegion>();
+ }
+
+ ms = boost::dynamic_pointer_cast<MidiSource>(source);
+ if (!ms) {
+ error << string_compose(_("Session: XMLNode describing a MidiRegion references a non-midi source id =%1"), s_id) << endmsg;
+ return boost::shared_ptr<MidiRegion>();
+ }
+
+ sources.push_back (ms);
+
+ try {
+ boost::shared_ptr<MidiRegion> region (boost::dynamic_pointer_cast<MidiRegion> (RegionFactory::create (sources, node)));
+ /* a final detail: this is the one and only place that we know how long missing files are */
+
+ if (region->whole_file()) {
+ for (SourceList::iterator sx = sources.begin(); sx != sources.end(); ++sx) {
+ boost::shared_ptr<SilentFileSource> sfp = boost::dynamic_pointer_cast<SilentFileSource> (*sx);
+ if (sfp) {
+ sfp->set_length (region->length());
+ }
+ }
+ }
+
+ return region;
+ }
+
+ catch (failed_constructor& err) {
+ return boost::shared_ptr<MidiRegion>();
+ }
+}
+
+XMLNode&
+Session::get_sources_as_xml ()
+
+{
+ XMLNode* node = new XMLNode (X_("Sources"));
+ Glib::Mutex::Lock lm (source_lock);
+
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+ node->add_child_nocopy (i->second->get_state());
+ }
+
+ return *node;
+}
+
+string
+Session::path_from_region_name (DataType type, string name, string identifier)
+{
+ char buf[PATH_MAX+1];
+ uint32_t n;
+ SessionDirectory sdir(get_best_session_directory_for_new_source());
+ sys::path source_dir = ((type == DataType::AUDIO)
+ ? sdir.sound_path() : sdir.midi_path());
+
+ string ext = ((type == DataType::AUDIO) ? ".wav" : ".mid");
+
+ for (n = 0; n < 999999; ++n) {
+ if (identifier.length()) {
+ snprintf (buf, sizeof(buf), "%s%s%" PRIu32 "%s", name.c_str(),
+ identifier.c_str(), n, ext.c_str());
+ } else {
+ snprintf (buf, sizeof(buf), "%s-%" PRIu32 "%s", name.c_str(),
+ n, ext.c_str());
+ }
+
+ sys::path source_path = source_dir / buf;
+
+ if (!sys::exists (source_path)) {
+ return source_path.to_string();
+ }
+ }
+
+ error << string_compose (_("cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"),
+ name, identifier)
+ << endmsg;
+
+ return "";
+}
+
+
+int
+Session::load_sources (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ boost::shared_ptr<Source> source;
+
+ nlist = node.children();
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ try {
+ if ((source = XMLSourceFactory (**niter)) == 0) {
+ error << _("Session: cannot create Source from XML description.") << endmsg;
+ }
+ }
+
+ catch (non_existent_source& err) {
+ warning << _("A sound file is missing. It will be replaced by silence.") << endmsg;
+ source = SourceFactory::createSilent (*this, **niter, max_frames, _current_frame_rate);
+ }
+ }
+
+ return 0;
+}
+
+boost::shared_ptr<Source>
+Session::XMLSourceFactory (const XMLNode& node)
+{
+ if (node.name() != "Source") {
+ return boost::shared_ptr<Source>();
+ }
+
+ try {
+ /* note: do peak building in another thread when loading session state */
+ return SourceFactory::create (*this, node, true);
+ }
+
+ catch (failed_constructor& err) {
+ error << _("Found a sound file that cannot be used by Ardour. Talk to the progammers.") << endmsg;
+ return boost::shared_ptr<Source>();
+ }
+}
+
+int
+Session::save_template (string template_name)
+{
+ XMLTree tree;
+
+ if (_state_of_the_state & CannotSave) {
+ return -1;
+ }
+
+ sys::path user_template_dir(user_template_directory());
+
+ try
+ {
+ sys::create_directories (user_template_dir);
+ }
+ catch(sys::filesystem_error& ex)
+ {
+ error << string_compose(_("Could not create mix templates directory \"%1\" (%2)"),
+ user_template_dir.to_string(), ex.what()) << endmsg;
+ return -1;
+ }
+
+ tree.set_root (&get_template());
+
+ sys::path template_file_path(user_template_dir);
+ template_file_path /= template_name + template_suffix;
+
+ if (sys::exists (template_file_path))
+ {
+ warning << string_compose(_("Template \"%1\" already exists - new version not created"),
+ template_file_path.to_string()) << endmsg;
+ return -1;
+ }
+
+ if (!tree.write (template_file_path.to_string())) {
+ error << _("mix template not saved") << endmsg;
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+Session::refresh_disk_space ()
+{
+#if HAVE_SYS_VFS_H
+ struct statfs statfsbuf;
+ vector<space_and_path>::iterator i;
+ Glib::Mutex::Lock lm (space_lock);
+ double scale;
+
+ /* get freespace on every FS that is part of the session path */
+
+ _total_free_4k_blocks = 0;
+
+ for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+ statfs ((*i).path.c_str(), &statfsbuf);
+
+ scale = statfsbuf.f_bsize/4096.0;
+
+ (*i).blocks = (uint32_t) floor (statfsbuf.f_bavail * scale);
+ _total_free_4k_blocks += (*i).blocks;
+ }
+#endif
+}
+
+string
+Session::get_best_session_directory_for_new_source ()
+{
+ vector<space_and_path>::iterator i;
+ string result = _session_dir->root_path().to_string();
+
+ /* handle common case without system calls */
+
+ if (session_dirs.size() == 1) {
+ return result;
+ }
+
+ /* OK, here's the algorithm we're following here:
+
+ We want to select which directory to use for
+ the next file source to be created. Ideally,
+ we'd like to use a round-robin process so as to
+ get maximum performance benefits from splitting
+ the files across multiple disks.
+
+ However, in situations without much diskspace, an
+ RR approach may end up filling up a filesystem
+ with new files while others still have space.
+ Its therefore important to pay some attention to
+ the freespace in the filesystem holding each
+ directory as well. However, if we did that by
+ itself, we'd keep creating new files in the file
+ system with the most space until it was as full
+ as all others, thus negating any performance
+ benefits of this RAID-1 like approach.
+
+ So, we use a user-configurable space threshold. If
+ there are at least 2 filesystems with more than this
+ much space available, we use RR selection between them.
+ If not, then we pick the filesystem with the most space.
+
+ This gets a good balance between the two
+ approaches.
+ */
+
+ refresh_disk_space ();
+
+ int free_enough = 0;
+
+ for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+ if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) {
+ free_enough++;
+ }
+ }
+
+ if (free_enough >= 2) {
+ /* use RR selection process, ensuring that the one
+ picked works OK.
+ */
+
+ i = last_rr_session_dir;
+
+ do {
+ if (++i == session_dirs.end()) {
+ i = session_dirs.begin();
+ }
+
+ if ((*i).blocks * 4096 >= Config->get_disk_choice_space_threshold()) {
+ if (create_session_directory ((*i).path)) {
+ result = (*i).path;
+ last_rr_session_dir = i;
+ return result;
+ }
+ }
+
+ } while (i != last_rr_session_dir);
+
+ } else {
+
+ /* pick FS with the most freespace (and that
+ seems to actually work ...)
+ */
+
+ vector<space_and_path> sorted;
+ space_and_path_ascending_cmp cmp;
+
+ sorted = session_dirs;
+ sort (sorted.begin(), sorted.end(), cmp);
+
+ for (i = sorted.begin(); i != sorted.end(); ++i) {
+ if (create_session_directory ((*i).path)) {
+ result = (*i).path;
+ last_rr_session_dir = i;
+ return result;
+ }
+ }
+ }
+
+ return result;
+}
+
+int
+Session::load_playlists (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ boost::shared_ptr<Playlist> playlist;
+
+ nlist = node.children();
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ if ((playlist = XMLPlaylistFactory (**niter)) == 0) {
+ error << _("Session: cannot create Playlist from XML description.") << endmsg;
+ }
+ }
+
+ return 0;
+}
+
+int
+Session::load_unused_playlists (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ boost::shared_ptr<Playlist> playlist;
+
+ nlist = node.children();
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ if ((playlist = XMLPlaylistFactory (**niter)) == 0) {
+ error << _("Session: cannot create Playlist from XML description.") << endmsg;
+ continue;
+ }
+
+ // now manually untrack it
+
+ track_playlist (false, boost::weak_ptr<Playlist> (playlist));
+ }
+
+ return 0;
+}
+
+boost::shared_ptr<Playlist>
+Session::XMLPlaylistFactory (const XMLNode& node)
+{
+ try {
+ return PlaylistFactory::create (*this, node);
+ }
+
+ catch (failed_constructor& err) {
+ return boost::shared_ptr<Playlist>();
+ }
+}
+
+int
+Session::load_named_selections (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ NamedSelection *ns;
+
+ nlist = node.children();
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ if ((ns = XMLNamedSelectionFactory (**niter)) == 0) {
+ error << _("Session: cannot create Named Selection from XML description.") << endmsg;
+ }
+ }
+
+ return 0;
+}
+
+NamedSelection *
+Session::XMLNamedSelectionFactory (const XMLNode& node)
+{
+ try {
+ return new NamedSelection (*this, node);
+ }
+
+ catch (failed_constructor& err) {
+ return 0;
+ }
+}
+
+string
+Session::automation_dir () const
+{
+ string res = _path;
+ res += "automation/";
+ return res;
+}
+
+string
+Session::analysis_dir () const
+{
+ string res = _path;
+ res += "analysis/";
+ return res;
+}
+
+int
+Session::load_bundles (XMLNode const & node)
+{
+ XMLNodeList nlist = node.children();
+ XMLNodeConstIterator niter;
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == "InputBundle") {
+ add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, true)));
+ } else if ((*niter)->name() == "OutputBundle") {
+ add_bundle (boost::shared_ptr<UserBundle> (new UserBundle (**niter, false)));
+ } else {
+ error << string_compose(_("Unknown node \"%1\" found in Bundles list from state file"), (*niter)->name()) << endmsg;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int
+Session::load_edit_groups (const XMLNode& node)
+{
+ return load_route_groups (node, true);
+}
+
+int
+Session::load_mix_groups (const XMLNode& node)
+{
+ return load_route_groups (node, false);
+}
+
+int
+Session::load_route_groups (const XMLNode& node, bool edit)
+{
+ XMLNodeList nlist = node.children();
+ XMLNodeConstIterator niter;
+ RouteGroup* rg;
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ if ((*niter)->name() == "RouteGroup") {
+ if (edit) {
+ rg = add_edit_group ("");
+ rg->set_state (**niter);
+ } else {
+ rg = add_mix_group ("");
+ rg->set_state (**niter);
+ }
+ }
+ }
+
+ return 0;
+}
+
+void
+Session::auto_save()
+{
+ save_state (_current_snapshot_name);
+}
+
+static bool
+state_file_filter (const string &str, void *arg)
+{
+ return (str.length() > strlen(statefile_suffix) &&
+ str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
+}
+
+struct string_cmp {
+ bool operator()(const string* a, const string* b) {
+ return *a < *b;
+ }
+};
+
+static string*
+remove_end(string* state)
+{
+ string statename(*state);
+
+ string::size_type start,end;
+ if ((start = statename.find_last_of ('/')) != string::npos) {
+ statename = statename.substr (start+1);
+ }
+
+ if ((end = statename.rfind(".ardour")) == string::npos) {
+ end = statename.length();
+ }
+
+ return new string(statename.substr (0, end));
+}
+
+vector<string *> *
+Session::possible_states (string path)
+{
+ PathScanner scanner;
+ vector<string*>* states = scanner (path, state_file_filter, 0, false, false);
+
+ transform(states->begin(), states->end(), states->begin(), remove_end);
+
+ string_cmp cmp;
+ sort (states->begin(), states->end(), cmp);
+
+ return states;
+}
+
+vector<string *> *
+Session::possible_states () const
+{
+ return possible_states(_path);
+}
+
+RouteGroup *
+Session::add_edit_group (string name)
+{
+ RouteGroup* rg = new RouteGroup (*this, name);
+ edit_groups.push_back (rg);
+ edit_group_added (rg); /* EMIT SIGNAL */
+ set_dirty();
+ return rg;
+}
+
+RouteGroup *
+Session::add_mix_group (string name)
+{
+ RouteGroup* rg = new RouteGroup (*this, name, RouteGroup::Relative);
+ mix_groups.push_back (rg);
+ mix_group_added (rg); /* EMIT SIGNAL */
+ set_dirty();
+ return rg;
+}
+
+void
+Session::remove_edit_group (RouteGroup& rg)
+{
+ list<RouteGroup*>::iterator i;
+
+ if ((i = find (edit_groups.begin(), edit_groups.end(), &rg)) != edit_groups.end()) {
+ (*i)->apply (&Route::drop_edit_group, this);
+ edit_groups.erase (i);
+ edit_group_removed (); /* EMIT SIGNAL */
+ }
+
+ delete &rg;
+}
+
+void
+Session::remove_mix_group (RouteGroup& rg)
+{
+ list<RouteGroup*>::iterator i;
+
+ if ((i = find (mix_groups.begin(), mix_groups.end(), &rg)) != mix_groups.end()) {
+ (*i)->apply (&Route::drop_mix_group, this);
+ mix_groups.erase (i);
+ mix_group_removed (); /* EMIT SIGNAL */
+ }
+
+ delete &rg;
+}
+
+RouteGroup *
+Session::mix_group_by_name (string name)
+{
+ list<RouteGroup *>::iterator i;
+
+ for (i = mix_groups.begin(); i != mix_groups.end(); ++i) {
+ if ((*i)->name() == name) {
+ return* i;
+ }
+ }
+ return 0;
+}
+
+RouteGroup *
+Session::edit_group_by_name (string name)
+{
+ list<RouteGroup *>::iterator i;
+
+ for (i = edit_groups.begin(); i != edit_groups.end(); ++i) {
+ if ((*i)->name() == name) {
+ return* i;
+ }
+ }
+ return 0;
+}
+
+void
+Session::begin_reversible_command (const string& name)
+{
+ current_trans = new UndoTransaction;
+ current_trans->set_name (name);
+}
+
+void
+Session::commit_reversible_command (Command *cmd)
+{
+ struct timeval now;
+
+ if (cmd) {
+ current_trans->add_command (cmd);
+ }
+
+ if (current_trans->empty()) {
+ return;
+ }
+
+ gettimeofday (&now, 0);
+ current_trans->set_timestamp (now);
+
+ _history.add (current_trans);
+}
+
+Session::GlobalRouteBooleanState
+Session::get_global_route_boolean (bool (Route::*method)(void) const)
+{
+ GlobalRouteBooleanState s;
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (!(*i)->is_hidden()) {
+ RouteBooleanState v;
+
+ v.first =* i;
+ Route* r = (*i).get();
+ v.second = (r->*method)();
+
+ s.push_back (v);
+ }
+ }
+
+ return s;
+}
+
+Session::GlobalRouteMeterState
+Session::get_global_route_metering ()
+{
+ GlobalRouteMeterState s;
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (!(*i)->is_hidden()) {
+ RouteMeterState v;
+
+ v.first =* i;
+ v.second = (*i)->meter_point();
+
+ s.push_back (v);
+ }
+ }
+
+ return s;
+}
+
+void
+Session::set_global_route_metering (GlobalRouteMeterState s, void* arg)
+{
+ for (GlobalRouteMeterState::iterator i = s.begin(); i != s.end(); ++i) {
+
+ boost::shared_ptr<Route> r = (i->first.lock());
+
+ if (r) {
+ r->set_meter_point (i->second, arg);
+ }
+ }
+}
+
+void
+Session::set_global_route_boolean (GlobalRouteBooleanState s, void (Route::*method)(bool, void*), void* arg)
+{
+ for (GlobalRouteBooleanState::iterator i = s.begin(); i != s.end(); ++i) {
+
+ boost::shared_ptr<Route> r = (i->first.lock());
+
+ if (r) {
+ Route* rp = r.get();
+ (rp->*method) (i->second, arg);
+ }
+ }
+}
+
+void
+Session::set_global_mute (GlobalRouteBooleanState s, void* src)
+{
+ set_global_route_boolean (s, &Route::set_mute, src);
+}
+
+void
+Session::set_global_solo (GlobalRouteBooleanState s, void* src)
+{
+ set_global_route_boolean (s, &Route::set_solo, src);
+}
+
+void
+Session::set_global_record_enable (GlobalRouteBooleanState s, void* src)
+{
+ set_global_route_boolean (s, &Route::set_record_enable, src);
+}
+
+#if 0
+UndoAction
+Session::global_mute_memento (void* src)
+{
+ return sigc::bind (mem_fun (*this, &Session::set_global_mute), get_global_route_boolean (&Route::muted), src);
+}
+
+UndoAction
+Session::global_metering_memento (void* src)
+{
+ return sigc::bind (mem_fun (*this, &Session::set_global_route_metering), get_global_route_metering (), src);
+}
+
+UndoAction
+Session::global_solo_memento (void* src)
+{
+ return sigc::bind (mem_fun (*this, &Session::set_global_solo), get_global_route_boolean (&Route::soloed), src);
+}
+
+UndoAction
+Session::global_record_enable_memento (void* src)
+{
+ return sigc::bind (mem_fun (*this, &Session::set_global_record_enable), get_global_route_boolean (&Route::record_enabled), src);
+}
+#endif
+
+static bool
+accept_all_non_peak_files (const string& path, void *arg)
+{
+ return (path.length() > 5 && path.find (peakfile_suffix) != (path.length() - 5));
+}
+
+static bool
+accept_all_state_files (const string& path, void *arg)
+{
+ return (path.length() > 7 && path.find (".ardour") == (path.length() - 7));
+}
+
+int
+Session::find_all_sources (string path, set<string>& result)
+{
+ XMLTree tree;
+ XMLNode* node;
+
+ if (!tree.read (path)) {
+ return -1;
+ }
+
+ if ((node = find_named_node (*tree.root(), "Sources")) == 0) {
+ return -2;
+ }
+
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+
+ nlist = node->children();
+
+ set_dirty();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ XMLProperty* prop;
+
+ if ((prop = (*niter)->property (X_("name"))) == 0) {
+ continue;
+ }
+
+ if (prop->value()[0] == '/') {
+ /* external file, ignore */
+ continue;
+ }
+
+ sys::path source_path = _session_dir->sound_path ();
+
+ source_path /= prop->value ();
+
+ result.insert (source_path.to_string ());
+ }
+
+ return 0;
+}
+
+int
+Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_this_snapshot)
+{
+ PathScanner scanner;
+ vector<string*>* state_files;
+ string ripped;
+ string this_snapshot_path;
+
+ result.clear ();
+
+ ripped = _path;
+
+ if (ripped[ripped.length()-1] == '/') {
+ ripped = ripped.substr (0, ripped.length() - 1);
+ }
+
+ state_files = scanner (ripped, accept_all_state_files, (void *) 0, false, true);
+
+ if (state_files == 0) {
+ /* impossible! */
+ return 0;
+ }
+
+ this_snapshot_path = _path;
+ this_snapshot_path += _current_snapshot_name;
+ this_snapshot_path += statefile_suffix;
+
+ for (vector<string*>::iterator i = state_files->begin(); i != state_files->end(); ++i) {
+
+ if (exclude_this_snapshot && **i == this_snapshot_path) {
+ continue;
+ }
+
+ if (find_all_sources (**i, result) < 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+struct RegionCounter {
+ typedef std::map<PBD::ID,boost::shared_ptr<AudioSource> > AudioSourceList;
+ AudioSourceList::iterator iter;
+ boost::shared_ptr<Region> region;
+ uint32_t count;
+
+ RegionCounter() : count (0) {}
+};
+
+int
+Session::cleanup_sources (Session::cleanup_report& rep)
+{
+ // FIXME: needs adaptation to midi
+
+ vector<boost::shared_ptr<Source> > dead_sources;
+ vector<boost::shared_ptr<Playlist> > playlists_tbd;
+ PathScanner scanner;
+ string sound_path;
+ vector<space_and_path>::iterator i;
+ vector<space_and_path>::iterator nexti;
+ vector<string*>* soundfiles;
+ vector<string> unused;
+ set<string> all_sources;
+ bool used;
+ string spath;
+ int ret = -1;
+
+ _state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
+
+
+ /* step 1: consider deleting all unused playlists */
+
+ for (PlaylistList::iterator x = unused_playlists.begin(); x != unused_playlists.end(); ++x) {
+ int status;
+
+ status = AskAboutPlaylistDeletion (*x);
+
+ switch (status) {
+ case -1:
+ ret = 0;
+ goto out;
+ break;
+
+ case 0:
+ playlists_tbd.push_back (*x);
+ break;
+
+ default:
+ /* leave it alone */
+ break;
+ }
+ }
+
+ /* now delete any that were marked for deletion */
+
+ for (vector<boost::shared_ptr<Playlist> >::iterator x = playlists_tbd.begin(); x != playlists_tbd.end(); ++x) {
+ (*x)->drop_references ();
+ }
+
+ playlists_tbd.clear ();
+
+ /* step 2: find all un-used sources */
+
+ rep.paths.clear ();
+ rep.space = 0;
+
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ) {
+
+ SourceMap::iterator tmp;
+
+ tmp = i;
+ ++tmp;
+
+ /* do not bother with files that are zero size, otherwise we remove the current "nascent"
+ capture files.
+ */
+
+ if (!i->second->used() && i->second->length() > 0) {
+ dead_sources.push_back (i->second);
+ i->second->GoingAway();
+ }
+
+ i = tmp;
+ }
+
+ /* build a list of all the possible sound directories for the session */
+
+ for (i = session_dirs.begin(); i != session_dirs.end(); ) {
+
+ nexti = i;
+ ++nexti;
+
+ SessionDirectory sdir ((*i).path);
+ sound_path += sdir.sound_path().to_string();
+
+ if (nexti != session_dirs.end()) {
+ sound_path += ':';
+ }
+
+ i = nexti;
+ }
+
+ /* now do the same thing for the files that ended up in the sounds dir(s)
+ but are not referenced as sources in any snapshot.
+ */
+
+ soundfiles = scanner (sound_path, accept_all_non_peak_files, (void *) 0, false, true);
+
+ if (soundfiles == 0) {
+ return 0;
+ }
+
+ /* find all sources, but don't use this snapshot because the
+ state file on disk still references sources we may have already
+ dropped.
+ */
+
+ find_all_sources_across_snapshots (all_sources, true);
+
+ /* add our current source list
+ */
+
+ for (SourceMap::iterator i = sources.begin(); i != sources.end(); ++i) {
+ boost::shared_ptr<AudioFileSource> fs;
+
+ if ((fs = boost::dynamic_pointer_cast<AudioFileSource> (i->second)) != 0) {
+ all_sources.insert (fs->path());
+ }
+ }
+
+ char tmppath1[PATH_MAX+1];
+ char tmppath2[PATH_MAX+1];
+
+ for (vector<string*>::iterator x = soundfiles->begin(); x != soundfiles->end(); ++x) {
+
+ used = false;
+ spath = **x;
+
+ for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
+
+ realpath(spath.c_str(), tmppath1);
+ realpath((*i).c_str(), tmppath2);
+
+ if (strcmp(tmppath1, tmppath2) == 0) {
+ used = true;
+ break;
+ }
+ }
+
+ if (!used) {
+ unused.push_back (spath);
+ }
+ }
+
+ /* now try to move all unused files into the "dead_sounds" directory(ies) */
+
+ for (vector<string>::iterator x = unused.begin(); x != unused.end(); ++x) {
+ struct stat statbuf;
+
+ rep.paths.push_back (*x);
+ if (stat ((*x).c_str(), &statbuf) == 0) {
+ rep.space += statbuf.st_size;
+ }
+
+ string newpath;
+
+ /* don't move the file across filesystems, just
+ stick it in the `dead_sound_dir_name' directory
+ on whichever filesystem it was already on.
+ */
+
+ if ((*x).find ("/sounds/") != string::npos) {
+
+ /* old school, go up 1 level */
+
+ newpath = Glib::path_get_dirname (*x); // "sounds"
+ newpath = Glib::path_get_dirname (newpath); // "session-name"
+
+ } else {
+
+ /* new school, go up 4 levels */
+
+ newpath = Glib::path_get_dirname (*x); // "audiofiles"
+ newpath = Glib::path_get_dirname (newpath); // "session-name"
+ newpath = Glib::path_get_dirname (newpath); // "interchange"
+ newpath = Glib::path_get_dirname (newpath); // "session-dir"
+ }
+
+ newpath += '/';
+ newpath += dead_sound_dir_name;
+
+ if (g_mkdir_with_parents (newpath.c_str(), 0755) < 0) {
+ error << string_compose(_("Session: cannot create session peakfile folder \"%1\" (%2)"), newpath, strerror (errno)) << endmsg;
+ return -1;
+ }
+
+ newpath += '/';
+ newpath += Glib::path_get_basename ((*x));
+
+ if (access (newpath.c_str(), F_OK) == 0) {
+
+ /* the new path already exists, try versioning */
+
+ char buf[PATH_MAX+1];
+ int version = 1;
+ string newpath_v;
+
+ snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
+ newpath_v = buf;
+
+ while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
+ snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
+ newpath_v = buf;
+ }
+
+ if (version == 999) {
+ error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
+ newpath)
+ << endmsg;
+ } else {
+ newpath = newpath_v;
+ }
+
+ } else {
+
+ /* it doesn't exist, or we can't read it or something */
+
+ }
+
+ if (::rename ((*x).c_str(), newpath.c_str()) != 0) {
+ error << string_compose (_("cannot rename audio file source from %1 to %2 (%3)"),
+ (*x), newpath, strerror (errno))
+ << endmsg;
+ goto out;
+ }
+
+ /* see if there an easy to find peakfile for this file, and remove it.
+ */
+
+ string peakpath = (*x).substr (0, (*x).find_last_of ('.'));
+ peakpath += peakfile_suffix;
+
+ if (access (peakpath.c_str(), W_OK) == 0) {
+ if (::unlink (peakpath.c_str()) != 0) {
+ error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
+ peakpath, _path, strerror (errno))
+ << endmsg;
+ /* try to back out */
+ rename (newpath.c_str(), _path.c_str());
+ goto out;
+ }
+ }
+ }
+
+ ret = 0;
+
+ /* dump the history list */
+
+ _history.clear ();
+
+ /* save state so we don't end up a session file
+ referring to non-existent sources.
+ */
+
+ save_state ("");
+
+ out:
+ _state_of_the_state = (StateOfTheState) (_state_of_the_state & ~InCleanup);
+
+ return ret;
+}
+
+int
+Session::cleanup_trash_sources (Session::cleanup_report& rep)
+{
+ // FIXME: needs adaptation for MIDI
+
+ vector<space_and_path>::iterator i;
+ string dead_sound_dir;
+ struct dirent* dentry;
+ struct stat statbuf;
+ DIR* dead;
+
+ rep.paths.clear ();
+ rep.space = 0;
+
+ for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
+
+ dead_sound_dir = (*i).path;
+ dead_sound_dir += dead_sound_dir_name;
+
+ if ((dead = opendir (dead_sound_dir.c_str())) == 0) {
+ continue;
+ }
+
+ while ((dentry = readdir (dead)) != 0) {
+
+ /* avoid '.' and '..' */
+
+ if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') ||
+ (dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) {
+ continue;
+ }
+
+ string fullpath;
+
+ fullpath = dead_sound_dir;
+ fullpath += '/';
+ fullpath += dentry->d_name;
+
+ if (stat (fullpath.c_str(), &statbuf)) {
+ continue;
+ }
+
+ if (!S_ISREG (statbuf.st_mode)) {
+ continue;
+ }
+
+ if (unlink (fullpath.c_str())) {
+ error << string_compose (_("cannot remove dead sound file %1 (%2)"),
+ fullpath, strerror (errno))
+ << endmsg;
+ }
+
+ rep.paths.push_back (dentry->d_name);
+ rep.space += statbuf.st_size;
+ }
+
+ closedir (dead);
+
+ }
+
+ return 0;
+}
+
+void
+Session::set_dirty ()
+{
+ bool was_dirty = dirty();
+
+ _state_of_the_state = StateOfTheState (_state_of_the_state | Dirty);
+
+
+ if (!was_dirty) {
+ DirtyChanged(); /* EMIT SIGNAL */
+ }
+}
+
+
+void
+Session::set_clean ()
+{
+ bool was_dirty = dirty();
+
+ _state_of_the_state = Clean;
+
+
+ if (was_dirty) {
+ DirtyChanged(); /* EMIT SIGNAL */
+ }
+}
+
+void
+Session::set_deletion_in_progress ()
+{
+ _state_of_the_state = StateOfTheState (_state_of_the_state | Deletion);
+
+}
+
+void
+Session::add_controllable (boost::shared_ptr<Controllable> c)
+{
+ /* this adds a controllable to the list managed by the Session.
+ this is a subset of those managed by the Controllable class
+ itself, and represents the only ones whose state will be saved
+ as part of the session.
+ */
+
+ Glib::Mutex::Lock lm (controllables_lock);
+ controllables.insert (c);
+}
+
+struct null_deleter { void operator()(void const *) const {} };
+
+void
+Session::remove_controllable (Controllable* c)
+{
+ if (_state_of_the_state | Deletion) {
+ return;
+ }
+
+ Glib::Mutex::Lock lm (controllables_lock);
+
+ Controllables::iterator x = controllables.find(
+ boost::shared_ptr<Controllable>(c, null_deleter()));
+
+ if (x != controllables.end()) {
+ controllables.erase (x);
+ }
+}
+
+boost::shared_ptr<Controllable>
+Session::controllable_by_id (const PBD::ID& id)
+{
+ Glib::Mutex::Lock lm (controllables_lock);
+
+ for (Controllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
+ if ((*i)->id() == id) {
+ return *i;
+ }
+ }
+
+ return boost::shared_ptr<Controllable>();
+}
+
+void
+Session::add_instant_xml (XMLNode& node)
+{
+ Stateful::add_instant_xml (node, _path);
+ Config->add_instant_xml (node);
+}
+
+XMLNode*
+Session::instant_xml (const string& node_name)
+{
+ return Stateful::instant_xml (node_name, _path);
+}
+
+int
+Session::save_history (string snapshot_name)
+{
+ XMLTree tree;
+
+ if (snapshot_name.empty()) {
+ snapshot_name = _current_snapshot_name;
+ }
+
+ const string history_filename = snapshot_name + history_suffix;
+ const string backup_filename = history_filename + backup_suffix;
+ const sys::path xml_path = _session_dir->root_path() / history_filename;
+ const sys::path backup_path = _session_dir->root_path() / backup_filename;
+
+ if (sys::exists (xml_path)) {
+ try
+ {
+ sys::rename (xml_path, backup_path);
+ }
+ catch (const sys::filesystem_error& err)
+ {
+ error << _("could not backup old history file, current history not saved") << endmsg;
+ return -1;
+ }
+ }
+
+
+ if (!Config->get_save_history() || Config->get_saved_history_depth() < 0) {
+ return 0;
+ }
+
+ tree.set_root (&_history.get_state (Config->get_saved_history_depth()));
+
+ if (!tree.write (xml_path.to_string()))
+ {
+ error << string_compose (_("history could not be saved to %1"), xml_path.to_string()) << endmsg;
+
+ try
+ {
+ sys::remove (xml_path);
+ sys::rename (backup_path, xml_path);
+ }
+ catch (const sys::filesystem_error& err)
+ {
+ error << string_compose (_("could not restore history file from backup %1 (%2)"),
+ backup_path.to_string(), err.what()) << endmsg;
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+Session::restore_history (string snapshot_name)
+{
+ XMLTree tree;
+
+ if (snapshot_name.empty()) {
+ snapshot_name = _current_snapshot_name;
+ }
+
+ const string xml_filename = snapshot_name + history_suffix;
+ const sys::path xml_path = _session_dir->root_path() / xml_filename;
+
+ cerr << "Loading history from " << xml_path.to_string() << endmsg;
+
+ if (!sys::exists (xml_path)) {
+ info << string_compose (_("%1: no history file \"%2\" for this session."),
+ _name, xml_path.to_string()) << endmsg;
+ return 1;
+ }
+
+ if (!tree.read (xml_path.to_string())) {
+ error << string_compose (_("Could not understand session history file \"%1\""),
+ xml_path.to_string()) << endmsg;
+ return -1;
+ }
+
+ // replace history
+ _history.clear();
+
+ for (XMLNodeConstIterator it = tree.root()->children().begin(); it != tree.root()->children().end(); it++) {
+
+ XMLNode *t = *it;
+ UndoTransaction* ut = new UndoTransaction ();
+ struct timeval tv;
+
+ ut->set_name(t->property("name")->value());
+ stringstream ss(t->property("tv_sec")->value());
+ ss >> tv.tv_sec;
+ ss.str(t->property("tv_usec")->value());
+ ss >> tv.tv_usec;
+ ut->set_timestamp(tv);
+
+ for (XMLNodeConstIterator child_it = t->children().begin();
+ child_it != t->children().end();
+ child_it++)
+ {
+ XMLNode *n = *child_it;
+ Command *c;
+
+ if (n->name() == "MementoCommand" ||
+ n->name() == "MementoUndoCommand" ||
+ n->name() == "MementoRedoCommand") {
+
+ if ((c = memento_command_factory(n))) {
+ ut->add_command(c);
+ }
+
+ } else if (n->name() == X_("GlobalRouteStateCommand")) {
+
+ if ((c = global_state_command_factory (*n))) {
+ ut->add_command (c);
+ }
+
+ } else if (n->name() == "DeltaCommand") {
+ PBD::ID id(n->property("midi_source")->value());
+ boost::shared_ptr<MidiSource> midi_source =
+ boost::dynamic_pointer_cast<MidiSource, Source>(source_by_id(id));
+ if(midi_source) {
+ ut->add_command(new MidiModel::DeltaCommand(midi_source->model(), *n));
+ } else {
+ error << "FIXME: Failed to downcast MidiSource for DeltaCommand" << endmsg;
+ }
+ } else {
+ error << string_compose(_("Couldn't figure out how to make a Command out of a %1 XMLNode."), n->name()) << endmsg;
+ }
+ }
+
+ _history.add (ut);
+ }
+
+ return 0;
+}
+
+void
+Session::config_changed (const char* parameter_name)
+{
+#define PARAM_IS(x) (!strcmp (parameter_name, (x)))
+
+ if (PARAM_IS ("seamless-loop")) {
+
+ } else if (PARAM_IS ("rf-speed")) {
+
+ } else if (PARAM_IS ("auto-loop")) {
+
+ } else if (PARAM_IS ("auto-input")) {
+
+ if (Config->get_monitoring_model() == HardwareMonitoring && transport_rolling()) {
+ /* auto-input only makes a difference if we're rolling */
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ (*i)->monitor_input (!Config->get_auto_input());
+ }
+ }
+ }
+
+ } else if (PARAM_IS ("punch-in")) {
+
+ Location* location;
+
+ if ((location = _locations.auto_punch_location()) != 0) {
+
+ if (Config->get_punch_in ()) {
+ replace_event (Event::PunchIn, location->start());
+ } else {
+ remove_event (location->start(), Event::PunchIn);
+ }
+ }
+
+ } else if (PARAM_IS ("punch-out")) {
+
+ Location* location;
+
+ if ((location = _locations.auto_punch_location()) != 0) {
+
+ if (Config->get_punch_out()) {
+ replace_event (Event::PunchOut, location->end());
+ } else {
+ clear_events (Event::PunchOut);
+ }
+ }
+
+ } else if (PARAM_IS ("edit-mode")) {
+
+ Glib::Mutex::Lock lm (playlist_lock);
+
+ for (PlaylistList::iterator i = playlists.begin(); i != playlists.end(); ++i) {
+ (*i)->set_edit_mode (Config->get_edit_mode ());
+ }
+
+ } else if (PARAM_IS ("use-video-sync")) {
+
+ waiting_for_sync_offset = Config->get_use_video_sync();
+
+ } else if (PARAM_IS ("mmc-control")) {
+
+ //poke_midi_thread ();
+
+ } else if (PARAM_IS ("mmc-device-id") || PARAM_IS ("mmc-receive-id")) {
+
+ if (mmc) {
+ mmc->set_receive_device_id (Config->get_mmc_receive_device_id());
+ }
+
+ } else if (PARAM_IS ("mmc-send-id")) {
+
+ if (mmc) {
+ mmc->set_send_device_id (Config->get_mmc_send_device_id());
+ }
+
+ } else if (PARAM_IS ("midi-control")) {
+
+ //poke_midi_thread ();
+
+ } else if (PARAM_IS ("raid-path")) {
+
+ setup_raid_path (Config->get_raid_path());
+
+ } else if (PARAM_IS ("smpte-format")) {
+
+ sync_time_vars ();
+
+ } else if (PARAM_IS ("video-pullup")) {
+
+ sync_time_vars ();
+
+ } else if (PARAM_IS ("seamless-loop")) {
+
+ if (play_loop && transport_rolling()) {
+ // to reset diskstreams etc
+ request_play_loop (true);
+ }
+
+ } else if (PARAM_IS ("rf-speed")) {
+
+ cumulative_rf_motion = 0;
+ reset_rf_scale (0);
+
+ } else if (PARAM_IS ("click-sound")) {
+
+ setup_click_sounds (1);
+
+ } else if (PARAM_IS ("click-emphasis-sound")) {
+
+ setup_click_sounds (-1);
+
+ } else if (PARAM_IS ("clicking")) {
+
+ if (Config->get_clicking()) {
+ if (_click_io && click_data) { // don't require emphasis data
+ _clicking = true;
+ }
+ } else {
+ _clicking = false;
+ }
+
+ } else if (PARAM_IS ("send-mtc")) {
+
+ /* only set the internal flag if we have
+ a port.
+ */
+
+ if (_mtc_port != 0) {
+ session_send_mtc = Config->get_send_mtc();
+ if (session_send_mtc) {
+ /* mark us ready to send */
+ next_quarter_frame_to_send = 0;
+ }
+ } else {
+ session_send_mtc = false;
+ }
+
+ } else if (PARAM_IS ("send-mmc")) {
+
+ /* only set the internal flag if we have
+ a port.
+ */
+
+ if (_mmc_port != 0) {
+ session_send_mmc = Config->get_send_mmc();
+ } else {
+ mmc = 0;
+ session_send_mmc = false;
+ }
+
+ } else if (PARAM_IS ("midi-feedback")) {
+
+ /* only set the internal flag if we have
+ a port.
+ */
+
+ if (_mtc_port != 0) {
+ session_midi_feedback = Config->get_midi_feedback();
+ }
+
+ } else if (PARAM_IS ("jack-time-master")) {
+
+ engine().reset_timebase ();
+
+ } else if (PARAM_IS ("native-file-header-format")) {
+
+ if (!first_file_header_format_reset) {
+ reset_native_file_format ();
+ }
+
+ first_file_header_format_reset = false;
+
+ } else if (PARAM_IS ("native-file-data-format")) {
+
+ if (!first_file_data_format_reset) {
+ reset_native_file_format ();
+ }
+
+ first_file_data_format_reset = false;
+
+ } else if (PARAM_IS ("slave-source")) {
+ set_slave_source (Config->get_slave_source());
+ } else if (PARAM_IS ("remote-model")) {
+ set_remote_control_ids ();
+ } else if (PARAM_IS ("denormal-model")) {
+ setup_fpu ();
+ } else if (PARAM_IS ("history-depth")) {
+ set_history_depth (Config->get_history_depth());
+ } else if (PARAM_IS ("sync-all-route-ordering")) {
+ sync_order_keys ();
+ }
+
+ set_dirty ();
+
+#undef PARAM_IS
+
+}
+
+void
+Session::set_history_depth (uint32_t d)
+{
+ _history.set_depth (d);
+}
diff --git a/libs/ardour/session_state_utils.cc b/libs/ardour/session_state_utils.cc
new file mode 100644
index 0000000000..e57dce039e
--- /dev/null
+++ b/libs/ardour/session_state_utils.cc
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <algorithm>
+
+#include <pbd/compose.h>
+#include <pbd/error.h>
+#include <pbd/file_utils.h>
+
+#include <ardour/session_state_utils.h>
+#include <ardour/filename_extensions.h>
+
+#include "i18n.h"
+
+namespace ARDOUR {
+
+bool
+create_backup_file (const sys::path & file_path)
+{
+ if (!sys::exists (file_path)) return false;
+
+ sys::path backup_path(file_path.to_string() + backup_suffix);
+
+ try
+ {
+ sys::copy_file (file_path, backup_path);
+ }
+ catch(sys::filesystem_error& ex)
+ {
+ error << string_compose (_("Unable to create a backup copy of file %1 (%2)"),
+ file_path.to_string(), ex.what())
+ << endmsg;
+ return false;
+ }
+ return true;
+}
+
+void
+get_state_files_in_directory (const sys::path & directory_path,
+ vector<sys::path> & result)
+{
+ Glib::PatternSpec state_file_pattern('*' + string(statefile_suffix));
+
+ find_matching_files_in_directory (directory_path, state_file_pattern,
+ result);
+}
+
+vector<string>
+get_file_names_no_extension (const vector<sys::path> & file_paths)
+{
+ vector<string> result;
+
+ std::transform (file_paths.begin(), file_paths.end(),
+ std::back_inserter(result), sys::basename);
+
+ sort (result.begin(), result.end(), std::less<string>());
+
+ return result;
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/session_time.cc b/libs/ardour/session_time.cc
new file mode 100644
index 0000000000..a388e31d80
--- /dev/null
+++ b/libs/ardour/session_time.cc
@@ -0,0 +1,606 @@
+
+/*
+ Copyright (C) 1999-2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <iostream>
+#include <cmath>
+#include <unistd.h>
+
+#include <ardour/timestamps.h>
+
+#include <pbd/error.h>
+#include <pbd/enumwriter.h>
+#include <pbd/stacktrace.h>
+
+#include <ardour/ardour.h>
+#include <ardour/configuration.h>
+#include <ardour/audioengine.h>
+#include <ardour/session.h>
+#include <ardour/tempo.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace PBD;
+
+/* BBT TIME*/
+
+void
+Session::bbt_time (nframes_t when, BBT_Time& bbt)
+{
+ _tempo_map->bbt_time (when, bbt);
+}
+
+/* SMPTE TIME */
+float
+Session::smpte_frames_per_second() const
+{
+ switch (Config->get_smpte_format()) {
+ case smpte_23976:
+ return 23.976;
+
+ break;
+ case smpte_24:
+ return 24;
+
+ break;
+ case smpte_24976:
+ return 24.976;
+
+ break;
+ case smpte_25:
+ return 25;
+
+ break;
+ case smpte_2997:
+ return 29.97;
+
+ break;
+ case smpte_2997drop:
+ return 29.97;
+
+ break;
+ case smpte_30:
+ return 30;
+
+ break;
+ case smpte_30drop:
+ return 30;
+
+ break;
+ case smpte_5994:
+ return 59.94;
+
+ break;
+ case smpte_60:
+ return 60;
+
+ break;
+ default:
+ cerr << "Editor received unexpected smpte type" << endl;
+ }
+ return 30.0;
+}
+bool
+Session::smpte_drop_frames() const
+{
+ switch (Config->get_smpte_format()) {
+ case smpte_23976:
+ return false;
+
+ break;
+ case smpte_24:
+ return false;
+
+ break;
+ case smpte_24976:
+ return false;
+
+ break;
+ case smpte_25:
+ return false;
+
+ break;
+ case smpte_2997:
+ return false;
+
+ break;
+ case smpte_2997drop:
+ return true;
+
+ break;
+ case smpte_30:
+ return false;
+
+ break;
+ case smpte_30drop:
+ return true;
+
+ break;
+ case smpte_5994:
+ return false;
+
+ break;
+ case smpte_60:
+ return false;
+
+ break;
+ default:
+ cerr << "Editor received unexpected smpte type" << endl;
+ }
+ return false;
+}
+void
+Session::sync_time_vars ()
+{
+ _current_frame_rate = (nframes_t) round (_base_frame_rate * (1.0 + (Config->get_video_pullup()/100.0)));
+ _frames_per_smpte_frame = (double) _current_frame_rate / (double) smpte_frames_per_second();
+ if (smpte_drop_frames()) {
+ _frames_per_hour = (long)(107892 * _frames_per_smpte_frame);
+ } else {
+ _frames_per_hour = (long)(3600 * rint(smpte_frames_per_second()) * _frames_per_smpte_frame);
+ }
+ _smpte_frames_per_hour = (nframes_t)rint(smpte_frames_per_second() * 3600.0);
+
+ last_smpte_valid = false;
+ // smpte type bits are the middle two in the upper nibble
+ switch ((int) ceil (smpte_frames_per_second())) {
+ case 24:
+ mtc_smpte_bits = 0;
+ break;
+
+ case 25:
+ mtc_smpte_bits = 0x20;
+ break;
+
+ case 30:
+ default:
+ if (smpte_drop_frames()) {
+ mtc_smpte_bits = 0x40;
+ } else {
+ mtc_smpte_bits = 0x60;
+ }
+ break;
+ };
+}
+
+int
+Session::set_smpte_format (SmpteFormat format)
+{
+ /* this will trigger any other changes needed */
+ Config->set_smpte_format (format);
+ return 0;
+}
+
+void
+Session::set_smpte_offset (nframes_t off)
+{
+ _smpte_offset = off;
+ last_smpte_valid = false;
+
+ SMPTEOffsetChanged (); /* EMIT SIGNAL */
+}
+
+void
+Session::set_smpte_offset_negative (bool neg)
+{
+ _smpte_offset_negative = neg;
+ last_smpte_valid = false;
+
+ SMPTEOffsetChanged (); /* EMIT SIGNAL */
+}
+
+void
+Session::smpte_to_sample( SMPTE::Time& smpte, nframes_t& sample, bool use_offset, bool use_subframes ) const
+{
+
+ if (smpte.drop) {
+ // The drop frame format was created to better approximate the 30000/1001 = 29.97002997002997....
+ // framerate of NTSC color TV. The used frame rate of drop frame is 29.97, which drifts by about
+ // 0.108 frame per hour, or about 1.3 frames per 12 hours. This is not perfect, but a lot better
+ // than using 30 non drop, which will drift with about 1.8 frame per minute.
+ // Using 29.97, drop frame real time can be accurate only every 10th minute (10 minutes of 29.97 fps
+ // is exactly 17982 frames). One minute is 1798.2 frames, but we count 30 frames per second
+ // (30 * 60 = 1800). This means that at the first minute boundary (at the end of 0:0:59:29) we
+ // are 1.8 frames too late relative to real time. By dropping 2 frames (jumping to 0:1:0:2) we are
+ // approx. 0.2 frames too early. This adds up with 0.2 too early for each minute until we are 1.8
+ // frames too early at 0:9:0:2 (9 * 0.2 = 1.8). The 10th minute brings us 1.8 frames later again
+ // (at end of 0:9:59:29), which sums up to 0 (we are back to zero at 0:10:0:0 :-).
+ //
+ // In table form:
+ //
+ // SMPTE value frames offset subframes offset seconds (rounded) 44100 sample (rounded)
+ // 0:00:00:00 0.0 0 0.000 0 (accurate)
+ // 0:00:59:29 1.8 144 60.027 2647177
+ // 0:01:00:02 -0.2 -16 60.060 2648648
+ // 0:01:59:29 1.6 128 120.020 5292883
+ // 0:02:00:02 -0.4 -32 120.053 5294354
+ // 0:02:59:29 1.4 112 180.013 7938588
+ // 0:03:00:02 -0.6 -48 180.047 7940060
+ // 0:03:59:29 1.2 96 240.007 10584294
+ // 0:04:00:02 -0.8 -64 240.040 10585766
+ // 0:04:59:29 1.0 80 300.000 13230000
+ // 0:05:00:02 -1.0 -80 300.033 13231471
+ // 0:05:59:29 0.8 64 359.993 15875706
+ // 0:06:00:02 -1.2 -96 360.027 15877177
+ // 0:06:59:29 0.6 48 419.987 18521411
+ // 0:07:00:02 -1.4 -112 420.020 18522883
+ // 0:07:59:29 0.4 32 478.980 21167117
+ // 0:08:00:02 -1.6 -128 480.013 21168589
+ // 0:08:59:29 0.2 16 539.973 23812823
+ // 0:09:00:02 -1.8 -144 540.007 23814294
+ // 0:09:59:29 0.0+ 0+ 599.967 26458529
+ // 0:10:00:00 0.0 0 600.000 26460000 (accurate)
+ //
+ // Per Sigmond <per@sigmond.no>
+
+ // Samples inside time dividable by 10 minutes (real time accurate)
+ nframes_t base_samples = (nframes_t) (((smpte.hours * 107892) + ((smpte.minutes / 10) * 17982)) * _frames_per_smpte_frame);
+
+ // Samples inside time exceeding the nearest 10 minutes (always offset, see above)
+ long exceeding_df_minutes = smpte.minutes % 10;
+ long exceeding_df_seconds = (exceeding_df_minutes * 60) + smpte.seconds;
+ long exceeding_df_frames = (30 * exceeding_df_seconds) + smpte.frames - (2 * exceeding_df_minutes);
+ nframes_t exceeding_samples = (nframes_t) rint(exceeding_df_frames * _frames_per_smpte_frame);
+ sample = base_samples + exceeding_samples;
+ } else {
+ /*
+ Non drop is easy.. just note the use of
+ rint(smpte.rate) * _frames_per_smpte_frame
+ (frames per SMPTE second), which is larger than
+ frame_rate() in the non-integer SMPTE rate case.
+ */
+
+ sample = (nframes_t)rint((((smpte.hours * 60 * 60) + (smpte.minutes * 60) + smpte.seconds) * (rint(smpte.rate) * _frames_per_smpte_frame)) + (smpte.frames * _frames_per_smpte_frame));
+ }
+
+ if (use_subframes) {
+ sample += (long) (((double)smpte.subframes * _frames_per_smpte_frame) / Config->get_subframes_per_frame());
+ }
+
+ if (use_offset) {
+ if (smpte_offset_negative()) {
+ if (sample >= smpte_offset()) {
+ sample -= smpte_offset();
+ } else {
+ /* Prevent song-time from becoming negative */
+ sample = 0;
+ }
+ } else {
+ if (smpte.negative) {
+ if (sample <= smpte_offset()) {
+ sample = smpte_offset() - sample;
+ } else {
+ sample = 0;
+ }
+ } else {
+ sample += smpte_offset();
+ }
+ }
+ }
+
+}
+
+
+void
+Session::sample_to_smpte( nframes_t sample, SMPTE::Time& smpte, bool use_offset, bool use_subframes ) const
+{
+ nframes_t offset_sample;
+
+ if (!use_offset) {
+ offset_sample = sample;
+ smpte.negative = false;
+ } else {
+ if (_smpte_offset_negative) {
+ offset_sample = sample + _smpte_offset;
+ smpte.negative = false;
+ } else {
+ if (sample < _smpte_offset) {
+ offset_sample = (_smpte_offset - sample);
+ smpte.negative = true;
+ } else {
+ offset_sample = sample - _smpte_offset;
+ smpte.negative = false;
+ }
+ }
+ }
+
+ double smpte_frames_left_exact;
+ double smpte_frames_fraction;
+ unsigned long smpte_frames_left;
+
+ // Extract whole hours. Do this to prevent rounding errors with
+ // high sample numbers in the calculations that follow.
+ smpte.hours = offset_sample / _frames_per_hour;
+ offset_sample = offset_sample % _frames_per_hour;
+
+ // Calculate exact number of (exceeding) smpte frames and fractional frames
+ smpte_frames_left_exact = (double) offset_sample / _frames_per_smpte_frame;
+ smpte_frames_fraction = smpte_frames_left_exact - floor( smpte_frames_left_exact );
+ smpte.subframes = (long) rint(smpte_frames_fraction * Config->get_subframes_per_frame());
+
+ // XXX Not sure if this is necessary anymore...
+ if (smpte.subframes == Config->get_subframes_per_frame()) {
+ // This can happen with 24 fps (and 29.97 fps ?)
+ smpte_frames_left_exact = ceil( smpte_frames_left_exact );
+ smpte.subframes = 0;
+ }
+
+ // Extract hour-exceeding frames for minute, second and frame calculations
+ smpte_frames_left = ((long) floor( smpte_frames_left_exact ));
+
+ if (smpte_drop_frames()) {
+ // See long explanation in smpte_to_sample()...
+
+ // Number of 10 minute chunks
+ smpte.minutes = (smpte_frames_left / 17982) * 10; // exactly 17982 frames in 10 minutes
+ // frames exceeding the nearest 10 minute barrier
+ long exceeding_df_frames = smpte_frames_left % 17982;
+
+ // Find minutes exceeding the nearest 10 minute barrier
+ if (exceeding_df_frames >= 1800) { // nothing to do if we are inside the first minute (0-1799)
+ exceeding_df_frames -= 1800; // take away first minute (different number of frames than the others)
+ long extra_minutes_minus_1 = exceeding_df_frames / 1798; // how many minutes after the first one
+ exceeding_df_frames -= extra_minutes_minus_1 * 1798; // take away the (extra) minutes just found
+ smpte.minutes += extra_minutes_minus_1 + 1; // update with exceeding minutes
+ }
+
+ // Adjust frame numbering for dropped frames (frame 0 and 1 skipped at start of every minute except every 10th)
+ if (smpte.minutes % 10) {
+ // Every minute except every 10th
+ if (exceeding_df_frames < 28) {
+ // First second, frames 0 and 1 are skipped
+ smpte.seconds = 0;
+ smpte.frames = exceeding_df_frames + 2;
+ } else {
+ // All other seconds, all 30 frames are counted
+ exceeding_df_frames -= 28;
+ smpte.seconds = (exceeding_df_frames / 30) + 1;
+ smpte.frames = exceeding_df_frames % 30;
+ }
+ } else {
+ // Every 10th minute, all 30 frames counted in all seconds
+ smpte.seconds = exceeding_df_frames / 30;
+ smpte.frames = exceeding_df_frames % 30;
+ }
+ } else {
+ // Non drop is easy
+ smpte.minutes = smpte_frames_left / ((long) rint (smpte_frames_per_second ()) * 60);
+ smpte_frames_left = smpte_frames_left % ((long) rint (smpte_frames_per_second ()) * 60);
+ smpte.seconds = smpte_frames_left / (long) rint(smpte_frames_per_second ());
+ smpte.frames = smpte_frames_left % (long) rint(smpte_frames_per_second ());
+ }
+
+ if (!use_subframes) {
+ smpte.subframes = 0;
+ }
+ /* set frame rate and drop frame */
+ smpte.rate = smpte_frames_per_second ();
+ smpte.drop = smpte_drop_frames();
+}
+
+void
+Session::smpte_time (nframes_t when, SMPTE::Time& smpte)
+{
+ if (last_smpte_valid && when == last_smpte_when) {
+ smpte = last_smpte;
+ return;
+ }
+
+ sample_to_smpte( when, smpte, true /* use_offset */, false /* use_subframes */ );
+
+ last_smpte_when = when;
+ last_smpte = smpte;
+ last_smpte_valid = true;
+}
+
+void
+Session::smpte_time_subframes (nframes_t when, SMPTE::Time& smpte)
+{
+ if (last_smpte_valid && when == last_smpte_when) {
+ smpte = last_smpte;
+ return;
+ }
+
+ sample_to_smpte( when, smpte, true /* use_offset */, true /* use_subframes */ );
+
+ last_smpte_when = when;
+ last_smpte = smpte;
+ last_smpte_valid = true;
+}
+
+void
+Session::smpte_duration (nframes_t when, SMPTE::Time& smpte) const
+{
+ sample_to_smpte( when, smpte, false /* use_offset */, true /* use_subframes */ );
+}
+
+void
+Session::smpte_duration_string (char* buf, nframes_t when) const
+{
+ SMPTE::Time smpte;
+
+ smpte_duration (when, smpte);
+ snprintf (buf, sizeof (buf), "%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 ":%02" PRIu32, smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
+}
+
+void
+Session::smpte_time (SMPTE::Time &t)
+
+{
+ smpte_time (_transport_frame, t);
+}
+
+int
+Session::jack_sync_callback (jack_transport_state_t state,
+ jack_position_t* pos)
+{
+ bool slave = synced_to_jack();
+
+ switch (state) {
+ case JackTransportStopped:
+ if (slave && _transport_frame != pos->frame && post_transport_work == 0) {
+ request_locate (pos->frame, false);
+ // cerr << "SYNC: stopped, locate to " << pos->frame << " from " << _transport_frame << endl;
+ return false;
+ } else {
+ return true;
+ }
+
+ case JackTransportStarting:
+ // cerr << "SYNC: starting @ " << pos->frame << " a@ " << _transport_frame << " our work = " << post_transport_work << " pos matches ? " << (_transport_frame == pos->frame) << endl;
+ if (slave) {
+ return _transport_frame == pos->frame && post_transport_work == 0;
+ } else {
+ return true;
+ }
+ break;
+
+ case JackTransportRolling:
+ // cerr << "SYNC: rolling slave = " << slave << endl;
+ if (slave) {
+ start_transport ();
+ }
+ break;
+
+ default:
+ error << string_compose (_("Unknown JACK transport state %1 in sync callback"), state)
+ << endmsg;
+ }
+
+ return true;
+}
+
+void
+Session::jack_timebase_callback (jack_transport_state_t state,
+ nframes_t nframes,
+ jack_position_t* pos,
+ int new_position)
+{
+ BBT_Time bbt;
+
+ /* frame info */
+
+ pos->frame = _transport_frame;
+ pos->valid = JackPositionTimecode;
+
+ /* BBT info */
+
+ if (_tempo_map) {
+
+ TempoMap::Metric metric (_tempo_map->metric_at (_transport_frame));
+ _tempo_map->bbt_time_with_metric (_transport_frame, bbt, metric);
+
+ pos->bar = bbt.bars;
+ pos->beat = bbt.beats;
+ pos->tick = bbt.ticks;
+
+ // XXX still need to set bar_start_tick
+
+ pos->beats_per_bar = metric.meter().beats_per_bar();
+ pos->beat_type = metric.meter().note_divisor();
+ pos->ticks_per_beat = Meter::ticks_per_beat;
+ pos->beats_per_minute = metric.tempo().beats_per_minute();
+
+ pos->valid = jack_position_bits_t (pos->valid | JackPositionBBT);
+ }
+
+#ifdef HAVE_JACK_VIDEO_SUPPORT
+ //poke audio video ratio so Ardour can track Video Sync
+ pos->audio_frames_per_video_frame = frame_rate() / smpte_frames_per_second();
+ pos->valid = jack_position_bits_t (pos->valid | JackAudioVideoRatio);
+#endif
+
+#if 0
+ /* SMPTE info */
+
+ t.smpte_offset = _smpte_offset;
+ t.smpte_frame_rate = smpte_frames_per_second();
+
+ if (_transport_speed) {
+
+ if (play_loop) {
+
+ Location* location = _locations.auto_loop_location();
+
+ if (location) {
+
+ t.transport_state = JackTransportLooping;
+ t.loop_start = location->start();
+ t.loop_end = location->end();
+ t.valid = jack_transport_bits_t (t.valid | JackTransportLoop);
+
+ } else {
+
+ t.loop_start = 0;
+ t.loop_end = 0;
+ t.transport_state = JackTransportRolling;
+
+ }
+
+ } else {
+
+ t.loop_start = 0;
+ t.loop_end = 0;
+ t.transport_state = JackTransportRolling;
+
+ }
+
+ }
+
+#endif
+}
+
+ARDOUR::nframes_t
+Session::convert_to_frames_at (nframes_t position, AnyTime& any)
+{
+ double secs;
+
+ switch (any.type) {
+ case AnyTime::BBT:
+ return _tempo_map->frame_time ( any.bbt);
+ break;
+
+ case AnyTime::SMPTE:
+ /* XXX need to handle negative values */
+ secs = any.smpte.hours * 60 * 60;
+ secs += any.smpte.minutes * 60;
+ secs += any.smpte.seconds;
+ secs += any.smpte.frames / smpte_frames_per_second();
+ if (_smpte_offset_negative)
+ {
+ return (nframes_t) floor (secs * frame_rate()) - _smpte_offset;
+ }
+ else
+ {
+ return (nframes_t) floor (secs * frame_rate()) + _smpte_offset;
+ }
+ break;
+
+ case AnyTime::Seconds:
+ return (nframes_t) floor (any.seconds * frame_rate());
+ break;
+
+ case AnyTime::Frames:
+ return any.frames;
+ break;
+ }
+
+ return any.frames;
+}
diff --git a/libs/ardour/session_transport.cc b/libs/ardour/session_transport.cc
new file mode 100644
index 0000000000..c86a868e56
--- /dev/null
+++ b/libs/ardour/session_transport.cc
@@ -0,0 +1,1355 @@
+/*
+ Copyright (C) 1999-2003 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cmath>
+#include <cerrno>
+#include <unistd.h>
+
+#include <sigc++/bind.h>
+#include <sigc++/retype.h>
+
+#include <pbd/undo.h>
+#include <pbd/error.h>
+#include <glibmm/thread.h>
+#include <pbd/pthread_utils.h>
+#include <pbd/memento_command.h>
+#include <pbd/stacktrace.h>
+
+#include <midi++/mmc.h>
+#include <midi++/port.h>
+
+#include <ardour/ardour.h>
+#include <ardour/audioengine.h>
+#include <ardour/session.h>
+#include <ardour/audio_diskstream.h>
+#include <ardour/auditioner.h>
+#include <ardour/slave.h>
+#include <ardour/location.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace sigc;
+using namespace PBD;
+
+void
+Session::request_input_change_handling ()
+{
+ if (!(_state_of_the_state & (InitialConnecting|Deletion))) {
+ Event* ev = new Event (Event::InputConfigurationChange, Event::Add, Event::Immediate, 0, 0.0);
+ queue_event (ev);
+ }
+}
+
+void
+Session::request_slave_source (SlaveSource src)
+{
+ Event* ev = new Event (Event::SetSlaveSource, Event::Add, Event::Immediate, 0, 0.0);
+
+ if (src == JACK) {
+ /* could set_seamless_loop() be disposed of entirely?*/
+ Config->set_seamless_loop (false);
+ } else {
+ Config->set_seamless_loop (true);
+ }
+ ev->slave = src;
+ queue_event (ev);
+}
+
+void
+Session::request_transport_speed (float speed)
+{
+ Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, speed);
+ queue_event (ev);
+}
+
+void
+Session::request_diskstream_speed (Diskstream& ds, float speed)
+{
+ Event* ev = new Event (Event::SetDiskstreamSpeed, Event::Add, Event::Immediate, 0, speed);
+ ev->set_ptr (&ds);
+ queue_event (ev);
+}
+
+void
+Session::request_stop (bool abort)
+{
+ Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, 0.0, abort);
+ queue_event (ev);
+}
+
+void
+Session::request_locate (nframes_t target_frame, bool with_roll)
+{
+ Event *ev = new Event (with_roll ? Event::LocateRoll : Event::Locate, Event::Add, Event::Immediate, target_frame, 0, false);
+ queue_event (ev);
+}
+
+void
+Session::force_locate (nframes_t target_frame, bool with_roll)
+{
+ Event *ev = new Event (with_roll ? Event::LocateRoll : Event::Locate, Event::Add, Event::Immediate, target_frame, 0, true);
+ queue_event (ev);
+}
+
+void
+Session::request_play_loop (bool yn)
+{
+ Event* ev;
+ Location *location = _locations.auto_loop_location();
+
+ if (location == 0 && yn) {
+ error << _("Cannot loop - no loop range defined")
+ << endmsg;
+ return;
+ }
+
+ ev = new Event (Event::SetLoop, Event::Add, Event::Immediate, 0, 0.0, yn);
+ queue_event (ev);
+
+ if (!yn && Config->get_seamless_loop() && transport_rolling()) {
+ // request an immediate locate to refresh the diskstreams
+ // after disabling looping
+ request_locate (_transport_frame-1, false);
+ }
+}
+
+void
+Session::realtime_stop (bool abort)
+{
+ /* assume that when we start, we'll be moving forwards */
+
+ // FIXME: where should this really be? [DR]
+ //send_full_time_code();
+ deliver_mmc (MIDI::MachineControl::cmdStop, _transport_frame);
+ deliver_mmc (MIDI::MachineControl::cmdLocate, _transport_frame);
+
+ if (_transport_speed < 0.0f) {
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportStop | PostTransportReverse);
+ } else {
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportStop);
+ }
+
+ if (actively_recording()) {
+
+ /* move the transport position back to where the
+ request for a stop was noticed. we rolled
+ past that point to pick up delayed input.
+ */
+
+#ifndef LEAVE_TRANSPORT_UNADJUSTED
+ decrement_transport_position (_worst_output_latency);
+#endif
+
+ /* the duration change is not guaranteed to have happened, but is likely */
+
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportDuration);
+ }
+
+ if (abort) {
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportAbort);
+ }
+
+ _clear_event_type (Event::StopOnce);
+ _clear_event_type (Event::RangeStop);
+ _clear_event_type (Event::RangeLocate);
+
+ disable_record (true);
+
+ reset_slave_state ();
+
+ _transport_speed = 0;
+
+ if (Config->get_use_video_sync()) {
+ waiting_for_sync_offset = true;
+ }
+
+ transport_sub_state = ((Config->get_slave_source() == None && Config->get_auto_return()) ? AutoReturning : 0);
+}
+
+void
+Session::butler_transport_work ()
+{
+ restart:
+ bool finished;
+ boost::shared_ptr<RouteList> r = routes.reader ();
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ int on_entry = g_atomic_int_get (&butler_should_do_transport_work);
+ finished = true;
+
+ if (post_transport_work & PostTransportCurveRealloc) {
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->curve_reallocate();
+ }
+ }
+
+ if (post_transport_work & PostTransportInputChange) {
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->non_realtime_input_change ();
+ }
+ }
+
+ if (post_transport_work & PostTransportSpeed) {
+ non_realtime_set_speed ();
+ }
+
+ if (post_transport_work & PostTransportReverse) {
+
+
+ clear_clicks();
+ cumulative_rf_motion = 0;
+ reset_rf_scale (0);
+
+ /* don't seek if locate will take care of that in non_realtime_stop() */
+
+ if (!(post_transport_work & PostTransportLocate)) {
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ if ((*i)->speed() != 1.0f || (*i)->speed() != -1.0f) {
+ (*i)->seek ((nframes_t) (_transport_frame * (double) (*i)->speed()));
+ }
+ else {
+ (*i)->seek (_transport_frame);
+ }
+ }
+ if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) {
+ /* new request, stop seeking, and start again */
+ g_atomic_int_dec_and_test (&butler_should_do_transport_work);
+ goto restart;
+ }
+ }
+ }
+ }
+
+ if (post_transport_work & PostTransportLocate) {
+ non_realtime_locate ();
+ }
+
+ if (post_transport_work & PostTransportStop) {
+ non_realtime_stop (post_transport_work & PostTransportAbort, on_entry, finished);
+ if (!finished) {
+ g_atomic_int_dec_and_test (&butler_should_do_transport_work);
+ goto restart;
+ }
+ }
+
+ if (post_transport_work & PostTransportOverWrite) {
+ non_realtime_overwrite (on_entry, finished);
+ if (!finished) {
+ g_atomic_int_dec_and_test (&butler_should_do_transport_work);
+ goto restart;
+ }
+ }
+
+ if (post_transport_work & PostTransportAudition) {
+ non_realtime_set_audition ();
+ }
+
+ g_atomic_int_dec_and_test (&butler_should_do_transport_work);
+}
+
+void
+Session::non_realtime_set_speed ()
+{
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->non_realtime_set_speed ();
+ }
+}
+
+void
+Session::non_realtime_overwrite (int on_entry, bool& finished)
+{
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->pending_overwrite) {
+ (*i)->overwrite_existing_buffers ();
+ }
+ if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) {
+ finished = false;
+ return;
+ }
+ }
+}
+
+
+void
+Session::non_realtime_locate ()
+{
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->non_realtime_locate (_transport_frame);
+ }
+}
+
+
+void
+Session::non_realtime_stop (bool abort, int on_entry, bool& finished)
+{
+ struct tm* now;
+ time_t xnow;
+ bool did_record;
+ bool saved;
+
+ did_record = false;
+ saved = false;
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->get_captured_frames () != 0) {
+ did_record = true;
+ break;
+ }
+ }
+
+ /* stop and locate are merged here because they share a lot of common stuff */
+
+ time (&xnow);
+ now = localtime (&xnow);
+
+ if (auditioner) {
+ auditioner->cancel_audition ();
+ }
+
+ clear_clicks();
+ cumulative_rf_motion = 0;
+ reset_rf_scale (0);
+
+ if (did_record) {
+ begin_reversible_command ("capture");
+
+ Location* loc = _locations.end_location();
+ bool change_end = false;
+
+ if (_transport_frame < loc->end()) {
+
+ /* stopped recording before current end */
+
+ if (_end_location_is_free) {
+
+ /* first capture for this session, move end back to where we are */
+
+ change_end = true;
+ }
+
+ } else if (_transport_frame > loc->end()) {
+
+ /* stopped recording after the current end, extend it */
+
+ change_end = true;
+ }
+
+ if (change_end) {
+ XMLNode &before = loc->get_state();
+ loc->set_end(_transport_frame);
+ XMLNode &after = loc->get_state();
+ add_command (new MementoCommand<Location>(*loc, &before, &after));
+ }
+
+ _end_location_is_free = false;
+ _have_captured = true;
+ }
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->transport_stopped (*now, xnow, abort);
+ }
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ if (!(*i)->is_hidden()) {
+ (*i)->set_pending_declick (0);
+ }
+ }
+
+ if (did_record) {
+ commit_reversible_command ();
+ }
+
+ if (_engine.running()) {
+ update_latency_compensation (true, abort);
+ }
+
+ if ((Config->get_slave_source() == None && Config->get_auto_return()) ||
+ (post_transport_work & PostTransportLocate) ||
+ (_requested_return_frame >= 0) ||
+ synced_to_jack()) {
+
+ if (pending_locate_flush) {
+ flush_all_inserts ();
+ }
+
+ if (((Config->get_slave_source() == None && Config->get_auto_return()) ||
+ synced_to_jack() ||
+ _requested_return_frame >= 0) &&
+ !(post_transport_work & PostTransportLocate)) {
+
+ bool do_locate = false;
+
+ if (_requested_return_frame >= 0) {
+ _transport_frame = _requested_return_frame;
+ _requested_return_frame = -1;
+ do_locate = true;
+ } else {
+ _transport_frame = last_stop_frame;
+ _requested_return_frame = -1;
+ }
+
+ if (synced_to_jack() && !play_loop) {
+ do_locate = true;
+ }
+
+ if (do_locate) {
+ // cerr << "non-realtimestop: transport locate to " << _transport_frame << endl;
+ _engine.transport_locate (_transport_frame);
+ }
+ }
+
+#ifndef LEAVE_TRANSPORT_UNADJUSTED
+ }
+#endif
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ if ((*i)->speed() != 1.0f || (*i)->speed() != -1.0f) {
+ (*i)->seek ((nframes_t) (_transport_frame * (double) (*i)->speed()));
+ }
+ else {
+ (*i)->seek (_transport_frame);
+ }
+ }
+ if (on_entry != g_atomic_int_get (&butler_should_do_transport_work)) {
+ finished = false;
+ /* we will be back */
+ return;
+ }
+ }
+
+#ifdef LEAVE_TRANSPORT_UNADJUSTED
+ }
+#endif
+
+ if (_requested_return_frame < 0) {
+ last_stop_frame = _transport_frame;
+ } else {
+ last_stop_frame = _requested_return_frame;
+ _requested_return_frame = -1;
+ }
+
+ if (did_record) {
+
+ /* XXX its a little odd that we're doing this here
+ when realtime_stop(), which has already executed,
+ will have done this.
+ JLC - so let's not because it seems unnecessary and breaks loop record
+ */
+#if 0
+ if (!Config->get_latched_record_enable()) {
+ g_atomic_int_set (&_record_status, Disabled);
+ } else {
+ g_atomic_int_set (&_record_status, Enabled);
+ }
+ RecordStateChanged (); /* emit signal */
+#endif
+ }
+
+ if ((post_transport_work & PostTransportLocate) && get_record_enabled()) {
+ /* capture start has been changed, so save pending state */
+ save_state ("", true);
+ saved = true;
+ }
+
+ /* always try to get rid of this */
+
+ remove_pending_capture_state ();
+
+ /* save the current state of things if appropriate */
+
+ if (did_record && !saved) {
+ save_state (_current_snapshot_name);
+ }
+
+ if (post_transport_work & PostTransportDuration) {
+ DurationChanged (); /* EMIT SIGNAL */
+ }
+
+ if (post_transport_work & PostTransportStop) {
+ _play_range = false;
+
+ /* do not turn off autoloop on stop */
+
+ }
+
+ nframes_t tf = _transport_frame;
+
+ PositionChanged (tf); /* EMIT SIGNAL */
+ TransportStateChange (); /* EMIT SIGNAL */
+
+ /* and start it up again if relevant */
+
+ if ((post_transport_work & PostTransportLocate) && Config->get_slave_source() == None && pending_locate_roll) {
+ request_transport_speed (1.0);
+ pending_locate_roll = false;
+ }
+}
+
+void
+Session::check_declick_out ()
+{
+ bool locate_required = transport_sub_state & PendingLocate;
+
+ /* this is called after a process() iteration. if PendingDeclickOut was set,
+ it means that we were waiting to declick the output (which has just been
+ done) before doing something else. this is where we do that "something else".
+
+ note: called from the audio thread.
+ */
+
+ if (transport_sub_state & PendingDeclickOut) {
+
+ if (locate_required) {
+ start_locate (pending_locate_frame, pending_locate_roll, pending_locate_flush);
+ transport_sub_state &= ~(PendingDeclickOut|PendingLocate);
+ } else {
+ stop_transport (pending_abort);
+ transport_sub_state &= ~(PendingDeclickOut|PendingLocate);
+ }
+ }
+}
+
+void
+Session::set_play_loop (bool yn)
+{
+ /* Called from event-handling context */
+
+ if ((actively_recording() && yn) || _locations.auto_loop_location() == 0) {
+ return;
+ }
+
+ set_dirty();
+
+ if (yn && Config->get_seamless_loop() && synced_to_jack()) {
+ warning << _("Seamless looping cannot be supported while Ardour is using JACK transport.\n"
+ "Recommend changing the configured options")
+ << endmsg;
+ return;
+ }
+
+
+ if ((play_loop = yn)) {
+
+ Location *loc;
+
+
+ if ((loc = _locations.auto_loop_location()) != 0) {
+
+ if (Config->get_seamless_loop()) {
+ // set all diskstreams to use internal looping
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ (*i)->set_loop (loc);
+ }
+ }
+ }
+ else {
+ // set all diskstreams to NOT use internal looping
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ (*i)->set_loop (0);
+ }
+ }
+ }
+
+ /* stick in the loop event */
+
+ Event* event = new Event (Event::AutoLoop, Event::Replace, loc->end(), loc->start(), 0.0f);
+ merge_event (event);
+
+ /* locate to start of loop and roll if current pos is outside of the loop range */
+ if (_transport_frame < loc->start() || _transport_frame > loc->end()) {
+ event = new Event (Event::LocateRoll, Event::Add, Event::Immediate, loc->start(), 0, !synced_to_jack());
+ merge_event (event);
+ }
+ else {
+ // locate to current position (+ 1 to force reload)
+ event = new Event (Event::LocateRoll, Event::Add, Event::Immediate, _transport_frame + 1, 0, !synced_to_jack());
+ merge_event (event);
+ }
+ }
+
+
+
+ } else {
+ clear_events (Event::AutoLoop);
+
+ // set all diskstreams to NOT use internal looping
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ (*i)->set_loop (0);
+ }
+ }
+
+ }
+}
+
+void
+Session::flush_all_inserts ()
+{
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->flush_processors ();
+ }
+}
+
+void
+Session::start_locate (nframes_t target_frame, bool with_roll, bool with_flush, bool with_loop)
+{
+ if (synced_to_jack()) {
+
+ float sp;
+ nframes_t pos;
+
+ _slave->speed_and_position (sp, pos);
+
+ if (target_frame != pos) {
+
+ /* tell JACK to change transport position, and we will
+ follow along later in ::follow_slave()
+ */
+
+ _engine.transport_locate (target_frame);
+
+ if (sp != 1.0f && with_roll) {
+ _engine.transport_start ();
+ }
+
+ }
+
+ } else {
+
+ locate (target_frame, with_roll, with_flush, with_loop);
+ }
+}
+
+void
+Session::locate (nframes_t target_frame, bool with_roll, bool with_flush, bool with_loop)
+{
+ if (actively_recording() && !with_loop) {
+ return;
+ }
+
+ if (_transport_frame == target_frame && !loop_changing && !with_loop) {
+ if (with_roll) {
+ set_transport_speed (1.0, false);
+ }
+ loop_changing = false;
+ return;
+ }
+
+ // Update SMPTE time
+ // [DR] FIXME: find out exactly where this should go below
+ _transport_frame = target_frame;
+ smpte_time(_transport_frame, transmitting_smpte_time);
+ outbound_mtc_smpte_frame = _transport_frame;
+ next_quarter_frame_to_send = 0;
+
+ if (_transport_speed && (!with_loop || loop_changing)) {
+ /* schedule a declick. we'll be called again when its done */
+
+ if (!(transport_sub_state & PendingDeclickOut)) {
+ transport_sub_state |= (PendingDeclickOut|PendingLocate);
+ pending_locate_frame = target_frame;
+ pending_locate_roll = with_roll;
+ pending_locate_flush = with_flush;
+ return;
+ }
+ }
+
+ if (transport_rolling() && (!auto_play_legal || !Config->get_auto_play()) && !with_roll && !(synced_to_jack() && play_loop)) {
+ realtime_stop (false);
+ }
+
+ if ( !with_loop || loop_changing) {
+
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportLocate);
+
+ if (with_roll) {
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportRoll);
+ }
+
+ schedule_butler_transport_work ();
+
+ } else {
+
+ /* this is functionally what clear_clicks() does but with a tentative lock */
+
+ Glib::RWLock::WriterLock clickm (click_lock, Glib::TRY_LOCK);
+
+ if (clickm.locked()) {
+
+ for (Clicks::iterator i = clicks.begin(); i != clicks.end(); ++i) {
+ delete *i;
+ }
+
+ clicks.clear ();
+ }
+ }
+
+ if (with_roll) {
+ /* switch from input if we're going to roll */
+ if (Config->get_monitoring_model() == HardwareMonitoring) {
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (!Config->get_auto_input());
+ }
+ }
+ }
+ } else {
+ /* otherwise we're going to stop, so do the opposite */
+ if (Config->get_monitoring_model() == HardwareMonitoring) {
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (true);
+ }
+ }
+ }
+ }
+
+ /* cancel looped playback if transport pos outside of loop range */
+ if (play_loop) {
+ Location* al = _locations.auto_loop_location();
+
+ if (al && (_transport_frame < al->start() || _transport_frame > al->end())) {
+ // cancel looping directly, this is called from event handling context
+ set_play_loop (false);
+ }
+ else if (al && _transport_frame == al->start()) {
+ if (with_loop) {
+ // this is only necessary for seamless looping
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ // tell it we've looped, so it can deal with the record state
+ (*i)->transport_looped(_transport_frame);
+ }
+ }
+ }
+
+ TransportLooped(); // EMIT SIGNAL
+ }
+ }
+
+ loop_changing = false;
+
+ _send_smpte_update = true;
+}
+
+/** Set the transport speed.
+ * @param speed New speed
+ * @param abort
+ */
+void
+Session::set_transport_speed (float speed, bool abort)
+{
+ if (_transport_speed == speed) {
+ return;
+ }
+
+ if (speed > 0) {
+ speed = min (8.0f, speed);
+ } else if (speed < 0) {
+ speed = max (-8.0f, speed);
+ }
+
+ if (transport_rolling() && speed == 0.0) {
+
+ /* we are rolling and we want to stop */
+
+ if (Config->get_monitoring_model() == HardwareMonitoring)
+ {
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->record_enabled ()) {
+ //cerr << "switching to input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (true);
+ }
+ }
+ }
+
+ if (synced_to_jack ()) {
+ _engine.transport_stop ();
+ } else {
+ stop_transport (abort);
+ }
+
+ } else if (transport_stopped() && speed == 1.0) {
+
+ /* we are stopped and we want to start rolling at speed 1 */
+
+ if (!get_record_enabled() && Config->get_stop_at_session_end() && _transport_frame >= current_end_frame()) {
+ return;
+ }
+
+ if (Config->get_monitoring_model() == HardwareMonitoring) {
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (Config->get_auto_input() && (*i)->record_enabled ()) {
+ //cerr << "switching from input" << __FILE__ << __LINE__ << endl << endl;
+ (*i)->monitor_input (false);
+ }
+ }
+ }
+
+ if (synced_to_jack()) {
+ _engine.transport_start ();
+ } else {
+ start_transport ();
+ }
+
+ } else {
+
+ if (!get_record_enabled() && Config->get_stop_at_session_end() && _transport_frame >= current_end_frame()) {
+ return;
+ }
+
+ if ((synced_to_jack()) && speed != 0.0 && speed != 1.0) {
+ warning << _("Global varispeed cannot be supported while Ardour is connected to JACK transport control")
+ << endmsg;
+ return;
+ }
+
+ if (actively_recording()) {
+ return;
+ }
+
+ if (speed > 0.0f && _transport_frame == current_end_frame()) {
+ return;
+ }
+
+ if (speed < 0.0f && _transport_frame == 0) {
+ return;
+ }
+
+ clear_clicks ();
+
+ /* if we are reversing relative to the current speed, or relative to the speed
+ before the last stop, then we have to do extra work.
+ */
+
+ if ((_transport_speed && speed * _transport_speed < 0.0f) || (_last_transport_speed * speed < 0.0f) || (_last_transport_speed == 0.0f && speed < 0.0f)) {
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportReverse);
+ }
+
+ _last_transport_speed = _transport_speed;
+ _transport_speed = speed;
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if ((*i)->realtime_set_speed ((*i)->speed(), true)) {
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
+ }
+ }
+
+ if (post_transport_work & (PostTransportSpeed|PostTransportReverse)) {
+ schedule_butler_transport_work ();
+ }
+ }
+}
+
+
+/** Stop the transport. */
+void
+Session::stop_transport (bool abort)
+{
+ if (_transport_speed == 0.0f) {
+ return;
+ }
+
+ if (actively_recording() && !(transport_sub_state & StopPendingCapture) &&
+ _worst_output_latency > current_block_size)
+ {
+
+ /* we need to capture the audio that has still not yet been received by the system
+ at the time the stop is requested, so we have to roll past that time.
+
+ we want to declick before stopping, so schedule the autostop for one
+ block before the actual end. we'll declick in the subsequent block,
+ and then we'll really be stopped.
+ */
+
+ Event *ev = new Event (Event::StopOnce, Event::Replace,
+ _transport_frame + _worst_output_latency - current_block_size,
+ 0, 0, abort);
+
+ merge_event (ev);
+ transport_sub_state |= StopPendingCapture;
+ pending_abort = abort;
+ return;
+ }
+
+
+ if ((transport_sub_state & PendingDeclickOut) == 0) {
+ transport_sub_state |= PendingDeclickOut;
+ /* we'll be called again after the declick */
+ pending_abort = abort;
+ return;
+ }
+
+ realtime_stop (abort);
+ schedule_butler_transport_work ();
+}
+
+void
+Session::start_transport ()
+{
+ _last_roll_location = _transport_frame;
+
+ /* if record status is Enabled, move it to Recording. if its
+ already Recording, move it to Disabled.
+ */
+
+ switch (record_status()) {
+ case Enabled:
+ if (!Config->get_punch_in()) {
+ enable_record ();
+ }
+ break;
+
+ case Recording:
+ if (!play_loop) {
+ disable_record (false);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!synced_to_jack() || _exporting) {
+ actually_start_transport ();
+ } else {
+ waiting_to_start = true;
+ }
+}
+
+void
+Session::actually_start_transport ()
+{
+ waiting_to_start = false;
+
+ transport_sub_state |= PendingDeclickIn;
+ _transport_speed = 1.0;
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->realtime_set_speed ((*i)->speed(), true);
+ }
+
+ deliver_mmc(MIDI::MachineControl::cmdDeferredPlay, _transport_frame);
+
+ TransportStateChange (); /* EMIT SIGNAL */
+}
+
+/** Do any transport work in the audio thread that needs to be done after the
+ * transport thread is finished. Audio thread, realtime safe.
+ */
+void
+Session::post_transport ()
+{
+ if (post_transport_work & PostTransportAudition) {
+ if (auditioner && auditioner->active()) {
+ process_function = &Session::process_audition;
+ } else {
+ process_function = &Session::process_with_events;
+ }
+ }
+
+ if (post_transport_work & PostTransportStop) {
+
+ transport_sub_state = 0;
+ }
+
+ if (post_transport_work & PostTransportLocate) {
+
+ if (((Config->get_slave_source() == None && (auto_play_legal && Config->get_auto_play())) && !_exporting) || (post_transport_work & PostTransportRoll)) {
+ start_transport ();
+
+ } else {
+ transport_sub_state = 0;
+ }
+ }
+
+ set_next_event ();
+
+ post_transport_work = PostTransportWork (0);
+}
+
+void
+Session::reset_rf_scale (nframes_t motion)
+{
+ cumulative_rf_motion += motion;
+
+ if (cumulative_rf_motion < 4 * _current_frame_rate) {
+ rf_scale = 1;
+ } else if (cumulative_rf_motion < 8 * _current_frame_rate) {
+ rf_scale = 4;
+ } else if (cumulative_rf_motion < 16 * _current_frame_rate) {
+ rf_scale = 10;
+ } else {
+ rf_scale = 100;
+ }
+
+ if (motion != 0) {
+ set_dirty();
+ }
+}
+
+void
+Session::set_slave_source (SlaveSource src)
+{
+ bool reverse = false;
+ bool non_rt_required = false;
+
+ if (_transport_speed) {
+ error << _("please stop the transport before adjusting slave settings") << endmsg;
+ return;
+ }
+
+// if (src == JACK && Config->get_jack_time_master()) {
+// return;
+// }
+
+ if (_slave) {
+ delete _slave;
+ _slave = 0;
+ }
+
+ if (_transport_speed < 0.0) {
+ reverse = true;
+ }
+
+ switch (src) {
+ case None:
+ stop_transport ();
+ break;
+
+ case MTC:
+ if (_mtc_port) {
+ try {
+ _slave = new MTC_Slave (*this, *_mtc_port);
+ }
+
+ catch (failed_constructor& err) {
+ return;
+ }
+
+ } else {
+ error << _("No MTC port defined: MTC slaving is impossible.") << endmsg;
+ return;
+ }
+ _desired_transport_speed = _transport_speed;
+ break;
+
+ case JACK:
+ _slave = new JACK_Slave (_engine.jack());
+ _desired_transport_speed = _transport_speed;
+ break;
+ };
+
+ Config->set_slave_source (src);
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ if (!(*i)->hidden()) {
+ if ((*i)->realtime_set_speed ((*i)->speed(), true)) {
+ non_rt_required = true;
+ }
+ (*i)->set_slaved (_slave);
+ }
+ }
+
+ if (reverse) {
+ reverse_diskstream_buffers ();
+ }
+
+ if (non_rt_required) {
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
+ schedule_butler_transport_work ();
+ }
+
+ set_dirty();
+}
+
+void
+Session::reverse_diskstream_buffers ()
+{
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportReverse);
+ schedule_butler_transport_work ();
+}
+
+void
+Session::set_diskstream_speed (Diskstream* stream, float speed)
+{
+ if (stream->realtime_set_speed (speed, false)) {
+ post_transport_work = PostTransportWork (post_transport_work | PostTransportSpeed);
+ schedule_butler_transport_work ();
+ set_dirty ();
+ }
+}
+
+void
+Session::set_audio_range (list<AudioRange>& range)
+{
+ Event *ev = new Event (Event::SetAudioRange, Event::Add, Event::Immediate, 0, 0.0f);
+ ev->audio_range = range;
+ queue_event (ev);
+}
+
+void
+Session::request_play_range (bool yn)
+{
+ Event* ev = new Event (Event::SetPlayRange, Event::Add, Event::Immediate, 0, 0.0f, yn);
+ queue_event (ev);
+}
+
+void
+Session::set_play_range (bool yn)
+{
+ /* Called from event-processing context */
+
+ if (_play_range != yn) {
+ _play_range = yn;
+ setup_auto_play ();
+
+ if (!_play_range) {
+ /* stop transport */
+ Event* ev = new Event (Event::SetTransportSpeed, Event::Add, Event::Immediate, 0, 0.0f, false);
+ merge_event (ev);
+ }
+ }
+}
+
+void
+Session::setup_auto_play ()
+{
+ /* Called from event-processing context */
+
+ Event* ev;
+
+ _clear_event_type (Event::RangeStop);
+ _clear_event_type (Event::RangeLocate);
+
+ if (!_play_range) {
+ return;
+ }
+
+ list<AudioRange>::size_type sz = current_audio_range.size();
+
+ if (sz > 1) {
+
+ list<AudioRange>::iterator i = current_audio_range.begin();
+ list<AudioRange>::iterator next;
+
+ while (i != current_audio_range.end()) {
+
+ next = i;
+ ++next;
+
+ /* locating/stopping is subject to delays for declicking.
+ */
+
+ nframes_t requested_frame = (*i).end;
+
+ if (requested_frame > current_block_size) {
+ requested_frame -= current_block_size;
+ } else {
+ requested_frame = 0;
+ }
+
+ if (next == current_audio_range.end()) {
+ ev = new Event (Event::RangeStop, Event::Add, requested_frame, 0, 0.0f);
+ } else {
+ ev = new Event (Event::RangeLocate, Event::Add, requested_frame, (*next).start, 0.0f);
+ }
+
+ merge_event (ev);
+
+ i = next;
+ }
+
+ } else if (sz == 1) {
+
+ ev = new Event (Event::RangeStop, Event::Add, current_audio_range.front().end, 0, 0.0f);
+ merge_event (ev);
+
+ }
+
+ /* now start rolling at the right place */
+
+ ev = new Event (Event::LocateRoll, Event::Add, Event::Immediate, current_audio_range.front().start, 0.0f, false);
+ merge_event (ev);
+}
+
+void
+Session::request_roll_at_and_return (nframes_t start, nframes_t return_to)
+{
+ Event *ev = new Event (Event::LocateRollLocate, Event::Add, Event::Immediate, return_to, 1.0);
+ ev->target2_frame = start;
+ queue_event (ev);
+}
+
+void
+Session::request_bounded_roll (nframes_t start, nframes_t end)
+{
+ request_stop ();
+ Event *ev = new Event (Event::StopOnce, Event::Replace, end, Event::Immediate, 0.0);
+ queue_event (ev);
+ request_locate (start, true);
+}
+
+void
+Session::engine_halted ()
+{
+ bool ignored;
+
+ /* there will be no more calls to process(), so
+ we'd better clean up for ourselves, right now.
+
+ but first, make sure the butler is out of
+ the picture.
+ */
+
+ g_atomic_int_set (&butler_should_do_transport_work, 0);
+ post_transport_work = PostTransportWork (0);
+ stop_butler ();
+
+ realtime_stop (false);
+ non_realtime_stop (false, 0, ignored);
+ transport_sub_state = 0;
+
+ TransportStateChange (); /* EMIT SIGNAL */
+}
+
+
+void
+Session::xrun_recovery ()
+{
+ Xrun (transport_frame()); //EMIT SIGNAL
+
+ if (Config->get_stop_recording_on_xrun() && actively_recording()) {
+
+ /* it didn't actually halt, but we need
+ to handle things in the same way.
+ */
+
+ engine_halted();
+ }
+}
+
+void
+Session::update_latency_compensation (bool with_stop, bool abort)
+{
+ bool update_jack = false;
+
+ if (_state_of_the_state & Deletion) {
+ return;
+ }
+
+ _worst_track_latency = 0;
+
+ boost::shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+
+ if (with_stop) {
+ (*i)->handle_transport_stopped (abort, (post_transport_work & PostTransportLocate),
+ (!(post_transport_work & PostTransportLocate) || pending_locate_flush));
+ }
+
+ nframes_t old_latency = (*i)->signal_latency ();
+ nframes_t track_latency = (*i)->update_total_latency ();
+
+ if (old_latency != track_latency) {
+ (*i)->update_port_total_latencies ();
+ update_jack = true;
+ }
+
+ if (!(*i)->is_hidden() && ((*i)->active())) {
+ _worst_track_latency = max (_worst_track_latency, track_latency);
+ }
+ }
+
+ if (update_jack) {
+ _engine.update_total_latencies ();
+ }
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->set_latency_delay (_worst_track_latency);
+ }
+
+ set_worst_io_latencies ();
+
+ /* reflect any changes in latencies into capture offsets
+ */
+
+ boost::shared_ptr<DiskstreamList> dsl = diskstreams.reader();
+
+ for (DiskstreamList::iterator i = dsl->begin(); i != dsl->end(); ++i) {
+ (*i)->set_capture_offset ();
+ }
+}
+
+void
+Session::allow_auto_play (bool yn)
+{
+ auto_play_legal = yn;
+}
+
+void
+Session::reset_jack_connection (jack_client_t* jack)
+{
+ JACK_Slave* js;
+
+ if (_slave && ((js = dynamic_cast<JACK_Slave*> (_slave)) != 0)) {
+ js->reset_client (jack);
+ }
+}
diff --git a/libs/ardour/session_utils.cc b/libs/ardour/session_utils.cc
new file mode 100644
index 0000000000..3b2baad69a
--- /dev/null
+++ b/libs/ardour/session_utils.cc
@@ -0,0 +1,39 @@
+
+#include <pbd/error.h>
+
+#include <ardour/session_directory.h>
+
+#include "i18n.h"
+
+namespace ARDOUR {
+
+using namespace PBD;
+
+bool
+create_session_directory (const string& session_directory_path)
+{
+ SessionDirectory sdir(session_directory_path);
+
+ try
+ {
+ // create all the required session directories
+ sdir.create();
+ }
+ catch(sys::filesystem_error& ex)
+ {
+ // log the exception
+ warning << string_compose
+ (
+ _("Unable to create session directory at path %1 : %2"),
+ session_directory_path,
+ ex.what()
+ );
+
+ return false;
+ }
+
+ // successfully created the session directory
+ return true;
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/session_vst.cc b/libs/ardour/session_vst.cc
new file mode 100644
index 0000000000..16233feba2
--- /dev/null
+++ b/libs/ardour/session_vst.cc
@@ -0,0 +1,315 @@
+/*
+ Copyright (C) 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <stdbool.h>
+#include <cstdio>
+
+#include <fst.h>
+#include <vst/aeffectx.h>
+
+#include <ardour/session.h>
+#include <ardour/vst_plugin.h>
+
+#include "i18n.h"
+
+// #define DEBUG_CALLBACKS
+
+#ifdef DEBUG_CALLBACKS
+#define SHOW_CALLBACK printf
+#else
+#define SHOW_CALLBACK(...)
+#endif
+
+using namespace ARDOUR;
+
+long Session::vst_callback (AEffect* effect,
+ long opcode,
+ long index,
+ long value,
+ void* ptr,
+ float opt)
+{
+ static VstTimeInfo _timeInfo;
+ VSTPlugin* plug;
+ Session* session;
+
+ SHOW_CALLBACK ("am callback, opcode = %d", opcode);
+
+ if (effect && effect->user) {
+ plug = static_cast<VSTPlugin*> (effect->user);
+ session = &plug->session();
+ } else {
+ plug = 0;
+ session = 0;
+ }
+
+ switch(opcode){
+
+ case audioMasterAutomate:
+ SHOW_CALLBACK ("amc: audioMasterAutomate\n");
+ // index, value, returns 0
+ if (effect) {
+ effect->setParameter (effect, index, opt);
+ }
+ return 0;
+
+ case audioMasterVersion:
+ SHOW_CALLBACK ("amc: audioMasterVersion\n");
+ // vst version, currently 2 (0 for older)
+ return 2;
+
+ case audioMasterCurrentId:
+ SHOW_CALLBACK ("amc: audioMasterCurrentId\n");
+ // returns the unique id of a plug that's currently
+ // loading
+ return 0;
+
+ case audioMasterIdle:
+ SHOW_CALLBACK ("amc: audioMasterIdle\n");
+ // call application idle routine (this will
+ // call effEditIdle for all open editors too)
+ if (effect) {
+ effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f);
+ }
+ return 0;
+
+ case audioMasterPinConnected:
+ SHOW_CALLBACK ("amc: audioMasterPinConnected\n");
+ // inquire if an input or output is beeing connected;
+ // index enumerates input or output counting from zero:
+ // value is 0 for input and != 0 otherwise. note: the
+ // return value is 0 for <true> such that older versions
+ // will always return true.
+ return 1;
+
+ case audioMasterWantMidi:
+ SHOW_CALLBACK ("amc: audioMasterWantMidi\n");
+ // <value> is a filter which is currently ignored
+ return 0;
+
+ case audioMasterGetTime:
+ SHOW_CALLBACK ("amc: audioMasterGetTime\n");
+ // returns const VstTimeInfo* (or 0 if not supported)
+ // <value> should contain a mask indicating which fields are required
+ // (see valid masks above), as some items may require extensive
+ // conversions
+ memset(&_timeInfo, 0, sizeof(_timeInfo));
+ if (session) {
+ _timeInfo.samplePos = session->transport_frame();
+ _timeInfo.sampleRate = session->frame_rate();
+ }
+ return (long)&_timeInfo;
+
+ case audioMasterProcessEvents:
+ SHOW_CALLBACK ("amc: audioMasterProcessEvents\n");
+ // VstEvents* in <ptr>
+ return 0;
+
+ case audioMasterSetTime:
+ SHOW_CALLBACK ("amc: audioMasterSetTime\n");
+ // VstTimenfo* in <ptr>, filter in <value>, not supported
+
+ case audioMasterTempoAt:
+ SHOW_CALLBACK ("amc: audioMasterTempoAt\n");
+ // returns tempo (in bpm * 10000) at sample frame location passed in <value>
+ return 0;
+
+ case audioMasterGetNumAutomatableParameters:
+ SHOW_CALLBACK ("amc: audioMasterGetNumAutomatableParameters\n");
+ return 0;
+
+ case audioMasterGetParameterQuantization:
+ SHOW_CALLBACK ("amc: audioMasterGetParameterQuantization\n");
+ // returns the integer value for +1.0 representation,
+ // or 1 if full single float precision is maintained
+ // in automation. parameter index in <value> (-1: all, any)
+ return 0;
+
+ case audioMasterIOChanged:
+ SHOW_CALLBACK ("amc: audioMasterIOChanged\n");
+ // numInputs and/or numOutputs has changed
+ return 0;
+
+ case audioMasterNeedIdle:
+ SHOW_CALLBACK ("amc: audioMasterNeedIdle\n");
+ // plug needs idle calls (outside its editor window)
+ return 0;
+
+ case audioMasterSizeWindow:
+ SHOW_CALLBACK ("amc: audioMasterSizeWindow\n");
+ // index: width, value: height
+ return 0;
+
+ case audioMasterGetSampleRate:
+ SHOW_CALLBACK ("amc: audioMasterGetSampleRate\n");
+ return 0;
+
+ case audioMasterGetBlockSize:
+ SHOW_CALLBACK ("amc: audioMasterGetBlockSize\n");
+ return 0;
+
+ case audioMasterGetInputLatency:
+ SHOW_CALLBACK ("amc: audioMasterGetInputLatency\n");
+ return 0;
+
+ case audioMasterGetOutputLatency:
+ SHOW_CALLBACK ("amc: audioMasterGetOutputLatency\n");
+ return 0;
+
+ case audioMasterGetPreviousPlug:
+ SHOW_CALLBACK ("amc: audioMasterGetPreviousPlug\n");
+ // input pin in <value> (-1: first to come), returns cEffect*
+ return 0;
+
+ case audioMasterGetNextPlug:
+ SHOW_CALLBACK ("amc: audioMasterGetNextPlug\n");
+ // output pin in <value> (-1: first to come), returns cEffect*
+
+ case audioMasterWillReplaceOrAccumulate:
+ SHOW_CALLBACK ("amc: audioMasterWillReplaceOrAccumulate\n");
+ // returns: 0: not supported, 1: replace, 2: accumulate
+ return 0;
+
+ case audioMasterGetCurrentProcessLevel:
+ SHOW_CALLBACK ("amc: audioMasterGetCurrentProcessLevel\n");
+ // returns: 0: not supported,
+ // 1: currently in user thread (gui)
+ // 2: currently in audio thread (where process is called)
+ // 3: currently in 'sequencer' thread (midi, timer etc)
+ // 4: currently offline processing and thus in user thread
+ // other: not defined, but probably pre-empting user thread.
+ return 0;
+
+ case audioMasterGetAutomationState:
+ SHOW_CALLBACK ("amc: audioMasterGetAutomationState\n");
+ // returns 0: not supported, 1: off, 2:read, 3:write, 4:read/write
+ // offline
+ return 0;
+
+ case audioMasterOfflineStart:
+ SHOW_CALLBACK ("amc: audioMasterOfflineStart\n");
+ case audioMasterOfflineRead:
+ SHOW_CALLBACK ("amc: audioMasterOfflineRead\n");
+ // ptr points to offline structure, see below. return 0: error, 1 ok
+ return 0;
+
+ case audioMasterOfflineWrite:
+ SHOW_CALLBACK ("amc: audioMasterOfflineWrite\n");
+ // same as read
+ return 0;
+
+ case audioMasterOfflineGetCurrentPass:
+ SHOW_CALLBACK ("amc: audioMasterOfflineGetCurrentPass\n");
+ case audioMasterOfflineGetCurrentMetaPass:
+ SHOW_CALLBACK ("amc: audioMasterOfflineGetCurrentMetaPass\n");
+ return 0;
+
+ case audioMasterSetOutputSampleRate:
+ SHOW_CALLBACK ("amc: audioMasterSetOutputSampleRate\n");
+ // for variable i/o, sample rate in <opt>
+ return 0;
+
+ case audioMasterGetSpeakerArrangement:
+ SHOW_CALLBACK ("amc: audioMasterGetSpeakerArrangement\n");
+ // (long)input in <value>, output in <ptr>
+ return 0;
+
+ case audioMasterGetVendorString:
+ SHOW_CALLBACK ("amc: audioMasterGetVendorString\n");
+ // fills <ptr> with a string identifying the vendor (max 64 char)
+ strcpy ((char*) ptr, "Linux Audio Systems");
+ return 0;
+
+ case audioMasterGetProductString:
+ SHOW_CALLBACK ("amc: audioMasterGetProductString\n");
+ // fills <ptr> with a string with product name (max 64 char)
+ strcpy ((char*) ptr, "Ardour");
+ return 0;
+
+ case audioMasterGetVendorVersion:
+ SHOW_CALLBACK ("amc: audioMasterGetVendorVersion\n");
+ // returns vendor-specific version
+ return 900;
+
+ case audioMasterVendorSpecific:
+ SHOW_CALLBACK ("amc: audioMasterVendorSpecific\n");
+ // no definition, vendor specific handling
+ return 0;
+
+ case audioMasterSetIcon:
+ SHOW_CALLBACK ("amc: audioMasterSetIcon\n");
+ // void* in <ptr>, format not defined yet
+ return 0;
+
+ case audioMasterCanDo:
+ SHOW_CALLBACK ("amc: audioMasterCanDo\n");
+ // string in ptr, see below
+ return 0;
+
+ case audioMasterGetLanguage:
+ SHOW_CALLBACK ("amc: audioMasterGetLanguage\n");
+ // see enum
+ return 0;
+
+ case audioMasterOpenWindow:
+ SHOW_CALLBACK ("amc: audioMasterOpenWindow\n");
+ // returns platform specific ptr
+ return 0;
+
+ case audioMasterCloseWindow:
+ SHOW_CALLBACK ("amc: audioMasterCloseWindow\n");
+ // close window, platform specific handle in <ptr>
+ return 0;
+
+ case audioMasterGetDirectory:
+ SHOW_CALLBACK ("amc: audioMasterGetDirectory\n");
+ // get plug directory, FSSpec on MAC, else char*
+ return 0;
+
+ case audioMasterUpdateDisplay:
+ SHOW_CALLBACK ("amc: audioMasterUpdateDisplay\n");
+ // something has changed, update 'multi-fx' display
+ if (effect) {
+ effect->dispatcher(effect, effEditIdle, 0, 0, NULL, 0.0f);
+ }
+ return 0;
+
+ case audioMasterBeginEdit:
+ SHOW_CALLBACK ("amc: audioMasterBeginEdit\n");
+ // begin of automation session (when mouse down), parameter index in <index>
+ return 0;
+
+ case audioMasterEndEdit:
+ SHOW_CALLBACK ("amc: audioMasterEndEdit\n");
+ // end of automation session (when mouse up), parameter index in <index>
+ return 0;
+
+ case audioMasterOpenFileSelector:
+ SHOW_CALLBACK ("amc: audioMasterOpenFileSelector\n");
+ // open a fileselector window with VstFileSelect* in <ptr>
+ return 0;
+
+ default:
+ SHOW_CALLBACK ("VST master dispatcher: undefed: %d, %d\n", opcode, effKeysRequired);
+ break;
+ }
+
+ return 0;
+}
+
diff --git a/libs/ardour/silentfilesource.cc b/libs/ardour/silentfilesource.cc
new file mode 100644
index 0000000000..b34944e342
--- /dev/null
+++ b/libs/ardour/silentfilesource.cc
@@ -0,0 +1,39 @@
+/*
+ Copyright (C) 2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <ardour/silentfilesource.h>
+
+using namespace ARDOUR;
+
+SilentFileSource::SilentFileSource (Session& s, const XMLNode& node, nframes_t len, float sr)
+ : AudioFileSource (s, node, false)
+{
+ _length = len;
+ _sample_rate = sr;
+}
+
+SilentFileSource::~SilentFileSource ()
+{
+}
+
+void
+SilentFileSource::set_length (nframes_t len)
+{
+ _length = len;
+}
diff --git a/libs/ardour/smf_reader.cc b/libs/ardour/smf_reader.cc
new file mode 100644
index 0000000000..48ec3dc386
--- /dev/null
+++ b/libs/ardour/smf_reader.cc
@@ -0,0 +1,285 @@
+/*
+ Copyright (C) 2008 Paul Davis
+ Written by Dave Robillard
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <cstring>
+#include <cstdio>
+#include <cassert>
+#include <iostream>
+#include <glibmm/miscutils.h>
+#include <midi++/events.h>
+
+#include <ardour/smf_reader.h>
+#include <ardour/midi_util.h>
+
+using namespace std;
+
+namespace ARDOUR {
+
+
+SMFReader::SMFReader(const string filename)
+ : _fd(NULL)
+ , _ppqn(0)
+ , _track(0)
+ , _track_size(0)
+{
+ if (filename.length() > 0) {
+ open(filename);
+ }
+}
+
+
+SMFReader::~SMFReader()
+{
+ if (_fd)
+ close();
+}
+
+
+bool
+SMFReader::open(const string& filename) throw (logic_error, UnsupportedTime)
+{
+ if (_fd)
+ throw logic_error("Attempt to start new read while write in progress.");
+
+ cout << "Opening SMF file " << filename << " for reading." << endl;
+
+ _fd = fopen(filename.c_str(), "r+");
+
+ if (_fd) {
+ // Read type (bytes 8..9)
+ fseek(_fd, 0, SEEK_SET);
+ char mthd[5];
+ mthd[4] = '\0';
+ fread(mthd, 1, 4, _fd);
+ if (strcmp(mthd, "MThd")) {
+ cerr << filename << " is not an SMF file, aborting." << endl;
+ fclose(_fd);
+ _fd = NULL;
+ return false;
+ }
+
+ // Read type (bytes 8..9)
+ fseek(_fd, 8, SEEK_SET);
+ uint16_t type_be = 0;
+ fread(&type_be, 2, 1, _fd);
+ _type = GUINT16_FROM_BE(type_be);
+
+ // Read number of tracks (bytes 10..11)
+ uint16_t num_tracks_be = 0;
+ fread(&num_tracks_be, 2, 1, _fd);
+ _num_tracks = GUINT16_FROM_BE(num_tracks_be);
+
+ // Read PPQN (bytes 12..13)
+ uint16_t ppqn_be = 0;
+ fread(&ppqn_be, 2, 1, _fd);
+ _ppqn = GUINT16_FROM_BE(ppqn_be);
+
+ // TODO: Absolute (SMPTE seconds) time support
+ if ((_ppqn & 0x8000) != 0)
+ throw UnsupportedTime();
+
+ seek_to_track(1);
+
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+/** Seek to the start of a given track, starting from 1.
+ * Returns true if specified track was found.
+ */
+bool
+SMFReader::seek_to_track(unsigned track) throw (std::logic_error)
+{
+ if (track == 0)
+ throw logic_error("Seek to track 0 out of range (must be >= 1)");
+
+ if (!_fd)
+ throw logic_error("Attempt to seek to track on unopened SMF file.");
+
+ unsigned track_pos = 0;
+
+ fseek(_fd, 14, SEEK_SET);
+ char id[5];
+ id[4] = '\0';
+ uint32_t chunk_size = 0;
+
+ while (!feof(_fd)) {
+ fread(id, 1, 4, _fd);
+
+ if (!strcmp(id, "MTrk")) {
+ ++track_pos;
+ } else {
+ std::cerr << "Unknown chunk ID " << id << endl;
+ }
+
+ uint32_t chunk_size_be;
+ fread(&chunk_size_be, 4, 1, _fd);
+ chunk_size = GUINT32_FROM_BE(chunk_size_be);
+
+ if (track_pos == track)
+ break;
+
+ fseek(_fd, chunk_size, SEEK_CUR);
+ }
+
+ if (!feof(_fd) && track_pos == track) {
+ _track = track;
+ _track_size = chunk_size;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+
+/** Read an event from the current position in file.
+ *
+ * File position MUST be at the beginning of a delta time, or this will die very messily.
+ * ev.buffer must be of size ev.size, and large enough for the event. The returned event
+ * will have it's time field set to it's delta time (so it's the caller's responsibility
+ * to keep track of delta time, even for ignored events).
+ *
+ * Returns event length (including status byte) on success, 0 if event was
+ * skipped (eg a meta event), or -1 on EOF (or end of track).
+ *
+ * If @a buf is not large enough to hold the event, 0 will be returned, but ev_size
+ * set to the actual size of the event.
+ */
+int
+SMFReader::read_event(size_t buf_len,
+ uint8_t* buf,
+ uint32_t* ev_size,
+ uint32_t* delta_time)
+ throw (std::logic_error, PrematureEOF, CorruptFile)
+{
+ if (_track == 0)
+ throw logic_error("Attempt to read from unopened SMF file");
+
+ if (!_fd || feof(_fd)) {
+ return -1;
+ }
+
+ assert(buf_len > 0);
+ assert(buf);
+ assert(ev_size);
+ assert(delta_time);
+
+ // Running status state
+ static uint8_t last_status = 0;
+ static uint32_t last_size = 0;
+
+ *delta_time = read_var_len(_fd);
+ int status = fgetc(_fd);
+ if (status == EOF)
+ throw PrematureEOF();
+ else if (status > 0xFF)
+ throw CorruptFile();
+
+ if (status < 0x80) {
+ if (last_status == 0)
+ throw CorruptFile();
+ status = last_status;
+ *ev_size = last_size;
+ fseek(_fd, -1, SEEK_CUR);
+ } else {
+ last_status = status;
+ *ev_size = midi_event_size(status) + 1;
+ last_size = *ev_size;
+ }
+
+ buf[0] = (uint8_t)status;
+
+ if (status == 0xFF) {
+ *ev_size = 0;
+ if (feof(_fd))
+ throw PrematureEOF();
+ uint8_t type = fgetc(_fd);
+ const uint32_t size = read_var_len(_fd);
+ /*cerr.flags(ios::hex);
+ cerr << "SMF - meta 0x" << (int)type << ", size = ";
+ cerr.flags(ios::dec);
+ cerr << size << endl;*/
+
+ if ((uint8_t)type == 0x2F) {
+ return -1; // we hit the logical EOF anyway...
+ } else {
+ fseek(_fd, size, SEEK_CUR);
+ return 0;
+ }
+ }
+
+ if (*ev_size > buf_len || *ev_size == 0 || feof(_fd)) {
+ //cerr << "SMF - Skipping event" << endl;
+ // Skip event, return 0
+ fseek(_fd, *ev_size - 1, SEEK_CUR);
+ return 0;
+ } else {
+ // Read event, return size
+ if (ferror(_fd))
+ throw CorruptFile();
+
+ fread(buf+1, 1, *ev_size - 1, _fd);
+
+ if ((buf[0] & 0xF0) == 0x90 && buf[2] == 0) {
+ buf[0] = (0x80 | (buf[0] & 0x0F));
+ buf[2] = 0x40;
+ }
+
+ return *ev_size;
+ }
+}
+
+
+void
+SMFReader::close()
+{
+ if (_fd)
+ fclose(_fd);
+
+ _fd = NULL;
+}
+
+
+uint32_t
+SMFReader::read_var_len(FILE* fd) throw (PrematureEOF)
+{
+ if (feof(fd))
+ throw PrematureEOF();
+
+ uint32_t value;
+ uint8_t c;
+
+ if ( (value = getc(fd)) & 0x80 ) {
+ value &= 0x7F;
+ do {
+ if (feof(fd))
+ throw PrematureEOF();
+ value = (value << 7) + ((c = getc(fd)) & 0x7F);
+ } while (c & 0x80);
+ }
+
+ return value;
+}
+
+
+} // namespace ARDOUR
+
diff --git a/libs/ardour/smf_source.cc b/libs/ardour/smf_source.cc
new file mode 100644
index 0000000000..bb43d4791a
--- /dev/null
+++ b/libs/ardour/smf_source.cc
@@ -0,0 +1,971 @@
+/*
+ Copyright (C) 2006 Paul Davis
+ Written by Dave Robillard, 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <vector>
+
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <pbd/mountpoint.h>
+#include <pbd/pathscanner.h>
+#include <pbd/stl_delete.h>
+#include <pbd/strsplit.h>
+
+#include <glibmm/miscutils.h>
+
+#include <ardour/smf_source.h>
+#include <ardour/session.h>
+#include <ardour/midi_ring_buffer.h>
+#include <ardour/midi_util.h>
+#include <ardour/tempo.h>
+#include <ardour/audioengine.h>
+#include <ardour/smf_reader.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+string SMFSource::_search_path;
+
+/*sigc::signal<void,struct tm*, time_t> SMFSource::HeaderPositionOffsetChanged;
+bool SMFSource::header_position_negative;
+uint64_t SMFSource::header_position_offset;
+*/
+
+SMFSource::SMFSource (Session& s, std::string path, Flag flags)
+ : MidiSource (s, region_name_from_path(path, false))
+ , _flags (Flag(flags | Writable)) // FIXME: this needs to be writable for now
+ , _allow_remove_if_empty(true)
+ , _fd (0)
+ , _last_ev_time(0)
+ , _track_size(4) // 4 bytes for the ever-present EOT event
+ , _header_size(22)
+ , _empty(true)
+{
+ /* constructor used for new internal-to-session files. file cannot exist */
+
+ if (init (path, false)) {
+ throw failed_constructor ();
+ }
+
+ if (open()) {
+ throw failed_constructor ();
+ }
+
+ assert(_name.find("/") == string::npos);
+}
+
+SMFSource::SMFSource (Session& s, const XMLNode& node)
+ : MidiSource (s, node)
+ , _flags (Flag (Writable|CanRename))
+ , _allow_remove_if_empty(true)
+ , _fd (0)
+ , _last_ev_time(0)
+ , _track_size(4) // 4 bytes for the ever-present EOT event
+ , _header_size(22)
+ , _empty(true)
+{
+ /* constructor used for existing internal-to-session files. file must exist */
+
+ if (set_state (node)) {
+ throw failed_constructor ();
+ }
+
+ if (init (_name, true)) {
+ throw failed_constructor ();
+ }
+
+ if (open()) {
+ throw failed_constructor ();
+ }
+
+ assert(_name.find("/") == string::npos);
+}
+
+SMFSource::~SMFSource ()
+{
+ if (removable()) {
+ unlink (_path.c_str());
+ }
+}
+
+bool
+SMFSource::removable () const
+{
+ return (_flags & Removable) && ((_flags & RemoveAtDestroy) ||
+ ((_flags & RemovableIfEmpty) && is_empty()));
+}
+
+int
+SMFSource::init (string pathstr, bool must_exist)
+{
+ bool is_new = false;
+
+ if (!find (pathstr, must_exist, is_new)) {
+ cerr << "cannot find " << pathstr << " with me = " << must_exist << endl;
+ return -1;
+ }
+
+ if (is_new && must_exist) {
+ return -1;
+ }
+
+ assert(_name.find("/") == string::npos);
+ return 0;
+}
+
+/** Attempt to open the SMF file for reading and writing.
+ *
+ * Currently SMFSource is always read/write.
+ *
+ * \return 0 on success
+ * -1 if the file can not be opened for reading,
+ * -2 if the file can not be opened for writing
+ */
+int
+SMFSource::open()
+{
+ //cerr << "Opening SMF file " << path() << " writeable: " << writable() << endl;
+
+ assert(writable()); // FIXME;
+
+ _fd = fopen(path().c_str(), "r+");
+
+ // File already exists
+ if (_fd) {
+ fseek(_fd, _header_size - 4, 0);
+ uint32_t track_size_be = 0;
+ fread(&track_size_be, 4, 1, _fd);
+ _track_size = GUINT32_FROM_BE(track_size_be);
+ _empty = _track_size > 4;
+ //cerr << "SMF - read track size " << _track_size << endl;
+
+ // We're making a new file
+ } else {
+ _fd = fopen(path().c_str(), "w+");
+ if (_fd == NULL) {
+ cerr << "ERROR: Can not open SMF file " << path() << " for writing: " <<
+ strerror(errno) << endl;
+ return -2;
+ }
+ _track_size = 4;
+ _empty = true;
+
+ // Write a tentative header just to pad things out so writing happens in the right spot
+ flush_header();
+ flush_footer();
+ }
+
+ return (_fd == 0) ? -1 : 0;
+}
+
+void
+SMFSource::close()
+{
+ if (_fd) {
+ flush_header();
+ flush_footer();
+ fclose(_fd);
+ _fd = NULL;
+ }
+}
+
+void
+SMFSource::seek_to_footer_position()
+{
+ uint8_t buffer[4];
+
+ // lets check if there is a track end marker at the end of the data
+ fseek(_fd, -4, SEEK_END);
+ //cerr << "SMFSource::seek_to_footer_position: At position: " << ftell(_fd);
+ size_t read_bytes = fread(buffer, sizeof(uint8_t), 4, _fd);
+ /*cerr << " read size: " << read_bytes << " buffer: ";
+ for (size_t i=0; i < read_bytes; ++i) {
+ printf("%x ", buffer[i]);
+ }
+ printf("\n");
+ */
+
+ if( (read_bytes == 4) &&
+ buffer[0] == 0x00 &&
+ buffer[1] == 0xFF &&
+ buffer[2] == 0x2F &&
+ buffer[3] == 0x00) {
+ // there is one, so overwrite it
+ fseek(_fd, -4, SEEK_END);
+ } else {
+ // there is none, so append
+ fseek(_fd, 0, SEEK_END);
+ }
+}
+
+int
+SMFSource::flush_header()
+{
+ // FIXME: write timeline position somehow?
+
+ //cerr << path() << " SMF Flushing header\n";
+
+ assert(_fd);
+
+ const uint16_t type = GUINT16_TO_BE(0); // SMF Type 0 (single track)
+ const uint16_t ntracks = GUINT16_TO_BE(1); // Number of tracks (always 1 for Type 0)
+ const uint16_t division = GUINT16_TO_BE(_ppqn); // Pulses per quarter note (beat)
+
+ char data[6];
+ memcpy(data, &type, 2);
+ memcpy(data+2, &ntracks, 2);
+ memcpy(data+4, &division, 2);
+
+ _fd = freopen(path().c_str(), "r+", _fd);
+ assert(_fd);
+ fseek(_fd, 0, SEEK_SET);
+ write_chunk("MThd", 6, data);
+ write_chunk_header("MTrk", _track_size);
+
+ fflush(_fd);
+
+ return 0;
+}
+
+int
+SMFSource::flush_footer()
+{
+ //cerr << path() << " SMF Flushing footer\n";
+ seek_to_footer_position();
+ write_footer();
+ seek_to_footer_position();
+
+ return 0;
+}
+
+void
+SMFSource::write_footer()
+{
+ write_var_len(0);
+ char eot[3] = { 0xFF, 0x2F, 0x00 }; // end-of-track meta-event
+ fwrite(eot, 1, 3, _fd);
+ fflush(_fd);
+}
+
+/** Returns the offset of the first event in the file with a time past @a start,
+ * relative to the start of the source.
+ *
+ * Returns -1 if not found.
+ */
+/*
+long
+SMFSource::find_first_event_after(nframes_t start)
+{
+ // FIXME: obviously this is slooow
+
+ fseek(_fd, _header_size, 0);
+
+ while ( ! feof(_fd) ) {
+ const uint32_t delta_time = read_var_len();
+
+ if (delta_time > start)
+ return delta_time;
+ }
+
+ return -1;
+}
+*/
+
+/** Read an event from the current position in file.
+ *
+ * File position MUST be at the beginning of a delta time, or this will die very messily.
+ * ev.buffer must be of size ev.size, and large enough for the event. The returned event
+ * will have it's time field set to it's delta time, in SMF tempo-based ticks, using the
+ * rate given by ppqn() (it is the caller's responsibility to calculate a real time).
+ *
+ * \a size should be the capacity of \a buf. If it is not large enough, \a buf will
+ * be freed and a new buffer allocated in its place, the size of which will be placed
+ * in size.
+ *
+ * Returns event length (including status byte) on success, 0 if event was
+ * skipped (eg a meta event), or -1 on EOF (or end of track).
+ */
+int
+SMFSource::read_event(uint32_t* delta_t, uint32_t* size, uint8_t** buf) const
+{
+ if (feof(_fd)) {
+ return -1;
+ }
+
+ assert(delta_t);
+ assert(size);
+ assert(buf);
+
+ try {
+ *delta_t = SMFReader::read_var_len(_fd);
+ } catch (...) {
+ return -1; // Premature EOF
+ }
+
+ if (feof(_fd)) {
+ return -1; // Premature EOF
+ }
+
+ const int status = fgetc(_fd);
+
+ if (status == EOF) {
+ return -1; // Premature EOF
+ }
+
+ //printf("Status @ %X = %X\n", (unsigned)ftell(_fd) - 1, status);
+
+ if (status == 0xFF) {
+ if (feof(_fd)) {
+ return -1; // Premature EOF
+ }
+ const int type = fgetc(_fd);
+ if ((unsigned char)type == 0x2F) {
+ return -1; // hit end of track
+ } else {
+ *size = 0;
+ return 0;
+ }
+ }
+
+ const int event_size = midi_event_size((unsigned char)status) + 1;
+ if (event_size <= 0) {
+ *size = 0;
+ return 0;
+ }
+
+ // Make sure we have enough scratch buffer
+ if (*size < (unsigned)event_size)
+ *buf = (uint8_t*)realloc(*buf, event_size);
+
+ *size = event_size;
+
+ (*buf)[0] = (unsigned char)status;
+ if (event_size > 1)
+ fread((*buf) + 1, 1, *size - 1, _fd);
+
+ /*printf("SMFSource %s read event: delta = %u, size = %u, data = ", _name.c_str(), *delta_t, *size);
+ for (size_t i=0; i < *size; ++i) {
+ printf("%X ", (*buf)[i]);
+ }
+ printf("\n");*/
+
+ return (int)*size;
+}
+
+/** All stamps in audio frames */
+nframes_t
+SMFSource::read_unlocked (MidiRingBuffer& dst, nframes_t start, nframes_t cnt, nframes_t stamp_offset, nframes_t negative_stamp_offset) const
+{
+ //cerr << "SMF read_unlocked " << name() << " read " << start << ", count=" << cnt << ", offset=" << stamp_offset << endl;
+
+ // 64 bits ought to be enough for anybody
+ uint64_t time = 0; // in SMF ticks, 1 tick per _ppqn
+
+ _read_data_count = 0;
+
+ // Output parameters for read_event (which will allocate scratch in buffer as needed)
+ uint32_t ev_delta_t = 0;
+ uint32_t ev_size = 0;
+ uint8_t* ev_buffer = 0;
+
+ size_t scratch_size = 0; // keep track of scratch to minimize reallocs
+
+ // FIXME: don't seek to start and search every read (brutal!)
+ fseek(_fd, _header_size, SEEK_SET);
+
+ // FIXME: assumes tempo never changes after start
+ const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat(
+ _session.engine().frame_rate(),
+ _session.tempo_map().meter_at(_timeline_position));
+
+ const uint64_t start_ticks = (uint64_t)((start / frames_per_beat) * _ppqn);
+
+ while (!feof(_fd)) {
+ int ret = read_event(&ev_delta_t, &ev_size, &ev_buffer);
+ if (ret == -1) { // EOF
+ //cerr << "SMF - EOF\n";
+ break;
+ }
+
+ time += ev_delta_t; // accumulate delta time
+
+ if (ret == 0) { // meta-event (skipped, just accumulate time)
+ //cerr << "SMF - META\n";
+ continue;
+ }
+
+ if (time >= start_ticks) {
+ const nframes_t ev_frame_time = (nframes_t)(
+ ((time / (double)_ppqn) * frames_per_beat)) + stamp_offset;
+
+ if (ev_frame_time <= start + cnt)
+ dst.write(ev_frame_time - negative_stamp_offset, ev_size, ev_buffer);
+ else
+ break;
+ }
+
+ _read_data_count += ev_size;
+
+ if (ev_size > scratch_size)
+ scratch_size = ev_size;
+ else
+ ev_size = scratch_size; // minimize realloc in read_event
+ }
+
+ return cnt;
+}
+
+/** All stamps in audio frames */
+nframes_t
+SMFSource::write_unlocked (MidiRingBuffer& src, nframes_t cnt)
+{
+ _write_data_count = 0;
+
+ double time;
+ size_t size;
+
+ size_t buf_capacity = 4;
+ uint8_t* buf = (uint8_t*)malloc(buf_capacity);
+
+ if (_model && ! _model->writing())
+ _model->start_write();
+
+ MIDI::Event ev(0.0, 4, NULL, true);
+
+ while (true) {
+ bool ret = src.full_peek(sizeof(double), (uint8_t*)&time);
+ if (!ret || time - _timeline_position > _length + cnt)
+ break;
+
+ ret = src.read_prefix(&time, &size);
+ if (!ret)
+ break;
+
+ if (size > buf_capacity) {
+ buf_capacity = size;
+ buf = (uint8_t*)realloc(buf, size);
+ }
+
+ ret = src.read_contents(size, buf);
+ if (!ret) {
+ cerr << "ERROR: Read time/size but not buffer, corrupt MIDI ring buffer" << endl;
+ break;
+ }
+
+ assert(time >= _timeline_position);
+ time -= _timeline_position;
+
+ ev.set(buf, size, time);
+ if (! (ev.is_channel_event() || ev.is_smf_meta_event() || ev.is_sysex()) ) {
+ //cerr << "SMFSource: WARNING: caller tried to write non SMF-Event of type " << std::hex << int(ev.buffer()[0]) << endl;
+ continue;
+ }
+
+ append_event_unlocked(Frames, ev);
+
+ if (_model)
+ _model->append(ev);
+ }
+
+ fflush(_fd);
+ free(buf);
+
+ const nframes_t oldlen = _length;
+ update_length(oldlen, cnt);
+
+ ViewDataRangeReady (_timeline_position + oldlen, cnt); /* EMIT SIGNAL */
+
+ return cnt;
+}
+
+
+void
+SMFSource::append_event_unlocked(EventTimeUnit unit, const MIDI::Event& ev)
+{
+ if (ev.size() == 0)
+ return;
+
+ /*printf("SMFSource: %s - append_event_unlocked chan = %u, time = %lf, size = %u, data = ",
+ name().c_str(), (unsigned)ev.channel(), ev.time(), ev.size());
+ for (size_t i=0; i < ev.size(); ++i) {
+ printf("%X ", ev.buffer()[i]);
+ }
+ printf("\n");*/
+
+ assert(ev.time() >= 0);
+
+ if (ev.time() < _last_ev_time) {
+ cerr << "SMFSource: Warning: Skipping event with ev.time() < _last_ev_time" << endl;
+ return;
+ }
+
+ uint32_t delta_time = 0;
+
+ if (unit == Frames) {
+ // FIXME: assumes tempo never changes after start
+ const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat(
+ _session.engine().frame_rate(),
+ _session.tempo_map().meter_at(_timeline_position));
+
+ delta_time = (uint32_t)((ev.time() - _last_ev_time) / frames_per_beat * _ppqn);
+ } else {
+ assert(unit == Beats);
+ delta_time = (uint32_t)((ev.time() - _last_ev_time) * _ppqn);
+ }
+
+
+ const size_t stamp_size = write_var_len(delta_time);
+ fwrite(ev.buffer(), 1, ev.size(), _fd);
+
+ _track_size += stamp_size + ev.size();
+ _write_data_count += ev.size();
+ _last_ev_time = ev.time();
+
+ if (ev.size() > 0)
+ _empty = false;
+}
+
+
+XMLNode&
+SMFSource::get_state ()
+{
+ XMLNode& root (MidiSource::get_state());
+ char buf[16];
+ snprintf (buf, sizeof (buf), "0x%x", (int)_flags);
+ root.add_property ("flags", buf);
+ return root;
+}
+
+int
+SMFSource::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+
+ if (MidiSource::set_state (node)) {
+ return -1;
+ }
+
+ if ((prop = node.property (X_("flags"))) != 0) {
+
+ int ival;
+ sscanf (prop->value().c_str(), "0x%x", &ival);
+ _flags = Flag (ival);
+
+ } else {
+
+ _flags = Flag (0);
+
+ }
+
+ assert(_name.find("/") == string::npos);
+
+ return 0;
+}
+
+void
+SMFSource::mark_for_remove ()
+{
+ if (!writable()) {
+ return;
+ }
+ _flags = Flag (_flags | RemoveAtDestroy);
+}
+
+void
+SMFSource::mark_streaming_midi_write_started (NoteMode mode, nframes_t start_frame)
+{
+ MidiSource::mark_streaming_midi_write_started (mode, start_frame);
+ _last_ev_time = 0;
+ fseek(_fd, _header_size, SEEK_SET);
+}
+
+void
+SMFSource::mark_streaming_write_completed ()
+{
+ MidiSource::mark_streaming_write_completed();
+
+ if (!writable()) {
+ return;
+ }
+
+ _model->set_edited(false);
+ flush_header();
+ flush_footer();
+}
+
+void
+SMFSource::mark_take (string id)
+{
+ if (writable()) {
+ _take_id = id;
+ }
+}
+
+int
+SMFSource::move_to_trash (const string trash_dir_name)
+{
+ string newpath;
+
+ if (!writable()) {
+ return -1;
+ }
+
+ /* don't move the file across filesystems, just
+ stick it in the 'trash_dir_name' directory
+ on whichever filesystem it was already on.
+ */
+
+ newpath = Glib::path_get_dirname (_path);
+ newpath = Glib::path_get_dirname (newpath);
+
+ newpath += '/';
+ newpath += trash_dir_name;
+ newpath += '/';
+ newpath += Glib::path_get_basename (_path);
+
+ if (access (newpath.c_str(), F_OK) == 0) {
+
+ /* the new path already exists, try versioning */
+
+ char buf[PATH_MAX+1];
+ int version = 1;
+ string newpath_v;
+
+ snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), version);
+ newpath_v = buf;
+
+ while (access (newpath_v.c_str(), F_OK) == 0 && version < 999) {
+ snprintf (buf, sizeof (buf), "%s.%d", newpath.c_str(), ++version);
+ newpath_v = buf;
+ }
+
+ if (version == 999) {
+ PBD::error << string_compose (_("there are already 1000 files with names like %1; versioning discontinued"),
+ newpath)
+ << endmsg;
+ } else {
+ newpath = newpath_v;
+ }
+
+ } else {
+
+ /* it doesn't exist, or we can't read it or something */
+
+ }
+
+ if (::rename (_path.c_str(), newpath.c_str()) != 0) {
+ PBD::error << string_compose (_("cannot rename midi file source from %1 to %2 (%3)"),
+ _path, newpath, strerror (errno))
+ << endmsg;
+ return -1;
+ }
+#if 0
+ if (::unlink (peakpath.c_str()) != 0) {
+ PBD::error << string_compose (_("cannot remove peakfile %1 for %2 (%3)"),
+ peakpath, _path, strerror (errno))
+ << endmsg;
+ /* try to back out */
+ rename (newpath.c_str(), _path.c_str());
+ return -1;
+ }
+
+ _path = newpath;
+ peakpath = "";
+#endif
+ /* file can not be removed twice, since the operation is not idempotent */
+
+ _flags = Flag (_flags & ~(RemoveAtDestroy|Removable|RemovableIfEmpty));
+
+ return 0;
+}
+
+bool
+SMFSource::safe_file_extension(const Glib::ustring& file)
+{
+ return (file.rfind(".mid") != Glib::ustring::npos);
+}
+
+// FIXME: Merge this with audiofilesource somehow (make a generic filesource?)
+bool
+SMFSource::find (string pathstr, bool must_exist, bool& isnew)
+{
+ string::size_type pos;
+ bool ret = false;
+
+ isnew = false;
+
+ /* clean up PATH:CHANNEL notation so that we are looking for the correct path */
+
+ if ((pos = pathstr.find_last_of (':')) == string::npos) {
+ pathstr = pathstr;
+ } else {
+ pathstr = pathstr.substr (0, pos);
+ }
+
+ if (pathstr[0] != '/') {
+
+ /* non-absolute pathname: find pathstr in search path */
+
+ vector<string> dirs;
+ int cnt;
+ string fullpath;
+ string keeppath;
+
+ if (_search_path.length() == 0) {
+ PBD::error << _("FileSource: search path not set") << endmsg;
+ goto out;
+ }
+
+ split (_search_path, dirs, ':');
+
+ cnt = 0;
+
+ for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); ++i) {
+
+ fullpath = *i;
+ if (fullpath[fullpath.length()-1] != '/') {
+ fullpath += '/';
+ }
+ fullpath += pathstr;
+
+ if (access (fullpath.c_str(), R_OK) == 0) {
+ keeppath = fullpath;
+ ++cnt;
+ }
+ }
+
+ if (cnt > 1) {
+
+ PBD::error << string_compose (_("FileSource: \"%1\" is ambigous when searching %2\n\t"), pathstr, _search_path) << endmsg;
+ goto out;
+
+ } else if (cnt == 0) {
+
+ if (must_exist) {
+ PBD::error << string_compose(_("Filesource: cannot find required file (%1): while searching %2"), pathstr, _search_path) << endmsg;
+ goto out;
+ } else {
+ isnew = true;
+ }
+ }
+
+ _name = pathstr;
+ _path = keeppath;
+ ret = true;
+
+ } else {
+
+ /* external files and/or very very old style sessions include full paths */
+
+ _path = pathstr;
+ _name = pathstr.substr (pathstr.find_last_of ('/') + 1);
+
+ if (access (_path.c_str(), R_OK) != 0) {
+
+ /* file does not exist or we cannot read it */
+
+ if (must_exist) {
+ PBD::error << string_compose(_("Filesource: cannot find required file (%1): %2"), _path, strerror (errno)) << endmsg;
+ goto out;
+ }
+
+ if (errno != ENOENT) {
+ PBD::error << string_compose(_("Filesource: cannot check for existing file (%1): %2"), _path, strerror (errno)) << endmsg;
+ goto out;
+ }
+
+ /* a new file */
+
+ isnew = true;
+ ret = true;
+
+ } else {
+
+ /* already exists */
+
+ ret = true;
+ }
+ }
+
+ out:
+ return ret;
+}
+
+void
+SMFSource::set_search_path (string p)
+{
+ _search_path = p;
+}
+
+
+void
+SMFSource::set_allow_remove_if_empty (bool yn)
+{
+ if (writable()) {
+ _allow_remove_if_empty = yn;
+ }
+}
+
+int
+SMFSource::set_source_name (string newname, bool destructive)
+{
+ //Glib::Mutex::Lock lm (_lock); FIXME
+ string oldpath = _path;
+ string newpath = Session::change_midi_path_by_name (oldpath, _name, newname, destructive);
+
+ if (newpath.empty()) {
+ PBD::error << string_compose (_("programming error: %1"), "cannot generate a changed midi path") << endmsg;
+ return -1;
+ }
+
+ if (rename (oldpath.c_str(), newpath.c_str()) != 0) {
+ PBD::error << string_compose (_("cannot rename midi file for %1 to %2"), _name, newpath) << endmsg;
+ return -1;
+ }
+
+ _name = Glib::path_get_basename (newpath);
+ _path = newpath;
+
+ return 0;//rename_peakfile (peak_path (_path));
+}
+
+bool
+SMFSource::is_empty () const
+{
+ return _empty;
+}
+
+
+void
+SMFSource::write_chunk_header(const char id[4], uint32_t length)
+{
+ const uint32_t length_be = GUINT32_TO_BE(length);
+
+ fwrite(id, 1, 4, _fd);
+ fwrite(&length_be, 4, 1, _fd);
+}
+
+void
+SMFSource::write_chunk(const char id[4], uint32_t length, void* data)
+{
+ write_chunk_header(id, length);
+
+ fwrite(data, 1, length, _fd);
+}
+
+/** Returns the size (in bytes) of the value written. */
+size_t
+SMFSource::write_var_len(uint32_t value)
+{
+ size_t ret = 0;
+
+ uint32_t buffer = value & 0x7F;
+
+ while ( (value >>= 7) ) {
+ buffer <<= 8;
+ buffer |= ((value & 0x7F) | 0x80);
+ }
+
+ while (true) {
+ //printf("Writing var len byte %X\n", (unsigned char)buffer);
+ ++ret;
+ fputc(buffer, _fd);
+ if (buffer & 0x80)
+ buffer >>= 8;
+ else
+ break;
+ }
+
+ return ret;
+}
+
+void
+SMFSource::load_model(bool lock, bool force_reload)
+{
+ if (_writing)
+ return;
+
+ if (lock)
+ Glib::Mutex::Lock lm (_lock);
+
+ if (_model && !force_reload && !_model->empty())
+ return;
+
+ if (! _model) {
+ _model = boost::shared_ptr<MidiModel>(new MidiModel(this));
+ cerr << _name << " loaded new model " << _model.get() << endl;
+ } else {
+ cerr << _name << " reloading model " << _model.get()
+ << " (" << _model->n_notes() << " notes)" <<endl;
+ _model->clear();
+ }
+
+ _model->start_write();
+
+ fseek(_fd, _header_size, SEEK_SET);
+
+ uint64_t time = 0; /* in SMF ticks */
+ MIDI::Event ev;
+
+ size_t scratch_size = 0; // keep track of scratch and minimize reallocs
+
+ // FIXME: assumes tempo never changes after start
+ const double frames_per_beat = _session.tempo_map().tempo_at(_timeline_position).frames_per_beat(
+ _session.engine().frame_rate(),
+ _session.tempo_map().meter_at(_timeline_position));
+
+ uint32_t delta_t = 0;
+ uint32_t size = 0;
+ uint8_t* buf = NULL;
+ int ret;
+ while ((ret = read_event(&delta_t, &size, &buf)) >= 0) {
+
+ ev.set(buf, size, 0.0);
+ time += delta_t;
+
+ if (ret > 0) { // didn't skip (meta) event
+ // make ev.time absolute time in frames
+ ev.time() = (double)time * frames_per_beat / (double)_ppqn;
+ _model->append(ev);
+ }
+
+ if (ev.size() > scratch_size)
+ scratch_size = ev.size();
+ else
+ ev.size() = scratch_size;
+ }
+
+ _model->end_write(false);
+ _model->set_edited(false);
+
+ free(buf);
+}
+
+
+void
+SMFSource::destroy_model()
+{
+ //cerr << _name << " destroying model " << _model.get() << endl;
+ _model.reset();
+}
+
diff --git a/libs/ardour/sndfile_helpers.cc b/libs/ardour/sndfile_helpers.cc
new file mode 100644
index 0000000000..58a51f8bbe
--- /dev/null
+++ b/libs/ardour/sndfile_helpers.cc
@@ -0,0 +1,209 @@
+/*
+ Copyright (C) 2000-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <map>
+#include <vector>
+
+#include <pbd/convert.h>
+
+#include <sndfile.h>
+#include <ardour/sndfile_helpers.h>
+
+#include "i18n.h"
+
+using std::map;
+using namespace std;
+
+const char * const sndfile_header_formats_strings[SNDFILE_HEADER_FORMATS+1] = {
+ N_("WAV"),
+ N_("AIFF"),
+ N_("CAF"),
+ N_("W64 (64 bit WAV)"),
+ N_("raw (no header)"),
+ 0
+};
+
+const char* const sndfile_file_endings_strings[SNDFILE_HEADER_FORMATS+1] = {
+ N_(".wav"),
+ N_(".aiff"),
+ N_(".caf"),
+ N_(".w64"),
+ N_(".raw"),
+ 0
+};
+
+int sndfile_header_formats[SNDFILE_HEADER_FORMATS] = {
+ SF_FORMAT_WAV,
+ SF_FORMAT_AIFF,
+ SF_FORMAT_CAF,
+ SF_FORMAT_W64,
+ SF_FORMAT_RAW
+};
+
+const char * const sndfile_bitdepth_formats_strings[SNDFILE_BITDEPTH_FORMATS+1] = {
+ N_("Signed 16 bit PCM"),
+ N_("Signed 24 bit PCM"),
+ N_("Signed 32 bit PCM"),
+ N_("Signed 8 bit PCM"),
+ N_("32 bit float"),
+ 0
+};
+
+int sndfile_bitdepth_formats[SNDFILE_BITDEPTH_FORMATS] = {
+ SF_FORMAT_PCM_16,
+ SF_FORMAT_PCM_24,
+ SF_FORMAT_PCM_32,
+ SF_FORMAT_PCM_S8,
+ SF_FORMAT_FLOAT
+};
+
+const char * const sndfile_endian_formats_strings[SNDFILE_ENDIAN_FORMATS+1] = {
+ N_("Little-endian (Intel)"),
+ N_("Big-endian (Mac)"),
+ 0
+};
+
+int sndfile_endian_formats[SNDFILE_ENDIAN_FORMATS] = {
+ SF_ENDIAN_LITTLE,
+ SF_ENDIAN_BIG
+};
+
+int
+sndfile_header_format_from_string (string str)
+{
+ for (int n = 0; sndfile_header_formats_strings[n]; ++n) {
+ if (str == sndfile_header_formats_strings[n]) {
+ return sndfile_header_formats[n];
+ }
+ }
+ return -1;
+}
+
+int
+sndfile_bitdepth_format_from_string (string str)
+{
+ for (int n = 0; sndfile_bitdepth_formats_strings[n]; ++n) {
+ if (str == sndfile_bitdepth_formats_strings[n]) {
+ return sndfile_bitdepth_formats[n];
+ }
+ }
+ return -1;
+}
+
+int
+sndfile_endian_format_from_string (string str)
+{
+ for (int n = 0; sndfile_endian_formats_strings[n]; ++n) {
+ if (str == sndfile_endian_formats_strings[n]) {
+ return sndfile_endian_formats[n];
+ }
+ }
+ return -1;
+}
+
+string
+sndfile_file_ending_from_string (string str)
+{
+ static vector<string> file_endings;
+
+ if (file_endings.empty()) {
+ file_endings = I18N((const char **) sndfile_file_endings_strings);
+ }
+
+ for (int n = 0; sndfile_header_formats_strings[n]; ++n) {
+ if (str == sndfile_header_formats_strings[n]) {
+ return file_endings[n];
+ }
+ }
+ return 0;
+}
+
+int
+sndfile_data_width (int format)
+{
+ int tval = format & 0xf;
+
+ switch (tval) {
+ case SF_FORMAT_PCM_S8:
+ case SF_FORMAT_PCM_U8:
+ return 8;
+ case SF_FORMAT_PCM_16:
+ return 16;
+ case SF_FORMAT_PCM_24:
+ return 24;
+ case SF_FORMAT_PCM_32:
+ return 32;
+ case SF_FORMAT_FLOAT:
+ return 1; // heh, heh
+ default:
+ // we don't handle anything else within ardour
+ return 0;
+ }
+}
+
+string
+sndfile_major_format(int format)
+{
+ static map<int, string> m;
+
+ if(m.empty()){
+ SF_FORMAT_INFO format_info;
+ int count;
+ sf_command(0, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int));
+ for (int i = 0; i < count; ++i){
+ format_info.format = i;
+ sf_command (0, SFC_GET_FORMAT_MAJOR,
+ &format_info, sizeof (format_info));
+ m[format_info.format & SF_FORMAT_TYPEMASK] = format_info.name;
+ }
+ }
+
+ map<int, string>::iterator p = m.find(format & SF_FORMAT_TYPEMASK);
+ if(p != m.end()){
+ return m[format & SF_FORMAT_TYPEMASK];
+ } else {
+ return "-Unknown-";
+ }
+}
+
+string
+sndfile_minor_format(int format)
+{
+ static map<int, string> m;
+
+ if(m.empty()){
+ SF_FORMAT_INFO format_info;
+ int count;
+ sf_command(0, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int));
+ for (int i = 0; i < count; ++i){
+ format_info.format = i;
+ sf_command (0, SFC_GET_FORMAT_SUBTYPE,
+ &format_info, sizeof (format_info));
+ m[format_info.format & SF_FORMAT_SUBMASK] = format_info.name;
+ }
+ }
+
+ map<int, string>::iterator p = m.find(format & SF_FORMAT_SUBMASK);
+ if(p != m.end()){
+ return m[format & SF_FORMAT_SUBMASK];
+ } else {
+ return "-Unknown-";
+ }
+}
+
diff --git a/libs/ardour/sndfileimportable.cc b/libs/ardour/sndfileimportable.cc
new file mode 100644
index 0000000000..eb0e8a8afb
--- /dev/null
+++ b/libs/ardour/sndfileimportable.cc
@@ -0,0 +1,47 @@
+#include <ardour/sndfileimportable.h>
+#include <sndfile.h>
+
+using namespace ARDOUR;
+using namespace std;
+
+SndFileImportableSource::SndFileImportableSource (const string& path)
+ : in (sf_open (path.c_str(), SFM_READ, &sf_info), sf_close)
+{
+ if (!in) throw failed_constructor();
+}
+
+SndFileImportableSource::~SndFileImportableSource ()
+{
+}
+
+nframes_t
+SndFileImportableSource::read (Sample* buffer, nframes_t nframes)
+{
+ nframes_t per_channel = nframes / sf_info.channels;
+ per_channel = sf_readf_float (in.get(), buffer, per_channel);
+ return per_channel * sf_info.channels;
+}
+
+uint
+SndFileImportableSource::channels () const
+{
+ return sf_info.channels;
+}
+
+nframes_t
+SndFileImportableSource::length () const
+{
+ return sf_info.frames;
+}
+
+nframes_t
+SndFileImportableSource::samplerate() const
+{
+ return sf_info.samplerate;
+}
+
+void
+SndFileImportableSource::seek (nframes_t pos)
+{
+ sf_seek (in.get(), 0, SEEK_SET);
+}
diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc
new file mode 100644
index 0000000000..ab090381b4
--- /dev/null
+++ b/libs/ardour/sndfilesource.cc
@@ -0,0 +1,900 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cstring>
+#include <cerrno>
+#include <climits>
+
+#include <pwd.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+
+#include <glibmm/miscutils.h>
+#include <ardour/sndfilesource.h>
+#include <ardour/sndfile_helpers.h>
+#include <ardour/utils.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+using Glib::ustring;
+
+gain_t* SndFileSource::out_coefficient = 0;
+gain_t* SndFileSource::in_coefficient = 0;
+nframes_t SndFileSource::xfade_frames = 64;
+const AudioFileSource::Flag SndFileSource::default_writable_flags = AudioFileSource::Flag (AudioFileSource::Writable|
+ AudioFileSource::Removable|
+ AudioFileSource::RemovableIfEmpty|
+ AudioFileSource::CanRename);
+SndFileSource::SndFileSource (Session& s, const XMLNode& node)
+ : AudioFileSource (s, node)
+{
+ init ();
+
+ if (open()) {
+ throw failed_constructor ();
+ }
+}
+
+SndFileSource::SndFileSource (Session& s, ustring path, int chn, Flag flags)
+ /* files created this way are never writable or removable */
+ : AudioFileSource (s, path, Flag (flags & ~(Writable|Removable|RemovableIfEmpty|RemoveAtDestroy)))
+{
+ _channel = chn;
+
+ init ();
+
+ if (open()) {
+ throw failed_constructor ();
+ }
+}
+
+SndFileSource::SndFileSource (Session& s, ustring path, SampleFormat sfmt, HeaderFormat hf, nframes_t rate, Flag flags)
+ : AudioFileSource (s, path, flags, sfmt, hf)
+{
+ int fmt = 0;
+
+ init ();
+
+ /* this constructor is used to construct new files, not open
+ existing ones.
+ */
+
+ file_is_new = true;
+
+ switch (hf) {
+ case CAF:
+ fmt = SF_FORMAT_CAF;
+ _flags = Flag (_flags & ~Broadcast);
+ break;
+
+ case AIFF:
+ fmt = SF_FORMAT_AIFF;
+ _flags = Flag (_flags & ~Broadcast);
+ break;
+
+ case BWF:
+ fmt = SF_FORMAT_WAV;
+ _flags = Flag (_flags | Broadcast);
+ break;
+
+ case WAVE:
+ fmt = SF_FORMAT_WAV;
+ _flags = Flag (_flags & ~Broadcast);
+ break;
+
+ case WAVE64:
+ fmt = SF_FORMAT_W64;
+ _flags = Flag (_flags & ~Broadcast);
+ break;
+
+ default:
+ fatal << string_compose (_("programming error: %1"), X_("unsupported audio header format requested")) << endmsg;
+ /*NOTREACHED*/
+ break;
+
+ }
+
+ switch (sfmt) {
+ case FormatFloat:
+ fmt |= SF_FORMAT_FLOAT;
+ break;
+
+ case FormatInt24:
+ fmt |= SF_FORMAT_PCM_24;
+ break;
+
+ case FormatInt16:
+ fmt |= SF_FORMAT_PCM_16;
+ break;
+ }
+
+ _info.channels = 1;
+ _info.samplerate = rate;
+ _info.format = fmt;
+
+ if (open()) {
+ throw failed_constructor();
+ }
+
+ if (writable() && (_flags & Broadcast)) {
+
+ _broadcast_info = new SF_BROADCAST_INFO;
+ memset (_broadcast_info, 0, sizeof (*_broadcast_info));
+
+ snprintf (_broadcast_info->description, sizeof (_broadcast_info->description), "BWF %s", _name.c_str());
+
+ struct utsname utsinfo;
+
+ if (uname (&utsinfo)) {
+ error << string_compose(_("FileSource: cannot get host information for BWF header (%1)"), strerror(errno)) << endmsg;
+ return;
+ }
+
+ snprintf (_broadcast_info->originator, sizeof (_broadcast_info->originator), "ardour:%s:%s:%s:%s:%s)",
+ Glib::get_real_name().c_str(),
+ utsinfo.nodename,
+ utsinfo.sysname,
+ utsinfo.release,
+ utsinfo.version);
+
+ _broadcast_info->version = 1;
+ _broadcast_info->time_reference_low = 0;
+ _broadcast_info->time_reference_high = 0;
+
+ /* XXX do something about this field */
+
+ snprintf (_broadcast_info->umid, sizeof (_broadcast_info->umid), "%s", "fnord");
+
+ /* coding history is added by libsndfile */
+
+ if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (_broadcast_info)) != SF_TRUE) {
+ char errbuf[256];
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ error << string_compose (_("cannot set broadcast info for audio file %1 (%2); dropping broadcast info for this file"), _path, errbuf) << endmsg;
+ _flags = Flag (_flags & ~Broadcast);
+ delete _broadcast_info;
+ _broadcast_info = 0;
+ }
+ }
+}
+
+void
+SndFileSource::init ()
+{
+ ustring file;
+
+ // lets try to keep the object initalizations here at the top
+ xfade_buf = 0;
+ sf = 0;
+ _broadcast_info = 0;
+
+ if (is_embedded()) {
+ _name = _path;
+ } else {
+ _name = Glib::path_get_basename (_path);
+ }
+
+ /* although libsndfile says we don't need to set this,
+ valgrind and source code shows us that we do.
+ */
+
+ memset (&_info, 0, sizeof(_info));
+
+ _capture_start = false;
+ _capture_end = false;
+ file_pos = 0;
+
+ if (destructive()) {
+ xfade_buf = new Sample[xfade_frames];
+ timeline_position = header_position_offset;
+ }
+
+ AudioFileSource::HeaderPositionOffsetChanged.connect (mem_fun (*this, &SndFileSource::handle_header_position_change));
+}
+
+int
+SndFileSource::open ()
+{
+ if ((sf = sf_open (_path.c_str(), (writable() ? SFM_RDWR : SFM_READ), &_info)) == 0) {
+ char errbuf[256];
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+#ifndef HAVE_COREAUDIO
+ /* if we have CoreAudio, we will be falling back to that if libsndfile fails,
+ so we don't want to see this message.
+ */
+
+ error << string_compose(_("SndFileSource: cannot open file \"%1\" for %2 (%3)"),
+ _path, (writable() ? "read+write" : "reading"), errbuf) << endmsg;
+#endif
+ return -1;
+ }
+
+ if (_channel >= _info.channels) {
+#ifndef HAVE_COREAUDIO
+ error << string_compose(_("SndFileSource: file only contains %1 channels; %2 is invalid as a channel number"), _info.channels, _channel) << endmsg;
+#endif
+ sf_close (sf);
+ sf = 0;
+ return -1;
+ }
+
+ _length = _info.frames;
+
+ _broadcast_info = new SF_BROADCAST_INFO;
+ memset (_broadcast_info, 0, sizeof (*_broadcast_info));
+
+ bool timecode_info_exists;
+
+ set_timeline_position (get_timecode_info (sf, _broadcast_info, timecode_info_exists));
+
+ if (_length != 0 && !timecode_info_exists) {
+ delete _broadcast_info;
+ _broadcast_info = 0;
+ _flags = Flag (_flags & ~Broadcast);
+ }
+
+ if (writable()) {
+ sf_command (sf, SFC_SET_UPDATE_HEADER_AUTO, 0, SF_FALSE);
+ }
+
+ return 0;
+}
+
+SndFileSource::~SndFileSource ()
+{
+ GoingAway (); /* EMIT SIGNAL */
+
+ if (sf) {
+ sf_close (sf);
+ sf = 0;
+
+ /* stupid libsndfile updated the headers on close,
+ so touch the peakfile if it exists and has data
+ to make sure its time is as new as the audio
+ file.
+ */
+
+ touch_peakfile ();
+ }
+
+ if (_broadcast_info) {
+ delete _broadcast_info;
+ }
+
+ if (xfade_buf) {
+ delete [] xfade_buf;
+ }
+}
+
+float
+SndFileSource::sample_rate () const
+{
+ return _info.samplerate;
+}
+
+nframes_t
+SndFileSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const
+{
+ int32_t nread;
+ float *ptr;
+ uint32_t real_cnt;
+ nframes_t file_cnt;
+
+ if (start > _length) {
+
+ /* read starts beyond end of data, just memset to zero */
+
+ file_cnt = 0;
+
+ } else if (start + cnt > _length) {
+
+ /* read ends beyond end of data, read some, memset the rest */
+
+ file_cnt = _length - start;
+
+ } else {
+
+ /* read is entirely within data */
+
+ file_cnt = cnt;
+ }
+
+ if (file_cnt != cnt) {
+ nframes_t delta = cnt - file_cnt;
+ memset (dst+file_cnt, 0, sizeof (Sample) * delta);
+ }
+
+ if (file_cnt) {
+
+ if (sf_seek (sf, (sf_count_t) start, SEEK_SET|SFM_READ) != (sf_count_t) start) {
+ char errbuf[256];
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ error << string_compose(_("SndFileSource: could not seek to frame %1 within %2 (%3)"), start, _name.substr (1), errbuf) << endmsg;
+ return 0;
+ }
+
+ if (_info.channels == 1) {
+ nframes_t ret = sf_read_float (sf, dst, file_cnt);
+ _read_data_count = cnt * sizeof(float);
+ return ret;
+ }
+ }
+
+ real_cnt = cnt * _info.channels;
+
+ Sample* interleave_buf = get_interleave_buffer (real_cnt);
+
+ nread = sf_read_float (sf, interleave_buf, real_cnt);
+ ptr = interleave_buf + _channel;
+ nread /= _info.channels;
+
+ /* stride through the interleaved data */
+
+ for (int32_t n = 0; n < nread; ++n) {
+ dst[n] = *ptr;
+ ptr += _info.channels;
+ }
+
+ _read_data_count = cnt * sizeof(float);
+
+ return nread;
+}
+
+nframes_t
+SndFileSource::write_unlocked (Sample *data, nframes_t cnt)
+{
+ if (destructive()) {
+ return destructive_write_unlocked (data, cnt);
+ } else {
+ return nondestructive_write_unlocked (data, cnt);
+ }
+}
+
+nframes_t
+SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt)
+{
+ if (!writable()) {
+ warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
+ return 0;
+ }
+
+ if (_info.channels != 1) {
+ fatal << string_compose (_("programming error: %1 %2"), X_("SndFileSource::write called on non-mono file"), _path) << endmsg;
+ /*NOTREACHED*/
+ return 0;
+ }
+
+ nframes_t oldlen;
+ int32_t frame_pos = _length;
+
+ if (write_float (data, frame_pos, cnt) != cnt) {
+ return 0;
+ }
+
+ oldlen = _length;
+ update_length (oldlen, cnt);
+
+ if (_build_peakfiles) {
+ compute_and_write_peaks (data, frame_pos, cnt, false, true);
+ }
+
+ _write_data_count = cnt;
+
+ return cnt;
+}
+
+nframes_t
+SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt)
+{
+ nframes_t old_file_pos;
+
+ if (!writable()) {
+ warning << string_compose (_("attempt to write a non-writable audio file source (%1)"), _path) << endmsg;
+ return 0;
+ }
+
+ if (_capture_start && _capture_end) {
+
+ /* start and end of capture both occur within the data we are writing,
+ so do both crossfades.
+ */
+
+ _capture_start = false;
+ _capture_end = false;
+
+ /* move to the correct location place */
+ file_pos = capture_start_frame - timeline_position;
+
+ // split cnt in half
+ nframes_t subcnt = cnt / 2;
+ nframes_t ofilepos = file_pos;
+
+ // fade in
+ if (crossfade (data, subcnt, 1) != subcnt) {
+ return 0;
+ }
+
+ file_pos += subcnt;
+ Sample * tmpdata = data + subcnt;
+
+ // fade out
+ subcnt = cnt - subcnt;
+ if (crossfade (tmpdata, subcnt, 0) != subcnt) {
+ return 0;
+ }
+
+ file_pos = ofilepos; // adjusted below
+
+ } else if (_capture_start) {
+
+ /* start of capture both occur within the data we are writing,
+ so do the fade in
+ */
+
+ _capture_start = false;
+ _capture_end = false;
+
+ /* move to the correct location place */
+ file_pos = capture_start_frame - timeline_position;
+
+ if (crossfade (data, cnt, 1) != cnt) {
+ return 0;
+ }
+
+ } else if (_capture_end) {
+
+ /* end of capture both occur within the data we are writing,
+ so do the fade out
+ */
+
+ _capture_start = false;
+ _capture_end = false;
+
+ if (crossfade (data, cnt, 0) != cnt) {
+ return 0;
+ }
+
+ } else {
+
+ /* in the middle of recording */
+
+ if (write_float (data, file_pos, cnt) != cnt) {
+ return 0;
+ }
+ }
+
+ old_file_pos = file_pos;
+ update_length (file_pos, cnt);
+
+ if (_build_peakfiles) {
+ compute_and_write_peaks (data, file_pos, cnt, false, true);
+ }
+
+ file_pos += cnt;
+
+ return cnt;
+}
+
+int
+SndFileSource::update_header (nframes_t when, struct tm& now, time_t tnow)
+{
+ set_timeline_position (when);
+
+ if (_flags & Broadcast) {
+ if (setup_broadcast_info (when, now, tnow)) {
+ return -1;
+ }
+ }
+
+ return flush_header ();
+}
+
+int
+SndFileSource::flush_header ()
+{
+ if (!writable() || (sf == 0)) {
+ warning << string_compose (_("attempt to flush a non-writable audio file source (%1)"), _path) << endmsg;
+ return -1;
+ }
+ return (sf_command (sf, SFC_UPDATE_HEADER_NOW, 0, 0) != SF_TRUE);
+}
+
+int
+SndFileSource::setup_broadcast_info (nframes_t when, struct tm& now, time_t tnow)
+{
+ if (!writable()) {
+ warning << string_compose (_("attempt to store broadcast info in a non-writable audio file source (%1)"), _path) << endmsg;
+ return -1;
+ }
+
+ if (!(_flags & Broadcast)) {
+ return 0;
+ }
+
+ /* random code is 9 digits */
+
+ int random_code = random() % 999999999;
+
+ snprintf (_broadcast_info->originator_reference, sizeof (_broadcast_info->originator_reference), "%2s%3s%12s%02d%02d%02d%9d",
+ Config->get_bwf_country_code().c_str(),
+ Config->get_bwf_organization_code().c_str(),
+ bwf_serial_number,
+ now.tm_hour,
+ now.tm_min,
+ now.tm_sec,
+ random_code);
+
+ snprintf (_broadcast_info->origination_date, sizeof (_broadcast_info->origination_date), "%4d-%02d-%02d",
+ 1900 + now.tm_year,
+ now.tm_mon,
+ now.tm_mday);
+
+ snprintf (_broadcast_info->origination_time, sizeof (_broadcast_info->origination_time), "%02d:%02d:%02d",
+ now.tm_hour,
+ now.tm_min,
+ now.tm_sec);
+
+ /* now update header position taking header offset into account */
+
+ set_header_timeline_position ();
+
+ if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
+ error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
+ _flags = Flag (_flags & ~Broadcast);
+ delete _broadcast_info;
+ _broadcast_info = 0;
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+SndFileSource::set_header_timeline_position ()
+{
+ if (!(_flags & Broadcast)) {
+ return;
+ }
+
+ _broadcast_info->time_reference_high = (timeline_position >> 32);
+ _broadcast_info->time_reference_low = (timeline_position & 0xffffffff);
+
+ if (sf_command (sf, SFC_SET_BROADCAST_INFO, _broadcast_info, sizeof (*_broadcast_info)) != SF_TRUE) {
+ error << string_compose (_("cannot set broadcast info for audio file %1; Dropping broadcast info for this file"), _path) << endmsg;
+ _flags = Flag (_flags & ~Broadcast);
+ delete _broadcast_info;
+ _broadcast_info = 0;
+ }
+}
+
+nframes_t
+SndFileSource::write_float (Sample* data, nframes_t frame_pos, nframes_t cnt)
+{
+ if (sf_seek (sf, frame_pos, SEEK_SET|SFM_WRITE) < 0) {
+ char errbuf[256];
+ sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ error << string_compose (_("%1: cannot seek to %2 (libsndfile error: %3"), _path, frame_pos, errbuf) << endmsg;
+ return 0;
+ }
+
+ if (sf_writef_float (sf, data, cnt) != (ssize_t) cnt) {
+ return 0;
+ }
+
+ return cnt;
+}
+
+nframes_t
+SndFileSource::natural_position() const
+{
+ return timeline_position;
+}
+
+bool
+SndFileSource::set_destructive (bool yn)
+{
+ if (yn) {
+ _flags = Flag (_flags | Destructive);
+ if (!xfade_buf) {
+ xfade_buf = new Sample[xfade_frames];
+ }
+ clear_capture_marks ();
+ timeline_position = header_position_offset;
+ } else {
+ _flags = Flag (_flags & ~Destructive);
+ timeline_position = 0;
+ /* leave xfade buf alone in case we need it again later */
+ }
+
+ return true;
+}
+
+void
+SndFileSource::clear_capture_marks ()
+{
+ _capture_start = false;
+ _capture_end = false;
+}
+
+void
+SndFileSource::mark_capture_start (nframes_t pos)
+{
+ if (destructive()) {
+ if (pos < timeline_position) {
+ _capture_start = false;
+ } else {
+ _capture_start = true;
+ capture_start_frame = pos;
+ }
+ }
+}
+
+void
+SndFileSource::mark_capture_end()
+{
+ if (destructive()) {
+ _capture_end = true;
+ }
+}
+
+nframes_t
+SndFileSource::crossfade (Sample* data, nframes_t cnt, int fade_in)
+{
+ nframes_t xfade = min (xfade_frames, cnt);
+ nframes_t nofade = cnt - xfade;
+ Sample* fade_data = 0;
+ nframes_t fade_position = 0; // in frames
+ ssize_t retval;
+ nframes_t file_cnt;
+
+ if (fade_in) {
+ fade_position = file_pos;
+ fade_data = data;
+ } else {
+ fade_position = file_pos + nofade;
+ fade_data = data + nofade;
+ }
+
+ if (fade_position > _length) {
+
+ /* read starts beyond end of data, just memset to zero */
+
+ file_cnt = 0;
+
+ } else if (fade_position + xfade > _length) {
+
+ /* read ends beyond end of data, read some, memset the rest */
+
+ file_cnt = _length - fade_position;
+
+ } else {
+
+ /* read is entirely within data */
+
+ file_cnt = xfade;
+ }
+
+ if (file_cnt) {
+
+ if ((retval = read_unlocked (xfade_buf, fade_position, file_cnt)) != (ssize_t) file_cnt) {
+ if (retval >= 0 && errno == EAGAIN) {
+ /* XXX - can we really trust that errno is meaningful here? yes POSIX, i'm talking to you.
+ * short or no data there */
+ memset (xfade_buf, 0, xfade * sizeof(Sample));
+ } else {
+ error << string_compose(_("SndFileSource: \"%1\" bad read retval: %2 of %5 (%3: %4)"), _path, retval, errno, strerror (errno), xfade) << endmsg;
+ return 0;
+ }
+ }
+ }
+
+ if (file_cnt != xfade) {
+ nframes_t delta = xfade - file_cnt;
+ memset (xfade_buf+file_cnt, 0, sizeof (Sample) * delta);
+ }
+
+ if (nofade && !fade_in) {
+ if (write_float (data, file_pos, nofade) != nofade) {
+ error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+ return 0;
+ }
+ }
+
+ if (xfade == xfade_frames) {
+
+ nframes_t n;
+
+ /* use the standard xfade curve */
+
+ if (fade_in) {
+
+ /* fade new material in */
+
+ for (n = 0; n < xfade; ++n) {
+ xfade_buf[n] = (xfade_buf[n] * out_coefficient[n]) + (fade_data[n] * in_coefficient[n]);
+ }
+
+ } else {
+
+
+ /* fade new material out */
+
+ for (n = 0; n < xfade; ++n) {
+ xfade_buf[n] = (xfade_buf[n] * in_coefficient[n]) + (fade_data[n] * out_coefficient[n]);
+ }
+ }
+
+ } else if (xfade < xfade_frames) {
+
+ gain_t in[xfade];
+ gain_t out[xfade];
+
+ /* short xfade, compute custom curve */
+
+ compute_equal_power_fades (xfade, in, out);
+
+ for (nframes_t n = 0; n < xfade; ++n) {
+ xfade_buf[n] = (xfade_buf[n] * out[n]) + (fade_data[n] * in[n]);
+ }
+
+ } else if (xfade) {
+
+ /* long xfade length, has to be computed across several calls */
+
+ }
+
+ if (xfade) {
+ if (write_float (xfade_buf, fade_position, xfade) != xfade) {
+ error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+ return 0;
+ }
+ }
+
+ if (fade_in && nofade) {
+ if (write_float (data + xfade, file_pos + xfade, nofade) != nofade) {
+ error << string_compose(_("SndFileSource: \"%1\" bad write (%2)"), _path, strerror (errno)) << endmsg;
+ return 0;
+ }
+ }
+
+ return cnt;
+}
+
+nframes_t
+SndFileSource::last_capture_start_frame () const
+{
+ if (destructive()) {
+ return capture_start_frame;
+ } else {
+ return 0;
+ }
+}
+
+void
+SndFileSource::handle_header_position_change ()
+{
+ if (destructive()) {
+ if ( _length != 0 ) {
+ error << string_compose(_("Filesource: start time is already set for existing file (%1): Cannot change start time."), _path ) << endmsg;
+ //in the future, pop up a dialog here that allows user to regenerate file with new start offset
+ } else if (writable()) {
+ timeline_position = header_position_offset;
+ set_header_timeline_position (); //this will get flushed if/when the file is recorded to
+ }
+ }
+}
+
+void
+SndFileSource::setup_standard_crossfades (nframes_t rate)
+{
+ /* This static method is assumed to have been called by the Session
+ before any DFS's are created.
+ */
+
+ xfade_frames = (nframes_t) floor ((Config->get_destructive_xfade_msecs () / 1000.0) * rate);
+
+ if (out_coefficient) {
+ delete [] out_coefficient;
+ }
+
+ if (in_coefficient) {
+ delete [] in_coefficient;
+ }
+
+ out_coefficient = new gain_t[xfade_frames];
+ in_coefficient = new gain_t[xfade_frames];
+
+ compute_equal_power_fades (xfade_frames, in_coefficient, out_coefficient);
+}
+
+void
+SndFileSource::set_timeline_position (int64_t pos)
+{
+ // destructive track timeline postion does not change
+ // except at instantion or when header_position_offset
+ // (session start) changes
+
+ if (!destructive()) {
+ AudioFileSource::set_timeline_position (pos);
+ }
+}
+
+int
+SndFileSource::get_soundfile_info (const ustring& path, SoundFileInfo& info, string& error_msg)
+{
+ SNDFILE *sf;
+ SF_INFO sf_info;
+ SF_BROADCAST_INFO binfo;
+ bool timecode_exists;
+
+ sf_info.format = 0; // libsndfile says to clear this before sf_open().
+
+ if ((sf = sf_open ((char*) path.c_str(), SFM_READ, &sf_info)) == 0) {
+ char errbuf[256];
+ error_msg = sf_error_str (0, errbuf, sizeof (errbuf) - 1);
+ return false;
+ }
+
+ info.samplerate = sf_info.samplerate;
+ info.channels = sf_info.channels;
+ info.length = sf_info.frames;
+ info.format_name = string_compose("Format: %1, %2",
+ sndfile_major_format(sf_info.format),
+ sndfile_minor_format(sf_info.format));
+
+ memset (&binfo, 0, sizeof (binfo));
+ info.timecode = get_timecode_info (sf, &binfo, timecode_exists);
+
+ if (!timecode_exists) {
+ info.timecode = 0;
+ }
+
+ sf_close (sf);
+
+ return true;
+}
+
+int64_t
+SndFileSource::get_timecode_info (SNDFILE* sf, SF_BROADCAST_INFO* binfo, bool& exists)
+{
+ if (sf_command (sf, SFC_GET_BROADCAST_INFO, binfo, sizeof (*binfo)) != SF_TRUE) {
+ exists = false;
+ return (header_position_offset);
+ }
+
+ /* XXX 64 bit alert: when JACK switches to a 64 bit frame count, this needs to use the high bits
+ of the time reference.
+ */
+
+ exists = true;
+ int64_t ret = (uint32_t) binfo->time_reference_high;
+ ret <<= 32;
+ ret |= (uint32_t) binfo->time_reference_low;
+ return ret;
+}
+
+bool
+SndFileSource::one_of_several_channels () const
+{
+ return _info.channels > 1;
+}
+
diff --git a/libs/ardour/source.cc b/libs/ardour/source.cc
new file mode 100644
index 0000000000..f08d08b86e
--- /dev/null
+++ b/libs/ardour/source.cc
@@ -0,0 +1,269 @@
+/*
+ Copyright (C) 2000 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <float.h>
+#include <cerrno>
+#include <ctime>
+#include <cmath>
+#include <iomanip>
+#include <algorithm>
+#include <fstream>
+
+#include <glibmm/thread.h>
+#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
+#include <pbd/xml++.h>
+#include <pbd/pthread_utils.h>
+
+#include <ardour/source.h>
+#include <ardour/playlist.h>
+#include <ardour/session.h>
+#include <ardour/transient_detector.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+
+Source::Source (Session& s, const string& name, DataType type)
+ : SessionObject(s, name)
+ , _type(type)
+{
+ // not true.. is this supposed to be an assertion?
+ //assert(_name.find("/") == string::npos);
+
+ _analysed = false;
+ _timestamp = 0;
+ _length = 0;
+ _in_use = 0;
+}
+
+Source::Source (Session& s, const XMLNode& node)
+ : SessionObject(s, "unnamed source")
+ , _type(DataType::AUDIO)
+{
+ _timestamp = 0;
+ _length = 0;
+ _analysed = false;
+ _in_use = 0;
+
+ if (set_state (node) || _type == DataType::NIL) {
+ throw failed_constructor();
+ }
+}
+
+Source::~Source ()
+{
+ notify_callbacks ();
+}
+
+XMLNode&
+Source::get_state ()
+{
+ XMLNode *node = new XMLNode ("Source");
+ char buf[64];
+
+ node->add_property ("name", _name);
+ node->add_property ("type", _type.to_string());
+ _id.print (buf, sizeof (buf));
+ node->add_property ("id", buf);
+
+ if (_timestamp != 0) {
+ snprintf (buf, sizeof (buf), "%ld", _timestamp);
+ node->add_property ("timestamp", buf);
+ }
+
+ return *node;
+}
+
+int
+Source::set_state (const XMLNode& node)
+{
+ const XMLProperty* prop;
+
+ if ((prop = node.property ("name")) != 0) {
+ _name = prop->value();
+ } else {
+ return -1;
+ }
+
+ if ((prop = node.property ("id")) != 0) {
+ _id = prop->value ();
+ } else {
+ return -1;
+ }
+
+ if ((prop = node.property ("type")) != 0) {
+ _type = DataType(prop->value());
+ }
+
+ if ((prop = node.property ("timestamp")) != 0) {
+ sscanf (prop->value().c_str(), "%ld", &_timestamp);
+ }
+
+ // Don't think this is valid, absolute paths fail
+ //assert(_name.find("/") == string::npos);
+
+ return 0;
+}
+
+void
+Source::update_length (nframes_t pos, nframes_t cnt)
+{
+ if (pos + cnt > _length) {
+ _length = pos+cnt;
+ }
+}
+
+void
+Source::add_playlist (boost::shared_ptr<Playlist> pl)
+{
+ std::pair<PlaylistMap::iterator,bool> res;
+ std::pair<boost::shared_ptr<Playlist>, uint32_t> newpair (pl, 1);
+ Glib::Mutex::Lock lm (_playlist_lock);
+
+ res = _playlists.insert (newpair);
+
+ if (!res.second) {
+ /* it already existed, bump count */
+ res.first->second++;
+ }
+
+ pl->GoingAway.connect (bind (mem_fun (*this, &Source::remove_playlist), boost::weak_ptr<Playlist> (pl)));
+}
+
+void
+Source::remove_playlist (boost::weak_ptr<Playlist> wpl)
+{
+ boost::shared_ptr<Playlist> pl (wpl.lock());
+
+ if (!pl) {
+ return;
+ }
+
+ PlaylistMap::iterator x;
+ Glib::Mutex::Lock lm (_playlist_lock);
+
+ if ((x = _playlists.find (pl)) != _playlists.end()) {
+ if (x->second > 1) {
+ x->second--;
+ } else {
+ _playlists.erase (x);
+ }
+ }
+}
+
+uint32_t
+Source::used () const
+{
+ return _playlists.size();
+}
+
+bool
+Source::has_been_analysed() const
+{
+ Glib::Mutex::Lock lm (_analysis_lock);
+ return _analysed;
+}
+
+void
+Source::set_been_analysed (bool yn)
+{
+ {
+ Glib::Mutex::Lock lm (_analysis_lock);
+ _analysed = yn;
+ }
+
+ if (yn) {
+ load_transients (get_transients_path());
+ AnalysisChanged(); // EMIT SIGNAL
+ }
+}
+
+int
+Source::load_transients (const string& path)
+{
+ ifstream file (path.c_str());
+
+ if (!file) {
+ return -1;
+ }
+
+ transients.clear ();
+
+ stringstream strstr;
+ double val;
+
+ while (file.good()) {
+ file >> val;
+
+ if (!file.fail()) {
+ nframes64_t frame = (nframes64_t) floor (val * _session.frame_rate());
+ transients.push_back (frame);
+ }
+ }
+
+ return 0;
+}
+
+string
+Source::get_transients_path () const
+{
+ vector<string> parts;
+ string s;
+
+ /* old sessions may not have the analysis directory */
+
+ _session.ensure_subdirs ();
+
+ s = _session.analysis_dir ();
+ parts.push_back (s);
+
+ s = _id.to_s();
+ s += '.';
+ s += TransientDetector::operational_identifier();
+ parts.push_back (s);
+
+ return Glib::build_filename (parts);
+}
+
+bool
+Source::check_for_analysis_data_on_disk ()
+{
+ /* looks to see if the analysis files for this source are on disk.
+ if so, mark us already analysed.
+ */
+
+ string path = get_transients_path ();
+ bool ok = true;
+
+ if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) {
+ ok = false;
+ }
+
+ // XXX add other tests here as appropriate
+
+ set_been_analysed (ok);
+ return ok;
+}
+
diff --git a/libs/ardour/source_factory.cc b/libs/ardour/source_factory.cc
new file mode 100644
index 0000000000..5338997659
--- /dev/null
+++ b/libs/ardour/source_factory.cc
@@ -0,0 +1,278 @@
+/*
+ Copyright (C) 2000-2006 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.
+
+ $Id$
+*/
+
+#include <pbd/error.h>
+#include <pbd/convert.h>
+#include <pbd/pthread_utils.h>
+#include <pbd/stacktrace.h>
+
+#include <ardour/source_factory.h>
+#include <ardour/sndfilesource.h>
+#include <ardour/silentfilesource.h>
+#include <ardour/configuration.h>
+#include <ardour/smf_source.h>
+
+#ifdef HAVE_COREAUDIO
+#define USE_COREAUDIO_FOR_FILES
+#endif
+
+#ifdef USE_COREAUDIO_FOR_FILES
+#include <ardour/coreaudiosource.h>
+#endif
+
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+
+sigc::signal<void,boost::shared_ptr<Source> > SourceFactory::SourceCreated;
+Glib::Cond* SourceFactory::PeaksToBuild;
+Glib::StaticMutex SourceFactory::peak_building_lock = GLIBMM_STATIC_MUTEX_INIT;
+std::list<boost::weak_ptr<AudioSource> > SourceFactory::files_with_peaks;
+
+static void
+peak_thread_work ()
+{
+ PBD::ThreadCreated (pthread_self(), string ("peakbuilder-") + to_string (pthread_self(), std::dec));
+
+ while (true) {
+
+ SourceFactory::peak_building_lock.lock ();
+
+ wait:
+ if (SourceFactory::files_with_peaks.empty()) {
+ SourceFactory::PeaksToBuild->wait (SourceFactory::peak_building_lock);
+ }
+
+ if (SourceFactory::files_with_peaks.empty()) {
+ goto wait;
+ }
+
+ boost::shared_ptr<AudioSource> as (SourceFactory::files_with_peaks.front().lock());
+ SourceFactory::files_with_peaks.pop_front ();
+ SourceFactory::peak_building_lock.unlock ();
+
+ if (!as) {
+ continue;
+ }
+
+ as->setup_peakfile ();
+ }
+}
+
+void
+SourceFactory::init ()
+{
+ PeaksToBuild = new Glib::Cond();
+
+ for (int n = 0; n < 2; ++n) {
+ Glib::Thread::create (sigc::ptr_fun (::peak_thread_work), false);
+ }
+}
+
+int
+SourceFactory::setup_peakfile (boost::shared_ptr<Source> s, bool async)
+{
+ boost::shared_ptr<AudioSource> as (boost::dynamic_pointer_cast<AudioSource> (s));
+
+ if (as) {
+
+ if (async) {
+
+ Glib::Mutex::Lock lm (peak_building_lock);
+ files_with_peaks.push_back (boost::weak_ptr<AudioSource> (as));
+ PeaksToBuild->broadcast ();
+
+ } else {
+
+ if (as->setup_peakfile ()) {
+ error << string_compose("SourceFactory: could not set up peakfile for %1", as->name()) << endmsg;
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+boost::shared_ptr<Source>
+SourceFactory::createSilent (Session& s, const XMLNode& node, nframes_t nframes, float sr)
+{
+ boost::shared_ptr<Source> ret (new SilentFileSource (s, node, nframes, sr));
+ // no analysis data - the file is non-existent
+ SourceCreated (ret);
+ return ret;
+}
+
+boost::shared_ptr<Source>
+SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks)
+{
+ DataType type = DataType::AUDIO;
+ const XMLProperty* prop = node.property("type");
+
+ if (prop) {
+ type = DataType(prop->value());
+ }
+
+ if (type == DataType::AUDIO) {
+
+ try {
+
+ boost::shared_ptr<Source> ret (new SndFileSource (s, node));
+ if (setup_peakfile (ret, defer_peaks)) {
+ return boost::shared_ptr<Source>();
+ }
+ ret->check_for_analysis_data_on_disk ();
+ SourceCreated (ret);
+ return ret;
+ }
+
+ catch (failed_constructor& err) {
+
+#ifdef USE_COREAUDIO_FOR_FILES
+
+ /* this is allowed to throw */
+
+ boost::shared_ptr<Source> ret (new CoreAudioSource (s, node));
+
+ if (setup_peakfile (ret, defer_peaks)) {
+ return boost::shared_ptr<Source>();
+ }
+
+ ret->check_for_analysis_data_on_disk ();
+ SourceCreated (ret);
+ return ret;
+#else
+ throw; // rethrow
+#endif
+ }
+
+ } else if (type == DataType::MIDI) {
+ boost::shared_ptr<Source> ret (new SMFSource (s, node));
+ ret->check_for_analysis_data_on_disk ();
+ SourceCreated (ret);
+ return ret;
+ }
+
+ return boost::shared_ptr<Source>();
+}
+
+boost::shared_ptr<Source>
+SourceFactory::createReadable (DataType type, Session& s, string path, int chn, AudioFileSource::Flag flags, bool announce, bool defer_peaks)
+{
+ if (type == DataType::AUDIO) {
+
+ if (!(flags & Destructive)) {
+
+ try {
+
+ boost::shared_ptr<Source> ret (new SndFileSource (s, path, chn, flags));
+
+ if (setup_peakfile (ret, defer_peaks)) {
+ return boost::shared_ptr<Source>();
+ }
+
+ ret->check_for_analysis_data_on_disk ();
+ if (announce) {
+ SourceCreated (ret);
+ }
+ return ret;
+ }
+
+ catch (failed_constructor& err) {
+#ifdef USE_COREAUDIO_FOR_FILES
+
+ boost::shared_ptr<Source> ret (new CoreAudioSource (s, path, chn, flags));
+ if (setup_peakfile (ret, defer_peaks)) {
+ return boost::shared_ptr<Source>();
+ }
+ ret->check_for_analysis_data_on_disk ();
+ if (announce) {
+ SourceCreated (ret);
+ }
+ return ret;
+
+#else
+ throw; // rethrow
+#endif
+ }
+
+ } else {
+ // eh?
+ }
+
+ } else if (type == DataType::MIDI) {
+
+ // FIXME: flags?
+ boost::shared_ptr<Source> ret (new SMFSource (s, path, SMFSource::Flag(0)));
+
+ if (announce) {
+ SourceCreated (ret);
+ }
+
+ return ret;
+
+ }
+
+ return boost::shared_ptr<Source>();
+}
+
+boost::shared_ptr<Source>
+SourceFactory::createWritable (DataType type, Session& s, std::string path, bool destructive, nframes_t rate, bool announce, bool defer_peaks)
+{
+ /* this might throw failed_constructor(), which is OK */
+
+ if (type == DataType::AUDIO) {
+ boost::shared_ptr<Source> ret (new SndFileSource
+ (s, path,
+ Config->get_native_file_data_format(),
+ Config->get_native_file_header_format(),
+ rate,
+ (destructive ? AudioFileSource::Flag (SndFileSource::default_writable_flags | AudioFileSource::Destructive) :
+ SndFileSource::default_writable_flags)));
+
+ if (setup_peakfile (ret, defer_peaks)) {
+ return boost::shared_ptr<Source>();
+ }
+
+ // no analysis data - this is a new file
+
+ if (announce) {
+ SourceCreated (ret);
+ }
+ return ret;
+
+ } else if (type == DataType::MIDI) {
+
+ boost::shared_ptr<Source> ret (new SMFSource (s, path));
+
+ // no analysis data - this is a new file
+
+ if (announce) {
+ SourceCreated (ret);
+ }
+ return ret;
+
+ }
+
+ return boost::shared_ptr<Source> ();
+}
diff --git a/libs/ardour/sse_functions.s b/libs/ardour/sse_functions.s
new file mode 100644
index 0000000000..934ce6887a
--- /dev/null
+++ b/libs/ardour/sse_functions.s
@@ -0,0 +1,531 @@
+/*
+ Copyright (C) 2005 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.
+
+ Author: Sampo Savolainen
+
+ $Id$
+*/
+
+
+#; void x86_sse_mix_buffers_with_gain (float *dst, float *src, long nframes, float gain);
+
+.globl x86_sse_mix_buffers_with_gain
+ .type x86_sse_mix_buffers_with_gain,@function
+
+x86_sse_mix_buffers_with_gain:
+#; 8(%ebp) = float *dst = %edi
+#; 12(%ebp) = float *src = %esi
+#; 16(%ebp) = long nframes = %ecx
+#; 20(%ebp) = float gain = st(0)
+
+ pushl %ebp
+ movl %esp, %ebp
+
+ #; save the registers
+#; pushl %eax
+ pushl %ebx
+#; pushl %ecx
+ pushl %edi
+ pushl %esi
+
+ #; if nframes == 0, go to end
+ movl 16(%ebp), %ecx #; nframes
+ cmp $0, %ecx
+ je .MBWG_END
+
+ #; Check for alignment
+
+ movl 8(%ebp), %edi #; dst
+ movl 12(%ebp), %esi #; src
+
+ movl %edi, %eax
+ andl $12, %eax #; mask alignemnt offset
+
+ movl %esi, %ebx
+ andl $12, %ebx #; mask alignment offset
+
+ cmp %eax, %ebx
+ jne .MBWG_NONALIGN #; if not aligned, calculate manually
+
+ #; if we are aligned
+ cmp $0, %ebx
+ jz .MBWG_SSE
+
+ #; Pre-loop, we need to run 1-3 frames "manually" without
+ #; SSE instructions
+
+ movss 20(%ebp), %xmm1 #; xmm1
+
+.MBWG_PRELOOP:
+
+ movss (%esi), %xmm0
+ mulss %xmm1, %xmm0
+ addss (%edi), %xmm0
+ movss %xmm0, (%edi)
+
+ addl $4, %edi #; dst++
+ addl $4, %esi #; src++
+ decl %ecx #; nframes--
+ jz .MBWG_END
+
+#; cmp $0, %ecx
+#; je .MBWG_END #; if we run out of frames, go to end
+
+ addl $4, %ebx
+
+ cmp $16, %ebx #; test if we've reached 16 byte alignment
+ jne .MBWG_PRELOOP
+
+
+.MBWG_SSE:
+
+ cmp $4, %ecx #; we know it's not zero, but if it's not >=4, then
+ jnge .MBWG_NONALIGN #; we jump straight to the "normal" code
+
+ #; copy gain to fill %xmm1
+ movss 20(%ebp), %xmm1
+ shufps $0x00, %xmm1, %xmm1
+
+
+.MBWG_SSELOOP:
+
+ movaps (%esi), %xmm0 #; source => xmm0
+ mulps %xmm1, %xmm0 #; apply gain to source
+ addps (%edi), %xmm0 #; mix with destination
+ movaps %xmm0, (%edi) #; copy result to destination
+
+ addl $16, %edi #; dst+=4
+ addl $16, %esi #; src+=4
+
+ subl $4, %ecx #; nframes-=4
+ cmp $4, %ecx
+ jge .MBWG_SSELOOP
+
+ cmp $0, %ecx
+ je .MBWG_END
+
+ #; if there are remaining frames, the nonalign code will do nicely
+ #; for the rest 1-3 frames.
+
+.MBWG_NONALIGN:
+ #; not aligned!
+
+ movss 20(%ebp), %xmm1 #; gain => xmm1
+
+.MBWG_NONALIGNLOOP:
+
+ movss (%esi), %xmm0
+ mulss %xmm1, %xmm0
+ addss (%edi), %xmm0
+ movss %xmm0, (%edi)
+
+ addl $4, %edi
+ addl $4, %esi
+
+ decl %ecx
+ jnz .MBWG_NONALIGNLOOP
+
+.MBWG_END:
+
+ popl %esi
+ popl %edi
+#; popl %ecx
+ popl %ebx
+#; popl %eax
+
+ #; return
+ leave
+ ret
+
+.size x86_sse_mix_buffers_with_gain, .-x86_sse_mix_buffers_with_gain
+
+
+
+
+#; void x86_sse_mix_buffers_no_gain (float *dst, float *src, long nframes);
+
+.globl x86_sse_mix_buffers_no_gain
+ .type x86_sse_mix_buffers_no_gain,@function
+
+x86_sse_mix_buffers_no_gain:
+#; 8(%ebp) = float *dst = %edi
+#; 12(%ebp) = float *src = %esi
+#; 16(%ebp) = long nframes = %ecx
+
+ pushl %ebp
+ movl %esp, %ebp
+
+ #; save the registers
+#; pushl %eax
+ pushl %ebx
+#; pushl %ecx
+ pushl %edi
+ pushl %esi
+
+ #; the real function
+
+ #; if nframes == 0, go to end
+ movl 16(%ebp), %ecx #; nframes
+ cmp $0, %ecx
+ je .MBNG_END
+
+ #; Check for alignment
+
+ movl 8(%ebp), %edi #; dst
+ movl 12(%ebp), %esi #; src
+
+ movl %edi, %eax
+ andl $12, %eax #; mask alignemnt offset
+
+ movl %esi, %ebx
+ andl $12, %ebx #; mask alignment offset
+
+ cmp %eax, %ebx
+ jne .MBNG_NONALIGN #; if not aligned, calculate manually
+
+ cmp $0, %ebx
+ je .MBNG_SSE
+
+ #; Pre-loop, we need to run 1-3 frames "manually" without
+ #; SSE instructions
+
+.MBNG_PRELOOP:
+
+ movss (%esi), %xmm0
+ addss (%edi), %xmm0
+ movss %xmm0, (%edi)
+
+ addl $4, %edi #; dst++
+ addl $4, %esi #; src++
+ decl %ecx #; nframes--
+ jz .MBNG_END
+ addl $4, %ebx
+
+ cmp $16, %ebx #; test if we've reached 16 byte alignment
+ jne .MBNG_PRELOOP
+
+.MBNG_SSE:
+
+ cmp $4, %ecx #; if there are frames left, but less than 4
+ jnge .MBNG_NONALIGN #; we can't run SSE
+
+.MBNG_SSELOOP:
+
+ movaps (%esi), %xmm0 #; source => xmm0
+ addps (%edi), %xmm0 #; mix with destination
+ movaps %xmm0, (%edi) #; copy result to destination
+
+ addl $16, %edi #; dst+=4
+ addl $16, %esi #; src+=4
+
+ subl $4, %ecx #; nframes-=4
+ cmp $4, %ecx
+ jge .MBNG_SSELOOP
+
+ cmp $0, %ecx
+ je .MBNG_END
+
+ #; if there are remaining frames, the nonalign code will do nicely
+ #; for the rest 1-3 frames.
+
+.MBNG_NONALIGN:
+ #; not aligned!
+
+ movss (%esi), %xmm0 #; src => xmm0
+ addss (%edi), %xmm0 #; xmm0 += dst
+ movss %xmm0, (%edi) #; xmm0 => dst
+
+ addl $4, %edi
+ addl $4, %esi
+
+ decl %ecx
+ jnz .MBNG_NONALIGN
+
+.MBNG_END:
+
+ popl %esi
+ popl %edi
+#; popl %ecx
+ popl %ebx
+#; popl %eax
+
+ #; return
+ leave
+ ret
+
+.size x86_sse_mix_buffers_no_gain, .-x86_sse_mix_buffers_no_gain
+
+
+
+
+#; void x86_sse_apply_gain_to_buffer (float *buf, long nframes, float gain);
+
+.globl x86_sse_apply_gain_to_buffer
+ .type x86_sse_apply_gain_to_buffer,@function
+
+x86_sse_apply_gain_to_buffer:
+#; 8(%ebp) = float *buf = %edi
+#; 12(%ebp) = long nframes = %ecx
+#; 16(%ebp) = float gain = st(0)
+
+ pushl %ebp
+ movl %esp, %ebp
+
+ #; save %edi
+ pushl %edi
+
+ #; the real function
+
+ #; if nframes == 0, go to end
+ movl 12(%ebp), %ecx #; nframes
+ cmp $0, %ecx
+ je .AG_END
+
+ #; create the gain buffer in %xmm1
+ movss 16(%ebp), %xmm1
+ shufps $0x00, %xmm1, %xmm1
+
+ #; Check for alignment
+
+ movl 8(%ebp), %edi #; buf
+ movl %edi, %edx #; buf => %edx
+ andl $12, %edx #; mask bits 1 & 2, result = 0, 4, 8 or 12
+ jz .AG_SSE #; if buffer IS aligned
+
+ #; PRE-LOOP
+ #; we iterate 1-3 times, doing normal x87 float comparison
+ #; so we reach a 16 byte aligned "buf" (=%edi) value
+
+.AGLP_START:
+
+ #; Load next value from the buffer
+ movss (%edi), %xmm0
+ mulss %xmm1, %xmm0
+ movss %xmm0, (%edi)
+
+ #; increment buffer, decrement counter
+ addl $4, %edi #; buf++;
+
+ decl %ecx #; nframes--
+ jz .AG_END #; if we run out of frames, we go to the end
+
+ addl $4, %edx #; one non-aligned byte less
+ cmp $16, %edx
+ jne .AGLP_START #; if more non-aligned frames exist, we do a do-over
+
+.AG_SSE:
+
+ #; We have reached the 16 byte aligned "buf" ("edi") value
+
+ #; Figure out how many loops we should do
+ movl %ecx, %eax #; copy remaining nframes to %eax for division
+ movl $0, %edx #; 0 the edx register
+
+
+ pushl %edi
+ movl $4, %edi
+ divl %edi #; %edx = remainder == 0
+ popl %edi
+
+ #; %eax = SSE iterations
+ cmp $0, %eax
+ je .AGPOST_START
+
+
+.AGLP_SSE:
+
+ movaps (%edi), %xmm0
+ mulps %xmm1, %xmm0
+ movaps %xmm0, (%edi)
+
+ addl $16, %edi
+#; subl $4, %ecx #; nframes-=4
+
+ decl %eax
+ jnz .AGLP_SSE
+
+ #; Next we need to post-process all remaining frames
+ #; the remaining frame count is in %ecx
+
+ #; if no remaining frames, jump to the end
+#; cmp $0, %ecx
+ andl $3, %ecx #; nframes % 4
+ je .AG_END
+
+.AGPOST_START:
+
+ movss (%edi), %xmm0
+ mulss %xmm1, %xmm0
+ movss %xmm0, (%edi)
+
+ #; increment buffer, decrement counter
+ addl $4, %edi #; buf++;
+
+ decl %ecx #; nframes--
+ jnz .AGPOST_START #; if we run out of frames, we go to the end
+
+.AG_END:
+
+
+ popl %edi
+
+ #; return
+ leave
+ ret
+
+.size x86_sse_apply_gain_to_buffer, .-x86_sse_apply_gain_to_buffer
+#; end proc
+
+
+
+#; float x86_sse_compute_peak(float *buf, long nframes, float current);
+
+.globl x86_sse_compute_peak
+ .type x86_sse_compute_peak,@function
+
+x86_sse_compute_peak:
+#; 8(%ebp) = float *buf = %edi
+#; 12(%ebp) = long nframes = %ecx
+#; 16(%ebp) = float current = st(0)
+
+ pushl %ebp
+ movl %esp, %ebp
+
+ #; save %edi
+ pushl %edi
+
+ #; the real function
+
+ #; Load "current" in xmm0
+ movss 16(%ebp), %xmm0
+
+ #; if nframes == 0, go to end
+ movl 12(%ebp), %ecx #; nframes
+ cmp $0, %ecx
+ je .CP_END
+
+ #; create the "abs" mask in %xmm2
+ pushl $2147483647
+ movss (%esp), %xmm2
+ addl $4, %esp
+ shufps $0x00, %xmm2, %xmm2
+
+ #; Check for alignment
+
+ movl 8(%ebp), %edi #; buf
+ movl %edi, %edx #; buf => %edx
+ andl $12, %edx #; mask bits 1 & 2, result = 0, 4, 8 or 12
+ jz .CP_SSE #; if buffer IS aligned
+
+ #; PRE-LOOP
+ #; we iterate 1-3 times, doing normal x87 float comparison
+ #; so we reach a 16 byte aligned "buf" (=%edi) value
+
+.LP_START:
+
+ #; Load next value from the buffer
+ movss (%edi), %xmm1
+ andps %xmm2, %xmm1
+ maxss %xmm1, %xmm0
+
+ #; increment buffer, decrement counter
+ addl $4, %edi #; buf++;
+
+ decl %ecx #; nframes--
+ jz .CP_END #; if we run out of frames, we go to the end
+
+ addl $4, %edx #; one non-aligned byte less
+ cmp $16, %edx
+ jne .LP_START #; if more non-aligned frames exist, we do a do-over
+
+.CP_SSE:
+
+ #; We have reached the 16 byte aligned "buf" ("edi") value
+
+ #; Figure out how many loops we should do
+ movl %ecx, %eax #; copy remaining nframes to %eax for division
+
+ shr $2,%eax #; unsigned divide by 4
+ jz .POST_START
+
+ #; %eax = SSE iterations
+
+ #; current maximum is at %xmm0, but we need to ..
+ shufps $0x00, %xmm0, %xmm0 #; shuffle "current" to all 4 FP's
+
+ #;prefetcht0 16(%edi)
+
+.LP_SSE:
+
+ movaps (%edi), %xmm1
+ andps %xmm2, %xmm1
+ maxps %xmm1, %xmm0
+
+ addl $16, %edi
+
+ decl %eax
+ jnz .LP_SSE
+
+ #; Calculate the maximum value contained in the 4 FP's in %xmm0
+ movaps %xmm0, %xmm1
+ shufps $0x4e, %xmm1, %xmm1 #; shuffle left & right pairs (1234 => 3412)
+ maxps %xmm1, %xmm0 #; maximums of the two pairs
+ movaps %xmm0, %xmm1
+ shufps $0xb1, %xmm1, %xmm1 #; shuffle the floats inside the two pairs (1234 => 2143)
+ maxps %xmm1, %xmm0
+
+ #; now every float in %xmm0 is the same value, current maximum value
+
+ #; Next we need to post-process all remaining frames
+ #; the remaining frame count is in %ecx
+
+ #; if no remaining frames, jump to the end
+
+ andl $3, %ecx #; nframes % 4
+ jz .CP_END
+
+.POST_START:
+
+ movss (%edi), %xmm1
+ andps %xmm2, %xmm1
+ maxss %xmm1, %xmm0
+
+ addl $4, %edi #; buf++;
+
+ decl %ecx #; nframes--;
+ jnz .POST_START
+
+.CP_END:
+
+ #; Load the value from xmm0 to the float stack for returning
+ movss %xmm0, 16(%ebp)
+ flds 16(%ebp)
+
+ popl %edi
+
+ #; return
+ leave
+ ret
+
+.size x86_sse_compute_peak, .-x86_sse_compute_peak
+#; end proc
+
+#ifdef __ELF__
+.section .note.GNU-stack,"",%progbits
+#endif
+
+
diff --git a/libs/ardour/sse_functions_64bit.s b/libs/ardour/sse_functions_64bit.s
new file mode 100644
index 0000000000..0242db3e77
--- /dev/null
+++ b/libs/ardour/sse_functions_64bit.s
@@ -0,0 +1,609 @@
+/*
+ Copyright (C) 2005-2006 Paul Davis, John Rigg
+
+ 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.
+
+ Author: Sampo Savolainen
+ 64-bit conversion: John Rigg
+
+ $Id$
+*/
+
+
+#; void x86_sse_mix_buffers_with_gain (float *dst, float *src, unsigned int nframes, float gain);
+
+.globl x86_sse_mix_buffers_with_gain
+ .type x86_sse_mix_buffers_with_gain,@function
+
+x86_sse_mix_buffers_with_gain:
+
+#; %rdi float *dst
+#; %rsi float *src
+#; %rdx unsigned int nframes
+#; %xmm0 float gain
+
+ pushq %rbp
+ movq %rsp, %rbp
+
+ #; save the registers
+ pushq %rbx
+ pushq %rdi
+ pushq %rsi
+
+ #; if nframes == 0, go to end
+ cmp $0, %rdx
+ je .MBWG_END
+
+ #; Check for alignment
+
+ movq %rdi, %rax
+ andq $12, %rax #; mask alignment offset
+
+ movq %rsi, %rbx
+ andq $12, %rbx #; mask alignment offset
+
+ cmp %rax, %rbx
+ jne .MBWG_NONALIGN #; if not aligned, calculate manually
+
+ #; if we are aligned
+ cmp $0, %rbx
+ jz .MBWG_SSE
+
+ #; Pre-loop, we need to run 1-3 frames "manually" without
+ #; SSE instructions
+
+.MBWG_PRELOOP:
+
+ #; gain is already in %xmm0
+ movss (%rsi), %xmm1
+ mulss %xmm0, %xmm1
+ addss (%rdi), %xmm1
+ movss %xmm1, (%rdi)
+
+ addq $4, %rdi #; dst++
+ addq $4, %rsi #; src++
+ decq %rdx #; nframes--
+ jz .MBWG_END
+
+ addq $4, %rbx
+
+ cmp $16, %rbx #; test if we've reached 16 byte alignment
+ jne .MBWG_PRELOOP
+
+
+.MBWG_SSE:
+
+ cmp $4, %rdx #; we know it's not zero, but if it's not >=4, then
+ jnge .MBWG_NONALIGN #; we jump straight to the "normal" code
+
+ #; gain is already in %xmm0
+ shufps $0x00, %xmm0, %xmm0
+
+
+.MBWG_SSELOOP:
+
+ movaps (%rsi), %xmm1 #; source => xmm0
+ mulps %xmm0, %xmm1 #; apply gain to source
+ addps (%rdi), %xmm1 #; mix with destination
+ movaps %xmm1, (%rdi) #; copy result to destination
+
+ addq $16, %rdi #; dst+=4
+ addq $16, %rsi #; src+=4
+
+ subq $4, %rdx #; nframes-=4
+ cmp $4, %rdx
+ jge .MBWG_SSELOOP
+
+ cmp $0, %rdx
+ je .MBWG_END
+
+ #; if there are remaining frames, the nonalign code will do nicely
+ #; for the rest 1-3 frames.
+
+.MBWG_NONALIGN:
+ #; not aligned!
+
+ #; gain is already in %xmm0
+
+.MBWG_NONALIGNLOOP:
+
+ movss (%rsi), %xmm1
+ mulss %xmm0, %xmm1
+ addss (%rdi), %xmm1
+ movss %xmm1, (%rdi)
+
+ addq $4, %rdi
+ addq $4, %rsi
+
+ decq %rdx
+ jnz .MBWG_NONALIGNLOOP
+
+.MBWG_END:
+
+ popq %rsi
+ popq %rdi
+ popq %rbx
+
+ #; return
+ leave
+ ret
+
+.size x86_sse_mix_buffers_with_gain, .-x86_sse_mix_buffers_with_gain
+
+
+#; void x86_sse_mix_buffers_no_gain (float *dst, float *src, unsigned int nframes);
+
+.globl x86_sse_mix_buffers_no_gain
+ .type x86_sse_mix_buffers_no_gain,@function
+
+x86_sse_mix_buffers_no_gain:
+
+#; %rdi float *dst
+#; %rsi float *src
+#; %rdx unsigned int nframes
+
+ pushq %rbp
+ movq %rsp, %rbp
+
+ #; save the registers
+ pushq %rbx
+ pushq %rdi
+ pushq %rsi
+
+ #; the real function
+
+ #; if nframes == 0, go to end
+ cmp $0, %rdx
+ je .MBNG_END
+
+ #; Check for alignment
+
+ movq %rdi, %rax
+ andq $12, %rax #; mask alignment offset
+
+ movq %rsi, %rbx
+ andq $12, %rbx #; mask alignment offset
+
+ cmp %rax, %rbx
+ jne .MBNG_NONALIGN #; if not aligned, calculate manually
+
+ cmp $0, %rbx
+ je .MBNG_SSE
+
+ #; Pre-loop, we need to run 1-3 frames "manually" without
+ #; SSE instructions
+
+.MBNG_PRELOOP:
+
+ movss (%rsi), %xmm0
+ addss (%rdi), %xmm0
+ movss %xmm0, (%rdi)
+
+ addq $4, %rdi #; dst++
+ addq $4, %rsi #; src++
+ decq %rdx #; nframes--
+ jz .MBNG_END
+ addq $4, %rbx
+
+ cmp $16, %rbx #; test if we've reached 16 byte alignment
+ jne .MBNG_PRELOOP
+
+.MBNG_SSE:
+
+ cmp $4, %rdx #; if there are frames left, but less than 4
+ jnge .MBNG_NONALIGN #; we can't run SSE
+
+.MBNG_SSELOOP:
+
+ movaps (%rsi), %xmm0 #; source => xmm0
+ addps (%rdi), %xmm0 #; mix with destination
+ movaps %xmm0, (%rdi) #; copy result to destination
+
+ addq $16, %rdi #; dst+=4
+ addq $16, %rsi #; src+=4
+
+ subq $4, %rdx #; nframes-=4
+ cmp $4, %rdx
+ jge .MBNG_SSELOOP
+
+ cmp $0, %rdx
+ je .MBNG_END
+
+ #; if there are remaining frames, the nonalign code will do nicely
+ #; for the rest 1-3 frames.
+
+.MBNG_NONALIGN:
+ #; not aligned!
+
+ movss (%rsi), %xmm0 #; src => xmm0
+ addss (%rdi), %xmm0 #; xmm0 += dst
+ movss %xmm0, (%rdi) #; xmm0 => dst
+
+ addq $4, %rdi
+ addq $4, %rsi
+
+ decq %rdx
+ jnz .MBNG_NONALIGN
+
+.MBNG_END:
+
+ popq %rsi
+ popq %rdi
+ popq %rbx
+
+ #; return
+ leave
+ ret
+
+.size x86_sse_mix_buffers_no_gain, .-x86_sse_mix_buffers_no_gain
+
+
+#; void x86_sse_apply_gain_to_buffer (float *buf, unsigned int nframes, float gain);
+
+.globl x86_sse_apply_gain_to_buffer
+ .type x86_sse_apply_gain_to_buffer,@function
+
+x86_sse_apply_gain_to_buffer:
+
+#; %rdi float *buf 32(%rbp)
+#; %rsi unsigned int nframes
+#; %xmm0 float gain
+#; %xmm1 float buf[0]
+
+ pushq %rbp
+ movq %rsp, %rbp
+
+ #; save %rdi
+ pushq %rdi
+
+ #; the real function
+
+ #; if nframes == 0, go to end
+ movq %rsi, %rcx #; nframes
+ cmp $0, %rcx
+ je .AG_END
+
+ #; set up the gain buffer (gain is already in %xmm0)
+ shufps $0x00, %xmm0, %xmm0
+
+ #; Check for alignment
+
+ movq %rdi, %rdx #; buf => %rdx
+ andq $12, %rdx #; mask bits 1 & 2, result = 0, 4, 8 or 12
+ jz .AG_SSE #; if buffer IS aligned
+
+ #; PRE-LOOP
+ #; we iterate 1-3 times, doing normal x87 float comparison
+ #; so we reach a 16 byte aligned "buf" (=%rdi) value
+
+.AGLP_START:
+
+ #; Load next value from the buffer into %xmm1
+ movss (%rdi), %xmm1
+ mulss %xmm0, %xmm1
+ movss %xmm1, (%rdi)
+
+ #; increment buffer, decrement counter
+ addq $4, %rdi #; buf++;
+
+ decq %rcx #; nframes--
+ jz .AG_END #; if we run out of frames, we go to the end
+
+ addq $4, %rdx #; one non-aligned byte less
+ cmp $16, %rdx
+ jne .AGLP_START #; if more non-aligned frames exist, we do a do-over
+
+.AG_SSE:
+
+ #; We have reached the 16 byte aligned "buf" ("rdi") value
+
+ #; Figure out how many loops we should do
+ movq %rcx, %rax #; copy remaining nframes to %rax for division
+ movq $0, %rdx #; 0 the edx register
+
+
+ pushq %rdi
+ movq $4, %rdi
+ divq %rdi #; %rdx = remainder == 0
+ popq %rdi
+
+ #; %rax = SSE iterations
+ cmp $0, %rax
+ je .AGPOST_START
+
+
+.AGLP_SSE:
+
+ movaps (%rdi), %xmm1
+ mulps %xmm0, %xmm1
+ movaps %xmm1, (%rdi)
+
+ addq $16, %rdi
+ subq $4, %rcx #; nframes-=4
+
+ decq %rax
+ jnz .AGLP_SSE
+
+ #; Next we need to post-process all remaining frames
+ #; the remaining frame count is in %rcx
+
+ #; if no remaining frames, jump to the end
+ cmp $0, %rcx
+ andq $3, %rcx #; nframes % 4
+ je .AG_END
+
+.AGPOST_START:
+
+ movss (%rdi), %xmm1
+ mulss %xmm0, %xmm1
+ movss %xmm1, (%rdi)
+
+ #; increment buffer, decrement counter
+ addq $4, %rdi #; buf++;
+
+ decq %rcx #; nframes--
+ jnz .AGPOST_START #; if we run out of frames, we go to the end
+
+.AG_END:
+
+
+ popq %rdi
+
+ #; return
+ leave
+ ret
+
+.size x86_sse_apply_gain_to_buffer, .-x86_sse_apply_gain_to_buffer
+#; end proc
+
+
+#; x86_sse_apply_gain_vector(float *buf, float *gain_vector, unsigned int nframes)
+
+.globl x86_sse_apply_gain_vector
+ .type x86_sse_apply_gain_vector,@function
+
+x86_sse_apply_gain_vector:
+
+#; %rdi float *buf
+#; %rsi float *gain_vector
+#; %rdx unsigned int nframes
+
+ pushq %rbp
+ movq %rsp, %rbp
+
+ #; Save registers
+ pushq %rdi
+ pushq %rsi
+ pushq %rbx
+
+ #; if nframes == 0 go to end
+ cmp $0, %rdx
+ je .AGA_END
+
+ #; Check alignment
+ movq %rdi, %rax
+ andq $12, %rax
+
+ movq %rsi, %rbx
+ andq $12, %rbx
+
+ cmp %rax,%rbx
+ jne .AGA_ENDLOOP
+
+ cmp $0, %rax
+ jz .AGA_SSE #; if buffers are aligned, jump to the SSE loop
+
+#; Buffers aren't 16 byte aligned, but they are unaligned by the same amount
+.AGA_ALIGNLOOP:
+
+ movss (%rdi), %xmm0 #; buf => xmm0
+ movss (%rsi), %xmm1 #; gain value => xmm1
+ mulss %xmm1, %xmm0 #; xmm1 * xmm0 => xmm0
+ movss %xmm0, (%rdi) #; signal with gain => buf
+
+ decq %rdx
+ jz .AGA_END
+
+ addq $4, %rdi #; buf++
+ addq $4, %rsi #; gab++
+
+ addq $4, %rax
+ cmp $16, %rax
+ jne .AGA_ALIGNLOOP
+
+#; There are frames left for sure, as that is checked in the beginning
+#; and within the previous loop. BUT, there might be less than 4 frames
+#; to process
+
+.AGA_SSE:
+ movq %rdx, %rax #; nframes => %rax
+ shr $2, %rax #; unsigned divide by 4
+
+ cmp $0, %rax #; Jos toimii ilman tätä, niin kiva
+ je .AGA_ENDLOOP
+
+.AGA_SSELOOP:
+ movaps (%rdi), %xmm0
+ movaps (%rsi), %xmm1
+ mulps %xmm1, %xmm0
+ movaps %xmm0, (%rdi)
+
+ addq $16, %rdi
+ addq $16, %rsi
+
+ decq %rax
+ jnz .AGA_SSELOOP
+
+ andq $3, %rdx #; Remaining frames are nframes & 3
+ jz .AGA_END
+
+
+#; Inside this loop, we know there are frames left to process
+#; but because either there are < 4 frames left, or the buffers
+#; are not aligned, we can't use the parallel SSE ops
+.AGA_ENDLOOP:
+ movss (%rdi), %xmm0 #; buf => xmm0
+ movss (%rsi), %xmm1 #; gain value => xmm1
+ mulss %xmm1, %xmm0 #; xmm1 * xmm0 => xmm0
+ movss %xmm0, (%rdi) #; signal with gain => buf
+
+ addq $4,%rdi
+ addq $4,%rsi
+ decq %rdx #; nframes--
+ jnz .AGA_ENDLOOP
+
+.AGA_END:
+
+ popq %rbx
+ popq %rsi
+ popq %rdi
+
+ leave
+ ret
+
+.size x86_sse_apply_gain_vector, .-x86_sse_apply_gain_vector
+#; end proc
+
+
+#; float x86_sse_compute_peak(float *buf, long nframes, float current);
+
+.globl x86_sse_compute_peak
+ .type x86_sse_compute_peak,@function
+
+
+x86_sse_compute_peak:
+
+#; %rdi float *buf 32(%rbp)
+#; %rsi unsigned int nframes
+#; %xmm0 float current
+#; %xmm1 float buf[0]
+
+ pushq %rbp
+ movq %rsp, %rbp
+
+ #; save %rdi
+ pushq %rdi
+
+ #; if nframes == 0, go to end
+ movq %rsi, %rcx #; nframes
+ cmp $0, %rcx
+ je .CP_END
+
+ #; create the "abs" mask in %xmm2
+ pushq $2147483647
+ movss (%rsp), %xmm2
+ addq $8, %rsp
+ shufps $0x00, %xmm2, %xmm2
+
+ #; Check for alignment
+
+ #;movq 8(%rbp), %rdi #; buf
+ movq %rdi, %rdx #; buf => %rdx
+ andq $12, %rdx #; mask bits 1 & 2, result = 0, 4, 8 or 12
+ jz .CP_SSE #; if buffer IS aligned
+
+ #; PRE-LOOP
+ #; we iterate 1-3 times, doing normal x87 float comparison
+ #; so we reach a 16 byte aligned "buf" (=%rdi) value
+
+.LP_START:
+
+ #; Load next value from the buffer
+ movss (%rdi), %xmm1
+ andps %xmm2, %xmm1
+ maxss %xmm1, %xmm0
+
+ #; increment buffer, decrement counter
+ addq $4, %rdi #; buf++;
+
+ decq %rcx #; nframes--
+ jz .CP_END #; if we run out of frames, we go to the end
+
+ addq $4, %rdx #; one non-aligned byte less
+ cmp $16, %rdx
+ jne .LP_START #; if more non-aligned frames exist, we do a do-over
+
+.CP_SSE:
+
+ #; We have reached the 16 byte aligned "buf" ("rdi") value
+
+ #; Figure out how many loops we should do
+ movq %rcx, %rax #; copy remaining nframes to %rax for division
+
+ shr $2,%rax #; unsigned divide by 4
+ jz .POST_START
+
+ #; %rax = SSE iterations
+
+ #; current maximum is at %xmm0, but we need to ..
+ shufps $0x00, %xmm0, %xmm0 #; shuffle "current" to all 4 FP's
+
+ #;prefetcht0 16(%rdi)
+
+.LP_SSE:
+
+ movaps (%rdi), %xmm1
+ andps %xmm2, %xmm1
+ maxps %xmm1, %xmm0
+
+ addq $16, %rdi
+
+ decq %rax
+ jnz .LP_SSE
+
+ #; Calculate the maximum value contained in the 4 FP's in %xmm0
+ movaps %xmm0, %xmm1
+ shufps $0x4e, %xmm1, %xmm1 #; shuffle left & right pairs (1234 => 3412)
+ maxps %xmm1, %xmm0 #; maximums of the two pairs
+ movaps %xmm0, %xmm1
+ shufps $0xb1, %xmm1, %xmm1 #; shuffle the floats inside the two pairs (1234 => 2143)
+ maxps %xmm1, %xmm0
+
+ #; now every float in %xmm0 is the same value, current maximum value
+
+ #; Next we need to post-process all remaining frames
+ #; the remaining frame count is in %rcx
+
+ #; if no remaining frames, jump to the end
+
+ andq $3, %rcx #; nframes % 4
+ jz .CP_END
+
+.POST_START:
+
+ movss (%rdi), %xmm1
+ andps %xmm2, %xmm1
+ maxss %xmm1, %xmm0
+
+ addq $4, %rdi #; buf++;
+
+ decq %rcx #; nframes--;
+ jnz .POST_START
+
+.CP_END:
+
+ popq %rdi
+
+ #; return
+ leave
+ ret
+
+.size x86_sse_compute_peak, .-x86_sse_compute_peak
+#; end proc
+
+#ifdef __ELF__
+.section .note.GNU-stack,"",%progbits
+#endif
+
diff --git a/libs/ardour/sse_functions_xmm.cc b/libs/ardour/sse_functions_xmm.cc
new file mode 100644
index 0000000000..9b37c37912
--- /dev/null
+++ b/libs/ardour/sse_functions_xmm.cc
@@ -0,0 +1,116 @@
+/*
+ Copyright (C) 2007 Paul sDavis
+ Written by Sampo Savolainen
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <xmmintrin.h>
+#include <ardour/types.h>
+
+void
+x86_sse_find_peaks(const ARDOUR::Sample* buf, nframes_t nframes, float *min, float *max)
+{
+ __m128 current_max, current_min, work;
+
+ // Load max and min values into all four slots of the XMM registers
+ current_min = _mm_set1_ps(*min);
+ current_max = _mm_set1_ps(*max);
+
+ // Work input until "buf" reaches 16 byte alignment
+ while ( ((unsigned long)buf) % 16 != 0 && nframes > 0) {
+
+ // Load the next float into the work buffer
+ work = _mm_set1_ps(*buf);
+
+ current_min = _mm_min_ps(current_min, work);
+ current_max = _mm_max_ps(current_max, work);
+
+ buf++;
+ nframes--;
+ }
+
+ // use 64 byte prefetch for quadruple quads
+ while (nframes >= 16) {
+ __builtin_prefetch(buf+64,0,0);
+
+ work = _mm_load_ps(buf);
+ current_min = _mm_min_ps(current_min, work);
+ current_max = _mm_max_ps(current_max, work);
+ buf+=4;
+ work = _mm_load_ps(buf);
+ current_min = _mm_min_ps(current_min, work);
+ current_max = _mm_max_ps(current_max, work);
+ buf+=4;
+ work = _mm_load_ps(buf);
+ current_min = _mm_min_ps(current_min, work);
+ current_max = _mm_max_ps(current_max, work);
+ buf+=4;
+ work = _mm_load_ps(buf);
+ current_min = _mm_min_ps(current_min, work);
+ current_max = _mm_max_ps(current_max, work);
+ buf+=4;
+ nframes-=16;
+ }
+
+ // work through aligned buffers
+ while (nframes >= 4) {
+
+ work = _mm_load_ps(buf);
+
+ current_min = _mm_min_ps(current_min, work);
+ current_max = _mm_max_ps(current_max, work);
+
+ buf+=4;
+ nframes-=4;
+ }
+
+ // work through the rest < 4 samples
+ while ( nframes > 0) {
+
+ // Load the next float into the work buffer
+ work = _mm_set1_ps(*buf);
+
+ current_min = _mm_min_ps(current_min, work);
+ current_max = _mm_max_ps(current_max, work);
+
+ buf++;
+ nframes--;
+ }
+
+ // Find min & max value in current_max through shuffle tricks
+
+ work = current_min;
+ work = _mm_shuffle_ps(work, work, _MM_SHUFFLE(2, 3, 0, 1));
+ work = _mm_min_ps (work, current_min);
+ current_min = work;
+ work = _mm_shuffle_ps(work, work, _MM_SHUFFLE(1, 0, 3, 2));
+ work = _mm_min_ps (work, current_min);
+
+ _mm_store_ss(min, work);
+
+ work = current_max;
+ work = _mm_shuffle_ps(work, work, _MM_SHUFFLE(2, 3, 0, 1));
+ work = _mm_max_ps (work, current_max);
+ current_max = work;
+ work = _mm_shuffle_ps(work, work, _MM_SHUFFLE(1, 0, 3, 2));
+ work = _mm_max_ps (work, current_max);
+
+ _mm_store_ss(max, work);
+}
+
+
+
diff --git a/libs/ardour/st_pitch.cc b/libs/ardour/st_pitch.cc
new file mode 100644
index 0000000000..3999c1a746
--- /dev/null
+++ b/libs/ardour/st_pitch.cc
@@ -0,0 +1,52 @@
+/*
+ Copyright (C) 2004-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+#include <cmath>
+
+#include <pbd/error.h>
+
+#include <ardour/types.h>
+#include <ardour/pitch.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/session.h>
+#include <ardour/audioregion.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+Pitch::Pitch (Session& s, TimeFXRequest& req)
+ : Filter (s)
+ , tsr (req)
+
+{
+ tsr.progress = 0.0f;
+}
+
+int
+Pitch::run (boost::shared_ptr<Region> region)
+{
+ tsr.progress = 1.0f;
+ tsr.done = true;
+
+ return 1;
+}
diff --git a/libs/ardour/st_stretch.cc b/libs/ardour/st_stretch.cc
new file mode 100644
index 0000000000..e96cd79f2d
--- /dev/null
+++ b/libs/ardour/st_stretch.cc
@@ -0,0 +1,215 @@
+/*
+ Copyright (C) 2004-2007 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+#include <cmath>
+
+#include <pbd/error.h>
+
+#include <ardour/types.h>
+#include <ardour/stretch.h>
+#include <ardour/audiofilesource.h>
+#include <ardour/session.h>
+#include <ardour/audioregion.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+using namespace soundtouch;
+
+Stretch::Stretch (Session& s, TimeFXRequest& req)
+ : Filter (s)
+ , tsr (req)
+{
+ float percentage;
+
+ /* the soundtouch code wants a *tempo* change percentage, which is
+ of opposite sign to the length change.
+ */
+
+ percentage = -tsr.time_fraction;
+
+ st.setSampleRate (s.frame_rate());
+ st.setChannels (1);
+ st.setTempoChange (percentage);
+ st.setPitchSemiTones (0);
+ st.setRateChange (0);
+
+ st.setSetting(SETTING_USE_QUICKSEEK, tsr.quick_seek);
+ st.setSetting(SETTING_USE_AA_FILTER, tsr.antialias);
+
+ tsr.progress = 0.0f;
+}
+
+Stretch::~Stretch ()
+{
+}
+
+int
+Stretch::run (boost::shared_ptr<Region> a_region)
+{
+ SourceList nsrcs;
+ nframes_t total_frames;
+ nframes_t done;
+ int ret = -1;
+ const nframes_t bufsize = 16384;
+ gain_t *gain_buffer = 0;
+ Sample *buffer = 0;
+ char suffix[32];
+ string new_name;
+ string::size_type at;
+
+ tsr.progress = 0.0f;
+ tsr.done = false;
+
+ boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion>(a_region);
+
+ total_frames = region->length() * region->n_channels();
+ done = 0;
+
+ /* the name doesn't need to be super-precise, but allow for 2 fractional
+ digits just to disambiguate close but not identical stretches.
+ */
+
+ snprintf (suffix, sizeof (suffix), "@%d", (int) floor (tsr.time_fraction * 100.0f));
+
+ /* create new sources */
+
+ if (make_new_sources (region, nsrcs, suffix)) {
+ goto out;
+ }
+
+ gain_buffer = new gain_t[bufsize];
+ buffer = new Sample[bufsize];
+
+ // soundtouch throws runtime_error on error
+
+ try {
+ for (uint32_t i = 0; i < nsrcs.size(); ++i) {
+
+ boost::shared_ptr<AudioSource> asrc
+ = boost::dynamic_pointer_cast<AudioSource>(nsrcs[i]);
+
+ nframes_t pos = 0;
+ nframes_t this_read = 0;
+
+ st.clear();
+
+ while (!tsr.cancel && pos < region->length()) {
+ nframes_t this_time;
+
+ this_time = min (bufsize, region->length() - pos);
+
+ /* read from the master (original) sources for the region,
+ not the ones currently in use, in case it's already been
+ subject to timefx.
+ */
+
+ if ((this_read = region->master_read_at (buffer, buffer, gain_buffer, pos + region->position(), this_time)) != this_time) {
+ error << string_compose (_("tempoize: error reading data from %1"), asrc->name()) << endmsg;
+ goto out;
+ }
+
+ pos += this_read;
+ done += this_read;
+
+ tsr.progress = (float) done / total_frames;
+
+ st.putSamples (buffer, this_read);
+
+ while ((this_read = st.receiveSamples (buffer, bufsize)) > 0 && !tsr.cancel) {
+ if (asrc->write (buffer, this_read) != this_read) {
+ error << string_compose (_("error writing tempo-adjusted data to %1"), asrc->name()) << endmsg;
+ goto out;
+ }
+ }
+ }
+
+ if (!tsr.cancel) {
+ st.flush ();
+ }
+
+ while (!tsr.cancel && (this_read = st.receiveSamples (buffer, bufsize)) > 0) {
+ if (asrc->write (buffer, this_read) != this_read) {
+ error << string_compose (_("error writing tempo-adjusted data to %1"), asrc->name()) << endmsg;
+ goto out;
+ }
+ }
+ }
+
+ } catch (runtime_error& err) {
+ error << _("timefx code failure. please notify ardour-developers.") << endmsg;
+ error << err.what() << endmsg;
+ goto out;
+ }
+
+ new_name = region->name();
+ at = new_name.find ('@');
+
+ // remove any existing stretch indicator
+
+ if (at != string::npos && at > 2) {
+ new_name = new_name.substr (0, at - 1);
+ }
+
+ new_name += suffix;
+
+ ret = finish (region, nsrcs, new_name);
+
+ /* now reset ancestral data for each new region */
+
+ for (vector<boost::shared_ptr<Region> >::iterator x = results.begin(); x != results.end(); ++x) {
+ nframes64_t astart = (*x)->ancestral_start();
+ nframes64_t alength = (*x)->ancestral_length();
+ nframes_t start;
+ nframes_t length;
+
+ // note: tsr.fraction is a percentage of original length. 100 = no change,
+ // 50 is half as long, 200 is twice as long, etc.
+
+ float stretch = (*x)->stretch() * (tsr.time_fraction/100.0);
+
+ start = (nframes_t) floor (astart + ((astart - (*x)->start()) / stretch));
+ length = (nframes_t) floor (alength / stretch);
+
+ (*x)->set_ancestral_data (start, length, stretch, (*x)->shift());
+ }
+
+ out:
+
+ if (gain_buffer) {
+ delete [] gain_buffer;
+ }
+
+ if (buffer) {
+ delete [] buffer;
+ }
+
+ if (ret || tsr.cancel) {
+ for (SourceList::iterator si = nsrcs.begin(); si != nsrcs.end(); ++si) {
+ (*si)->mark_for_remove ();
+ }
+ }
+
+ tsr.done = true;
+
+ return ret;
+}
diff --git a/libs/ardour/tape_file_matcher.cc b/libs/ardour/tape_file_matcher.cc
new file mode 100644
index 0000000000..835a951f8a
--- /dev/null
+++ b/libs/ardour/tape_file_matcher.cc
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2007 Tim Mayberry
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <pbd/error.h>
+
+#include <ardour/tape_file_matcher.h>
+
+#include "i18n.h"
+
+namespace {
+
+const char* const tape_file_regex_string = X_("/T[0-9][0-9][0-9][0-9]-");
+
+}
+
+namespace ARDOUR {
+
+TapeFileMatcher::TapeFileMatcher()
+{
+ int err;
+
+ if ((err = regcomp (&m_compiled_pattern,
+ tape_file_regex_string, REG_EXTENDED|REG_NOSUB)))
+ {
+ char msg[256];
+
+ regerror (err, &m_compiled_pattern, msg, sizeof (msg));
+
+ PBD::error << string_compose (_("Cannot compile tape track regexp for use (%1)"), msg) << endmsg;
+ // throw
+ }
+
+}
+
+bool
+TapeFileMatcher::matches (const string& audio_filename) const
+{
+
+ if (regexec (&m_compiled_pattern, audio_filename.c_str(), 0, 0, 0) == 0)
+ {
+ // matches
+ return true;
+ }
+ return false;
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/template_utils.cc b/libs/ardour/template_utils.cc
new file mode 100644
index 0000000000..171939dac2
--- /dev/null
+++ b/libs/ardour/template_utils.cc
@@ -0,0 +1,34 @@
+#include <algorithm>
+
+#include <pbd/filesystem.h>
+
+#include <ardour/template_utils.h>
+#include <ardour/directory_names.h>
+#include <ardour/filesystem_paths.h>
+
+namespace ARDOUR {
+
+sys::path
+system_template_directory ()
+{
+ SearchPath spath(system_data_search_path());
+ spath.add_subdirectory_to_paths(templates_dir_name);
+
+ // just return the first directory in the search path that exists
+ SearchPath::const_iterator i = std::find_if(spath.begin(), spath.end(), sys::exists);
+
+ if (i == spath.end()) return sys::path();
+
+ return *i;
+}
+
+sys::path
+user_template_directory ()
+{
+ sys::path p(user_config_directory());
+ p /= templates_dir_name;
+
+ return p;
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/tempo.cc b/libs/ardour/tempo.cc
new file mode 100644
index 0000000000..ab7b7c096e
--- /dev/null
+++ b/libs/ardour/tempo.cc
@@ -0,0 +1,1539 @@
+/*
+ Copyright (C) 2000-2002 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+#include <unistd.h>
+
+#include <cmath>
+
+#include <sigc++/bind.h>
+
+#include <glibmm/thread.h>
+#include <pbd/xml++.h>
+#include <ardour/tempo.h>
+#include <ardour/utils.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+/* _default tempo is 4/4 qtr=120 */
+
+Meter TempoMap::_default_meter (4.0, 4.0);
+Tempo TempoMap::_default_tempo (120.0);
+
+const double Meter::ticks_per_beat = 1920.0;
+
+double Tempo::frames_per_beat (nframes_t sr, const Meter& meter) const
+{
+ return ((60.0 * sr) / (_beats_per_minute * meter.note_divisor()/_note_type));
+}
+
+/***********************************************************************/
+
+double
+Meter::frames_per_bar (const Tempo& tempo, nframes_t sr) const
+{
+ return ((60.0 * sr * _beats_per_bar) / (tempo.beats_per_minute() * _note_type/tempo.note_type()));
+}
+
+/***********************************************************************/
+
+const string TempoSection::xml_state_node_name = "Tempo";
+
+TempoSection::TempoSection (const XMLNode& node)
+ : MetricSection (BBT_Time()), Tempo (TempoMap::default_tempo())
+{
+ const XMLProperty *prop;
+ BBT_Time start;
+ LocaleGuard lg (X_("POSIX"));
+
+ if ((prop = node.property ("start")) == 0) {
+ error << _("TempoSection XML node has no \"start\" property") << endmsg;
+ throw failed_constructor();
+ }
+
+ if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+ &start.bars,
+ &start.beats,
+ &start.ticks) < 3) {
+ error << _("TempoSection XML node has an illegal \"start\" value") << endmsg;
+ throw failed_constructor();
+ }
+
+ set_start (start);
+
+ if ((prop = node.property ("beats-per-minute")) == 0) {
+ error << _("TempoSection XML node has no \"beats-per-minute\" property") << endmsg;
+ throw failed_constructor();
+ }
+
+ if (sscanf (prop->value().c_str(), "%lf", &_beats_per_minute) != 1 || _beats_per_minute < 0.0) {
+ error << _("TempoSection XML node has an illegal \"beats_per_minute\" value") << endmsg;
+ throw failed_constructor();
+ }
+
+ if ((prop = node.property ("note-type")) == 0) {
+ /* older session, make note type be quarter by default */
+ _note_type = 4.0;
+ } else {
+ if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 1.0) {
+ error << _("TempoSection XML node has an illegal \"note-type\" value") << endmsg;
+ throw failed_constructor();
+ }
+ }
+
+ if ((prop = node.property ("movable")) == 0) {
+ error << _("TempoSection XML node has no \"movable\" property") << endmsg;
+ throw failed_constructor();
+ }
+
+ set_movable (prop->value() == "yes");
+}
+
+XMLNode&
+TempoSection::get_state() const
+{
+ XMLNode *root = new XMLNode (xml_state_node_name);
+ char buf[256];
+ LocaleGuard lg (X_("POSIX"));
+
+ snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+ start().bars,
+ start().beats,
+ start().ticks);
+ root->add_property ("start", buf);
+ snprintf (buf, sizeof (buf), "%f", _beats_per_minute);
+ root->add_property ("beats-per-minute", buf);
+ snprintf (buf, sizeof (buf), "%f", _note_type);
+ root->add_property ("note-type", buf);
+ snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
+ root->add_property ("movable", buf);
+
+ return *root;
+}
+
+/***********************************************************************/
+
+const string MeterSection::xml_state_node_name = "Meter";
+
+MeterSection::MeterSection (const XMLNode& node)
+ : MetricSection (BBT_Time()), Meter (TempoMap::default_meter())
+{
+ const XMLProperty *prop;
+ BBT_Time start;
+ LocaleGuard lg (X_("POSIX"));
+
+ if ((prop = node.property ("start")) == 0) {
+ error << _("MeterSection XML node has no \"start\" property") << endmsg;
+ throw failed_constructor();
+ }
+
+ if (sscanf (prop->value().c_str(), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+ &start.bars,
+ &start.beats,
+ &start.ticks) < 3) {
+ error << _("MeterSection XML node has an illegal \"start\" value") << endmsg;
+ throw failed_constructor();
+ }
+
+ set_start (start);
+
+ if ((prop = node.property ("beats-per-bar")) == 0) {
+ error << _("MeterSection XML node has no \"beats-per-bar\" property") << endmsg;
+ throw failed_constructor();
+ }
+
+ if (sscanf (prop->value().c_str(), "%lf", &_beats_per_bar) != 1 || _beats_per_bar < 0.0) {
+ error << _("MeterSection XML node has an illegal \"beats-per-bar\" value") << endmsg;
+ throw failed_constructor();
+ }
+
+ if ((prop = node.property ("note-type")) == 0) {
+ error << _("MeterSection XML node has no \"note-type\" property") << endmsg;
+ throw failed_constructor();
+ }
+
+ if (sscanf (prop->value().c_str(), "%lf", &_note_type) != 1 || _note_type < 0.0) {
+ error << _("MeterSection XML node has an illegal \"note-type\" value") << endmsg;
+ throw failed_constructor();
+ }
+
+ if ((prop = node.property ("movable")) == 0) {
+ error << _("MeterSection XML node has no \"movable\" property") << endmsg;
+ throw failed_constructor();
+ }
+
+ set_movable (prop->value() == "yes");
+}
+
+XMLNode&
+MeterSection::get_state() const
+{
+ XMLNode *root = new XMLNode (xml_state_node_name);
+ char buf[256];
+ LocaleGuard lg (X_("POSIX"));
+
+ snprintf (buf, sizeof (buf), "%" PRIu32 "|%" PRIu32 "|%" PRIu32,
+ start().bars,
+ start().beats,
+ start().ticks);
+ root->add_property ("start", buf);
+ snprintf (buf, sizeof (buf), "%f", _note_type);
+ root->add_property ("note-type", buf);
+ snprintf (buf, sizeof (buf), "%f", _beats_per_bar);
+ root->add_property ("beats-per-bar", buf);
+ snprintf (buf, sizeof (buf), "%s", movable()?"yes":"no");
+ root->add_property ("movable", buf);
+
+ return *root;
+}
+
+/***********************************************************************/
+
+struct MetricSectionSorter {
+ bool operator() (const MetricSection* a, const MetricSection* b) {
+ return a->start() < b->start();
+ }
+};
+
+TempoMap::TempoMap (nframes_t fr)
+{
+ metrics = new Metrics;
+ _frame_rate = fr;
+ last_bbt_valid = false;
+ BBT_Time start;
+
+ start.bars = 1;
+ start.beats = 1;
+ start.ticks = 0;
+
+ TempoSection *t = new TempoSection (start, _default_tempo.beats_per_minute(), _default_tempo.note_type());
+ MeterSection *m = new MeterSection (start, _default_meter.beats_per_bar(), _default_meter.note_divisor());
+
+ t->set_movable (false);
+ m->set_movable (false);
+
+ /* note: frame time is correct (zero) for both of these */
+
+ metrics->push_back (t);
+ metrics->push_back (m);
+}
+
+TempoMap::~TempoMap ()
+{
+}
+
+int
+TempoMap::move_metric_section (MetricSection& section, const BBT_Time& when)
+{
+ if (when == section.start() || !section.movable()) {
+ return -1;
+ }
+
+ Glib::RWLock::WriterLock lm (lock);
+ MetricSectionSorter cmp;
+
+ if (when.beats != 1) {
+
+ /* position by audio frame, then recompute BBT timestamps from the audio ones */
+
+ nframes_t frame = frame_time (when);
+ // cerr << "nominal frame time = " << frame << endl;
+
+ nframes_t prev_frame = round_to_type (frame, -1, Beat);
+ nframes_t next_frame = round_to_type (frame, 1, Beat);
+
+ // cerr << "previous beat at " << prev_frame << " next at " << next_frame << endl;
+
+ /* use the closest beat */
+
+ if ((frame - prev_frame) < (next_frame - frame)) {
+ frame = prev_frame;
+ } else {
+ frame = next_frame;
+ }
+
+ // cerr << "actual frame time = " << frame << endl;
+ section.set_frame (frame);
+ // cerr << "frame time = " << section.frame() << endl;
+ timestamp_metrics (false);
+ // cerr << "new BBT time = " << section.start() << endl;
+ metrics->sort (cmp);
+
+ } else {
+
+ /* positioned at bar start already, so just put it there */
+
+ section.set_start (when);
+ metrics->sort (cmp);
+ timestamp_metrics (true);
+ }
+
+
+ return 0;
+}
+
+void
+TempoMap::move_tempo (TempoSection& tempo, const BBT_Time& when)
+{
+ if (move_metric_section (tempo, when) == 0) {
+ StateChanged (Change (0));
+ }
+}
+
+void
+TempoMap::move_meter (MeterSection& meter, const BBT_Time& when)
+{
+ if (move_metric_section (meter, when) == 0) {
+ StateChanged (Change (0));
+ }
+}
+
+void
+TempoMap::remove_tempo (const TempoSection& tempo)
+{
+ bool removed = false;
+
+ {
+ Glib::RWLock::WriterLock lm (lock);
+ Metrics::iterator i;
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+ if (dynamic_cast<TempoSection*> (*i) != 0) {
+ if (tempo.frame() == (*i)->frame()) {
+ if ((*i)->movable()) {
+ metrics->erase (i);
+ removed = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (removed) {
+ StateChanged (Change (0));
+ }
+}
+
+void
+TempoMap::remove_meter (const MeterSection& tempo)
+{
+ bool removed = false;
+
+ {
+ Glib::RWLock::WriterLock lm (lock);
+ Metrics::iterator i;
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+ if (dynamic_cast<MeterSection*> (*i) != 0) {
+ if (tempo.frame() == (*i)->frame()) {
+ if ((*i)->movable()) {
+ metrics->erase (i);
+ removed = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (removed) {
+ StateChanged (Change (0));
+ }
+}
+
+void
+TempoMap::do_insert (MetricSection* section, bool with_bbt)
+{
+ Metrics::iterator i;
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+
+ if (with_bbt) {
+ if ((*i)->start() < section->start()) {
+ continue;
+ }
+ } else {
+ if ((*i)->frame() < section->frame()) {
+ continue;
+ }
+ }
+
+ metrics->insert (i, section);
+ break;
+ }
+
+ if (i == metrics->end()) {
+ metrics->insert (metrics->end(), section);
+ }
+
+ timestamp_metrics (with_bbt);
+}
+
+void
+TempoMap::add_tempo (const Tempo& tempo, BBT_Time where)
+{
+ {
+ Glib::RWLock::WriterLock lm (lock);
+
+ /* new tempos always start on a beat */
+
+ where.ticks = 0;
+
+ 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));
+}
+
+void
+TempoMap::replace_tempo (TempoSection& existing, const Tempo& replacement)
+{
+ bool replaced = false;
+
+ {
+ Glib::RWLock::WriterLock lm (lock);
+ Metrics::iterator i;
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+ TempoSection *ts;
+
+ if ((ts = dynamic_cast<TempoSection*>(*i)) != 0 && ts == &existing) {
+
+ *((Tempo *) ts) = replacement;
+
+ replaced = true;
+ timestamp_metrics (true);
+
+ break;
+ }
+ }
+ }
+
+ if (replaced) {
+ StateChanged (Change (0));
+ }
+}
+
+void
+TempoMap::add_meter (const Meter& meter, BBT_Time where)
+{
+ {
+ Glib::RWLock::WriterLock lm (lock);
+
+ /* a new meter always starts a new bar on the first beat. so
+ round the start time appropriately. remember that
+ `where' is based on the existing tempo map, not
+ the result after we insert the new meter.
+
+ */
+
+ if (where.beats != 1) {
+ where.beats = 1;
+ where.bars++;
+ }
+
+ /* new meters *always* start on a beat. */
+
+ where.ticks = 0;
+
+ 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));
+}
+
+void
+TempoMap::replace_meter (MeterSection& existing, const Meter& replacement)
+{
+ bool replaced = false;
+
+ {
+ Glib::RWLock::WriterLock lm (lock);
+ Metrics::iterator i;
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+ MeterSection *ms;
+ if ((ms = dynamic_cast<MeterSection*>(*i)) != 0 && ms == &existing) {
+
+ *((Meter*) ms) = replacement;
+
+ replaced = true;
+ timestamp_metrics (true);
+ break;
+ }
+ }
+ }
+
+ if (replaced) {
+ StateChanged (Change (0));
+ }
+}
+
+void
+TempoMap::change_initial_tempo (double beats_per_minute, double note_type)
+{
+ Tempo newtempo (beats_per_minute, note_type);
+ TempoSection* t;
+
+ for (Metrics::iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if ((t = dynamic_cast<TempoSection*> (*i)) != 0) {
+ *((Tempo*) t) = newtempo;
+ StateChanged (Change (0));
+ break;
+ }
+ }
+}
+
+void
+TempoMap::change_existing_tempo_at (nframes_t where, double beats_per_minute, double note_type)
+{
+ Tempo newtempo (beats_per_minute, note_type);
+
+ 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
+{
+ const MeterSection *m = 0;
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if ((m = dynamic_cast<const MeterSection *> (*i)) != 0) {
+ return *m;
+ }
+ }
+
+ fatal << _("programming error: no tempo section in tempo map!") << endmsg;
+ /*NOTREACHED*/
+ return *m;
+}
+
+const TempoSection&
+TempoMap::first_tempo () const
+{
+ const TempoSection *t = 0;
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if ((t = dynamic_cast<const TempoSection *> (*i)) != 0) {
+ return *t;
+ }
+ }
+
+ fatal << _("programming error: no tempo section in tempo map!") << endmsg;
+ /*NOTREACHED*/
+ return *t;
+}
+
+void
+TempoMap::timestamp_metrics (bool use_bbt)
+{
+ Metrics::iterator i;
+ const Meter* meter;
+ const Tempo* tempo;
+ Meter *m;
+ Tempo *t;
+
+ meter = &first_meter ();
+ tempo = &first_tempo ();
+
+ if (use_bbt) {
+
+ // cerr << "\n\n\n ######################\nTIMESTAMP via BBT ##############\n" << endl;
+
+ nframes_t current = 0;
+ nframes_t section_frames;
+ BBT_Time start;
+ BBT_Time end;
+
+ 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*/
+ }
+ }
+
+ } else {
+
+ // cerr << "\n\n\n ######################\nTIMESTAMP via AUDIO ##############\n" << endl;
+
+ bool first = true;
+ MetricSection* prev = 0;
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+
+ BBT_Time bbt;
+ Metric metric (*meter, *tempo);
+
+ if (prev) {
+ metric.set_start (prev->start());
+ } else {
+ // metric will be at frames=0 bbt=1|1|0 by default
+ // which is correct for our purpose
+ }
+
+ bbt_time_with_metric ((*i)->frame(), bbt, metric);
+
+ // cerr << "timestamp @ " << (*i)->frame() << " with " << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << " => ";
+
+
+ if (first) {
+ first = false;
+ } else {
+
+ if (bbt.ticks > Meter::ticks_per_beat/2) {
+ /* round up to next beat */
+ bbt.beats += 1;
+ }
+
+ bbt.ticks = 0;
+
+ if (bbt.beats != 1) {
+ /* round up to next bar */
+ bbt.bars += 1;
+ bbt.beats = 1;
+ }
+ }
+
+ //s cerr << bbt.bars << "|" << bbt.beats << "|" << bbt.ticks << endl;
+
+ (*i)->set_start (bbt);
+
+ if ((t = dynamic_cast<TempoSection*>(*i)) != 0) {
+ tempo = t;
+ // cerr << "NEW TEMPO, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+ } else if ((m = dynamic_cast<MeterSection*>(*i)) != 0) {
+ meter = m;
+ // cerr << "NEW METER, frame = " << (*i)->frame() << " start = " << (*i)->start() <<endl;
+ } else {
+ fatal << _("programming error: unhandled MetricSection type") << endmsg;
+ /*NOTREACHED*/
+ }
+
+ prev = (*i);
+ }
+ }
+
+ // dump (cerr);
+ // cerr << "###############################################\n\n\n" << endl;
+
+}
+
+TempoMap::Metric
+TempoMap::metric_at (nframes_t frame) const
+{
+ Metric m (first_meter(), first_tempo());
+ const Meter* meter;
+ const Tempo* tempo;
+
+ /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
+ at something, because we insert the default tempo and meter during
+ TempoMap construction.
+
+ now see if we can find better candidates.
+ */
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+
+ if ((*i)->frame() > frame) {
+ break;
+ }
+
+ if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ m.set_tempo (*tempo);
+ } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ m.set_meter (*meter);
+ }
+
+ m.set_frame ((*i)->frame ());
+ m.set_start ((*i)->start ());
+ }
+
+ return m;
+}
+
+TempoMap::Metric
+TempoMap::metric_at (BBT_Time bbt) const
+{
+ Metric m (first_meter(), first_tempo());
+ const Meter* meter;
+ const Tempo* tempo;
+
+ /* at this point, we are *guaranteed* to have m.meter and m.tempo pointing
+ at something, because we insert the default tempo and meter during
+ TempoMap construction.
+
+ now see if we can find better candidates.
+ */
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+
+ BBT_Time section_start ((*i)->start());
+
+ if (section_start.bars > bbt.bars || (section_start.bars == bbt.bars && section_start.beats > bbt.beats)) {
+ break;
+ }
+
+ if ((tempo = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ m.set_tempo (*tempo);
+ } else if ((meter = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ m.set_meter (*meter);
+ }
+
+ m.set_frame ((*i)->frame ());
+ m.set_start (section_start);
+ }
+
+ return m;
+}
+
+void
+TempoMap::bbt_time (nframes_t frame, BBT_Time& bbt) const
+{
+ {
+ Glib::RWLock::ReaderLock lm (lock);
+ bbt_time_unlocked (frame, bbt);
+ }
+}
+
+void
+TempoMap::bbt_time_unlocked (nframes_t frame, BBT_Time& bbt) const
+{
+ bbt_time_with_metric (frame, bbt, metric_at (frame));
+}
+
+void
+TempoMap::bbt_time_with_metric (nframes_t frame, BBT_Time& bbt, const Metric& metric) const
+{
+ nframes_t frame_diff;
+
+ uint32_t xtra_bars = 0;
+ double xtra_beats = 0;
+ double beats = 0;
+
+ // cerr << "---- BBT time for " << frame << " using metric @ " << metric.frame() << " BBT " << metric.start() << endl;
+
+ const double beats_per_bar = metric.meter().beats_per_bar();
+ const double frames_per_bar = metric.meter().frames_per_bar (metric.tempo(), _frame_rate);
+ const double beat_frames = metric.tempo().frames_per_beat (_frame_rate, metric.meter());
+
+ /* now compute how far beyond that point we actually are. */
+
+ frame_diff = frame - metric.frame();
+
+ // cerr << "----\tdelta = " << frame_diff << endl;
+
+ xtra_bars = (uint32_t) floor (frame_diff / frames_per_bar);
+ frame_diff -= (uint32_t) floor (xtra_bars * frames_per_bar);
+ xtra_beats = (double) frame_diff / beat_frames;
+
+ // cerr << "---\tmeaning " << xtra_bars << " xtra bars and " << xtra_beats << " xtra beats\n";
+
+ /* and set the returned value */
+
+ /* and correct beat/bar shifts to match the meter.
+ remember: beat and bar counting is 1-based,
+ not zero-based
+ also the meter may contain a fraction
+ */
+
+ bbt.bars = metric.start().bars + xtra_bars;
+
+ beats = (double) metric.start().beats + xtra_beats;
+
+ bbt.bars += (uint32_t) floor(beats/ (beats_per_bar+1) );
+
+ beats = fmod(beats - 1, beats_per_bar )+ 1.0;
+ bbt.ticks = (uint32_t)( round((beats - floor(beats)) *(double) Meter::ticks_per_beat));
+ bbt.beats = (uint32_t) floor(beats);
+
+ // cerr << "-----\t RETURN " << bbt << endl;
+}
+
+nframes_t
+TempoMap::count_frames_between ( const BBT_Time& start, const BBT_Time& end) const
+{
+ /* for this to work with fractional measure types, start and end have to be "legal" BBT types,
+ that means that the beats and ticks should be inside a bar
+ */
+
+ nframes_t frames = 0;
+ nframes_t start_frame = 0;
+ nframes_t end_frame = 0;
+
+ Metric m = metric_at (start);
+
+ uint32_t bar_offset = start.bars - m.start().bars;
+
+ double beat_offset = bar_offset*m.meter().beats_per_bar() - (m.start().beats-1) + (start.beats -1)
+ + start.ticks/Meter::ticks_per_beat;
+
+
+ start_frame = m.frame() + (nframes_t) rint( beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
+
+ m = metric_at(end);
+
+ bar_offset = end.bars - m.start().bars;
+
+ beat_offset = bar_offset * m.meter().beats_per_bar() - (m.start().beats -1) + (end.beats - 1)
+ + end.ticks/Meter::ticks_per_beat;
+
+ end_frame = m.frame() + (nframes_t) rint(beat_offset * m.tempo().frames_per_beat(_frame_rate, m.meter()));
+
+ frames = end_frame - start_frame;
+
+ return frames;
+
+}
+
+nframes_t
+TempoMap::count_frames_between_metrics (const Meter& meter, const Tempo& tempo, const BBT_Time& start, const BBT_Time& end) const
+{
+ /* this is used in timestamping the metrics by actually counting the beats */
+
+ nframes_t frames = 0;
+ uint32_t bar = start.bars;
+ double beat = (double) start.beats;
+ double beats_counted = 0;
+ double beats_per_bar = 0;
+ double beat_frames = 0;
+
+ beats_per_bar = meter.beats_per_bar();
+ beat_frames = tempo.frames_per_beat (_frame_rate,meter);
+
+ frames = 0;
+
+ while (bar < end.bars || (bar == end.bars && beat < end.beats)) {
+
+ if (beat >= beats_per_bar) {
+ beat = 1;
+ ++bar;
+ ++beats_counted;
+
+ if (beat > beats_per_bar) {
+
+ /* this is a fractional beat at the end of a fractional bar
+ so it should only count for the fraction
+ */
+
+ beats_counted -= (ceil(beats_per_bar) - beats_per_bar);
+ }
+
+ } else {
+ ++beat;
+ ++beats_counted;
+ }
+ }
+
+ // cerr << "Counted " << beats_counted << " from " << start << " to " << end
+ // << " bpb were " << beats_per_bar
+ // << " fpb was " << beat_frames
+ // << endl;
+
+ frames = (nframes_t) floor (beats_counted * beat_frames);
+
+ return frames;
+
+}
+
+nframes_t
+TempoMap::frame_time (const BBT_Time& bbt) const
+{
+ BBT_Time start ; /* 1|1|0 */
+
+ return count_frames_between ( start, bbt);
+}
+
+nframes_t
+TempoMap::bbt_duration_at (nframes_t pos, const BBT_Time& bbt, int dir) const
+{
+ nframes_t frames = 0;
+
+ BBT_Time when;
+ bbt_time(pos,when);
+
+ {
+ Glib::RWLock::ReaderLock lm (lock);
+ frames = bbt_duration_at_unlocked (when, bbt,dir);
+ }
+
+ return frames;
+}
+
+nframes_t
+TempoMap::bbt_duration_at_unlocked (const BBT_Time& when, const BBT_Time& bbt, int dir) const
+{
+
+ nframes_t frames = 0;
+
+ double beats_per_bar;
+ BBT_Time result;
+
+ result.bars = max(1U,when.bars + dir * bbt.bars) ;
+ result.beats = 1;
+ result.ticks = 0;
+
+ Metric metric = metric_at(result);
+ beats_per_bar = metric.meter().beats_per_bar();
+
+
+
+ /*reduce things to legal bbt values
+ we have to handle possible fractional=shorter beats at the end of measures
+ and things like 0|11|9000 as a duration in a 4.5/4 measure
+ the musical decision is that the fractional beat is also a beat , although a shorter one
+ */
+
+
+ if (dir >= 0) {
+ result.beats = when.beats + bbt.beats;
+ result.ticks = when.ticks + bbt.ticks;
+
+ while (result.beats >= (beats_per_bar+1)) {
+ result.bars++;
+ result.beats -= (uint32_t) ceil(beats_per_bar);
+ metric = metric_at(result); // maybe there is a meter change
+ beats_per_bar = metric.meter().beats_per_bar();
+
+ }
+ /*we now counted the beats and landed in the target measure, now deal with ticks
+ this seems complicated, but we want to deal with the corner case of a sequence of time signatures like 0.2/4-0.7/4
+ and with request like bbt = 3|2|9000 ,so we repeat the same loop but add ticks
+ */
+
+ /* of course gtk_ardour only allows bar with at least 1.0 beats .....
+ */
+
+ uint32_t ticks_at_beat = (uint32_t) ( result.beats == ceil(beats_per_bar) ?
+ (1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat
+ : Meter::ticks_per_beat );
+
+ while (result.ticks >= ticks_at_beat) {
+ result.beats++;
+ result.ticks -= ticks_at_beat;
+ if (result.beats >= (beats_per_bar+1)) {
+ result.bars++;
+ result.beats = 1;
+ metric = metric_at(result); // maybe there is a meter change
+ beats_per_bar = metric.meter().beats_per_bar();
+ }
+ ticks_at_beat= (uint32_t) ( result.beats == ceil(beats_per_bar) ?
+ (1 - (ceil(beats_per_bar) - beats_per_bar) )* Meter::ticks_per_beat
+ : Meter::ticks_per_beat);
+
+ }
+
+
+ } else {
+ uint32_t b = bbt.beats;
+
+ /* count beats */
+ while( b > when.beats ) {
+
+ result.bars = max(1U,result.bars-- ) ;
+ metric = metric_at(result); // maybe there is a meter change
+ beats_per_bar = metric.meter().beats_per_bar();
+ if (b >= ceil(beats_per_bar)) {
+
+ b -= (uint32_t) ceil(beats_per_bar);
+ } else {
+ b = (uint32_t) ceil(beats_per_bar)- b + when.beats ;
+ }
+ }
+ result.beats = when.beats - b;
+
+ /*count ticks */
+
+ if (bbt.ticks <= when.ticks) {
+ result.ticks = when.ticks - bbt.ticks;
+ } else {
+
+ uint32_t ticks_at_beat= (uint32_t) Meter::ticks_per_beat;
+ uint32_t t = bbt.ticks - when.ticks;
+
+ do {
+
+ if (result.beats == 1) {
+ result.bars = max(1U,result.bars-- ) ;
+ metric = metric_at(result); // maybe there is a meter change
+ beats_per_bar = metric.meter().beats_per_bar();
+ result.beats = (uint32_t) ceil(beats_per_bar);
+ ticks_at_beat = (uint32_t) ((1 - (ceil(beats_per_bar) - beats_per_bar))* Meter::ticks_per_beat) ;
+ } else {
+ result.beats --;
+ ticks_at_beat = (uint32_t) Meter::ticks_per_beat;
+ }
+
+ if (t <= ticks_at_beat) {
+ result.ticks = ticks_at_beat - t;
+ } else {
+ t-= ticks_at_beat;
+ }
+ } while (t > ticks_at_beat);
+
+ }
+
+
+ }
+
+ if (dir < 0 ) {
+ frames = count_frames_between( result,when);
+ } else {
+ frames = count_frames_between(when,result);
+ }
+
+ return frames;
+}
+
+
+
+nframes_t
+TempoMap::round_to_bar (nframes_t fr, int dir)
+{
+ {
+ Glib::RWLock::ReaderLock lm (lock);
+ return round_to_type (fr, dir, Bar);
+ }
+}
+
+
+nframes_t
+TempoMap::round_to_beat (nframes_t fr, int dir)
+{
+ {
+ Glib::RWLock::ReaderLock lm (lock);
+ return round_to_type (fr, dir, Beat);
+ }
+}
+
+nframes_t
+
+TempoMap::round_to_beat_subdivision (nframes_t fr, int sub_num)
+{
+
+ BBT_Time the_beat;
+ uint32_t ticks_one_half_subdivisions_worth;
+ uint32_t ticks_one_subdivisions_worth;
+
+ bbt_time(fr, the_beat);
+
+ ticks_one_subdivisions_worth = (uint32_t)Meter::ticks_per_beat / sub_num;
+ ticks_one_half_subdivisions_worth = ticks_one_subdivisions_worth / 2;
+
+ if (the_beat.ticks % ticks_one_subdivisions_worth > ticks_one_half_subdivisions_worth) {
+ uint32_t difference = ticks_one_subdivisions_worth - (the_beat.ticks % ticks_one_subdivisions_worth);
+ if (the_beat.ticks + difference >= (uint32_t)Meter::ticks_per_beat) {
+ the_beat.beats++;
+ the_beat.ticks += difference;
+ the_beat.ticks -= (uint32_t)Meter::ticks_per_beat;
+ } else {
+ the_beat.ticks += difference;
+ }
+ } else {
+ the_beat.ticks -= the_beat.ticks % ticks_one_subdivisions_worth;
+ }
+
+ return frame_time (the_beat);
+}
+
+nframes_t
+TempoMap::round_to_type (nframes_t frame, int dir, BBTPointType type)
+{
+ Metric metric = metric_at (frame);
+ BBT_Time bbt;
+ BBT_Time start;
+ bbt_time_with_metric (frame, bbt, metric);
+
+ switch (type) {
+ case Bar:
+ if (dir < 0) {
+ /* relax */
+ } else if (dir > 0) {
+ if (bbt.beats > 0) {
+ bbt.bars++;
+ } else if (metric.frame() < frame) {
+ bbt.bars++;
+ }
+ } else {
+ if (bbt.beats > metric.meter().beats_per_bar()/2) {
+ bbt.bars++;
+ }
+ }
+ bbt.beats = 1;
+ bbt.ticks = 0;
+ break;
+
+ case Beat:
+ if (dir < 0) {
+ /* relax */
+ } else if (dir > 0) {
+ if (bbt.ticks > 0) {
+ bbt.beats++;
+ } else if (metric.frame() < frame) {
+ bbt.beats++;
+ }
+ } else {
+ if (bbt.ticks >= (Meter::ticks_per_beat/2)) {
+ bbt.beats++;
+ }
+ }
+ if (bbt.beats > ceil(metric.meter().beats_per_bar()) ) {
+ bbt.beats = 1;
+ bbt.bars++;
+ }
+ bbt.ticks = 0;
+ break;
+
+ }
+
+ /*
+ cerr << "for " << frame << " round to " << bbt << " using "
+ << metric.start()
+ << endl;
+ */
+ return metric.frame() + count_frames_between (metric.start(), bbt);
+}
+
+TempoMap::BBTPointList *
+TempoMap::get_points (nframes_t lower, nframes_t upper) const
+{
+
+ Metrics::const_iterator i;
+ BBTPointList *points;
+ double current;
+ const MeterSection* meter;
+ const MeterSection* m;
+ const TempoSection* tempo;
+ const TempoSection* t;
+ uint32_t bar;
+ uint32_t beat;
+ double beats_per_bar;
+ double beat_frame;
+ double beat_frames;
+ double frames_per_bar;
+ double delta_bars;
+ double delta_beats;
+ double dummy;
+ nframes_t limit;
+
+ meter = &first_meter ();
+ tempo = &first_tempo ();
+
+ /* find the starting point */
+
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+
+ if ((*i)->frame() > lower) {
+ break;
+ }
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ }
+ }
+
+ /* We now have:
+
+ meter -> the Meter for "lower"
+ tempo -> the Tempo for "lower"
+ i -> for first new metric after "lower", possibly metrics->end()
+
+ Now start generating points.
+ */
+
+ beats_per_bar = meter->beats_per_bar ();
+ frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
+ beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
+
+ if (meter->frame() > tempo->frame()) {
+ bar = meter->start().bars;
+ beat = meter->start().beats;
+ current = meter->frame();
+ } else {
+ bar = tempo->start().bars;
+ beat = tempo->start().beats;
+ current = tempo->frame();
+ }
+
+ /* initialize current to point to the bar/beat just prior to the
+ lower frame bound passed in. assumes that current is initialized
+ above to be on a beat.
+ */
+
+ delta_bars = (lower-current) / frames_per_bar;
+ delta_beats = modf(delta_bars, &dummy) * beats_per_bar;
+ current += (floor(delta_bars) * frames_per_bar) + (floor(delta_beats) * beat_frames);
+
+ // adjust bars and beats too
+ bar += (uint32_t) (floor(delta_bars));
+ beat += (uint32_t) (floor(delta_beats));
+
+ points = new BBTPointList;
+
+ do {
+
+ if (i == metrics->end()) {
+ limit = upper;
+ // cerr << "== limit set to end of request @ " << limit << endl;
+ } else {
+ // cerr << "== limit set to next metric @ " << (*i)->frame() << endl;
+ limit = (*i)->frame();
+ }
+
+ limit = min (limit, upper);
+
+ while (current < limit) {
+
+ /* if we're at the start of a bar, add bar point */
+
+ 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));
+
+ }
+ }
+
+ /* add some beats if we can */
+
+ beat_frame = current;
+
+ 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;
+ current+= beat_frames;
+
+ beat++;
+ }
+
+ // cerr << "out of beats, @ end ? " << (i == metrics->end()) << " out of bpb ? "
+ // << (beat > ceil(beats_per_bar))
+ // << endl;
+
+ if (beat > ceil(beats_per_bar) || i != metrics->end()) {
+
+ /* we walked an entire bar. its
+ important to move `current' forward
+ by the actual frames_per_bar, not move it to
+ an integral beat_frame, so that metrics with
+ non-integral beats-per-bar have
+ their bar positions set
+ correctly. consider a metric with
+ 9-1/2 beats-per-bar. the bar we
+ just filled had 10 beat marks,
+ but the bar end is 1/2 beat before
+ the last beat mark.
+ And it is also possible that a tempo
+ change occured in the middle of a bar,
+ so we subtract the possible extra fraction from the current
+ */
+
+ if (beat > ceil (beats_per_bar)) {
+ /* next bar goes where the numbers suggest */
+ current -= beat_frames * (ceil(beats_per_bar)-beats_per_bar);
+ // cerr << "++ next bar from numbers\n";
+ } else {
+ /* next bar goes where the next metric is */
+ current = limit;
+ // cerr << "++ next bar at next metric\n";
+ }
+ bar++;
+ beat = 1;
+ }
+
+ }
+
+ /* if we're done, then we're done */
+
+ if (current >= upper) {
+ break;
+ }
+
+ /* i is an iterator that refers to the next metric (or none).
+ if there is a next metric, move to it, and continue.
+ */
+
+ if (i != metrics->end()) {
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ tempo = t;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ meter = m;
+ /* new MeterSection, beat always returns to 1 */
+ beat = 1;
+ }
+
+ current = (*i)->frame ();
+ // cerr << "loop around with current @ " << current << endl;
+
+ beats_per_bar = meter->beats_per_bar ();
+ frames_per_bar = meter->frames_per_bar (*tempo, _frame_rate);
+ beat_frames = tempo->frames_per_beat (_frame_rate, *meter);
+
+ ++i;
+ }
+
+ } while (1);
+
+ 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)
+{
+ Metric m (metric_at (frame));
+ return m.tempo();
+}
+
+
+const Meter&
+TempoMap::meter_at (nframes_t frame)
+{
+ Metric m (metric_at (frame));
+ return m.meter();
+}
+
+XMLNode&
+TempoMap::get_state ()
+{
+ Metrics::const_iterator i;
+ XMLNode *root = new XMLNode ("TempoMap");
+
+ {
+ Glib::RWLock::ReaderLock lm (lock);
+ for (i = metrics->begin(); i != metrics->end(); ++i) {
+ root->add_child_nocopy ((*i)->get_state());
+ }
+ }
+
+ return *root;
+}
+
+int
+TempoMap::set_state (const XMLNode& node)
+{
+ {
+ Glib::RWLock::WriterLock lm (lock);
+
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ Metrics old_metrics (*metrics);
+
+ metrics->clear();
+
+ nlist = node.children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+ XMLNode* child = *niter;
+
+ if (child->name() == TempoSection::xml_state_node_name) {
+
+ try {
+ metrics->push_back (new TempoSection (*child));
+ }
+
+ catch (failed_constructor& err){
+ error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
+ *metrics = old_metrics;
+ break;
+ }
+
+ } else if (child->name() == MeterSection::xml_state_node_name) {
+
+ try {
+ metrics->push_back (new MeterSection (*child));
+ }
+
+ catch (failed_constructor& err) {
+ error << _("Tempo map: could not set new state, restoring old one.") << endmsg;
+ *metrics = old_metrics;
+ break;
+ }
+ }
+ }
+
+ if (niter == nlist.end()) {
+
+ MetricSectionSorter cmp;
+ metrics->sort (cmp);
+ timestamp_metrics (true);
+ }
+ }
+
+ StateChanged (Change (0));
+
+ return 0;
+}
+
+void
+TempoMap::dump (std::ostream& o) const
+{
+ const MeterSection* m;
+ const TempoSection* t;
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+
+ if ((t = dynamic_cast<const TempoSection*>(*i)) != 0) {
+ o << "Tempo @ " << *i << ' ' << t->beats_per_minute() << " BPM (denom = " << t->note_type() << ") at " << t->start() << " frame= " << t->frame() << " (move? "
+ << t->movable() << ')' << endl;
+ } else if ((m = dynamic_cast<const MeterSection*>(*i)) != 0) {
+ o << "Meter @ " << *i << ' ' << m->beats_per_bar() << '/' << m->note_divisor() << " at " << m->start() << " frame= " << m->frame()
+ << " (move? " << m->movable() << ')' << endl;
+ }
+ }
+}
+
+int
+TempoMap::n_tempos() const
+{
+ Glib::RWLock::ReaderLock lm (lock);
+ int cnt = 0;
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if (dynamic_cast<const TempoSection*>(*i) != 0) {
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
+
+int
+TempoMap::n_meters() const
+{
+ Glib::RWLock::ReaderLock lm (lock);
+ int cnt = 0;
+
+ for (Metrics::const_iterator i = metrics->begin(); i != metrics->end(); ++i) {
+ if (dynamic_cast<const MeterSection*>(*i) != 0) {
+ cnt++;
+ }
+ }
+
+ return cnt;
+}
diff --git a/libs/ardour/track.cc b/libs/ardour/track.cc
new file mode 100644
index 0000000000..052105cc85
--- /dev/null
+++ b/libs/ardour/track.cc
@@ -0,0 +1,224 @@
+/*
+ Copyright (C) 2006 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+#include <pbd/error.h>
+#include <sigc++/retype.h>
+#include <sigc++/retype_return.h>
+#include <sigc++/bind.h>
+
+#include <ardour/track.h>
+#include <ardour/diskstream.h>
+#include <ardour/session.h>
+#include <ardour/io_processor.h>
+#include <ardour/audioregion.h>
+#include <ardour/audiosource.h>
+#include <ardour/route_group_specialized.h>
+#include <ardour/processor.h>
+#include <ardour/audioplaylist.h>
+#include <ardour/panner.h>
+#include <ardour/utils.h>
+
+#include "i18n.h"
+
+using namespace std;
+using namespace ARDOUR;
+using namespace PBD;
+
+Track::Track (Session& sess, string name, Route::Flag flag, TrackMode mode, DataType default_type)
+ : Route (sess, name, 1, -1, -1, -1, flag, default_type)
+ , _rec_enable_control (new RecEnableControllable(*this))
+{
+ _declickable = true;
+ _freeze_record.state = NoFreeze;
+ _saved_meter_point = _meter_point;
+ _mode = mode;
+}
+
+Track::Track (Session& sess, const XMLNode& node, DataType default_type)
+ : Route (sess, node)
+ , _rec_enable_control (new RecEnableControllable(*this))
+{
+ _freeze_record.state = NoFreeze;
+ _declickable = true;
+ _saved_meter_point = _meter_point;
+}
+
+Track::~Track ()
+{
+}
+
+void
+Track::set_meter_point (MeterPoint p, void *src)
+{
+ Route::set_meter_point (p, src);
+}
+
+XMLNode&
+Track::get_state ()
+{
+ return state (true);
+}
+
+XMLNode&
+Track::get_template ()
+{
+ return state (false);
+}
+
+void
+Track::toggle_monitor_input ()
+{
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end(); ++i) {
+ i->ensure_monitor_input(!i->monitoring_input());
+ }
+}
+
+ARDOUR::nframes_t
+Track::update_total_latency ()
+{
+ nframes_t old = _own_latency;
+
+ if (_user_latency) {
+ _own_latency = _user_latency;
+ } else {
+ _own_latency = 0;
+
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if ((*i)->active ()) {
+ _own_latency += (*i)->signal_latency ();
+ }
+ }
+ }
+
+ set_port_latency (_own_latency);
+
+ if (old != _own_latency) {
+ signal_latency_changed (); /* EMIT SIGNAL */
+ }
+
+ return _own_latency;
+}
+
+Track::FreezeRecord::~FreezeRecord ()
+{
+ for (vector<FreezeRecordProcessorInfo*>::iterator i = processor_info.begin(); i != processor_info.end(); ++i) {
+ delete *i;
+ }
+}
+
+Track::FreezeState
+Track::freeze_state() const
+{
+ return _freeze_record.state;
+}
+
+Track::RecEnableControllable::RecEnableControllable (Track& s)
+ : Controllable (X_("recenable")), track (s)
+{
+}
+
+void
+Track::RecEnableControllable::set_value (float val)
+{
+ bool bval = ((val >= 0.5f) ? true: false);
+ track.set_record_enable (bval, this);
+}
+
+float
+Track::RecEnableControllable::get_value (void) const
+{
+ if (track.record_enabled()) { return 1.0f; }
+ return 0.0f;
+}
+
+bool
+Track::record_enabled () const
+{
+ return _diskstream->record_enabled ();
+}
+
+bool
+Track::can_record()
+{
+ bool will_record = true;
+ for (PortSet::iterator i = _inputs.begin(); i != _inputs.end() && will_record; ++i) {
+ if (!i->connected())
+ will_record = false;
+ }
+
+ return will_record;
+}
+
+void
+Track::set_record_enable (bool yn, void *src)
+{
+ if (_freeze_record.state == Frozen) {
+ return;
+ }
+
+ if (_mix_group && src != _mix_group && _mix_group->is_active()) {
+ _mix_group->apply (&Track::set_record_enable, yn, _mix_group);
+ return;
+ }
+
+ /* keep track of the meter point as it was before we rec-enabled */
+ if (!_diskstream->record_enabled()) {
+ _saved_meter_point = _meter_point;
+ }
+
+ _diskstream->set_record_enabled (yn);
+
+ if (_diskstream->record_enabled()) {
+ set_meter_point (MeterInput, this);
+ } else {
+ set_meter_point (_saved_meter_point, this);
+ }
+
+ _rec_enable_control->Changed ();
+}
+
+
+bool
+Track::set_name (const string& str)
+{
+ bool ret;
+
+ if (record_enabled() && _session.actively_recording()) {
+ /* this messes things up if done while recording */
+ return false;
+ }
+
+ if (_diskstream->set_name (str)) {
+ return false;
+ }
+
+ /* save state so that the statefile fully reflects any filename changes */
+
+ if ((ret = IO::set_name (str)) == 0) {
+ _session.save_state ("");
+ }
+
+ return ret;
+}
+
+void
+Track::set_latency_delay (nframes_t longest_session_latency)
+{
+ Route::set_latency_delay (longest_session_latency);
+ _diskstream->set_roll_delay (_roll_delay);
+}
+
diff --git a/libs/ardour/transient_detector.cc b/libs/ardour/transient_detector.cc
new file mode 100644
index 0000000000..d3200d72e4
--- /dev/null
+++ b/libs/ardour/transient_detector.cc
@@ -0,0 +1,118 @@
+#include <ardour/transient_detector.h>
+
+#include "i18n.h"
+
+using namespace Vamp;
+using namespace ARDOUR;
+using namespace std;
+
+/* need a static initializer function for this */
+
+string TransientDetector::_op_id = X_("libardourvampplugins:percussiononsets:2");
+
+TransientDetector::TransientDetector (float sr)
+ : AudioAnalyser (sr, X_("libardourvampplugins:percussiononsets"))
+{
+ /* update the op_id */
+
+ _op_id = X_("libardourvampplugins:percussiononsets");
+
+ // XXX this should load the above-named plugin and get the current version
+
+ _op_id += ":2";
+}
+
+TransientDetector::~TransientDetector()
+{
+}
+
+string
+TransientDetector::operational_identifier()
+{
+ return _op_id;
+}
+
+int
+TransientDetector::run (const std::string& path, Readable* src, uint32_t channel, AnalysisFeatureList& 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);
+ }
+}
+
+void
+TransientDetector::cleanup_transients (AnalysisFeatureList& t, float sr, float gap_msecs)
+{
+ if (t.empty()) {
+ return;
+ }
+
+ t.sort ();
+
+ /* remove duplicates or other things that are too close */
+
+ AnalysisFeatureList::iterator i = t.begin();
+ AnalysisFeatureList::iterator f, b;
+ const nframes64_t gap_frames = (nframes64_t) floor (gap_msecs * (sr / 1000.0));
+
+ while (i != t.end()) {
+
+ // move front iterator to just past i, and back iterator the same place
+
+ f = i;
+ ++f;
+ b = f;
+
+ // move f until we find a new value that is far enough away
+
+ while ((f != t.end()) && (((*f) - (*i)) < gap_frames)) {
+ ++f;
+ }
+
+ i = f;
+
+ // if f moved forward from b, we had duplicates/too-close points: get rid of them
+
+ if (b != f) {
+ t.erase (b, f);
+ }
+ }
+}
diff --git a/libs/ardour/user_bundle.cc b/libs/ardour/user_bundle.cc
new file mode 100644
index 0000000000..471d823496
--- /dev/null
+++ b/libs/ardour/user_bundle.cc
@@ -0,0 +1,198 @@
+#include <cassert>
+#include <pbd/failed_constructor.h>
+#include <pbd/compose.h>
+#include <pbd/xml++.h>
+#include "ardour/user_bundle.h"
+#include "ardour/port_set.h"
+#include "ardour/io.h"
+#include "ardour/session.h"
+#include "ardour/audioengine.h"
+#include "i18n.h"
+
+ARDOUR::UserBundle::UserBundle (std::string const & n)
+ : Bundle (n)
+{
+
+}
+
+ARDOUR::UserBundle::UserBundle (XMLNode const & x, bool i)
+ : Bundle (i)
+{
+ if (set_state (x)) {
+ throw failed_constructor ();
+ }
+}
+
+uint32_t
+ARDOUR::UserBundle::nchannels () const
+{
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return _ports.size ();
+}
+
+const ARDOUR::PortList&
+ARDOUR::UserBundle::channel_ports (uint32_t n) const
+{
+ assert (n < nchannels ());
+
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return _ports[n];
+}
+
+void
+ARDOUR::UserBundle::add_port_to_channel (uint32_t c, std::string const & p)
+{
+ assert (c < nchannels ());
+
+ PortsWillChange (c);
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports[c].push_back (p);
+ }
+
+ PortsHaveChanged (c);
+}
+
+void
+ARDOUR::UserBundle::remove_port_from_channel (uint32_t c, std::string const & p)
+{
+ assert (c < nchannels ());
+
+ PortsWillChange (c);
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ PortList::iterator i = std::find (_ports[c].begin(), _ports[c].end(), p);
+ if (i != _ports[c].end()) {
+ _ports[c].erase (i);
+ }
+ }
+
+ PortsHaveChanged (c);
+}
+
+bool
+ARDOUR::UserBundle::port_attached_to_channel (uint32_t c, std::string const & p) const
+{
+ assert (c < nchannels ());
+
+ Glib::Mutex::Lock lm (_ports_mutex);
+ return std::find (_ports[c].begin(), _ports[c].end(), p) != _ports[c].end();
+}
+
+void
+ARDOUR::UserBundle::add_channel ()
+{
+ ConfigurationWillChange ();
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports.resize (_ports.size() + 1);
+ }
+
+ ConfigurationHasChanged ();
+}
+
+void
+ARDOUR::UserBundle::set_channels (uint32_t n)
+{
+ ConfigurationWillChange ();
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports.resize (n);
+ }
+
+ ConfigurationHasChanged ();
+}
+
+void
+ARDOUR::UserBundle::remove_channel (uint32_t r)
+{
+ assert (r < nchannels ());
+
+ ConfigurationWillChange ();
+
+ {
+ Glib::Mutex::Lock lm (_ports_mutex);
+ _ports.erase (_ports.begin() + r, _ports.begin() + r + 1);
+ }
+
+ ConfigurationHasChanged ();
+}
+
+int
+ARDOUR::UserBundle::set_state (XMLNode const & node)
+{
+ XMLProperty const * name;
+
+ if ((name = node.property ("name")) == 0) {
+ PBD::error << _("Node for Bundle has no \"name\" property") << endmsg;
+ return -1;
+ }
+
+ set_name (name->value ());
+
+ XMLNodeList const channels = node.children ();
+
+ int n = 0;
+ for (XMLNodeConstIterator i = channels.begin(); i != channels.end(); ++i) {
+
+ if ((*i)->name() != "Channel") {
+ PBD::error << string_compose (_("Unknown node \"%s\" in Bundle"), (*i)->name()) << endmsg;
+ return -1;
+ }
+
+ add_channel ();
+
+ XMLNodeList const ports = (*i)->children ();
+
+ for (XMLNodeConstIterator j = ports.begin(); j != ports.end(); ++j) {
+ if ((*j)->name() != "Port") {
+ PBD::error << string_compose (_("Unknown node \"%s\" in Bundle"), (*j)->name()) << endmsg;
+ return -1;
+ }
+
+ if ((name = (*j)->property ("name")) == 0) {
+ PBD::error << _("Node for Port has no \"name\" property") << endmsg;
+ return -1;
+ }
+
+ add_port_to_channel (n, name->value ());
+ }
+
+ ++n;
+ }
+
+ return 0;
+}
+
+XMLNode&
+ARDOUR::UserBundle::get_state ()
+{
+ XMLNode *node;
+
+ if (ports_are_inputs ()) {
+ node = new XMLNode ("InputBundle");
+ } else {
+ node = new XMLNode ("OutputBundle");
+ }
+
+ node->add_property ("name", name ());
+
+ for (std::vector<PortList>::iterator i = _ports.begin(); i != _ports.end(); ++i) {
+
+ XMLNode* c = new XMLNode ("Channel");
+
+ for (PortList::iterator j = i->begin(); j != i->end(); ++j) {
+ XMLNode* p = new XMLNode ("Port");
+ p->add_property ("name", *j);
+ c->add_child_nocopy (*p);
+ }
+
+ node->add_child_nocopy (*c);
+ }
+
+ return *node;
+}
diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc
new file mode 100644
index 0000000000..f30f568f9d
--- /dev/null
+++ b/libs/ardour/utils.cc
@@ -0,0 +1,529 @@
+/*
+ Copyright (C) 2000-2003 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.
+
+*/
+
+#define __STDC_FORMAT_MACROS 1
+#include <stdint.h>
+
+#include <cstdio> /* for sprintf */
+#include <cstring>
+#include <cmath>
+#include <cctype>
+#include <string>
+#include <cerrno>
+#include <iostream>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#ifdef HAVE_WORDEXP
+#include <wordexp.h>
+#endif
+
+#include <pbd/error.h>
+#include <pbd/stacktrace.h>
+#include <pbd/xml++.h>
+#include <pbd/basename.h>
+#include <ardour/utils.h>
+
+#include "i18n.h"
+
+using namespace ARDOUR;
+using namespace std;
+using namespace PBD;
+using Glib::ustring;
+
+ustring
+legalize_for_path (ustring str)
+{
+ ustring::size_type pos;
+ ustring legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: ";
+ ustring legal;
+
+ legal = str;
+ pos = 0;
+
+ while ((pos = legal.find_first_not_of (legal_chars, pos)) != string::npos) {
+ legal.replace (pos, 1, "_");
+ pos += 1;
+ }
+
+ return legal;
+}
+
+string bump_name_once(std::string name)
+{
+ string::size_type period;
+ string newname;
+
+ if ((period = name.find_last_of ('.')) == string::npos) {
+ newname = name;
+ newname += ".1";
+ } else {
+ int isnumber = 1;
+ const char *last_element = name.c_str() + period + 1;
+ for (size_t i = 0; i < strlen(last_element); i++) {
+ if (!isdigit(last_element[i])) {
+ isnumber = 0;
+ break;
+ }
+ }
+
+ errno = 0;
+ long int version = strtol (name.c_str()+period+1, (char **)NULL, 10);
+
+ if (isnumber == 0 || errno != 0) {
+ // last_element is not a number, or is too large
+ newname = name;
+ newname += ".1";
+ } else {
+ char buf[32];
+
+ snprintf (buf, sizeof(buf), "%ld", version+1);
+
+ newname = name.substr (0, period+1);
+ newname += buf;
+ }
+ }
+
+ return newname;
+
+}
+
+ostream&
+operator<< (ostream& o, const BBT_Time& bbt)
+{
+ o << bbt.bars << '|' << bbt.beats << '|' << bbt.ticks;
+ return o;
+}
+
+XMLNode *
+find_named_node (const XMLNode& node, string name)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ XMLNode* child;
+
+ nlist = node.children();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ child = *niter;
+
+ if (child->name() == name) {
+ return child;
+ }
+ }
+
+ return 0;
+}
+
+int
+cmp_nocase (const string& s, const string& s2)
+{
+ string::const_iterator p = s.begin();
+ string::const_iterator p2 = s2.begin();
+
+ while (p != s.end() && p2 != s2.end()) {
+ if (toupper(*p) != toupper(*p2)) {
+ return (toupper(*p) < toupper(*p2)) ? -1 : 1;
+ }
+ ++p;
+ ++p2;
+ }
+
+ return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1;
+}
+
+int
+touch_file (ustring path)
+{
+ int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660);
+ if (fd >= 0) {
+ close (fd);
+ return 0;
+ }
+ return 1;
+}
+
+ustring
+region_name_from_path (ustring path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one)
+{
+ path = PBD::basename_nosuffix (path);
+
+ if (strip_channels) {
+
+ /* remove any "?R", "?L" or "?[a-z]" channel identifier */
+
+ ustring::size_type len = path.length();
+
+ if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
+ (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
+
+ path = path.substr (0, path.length() - 2);
+ }
+ }
+
+ if (add_channel_suffix) {
+
+ path += '%';
+
+ if (total > 2) {
+ path += (char) ('a' + this_one);
+ } else {
+ path += (char) (this_one == 0 ? 'L' : 'R');
+ }
+ }
+
+ return path;
+}
+
+bool
+path_is_paired (ustring path, ustring& pair_base)
+{
+ ustring::size_type pos;
+
+ /* remove any leading path */
+
+ if ((pos = path.find_last_of ('/')) != string::npos) {
+ path = path.substr(pos+1);
+ }
+
+ /* remove filename suffixes etc. */
+
+ if ((pos = path.find_last_of ('.')) != string::npos) {
+ path = path.substr (0, pos);
+ }
+
+ ustring::size_type len = path.length();
+
+ /* look for possible channel identifier: "?R", "%R", ".L" etc. */
+
+ if (len > 3 && (path[len-2] == '%' || path[len-2] == '?' || path[len-2] == '.') &&
+ (path[len-1] == 'R' || path[len-1] == 'L' || (islower (path[len-1])))) {
+
+ pair_base = path.substr (0, len-2);
+ return true;
+
+ }
+
+ return false;
+}
+
+ustring
+path_expand (ustring path)
+{
+#ifdef HAVE_WORDEXP
+ /* Handle tilde and environment variable expansion in session path */
+ string ret = path;
+
+ wordexp_t expansion;
+ switch (wordexp (path.c_str(), &expansion, WRDE_NOCMD|WRDE_UNDEF)) {
+ case 0:
+ break;
+ default:
+ error << string_compose (_("illegal or badly-formed string used for path (%1)"), path) << endmsg;
+ goto out;
+ }
+
+ if (expansion.we_wordc > 1) {
+ error << string_compose (_("path (%1) is ambiguous"), path) << endmsg;
+ goto out;
+ }
+
+ ret = expansion.we_wordv[0];
+ out:
+ wordfree (&expansion);
+ return ret;
+
+#else
+ return path;
+#endif
+}
+
+#if defined(HAVE_COREAUDIO) || defined(HAVE_AUDIOUNITS)
+string
+CFStringRefToStdString(CFStringRef stringRef)
+{
+ CFIndex size =
+ CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) ,
+ kCFStringEncodingUTF8);
+ char *buf = new char[size];
+
+ std::string result;
+
+ if(CFStringGetCString(stringRef, buf, size, kCFStringEncodingUTF8)) {
+ result = buf;
+ }
+ delete [] buf;
+ return result;
+}
+#endif // HAVE_COREAUDIO
+
+void
+compute_equal_power_fades (nframes_t nframes, float* in, float* out)
+{
+ double step;
+
+ step = 1.0/nframes;
+
+ in[0] = 0.0f;
+
+ for (nframes_t i = 1; i < nframes - 1; ++i) {
+ in[i] = in[i-1] + step;
+ }
+
+ in[nframes-1] = 1.0;
+
+ const float pan_law_attenuation = -3.0f;
+ const float scale = 2.0f - 4.0f * powf (10.0f,pan_law_attenuation/20.0f);
+
+ for (nframes_t n = 0; n < nframes; ++n) {
+ float inVal = in[n];
+ float outVal = 1 - inVal;
+ out[n] = outVal * (scale * outVal + 1.0f - scale);
+ in[n] = inVal * (scale * inVal + 1.0f - scale);
+ }
+}
+
+EditMode
+string_to_edit_mode (string str)
+{
+ if (str == _("Splice Edit")) {
+ return Splice;
+ } else if (str == _("Slide Edit")) {
+ return Slide;
+ } else if (str == _("Lock Edit")) {
+ return Lock;
+ }
+ fatal << string_compose (_("programming error: unknown edit mode string \"%1\""), str) << endmsg;
+ /*NOTREACHED*/
+ return Slide;
+}
+
+const char*
+edit_mode_to_string (EditMode mode)
+{
+ switch (mode) {
+ case Slide:
+ return _("Slide Edit");
+
+ case Lock:
+ return _("Lock Edit");
+
+ default:
+ case Splice:
+ return _("Splice Edit");
+ }
+}
+
+SlaveSource
+string_to_slave_source (string str)
+{
+ if (str == _("Internal")) {
+ return None;
+ }
+
+ if (str == _("MTC")) {
+ return MTC;
+ }
+
+ if (str == _("JACK")) {
+ return JACK;
+ }
+
+ fatal << string_compose (_("programming error: unknown slave source string \"%1\""), str) << endmsg;
+ /*NOTREACHED*/
+ return None;
+}
+
+const char*
+slave_source_to_string (SlaveSource src)
+{
+ switch (src) {
+ case JACK:
+ return _("JACK");
+
+ case MTC:
+ return _("MTC");
+
+ default:
+ case None:
+ return _("Internal");
+
+ }
+}
+
+/* I don't really like hard-coding these falloff rates here
+ * Probably should use a map of some kind that could be configured
+ * These rates are db/sec.
+*/
+
+#define METER_FALLOFF_OFF 0.0f
+#define METER_FALLOFF_SLOWEST 6.6f // BBC standard
+#define METER_FALLOFF_SLOW 8.6f // BBC standard
+#define METER_FALLOFF_MEDIUM 20.0f
+#define METER_FALLOFF_FAST 32.0f
+#define METER_FALLOFF_FASTER 46.0f
+#define METER_FALLOFF_FASTEST 70.0f
+
+float
+meter_falloff_to_float (MeterFalloff falloff)
+{
+ switch (falloff) {
+ case MeterFalloffOff:
+ return METER_FALLOFF_OFF;
+ case MeterFalloffSlowest:
+ return METER_FALLOFF_SLOWEST;
+ case MeterFalloffSlow:
+ return METER_FALLOFF_SLOW;
+ case MeterFalloffMedium:
+ return METER_FALLOFF_MEDIUM;
+ case MeterFalloffFast:
+ return METER_FALLOFF_FAST;
+ case MeterFalloffFaster:
+ return METER_FALLOFF_FASTER;
+ case MeterFalloffFastest:
+ return METER_FALLOFF_FASTEST;
+ default:
+ return METER_FALLOFF_FAST;
+ }
+}
+
+MeterFalloff
+meter_falloff_from_float (float val)
+{
+ if (val == METER_FALLOFF_OFF) {
+ return MeterFalloffOff;
+ }
+ else if (val <= METER_FALLOFF_SLOWEST) {
+ return MeterFalloffSlowest;
+ }
+ else if (val <= METER_FALLOFF_SLOW) {
+ return MeterFalloffSlow;
+ }
+ else if (val <= METER_FALLOFF_MEDIUM) {
+ return MeterFalloffMedium;
+ }
+ else if (val <= METER_FALLOFF_FAST) {
+ return MeterFalloffFast;
+ }
+ else if (val <= METER_FALLOFF_FASTER) {
+ return MeterFalloffFaster;
+ }
+ else {
+ return MeterFalloffFastest;
+ }
+}
+
+float
+meter_hold_to_float (MeterHold hold)
+{
+ switch (hold) {
+ case MeterHoldOff:
+ return 0.0f;
+ case MeterHoldShort:
+ return 40.0f;
+ case MeterHoldMedium:
+ return 100.0f;
+ case MeterHoldLong:
+ default:
+ return 200.0f;
+ }
+}
+
+AutoState
+ARDOUR::string_to_auto_state (std::string str)
+{
+ if (str == X_("Off")) {
+ return Off;
+ } else if (str == X_("Play")) {
+ return Play;
+ } else if (str == X_("Write")) {
+ return Write;
+ } else if (str == X_("Touch")) {
+ return Touch;
+ }
+
+ fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState string: ", str) << endmsg;
+ /*NOTREACHED*/
+ return Touch;
+}
+
+string
+ARDOUR::auto_state_to_string (AutoState as)
+{
+ /* to be used only for XML serialization, no i18n done */
+
+ switch (as) {
+ case Off:
+ return X_("Off");
+ break;
+ case Play:
+ return X_("Play");
+ break;
+ case Write:
+ return X_("Write");
+ break;
+ case Touch:
+ return X_("Touch");
+ }
+
+ fatal << string_compose (_("programming error: %1 %2"), "illegal AutoState type: ", as) << endmsg;
+ /*NOTREACHED*/
+ return "";
+}
+
+AutoStyle
+ARDOUR::string_to_auto_style (std::string str)
+{
+ if (str == X_("Absolute")) {
+ return Absolute;
+ } else if (str == X_("Trim")) {
+ return Trim;
+ }
+
+ fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle string: ", str) << endmsg;
+ /*NOTREACHED*/
+ return Trim;
+}
+
+string
+ARDOUR::auto_style_to_string (AutoStyle as)
+{
+ /* to be used only for XML serialization, no i18n done */
+
+ switch (as) {
+ case Absolute:
+ return X_("Absolute");
+ break;
+ case Trim:
+ return X_("Trim");
+ break;
+ }
+
+ fatal << string_compose (_("programming error: %1 %2"), "illegal AutoStyle type: ", as) << endmsg;
+ /*NOTREACHED*/
+ return "";
+}
+
+extern "C" {
+ void c_stacktrace() { stacktrace (cerr); }
+}
diff --git a/libs/ardour/vst_plugin.cc b/libs/ardour/vst_plugin.cc
new file mode 100644
index 0000000000..47b5cb4fba
--- /dev/null
+++ b/libs/ardour/vst_plugin.cc
@@ -0,0 +1,512 @@
+/*
+ Copyright (C) 2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <algorithm>
+#include <vector>
+#include <string>
+#include <ctype.h>
+
+#include <cstdlib>
+#include <cstdio> // so libraptor doesn't complain
+#include <cmath>
+#include <dirent.h>
+#include <string.h> // for memmove
+#include <sys/stat.h>
+#include <cerrno>
+
+#include <lrdf.h>
+#include <fst.h>
+
+#include <pbd/compose.h>
+#include <pbd/error.h>
+#include <pbd/pathscanner.h>
+#include <pbd/xml++.h>
+
+#include <vst/aeffectx.h>
+
+#include <ardour/session.h>
+#include <ardour/audioengine.h>
+#include <ardour/filesystem_paths.h>
+#include <ardour/vst_plugin.h>
+#include <ardour/buffer_set.h>
+
+#include <pbd/stl_delete.h>
+
+#include "i18n.h"
+#include <locale.h>
+
+using namespace ARDOUR;
+using namespace PBD;
+using std::min;
+using std::max;
+
+VSTPlugin::VSTPlugin (AudioEngine& e, Session& session, FSTHandle* h)
+ : Plugin (e, session)
+{
+ handle = h;
+
+ if ((_fst = fst_instantiate (handle, Session::vst_callback, this)) == 0) {
+ throw failed_constructor();
+ }
+
+ _plugin = _fst->plugin;
+ _plugin->user = this;
+
+ /* set rate and blocksize */
+
+ _plugin->dispatcher (_plugin, effSetSampleRate, 0, 0, NULL,
+ (float) session.frame_rate());
+ _plugin->dispatcher (_plugin, effSetBlockSize, 0,
+ session.get_block_size(), NULL, 0.0f);
+
+ /* set program to zero */
+
+ _plugin->dispatcher (_plugin, effSetProgram, 0, 0, NULL, 0.0f);
+
+ Plugin::setup_controls ();
+}
+
+VSTPlugin::VSTPlugin (const VSTPlugin &other)
+ : Plugin (other)
+{
+ handle = other.handle;
+
+ if ((_fst = fst_instantiate (handle, Session::vst_callback, this)) == 0) {
+ throw failed_constructor();
+ }
+ _plugin = _fst->plugin;
+
+ Plugin::setup_controls ();
+}
+
+VSTPlugin::~VSTPlugin ()
+{
+ deactivate ();
+ GoingAway (); /* EMIT SIGNAL */
+ fst_close (_fst);
+}
+
+void
+VSTPlugin::set_block_size (nframes_t nframes)
+{
+ deactivate ();
+ _plugin->dispatcher (_plugin, effSetBlockSize, 0, nframes, NULL, 0.0f);
+ activate ();
+}
+
+float
+VSTPlugin::default_value (uint32_t port)
+{
+ return 0;
+}
+
+void
+VSTPlugin::set_parameter (uint32_t which, float val)
+{
+ _plugin->setParameter (_plugin, which, val);
+ //ParameterChanged (which, val); /* EMIT SIGNAL */
+}
+
+float
+VSTPlugin::get_parameter (uint32_t which) const
+{
+ return _plugin->getParameter (_plugin, which);
+
+}
+
+uint32_t
+VSTPlugin::nth_parameter (uint32_t n, bool& ok) const
+{
+ ok = true;
+ return n;
+}
+
+XMLNode&
+VSTPlugin::get_state()
+{
+ XMLNode *root = new XMLNode (state_node_name());
+ LocaleGuard lg (X_("POSIX"));
+
+ if (_plugin->flags & effFlagsProgramChunks) {
+
+ /* fetch the current chunk */
+
+ void* data;
+ long data_size;
+
+ if ((data_size = _plugin->dispatcher (_plugin, effGetChunk, 0, 0, &data, false)) == 0) {
+ return *root;
+ }
+
+ /* save it to a file */
+
+ string path;
+ struct stat sbuf;
+
+ sys::path user_vst_directory(user_config_directory());
+
+ user_vst_directory /= "vst";
+
+ path = user_vst_directory.to_string();
+
+ if (stat (path.c_str(), &sbuf)) {
+ if (errno == ENOENT) {
+ if (g_mkdir_with_parents (path.c_str(), 0600)) {
+ error << string_compose (_("cannot create VST chunk directory: %1"),
+ strerror (errno))
+ << endmsg;
+ return *root;
+ }
+
+ } else {
+
+ warning << string_compose (_("cannot check VST chunk directory: %1"), strerror (errno))
+ << endmsg;
+ return *root;
+ }
+
+ } else if (!S_ISDIR (sbuf.st_mode)) {
+ error << string_compose (_("%1 exists but is not a directory"), path)
+ << endmsg;
+ return *root;
+ }
+
+ path += "something";
+
+ /* store information */
+
+ XMLNode* chunk_node = new XMLNode (X_("chunk"));
+ chunk_node->add_property ("path", path);
+
+ root->add_child_nocopy (*chunk_node);
+
+ } else {
+
+ XMLNode* parameters = new XMLNode ("parameters");
+
+ for (long n = 0; n < _plugin->numParams; ++n) {
+ char index[64];
+ char val[32];
+ snprintf (index, sizeof (index), "param_%ld", n);
+ snprintf (val, sizeof (val), "%.12g", _plugin->getParameter (_plugin, n));
+ parameters->add_property (index, val);
+ }
+
+ root->add_child_nocopy (*parameters);
+ }
+
+ return *root;
+}
+
+int
+VSTPlugin::set_state(const XMLNode& node)
+{
+ LocaleGuard lg (X_("POSIX"));
+
+ if (node.name() != state_node_name()) {
+ error << _("Bad node sent to VSTPlugin::set_state") << endmsg;
+ return 0;
+ }
+
+ XMLNode* child;
+
+ if ((child = find_named_node (node, X_("chunks"))) != 0) {
+
+ return 0;
+
+ } else if ((child = find_named_node (node, X_("parameters"))) != 0) {
+
+ XMLPropertyList::const_iterator i;
+
+ for (i = child->properties().begin(); i != child->properties().end(); ++i) {
+ long param;
+ float val;
+
+ sscanf ((*i)->name().c_str(), "param_%ld", &param);
+ sscanf ((*i)->value().c_str(), "%f", &val);
+
+ _plugin->setParameter (_plugin, param, val);
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int
+VSTPlugin::get_parameter_descriptor (uint32_t which, ParameterDescriptor& desc) const
+{
+ VstParameterProperties prop;
+
+ desc.min_unbound = false;
+ desc.max_unbound = false;
+
+ if (_plugin->dispatcher (_plugin, effGetParameterProperties, which, 0, &prop, 0)) {
+
+ /* i have yet to find or hear of a VST plugin that uses this */
+
+ if (prop.flags & kVstParameterUsesIntegerMinMax) {
+ desc.lower = prop.minInteger;
+ desc.upper = prop.maxInteger;
+ } else {
+ desc.lower = 0;
+ desc.upper = 1.0;
+ }
+
+ if (prop.flags & kVstParameterUsesIntStep) {
+
+ desc.step = prop.stepInteger;
+ desc.smallstep = prop.stepInteger;
+ desc.largestep = prop.stepInteger;
+
+ } else if (prop.flags & kVstParameterUsesFloatStep) {
+
+ desc.step = prop.stepFloat;
+ desc.smallstep = prop.smallStepFloat;
+ desc.largestep = prop.largeStepFloat;
+
+ } else {
+
+ float range = desc.upper - desc.lower;
+
+ desc.step = range / 100.0f;
+ desc.smallstep = desc.step / 2.0f;
+ desc.largestep = desc.step * 10.0f;
+ }
+
+ desc.toggled = prop.flags & kVstParameterIsSwitch;
+ desc.logarithmic = false;
+ desc.sr_dependent = false;
+ desc.label = prop.label;
+
+ } else {
+
+ /* old style */
+
+ char label[64];
+ label[0] = '\0';
+
+ _plugin->dispatcher (_plugin, effGetParamName, which, 0, label, 0);
+
+ desc.label = label;
+ desc.integer_step = false;
+ desc.lower = 0.0f;
+ desc.upper = 1.0f;
+ desc.step = 0.01f;
+ desc.smallstep = 0.005f;
+ desc.largestep = 0.1f;
+ desc.toggled = false;
+ desc.logarithmic = false;
+ desc.sr_dependent = false;
+ }
+
+ return 0;
+}
+
+bool
+VSTPlugin::load_preset (string name)
+{
+ if (_plugin->flags & effFlagsProgramChunks) {
+ error << _("no support for presets using chunks at this time")
+ << endmsg;
+ return false;
+ }
+ return Plugin::load_preset (name);
+}
+
+bool
+VSTPlugin::save_preset (string name)
+{
+ if (_plugin->flags & effFlagsProgramChunks) {
+ error << _("no support for presets using chunks at this time")
+ << endmsg;
+ return false;
+ }
+ return Plugin::save_preset (name, "vst");
+}
+
+string
+VSTPlugin::describe_parameter (uint32_t param)
+{
+ char name[64];
+ _plugin->dispatcher (_plugin, effGetParamName, param, 0, name, 0);
+ return name;
+}
+
+nframes_t
+VSTPlugin::signal_latency () const
+{
+ if (_user_latency) {
+ return _user_latency;
+ }
+
+ return _plugin->initialDelay;
+}
+
+set<uint32_t>
+VSTPlugin::automatable () const
+{
+ set<uint32_t> ret;
+
+ for (uint32_t i = 0; i < parameter_count(); ++i){
+ ret.insert (ret.end(), i);
+ }
+
+ return ret;
+}
+
+int
+VSTPlugin::connect_and_run (BufferSet& bufs, uint32_t& in_index, uint32_t& out_index, nframes_t nframes, nframes_t offset)
+{
+ float *ins[_plugin->numInputs];
+ float *outs[_plugin->numOutputs];
+ int32_t i;
+
+ const uint32_t nbufs = bufs.count().n_audio();
+
+ for (i = 0; i < (int32_t) _plugin->numInputs; ++i) {
+ ins[i] = bufs.get_audio(min((uint32_t) in_index, nbufs - 1)).data() + offset;
+ in_index++;
+ }
+
+ for (i = 0; i < (int32_t) _plugin->numOutputs; ++i) {
+ outs[i] = bufs.get_audio(min((uint32_t) out_index, nbufs - 1)).data() + offset;
+
+ /* unbelievably, several VST plugins still rely on Cubase
+ behaviour and do not silence the buffer in processReplacing
+ when they have no output.
+ */
+
+ // memset (outs[i], 0, sizeof (Sample) * nframes);
+ out_index++;
+ }
+
+
+ /* we already know it can support processReplacing */
+
+ _plugin->processReplacing (_plugin, ins, outs, nframes);
+
+ return 0;
+}
+
+void
+VSTPlugin::deactivate ()
+{
+ _plugin->dispatcher (_plugin, effMainsChanged, 0, 0, NULL, 0.0f);
+}
+
+void
+VSTPlugin::activate ()
+{
+ _plugin->dispatcher (_plugin, effMainsChanged, 0, 1, NULL, 0.0f);
+}
+
+string
+VSTPlugin::unique_id() const
+{
+ char buf[32];
+ snprintf (buf, sizeof (buf), "%d", _plugin->uniqueID);
+ return string (buf);
+}
+
+
+const char *
+VSTPlugin::name () const
+{
+ return handle->name;
+}
+
+const char *
+VSTPlugin::maker () const
+{
+ return "imadeit";
+}
+
+const char *
+VSTPlugin::label () const
+{
+ return handle->name;
+}
+
+uint32_t
+VSTPlugin::parameter_count() const
+{
+ return _plugin->numParams;
+}
+
+bool
+VSTPlugin::has_editor () const
+{
+ return _plugin->flags & effFlagsHasEditor;
+}
+
+void
+VSTPlugin::print_parameter (uint32_t param, char *buf, uint32_t len) const
+{
+ char lab[9];
+ char *first_nonws;
+
+ _plugin->dispatcher (_plugin, effGetParamLabel, param, 0, lab, 0);
+ _plugin->dispatcher (_plugin, effGetParamDisplay, param, 0, buf, 0);
+
+ if (buf[0] == '\0') {
+ return;
+ }
+
+ first_nonws = buf;
+ while (*first_nonws && isspace (*first_nonws)) {
+ first_nonws++;
+ }
+ if (*first_nonws == '\0') {
+ return;
+ }
+
+ memmove (buf, first_nonws, strlen (buf) - (first_nonws - buf) + 1);
+}
+
+PluginPtr
+VSTPluginInfo::load (Session& session)
+{
+ try {
+ PluginPtr plugin;
+
+ if (Config->get_use_vst()) {
+ FSTHandle* handle;
+
+ handle = fst_load(path.c_str());
+
+ if ( (int)handle == -1) {
+ error << string_compose(_("VST: cannot load module from \"%1\""), path) << endmsg;
+ } else {
+ plugin.reset (new VSTPlugin (session.engine(), session, handle));
+ }
+ } else {
+ error << _("You asked ardour to not use any VST plugins") << endmsg;
+ return PluginPtr ((Plugin*) 0);
+ }
+
+ plugin->set_info(PluginInfoPtr(new VSTPluginInfo(*this)));
+ return plugin;
+ }
+
+ catch (failed_constructor &err) {
+ return PluginPtr ((Plugin*) 0);
+ }
+}