diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2007-10-11 22:07:47 +0000 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2007-10-11 22:07:47 +0000 |
commit | f7f9d6fdc40248b190ec9c6e1a886261d55777ae (patch) | |
tree | 080723e9dc35a66013b37acbafc67a6afa929302 | |
parent | aa1f736a651376534acaa2268b65d42a3786fff7 (diff) |
merge from 2.0-ongoing by hand, minus key binding editor
git-svn-id: svn://localhost/ardour2/trunk@2539 d708f5d6-7413-0410-9779-e7cbd77b26cf
134 files changed, 6835 insertions, 2344 deletions
diff --git a/SConstruct b/SConstruct index cddc7384c8..5f6bde5103 100644 --- a/SConstruct +++ b/SConstruct @@ -16,7 +16,7 @@ import SCons.Node.FS SConsignFile() EnsureSConsVersion(0, 96) -ardour_version = '2.1pre' +ardour_version = '3.0' subst_dict = { } @@ -30,6 +30,7 @@ opts.AddOptions( BoolOption('AUDIOUNITS', 'Compile with Apple\'s AudioUnit library. (experimental)', 0), BoolOption('CMT', 'Compile with support for CMT Additions', 1), BoolOption('COREAUDIO', 'Compile with Apple\'s CoreAudio library', 0), + BoolOption('GTKOSX', 'Compile for use with GTK-OSX, not GTK-X11', 0), BoolOption('DEBUG', 'Set to build with debugging information and no optimizations', 1), PathOption('DESTDIR', 'Set the intermediate install "prefix"', '/'), EnumOption('DIST_TARGET', 'Build target for cross compiling packagers', 'auto', allowed_values=('auto', 'i386', 'i686', 'x86_64', 'powerpc', 'tiger', 'panther', 'none' ), ignorecase=2), @@ -711,8 +712,13 @@ if env['LIBLO']: def prep_libcheck(topenv, libinfo): if topenv['DIST_TARGET'] == 'panther' or topenv['DIST_TARGET'] == 'tiger': + # + # rationale: GTK-Quartz uses jhbuild and installs to /opt/gtk by default. + # All libraries needed should be built against this location + if topenv['GTKOSX']: + libinfo.Append(CCFLAGS="-I/opt/gtk/include", LINKFLAGS="-L/opt/gtk/lib") libinfo.Append(CCFLAGS="-I/opt/local/include", LINKFLAGS="-L/opt/local/lib") - + prep_libcheck(env, env) # @@ -803,10 +809,18 @@ else: libraries['dmalloc'] = conf.Finish () # -# Audio/MIDI library (needed for MIDI, since audio is all handled via JACK) +# Audio/MIDI library (needed for MIDI, since audio is all handled via JACK. Note, however, that +# we still need ALSA & CoreAudio to discover audio devices for the engine +# dialog, regardless of what MIDI subsystem is being used) # conf = Configure(env) + +if conf.CheckCHeader('alsa/asoundlib.h'): + libraries['sysaudio'] = LibraryInfo (LIBS='asound') +elif conf.CheckCHeader('/System/Library/Frameworks/CoreAudio.framework/Versions/A/Headers/CoreAudio.h'): + libraries['sysaudio'] = LibraryInfo (LINKFLAGS= '-framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox -bind_at_load') + if conf.CheckCHeader('jack/midiport.h'): libraries['sysmidi'] = LibraryInfo (LIBS='jack') env['SYSMIDI'] = 'JACK MIDI' @@ -821,7 +835,13 @@ elif conf.CheckCHeader('alsa/asoundlib.h'): print "Using ALSA MIDI" elif conf.CheckCHeader('/System/Library/Frameworks/CoreMIDI.framework/Headers/CoreMIDI.h'): # this line is needed because scons can't handle -framework in ParseConfig() yet. - libraries['sysmidi'] = LibraryInfo (LINKFLAGS= '-framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox -bind_at_load') + if env['GTKOSX']: + # We need Carbon as well as the rest + libraries['sysmidi'] = LibraryInfo ( + LINKFLAGS = ' -framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox -framework Carbon -bind_at_load' ) + else: + libraries['sysmidi'] = LibraryInfo ( + LINKFLAGS = ' -framework CoreMIDI -framework CoreFoundation -framework CoreAudio -framework CoreServices -framework AudioUnit -framework AudioToolbox -bind_at_load' ) env['SYSMIDI'] = 'CoreMIDI' subst_dict['%MIDITAG%'] = "ardour" subst_dict['%MIDITYPE%'] = "coremidi" @@ -1148,9 +1168,14 @@ if os.path.exists('.svn'): # specbuild = env.SubstInFile ('ardour.spec','ardour.spec.in', SUBST_DICT = subst_dict) the_revision = env.Command ('frobnicatory_decoy', [], create_stored_revision) +remove_ardour = env.Command ('frobnicatory_decoy2', [], + [ Delete ('$PREFIX/etc/ardour2'), + Delete ('$PREFIX/lib/ardour2'), + Delete ('$PREFIX/bin/ardour2')]) env.Alias('revision', the_revision) env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour_system.rc')) +env.Alias('uninstall', remove_ardour) Default (sysrcbuild) diff --git a/gtk2_ardour/SConscript b/gtk2_ardour/SConscript index b96498602e..12e3225f89 100644 --- a/gtk2_ardour/SConscript +++ b/gtk2_ardour/SConscript @@ -13,7 +13,7 @@ gtkmmtests = env.Copy() # this defines the version number of the GTK interface to ardour # -domain = 'gtk_ardour' +domain = 'gtk2_ardour' gtkardour.Append(DOMAIN=domain, MAJOR=1,MINOR=0,MICRO=2) gtkardour.Append(CCFLAGS="-DPACKAGE=\\\"" + domain + "\\\"") @@ -24,6 +24,9 @@ gtkardour.Append(CPPPATH="#/") # for top level svn_revision.h gtkardour.Append(PACKAGE=domain) gtkardour.Append(POTFILE=domain + '.pot') +if gtkardour['DIST_TARGET'] == 'panther' or gtkardour['DIST_TARGET'] == 'tiger': + gtkardour.Append (LINKFLAGS="-Xlinker -headerpad -Xlinker 2048") + gtkardour.Merge ([ libraries['ardour'], libraries['ardour_cp'], @@ -48,7 +51,8 @@ gtkardour.Merge ([ libraries['xslt'], libraries['soundtouch'], libraries['samplerate'], - libraries['jack'] + libraries['jack'], + libraries['sysaudio'] ]) gtkmmtests.Append(CXXFLAGS="-DLIBSIGC_DISABLE_DEPRECATED") @@ -80,6 +84,15 @@ audiounit_files=Split(""" au_pluginui.cc """) +gtkosx_files=Split(""" +sync-menu.c +cocoacarbon.c +""") + +x11_files=Split(""" +x11.cc +""") + gtkardour_files=Split(""" about.cc actions.cc @@ -143,6 +156,7 @@ editor_selection.cc editor_selection_list.cc editor_tempodisplay.cc editor_timefx.cc +engine_dialog.cc export_dialog.cc export_session_dialog.cc export_region_dialog.cc @@ -258,6 +272,12 @@ if env['CMT']: extra_sources += cmt_files gtkardour.Append (CCFLAGS="-DWITH_CMT") +if gtkardour['GTKOSX']: + extra_sources += gtkosx_files + gtkardour.Append (CCFLAGS="-DTOP_MENUBAR -DGTKOSX") +else: + extra_sources += x11_files + if gtkardour['AUDIOUNITS']: extra_sources += audiounit_files gtkardour.Append(CCFLAGS='-DHAVE_AUDIOUNITS') @@ -292,41 +312,43 @@ if gtkardour['DIST_TARGET'] == 'panther' or gtkardour['DIST_TARGET'] == 'tiger': # # OS X font rendering is different even with X11 # - my_font_dict['%FONT_TINY%'] = 'sans 7' - my_font_dict['%FONT_SMALLER%'] = 'sans 9' - my_font_dict['%FONT_SMALL%'] = 'sans 10' - my_font_dict['%FONT_NORMAL%'] = 'sans 11' - my_font_dict['%FONT_BIG%'] = 'sans 15' - my_font_dict['%FONT_BIGGER%'] = 'sans 16' - my_font_dict['%FONT_LARGE%'] = 'sans 20' - my_font_dict['%FONT_LARGER%'] = 'sans 28' - my_font_dict['%FONT_HUGER%'] = 'sans 36' - my_font_dict['%FONT_MASSIVE%'] = 'sans 60' - my_font_dict['%FONT_BOLD_TINY%'] = 'sans bold 7' - my_font_dict['%FONT_BOLD_SMALLER%'] = 'sans bold 9' - my_font_dict['%FONT_BOLD_SMALL%'] = 'sans bold 10' - my_font_dict['%FONT_BOLD_NORMAL%'] = 'sans bold 11' - my_font_dict['%FONT_BOLD_BIG%'] = 'sans bold 15' - my_font_dict['%FONT_BOLD_BIGGER%'] = 'sans bold 16' - my_font_dict['%FONT_BOLD_LARGE%'] = 'sans bold 20' - my_font_dict['%FONT_BOLD_LARGER%'] = 'sans bold 28' - my_font_dict['%FONT_BOLD_HUGER%'] = 'sans bold 36' - my_font_dict['%FONT_BOLD_MASSIVE%'] = 'sans bold 60' - my_font_dict['%FONT_ITALIC_TINY%'] = 'sans italic 7' - my_font_dict['%FONT_ITALIC_SMALLER%'] = 'sans italic 9' - my_font_dict['%FONT_ITALIC_SMALL%'] = 'sans italic 10' - my_font_dict['%FONT_ITALIC_NORMAL%'] = 'sans italic 11' - my_font_dict['%FONT_ITALIC_BIG%'] = 'sans italic 15' - my_font_dict['%FONT_ITALIC_BIGGER%'] = 'sans italic 16' - my_font_dict['%FONT_ITALIC_LARGE%'] = 'sans italic 20' - my_font_dict['%FONT_ITALIC_LARGER%'] = 'sans italic 28' - my_font_dict['%FONT_ITALIC_HUGER%'] = 'sans italic 36' - my_font_dict['%FONT_ITALIC_MASSIVE%'] = 'sans italic 60' + my_font_dict['%FONT_TINY%'] = 'Lucida Grande 7' + my_font_dict['%FONT_SMALLERER%'] = 'Lucida Grande 8' + my_font_dict['%FONT_SMALLER%'] = 'Lucida Grande 9' + my_font_dict['%FONT_SMALL%'] = 'Lucida Grande 10' + my_font_dict['%FONT_NORMAL%'] = 'Lucida Grande 11' + my_font_dict['%FONT_BIG%'] = 'Lucida Grande 12' + my_font_dict['%FONT_BIGGER%'] = 'Lucida Grande 14' + my_font_dict['%FONT_LARGE%'] = 'Lucida Grande 18' + my_font_dict['%FONT_LARGER%'] = 'Lucida Grande 28' + my_font_dict['%FONT_HUGER%'] = 'Lucida Grande 36' + my_font_dict['%FONT_MASSIVE%'] = 'Lucida Grande 60' + my_font_dict['%FONT_BOLD_TINY%'] = 'Lucida Grande bold 7' + my_font_dict['%FONT_BOLD_SMALLER%'] = 'Lucida Grande bold 9' + my_font_dict['%FONT_BOLD_SMALL%'] = 'Lucida Grande bold 10' + my_font_dict['%FONT_BOLD_NORMAL%'] = 'Lucida Grande bold 11' + my_font_dict['%FONT_BOLD_BIG%'] = 'Lucida Grande bold 13' + my_font_dict['%FONT_BOLD_BIGGER%'] = 'Lucida Grande bold 14' + my_font_dict['%FONT_BOLD_LARGE%'] = 'Lucida Grande bold 20' + my_font_dict['%FONT_BOLD_LARGER%'] = 'Lucida Grande bold 25' + my_font_dict['%FONT_BOLD_HUGER%'] = 'Lucida Grande bold 36' + my_font_dict['%FONT_BOLD_MASSIVE%'] = 'Lucida Grande bold 60' + my_font_dict['%FONT_ITALIC_TINY%'] = 'Lucida Grande italic 7' + my_font_dict['%FONT_ITALIC_SMALLER%'] = 'Lucida Grande italic 9' + my_font_dict['%FONT_ITALIC_SMALL%'] = 'Lucida Grande italic 10' + my_font_dict['%FONT_ITALIC_NORMAL%'] = 'Lucida Grande italic 11' + my_font_dict['%FONT_ITALIC_BIG%'] = 'Lucida Grande italic 15' + my_font_dict['%FONT_ITALIC_BIGGER%'] = 'Lucida Grande italic 16' + my_font_dict['%FONT_ITALIC_LARGE%'] = 'Lucida Grande italic 20' + my_font_dict['%FONT_ITALIC_LARGER%'] = 'Lucida Grande italic 28' + my_font_dict['%FONT_ITALIC_HUGER%'] = 'Lucida Grande italic 36' + my_font_dict['%FONT_ITALIC_MASSIVE%'] = 'Lucida Grande italic 60' else: # # Linux/X11 font rendering # my_font_dict['%FONT_TINY%'] = 'sans 4' + my_font_dict['%FONT_SMALLERER%'] = 'sans 6' my_font_dict['%FONT_SMALLER%'] = 'sans 6' my_font_dict['%FONT_SMALL%'] = 'sans 7' my_font_dict['%FONT_NORMAL%'] = 'sans 8' @@ -411,6 +433,7 @@ if env['NLS']: env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), ardour_dark_theme)) env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), ardour_light_theme)) env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour.menus')) +env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour-sae.menus')) env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour.bindings')) env.Alias('install', env.Install(os.path.join(config_prefix, 'ardour2'), 'ardour2_ui_default.conf')) # data files @@ -428,7 +451,9 @@ env.Alias ('tarball', env.Distribute (env['DISTTREE'], 'ardev_common.sh.in', 'ardev', 'ardbg', 'ardour2_ui_dark.rc', 'ardour2_ui_light.rc', 'splash.png', - 'ardour.menus', 'ardour.bindings.in', 'ardour2_ui_default.conf', + 'ardour.menus', + 'ardour-sae.menus', + 'ardour.bindings.in', 'ardour2_ui_default.conf', 'editor_xpms' ] + gtkardour_files + @@ -437,6 +462,8 @@ env.Alias ('tarball', env.Distribute (env['DISTTREE'], icon_files + skipped_files + audiounit_files + + gtkosx_files + + x11_files + fft_analysis_files + glob.glob('po/*.po') + glob.glob('*.h'))) diff --git a/gtk2_ardour/ardbg b/gtk2_ardour/ardbg index ab99296f45..063b2b1b6d 100755 --- a/gtk2_ardour/ardbg +++ b/gtk2_ardour/ardbg @@ -2,4 +2,4 @@ dir=`dirname "$0"` . $dir/ardev_common.sh LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH -exec gdb $EXECUTABLE $* +exec gdb $EXECUTABLE "$@" diff --git a/gtk2_ardour/ardev b/gtk2_ardour/ardev index 5dd8fc9d13..7980c43d93 100755 --- a/gtk2_ardour/ardev +++ b/gtk2_ardour/ardev @@ -1,4 +1,4 @@ #!/bin/sh . `dirname "$0"`/ardev_common.sh LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH -exec $EXECUTABLE $* +exec $EXECUTABLE "$@" diff --git a/gtk2_ardour/ardour.menus b/gtk2_ardour/ardour.menus index 24adba9f7f..128ca81c27 100644 --- a/gtk2_ardour/ardour.menus +++ b/gtk2_ardour/ardour.menus @@ -13,13 +13,7 @@ <separator/> <menuitem action='AddTrackBus'/> <separator/> - <menu action='addExistingAudioFiles'> - <menuitem action='addExternalAudioAsRegion'/> - <menuitem action='addExternalAudioToTrack'/> - <separator/> - <menuitem action='addExternalAudioAsTrack'/> - <menuitem action='addExternalAudioAsTapeTrack'/> - </menu> + <menuitem action='addExistingAudioFiles'/> <separator/> <menu name='Export' action='Export'> <menuitem action='ExportSession'/> @@ -140,7 +134,6 @@ <menuitem action='set-mouse-mode-gain'/> <menuitem action='set-mouse-mode-zoom'/> <menuitem action='set-mouse-mode-timefx'/> - <menuitem action='set-mouse-mode-note'/> </menu> </menu> <menu name='View' action = 'View'> @@ -215,7 +208,7 @@ <menuitem action='ToggleMeasureVisibility'/> <separator/> <menuitem action='show-editor-mixer'/> - <menuitem action='show-editor-list'/> + <menuitem action='SyncEditorAndMixerTrackOrder'/> </menu> <menu name='JACK' action='JACK'> <menuitem action='JACKDisconnect'/> @@ -304,10 +297,6 @@ <menuitem action='UseSoftwareMonitoring'/> <menuitem action='UseExternalMonitoring'/> </menu> - <menu action='Plugins'> - <menuitem action='DisableAllPlugins'/> - <menuitem action='ABAllPlugins'/> - </menu> <menu action='Metering'> <menu action='MeteringFallOffRate'> <menuitem action='MeterFalloffOff'/> @@ -370,13 +359,15 @@ <menuitem action='GainReduceFastTransport'/> <menuitem action='PrimaryClockDeltaEditCursor'/> <menuitem action='SecondaryClockDeltaEditCursor'/> + <menuitem action='OnlyCopyImportedFiles'/> + <separator/> </menu> <menu name='Help' action='Help'> <menuitem action='About'/> </menu> </menubar> - <popup name='processormenu'> + <popup name='redirectmenu'> <menuitem action='newplugin'/> <menuitem action='newinsert'/> <menuitem action='newsend'/> @@ -399,9 +390,6 @@ <menuitem action='activate_all'/> <menuitem action='deactivate_all'/> <separator/> - <menuitem action='deactivate_plugins'/> - <menuitem action='a_b_plugins'/> - <separator/> <menuitem action='edit'/> </popup> diff --git a/gtk2_ardour/ardour.sh.in b/gtk2_ardour/ardour.sh.in index beed436cbe..2f1092c3fe 100644 --- a/gtk2_ardour/ardour.sh.in +++ b/gtk2_ardour/ardour.sh.in @@ -15,6 +15,6 @@ fi ulimit -c unlimited -exec %INSTALL_PREFIX%/%LIBDIR%/ardour2/ardour-%VERSION% $* +exec %INSTALL_PREFIX%/%LIBDIR%/ardour2/ardour-%VERSION% "$@" diff --git a/gtk2_ardour/ardour2_ui_dark.rc.in b/gtk2_ardour/ardour2_ui_dark.rc.in index 1420fc8660..ba8e5fe1e1 100644 --- a/gtk2_ardour/ardour2_ui_dark.rc.in +++ b/gtk2_ardour/ardour2_ui_dark.rc.in @@ -4,17 +4,17 @@ style "very_small_text" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" } style "small_text" { - font_name = "%FONT_NORMAL%" + font_name = "%FONT_SMALL%" } style "small_bold_text" { - font_name = "%FONT_BOLD_NORMAL%" + font_name = "%FONT_BOLD_SMALL%" } style "medium_bold_text" @@ -73,7 +73,7 @@ style "verbose_canvas_cursor" style "marker_text" { - font_name = "%FONT_NORMAL%" + font_name = "%FONT_SMALL%" } style "time_axis_view_item_name" @@ -159,7 +159,7 @@ style "default_buttons_menus" style "very_small_button" = "default_buttons_menus" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" ythickness = 0 xthickness = 0 } @@ -220,21 +220,21 @@ style "track_rec_enable_button_alternate" = "small_button" style "mixer_track_rec_enable_button" = "track_rec_enable_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } style "mixer_track_rec_enable_button_alternate" = "track_rec_enable_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } style "mixer_track_rec_enable_button_active" = "track_rec_enable_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } @@ -272,20 +272,20 @@ style "solo_button_active" = "small_button" style "mixer_solo_button" = "solo_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_solo_button_alternate" = "solo_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_solo_button_active" = "solo_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -323,21 +323,21 @@ style "mute_button_active" = "small_button" style "mixer_mute_button" = "mute_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_mute_button_alternate" = "mute_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_mute_button_active" = "mute_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -351,7 +351,7 @@ style "multiline_combo" = "small_button" style "mixer_mute_button" = "mute_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } @@ -454,7 +454,7 @@ style "ardour_progressbars" = "default_buttons_menus" bg[PRELIGHT] = { 0.00, 0.36, 0.40 } } -style "options_window" = "default_base" +style "preferences" = "default_base" { font_name = "%FONT_NORMAL%" fg[PRELIGHT] = { 0.80, 0.80, 0.80 } @@ -667,7 +667,7 @@ style "transport_clock_display_delta" = "transport_clock_display" style "tempo_meter_clock_display" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" fg[NORMAL] = { 1.0, 1.0, 1.0 } fg[ACTIVE] = { 1.0, 1.0, 0.0 } fg[SELECTED] = { 1.0, 0, 0 } @@ -694,31 +694,6 @@ style "editor_time_ruler" = "small_text" bg[NORMAL] = { 0.09, 0.09, 0.09 } } -style "time_bar_label_base" = "default_base" -{ - font_name = "sans 6" - fg[NORMAL] = { 0.77, 0.77, 0.72 } - bg[NORMAL] = { 0.28, 0.29, 0.34 } - bg[ACTIVE] = { 0.30, 0.30, 0.34 } - bg[PRELIGHT] = { 0.30, 0.30, 0.34 } - bg[INSENSITIVE] = { 0.30, 0.30, 0.34 } - bg[SELECTED] = { 0.30, 0.30, 0.34 } -} - -style "time_bar_padding" = "default_base" -{ - fg[NORMAL] = { 0.09, 0.09, 0.09 } - fg[ACTIVE] = { 0.09, 0.09, 0.09 } - fg[PRELIGHT] = { 0.09, 0.09, 0.09 } - fg[INSENSITIVE] = { 0.09, 0.09, 0.09 } - fg[SELECTED] = { 0.09, 0.09, 0.09 } - bg[NORMAL] = { 0.09, 0.09, 0.09 } - bg[ACTIVE] = { 0.09, 0.09, 0.09 } - bg[PRELIGHT] = { 0.09, 0.09, 0.09 } - bg[INSENSITIVE] = { 0.09, 0.09, 0.09 } - bg[SELECTED] = { 0.09, 0.09, 0.09 } -} - style "audio_bus_base" { font_name = "%FONT_SMALLER%" @@ -758,6 +733,38 @@ style "midi_track_base" = "default_base" bg[SELECTED] = { 0.20, 0.20, 0.20 } } +style "track_controls_inactive" +{ + bg[NORMAL] = { 0.60, 0.60, 0.66 } + bg[ACTIVE] = { 0.60, 0.60, 0.66 } + bg[INSENSITIVE] = { 0.60, 0.60, 0.66 } + bg[SELECTED] = { 0.60, 0.60, 0.66 } + bg[PRELIGHT] = { 0.60, 0.60, 0.66 } + + #font_name = "sans 18" + fg[NORMAL] = { 0.7, 0.8, 0.2 } +} + +style "audio_track_metrics" = "audio_track_base" +{ + font_name = "%FONT_TINY%" +} + +style "audio_bus_metrics" = "audio_bus_base" +{ + font_name = "%FONT_TINY%" +} + +style "audio_track_metrics_inactive" = "track_controls_inactive" +{ + font_name = "%FONT_TINY%" +} + +style "audio_bus_metrics_inactive" = "track_controls_inactive" +{ + font_name = "%FONT_TINY%" +} + style "track_name_display" { font_name = "%FONT_NORMAL%" @@ -864,11 +871,11 @@ style "treeview_display" = "small_bold_text" style "main_canvas_area" { - bg[NORMAL] = { 0.40, 0.40, 0.44 } - bg[ACTIVE] = { 0.40, 0.40, 0.44 } - bg[INSENSITIVE] = { 0.40, 0.40, 0.44 } - bg[SELECTED] = { 0.40, 0.40, 0.44 } - bg[PRELIGHT] = { 0.40, 0.40, 0.44 } + bg[NORMAL] = { 0.30, 0.30, 0.34 } + bg[ACTIVE] = { 0.30, 0.30, 0.34 } + bg[INSENSITIVE] = { 0.30, 0.30, 0.34 } + bg[SELECTED] = { 0.30, 0.30, 0.34 } + bg[PRELIGHT] = { 0.30, 0.30, 0.34 } } style "track_controls_inactive" @@ -1123,12 +1130,6 @@ style "tearoff_arrow" = "medium_bold_entry" bg[PRELIGHT] = { 0.80, 0.80, 0.80 } } -style "meter_metrics_strip" = "default_base" -{ - font_name = "%FONT_TINY%" - fg[NORMAL] = { 1.0, 0.8, 0.2 } -} - style "location_row_button" = "default_buttons_menus" { font_name = "%FONT_BIG%" @@ -1234,15 +1235,15 @@ widget "*MixerMonitorInputButton.*" style:highest "very_small_button" widget "*MixerIOButton" style:highest "very_small_button" widget "*MixerIOButtonLabel" style:highest "very_small_button" widget "*AddRouteDialogSpinner" style:highest "ardour_adjusters" -widget "*AddRouteDialogRadioButton*" style:highest "options_window" -widget "*OptionsNotebook" style:highest "options_window" -widget "*OptionEditorToggleButton*" style:highest "options_window" -widget "*OptionsLabel" style:highest "options_window" -widget "*OptionEditorAuditionerLabel" style:highest "options_window" +widget "*AddRouteDialogRadioButton*" style:highest "preferences" +widget "*OptionsNotebook" style:highest "preferences" +widget "*OptionEditorToggleButton*" style:highest "preferences" +widget "*OptionsLabel" style:highest "preferences" +widget "*OptionEditorAuditionerLabel" style:highest "preferences" widget "*OptionsEntry" style:highest "option_entry" -widget "*InspectorNotebook" style:highest "options_window" -widget "*NewSessionDialog" style:highest "options_window" -widget "*NewSessionDialogButton*" style:highest "options_window" +widget "*InspectorNotebook" style:highest "preferences" +widget "*NewSessionDialog" style:highest "preferences" +widget "*NewSessionDialogButton*" style:highest "preferences" widget "*MixerSendSwitch*" style:highest "very_small_red_active_and_selected_button" widget "*OptionEditorToggleButton" style:highest "small_red_active_and_selected_button" widget "*NewSessionDialogButton" style:highest "small_red_active_and_selected_button" @@ -1346,15 +1347,24 @@ widget "*BBTRuler" style:highest "editor_time_ruler" widget "*FramesRuler" style:highest "editor_time_ruler" widget "*MinSecRuler" style:highest "editor_time_ruler" widget "*BaseFrame" style:highest "base_frame" + widget "*AudioTrackStripBase" style:highest "audio_track_base" +widget "*AudioBusStripBase" style:highest "audio_bus_base" widget "*MidiTrackStripBase" style:highest "midi_track_base" -widget "*TimebarLabelBase" style:highest "time_bar_label_base" -widget "*TimebarPadding" style:highest "time_bar_padding" +widget "*AudioTrackStripBaseInactive" style:highest "track_controls_inactive" +widget "*AudioBusStripBaseInactive" style:highest "track_controls_inactive" +widget "*MidiTrackStripBaseInactive" style:highest "track_controls_inactive" +widget "*FaderMetricsStrip" style:highest "audio_track_metrics" +widget "*AudioTrackMetrics" style:highest "audio_track_metrics" +widget "*AudioBusMetrics" style:highest "audio_bus_metrics" +widget "*AudioTrackMetricsInactive" style:highest "audio_track_metrics_inactive" +widget "*AudioBusMetricsInactive" style:highest "audio_bus_metrics_inactive" + widget "*TimeAxisViewControlsBaseUnselected" style:highest "audio_track_base" widget "*AudioTrackControlsBaseUnselected" style:highest "audio_track_base" widget "*MidiTrackControlsBaseUnselected" style:highest "midi_track_base" widget "*AudioTrackFader" style:highest "gain_fader" -widget "*AudioBusStripBase" style:highest "audio_bus_base" + widget "*BusControlsBaseUnselected" style:highest "audio_bus_base" widget "*AudioBusFader" style:highest "gain_fader" widget "*TrackSeparator" style:highest "track_separator" @@ -1386,9 +1396,6 @@ widget "*AutomationTrackName" style:highest "automation_track_name" widget "*AudioTrackControlsBaseInactiveSelected" style:highest "track_controls_inactive" widget "*BusControlsBaseInactiveSelected" style:highest "track_controls_inactive" widget "*AutomationTrackControlsBaseInactiveSelected" style:highest "track_controls_inactive" -widget "*AudioTrackStripBaseInactive" style:highest "track_controls_inactive" -widget "*MidiTrackStripBaseInactive" style:highest "track_controls_inactive" -widget "*AudioBusStripBaseInactive" style:highest "track_controls_inactive" widget "*AudioTrackControlsBaseSelected" style:highest "edit_controls_base_selected" widget "*MidiTrackControlsBaseSelected" style:highest "edit_controls_base_selected" widget "*BusControlsBaseSelected" style:highest "edit_controls_base_selected" @@ -1485,8 +1492,7 @@ widget "*PluginSaveButton" style:highest "small_button" widget "*PluginSaveButton*" style:highest "small_button" widget "*PluginLoadButton" style:highest "small_button" widget "*PluginLoadButton*" style:highest "small_button" -widget "*FaderMetricsStrip" style:highest "meter_metrics_strip" -widget "*MeterMetricsStrip" style:highest "meter_metrics_strip" + widget "*MetricDialogFrame" style:highest "base_frame" widget "*MetricEntry" style:highest "medium_bold_entry" widget "*MetricButton" style:highest "default_buttons_menus" diff --git a/gtk2_ardour/ardour2_ui_default.conf b/gtk2_ardour/ardour2_ui_default.conf index e0d6ea2773..034dd75b0f 100644 --- a/gtk2_ardour/ardour2_ui_default.conf +++ b/gtk2_ardour/ardour2_ui_default.conf @@ -3,21 +3,21 @@ <Canvas> <Option name="waveform" value="000000cc"/> <Option name="clipped waveform" value="ff0000e5"/> - <Option name="region base" value="bfbfc172"/> - <Option name="selected region base" value="b591a8d0"/> - <Option name="audio track base" value="b6c3cb88"/> - <Option name="audio bus base" value="cbc1de78"/> - <Option name="midi track base" value="ffd0d850"/> - <Option name="midi bus base" value="ffceea40"/> + <Option name="region base" value="bfbfc1aa"/> + <Option name="selected region base" value="b591a8ff"/> + <Option name="audio track base" value="c6d3d868"/> + <Option name="audio bus base" value="dbd1ea68"/> + <Option name="midi track base" value="ff8f8f3d"/> + <Option name="midi bus base" value="ff0000ee"/> <Option name="time-stretch-fill" value="e2b5b596"/> <Option name="time-stretch-outline" value="63636396"/> <Option name="automation line" value="44bc59ff"/> <Option name="processor automation line" value="7aa3f9ff"/> - <Option name="control point fill" value="ffffff66"/> - <Option name="control point outline" value="ffffffaa"/> + <Option name="control point fill" value="000000ff"/> + <Option name="control point outline" value="000000ff"/> <Option name="entered control point outline" value="ff0000ee"/> <Option name="entered control point selected" value="ff3535ff"/> - <Option name="entered control point" value="ffffffaa"/> + <Option name="entered control point" value="000000cc"/> <Option name="control point selected" value="00ff00ff"/> <Option name="control point" value="ff0000ff"/> <Option name="automation track fill" value="a0a0ce68"/> @@ -40,7 +40,7 @@ <Option name="location punch" value="7c3a3aff"/> <Option name="verbose canvas cursor" value="f4f214bc"/> <Option name="marker label" value="000000ff"/> - <Option name="marker bar separator" value="aaaaaa77"/> + <Option name="marker bar separator" value="30303088"/> <Option name="tempo bar" value="72727fff"/> <Option name="meterbar" value="666672ff"/> <Option name="markerbar" value="7f7f8cff"/> @@ -60,8 +60,8 @@ <Option name="EnteredMarker" value="dd6363ff"/> <Option name="MeterMarker" value="f2425bff"/> <Option name="TempoMarker" value="f2425bff"/> - <Option name="MeasureLineBeat" value="b5b5b576"/> - <Option name="MeasureLineBar" value="d9d9d99c"/> + <Option name="MeasureLineBeat" value="72727266"/> + <Option name="MeasureLineBar" value="8c8c988c"/> <Option name="GhostTrackBase" value="44007c7f"/> <Option name="GhostTrackWave" value="02fd004c"/> <Option name="GhostTrackWaveClip" value="ff000000"/> @@ -74,8 +74,8 @@ <Option name="RecordingRect" value="e5c6c6ff"/> <Option name="SelectionRect" value="e8f4d377"/> <Option name="Selection" value="636363b2"/> - <Option name="VestigialFrame" value="0000000f"/> - <Option name="TimeAxisFrame" value="0000000f"/> + <Option name="VestigialFrame" value="44007c0f"/> + <Option name="TimeAxisFrame" value="44007c0f"/> <Option name="NameHighlightFill" value="0000ffff"/> <Option name="NameHighlightOutline" value="7c00ff96"/> <Option name="FrameHandle" value="7c00ff96"/> @@ -83,15 +83,6 @@ <Option name="TrimHandle" value="1900ff44"/> <Option name="EditCursor" value="0000ffff"/> <Option name="PlayHead" value="ff0000ff"/> - <Option name="MidiSelectRectOutline" value="5555ffff"/> - <Option name="MidiSelectRectFill" value="8888ff88"/> - <Option name="MidiNoteOutlineMin" value="22ff22b0"/> - <Option name="MidiNoteOutlineMid" value="ffff22b0"/> - <Option name="MidiNoteOutlineMax" value="ff2222b0"/> - <Option name="MidiNoteFillMin" value="33ee338a"/> - <Option name="MidiNoteFillMid" value="eeee338a"/> - <Option name="MidiNoteFillMax" value="ee33338a"/> - <Option name="MidiNoteSelectedOutline" value="5566ffee"/> </Canvas> </Ardour> diff --git a/gtk2_ardour/ardour2_ui_light.rc.in b/gtk2_ardour/ardour2_ui_light.rc.in index 4172b73a5b..b8c3683e91 100644 --- a/gtk2_ardour/ardour2_ui_light.rc.in +++ b/gtk2_ardour/ardour2_ui_light.rc.in @@ -4,17 +4,17 @@ style "very_small_text" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" } style "small_text" { - font_name = "%FONT_NORMAL%" + font_name = "%FONT_SMALL%" } style "small_bold_text" { - font_name = "%FONT_BOLD_NORMAL%" + font_name = "%FONT_BOLD_SMALL%" } style "medium_bold_text" @@ -162,7 +162,7 @@ style "default_buttons_menus" style "very_small_button" = "default_buttons_menus" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" ythickness = 0 xthickness = 0 } @@ -223,21 +223,21 @@ style "track_rec_enable_button_alternate" = "small_button" style "mixer_track_rec_enable_button" = "track_rec_enable_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } style "mixer_track_rec_enable_button_alternate" = "track_rec_enable_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } style "mixer_track_rec_enable_button_active" = "track_rec_enable_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" xthickness = 0 ythickness = 0 } @@ -275,20 +275,20 @@ style "solo_button_active" = "small_button" style "mixer_solo_button" = "solo_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_solo_button_alternate" = "solo_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_solo_button_active" = "solo_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -326,21 +326,21 @@ style "mute_button_active" = "small_button" style "mixer_mute_button" = "mute_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_mute_button_alternate" = "mute_button_alternate" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } style "mixer_mute_button_active" = "mute_button_active" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -354,7 +354,7 @@ style "multiline_combo" = "small_button" style "mixer_mute_button" = "mute_button" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLERER%" xthickness = 0 ythickness = 0 } @@ -457,7 +457,7 @@ style "ardour_progressbars" = "default_buttons_menus" bg[PRELIGHT] = { 0.00, 0.36, 0.40 } } -style "options_window" = "default_base" +style "preferences" = "default_base" { font_name = "%FONT_NORMAL%" fg[PRELIGHT] = { 0.80, 0.80, 0.80 } @@ -672,7 +672,7 @@ style "transport_clock_display_delta" = "transport_clock_display" style "tempo_meter_clock_display" { - font_name = "%FONT_SMALL%" + font_name = "%FONT_SMALLER%" fg[NORMAL] = { 1.0, 1.0, 1.0 } fg[ACTIVE] = { 1.0, 1.0, 0.0 } fg[SELECTED] = { 1.0, 0, 0 } @@ -737,6 +737,38 @@ style "midi_track_base" = "default_base" bg[SELECTED] = { 0.70, 0.70, 0.80 } } +style "track_controls_inactive" +{ + bg[NORMAL] = { 0.60, 0.60, 0.66 } + bg[ACTIVE] = { 0.60, 0.60, 0.66 } + bg[INSENSITIVE] = { 0.60, 0.60, 0.66 } + bg[SELECTED] = { 0.60, 0.60, 0.66 } + bg[PRELIGHT] = { 0.60, 0.60, 0.66 } + + #font_name = "sans 18" + fg[NORMAL] = { 0.7, 0.8, 0.2 } +} + +style "audio_track_metrics" = "audio_track_base" +{ + font_name = "%FONT_TINY%" +} + +style "audio_bus_metrics" = "audio_bus_base" +{ + font_name = "%FONT_TINY%" +} + +style "audio_track_metrics_inactive" = "track_controls_inactive" +{ + font_name = "%FONT_TINY%" +} + +style "audio_bus_metrics_inactive" = "track_controls_inactive" +{ + font_name = "%FONT_TINY%" +} + style "track_name_display" { font_name = "%FONT_NORMAL%" @@ -1102,15 +1134,9 @@ style "tearoff_arrow" = "medium_bold_entry" bg[PRELIGHT] = { 0.30, 0.30, 0.30 } } -style "meter_metrics_strip" = "default_base" -{ - font_name = "%FONT_TINY%" - fg[NORMAL] = { 1.0, 0.8, 0.2 } -} - style "location_row_button" = "default_buttons_menus" { - font_name = "%FONT_BIG%" + font_name = "%FONT_SMALL%" } style "location_rows_clock" = "default_clock_display" @@ -1213,15 +1239,15 @@ widget "*MixerMonitorInputButton.*" style:highest "very_small_button" widget "*MixerIOButton" style:highest "very_small_button" widget "*MixerIOButtonLabel" style:highest "very_small_button" widget "*AddRouteDialogSpinner" style:highest "ardour_adjusters" -widget "*AddRouteDialogRadioButton*" style:highest "options_window" -widget "*OptionsNotebook" style:highest "options_window" -widget "*OptionEditorToggleButton*" style:highest "options_window" -widget "*OptionsLabel" style:highest "options_window" -widget "*OptionEditorAuditionerLabel" style:highest "options_window" +widget "*AddRouteDialogRadioButton*" style:highest "preferences" +widget "*OptionsNotebook" style:highest "preferences" +widget "*OptionEditorToggleButton*" style:highest "preferences" +widget "*OptionsLabel" style:highest "preferences" +widget "*OptionEditorAuditionerLabel" style:highest "preferences" widget "*OptionsEntry" style:highest "option_entry" -widget "*InspectorNotebook" style:highest "options_window" -widget "*NewSessionDialog" style:highest "options_window" -widget "*NewSessionDialogButton*" style:highest "options_window" +widget "*InspectorNotebook" style:highest "preferences" +widget "*NewSessionDialog" style:highest "preferences" +widget "*NewSessionDialogButton*" style:highest "preferences" widget "*MixerSendSwitch*" style:highest "very_small_red_active_and_selected_button" widget "*OptionEditorToggleButton" style:highest "small_red_active_and_selected_button" widget "*NewSessionDialogButton" style:highest "small_red_active_and_selected_button" @@ -1325,11 +1351,24 @@ widget "*BBTRuler" style:highest "editor_time_ruler" widget "*FramesRuler" style:highest "editor_time_ruler" widget "*MinSecRuler" style:highest "editor_time_ruler" widget "*BaseFrame" style:highest "base_frame" -widget "*AudioTrackStripBase*" style:highest "audio_track_base" + +widget "*AudioTrackStripBase" style:highest "audio_track_base" +widget "*AudioBusStripBase" style:highest "audio_bus_base" +widget "*MidiTrackStripBase" style:highest "midi_track_base" +widget "*AudioTrackStripBaseInactive" style:highest "track_controls_inactive" +widget "*AudioBusStripBaseInactive" style:highest "track_controls_inactive" +widget "*MidiTrackStripBaseInactive" style:highest "track_controls_inactive" +widget "*FaderMetricsStrip" style:highest "audio_track_metrics" +widget "*AudioTrackMetrics" style:highest "audio_track_metrics" +widget "*AudioBusMetrics" style:highest "audio_bus_metrics" +widget "*AudioTrackMetricsInactive" style:highest "audio_track_metrics_inactive" +widget "*AudioBusMetricsInactive" style:highest "audio_bus_metrics_inactive" + widget "*TimeAxisViewControlsBaseUnselected" style:highest "audio_track_base" widget "*AudioTrackControlsBaseUnselected" style:highest "audio_track_base" +widget "*MidiTrackControlsBaseUnselected" style:highest "midi_track_base" widget "*AudioTrackFader" style:highest "gain_fader" -widget "*AudioBusStripBase" style:highest "audio_bus_base" + widget "*BusControlsBaseUnselected" style:highest "audio_bus_base" widget "*AudioBusFader" style:highest "gain_fader" widget "*TrackSeparator" style:highest "track_separator" @@ -1361,9 +1400,8 @@ widget "*AutomationTrackName" style:highest "automation_track_name" widget "*AudioTrackControlsBaseInactiveSelected" style:highest "track_controls_inactive" widget "*BusControlsBaseInactiveSelected" style:highest "track_controls_inactive" widget "*AutomationTrackControlsBaseInactiveSelected" style:highest "track_controls_inactive" -widget "*AudioTrackStripBaseInactive" style:highest "track_controls_inactive" -widget "*AudioBusStripBaseInactive" style:highest "track_controls_inactive" widget "*AudioTrackControlsBaseSelected" style:highest "edit_controls_base_selected" +widget "*MidiTrackControlsBaseSelected" style:highest "edit_controls_base_selected" widget "*BusControlsBaseSelected" style:highest "edit_controls_base_selected" widget "*AutomationTrackControlsBase" style:highest "automation_track_controls_base" widget "*AutomationTrackControlsBaseSelected" style:highest "edit_controls_base_selected" @@ -1458,8 +1496,7 @@ widget "*PluginSaveButton" style:highest "small_button" widget "*PluginSaveButton*" style:highest "small_button" widget "*PluginLoadButton" style:highest "small_button" widget "*PluginLoadButton*" style:highest "small_button" -widget "*FaderMetricsStrip" style:highest "meter_metrics_strip" -widget "*MeterMetricsStrip" style:highest "meter_metrics_strip" + widget "*MetricDialogFrame" style:highest "base_frame" widget "*MetricEntry" style:highest "medium_bold_entry" widget "*MetricButton" style:highest "default_buttons_menus" diff --git a/gtk2_ardour/ardour_dialog.cc b/gtk2_ardour/ardour_dialog.cc index c5162919d4..795b924075 100644 --- a/gtk2_ardour/ardour_dialog.cc +++ b/gtk2_ardour/ardour_dialog.cc @@ -34,6 +34,15 @@ ArdourDialog::ArdourDialog (string title, bool modal, bool use_seperator) set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); } +ArdourDialog::ArdourDialog (Gtk::Window& parent, string title, bool modal, bool use_seperator) + : Dialog (title, parent, modal, use_seperator) +{ + session = 0; + + set_type_hint(Gdk::WINDOW_TYPE_HINT_DIALOG); + set_position (Gtk::WIN_POS_CENTER_ON_PARENT); +} + ArdourDialog::~ArdourDialog () { } diff --git a/gtk2_ardour/ardour_dialog.h b/gtk2_ardour/ardour_dialog.h index 069768c143..13248e14de 100644 --- a/gtk2_ardour/ardour_dialog.h +++ b/gtk2_ardour/ardour_dialog.h @@ -37,6 +37,7 @@ class ArdourDialog : public Gtk::Dialog { public: ArdourDialog (std::string title, bool modal = false, bool use_separator = false); + ArdourDialog (Gtk::Window& parent, std::string title, bool modal = false, bool use_separator = false); ~ArdourDialog(); bool on_enter_notify_event (GdkEventCrossing*); diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc index cac63dc752..962ada5035 100644 --- a/gtk2_ardour/ardour_ui.cc +++ b/gtk2_ardour/ardour_ui.cc @@ -36,6 +36,7 @@ #include <gtkmm/accelmap.h> #include <pbd/error.h> +#include <pbd/misc.h> #include <pbd/compose.h> #include <pbd/failed_constructor.h> #include <pbd/enumwriter.h> @@ -50,8 +51,7 @@ #include <gtkmm2ext/popup.h> #include <gtkmm2ext/window_title.h> -#include <midi++/port.h> -#include <midi++/mmc.h> +#include <midi++/manager.h> #include <ardour/ardour.h> #include <ardour/profile.h> @@ -157,12 +157,18 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) auditioning_alert_button (_("AUDITION")), solo_alert_button (_("SOLO")), - shown_flag (false) + shown_flag (false), + error_log_button (_("Errors")) + { using namespace Gtk::Menu_Helpers; Gtkmm2ext::init(); + +#ifdef TOP_MENUBAR + _auto_display_errors = false; +#endif about = 0; if (theArdourUI == 0) { @@ -180,6 +186,9 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) session_selector_window = 0; last_key_press_time = 0; connection_editor = 0; + _will_create_new_session_automatically = false; + new_session_dialog = 0; + loading_dialog = 0; add_route_dialog = 0; route_params = 0; option_editor = 0; @@ -190,7 +199,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) session_loaded = false; last_speed_displayed = -1.0f; ab_direction = true; - + sys::path key_bindings_file; find_file_in_search_path (ardour_search_path() + system_config_search_path(), @@ -225,31 +234,80 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[]) ARDOUR::Session::AskAboutPendingState.connect (mem_fun(*this, &ARDOUR_UI::pending_state_dialog)); - /* have to wait for AudioEngine and Configuration before proceeding */ + /* lets get this party started */ + + try { + ARDOUR::init (ARDOUR_COMMAND_LINE::use_vst, ARDOUR_COMMAND_LINE::try_hw_optimization); + setup_gtk_ardour_enums (); + Config->set_current_owner (ConfigVariableBase::Interface); + setup_profile (); + + } catch (failed_constructor& err) { + error << _("could not initialize Ardour.") << endmsg; + // pass it on up + throw err; + } + + /* we like keyboards */ + + keyboard = new Keyboard; + + starting.connect (mem_fun(*this, &ARDOUR_UI::startup)); + stopping.connect (mem_fun(*this, &ARDOUR_UI::shutdown)); + + platform_specific (); } -void -ARDOUR_UI::set_engine (AudioEngine& e) +int +ARDOUR_UI::create_engine () { - engine = &e; + // this gets called every time by new_session() + + if (engine) { + return 0; + } + + try { + engine = new ARDOUR::AudioEngine (ARDOUR_COMMAND_LINE::jack_client_name); + + } catch (...) { + + return -1; + } engine->Stopped.connect (mem_fun(*this, &ARDOUR_UI::engine_stopped)); engine->Running.connect (mem_fun(*this, &ARDOUR_UI::engine_running)); engine->Halted.connect (mem_fun(*this, &ARDOUR_UI::engine_halted)); engine->SampleRateChanged.connect (mem_fun(*this, &ARDOUR_UI::update_sample_rate)); - ActionManager::init (); - new_session_dialog = new NewSessionDialog(); + post_engine (); - _tooltips.enable(); + return 0; +} - keyboard = new Keyboard; +void +ARDOUR_UI::post_engine () +{ + extern int setup_midi (); + + /* Things to be done once we create the AudioEngine + */ + + MIDI::Manager::instance()->set_api_data (engine->jack()); + setup_midi (); + + check_memory_locking(); + + ActionManager::init (); + _tooltips.enable(); if (setup_windows ()) { throw failed_constructor (); } - if (GTK_ARDOUR::show_key_actions) { + /* this is the first point at which all the keybindings are available */ + + if (ARDOUR_COMMAND_LINE::show_key_actions) { vector<string> names; vector<string> paths; vector<string> keys; @@ -266,9 +324,6 @@ ARDOUR_UI::set_engine (AudioEngine& e) exit (0); } - /* start with timecode, metering enabled - */ - blink_timeout_tag = -1; /* the global configuration object is now valid */ @@ -287,15 +342,26 @@ ARDOUR_UI::set_engine (AudioEngine& e) /* start the time-of-day-clock */ +#ifndef GTKOSX + /* OS X provides an always visible wallclock, so don't be stupid */ update_wall_clock (); Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::update_wall_clock), 60000); +#endif update_disk_space (); update_cpu_load (); update_sample_rate (engine->frame_rate()); - starting.connect (mem_fun(*this, &ARDOUR_UI::startup)); - stopping.connect (mem_fun(*this, &ARDOUR_UI::shutdown)); + /* now start and maybe save state */ + + if (do_engine_start () == 0) { + if (session && _session_is_new) { + /* we need to retain initial visual + settings for a new session + */ + session->save_state (""); + } + } } ARDOUR_UI::~ARDOUR_UI () @@ -317,6 +383,11 @@ ARDOUR_UI::~ARDOUR_UI () if (add_route_dialog) { delete add_route_dialog; } + + + if (new_session_dialog) { + delete new_session_dialog; + } } gint @@ -436,7 +507,7 @@ ARDOUR_UI::save_ardour_state () if (session) { session->add_instant_xml (enode); - session->add_instant_xml (mnode); + session->add_instant_xml (mnode); } else { Config->add_instant_xml (enode); Config->add_instant_xml (mnode); @@ -479,9 +550,151 @@ ARDOUR_UI::update_autosave () } void +ARDOUR_UI::backend_audio_error (bool we_set_params, Gtk::Window* toplevel) +{ + string title; + if (we_set_params) { + title = _("Ardour could not start JACK"); + } else { + title = _("Ardour could not connect to JACK."); + } + + MessageDialog win (title, + false, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_NONE); + + if (we_set_params) { + win.set_secondary_text(_("There are several possible reasons:\n\ +\n\ +1) You requested audio parameters that are not supported..\n\ +2) JACK is running as another user.\n\ +\n\ +Please consider the possibilities, and perhaps try different parameters.")); + } else { + win.set_secondary_text(_("There are several possible reasons:\n\ +\n\ +1) JACK is not running.\n\ +2) JACK is running as another user, perhaps root.\n\ +3) There is already another client called \"ardour\".\n\ +\n\ +Please consider the possibilities, and perhaps (re)start JACK.")); + } + + if (toplevel) { + win.set_transient_for (*toplevel); + } + + if (we_set_params) { + win.add_button (Stock::OK, RESPONSE_CLOSE); + } else { + win.add_button (Stock::QUIT, RESPONSE_CLOSE); + } + + win.set_default_response (RESPONSE_CLOSE); + + win.show_all (); + win.set_position (Gtk::WIN_POS_CENTER); + + if (!ARDOUR_COMMAND_LINE::no_splash) { + hide_splash (); + } + + /* we just don't care about the result, but we want to block */ + + win.run (); +} + +void ARDOUR_UI::startup () { - check_memory_locking(); + string name, path; + bool isnew; + + new_session_dialog = new NewSessionDialog(); + + /* If no session name is given: we're not loading a session yet, nor creating a new one */ + + if (ARDOUR_COMMAND_LINE::session_name.length()) { + + /* Load session or start the new session dialog */ + + if (find_session (ARDOUR_COMMAND_LINE::session_name, path, name, isnew)) { + error << string_compose(_("could not load command line session \"%1\""), + ARDOUR_COMMAND_LINE::session_name) << endmsg; + return; + } + + if (!ARDOUR_COMMAND_LINE::new_session) { + + /* Supposed to be loading an existing session, but the session doesn't exist */ + + if (isnew) { + error << string_compose (_("\n\nNo session named \"%1\" exists.\n" + "To create it from the command line, start ardour as \"ardour --new %1"), path) + << endmsg; + return; + } + } + + new_session_dialog->set_session_name (name); + new_session_dialog->set_session_folder (Glib::path_get_basename (path)); + _session_is_new = isnew; + } + + hide_splash (); + + bool have_backend = EngineControl::engine_running(); + bool need_nsd; + bool load_needed = false; + + if (have_backend) { + + /* backend audio is working */ + + if (ARDOUR_COMMAND_LINE::session_name.empty() || ARDOUR_COMMAND_LINE::new_session) { + /* need NSD to get session name and other info */ + need_nsd = true; + } else { + need_nsd = false; + } + + } else { + + XMLNode* audio_setup = Config->extra_xml ("AudioSetup"); + + if (audio_setup) { + new_session_dialog->engine_control.set_state (*audio_setup); + } + + /* no backend audio, must bring up NSD to check configuration */ + + need_nsd = true; + } + + if (need_nsd) { + + if (!get_session_parameters (ARDOUR_COMMAND_LINE::session_name, have_backend, ARDOUR_COMMAND_LINE::new_session)) { + return; + } + + } else { + + if (create_engine ()) { + backend_audio_error (false); + exit (1); + } + + load_needed = true; + } + + if (load_needed) { + if (load_session (ARDOUR_COMMAND_LINE::session_name, name)) { + return; + } + } + + show (); } void @@ -679,6 +892,8 @@ ARDOUR_UI::every_point_one_seconds () gint ARDOUR_UI::every_point_zero_one_seconds () { + // august 2007: actual update frequency: 40Hz, not 100Hz + SuperRapidScreenUpdate(); /* EMIT_SIGNAL */ return TRUE; } @@ -1001,6 +1216,7 @@ ARDOUR_UI::open_session () open_session_selector = new Gtk::FileChooserDialog (_("open session"), FILE_CHOOSER_ACTION_OPEN); open_session_selector->add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); open_session_selector->add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_ACCEPT); + open_session_selector->set_default_response(Gtk::RESPONSE_ACCEPT); FileFilter session_filter; session_filter.add_pattern ("*.ardour"); @@ -1524,21 +1740,6 @@ ARDOUR_UI::setup_theme () theme_manager->setup_theme(); } -gint -ARDOUR_UI::start_engine () -{ - if (do_engine_start () == 0) { - if (session && _session_is_new) { - /* we need to retain initial visual - settings for a new session - */ - session->save_state (""); - } - } - - return FALSE; -} - void ARDOUR_UI::update_clocks () { @@ -1832,30 +2033,34 @@ ARDOUR_UI::save_template () } bool -ARDOUR_UI::new_session (std::string predetermined_path) +ARDOUR_UI::get_session_parameters (Glib::ustring predetermined_path, bool have_engine, bool should_be_new) { string session_name; string session_path; + string template_name; - if (!check_audioengine()) { - return false; + if (!loading_dialog) { + loading_dialog = new MessageDialog (*new_session_dialog, + "", + false, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_NONE); } - + int response = Gtk::RESPONSE_NONE; new_session_dialog->set_modal(true); new_session_dialog->set_name (predetermined_path); new_session_dialog->reset_recent(); - new_session_dialog->show(); + new_session_dialog->set_position (WIN_POS_CENTER); new_session_dialog->set_current_page (0); do { - response = new_session_dialog->run (); + new_session_dialog->set_have_engine (have_engine); - if (!check_audioengine()) { - new_session_dialog->hide (); - return false; - } + new_session_dialog->show(); + new_session_dialog->present (); + response = new_session_dialog->run (); _session_is_new = false; @@ -1869,65 +2074,119 @@ ARDOUR_UI::new_session (std::string predetermined_path) } else if (response == Gtk::RESPONSE_NONE) { - /* Clear was pressed */ - new_session_dialog->reset(); + /* Clear was pressed */ + new_session_dialog->reset(); + continue; + } - } else if (response == Gtk::RESPONSE_YES) { + /* first things first ... if we're here to help set up audio parameters + this is where want to do that. + */ - /* YES == OPEN, but there's no enum for that */ + if (!have_engine) { + if (new_session_dialog->engine_control.setup_engine ()) { + new_session_dialog->hide (); + return false; + } + } - session_name = new_session_dialog->session_name(); +#ifdef GTKOSX + /* X11 users will always have fontconfig info around, but new GTK-OSX users + may not and it can take a while to build it. Warn them. + */ + Glib::ustring fontconfig = Glib::build_filename (Glib::get_home_dir(), ".fontconfig"); + + if (!Glib::file_test (fontconfig, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_DIR)) { + MessageDialog msg (*new_session_dialog, + _("Welcome to Ardour.\n\n" + "The program will take a bit longer to start up\n" + "while the system fonts are checked.\n\n" + "This will only be done once, and you will\n" + "not see this message again\n"), + true, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_OK); + msg.show_all (); + msg.present (); + msg.run (); + } +#endif + loading_dialog->set_message (_("Starting audio engine")); + loading_dialog->show_all (); + flush_pending (); + + if (create_engine ()) { + backend_audio_error (!have_engine, new_session_dialog); + loading_dialog->hide (); + flush_pending (); + /* audio setup page */ + new_session_dialog->set_current_page (2); + /* try again */ + response = Gtk::RESPONSE_NONE; + continue; + } + + have_engine = true; + + /* now handle possible affirmative responses */ + + if (response == Gtk::RESPONSE_YES) { + + /* YES == OPEN from the session selector */ + + session_name = new_session_dialog->session_name(); + if (session_name.empty()) { response = Gtk::RESPONSE_NONE; continue; } if (session_name[0] == '/' || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { load_session (Glib::path_get_dirname (session_name), session_name); } else { session_path = new_session_dialog->session_folder(); load_session (session_path, session_name); } - + } else if (response == Gtk::RESPONSE_OK) { - session_name = new_session_dialog->session_name(); - - if (!new_session_dialog->on_new_session_page ()) { - - /* XXX this is a bit of a hack.. - i really want the new sesion dialog to return RESPONSE_YES - if we're on page 1 (the load page) - Unfortunately i can't see how atm.. - */ + /* OK == OPEN button */ - if (session_name.empty()) { - response = Gtk::RESPONSE_NONE; - continue; - } + session_name = new_session_dialog->session_name(); + + if (session_name.empty()) { + response = Gtk::RESPONSE_NONE; + continue; + } + + switch (new_session_dialog->get_current_page()) { + case 1: /* recent session selector */ + case 2: /* audio engine control */ if (session_name[0] == '/' || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { load_session (Glib::path_get_dirname (session_name), session_name); } else { session_path = new_session_dialog->session_folder(); load_session (session_path, session_name); } + break; - } else { + case 0: /* nominally the "new" session creator, but could be in use for an old session */ - if (session_name.empty()) { - response = Gtk::RESPONSE_NONE; - continue; - } + if (new_session_dialog->get_current_page() == 0 && ARDOUR_COMMAND_LINE::session_name.empty()) { + should_be_new = true; + } + + /* handle what appear to be paths rather than just a name */ if (session_name[0] == '/' || - (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || - (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { + (session_name.length() > 2 && session_name[0] == '.' && session_name[1] == '/') || + (session_name.length() > 3 && session_name[0] == '.' && session_name[1] == '.' && session_name[2] == '/')) { session_path = Glib::path_get_dirname (session_name); session_name = Glib::path_get_basename (session_name); @@ -1937,108 +2196,144 @@ ARDOUR_UI::new_session (std::string predetermined_path) session_path = new_session_dialog->session_folder(); } - + //XXX This is needed because session constructor wants a //non-existant path. hopefully this will be fixed at some point. - + session_path = Glib::build_filename (session_path, session_name); + + if (!should_be_new) { - if (g_file_test (session_path.c_str(), GFileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { + load_session (session_path, session_name); + continue; /* leaves while() loop because response != NONE */ + + } else if (Glib::file_test (session_path, Glib::FileTest (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) { Glib::ustring str = string_compose (_("This session\n%1\nalready exists. Do you want to open it?"), session_path); MessageDialog msg (str, - false, - Gtk::MESSAGE_WARNING, - Gtk::BUTTONS_YES_NO, - true); + false, + Gtk::MESSAGE_WARNING, + Gtk::BUTTONS_YES_NO, + true); msg.set_name (X_("CleanupDialog")); msg.set_wmclass (X_("existing_session"), "Ardour"); msg.set_position (Gtk::WIN_POS_MOUSE); - + switch (msg.run()) { - case RESPONSE_YES: - load_session (session_path, session_name); - goto done; - break; - default: - response = RESPONSE_NONE; - new_session_dialog->reset (); - continue; + case RESPONSE_YES: + new_session_dialog->hide (); + goto_editor_window (); + flush_pending (); + load_session (session_path, session_name); + goto done; + break; + default: + response = RESPONSE_NONE; + new_session_dialog->reset (); + continue; } - } + } - _session_is_new = true; + _session_is_new = true; + + if (new_session_dialog->use_session_template()) { - std::string template_name = new_session_dialog->session_template_name(); + template_name = new_session_dialog->session_template_name(); - if (new_session_dialog->use_session_template()) { + new_session_dialog->hide (); + goto_editor_window (); + flush_pending (); load_session (session_path, session_name, &template_name); - + } else { uint32_t cchns; uint32_t mchns; AutoConnectOption iconnect; AutoConnectOption oconnect; + uint32_t nphysin; + uint32_t nphysout; + + if (Profile->get_sae()) { - if (new_session_dialog->create_control_bus()) { - cchns = (uint32_t) new_session_dialog->control_channel_count(); - } else { cchns = 0; - } + mchns = 2; + iconnect = AutoConnectPhysical; + oconnect = AutoConnectMaster; + nphysin = 0; // use all available + nphysout = 0; // use all available - if (new_session_dialog->create_master_bus()) { - mchns = (uint32_t) new_session_dialog->master_channel_count(); } else { - mchns = 0; - } - if (new_session_dialog->connect_inputs()) { - iconnect = AutoConnectPhysical; - } else { - iconnect = AutoConnectOption (0); + /* get settings from advanced section of NSD */ + + if (new_session_dialog->create_control_bus()) { + cchns = (uint32_t) new_session_dialog->control_channel_count(); + } else { + cchns = 0; + } + + if (new_session_dialog->create_master_bus()) { + mchns = (uint32_t) new_session_dialog->master_channel_count(); + } else { + mchns = 0; + } + + if (new_session_dialog->connect_inputs()) { + iconnect = AutoConnectPhysical; + } else { + iconnect = AutoConnectOption (0); + } + + /// @todo some minor tweaks. + + if (new_session_dialog->connect_outs_to_master()) { + oconnect = AutoConnectMaster; + } else if (new_session_dialog->connect_outs_to_physical()) { + oconnect = AutoConnectPhysical; + } else { + oconnect = AutoConnectOption (0); + } + + nphysin = (uint32_t) new_session_dialog->input_limit_count(); + nphysout = (uint32_t) new_session_dialog->output_limit_count(); } - /// @todo some minor tweaks. - - if (new_session_dialog->connect_outs_to_master()) { - oconnect = AutoConnectMaster; - } else if (new_session_dialog->connect_outs_to_physical()) { - oconnect = AutoConnectPhysical; - } else { - oconnect = AutoConnectOption (0); - } - - uint32_t nphysin = (uint32_t) new_session_dialog->input_limit_count(); - uint32_t nphysout = (uint32_t) new_session_dialog->output_limit_count(); - - if (!build_session (session_path, - session_name, - cchns, - mchns, - iconnect, - oconnect, - nphysin, - nphysout, - engine->frame_rate() * 60 * 5)) { - + new_session_dialog->hide (); + goto_editor_window (); + flush_pending (); + + if (build_session (session_path, + session_name, + cchns, + mchns, + iconnect, + oconnect, + nphysin, + nphysout, + engine->frame_rate() * 60 * 5)) { + response = Gtk::RESPONSE_NONE; new_session_dialog->reset (); continue; } } + break; + + default: + break; } } - + } while (response == Gtk::RESPONSE_NONE); done: show(); - new_session_dialog->get_window()->set_cursor(); + loading_dialog->hide (); new_session_dialog->hide(); return true; } @@ -2050,21 +2345,32 @@ ARDOUR_UI::close_session() return; } - unload_session(); - new_session (); + unload_session (true); + + get_session_parameters ("", true, false); } int ARDOUR_UI::load_session (const string & path, const string & snap_name, string* mix_template) { Session *new_session; + int unload_status; + int retval = -1; + session_loaded = false; - + if (!check_audioengine()) { return -1; } - if(!unload_session ()) return -1; + unload_status = unload_session (); + + if (unload_status < 0) { + goto out; + } else if (unload_status > 0) { + retval = 0; + goto out; + } /* if it already exists, we must have write access */ @@ -2072,17 +2378,23 @@ ARDOUR_UI::load_session (const string & path, const string & snap_name, string* MessageDialog msg (*editor, _("You do not have write access to this session.\n" "This prevents the session from being loaded.")); msg.run (); - return -1; + goto out; + } + + if (loading_dialog) { + loading_dialog->set_markup (_("Please wait while Ardour loads your session")); + flush_pending (); } + disable_screen_updates (); + try { new_session = new Session (*engine, path, snap_name, mix_template); } catch (...) { - error << string_compose(_("Session \"%1 (snapshot %2)\" did not load successfully"), path, snap_name) << endmsg; - return -1; + goto out; } connect_to_session (new_session); @@ -2098,10 +2410,15 @@ ARDOUR_UI::load_session (const string & path, const string & snap_name, string* } editor->edit_cursor_position (true); - return 0; + enable_screen_updates (); + flush_pending (); + retval = 0; + + out: + return retval; } -bool +int ARDOUR_UI::build_session (const string & path, const string & snap_name, uint32_t control_channels, uint32_t master_channels, @@ -2112,14 +2429,21 @@ ARDOUR_UI::build_session (const string & path, const string & snap_name, nframes_t initial_length) { Session *new_session; + int x; if (!check_audioengine()) { - return false; + return -1; } session_loaded = false; - if (!unload_session ()) return false; + x = unload_session (); + + if (x < 0) { + return -1; + } else if (x > 0) { + return 0; + } _session_is_new = true; @@ -2132,13 +2456,13 @@ ARDOUR_UI::build_session (const string & path, const string & snap_name, MessageDialog msg (string_compose(_("Could not create session in \"%1\""), path)); msg.run (); - return false; + return -1; } connect_to_session (new_session); session_loaded = true; - return true; + return 0; } void @@ -2616,8 +2940,10 @@ ARDOUR_UI::cmdline_new_session (string path) path = str; } - new_session (path); - + get_session_parameters (path, false, true); + + _will_create_new_session_automatically = false; /* done it */ + return FALSE; /* don't call it again */ } @@ -2868,6 +3194,12 @@ ARDOUR_UI::setup_profile () if (gdk_screen_width() < 1200) { Profile->set_small_screen (); } + + + if (getenv ("ARDOUR_SAE")) { + Profile->set_sae (); + Profile->set_single_package (); + } } void diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h index 39bd8db4a6..427d40dffe 100644 --- a/gtk2_ardour/ardour_ui.h +++ b/gtk2_ardour/ardour_ui.h @@ -50,6 +50,7 @@ #include <gtkmm/togglebutton.h> #include <gtkmm/treeview.h> #include <gtkmm/menubar.h> +#include <gtkmm/textbuffer.h> #include <gtkmm/adjustment.h> #include <gtkmm2ext/gtk_ui.h> #include <gtkmm2ext/click_box.h> @@ -111,8 +112,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI int load_session (const string & path, const string & snapshot, string* mix_template = 0); bool session_loaded; - /// @return true if building the session was successful - bool build_session (const string & path, const string & snapshot, + /// @return zero if building the session was successful + int build_session (const string & path, const string & snapshot, uint32_t ctl_chns, uint32_t master_chns, ARDOUR::AutoConnectOption input_connect, @@ -124,11 +125,20 @@ class ARDOUR_UI : public Gtkmm2ext::UI ARDOUR::Session* the_session() { return session; } - bool new_session(std::string path = string()); + bool will_create_new_session_automatically() const { + return _will_create_new_session_automatically; + } + + void set_will_create_new_session_automatically (bool yn) { + _will_create_new_session_automatically = yn; + } + + bool get_session_parameters (Glib::ustring path, bool have_engine = false, bool should_be_new = false); + gint cmdline_new_session (string path); /// @return true if session was successfully unloaded. - bool unload_session (); + int unload_session (bool hide_stuff = false); void close_session(); int save_state_canfail (string state_name = ""); @@ -204,8 +214,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI session_add_midi_route (false); }*/ - void set_engine (ARDOUR::AudioEngine&); - gint start_engine (); + int create_engine (); + void post_engine (); gint exit_on_main_window_close (GdkEventAny *); @@ -221,6 +231,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI void setup_profile (); void setup_theme (); + void set_shuttle_fract (double); + protected: friend class PublicEditor; @@ -293,6 +305,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI static ARDOUR_UI *theArdourUI; + void backend_audio_error (bool we_set_params, Gtk::Window* toplevel = 0); void startup (); void shutdown (); @@ -438,7 +451,6 @@ class ARDOUR_UI : public Gtkmm2ext::UI gint shuttle_box_expose (GdkEventExpose*); gint mouse_shuttle (double x, bool force); void use_shuttle_fract (bool force); - void set_shuttle_fract (double); bool shuttle_grabbed; double shuttle_fract; @@ -507,6 +519,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI Gtk::EventBox menu_bar_base; Gtk::HBox menu_hbox; + void use_menubar_as_top_menubar (); void build_menu_bar (); void build_control_surface_menu (); @@ -541,6 +554,8 @@ class ARDOUR_UI : public Gtkmm2ext::UI gint session_menu (GdkEventButton *); + bool _will_create_new_session_automatically; + NewSessionDialog* new_session_dialog; void open_session (); @@ -683,6 +698,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI void set_remote_model (ARDOUR::RemoteModel); void set_denormal_model (ARDOUR::DenormalModel); + void toggle_sync_order_keys (); void toggle_StopPluginsWithTransport(); void toggle_DoNotRunPluginsWhileRecording(); void toggle_VerifyRemoveLastCapture(); @@ -696,16 +712,19 @@ class ARDOUR_UI : public Gtkmm2ext::UI void toggle_RegionEquivalentsOverlap (); void toggle_PrimaryClockDeltaEditCursor (); void toggle_SecondaryClockDeltaEditCursor (); + void toggle_only_copy_imported_files (); void mtc_port_changed (); void map_solo_model (); void map_monitor_model (); void map_denormal_model (); + void map_denormal_protection (); void map_remote_model (); void map_file_header_format (); void map_file_data_format (); void map_input_auto_connect (); void map_output_auto_connect (); + void map_only_copy_imported_files (); void parameter_changed (const char*); void set_meter_hold (ARDOUR::MeterHold); @@ -725,6 +744,16 @@ class ARDOUR_UI : public Gtkmm2ext::UI bool ab_direction; void disable_all_plugins (); void ab_all_plugins (); + + void audioengine_setup (); + + void display_message (const char *prefix, gint prefix_len, + Glib::RefPtr<Gtk::TextBuffer::Tag> ptag, Glib::RefPtr<Gtk::TextBuffer::Tag> mtag, const char *msg); + Gtk::Label status_bar_label; + Gtk::ToggleButton error_log_button; + Gtk::MessageDialog* loading_dialog; + + void platform_specific (); }; #endif /* __ardour_gui_h__ */ diff --git a/gtk2_ardour/ardour_ui2.cc b/gtk2_ardour/ardour_ui2.cc index 489728286c..8f755a3d93 100644 --- a/gtk2_ardour/ardour_ui2.cc +++ b/gtk2_ardour/ardour_ui2.cc @@ -78,14 +78,54 @@ ARDOUR_UI::setup_windows () theme_manager->signal_unmap().connect (bind (sigc::ptr_fun(&ActionManager::uncheck_toggleaction), X_("<Actions>/Common/ToggleThemeManager"))); - top_packer.pack_start (menu_bar_base, false, false); - top_packer.pack_start (transport_frame, false, false); + top_packer.pack_start (transport_frame, false, false); + +#ifdef TOP_MENUBAR + HBox* status_bar_packer = manage (new HBox); + + status_bar_label.set_size_request (300, -1); + status_bar_packer->pack_start (status_bar_label, true, true, 6); + status_bar_packer->pack_start (error_log_button, false, false); + + error_log_button.signal_clicked().connect (mem_fun (*this, &UI::toggle_errors)); + + editor->get_status_bar_packer().pack_start (*status_bar_packer, true, true); + editor->get_status_bar_packer().pack_start (menu_bar_base, false, false, 6); +#else + top_packer.pack_start (menu_bar_base, false, false); +#endif editor->add_toplevel_controls (top_packer); return 0; } + void +ARDOUR_UI::display_message (const char *prefix, gint prefix_len, RefPtr<TextBuffer::Tag> ptag, RefPtr<TextBuffer::Tag> mtag, const char *msg) +{ + ustring text; + + UI::display_message (prefix, prefix_len, ptag, mtag, msg); +#ifdef TOP_MENUBAR + + if (strcmp (prefix, _("[ERROR]: ")) == 0) { + text = "<span color=\"red\" weight=\"bold\">"; + } else if (strcmp (prefix, _("[WARNING]: ")) == 0) { + text = "<span color=\"yellow\" weight=\"bold\">"; + } else if (strcmp (prefix, _("[INFO]: ")) == 0) { + text = "<span color=\"green\" weight=\"bold\">"; + } else { + text = "<span color=\"blue\" weight=\"bold\">???"; + } + + text += prefix; + text += "</span>"; + text += msg; + + status_bar_label.set_markup (text); +#endif +} + void ARDOUR_UI::transport_stopped () { diff --git a/gtk2_ardour/ardour_ui_dependents.cc b/gtk2_ardour/ardour_ui_dependents.cc index 725180e0ab..6464c52967 100644 --- a/gtk2_ardour/ardour_ui_dependents.cc +++ b/gtk2_ardour/ardour_ui_dependents.cc @@ -47,7 +47,8 @@ void ARDOUR_UI::shutdown () { if (session) { - delete session; + /* we're exiting cleanly, so remove any auto-save data */ + session->remove_pending_capture_state (); session = 0; } @@ -109,11 +110,13 @@ void ARDOUR_UI::goto_editor_window () { editor->show_window (); + editor->present (); } void ARDOUR_UI::goto_mixer_window () { mixer->show_window (); + mixer->present (); } gint diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc index c6353df782..9a71443489 100644 --- a/gtk2_ardour/ardour_ui_dialogs.cc +++ b/gtk2_ardour/ardour_ui_dialogs.cc @@ -162,21 +162,26 @@ ARDOUR_UI::connect_to_session (Session *s) point_zero_one_second_connection = Glib::signal_timeout().connect (mem_fun(*this, &ARDOUR_UI::every_point_zero_one_seconds), 40); } -bool -ARDOUR_UI::unload_session () +int +ARDOUR_UI::unload_session (bool hide_stuff) { if (session && session->dirty()) { switch (ask_about_saving_session (_("close"))) { case -1: // cancel - return false; + return 1; case 1: session->save_state (""); break; } } - editor->hide (); + + if (hide_stuff) { + editor->hide (); + mixer->hide (); + } + second_connection.disconnect (); point_one_second_connection.disconnect (); point_oh_five_second_connection.disconnect (); @@ -204,16 +209,12 @@ ARDOUR_UI::unload_session () option_editor->set_session (0); } - if (mixer) { - mixer->hide (); - } - delete session; session = 0; update_buffer_load (); - return true; + return 0; } int diff --git a/gtk2_ardour/ardour_ui_ed.cc b/gtk2_ardour/ardour_ui_ed.cc index a043128700..c850d72048 100644 --- a/gtk2_ardour/ardour_ui_ed.cc +++ b/gtk2_ardour/ardour_ui_ed.cc @@ -34,8 +34,10 @@ #include "ardour_ui.h" #include "public_editor.h" #include "audio_clock.h" +#include "engine_dialog.h" #include "editor.h" #include "actions.h" +#include "sync-menu.h" #include <ardour/session.h> #include <ardour/profile.h> @@ -80,6 +82,8 @@ ARDOUR_UI::install_actions () /* menus + submenus that need action items */ ActionManager::register_action (main_actions, X_("Session"), _("Session")); + ActionManager::register_action (main_actions, X_("Files"), _("Files")); + ActionManager::register_action (main_actions, X_("Regions"), _("Regions")); ActionManager::register_action (main_actions, X_("Cleanup"), _("Cleanup")); ActionManager::register_action (main_actions, X_("Sync"), _("Sync")); ActionManager::register_action (main_actions, X_("Options"), _("Options")); @@ -98,7 +102,7 @@ ARDOUR_UI::install_actions () /* the real actions */ - act = ActionManager::register_action (main_actions, X_("New"), _("New"), hide_return (bind (mem_fun(*this, &ARDOUR_UI::new_session), string ()))); + act = ActionManager::register_action (main_actions, X_("New"), _("New"), hide_return (bind (mem_fun(*this, &ARDOUR_UI::get_session_parameters), string (), true, true))); ActionManager::register_action (main_actions, X_("Open"), _("Open"), mem_fun(*this, &ARDOUR_UI::open_session)); ActionManager::register_action (main_actions, X_("Recent"), _("Recent"), mem_fun(*this, &ARDOUR_UI::open_recent_session)); @@ -191,7 +195,7 @@ ARDOUR_UI::install_actions () ActionManager::register_action (common_actions, X_("goto-editor"), _("Show Editor"), mem_fun(*this, &ARDOUR_UI::goto_editor_window)); ActionManager::register_action (common_actions, X_("goto-mixer"), _("Show Mixer"), mem_fun(*this, &ARDOUR_UI::goto_mixer_window)); - ActionManager::register_toggle_action (common_actions, X_("ToggleOptionsEditor"), _("Options Editor"), mem_fun(*this, &ARDOUR_UI::toggle_options_window)); + ActionManager::register_toggle_action (common_actions, X_("ToggleOptionsEditor"), _("Preferences"), mem_fun(*this, &ARDOUR_UI::toggle_options_window)); act = ActionManager::register_toggle_action (common_actions, X_("ToggleInspector"), _("Track/Bus Inspector"), mem_fun(*this, &ARDOUR_UI::toggle_route_params_window)); ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_toggle_action (common_actions, X_("ToggleConnections"), _("Connections"), mem_fun(*this, &ARDOUR_UI::toggle_connection_editor)); @@ -200,7 +204,6 @@ ARDOUR_UI::install_actions () ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_toggle_action (common_actions, X_("ToggleBigClock"), _("Big Clock"), mem_fun(*this, &ARDOUR_UI::toggle_big_clock_window)); ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (common_actions, X_("About"), _("About"), mem_fun(*this, &ARDOUR_UI::show_splash)); act = ActionManager::register_toggle_action (common_actions, X_("ToggleThemeManager"), _("Theme Manager"), mem_fun(*this, &ARDOUR_UI::toggle_theme_manager)); ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_action (common_actions, X_("AddAudioTrack"), _("Add Audio Track"), bind (mem_fun(*this, &ARDOUR_UI::session_add_audio_track), 1, 1, ARDOUR::Normal, 1)); @@ -216,6 +219,8 @@ ARDOUR_UI::install_actions () act = ActionManager::register_action (common_actions, X_("RemoveLastCapture"), _("Remove Last Capture"), mem_fun(*this, &ARDOUR_UI::remove_last_capture)); ActionManager::session_sensitive_actions.push_back (act); + ActionManager::register_action (common_actions, X_("About"), _("About"), mem_fun(*this, &ARDOUR_UI::show_splash)); + Glib::RefPtr<ActionGroup> transport_actions = ActionGroup::create (X_("Transport")); /* do-nothing action for the "transport" menu bar item */ @@ -402,6 +407,7 @@ ARDOUR_UI::install_actions () act->set_sensitive (false); #endif + ActionManager::register_toggle_action (option_actions, X_("SyncEditorAndMixerTrackOrder"), _("Sync Editor and Mixer track order"), mem_fun (*this, &ARDOUR_UI::toggle_sync_order_keys)); ActionManager::register_toggle_action (option_actions, X_("StopPluginsWithTransport"), _("Stop plugins with transport"), mem_fun (*this, &ARDOUR_UI::toggle_StopPluginsWithTransport)); ActionManager::register_toggle_action (option_actions, X_("VerifyRemoveLastCapture"), _("Verify remove last capture"), mem_fun (*this, &ARDOUR_UI::toggle_VerifyRemoveLastCapture)); ActionManager::register_toggle_action (option_actions, X_("PeriodicSafetyBackups"), _("Make periodic safety backups"), mem_fun (*this, &ARDOUR_UI::toggle_PeriodicSafetyBackups)); @@ -412,26 +418,45 @@ ARDOUR_UI::install_actions () ActionManager::register_toggle_action (option_actions, X_("RegionEquivalentsOverlap"), _("Region equivalents overlap"), mem_fun (*this, &ARDOUR_UI::toggle_RegionEquivalentsOverlap)); ActionManager::register_toggle_action (option_actions, X_("PrimaryClockDeltaEditCursor"), _("Primary Clock delta to edit cursor"), mem_fun (*this, &ARDOUR_UI::toggle_PrimaryClockDeltaEditCursor)); ActionManager::register_toggle_action (option_actions, X_("SecondaryClockDeltaEditCursor"), _("Secondary Clock delta to edit cursor"), mem_fun (*this, &ARDOUR_UI::toggle_SecondaryClockDeltaEditCursor)); + ActionManager::register_toggle_action (option_actions, X_("OnlyCopyImportedFiles"), _("Always copy imported files"), mem_fun (*this, &ARDOUR_UI::toggle_only_copy_imported_files)); RadioAction::Group denormal_group; ActionManager::register_toggle_action (option_actions, X_("DenormalProtection"), _("Use DC bias"), mem_fun (*this, &ARDOUR_UI::toggle_denormal_protection)); - - FPU fpu; - ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalNone"), _("No processor handling"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalNone)); - act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZ"), _("Use FlushToZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZ)); - if (!fpu.has_flush_to_zero()) { + // as of September 10th 2007, Valgrind cannot handle various FPU flag setting instructions + // so avoid them + + if (getenv ("ARDOUR_RUNNING_UNDER_VALGRIND")) { + + /* we still need these actions to exist, but make them all insensitive */ + + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZ"), _("Use FlushToZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZ)); act->set_sensitive (false); - } - act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalDAZ"), _("Use DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalDAZ)); - if (!fpu.has_denormals_are_zero()) { + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalDAZ"), _("Use DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalDAZ)); act->set_sensitive (false); - } - act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZDAZ"), _("Use FlushToZero & DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZDAZ)); - if (!fpu.has_flush_to_zero() || !fpu.has_denormals_are_zero()) { + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZDAZ"), _("Use FlushToZero & DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZDAZ)); act->set_sensitive (false); + + } else { + + FPU fpu; + + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZ"), _("Use FlushToZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZ)); + if (!fpu.has_flush_to_zero()) { + act->set_sensitive (false); + } + + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalDAZ"), _("Use DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalDAZ)); + if (!fpu.has_denormals_are_zero()) { + act->set_sensitive (false); + } + + act = ActionManager::register_radio_action (option_actions, denormal_group, X_("DenormalFTZDAZ"), _("Use FlushToZero & DenormalsAreZero"), bind (mem_fun (*this, &ARDOUR_UI::set_denormal_model), DenormalFTZDAZ)); + if (!fpu.has_flush_to_zero() || !fpu.has_denormals_are_zero()) { + act->set_sensitive (false); + } } act = ActionManager::register_toggle_action (option_actions, X_("DoNotRunPluginsWhileRecording"), _("Do not run plugins while recording"), mem_fun (*this, &ARDOUR_UI::toggle_DoNotRunPluginsWhileRecording)); @@ -696,7 +721,9 @@ ARDOUR_UI::build_control_surface_menu () void ARDOUR_UI::build_menu_bar () { - build_control_surface_menu (); + if (!Profile->get_sae()) { + build_control_surface_menu (); + } menu_bar = dynamic_cast<MenuBar*> (ActionManager::get_widget (X_("/Main"))); menu_bar->set_name ("MainMenuBar"); @@ -731,11 +758,20 @@ ARDOUR_UI::build_menu_bar () sample_rate_box.set_name ("SampleRate"); sample_rate_label.set_name ("SampleRate"); - menu_hbox.pack_start (*menu_bar, true, true); - if (!Profile->get_small_screen()) { - menu_hbox.pack_end (wall_clock_box, false, false, 2); - menu_hbox.pack_end (disk_space_box, false, false, 4); +#ifndef TOP_MENUBAR + menu_hbox.pack_start (*menu_bar, true, true); +#else + use_menubar_as_top_menubar (); +#endif + + if (!Profile->get_small_screen()) { +#ifndef GTKOSX + // OSX provides its own wallclock, thank you very much + menu_hbox.pack_end (wall_clock_box, false, false, 2); +#endif + menu_hbox.pack_end (disk_space_box, false, false, 4); } + menu_hbox.pack_end (cpu_load_box, false, false, 4); menu_hbox.pack_end (buffer_load_box, false, false, 4); menu_hbox.pack_end (sample_rate_box, false, false, 4); @@ -745,12 +781,23 @@ ARDOUR_UI::build_menu_bar () } void +ARDOUR_UI::use_menubar_as_top_menubar () +{ +#ifdef GTKOSX + ige_mac_menu_set_menu_bar ((GtkMenuShell*) menu_bar->gobj()); + // ige_mac_menu_set_quit_menu_item (some_item->gobj()); +#endif +} + + +void ARDOUR_UI::setup_clock () { ARDOUR_UI::Clock.connect (bind (mem_fun (big_clock, &AudioClock::set), false)); big_clock_window = new Window (WINDOW_TOPLEVEL); + big_clock_window->set_keep_above (true); big_clock_window->set_border_width (0); big_clock_window->add (big_clock); @@ -761,9 +808,5 @@ ARDOUR_UI::setup_clock () big_clock_window->signal_realize().connect (bind (sigc::ptr_fun (set_decoration), big_clock_window, (Gdk::DECOR_BORDER|Gdk::DECOR_RESIZEH))); big_clock_window->signal_unmap().connect (bind (sigc::ptr_fun(&ActionManager::uncheck_toggleaction), X_("<Actions>/Common/ToggleBigClock"))); - if (editor) { - editor->ensure_float (*big_clock_window); - } - manage_window (*big_clock_window); } diff --git a/gtk2_ardour/ardour_ui_options.cc b/gtk2_ardour/ardour_ui_options.cc index 4a6bef6b5f..d013f98e5b 100644 --- a/gtk2_ardour/ardour_ui_options.cc +++ b/gtk2_ardour/ardour_ui_options.cc @@ -82,6 +82,13 @@ ARDOUR_UI::toggle_denormal_protection () } void +ARDOUR_UI::toggle_only_copy_imported_files () +{ + ActionManager::toggle_config_state ("options", "OnlyCopyImportedFiles", &Configuration::set_only_copy_imported_files, &Configuration::get_only_copy_imported_files); +} + + +void ARDOUR_UI::set_native_file_header_format (HeaderFormat hf) { const char *action = 0; @@ -459,6 +466,12 @@ ARDOUR_UI::toggle_StopRecordingOnXrun() } void +ARDOUR_UI::toggle_sync_order_keys () +{ + ActionManager::toggle_config_state ("options", "SyncEditorAndMixerTrackOrder", &Configuration::set_sync_all_route_ordering, &Configuration::get_sync_all_route_ordering); +} + +void ARDOUR_UI::toggle_StopTransportAtEndOfSession() { ActionManager::toggle_config_state ("options", "StopTransportAtEndOfSession", &Configuration::set_stop_at_session_end, &Configuration::get_stop_at_session_end); @@ -577,6 +590,19 @@ ARDOUR_UI::map_monitor_model () } void +ARDOUR_UI::map_denormal_protection () +{ + Glib::RefPtr<Action> act = ActionManager::get_action ("options", X_("DenormalProtection")); + if (act) { + Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act); + + if (tact && !tact->get_active()) { + tact->set_active (Config->get_denormal_protection()); + } + } +} + +void ARDOUR_UI::map_denormal_model () { const char* on = 0; @@ -762,6 +788,21 @@ ARDOUR_UI::map_output_auto_connect () } void +ARDOUR_UI::map_only_copy_imported_files () +{ + Glib::RefPtr<Action> act = ActionManager::get_action ("options", X_("OnlyCopyImportedFiles")); + if (act) { + Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act); + + if (tact && !tact->get_active()) { + tact->set_active (Config->get_only_copy_imported_files()); + } + } + +} + + +void ARDOUR_UI::map_meter_falloff () { const char* action = X_("MeterFalloffMedium"); @@ -995,12 +1036,16 @@ ARDOUR_UI::parameter_changed (const char* parameter_name) ActionManager::map_some_state ("options", "PeriodicSafetyBackups", &Configuration::get_periodic_safety_backups); } else if (PARAM_IS ("stop-recording-on-xrun")) { ActionManager::map_some_state ("options", "StopRecordingOnXrun", &Configuration::get_stop_recording_on_xrun); + } else if (PARAM_IS ("sync-all-route-ordering")) { + ActionManager::map_some_state ("options", "SyncEditorAndMixerTrackOrder", &Configuration::get_sync_all_route_ordering); } else if (PARAM_IS ("stop-at-session-end")) { ActionManager::map_some_state ("options", "StopTransportAtEndOfSession", &Configuration::get_stop_at_session_end); } else if (PARAM_IS ("monitoring-model")) { map_monitor_model (); } else if (PARAM_IS ("denormal-model")) { map_denormal_model (); + } else if (PARAM_IS ("denormal-protection")) { + map_denormal_protection (); } else if (PARAM_IS ("remote-model")) { map_remote_model (); } else if (PARAM_IS ("use-video-sync")) { @@ -1062,8 +1107,9 @@ ARDOUR_UI::parameter_changed (const char* parameter_name) ActionManager::map_some_state ("options", "PrimaryClockDeltaEditCursor", &Configuration::get_primary_clock_delta_edit_cursor); } else if (PARAM_IS ("secondary-clock-delta-edit-cursor")) { ActionManager::map_some_state ("options", "SecondaryClockDeltaEditCursor", &Configuration::get_secondary_clock_delta_edit_cursor); - } - + } else if (PARAM_IS ("only-copy-imported-files")) { + map_only_copy_imported_files (); + } #undef PARAM_IS } diff --git a/gtk2_ardour/arprof b/gtk2_ardour/arprof index 05a469cb17..47c11cdb99 100755 --- a/gtk2_ardour/arprof +++ b/gtk2_ardour/arprof @@ -6,4 +6,4 @@ if [ gprofhelper.c -nt gprofhelper.so ] ; then fi . ardev_common.sh -LDPRELOAD=./gprofhelper.so $EXECUTABLE $* +LDPRELOAD=./gprofhelper.so $EXECUTABLE "$@" diff --git a/gtk2_ardour/arval b/gtk2_ardour/arval index 920e7cb1a8..5661b4cd11 100755 --- a/gtk2_ardour/arval +++ b/gtk2_ardour/arval @@ -1,4 +1,4 @@ #!/bin/sh . ardev_common.sh export ARDOUR_RUNNING_UNDER_VALGRIND=TRUE -exec valgrind --num-callers=50 --tool=memcheck $EXECUTABLE --novst $* +exec valgrind --num-callers=50 --tool=memcheck $EXECUTABLE --novst "$@" diff --git a/gtk2_ardour/audio_region_view.cc b/gtk2_ardour/audio_region_view.cc index dc6de8d0f6..4bc10e93ec 100644 --- a/gtk2_ardour/audio_region_view.cc +++ b/gtk2_ardour/audio_region_view.cc @@ -28,6 +28,8 @@ #include <ardour/audioregion.h> #include <ardour/audiosource.h> #include <ardour/audio_diskstream.h> +#include <ardour/profile.h> + #include <pbd/memento_command.h> #include <pbd/stacktrace.h> @@ -177,7 +179,9 @@ AudioRegionView::init (Gdk::Color& basic_color, bool wfd) line_name += ':'; line_name += "gain"; - gain_line = new AudioRegionGainLine (line_name, trackview.session(), *this, *group, audio_region()->envelope()); + if (!Profile->get_sae()) { + gain_line = new AudioRegionGainLine (line_name, trackview.session(), *this, *group, audio_region()->envelope()); + } if (!(_flags & EnvelopeVisible)) { gain_line->hide (); @@ -814,6 +818,8 @@ AudioRegionView::create_waves () if (audio_region()->audio_source(n)->peaks_ready (bind (mem_fun(*this, &AudioRegionView::peaks_ready_handler), n), data_ready_connection)) { create_one_wave (n, true); } else { + // we'll get a PeaksReady signal from the source in the future + // and will call create_one_wave(n) then. } } else { create_one_wave (n, true); diff --git a/gtk2_ardour/audio_streamview.cc b/gtk2_ardour/audio_streamview.cc index bbe573dab2..22f8fa6239 100644 --- a/gtk2_ardour/audio_streamview.cc +++ b/gtk2_ardour/audio_streamview.cc @@ -74,6 +74,7 @@ AudioStreamView::AudioStreamView (AudioTimeAxisView& tv) use_rec_regions = tv.editor.show_waveforms_recording (); + } AudioStreamView::~AudioStreamView () diff --git a/gtk2_ardour/axis_view.h b/gtk2_ardour/axis_view.h index 51f744c4e5..e64ef99b16 100644 --- a/gtk2_ardour/axis_view.h +++ b/gtk2_ardour/axis_view.h @@ -62,6 +62,9 @@ class AxisView : public virtual Selectable sigc::signal<void> Hiding; sigc::signal<void> GoingAway; + void set_old_order_key (uint32_t ok) { _old_order_key = ok; } + uint32_t old_order_key() const { return _old_order_key; } + protected: AxisView (ARDOUR::Session& sess); @@ -84,7 +87,8 @@ class AxisView : public virtual Selectable Gtk::Label name_label; bool _marked_for_display; - + uint32_t _old_order_key; + }; /* class AxisView */ #endif /* __ardour_gtk_axis_view_h__ */ diff --git a/gtk2_ardour/editing.h b/gtk2_ardour/editing.h index d7f8f6ece8..93bbb603ee 100644 --- a/gtk2_ardour/editing.h +++ b/gtk2_ardour/editing.h @@ -34,6 +34,8 @@ #define ZOOMFOCUS(a) /*empty*/ #define DISPLAYCONTROL(a) /*empty*/ #define IMPORTMODE(a) /*empty*/ +#define IMPORTPOSITION(a) +#define IMPORTDISPOSITION(a) namespace Editing { @@ -135,6 +137,7 @@ DisplayControl str2displaycontrol (const std::string &); #undef DISPLAYCONTROL #define DISPLAYCONTROL(a) /*empty*/ + // IMPORTMODE #undef IMPORTMODE #define IMPORTMODE(a) a, @@ -142,13 +145,29 @@ enum ImportMode { #include "editing_syms.h" }; -extern const char *importmodestrs[]; -inline const char* enum2str(ImportMode m) {return importmodestrs[m];} -ImportMode str2importmode (const std::string &); - #undef IMPORTMODE #define IMPORTMODE(a) /*empty*/ +// IMPORTPOSITION +#undef IMPORTPOSITION +#define IMPORTPOSITION(a) a, +enum ImportPosition { + #include "editing_syms.h" +}; + +#undef IMPORTPOSITION +#define IMPORTPOSITION(a) /*empty*/ + +// IMPORTDISPOSITION +#undef IMPORTDISPOSITION +#define IMPORTDISPOSITION(a) a, +enum ImportDisposition { + #include "editing_syms.h" +}; + +#undef IMPORTDISPOSITION +#define IMPORTDISPOSITION(a) /*empty*/ + ///////////////////// // These don't need their state saved. yet... enum CutCopyOp { diff --git a/gtk2_ardour/editing_syms.h b/gtk2_ardour/editing_syms.h index a057f6dd64..a520b0d318 100644 --- a/gtk2_ardour/editing_syms.h +++ b/gtk2_ardour/editing_syms.h @@ -83,3 +83,15 @@ IMPORTMODE(ImportAsRegion=0) IMPORTMODE(ImportToTrack=1) IMPORTMODE(ImportAsTrack=2) IMPORTMODE(ImportAsTapeTrack=3) + +// if this is changed, remember to update the string table in sfdb_ui.cc +IMPORTPOSITION(ImportAtTimestamp=0) +IMPORTPOSITION(ImportAtEditCursor=1) +IMPORTPOSITION(ImportAtPlayhead=2) +IMPORTPOSITION(ImportAtStart=3) + +// if this is changed, remember to update the string table in sfdb_ui.cc +IMPORTDISPOSITION(ImportDistinctFiles=0) +IMPORTDISPOSITION(ImportMergeFiles=1) +IMPORTDISPOSITION(ImportSerializeFiles=2) +IMPORTDISPOSITION(ImportDistinctChannels=3) diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index 031262c41e..f0a2bfa809 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -25,6 +25,8 @@ #include <string> #include <algorithm> +#include <boost/none.hpp> + #include <sigc++/bind.h> #include <pbd/convert.h> @@ -56,6 +58,7 @@ #include <ardour/session_state_utils.h> #include <ardour/tempo.h> #include <ardour/utils.h> +#include <ardour/profile.h> #include <control_protocol/control_protocol.h> @@ -77,6 +80,7 @@ #include "crossfade_edit.h" #include "canvas_impl.h" #include "actions.h" +#include "sfdb_ui.h" #include "gui_thread.h" #ifdef FFT_ANALYSIS @@ -139,8 +143,8 @@ static const gchar *_zoom_focus_strings[] = { N_("Left"), N_("Right"), N_("Center"), - N_("Play"), - N_("Edit"), + N_("Playhead"), + N_("Edit Cursor"), 0 }; @@ -267,6 +271,7 @@ Editor::Editor () autoscroll_active = false; autoscroll_timeout_tag = -1; interthread_progress_window = 0; + logo_item = 0; #ifdef FFT_ANALYSIS analysis_window = 0; @@ -278,6 +283,7 @@ Editor::Editor () _show_waveforms_recording = true; first_action_message = 0; export_dialog = 0; + export_range_markers_dialog = 0; show_gain_after_trim = false; ignore_route_list_reorder = false; no_route_list_redisplay = false; @@ -302,6 +308,8 @@ Editor::Editor () new_transport_marker_menu = 0; editor_mixer_strip_width = Wide; show_editor_mixer_when_tracks_arrive = false; + region_edit_menu_split_multichannel_item = 0; + region_edit_menu_split_item = 0; temp_location = 0; leftmost_frame = 0; ignore_mouse_mode_toggle = false; @@ -321,6 +329,17 @@ Editor::Editor () _dragging_playhead = false; _dragging_hscrollbar = false; + _scrubbing = false; + mouse_direction = 1; + mouse_speed_update = -1; + mouse_speed_size = 16; + mouse_speed = new double[mouse_speed_size]; + memset (mouse_speed, 0, sizeof(double) * mouse_speed_size); + mouse_speed_entries = 0; + + sfbrowser = 0; + ignore_route_order_sync = false; + location_marker_color = ARDOUR_UI::config()->canvasvar_LocationMarker.get(); location_range_color = ARDOUR_UI::config()->canvasvar_LocationRange.get(); location_cd_marker_color = ARDOUR_UI::config()->canvasvar_LocationCDMarker.get(); @@ -345,7 +364,7 @@ Editor::Editor () edit_controls_vbox.set_spacing (0); horizontal_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::canvas_horizontally_scrolled)); - vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling)); + vertical_adjustment.signal_value_changed().connect (mem_fun(*this, &Editor::tie_vertical_scrolling), true); track_canvas.set_hadjustment (horizontal_adjustment); track_canvas.set_vadjustment (vertical_adjustment); @@ -688,6 +707,9 @@ Editor::Editor () set_name ("EditorWindow"); add_accel_group (ActionManager::ui_manager->get_accel_group()); + status_bar_hpacker.show (); + + vpacker.pack_end (status_bar_hpacker, false, false); vpacker.pack_end (global_hpacker, true, true); /* register actions now so that set_state() can find them and set toggles/checks etc */ @@ -756,6 +778,7 @@ Editor::Editor () ControlProtocol::ScrollTimeline.connect (mem_fun (*this, &Editor::control_scroll)); Config->ParameterChanged.connect (mem_fun (*this, &Editor::parameter_changed)); + Route::SyncOrderKeys.connect (mem_fun (*this, &Editor::sync_order_keys)); constructed = true; instant_save (); @@ -854,9 +877,27 @@ void Editor::tie_vertical_scrolling () { double y1 = vertical_adjustment.get_value(); + + playhead_cursor->set_y_axis (y1); + edit_cursor->set_y_axis (y1); + if (logo_item) { + logo_item->property_y() = y1; + } + controls_layout.get_vadjustment()->set_value (y1); - playhead_cursor->set_y_axis(y1); - edit_cursor->set_y_axis(y1); + +#ifdef GTKOSX + /* the way idle updates and immediate window flushing work on GTK-Quartz + requires that we force an immediate redraw right here. The controls + layout will do the same all by itself, as does the canvas widget, but + most of the time, the canvas itself hasn't updated itself because its + idle handler hasn't run. consequently, the call that its layout makes + to gdk_window_process_updates() finds nothing to do. here, we force + the update to happen, then request a flush of the new window state. + */ + track_canvas.update_now (); + gdk_window_process_updates (GTK_LAYOUT(track_canvas.gobj())->bin_window, true); +#endif } void @@ -912,40 +953,65 @@ Editor::control_scroll (float fraction) } double step = fraction * current_page_frames(); - nframes_t target; - if ((fraction < 0.0f) && (session->transport_frame() < (nframes_t) fabs(step))) { - target = 0; - } else if ((fraction > 0.0f) && (max_frames - session->transport_frame() < step)) { - target = (max_frames - (current_page_frames()*2)); // allow room for slop in where the PH is on the screen + /* + _control_scroll_target is an optional<T> + + it acts like a pointer to an nframes_t, with + a operator conversion to boolean to check + that it has a value could possibly use + playhead_cursor->current_frame to store the + value and a boolean in the class to know + when it's out of date + */ + + if (!_control_scroll_target) { + _control_scroll_target = session->transport_frame(); + _dragging_playhead = true; + } + + if ((fraction < 0.0f) && (*_control_scroll_target < (nframes_t) fabs(step))) { + *_control_scroll_target = 0; + } else if ((fraction > 0.0f) && (max_frames - *_control_scroll_target < step)) { + *_control_scroll_target = max_frames - (current_page_frames()*2); // allow room for slop in where the PH is on the screen } else { - target = (session->transport_frame() + (nframes_t) floor ((fraction * current_page_frames()))); + *_control_scroll_target += (nframes_t) floor (step); } /* move visuals, we'll catch up with it later */ - playhead_cursor->set_position (target); - - if (target > (current_page_frames() / 2)) { + playhead_cursor->set_position (*_control_scroll_target); + UpdateAllTransportClocks (*_control_scroll_target); + + if (*_control_scroll_target > (current_page_frames() / 2)) { /* try to center PH in window */ - reset_x_origin (target - (current_page_frames()/2)); + reset_x_origin (*_control_scroll_target - (current_page_frames()/2)); } else { reset_x_origin (0); } - /* cancel the existing */ + /* + Now we do a timeout to actually bring the session to the right place + according to the playhead. This is to avoid reading disk buffers on every + call to control_scroll, which is driven by ScrollTimeline and therefore + probably by a control surface wheel which can generate lots of events. + */ + /* cancel the existing timeout */ control_scroll_connection.disconnect (); - /* add the next one */ + /* add the next timeout */ - control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), target), 50); + control_scroll_connection = Glib::signal_timeout().connect (bind (mem_fun (*this, &Editor::deferred_control_scroll), *_control_scroll_target), 250); } bool Editor::deferred_control_scroll (nframes_t target) { - session->request_locate (target); + session->request_locate (*_control_scroll_target, session->transport_rolling()); + // reset for next stream + _control_scroll_target = boost::none; + _dragging_playhead = false; return false; } @@ -1153,6 +1219,10 @@ Editor::connect_to_session (Session *t) session->locations()->StateChanged.connect (mem_fun(*this, &Editor::refresh_location_display_s)); session->locations()->end_location()->changed.connect (mem_fun(*this, &Editor::end_location_changed)); + if (sfbrowser) { + sfbrowser->set_session (session); + } + handle_new_duration (); redisplay_regions (); @@ -1271,12 +1341,18 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i } items.push_back (SeparatorElem()); - - items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow))); + + if (Profile->get_sae()) { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); + } else { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Fast))); + items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogB))); + items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::LogA))); + items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_in_shape), AudioRegion::Slow))); + } + break; case FadeOutItem: @@ -1289,11 +1365,16 @@ Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* i items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); - items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); - items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA))); - items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB))); - items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast))); + if (Profile->get_sae()) { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + } else { + items.push_back (MenuElem (_("Linear"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Linear))); + items.push_back (MenuElem (_("Slowest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Slow))); + items.push_back (MenuElem (_("Slow"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogA))); + items.push_back (MenuElem (_("Fast"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::LogB))); + items.push_back (MenuElem (_("Fastest"), bind (mem_fun (*this, &Editor::set_fade_out_shape), AudioRegion::Fast))); + } break; @@ -1486,6 +1567,7 @@ void Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items) { using namespace Menu_Helpers; + sigc::connection fooc; Menu *region_menu = manage (new Menu); MenuList& items = region_menu->items(); region_menu->set_name ("ArdourContextMenu"); @@ -1515,14 +1597,52 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items) items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Lock"), bind (mem_fun (*this, &Editor::set_region_lock), true))); - items.push_back (MenuElem (_("Unlock"), bind (mem_fun (*this, &Editor::set_region_lock), false))); - items.push_back (MenuElem (_("Lock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), true))); - items.push_back (MenuElem (_("Unlock Position"), bind (mem_fun (*this, &Editor::set_region_position_lock), false))); - items.push_back (MenuElem (_("Mute"), bind (mem_fun (*this, &Editor::set_region_mute), true))); - items.push_back (MenuElem (_("Unmute"), bind (mem_fun (*this, &Editor::set_region_mute), false))); - items.push_back (MenuElem (_("Opaque"), bind (mem_fun (*this, &Editor::set_region_opaque), true))); - items.push_back (MenuElem (_("Transparent"), bind (mem_fun (*this, &Editor::set_region_opaque), false))); + items.push_back (CheckMenuElem (_("Lock"))); + region_lock_item = static_cast<CheckMenuItem*>(&items.back()); + fooc = region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_lock)); + +#if FIXUP_REGION_MENU + if (region->locked()) { + fooc.block (true); + region_lock_item->set_active(); + fooc.block (false); + } +#endif + + items.push_back (CheckMenuElem (_("Lock Position"))); + region_lock_position_item = static_cast<CheckMenuItem*>(&items.back()); + fooc = region_lock_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_position_lock)); +#if FIXUP_REGION_MENU + if (region->locked()) { + fooc.block (true); + region_lock_position_item->set_active(); + fooc.block (false); + } +#endif + + items.push_back (CheckMenuElem (_("Mute"))); + region_mute_item = static_cast<CheckMenuItem*>(&items.back()); + fooc = region_mute_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_mute)); +#if FIXUP_REGION_MENU + if (region->muted()) { + fooc.block (true); + region_mute_item->set_active(); + fooc.block (false); + } +#endif + + if (!Profile->get_sae()) { + items.push_back (CheckMenuElem (_("Opaque"))); + region_opaque_item = static_cast<CheckMenuItem*>(&items.back()); + fooc = region_opaque_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_region_opaque)); +#if FIXUP_REGION_MENU + if (region->opaque()) { + fooc.block (true); + region_opaque_item->set_active(); + fooc.block (false); + } +#endif + } /* We allow "Original position" if at least one region is not at its natural position @@ -1549,16 +1669,48 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items) MenuList& envelopes_items = envelopes_menu->items(); envelopes_menu->set_name ("ArdourContextMenu"); - envelopes_items.push_back (MenuElem (_("Reset"), mem_fun(*this, &Editor::reset_region_gain_envelopes))); - envelopes_items.push_back (MenuElem (_("Visible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), true))); - envelopes_items.push_back (MenuElem (_("Invisible"), bind (mem_fun (*this, &Editor::set_gain_envelope_visibility), false))); - envelopes_items.push_back (MenuElem (_("Active"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), true))); - envelopes_items.push_back (MenuElem (_("Inactive"), bind (mem_fun (*this, &Editor::set_gain_envelope_active), false))); +#if FIXUP_REGION_MENU + + XXX NEED TO RESOLVE ONE v. MANY REGION ISSUE + + RegionView* rv = sv->find_view (ar); + AudioRegionView* arv = dynamic_cast<AudioRegionView*>(rv); + + if (!Profile->get_sae()) { + envelopes_items.push_back (MenuElem (_("Reset Envelope"), mem_fun(*this, &Editor::reset_region_gain_envelopes))); + + envelopes_items.push_back (CheckMenuElem (_("Envelope Visible"))); + region_envelope_visible_item = static_cast<CheckMenuItem*> (&items.back()); + fooc = region_envelope_visible_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_visibility)); + if (arv->envelope_visible()) { + fooc.block (true); + region_envelope_visible_item->set_active (true); + fooc.block (false); + } + + envelopes_items.push_back (CheckMenuElem (_("Envelope Active"))); + region_envelope_active_item = static_cast<CheckMenuItem*> (&items.back()); + fooc = region_envelope_active_item->signal_activate().connect (mem_fun(*this, &Editor::toggle_gain_envelope_active)); + + if (ar->envelope_active()) { + fooc.block (true); + region_envelope_active_item->set_active (true); + fooc.block (false); + } + + items.push_back (SeparatorElem()); + } +#endif items.push_back (MenuElem (_("Envelopes"), *envelopes_menu)); - items.push_back (MenuElem (_("Denormalize"), mem_fun (*this, &Editor::denormalize_regions))); - items.push_back (MenuElem (_("Normalize"), mem_fun (*this, &Editor::normalize_regions))); +#if FIXUP_REGION_MENU + if (ar->scale_amplitude() != 1.0f) { + items.push_back (MenuElem (_("DeNormalize"), mem_fun(*this, &Editor::denormalize_regions))); + } else { + items.push_back (MenuElem (_("Normalize"), mem_fun(*this, &Editor::normalize_regions))); + } +#endif } /* Find out if we have a selected MIDI region */ @@ -1631,7 +1783,7 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) selection_menu->set_name ("ArdourContextMenu"); items.push_back (MenuElem (_("Play range"), mem_fun(*this, &Editor::play_selection))); - items.push_back (MenuElem (_("Loop range"), mem_fun(*this, &Editor::set_route_loop_selection))); + items.push_back (MenuElem (_("Loop range"), bind (mem_fun(*this, &Editor::set_loop_from_selection), true))); #ifdef FFT_ANALYSIS items.push_back (SeparatorElem()); @@ -1639,15 +1791,22 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) #endif items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Separate range to track"), mem_fun(*this, &Editor::separate_region_from_selection))); - items.push_back (MenuElem (_("Separate range to region list"), mem_fun(*this, &Editor::new_region_from_selection))); + items.push_back (MenuElem (_("Extend Range to End of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_end_of_region), false))); + items.push_back (MenuElem (_("Extend Range to Start of Region"), bind (mem_fun(*this, &Editor::extend_selection_to_start_of_region), false))); + + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Convert to region in-place"), mem_fun(*this, &Editor::separate_region_from_selection))); + items.push_back (MenuElem (_("Convert to region in region list"), mem_fun(*this, &Editor::new_region_from_selection))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Select all in range"), mem_fun(*this, &Editor::select_all_selectables_using_time_selection))); + + items.push_back (SeparatorElem()); + items.push_back (MenuElem (_("Set loop from selection"), bind (mem_fun(*this, &Editor::set_loop_from_selection), false))); + items.push_back (MenuElem (_("Set punch from selection"), mem_fun(*this, &Editor::set_punch_from_selection))); + items.push_back (SeparatorElem()); - items.push_back (MenuElem (_("Add range markers"), mem_fun (*this, &Editor::add_location_from_selection))); - items.push_back (MenuElem (_("Set range to loop range"), mem_fun(*this, &Editor::set_selection_from_loop))); - items.push_back (MenuElem (_("Set range to punch range"), mem_fun(*this, &Editor::set_selection_from_punch))); + items.push_back (MenuElem (_("Add Range Markers"), mem_fun (*this, &Editor::add_location_from_selection))); items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Crop region to range"), mem_fun(*this, &Editor::crop_region_to_selection))); items.push_back (MenuElem (_("Fill range with region"), mem_fun(*this, &Editor::region_fill_selection))); @@ -1656,9 +1815,6 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items) items.push_back (SeparatorElem()); items.push_back (MenuElem (_("Bounce range"), mem_fun(*this, &Editor::bounce_range_selection))); items.push_back (MenuElem (_("Export range"), mem_fun(*this, &Editor::export_selection))); - - edit_items.push_back (MenuElem (_("Range"), *selection_menu)); - edit_items.push_back (SeparatorElem()); } /** Add context menu items relevant to busses or audio tracks. @@ -2685,8 +2841,10 @@ Editor::convert_drop_to_paths (vector<ustring>& paths, vector<ustring> uris = data.get_uris(); + cerr << "there were " << uris.size() << " in that drag data\n"; + if (uris.empty()) { - + /* This is seriously fucked up. Nautilus doesn't say that its URI lists are actually URI lists. So do it by hand. */ @@ -2736,10 +2894,34 @@ Editor::convert_drop_to_paths (vector<ustring>& paths, } for (vector<ustring>::iterator i = uris.begin(); i != uris.end(); ++i) { + if ((*i).substr (0,7) == "file://") { - string p = *i; + + ustring p = *i; PBD::url_decode (p); - paths.push_back (p.substr (7)); + + // scan forward past three slashes + + ustring::size_type slashcnt = 0; + ustring::size_type n = 0; + ustring::iterator x = p.begin(); + + while (slashcnt < 3 && x != p.end()) { + if ((*x) == '/') { + slashcnt++; + } else if (slashcnt == 3) { + break; + } + ++n; + ++x; + } + + if (slashcnt != 3 || x == p.end()) { + error << _("malformed URL passed to drag-n-drop code") << endmsg; + continue; + } + + paths.push_back (p.substr (n - 1)); } } @@ -3838,3 +4020,60 @@ Editor::edit_cursor_position(bool sync) return edit_cursor->current_frame; } + +void +Editor::set_loop_range (nframes_t start, nframes_t end, string cmd) +{ + if (!session) return; + + begin_reversible_command (cmd); + + Location* tll; + + if ((tll = transport_loop_location()) == 0) { + Location* loc = new Location (start, end, _("Loop"), Location::IsAutoLoop); + XMLNode &before = session->locations()->get_state(); + session->locations()->add (loc, true); + session->set_auto_loop_location (loc); + XMLNode &after = session->locations()->get_state(); + session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after)); + } + else { + XMLNode &before = tll->get_state(); + tll->set_hidden (false, this); + tll->set (start, end); + XMLNode &after = tll->get_state(); + session->add_command (new MementoCommand<Location>(*tll, &before, &after)); + } + + commit_reversible_command (); +} + +void +Editor::set_punch_range (nframes_t start, nframes_t end, string cmd) +{ + if (!session) return; + + begin_reversible_command (cmd); + + Location* tpl; + + if ((tpl = transport_punch_location()) == 0) { + Location* loc = new Location (start, end, _("Loop"), Location::IsAutoPunch); + XMLNode &before = session->locations()->get_state(); + session->locations()->add (loc, true); + session->set_auto_loop_location (loc); + XMLNode &after = session->locations()->get_state(); + session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after)); + } + else { + XMLNode &before = tpl->get_state(); + tpl->set_hidden (false, this); + tpl->set (start, end); + XMLNode &after = tpl->get_state(); + session->add_command (new MementoCommand<Location>(*tpl, &before, &after)); + } + + commit_reversible_command (); +} + diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 976ad68188..63cbc042ac 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -26,9 +26,14 @@ #include <string> #include <sys/time.h> +#include <glibmm/ustring.h> + +#include <boost/optional.hpp> + #include <libgnomecanvasmm/canvas.h> #include <libgnomecanvasmm/group.h> #include <libgnomecanvasmm/line.h> +#include <libgnomecanvasmm/pixbuf.h> #include <cmath> @@ -100,6 +105,7 @@ class MixerStrip; class StreamView; class AudioStreamView; class ControlPoint; +class SoundFileOmega; #ifdef FFT_ANALYSIS class AnalysisWindow; #endif @@ -249,6 +255,7 @@ class Editor : public PublicEditor void add_toplevel_controls (Gtk::Container&); + Gtk::HBox& get_status_bar_packer() { return status_bar_hpacker; } void set_zoom_focus (Editing::ZoomFocus); Editing::ZoomFocus get_zoom_focus () const { return zoom_focus; } @@ -298,6 +305,7 @@ class Editor : public PublicEditor void toggle_waveform_visibility (); void toggle_waveforms_while_recording (); void toggle_measure_visibility (); + void toggle_logo_visibility (); /* SMPTE timecode & video sync */ @@ -346,6 +354,8 @@ class Editor : public PublicEditor void reposition_and_zoom (nframes_t, double); nframes_t edit_cursor_position(bool); + bool update_mouse_speed (); + bool decelerate_mouse_speed (); protected: void map_transport_state (); @@ -360,6 +370,9 @@ class Editor : public PublicEditor ARDOUR::Session *session; bool constructed; + // to keep track of the playhead position for control_scroll + boost::optional<nframes_t> _control_scroll_target; + PlaylistSelector* _playlist_selector; void set_frames_per_unit (double); @@ -464,6 +477,9 @@ class Editor : public PublicEditor void set_selected_regionview_from_region_list (boost::shared_ptr<ARDOUR::Region> region, Selection::Operation op = Selection::Set); void collect_new_region_view (RegionView *); + Gtk::MenuItem* region_edit_menu_split_item; + Gtk::MenuItem* region_edit_menu_split_multichannel_item; + void popup_track_context_menu (int, int, nframes_t); Gtk::Menu* build_track_context_menu (nframes_t); void add_bus_or_audio_track_context_items (Gtk::Menu_Helpers::MenuList&); @@ -507,6 +523,7 @@ class Editor : public PublicEditor Gtk::Frame time_button_frame; ArdourCanvas::Group *minsec_group; + ArdourCanvas::Pixbuf *logo_item; ArdourCanvas::Group *bbt_group; ArdourCanvas::Group *smpte_group; ArdourCanvas::Group *frame_group; @@ -671,6 +688,7 @@ class Editor : public PublicEditor Gtk::VBox track_canvas_vbox; Gtk::VBox time_canvas_vbox; Gtk::VBox edit_controls_vbox; + Gtk::HBox edit_controls_hbox; void control_scroll (float); bool deferred_control_scroll (nframes_t); @@ -728,6 +746,7 @@ class Editor : public PublicEditor Gtk::Menu *region_list_menu; Gtk::ScrolledWindow region_list_scroller; + Gtk::Frame region_list_frame; bool region_list_display_key_press (GdkEventKey *); bool region_list_display_key_release (GdkEventKey *); @@ -868,10 +887,10 @@ class Editor : public PublicEditor /* EDITING OPERATIONS */ void reset_point_selection (); - void set_region_mute (bool); - void set_region_lock (bool); - void set_region_position_lock (bool); - void set_region_opaque (bool); + void toggle_region_mute (); + void toggle_region_lock (); + void toggle_region_opaque (); + void toggle_region_position_lock (); void raise_region_to_top (); void lower_region_to_bottom (); void split_region (); @@ -951,15 +970,32 @@ class Editor : public PublicEditor void insert_region_list_drag (boost::shared_ptr<ARDOUR::Region>, int x, int y); void insert_region_list_selection (float times); + /* import & embed */ + void add_external_audio_action (Editing::ImportMode); + void external_audio_dialog (); + bool check_multichannel_status (const std::vector<Glib::ustring>& paths); + + SoundFileOmega* sfbrowser; + + void bring_in_external_audio (Editing::ImportMode mode, nframes64_t& pos); + void do_import (vector<Glib::ustring> paths, Editing::ImportDisposition, Editing::ImportMode mode, ARDOUR::SrcQuality, nframes64_t&); + + void _do_embed (vector<Glib::ustring> paths, Editing::ImportDisposition, Editing::ImportMode mode, nframes64_t&); + void do_embed (vector<Glib::ustring> paths, Editing::ImportDisposition, Editing::ImportMode mode, nframes64_t&); + bool idle_do_embed (vector<Glib::ustring> paths, Editing::ImportDisposition, Editing::ImportMode mode, nframes64_t&); - void bring_in_external_audio (Editing::ImportMode mode, ARDOUR::AudioTrack*, nframes_t& pos, bool prompt); - void do_import (vector<Glib::ustring> paths, bool split, Editing::ImportMode mode, ARDOUR::AudioTrack*, nframes_t&, bool); - void do_embed (vector<Glib::ustring> paths, bool split, Editing::ImportMode mode, ARDOUR::AudioTrack*, nframes_t&, bool); - int import_sndfile (vector<Glib::ustring> paths, Editing::ImportMode mode, ARDOUR::AudioTrack* track, nframes_t& pos); - int embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_files, bool& check_sample_rate, Editing::ImportMode mode, - ARDOUR::AudioTrack* track, nframes_t& pos, bool prompt); - int finish_bringing_in_audio (boost::shared_ptr<ARDOUR::AudioRegion> region, uint32_t, uint32_t, ARDOUR::AudioTrack* track, nframes_t& pos, Editing::ImportMode mode); + int import_sndfiles (vector<Glib::ustring> paths, Editing::ImportMode mode, ARDOUR::SrcQuality, nframes64_t& pos, + int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::AudioTrack>&); + int embed_sndfiles (vector<Glib::ustring> paths, bool multiple_files, bool& check_sample_rate, Editing::ImportMode mode, + nframes64_t& pos, int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::AudioTrack>&); + + int add_sources (vector<Glib::ustring> paths, ARDOUR::SourceList& sources, nframes64_t& pos, Editing::ImportMode, + int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::AudioTrack>&, bool add_channel_suffix); + int finish_bringing_in_audio (boost::shared_ptr<ARDOUR::AudioRegion> region, uint32_t, uint32_t, nframes64_t& pos, Editing::ImportMode mode, + boost::shared_ptr<ARDOUR::AudioTrack>& existing_track); + + boost::shared_ptr<ARDOUR::AudioTrack> get_nth_selected_audio_track (int nth) const; /* generic interthread progress window */ @@ -1030,7 +1066,11 @@ class Editor : public PublicEditor void add_location_from_audio_region (); void add_location_from_selection (); - void set_route_loop_selection (); + void set_loop_from_selection (bool play); + void set_punch_from_selection (); + + void set_loop_range (nframes_t start, nframes_t end, std::string cmd); + void set_punch_range (nframes_t start, nframes_t end, std::string cmd); void add_location_from_playhead_cursor (); @@ -1040,6 +1080,18 @@ class Editor : public PublicEditor void start_scrolling (); void stop_scrolling (); + bool _scrubbing; + bool have_full_mouse_speed; + nframes64_t last_scrub_frame; + double last_scrub_time; + int mouse_speed_update; + double mouse_direction; + double compute_mouse_speed (); + void add_mouse_speed (double, double); + double* mouse_speed; + size_t mouse_speed_entries; + size_t mouse_speed_size; + void keyboard_selection_begin (); void keyboard_selection_finish (bool add); bool have_pending_keyboard_selection; @@ -1488,10 +1540,12 @@ class Editor : public PublicEditor add (text); add (visible); add (tv); + add (route); } Gtk::TreeModelColumn<Glib::ustring> text; Gtk::TreeModelColumn<bool> visible; Gtk::TreeModelColumn<TimeAxisView*> tv; + Gtk::TreeModelColumn<boost::shared_ptr<ARDOUR::Route> > route; }; RouteDisplayModelColumns route_display_columns; @@ -1502,11 +1556,16 @@ class Editor : public PublicEditor Gtk::ScrolledWindow route_list_scroller; Gtk::Menu* route_list_menu; + void sync_order_keys (); + bool ignore_route_order_sync; + bool route_list_display_button_press (GdkEventButton*); bool route_list_selection_filter (const Glib::RefPtr<Gtk::TreeModel>& model, const Gtk::TreeModel::Path& path, bool yn); void route_list_change (const Gtk::TreeModel::Path&,const Gtk::TreeModel::iterator&); void route_list_delete (const Gtk::TreeModel::Path&); + void track_list_reorder (const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter, int* new_order); + void initial_route_list_display (); void redisplay_route_list(); void route_list_reordered (const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter, int* what); @@ -1858,14 +1917,15 @@ class Editor : public PublicEditor bool _new_regionviews_show_envelope; - void set_gain_envelope_visibility (bool); - void set_gain_envelope_active (bool); + void toggle_gain_envelope_visibility (); + void toggle_gain_envelope_active (); void reset_region_gain_envelopes (); Gtk::CheckMenuItem* region_envelope_visible_item; Gtk::CheckMenuItem* region_envelope_active_item; Gtk::CheckMenuItem* region_mute_item; Gtk::CheckMenuItem* region_lock_item; + Gtk::CheckMenuItem* region_lock_position_item; Gtk::CheckMenuItem* region_opaque_item; bool on_key_press_event (GdkEventKey*); @@ -1876,6 +1936,8 @@ class Editor : public PublicEditor Glib::RefPtr<Gtk::Action> redo_action; void history_changed (); + + Gtk::HBox status_bar_hpacker; }; #endif /* __ardour_editor_h__ */ diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 7fe0de9aae..051af2c898 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -66,7 +66,6 @@ Editor::register_actions () /* add named actions for the editor */ - act = ActionManager::register_toggle_action (editor_actions, "show-editor-mixer", _("Show Editor Mixer"), mem_fun (*this, &Editor::editor_mixer_button_toggled)); ActionManager::session_sensitive_actions.push_back (act); act = ActionManager::register_toggle_action (editor_actions, "show-editor-list", _("Show Editor List"), mem_fun (*this, &Editor::editor_list_button_toggled)); @@ -385,19 +384,17 @@ Editor::register_actions () act = ActionManager::register_action (editor_actions, X_("addExternalAudioToRegionList"), _("Add External Audio"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion)); ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (editor_actions, X_("addExternalAudioAsRegion"), _("as Region(s)"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsRegion)); - ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (editor_actions, X_("addExternalAudioAsTrack"), _("as Tracks"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsTrack)); - ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (editor_actions, X_("addExternalAudioAsTapeTrack"), _("as Tape Tracks"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportAsTapeTrack)); - ActionManager::session_sensitive_actions.push_back (act); - act = ActionManager::register_action (editor_actions, X_("addExternalAudioToTrack"), _("to Tracks"), bind (mem_fun(*this, &Editor::add_external_audio_action), ImportToTrack)); - ActionManager::session_sensitive_actions.push_back (act); ActionManager::register_toggle_action (editor_actions, X_("ToggleWaveformVisibility"), _("Show Waveforms"), mem_fun (*this, &Editor::toggle_waveform_visibility)); ActionManager::register_toggle_action (editor_actions, X_("ToggleWaveformsWhileRecording"), _("Show Waveforms While Recording"), mem_fun (*this, &Editor::toggle_waveforms_while_recording)); act = ActionManager::register_toggle_action (editor_actions, X_("ToggleMeasureVisibility"), _("Show Measures"), mem_fun (*this, &Editor::toggle_measure_visibility)); + /* if there is a logo in the editor canvas, its always visible at startup */ + + act = ActionManager::register_toggle_action (editor_actions, X_("ToggleLogoVisibility"), _("Show Logo"), mem_fun (*this, &Editor::toggle_logo_visibility)); + Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act); + tact->set_active (true); + RadioAction::Group layer_model_group; ActionManager::register_radio_action (editor_actions, layer_model_group, X_("LayerLaterHigher"), _("Later is Higher"), bind (mem_fun (*this, &Editor::set_layer_model), LaterHigher)); @@ -474,6 +471,23 @@ Editor::toggle_measure_visibility () } void +Editor::toggle_logo_visibility () +{ + Glib::RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleLogoVisibility")); + + if (act) { + Glib::RefPtr<ToggleAction> tact = Glib::RefPtr<ToggleAction>::cast_dynamic(act); + if (logo_item) { + if (tact->get_active()) { + logo_item->show (); + } else { + logo_item->hide (); + } + } + } +} + +void Editor::set_crossfade_model (CrossfadeModel model) { RefPtr<Action> act; diff --git a/gtk2_ardour/editor_audio_import.cc b/gtk2_ardour/editor_audio_import.cc index 263ac0c31f..0dc0d707c0 100644 --- a/gtk2_ardour/editor_audio_import.cc +++ b/gtk2_ardour/editor_audio_import.cc @@ -19,9 +19,12 @@ #include <sys/types.h> #include <sys/stat.h> +#include <sys/time.h> #include <errno.h> #include <unistd.h> +#include <sndfile.h> + #include <pbd/pthread_utils.h> #include <pbd/basename.h> #include <pbd/shortpath.h> @@ -63,166 +66,307 @@ using Glib::ustring; /* Functions supporting the incorporation of external (non-captured) audio material into ardour */ void -Editor::add_external_audio_action (ImportMode mode) +Editor::add_external_audio_action (ImportMode mode_hint) { - nframes_t& pos = edit_cursor->current_frame; - boost::shared_ptr<AudioTrack> track; - - if (!selection->tracks.empty()) { - AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(selection->tracks.front()); - if (atv) { - track = atv->audio_track(); - } + if (session == 0) { + MessageDialog msg (0, _("You can't import or embed an audiofile until you have a session loaded.")); + msg.run (); + return; + } + + if (sfbrowser == 0) { + sfbrowser = new SoundFileOmega (*this, _("Add existing audio"), session, 0, true, mode_hint); + } else { + sfbrowser->set_mode (mode_hint); } - bring_in_external_audio (mode, track.get(), pos, false); + external_audio_dialog (); } void -Editor::bring_in_external_audio (ImportMode mode, AudioTrack* track, nframes_t& pos, bool prompt) +Editor::external_audio_dialog () { + vector<Glib::ustring> paths; + uint32_t track_cnt; + if (session == 0) { MessageDialog msg (0, _("You can't import or embed an audiofile until you have a session loaded.")); msg.run (); return; } + + track_cnt = 0; - SoundFileOmega sfdb (_("Add existing audio to session"), session); - sfdb.set_mode (mode); + for (TrackSelection::iterator x = selection->tracks.begin(); x != selection->tracks.end(); ++x) { + AudioTimeAxisView* atv = dynamic_cast<AudioTimeAxisView*>(*x); + + if (!atv) { + continue; + } else if (atv->is_audio_track()) { + track_cnt++; + } + } + + if (sfbrowser == 0) { + sfbrowser = new SoundFileOmega (*this, _("Add existing audio"), session, track_cnt, true); + } else { + sfbrowser->reset (track_cnt); + } + + sfbrowser->show_all (); + + again: + int response = sfbrowser->run (); - switch (sfdb.run()) { - case SoundFileOmega::ResponseImport: - do_import (sfdb.get_paths(), sfdb.get_split(), sfdb.get_mode(), track, pos, prompt); + switch (response) { + case RESPONSE_APPLY: + // leave the dialog open break; - - case SoundFileOmega::ResponseEmbed: - do_embed (sfdb.get_paths(), sfdb.get_split(), sfdb.get_mode(), track, pos, prompt); + + case RESPONSE_OK: + sfbrowser->hide (); break; default: + // cancel from the browser - we are done + sfbrowser->hide (); + return; + } + + /* lets do it */ + + paths = sfbrowser->get_paths (); + + ImportPosition pos = sfbrowser->get_position (); + ImportMode mode = sfbrowser->get_mode (); + ImportDisposition chns = sfbrowser->get_channel_disposition (); + nframes64_t where; + + switch (pos) { + case ImportAtEditCursor: + where = edit_cursor->current_frame; + break; + case ImportAtTimestamp: + where = -1; + break; + case ImportAtPlayhead: + where = playhead_cursor->current_frame; break; + case ImportAtStart: + where = session->current_start_frame(); + break; + } + + SrcQuality quality = sfbrowser->get_src_quality(); + + if (sfbrowser->copy_files_btn.get_active()) { + do_import (paths, chns, mode, quality, where); + } else { + do_embed (paths, chns, mode, where); + } + + if (response == RESPONSE_APPLY) { + sfbrowser->clear_selection (); + goto again; } } -void -Editor::do_import (vector<ustring> paths, bool split, ImportMode mode, AudioTrack* track, nframes_t& pos, bool prompt) +boost::shared_ptr<AudioTrack> +Editor::get_nth_selected_audio_track (int nth) const { - /* SFDB sets "multichan" to true to indicate "split channels" - so reverse the setting to match the way libardour - interprets it. - */ + AudioTimeAxisView* atv; + TrackSelection::iterator x; - import_status.multichan = !split; + for (x = selection->tracks.begin(); nth > 0 && x != selection->tracks.end(); ++x) { - if (interthread_progress_window == 0) { - build_interthread_progress_window (); + atv = dynamic_cast<AudioTimeAxisView*>(*x); + + if (!atv) { + continue; + } else if (atv->is_audio_track()) { + --nth; + } } + + if (x == selection->tracks.end()) { + atv = dynamic_cast<AudioTimeAxisView*>(selection->tracks.back()); + } else { + atv = dynamic_cast<AudioTimeAxisView*>(*x); + } + + if (!atv || !atv->is_audio_track()) { + return boost::shared_ptr<AudioTrack>(); + } + + return atv->audio_track(); +} +void +Editor::do_import (vector<ustring> paths, ImportDisposition chns, ImportMode mode, SrcQuality quality, nframes64_t& pos) +{ + boost::shared_ptr<AudioTrack> track; vector<ustring> to_import; + bool ok = false; + int nth = 0; - for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - - to_import.clear (); - to_import.push_back (*a); - - import_sndfile (to_import, mode, track, pos); + if (interthread_progress_window == 0) { + build_interthread_progress_window (); } - interthread_progress_window->hide_all (); -} + switch (chns) { + case Editing::ImportDistinctFiles: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { -void -Editor::do_embed (vector<ustring> paths, bool split, ImportMode mode, AudioTrack* track, nframes_t& pos, bool prompt) -{ - bool multiple_files = paths.size() > 1; - bool check_sample_rate = true; - vector<ustring>::iterator a; + to_import.clear (); + to_import.push_back (*a); - for (a = paths.begin(); a != paths.end(); ) { + if (mode == Editing::ImportToTrack) { + track = get_nth_selected_audio_track (nth++); + } + + if (import_sndfiles (to_import, mode, quality, pos, 1, -1, track)) { + goto out; + } - cerr << "Considering embed of " << (*a) << endl; - - Glib::ustring path = *a; - Glib::ustring pair_base; - vector<ustring> to_embed; + } + break; - to_embed.push_back (path); - a = paths.erase (a); + case Editing::ImportDistinctChannels: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - if (path_is_paired (path, pair_base)) { + to_import.clear (); + to_import.push_back (*a); - ustring::size_type len = pair_base.length(); + if (import_sndfiles (to_import, mode, quality, pos, -1, -1, track)) { + goto out; + } - for (vector<Glib::ustring>::iterator b = paths.begin(); b != paths.end(); ) { + } + break; - if (((*b).substr (0, len) == pair_base) && ((*b).length() == path.length())) { + case Editing::ImportMergeFiles: + /* create 1 region from all paths, add to 1 track, + ignore "track" + */ + if (import_sndfiles (paths, mode, quality, pos, 1, 1, track)) { + goto out; + } + break; - to_embed.push_back (*b); - - /* don't process this one again */ + case Editing::ImportSerializeFiles: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - b = paths.erase (b); - break; + to_import.clear (); + to_import.push_back (*a); + + /* create 1 region from this path, add to 1 track, + reuse "track" across paths + */ - } else { - ++b; - } + if (import_sndfiles (to_import, mode, quality, pos, 1, 1, track)) { + goto out; } + } + break; + } - if (to_embed.size() > 1) { + ok = true; + + out: + if (ok) { + session->save_state (""); + } - vector<string> choices; + interthread_progress_window->hide_all (); +} - choices.push_back (string_compose (_("Import as a %1 region"), - to_embed.size() > 2 ? _("multichannel") : _("stereo"))); - choices.push_back (_("Import as multiple regions")); - - Choice chooser (string_compose (_("Paired files detected (%1, %2 ...).\nDo you want to:"), - to_embed[0], - to_embed[1]), - choices); - - if (chooser.run () == 0) { - - /* keep them paired */ +bool +Editor::idle_do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos) +{ + _do_embed (paths, chns, mode, pos); + return false; +} - if (embed_sndfile (to_embed, split, multiple_files, check_sample_rate, mode, track, pos, prompt) < -1) { - break; - } +void +Editor::do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos) +{ +#ifdef GTKOSX + Glib::signal_idle().connect (bind (mem_fun (*this, &Editor::idle_do_embed), paths, chns, mode, pos)); +#else + _do_embed (paths, chns, mode, pos); +#endif +} - } else { +void +Editor::_do_embed (vector<ustring> paths, ImportDisposition chns, ImportMode mode, nframes64_t& pos) +{ + boost::shared_ptr<AudioTrack> track; + bool check_sample_rate = true; + bool ok = false; + vector<ustring> to_embed; + bool multi = paths.size() > 1; + int nth = 0; - /* one thing per file */ + switch (chns) { + case Editing::ImportDistinctFiles: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - vector<ustring> foo; + to_embed.clear (); + to_embed.push_back (*a); - for (vector<ustring>::iterator x = to_embed.begin(); x != to_embed.end(); ++x) { + if (mode == Editing::ImportToTrack) { + track = get_nth_selected_audio_track (nth++); + } - foo.clear (); - foo.push_back (*x); + if (embed_sndfiles (to_embed, multi, check_sample_rate, mode, pos, 1, -1, track) < -1) { + goto out; + } + } + break; + + case Editing::ImportDistinctChannels: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { - if (embed_sndfile (foo, split, multiple_files, check_sample_rate, mode, track, pos, prompt) < -1) { - break; - } - } + to_embed.clear (); + to_embed.push_back (*a); + + if (embed_sndfiles (to_embed, multi, check_sample_rate, mode, pos, -1, -1, track) < -1) { + goto out; } + } + break; - } else { - - if (embed_sndfile (to_embed, split, multiple_files, check_sample_rate, mode, track, pos, prompt) < -1) { - break; + case Editing::ImportMergeFiles: + if (embed_sndfiles (paths, multi, check_sample_rate, mode, pos, 1, 1, track) < -1) { + goto out; + } + break; + + case Editing::ImportSerializeFiles: + for (vector<ustring>::iterator a = paths.begin(); a != paths.end(); ++a) { + + to_embed.clear (); + to_embed.push_back (*a); + + if (embed_sndfiles (to_embed, multi, check_sample_rate, mode, pos, 1, 1, track) < -1) { + goto out; } } + break; } + + ok = true; - if (a == paths.end()) { + out: + if (ok) { session->save_state (""); } } int -Editor::import_sndfile (vector<ustring> paths, ImportMode mode, AudioTrack* track, nframes_t& pos) +Editor::import_sndfiles (vector<ustring> paths, ImportMode mode, SrcQuality quality, nframes64_t& pos, + int target_regions, int target_tracks, boost::shared_ptr<AudioTrack>& track) { WindowTitle title = string_compose (_("importing %1"), paths.front()); @@ -238,7 +382,8 @@ Editor::import_sndfile (vector<ustring> paths, ImportMode mode, AudioTrack* trac import_status.cancel = false; import_status.freeze = false; import_status.done = 0.0; - + import_status.quality = quality; + interthread_progress_connection = Glib::signal_timeout().connect (bind (mem_fun(*this, &Editor::import_progress_timeout), (gpointer) 0), 100); @@ -262,28 +407,31 @@ Editor::import_sndfile (vector<ustring> paths, ImportMode mode, AudioTrack* trac interthread_progress_connection.disconnect (); /* import thread finished - see if we should build a new track */ + + boost::shared_ptr<AudioRegion> r; - if (!import_status.new_regions.empty()) { - boost::shared_ptr<AudioRegion> region = boost::dynamic_pointer_cast<AudioRegion>(import_status.new_regions.front()); - finish_bringing_in_audio (region, region->n_channels(), region->n_channels(), track, pos, mode); + if (import_status.cancel || import_status.sources.empty()) { + goto out; + } + + if (add_sources (paths, import_status.sources, pos, mode, target_regions, target_tracks, track, false) == 0) { + session->save_state (""); } + out: track_canvas.get_window()->set_cursor (*current_canvas_cursor); return 0; } int -Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_files, bool& check_sample_rate, ImportMode mode, - AudioTrack* track, nframes_t& pos, bool prompt) +Editor::embed_sndfiles (vector<Glib::ustring> paths, bool multifile, + bool& check_sample_rate, ImportMode mode, nframes64_t& pos, int target_regions, int target_tracks, + boost::shared_ptr<AudioTrack>& track) { boost::shared_ptr<AudioFileSource> source; SourceList sources; - boost::shared_ptr<AudioRegion> region; string linked_path; SoundFileInfo finfo; - ustring region_name; - uint32_t input_chan = 0; - uint32_t output_chan = 0; int ret = 0; track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH)); @@ -294,10 +442,8 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi ustring path = *p; /* lets see if we can link it into the session */ - - sys::path tmp = session->session_directory().sound_path(); - - tmp /= Glib::path_get_basename(path); + + sys::path tmp = session->session_directory().sound_path() / Glib::path_get_basename(path); linked_path = tmp.to_string(); if (link (path.c_str(), linked_path.c_str()) == 0) { @@ -323,22 +469,21 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi } } } - } /* note that we temporarily truncated _id at the colon */ string error_msg; - + if (!AudioFileSource::get_soundfile_info (path, finfo, error_msg)) { - error << string_compose(_("Editor: cannot open file \"%1\", (%2)"), selection, error_msg ) << endmsg; + error << string_compose(_("Editor: cannot open file \"%1\", (%2)"), path, error_msg ) << endmsg; goto out; } if (check_sample_rate && (finfo.samplerate != (int) session->frame_rate())) { vector<string> choices; - if (multiple_files) { + if (multifile) { choices.push_back (_("Cancel entire import")); choices.push_back (_("Don't embed it")); choices.push_back (_("Embed all without questions")); @@ -390,14 +535,8 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi } track_canvas.get_window()->set_cursor (Gdk::Cursor (Gdk::WATCH)); - ARDOUR_UI::instance()->flush_pending (); - - /* make the proper number of channels in the region */ - - input_chan += finfo.channels; - for (int n = 0; n < finfo.channels; ++n) - { + for (int n = 0; n < finfo.channels; ++n) { try { /* check if we have this thing embedded already */ @@ -409,7 +548,8 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi (DataType::AUDIO, *session, path, n, (mode == ImportAsTapeTrack ? AudioFileSource::Destructive : - AudioFileSource::Flag (0)))); + AudioFileSource::Flag (0)), + true, true)); } else { source = boost::dynamic_pointer_cast<AudioFileSource> (s); } @@ -430,14 +570,74 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi goto out; } - if (sources[0]->natural_position() != 0) { - pos = sources[0]->natural_position(); - } + ret = add_sources (paths, sources, pos, mode, target_regions, target_tracks, track, true); - region_name = region_name_from_path (paths.front(), (sources.size() > 1)); - - region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (sources, 0, sources[0]->length(), region_name, 0, - Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External))); + out: + track_canvas.get_window()->set_cursor (*current_canvas_cursor); + return ret; +} + +int +Editor::add_sources (vector<Glib::ustring> paths, SourceList& sources, nframes64_t& pos, ImportMode mode, + int target_regions, int target_tracks, boost::shared_ptr<AudioTrack>& track, bool add_channel_suffix) +{ + vector<boost::shared_ptr<AudioRegion> > regions; + ustring region_name; + uint32_t input_chan = 0; + uint32_t output_chan = 0; + + if (pos == -1) { // "use timestamp" + if (sources[0]->natural_position() != 0) { + pos = sources[0]->natural_position(); + } else { + // XXX is this the best alternative ? + pos = edit_cursor->current_frame; + } + } + + if (target_regions == 1) { + + /* take all the sources we have and package them up as a region */ + + region_name = region_name_from_path (paths.front(), (sources.size() > 1), false); + + regions.push_back (boost::dynamic_pointer_cast<AudioRegion> + (RegionFactory::create (sources, 0, sources[0]->length(), region_name, 0, + Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)))); + + } else if (target_regions == -1) { + + /* take each source and create a region for each one */ + + SourceList just_one; + SourceList::iterator x; + uint32_t n; + + for (n = 0, x = sources.begin(); x != sources.end(); ++x, ++n) { + + just_one.clear (); + just_one.push_back (*x); + + boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (*x); + + region_name = region_name_from_path (afs->path(), false, true, sources.size(), n); + + regions.push_back (boost::dynamic_pointer_cast<AudioRegion> + (RegionFactory::create (just_one, 0, (*x)->length(), region_name, 0, + Region::Flag (Region::DefaultFlags|Region::WholeFile|Region::External)))); + + } + } + + if (target_regions == 1) { + input_chan = regions.front()->n_channels(); + } else { + if (target_tracks == 1) { + input_chan = regions.size(); + } else { + input_chan = 1; + } + } if (Config->get_output_auto_connect() & AutoConnectMaster) { output_chan = (session->master_out() ? session->master_out()->n_inputs().n_audio() : input_chan); @@ -445,15 +645,31 @@ Editor::embed_sndfile (vector<Glib::ustring> paths, bool split, bool multiple_fi output_chan = input_chan; } - finish_bringing_in_audio (region, input_chan, output_chan, track, pos, mode); - - out: - track_canvas.get_window()->set_cursor (*current_canvas_cursor); - return ret; -} + int n = 0; + + for (vector<boost::shared_ptr<AudioRegion> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) { + + finish_bringing_in_audio (*r, input_chan, output_chan, pos, mode, track); + if (target_tracks != 1) { + track.reset (); + } else { + pos += (*r)->length(); + } + } + + /* setup peak file building in another thread */ + + for (SourceList::iterator x = sources.begin(); x != sources.end(); ++x) { + SourceFactory::setup_peakfile (*x, true); + } + + return 0; +} + int -Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_t in_chans, uint32_t out_chans, AudioTrack* track, nframes_t& pos, ImportMode mode) +Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_t in_chans, uint32_t out_chans, nframes64_t& pos, + ImportMode mode, boost::shared_ptr<AudioTrack>& existing_track) { switch (mode) { case ImportAsRegion: @@ -461,38 +677,52 @@ Editor::finish_bringing_in_audio (boost::shared_ptr<AudioRegion> region, uint32_ break; case ImportToTrack: - if (track) { - boost::shared_ptr<Playlist> playlist = track->diskstream()->playlist(); - - boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); - begin_reversible_command (_("insert sndfile")); - XMLNode &before = playlist->get_state(); - playlist->add_region (copy, pos); - session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state())); - commit_reversible_command (); + { + if (!existing_track) { + + existing_track = get_nth_selected_audio_track (0); - pos += region->length(); + if (!existing_track) { + return -1; + } } + + boost::shared_ptr<Playlist> playlist = existing_track->diskstream()->playlist(); + boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); + begin_reversible_command (_("insert sndfile")); + XMLNode &before = playlist->get_state(); + playlist->add_region (copy, pos); + session->add_command (new MementoCommand<Playlist>(*playlist, &before, &playlist->get_state())); + commit_reversible_command (); break; - + } + case ImportAsTrack: { - list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Normal, 1)); - if (!at.empty()) { - boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); - at.front()->diskstream()->playlist()->add_region (copy, pos); - at.front()->set_name(short_path(copy->name(), 32)); + if (!existing_track) { + list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Normal, 1)); + + if (at.empty()) { + return -1; + } + + existing_track = at.front(); + existing_track->set_name (region->name()); } + + boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); + existing_track->diskstream()->playlist()->add_region (copy, pos); break; } + case ImportAsTapeTrack: { list<boost::shared_ptr<AudioTrack> > at (session->new_audio_track (in_chans, out_chans, Destructive)); if (!at.empty()) { boost::shared_ptr<AudioRegion> copy (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); + at.front()->set_name (basename_nosuffix (copy->name())); at.front()->diskstream()->playlist()->add_region (copy, pos); - at.front()->set_name(short_path(copy->name(), 32)); } break; } diff --git a/gtk2_ardour/editor_audiotrack.cc b/gtk2_ardour/editor_audiotrack.cc index cdcbb43f76..87fa8a06b8 100644 --- a/gtk2_ardour/editor_audiotrack.cc +++ b/gtk2_ardour/editor_audiotrack.cc @@ -26,11 +26,13 @@ #include "audio_region_view.h" #include "selection.h" +#include "i18n.h" + using namespace ARDOUR; using namespace PBD; void -Editor::set_route_loop_selection () +Editor::set_loop_from_selection (bool play) { if (session == 0 || selection->time.empty()) { return; @@ -38,18 +40,26 @@ Editor::set_route_loop_selection () nframes_t start = selection->time[clicked_selection].start; nframes_t end = selection->time[clicked_selection].end; + + set_loop_range (start, end, _("set loop range from selection")); - Location* loc = transport_loop_location(); - - if (loc) { - - loc->set (start, end); - - // enable looping, reposition and start rolling + if (play) { session->request_play_loop (true); - session->request_locate (loc->start(), true); + session->request_locate (start, true); + } +} + +void +Editor::set_punch_from_selection () +{ + if (session == 0 || selection->time.empty()) { + return; } + nframes_t start = selection->time[clicked_selection].start; + nframes_t end = selection->time[clicked_selection].end; + + set_punch_range (start, end, _("set punch range from selection")); } void diff --git a/gtk2_ardour/editor_canvas.cc b/gtk2_ardour/editor_canvas.cc index c5a8695158..86beec387e 100644 --- a/gtk2_ardour/editor_canvas.cc +++ b/gtk2_ardour/editor_canvas.cc @@ -22,6 +22,7 @@ #include <gtkmm2ext/utils.h> #include <ardour/audioregion.h> +#include <ardour/profile.h> #include "ardour_ui.h" #include "editor.h" @@ -143,11 +144,21 @@ Editor::initialize_canvas () verbose_cursor_visible = false; - cursor_group = new ArdourCanvas::Group (*track_canvas.root(), 0.0, 0.0); + if (Profile->get_sae()) { + Image img (::get_icon (X_("saelogo"))); + logo_item = new ArdourCanvas::Pixbuf (*track_canvas.root(), 0.0, 0.0, img.get_pixbuf()); + // logo_item->property_height_in_pixels() = true; + // logo_item->property_width_in_pixels() = true; + // logo_item->property_height_set() = true; + // logo_item->property_width_set() = true; + logo_item->show (); + } + + /* a group to hold time (measure) lines */ time_line_group = new ArdourCanvas::Group (*track_canvas.root(), 0.0, 0.0); tempo_lines = new TempoLines(track_canvas, time_line_group); - + cursor_group = new ArdourCanvas::Group (*track_canvas.root(), 0.0, 0.0); time_canvas.set_name ("EditorTimeCanvas"); time_canvas.add_events (Gdk::POINTER_MOTION_HINT_MASK); time_canvas.set_flags (CAN_FOCUS); @@ -257,6 +268,10 @@ Editor::initialize_canvas () initial_ruler_update_required = true; track_canvas.signal_size_allocate().connect (mem_fun(*this, &Editor::track_canvas_allocate)); + if (logo_item) { + logo_item->lower_to_bottom (); + } + ColorsChanged.connect (mem_fun (*this, &Editor::color_handler)); color_handler(); @@ -348,6 +363,11 @@ Editor::track_canvas_size_allocated () update_fixed_rulers(); redisplay_tempo (true); + + if (logo_item) { + // logo_item->property_height() = canvas_height; + // logo_item->property_width() = canvas_width; + } Resized (); /* EMIT_SIGNAL */ @@ -455,7 +475,7 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context, vector<ustring> paths; string spath; GdkEvent ev; - nframes_t frame; + nframes64_t frame; if (convert_drop_to_paths (paths, context, x, y, data, info, time)) { goto out; @@ -483,15 +503,18 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context, /* drop onto canvas background: create new tracks */ - nframes_t pos = 0; - do_embed (paths, false, ImportAsTrack, 0, pos, false); + do_embed (paths, Editing::ImportDistinctFiles, ImportAsTrack, frame); } else if ((tv = dynamic_cast<RouteTimeAxisView*>(tvp)) != 0) { /* check that its an audio track, not a bus */ + /* check that its an audio track, not a bus */ + if (tv->get_diskstream()) { - do_embed (paths, false, ImportToTrack, tv->audio_track().get(), frame, true); + /* select the track, then embed */ + selection->set (tv); + do_embed (paths, Editing::ImportDistinctFiles, ImportToTrack, frame); } } @@ -697,6 +720,10 @@ Editor::canvas_horizontally_scrolled () reset_scrolling_region (); } + if (logo_item) { + logo_item->property_x() = horizontal_adjustment.get_value (); + } + update_fixed_rulers (); redisplay_tempo (!_dragging_hscrollbar); diff --git a/gtk2_ardour/editor_keyboard.cc b/gtk2_ardour/editor_keyboard.cc index 4b0088f075..28b86f53fc 100644 --- a/gtk2_ardour/editor_keyboard.cc +++ b/gtk2_ardour/editor_keyboard.cc @@ -43,6 +43,14 @@ Editor::kbd_driver (sigc::slot<void,GdkEvent*> theslot, bool use_track_canvas, b doit = true; } + /* any use of "keyboard mouse buttons" invalidates an existing grab + */ + + if (drag_info.item) { + drag_info.item->ungrab (GDK_CURRENT_TIME); + drag_info.item = 0; + } + if (doit) { if (entered_regionview && can_select) { diff --git a/gtk2_ardour/editor_markers.cc b/gtk2_ardour/editor_markers.cc index 29a212f2f5..724ff2c31a 100644 --- a/gtk2_ardour/editor_markers.cc +++ b/gtk2_ardour/editor_markers.cc @@ -909,56 +909,13 @@ Editor::new_transport_marker_menu_popdown (GdkEventAny *ev) void Editor::new_transport_marker_menu_set_loop () { - if (!session) return; - - begin_reversible_command (_("set loop range")); - - Location* tll; - - if ((tll = transport_loop_location()) == 0) { - Location* loc = new Location (temp_location->start(), temp_location->end(), _("Loop"), Location::IsAutoLoop); - XMLNode &before = session->locations()->get_state(); - session->locations()->add (loc, true); - session->set_auto_loop_location (loc); - XMLNode &after = session->locations()->get_state(); - session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after)); - } - else { - XMLNode &before = tll->get_state(); - tll->set_hidden (false, this); - tll->set (temp_location->start(), temp_location->end()); - XMLNode &after = tll->get_state(); - session->add_command (new MementoCommand<Location>(*tll, &before, &after)); - } - - commit_reversible_command (); + set_loop_range (temp_location->start(), temp_location->end(), _("set loop range")); } void Editor::new_transport_marker_menu_set_punch () { - if (!session) return; - - begin_reversible_command (_("set punch range")); - - Location* tpl; - - if ((tpl = transport_punch_location()) == 0) { - tpl = new Location (temp_location->start(), temp_location->end(), _("Punch"), Location::IsAutoPunch); - XMLNode &before = session->locations()->get_state(); - session->locations()->add (tpl, true); - session->set_auto_punch_location (tpl); - XMLNode &after = session->locations()->get_state(); - session->add_command (new MementoCommand<Locations>(*(session->locations()), &before, &after)); - } else { - XMLNode &before = tpl->get_state(); - tpl->set_hidden(false, this); - tpl->set(temp_location->start(), temp_location->end()); - XMLNode &after = tpl->get_state(); - session->add_command (new MementoCommand<Location>(*tpl, &before, &after)); - } - - commit_reversible_command (); + set_punch_range (temp_location->start(), temp_location->end(), _("set punch range")); } void diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index e8bc98a73c..da95d50d95 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -442,8 +442,23 @@ Editor::button_selection (ArdourCanvas::Item* item, GdkEvent* event, ItemType it switch (item_type) { case RegionItem: - case RegionViewNameHighlight: - case RegionViewName: + if (mouse_mode != MouseRange) { + commit = set_selected_regionview_from_click (press, op, true); + } else if (event->type == GDK_BUTTON_PRESS) { + commit = set_selected_track_from_click (press, op, false); + } + break; + + case RegionViewNameHighlight: + case RegionViewName: + if (mouse_mode != MouseRange) { + commit = set_selected_regionview_from_click (press, op, true); + } else if (event->type == GDK_BUTTON_PRESS) { + commit = set_selected_track_from_click (press, op, false); + } + break; + + case FadeInHandleItem: case FadeInItem: case FadeOutHandleItem: @@ -754,7 +769,12 @@ Editor::button_press_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemTyp break; case MouseAudition: - /* handled in release */ + _scrubbing = true; + last_scrub_frame = 0; + last_scrub_time = 0; + have_full_mouse_speed = false; + memset (mouse_speed, 0, sizeof (double) * mouse_speed_size); + /* rest handled in motion & release */ break; default: @@ -906,10 +926,19 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT break; case StreamItem: + popup_track_context_menu (1, event->button.time, where); + break; + case RegionItem: case RegionViewNameHighlight: case RegionViewName: + popup_track_context_menu (1, event->button.time, where); + break; + case SelectionItem: + popup_track_context_menu (1, event->button.time, where); + break; + case AutomationTrackItem: case CrossfadeViewItem: popup_track_context_menu (1, event->button.time, where); @@ -1073,13 +1102,20 @@ Editor::button_release_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemT break; case MouseAudition: - switch (item_type) { - case RegionItem: - audition_selected_region (); - break; - default: - break; - } + _scrubbing = false; + if (last_scrub_frame == 0) { + /* no drag, just a click */ + switch (item_type) { + case RegionItem: + audition_selected_region (); + break; + default: + break; + } + } else { + /* make sure we stop */ + session->request_transport_speed (0.0); + } break; default: @@ -1424,6 +1460,12 @@ Editor::left_automation_track () return false; } +static gboolean +_update_mouse_speed (void *arg) +{ + return static_cast<Editor*>(arg)->update_mouse_speed (); +} + bool Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item_type, bool from_autoscroll) { @@ -1455,6 +1497,66 @@ Editor::motion_handler (ArdourCanvas::Item* item, GdkEvent* event, ItemType item drag_info.current_pointer_frame = event_frame (event, &drag_info.current_pointer_x, &drag_info.current_pointer_y); + switch (mouse_mode) { + case MouseAudition: + if (_scrubbing) { + struct timeval tmnow; + + if (last_scrub_frame == 0) { + + /* first motion, just set up the variables */ + + last_scrub_frame = (nframes64_t) drag_info.current_pointer_frame; + gettimeofday (&tmnow, 0); + last_scrub_time = tmnow.tv_sec * 1000000.0 + tmnow.tv_usec; + session->request_locate (last_scrub_frame, true); + + } else { + /* how fast is the mouse moving ? */ + + double speed; + nframes_t distance; + double time; + double dir; + +#if 1 + if (last_scrub_frame < (nframes64_t) drag_info.current_pointer_frame) { + distance = (nframes64_t) drag_info.current_pointer_frame - last_scrub_frame; + dir = 1.0; + } else { + distance = last_scrub_frame - (nframes64_t) drag_info.current_pointer_frame; + dir = -1.0; + } +#else + if (drag_info.grab_x < drag_info.current_pointer_x) { + distance = drag_info.current_pointer_x - drag_info.grab_x; + dir = -1.0; + } else { + distance = drag_info.grab_x - drag_info.current_pointer_x; + dir = 1.0; + } +#endif + + gettimeofday (&tmnow, 0); + time = (tmnow.tv_sec * 1000000.0 + tmnow.tv_usec) - last_scrub_time; + last_scrub_frame = drag_info.current_pointer_frame; + last_scrub_time = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec; + speed = ((double)distance/session->frame_rate()) / (time/1000000.0); // frames/sec + + add_mouse_speed (speed, dir); + + if (mouse_speed_update < 0) { + mouse_speed_update = g_timeout_add (10, _update_mouse_speed, this); + update_mouse_speed (); + } + } + } + + default: + break; + } + + if (!from_autoscroll && drag_info.item) { /* item != 0 is the best test i can think of for dragging. */ @@ -3469,6 +3571,10 @@ Editor::region_drag_finished_callback (ArdourCanvas::Item* item, GdkEvent* event rtv->reveal_dependent_views (*latest_regionview); selection->add (latest_regionview); } + + /* if the original region was locked, we don't care for the new one */ + + newregion->set_locked (false); } else { @@ -4832,6 +4938,11 @@ Editor::end_time_fx (ArdourCanvas::Item* item, GdkEvent* event) if (drag_info.first_move) { return; } + + if (drag_info.last_pointer_frame < clicked_regionview->region()->position()) { + /* backwards drag of the left edge - not usable */ + return; + } nframes_t newlen = drag_info.last_pointer_frame - clicked_regionview->region()->position(); float percentage = (float) ((double) newlen - (double) clicked_regionview->region()->length()) / ((double) newlen) * 100.0f; @@ -4909,3 +5020,91 @@ Editor::track_height_step_timeout () } return true; } + + +void +Editor::add_mouse_speed (double speed, double dir) +{ + size_t index; + + mouse_direction = dir; + + index = mouse_speed_entries; + + if (++index >= mouse_speed_size) { + index = 0; + have_full_mouse_speed = true; + } + + mouse_speed[index] = speed; + mouse_speed_entries = index; +} + +double +Editor::compute_mouse_speed () +{ + double total = 0; + + if (!have_full_mouse_speed) { + + /* partial speed buffer, just use whatever we have so far */ + + if (mouse_speed_entries == 0 ) { + return 0.0; + } + + for (size_t n = 0; n < mouse_speed_entries; ++n) { + total += mouse_speed[n]; + } + + return mouse_direction * total/mouse_speed_entries; + } + + /* compute the average (effectively low-pass filtering) mouse speed + across the entire buffer. + */ + + for (size_t n = 0; n < mouse_speed_size; ++n) { + total += mouse_speed[n]; + } + + + return mouse_direction * total/mouse_speed_size; +} + +bool +Editor::update_mouse_speed () +{ + double speed; + + if (!_scrubbing) { + session->request_transport_speed (0.0); + mouse_speed_update = -1; + return false; + } + + speed = compute_mouse_speed (); + + struct timeval tmnow; + + gettimeofday (&tmnow, 0); + double now = (tmnow.tv_sec * 1000000.0) + tmnow.tv_usec; + + if (now - last_scrub_time > 250000) { + + // 0.25 seconds since last mouse motion, start to brake + + if (fabs (speed) < 0.1) { + /* don't asymptotically approach zero */ + memset (mouse_speed, 0, sizeof (double) * mouse_speed_size); + speed = 0.0; + } else if (fabs (speed) < 0.25) { + add_mouse_speed (fabs (speed * 0.2), mouse_direction); + } else { + add_mouse_speed (fabs (speed * 0.6), mouse_direction); + } + } + + session->request_transport_speed (speed); + return _scrubbing; +} diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 8906837031..ca2d7039ad 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -119,7 +119,22 @@ Editor::split_regions_at (nframes_t where, RegionSelection& regions) { begin_reversible_command (_("split")); - snap_to (where); + // if splitting a single region, and snap-to is using + // region boundaries, don't pay attention to them + + if (regions.size() == 1) { + switch (snap_type) { + case SnapToRegionStart: + case SnapToRegionSync: + case SnapToRegionEnd: + break; + default: + snap_to (where); + } + } else { + snap_to (where); + } + for (RegionSelection::iterator a = regions.begin(); a != regions.end(); ) { RegionSelection::iterator tmp; @@ -178,16 +193,17 @@ Editor::remove_selected_regions () /* XXX: should be called remove regions if we're removing more than one */ begin_reversible_command (_("remove region")); + while (!selection->regions.empty()) { boost::shared_ptr<Region> region = selection->regions.front()->region (); boost::shared_ptr<Playlist> playlist = region->playlist (); - + XMLNode &before = playlist->get_state(); playlist->remove_region (region); XMLNode &after = playlist->get_state(); session->add_command(new MementoCommand<Playlist>(*playlist, &before, &after)); } - + commit_reversible_command (); } @@ -497,12 +513,13 @@ Editor::build_region_boundary_cache () for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) { if ((r = find_next_region (pos, *p, 1, tlist, &ontrack)) == 0) { - at_end = true; + if (*p == interesting_points.back()) { + at_end = true; + } /* move to next point type */ continue; } - switch (*p) { case Start: rpos = r->first_frame(); @@ -3423,73 +3440,94 @@ Editor::reset_region_gain_envelopes () session->commit_reversible_command (); } -/** Set whether or not gain envelopes are visible for the selected regions. - * @param yn true to make visible, false to make invisible. - */ void -Editor::set_gain_envelope_visibility (bool yn) +Editor::toggle_gain_envelope_visibility () { for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); if (arv) { - if (arv->envelope_visible() != yn) { - arv->set_envelope_visible (yn); + bool x = region_envelope_visible_item->get_active(); + if (x != arv->envelope_visible()) { + arv->set_envelope_visible (x); } } } } -/** Set whether or not gain envelopes are active for the selected regions. - * @param yn true to make active, false to make inactive. - */ void -Editor::set_gain_envelope_active (bool yn) +Editor::toggle_gain_envelope_active () { for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); if (arv) { - if (arv->audio_region()->envelope_active() != yn) { - arv->audio_region()->set_envelope_active (yn); + bool x = region_envelope_active_item->get_active(); + if (x != arv->audio_region()->envelope_active()) { + arv->audio_region()->set_envelope_active (x); } } } } -/** Set the locked state of all selected regions to a particular value. +/** Set the position-locked state of all selected regions to a particular value. * @param yn true to make locked, false to make unlocked. */ void -Editor::set_region_lock (bool yn) +Editor::toggle_region_position_lock () { + bool x = region_lock_position_item->get_active(); + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - (*i)->region()->set_locked (yn); + AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); + if (arv) { + if (x != arv->audio_region()->locked()) { + arv->audio_region()->set_position_locked (x); + } + } } } -/** Set the position-locked state of all selected regions to a particular value. - * @param yn true to make locked, false to make unlocked. - */ void -Editor::set_region_position_lock (bool yn) +Editor::toggle_region_lock () { + bool x = region_lock_item->get_active(); + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - (*i)->region()->set_position_locked (yn); + AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); + if (arv) { + if (x != arv->audio_region()->locked()) { + arv->audio_region()->set_locked (x); + } + } } } void -Editor::set_region_mute (bool yn) +Editor::toggle_region_mute () { + bool x = region_mute_item->get_active(); + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - (*i)->region()->set_muted (yn); + AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); + if (arv) { + if (x != arv->audio_region()->muted()) { + arv->audio_region()->set_muted (x); + } + } } } void -Editor::set_region_opaque (bool yn) +Editor::toggle_region_opaque () { + bool x = region_opaque_item->get_active(); + for (RegionSelection::iterator i = selection->regions.begin(); i != selection->regions.end(); ++i) { - (*i)->region()->set_opaque (yn); + AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*i); + if (arv) { + if (x != arv->audio_region()->opaque()) { + arv->audio_region()->set_opaque (x); + } + } } } diff --git a/gtk2_ardour/editor_region_list.cc b/gtk2_ardour/editor_region_list.cc index a9df928222..f1001cb8e4 100644 --- a/gtk2_ardour/editor_region_list.cc +++ b/gtk2_ardour/editor_region_list.cc @@ -626,8 +626,8 @@ Editor::region_list_display_drag_data_received (const RefPtr<Gdk::DragContext>& vector<ustring> paths; if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) { - nframes_t pos = 0; - do_embed (paths, false, ImportAsRegion, 0, pos, true); + nframes64_t pos = 0; + do_embed (paths, Editing::ImportDistinctFiles, ImportAsRegion, pos); context->drag_finish (true, false, time); } } diff --git a/gtk2_ardour/editor_route_list.cc b/gtk2_ardour/editor_route_list.cc index e11738f65b..5e10a578f5 100644 --- a/gtk2_ardour/editor_route_list.cc +++ b/gtk2_ardour/editor_route_list.cc @@ -53,6 +53,10 @@ Editor::handle_new_route (Session::RouteList& routes) TreeModel::Row parent; TreeModel::Row row; + ignore_route_list_reorder = true; + ignore_route_order_sync = true; + no_route_list_redisplay = true; + for (Session::RouteList::iterator x = routes.begin(); x != routes.end(); ++x) { boost::shared_ptr<Route> route = (*x); @@ -100,6 +104,7 @@ Editor::handle_new_route (Session::RouteList& routes) row[route_display_columns.text] = route->name(); row[route_display_columns.visible] = tv->marked_for_display(); row[route_display_columns.tv] = tv; + row[route_display_columns.route] = route; track_views.push_back (tv); @@ -114,11 +119,18 @@ Editor::handle_new_route (Session::RouteList& routes) ignore_route_list_reorder = false; + tv->set_old_order_key (route_display_model->children().size() - 1); route->gui_changed.connect (mem_fun(*this, &Editor::handle_gui_changes)); tv->GoingAway.connect (bind (mem_fun(*this, &Editor::remove_route), tv)); } + ignore_route_list_reorder = false; + ignore_route_order_sync = false; + no_route_list_redisplay = false; + + redisplay_route_list (); + if (show_editor_mixer_when_tracks_arrive) { show_editor_mixer (true); } @@ -234,6 +246,33 @@ Editor::route_list_reordered (const TreeModel::Path& path,const TreeModel::itera redisplay_route_list (); } + +void +Editor::sync_order_keys () +{ + vector<int> neworder; + TreeModel::Children rows = route_display_model->children(); + TreeModel::Children::iterator ri; + + if (ignore_route_order_sync || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) { + return; + } + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + neworder.push_back (0); + } + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + TimeAxisView* tv = (*ri)[route_display_columns.tv]; + boost::shared_ptr<Route> route = (*ri)[route_display_columns.route]; + neworder[route->order_key (X_("editor"))] = tv->old_order_key (); + } + + ignore_route_list_reorder = true; + route_display_model->reorder (neworder); + ignore_route_list_reorder = false; +} + void Editor::redisplay_route_list () { @@ -288,6 +327,13 @@ Editor::redisplay_route_list () cursor_group->raise_to_top (); reset_scrolling_region (); + + if (Config->get_sync_all_route_ordering() && !ignore_route_list_reorder) { + ignore_route_order_sync = true; + Route::SyncOrderKeys (); // EMIT SIGNAL + ignore_route_order_sync = false; + } + } void @@ -524,6 +570,13 @@ Editor::initial_route_list_display () } void +Editor::track_list_reorder (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter, int* new_order) +{ + session->set_remote_control_ids(); + redisplay_route_list (); +} + +void Editor::route_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter) { session->set_remote_control_ids(); diff --git a/gtk2_ardour/editor_rulers.cc b/gtk2_ardour/editor_rulers.cc index cfc7157510..88ff3a0c1e 100644 --- a/gtk2_ardour/editor_rulers.cc +++ b/gtk2_ardour/editor_rulers.cc @@ -152,14 +152,14 @@ Editor::ruler_button_press (GdkEventButton* ev) switch (ev->button) { case 1: - // Since we are about to move the playhead, cancel any running + // Since we will locate the playhead on button release, cancel any running // auditions. if (session->is_auditioning()) { session->cancel_audition (); } - /* transport playhead */ + /* playhead cursor */ snap_to (where); - session->request_locate (where); + playhead_cursor->set_position (where); _dragging_playhead = true; break; diff --git a/gtk2_ardour/engine_dialog.cc b/gtk2_ardour/engine_dialog.cc new file mode 100644 index 0000000000..5be28e9226 --- /dev/null +++ b/gtk2_ardour/engine_dialog.cc @@ -0,0 +1,1126 @@ +#include <vector> +#include <cmath> +#include <fstream> + +#include <glibmm.h> +#include <pbd/xml++.h> + +#ifdef __APPLE__ +#include <CoreAudio/CoreAudio.h> +#include <CoreFoundation/CFString.h> +#include <sys/param.h> +#include <mach-o/dyld.h> +#else +#include <alsa/asoundlib.h> +#endif + +#include <ardour/profile.h> +#include <jack/jack.h> + +#include <gtkmm/stock.h> +#include <gtkmm2ext/utils.h> + +#include <pbd/convert.h> +#include <pbd/error.h> + +#ifdef __APPLE +#include <CFBundle.h> +#endif + +#include "engine_dialog.h" +#include "i18n.h" + +using namespace std; +using namespace Gtk; +using namespace Gtkmm2ext; +using namespace PBD; +using namespace Glib; + +EngineControl::EngineControl () + : periods_adjustment (2, 2, 16, 1, 2), + periods_spinner (periods_adjustment), + priority_adjustment (60, 10, 90, 1, 10), + priority_spinner (priority_adjustment), + ports_adjustment (128, 8, 1024, 1, 16), + ports_spinner (ports_adjustment), + realtime_button (_("Realtime")), + no_memory_lock_button (_("Do not lock memory")), + unlock_memory_button (_("Unlock memory")), + soft_mode_button (_("No zombies")), + monitor_button (_("Provide monitor ports")), + force16bit_button (_("Force 16 bit")), + hw_monitor_button (_("H/W monitoring")), + hw_meter_button (_("H/W metering")), + verbose_output_button (_("Verbose output")), + start_button (_("Start")), + stop_button (_("Stop")), +#ifdef __APPLE__ + basic_packer (5, 2), + options_packer (4, 2), + device_packer (4, 2) +#else + basic_packer (8, 2), + options_packer (14, 2), + device_packer (6, 2) +#endif +{ + using namespace Notebook_Helpers; + Label* label; + vector<string> strings; + int row = 0; + + _used = false; + + strings.push_back (_("8000Hz")); + strings.push_back (_("22050Hz")); + strings.push_back (_("44100Hz")); + strings.push_back (_("48000Hz")); + strings.push_back (_("88200Hz")); + strings.push_back (_("96000Hz")); + strings.push_back (_("192000Hz")); + set_popdown_strings (sample_rate_combo, strings); + sample_rate_combo.set_active_text ("48000Hz"); + + strings.clear (); + strings.push_back ("32"); + strings.push_back ("64"); + strings.push_back ("128"); + strings.push_back ("256"); + strings.push_back ("512"); + strings.push_back ("1024"); + strings.push_back ("2048"); + strings.push_back ("4096"); + strings.push_back ("8192"); + set_popdown_strings (period_size_combo, strings); + period_size_combo.set_active_text ("1024"); + + strings.clear (); + strings.push_back (_("None")); + strings.push_back (_("Triangular")); + strings.push_back (_("Rectangular")); + strings.push_back (_("Shaped")); + set_popdown_strings (dither_mode_combo, strings); + dither_mode_combo.set_active_text (_("None")); + + /* basic parameters */ + + basic_packer.set_spacings (6); + + strings.clear (); +#ifdef __APPLE__ + strings.push_back (X_("CoreAudio")); +#else + strings.push_back (X_("ALSA")); + strings.push_back (X_("OSS")); + strings.push_back (X_("FFADO")); +#endif + strings.push_back (X_("NetJACK")); + strings.push_back (X_("Dummy")); + set_popdown_strings (driver_combo, strings); + driver_combo.set_active_text (strings.front()); + + /* figure out available devices and set up interface_combo */ + + enumerate_devices (); + driver_combo.signal_changed().connect (mem_fun (*this, &EngineControl::driver_changed)); + driver_changed (); + + strings.clear (); + strings.push_back (_("Playback/Recording on 1 Device")); + strings.push_back (_("Playback/Recording on 2 Devices")); + strings.push_back (_("Playback only")); + strings.push_back (_("Recording only")); + set_popdown_strings (audio_mode_combo, strings); + audio_mode_combo.set_active_text (strings.front()); + + audio_mode_combo.signal_changed().connect (mem_fun (*this, &EngineControl::audio_mode_changed)); + audio_mode_changed (); + + row = 0; + + label = manage (new Label (_("Driver"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (driver_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + + label = manage (new Label (_("Interface"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (interface_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + + label = manage (new Label (_("Sample Rate"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + + label = manage (new Label (_("Buffer size"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (period_size_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + +#ifndef __APPLE__ + label = manage (new Label (_("Number of buffers"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (periods_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + periods_spinner.set_value (2); + row++; +#endif + + label = manage (new Label (_("Approximate latency"))); + label->set_alignment (0.0, 0.5); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (latency_label, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; + + sample_rate_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency)); + periods_adjustment.signal_value_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency)); + period_size_combo.signal_changed().connect (mem_fun (*this, &EngineControl::redisplay_latency)); + redisplay_latency(); + row++; + /* no audio mode with CoreAudio, its duplex or nuthin' */ + +#ifndef __APPLE__ + label = manage (new Label (_("Audio Mode"))); + basic_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + basic_packer.attach (audio_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + row++; +#endif + + interface_combo.set_size_request (125, -1); + input_device_combo.set_size_request (125, -1); + output_device_combo.set_size_request (125, -1); + + /* + + if (engine_running()) { + start_button.set_sensitive (false); + } else { + stop_button.set_sensitive (false); + } + + start_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine)); + stop_button.signal_clicked().connect (mem_fun (*this, &EngineControl::start_engine)); + */ + + button_box.pack_start (start_button, false, false); + button_box.pack_start (stop_button, false, false); + + // basic_packer.attach (button_box, 0, 2, 8, 9, FILL|EXPAND, (AttachOptions) 0); + + /* options */ + + options_packer.set_spacings (6); + row = 0; + + options_packer.attach (realtime_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + + realtime_button.signal_toggled().connect (mem_fun (*this, &EngineControl::realtime_changed)); + realtime_changed (); + +#ifndef __APPLE__ + label = manage (new Label (_("Realtime Priority"))); + label->set_alignment (1.0, 0.5); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + options_packer.attach (priority_spinner, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + priority_spinner.set_value (60); + + options_packer.attach (no_memory_lock_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (unlock_memory_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (soft_mode_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (force16bit_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (hw_monitor_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (hw_meter_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; +#else + options_packer.attach (verbose_output_button, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; +#endif + + strings.clear (); + strings.push_back (_("Ignore")); + strings.push_back ("500 msec"); + strings.push_back ("1 sec"); + strings.push_back ("2 sec"); + strings.push_back ("10 sec"); + set_popdown_strings (timeout_combo, strings); + timeout_combo.set_active_text (strings.front ()); + + label = manage (new Label (_("Client timeout"))); + label->set_alignment (1.0, 0.5); + options_packer.attach (timeout_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0)); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + + label = manage (new Label (_("Number of ports"))); + label->set_alignment (1.0, 0.5); + options_packer.attach (ports_spinner, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0)); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + +#ifndef __APPLE__ + label = manage (new Label (_("Dither"))); + label->set_alignment (1.0, 0.5); + options_packer.attach (dither_mode_combo, 1, 2, row, row + 1, FILL|EXPAND, AttachOptions(0)); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; +#endif + + find_jack_servers (server_strings); + + if (server_strings.empty()) { + fatal << _("No JACK server found anywhere on this system. Please install JACK and restart") << endmsg; + /*NOTREACHED*/ + } + + set_popdown_strings (serverpath_combo, server_strings); + serverpath_combo.set_active_text (server_strings.front()); + + if (server_strings.size() > 1) { + label = manage (new Label (_("Server:"))); + options_packer.attach (*label, 0, 1, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + label->set_alignment (0.0, 0.5); + options_packer.attach (serverpath_combo, 1, 2, row, row + 1, FILL|EXPAND, (AttachOptions) 0); + ++row; + } + + /* device settings */ + + device_packer.set_spacings (6); + row = 0; + +#ifndef __APPLE__ + label = manage (new Label (_("Input device"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (input_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + label = manage (new Label (_("Output device"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (output_device_combo, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; +#endif + label = manage (new Label (_("Input channels"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (input_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + label = manage (new Label (_("Output channels"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (output_channels, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + label = manage (new Label (_("Hardware input latency (samples)"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (input_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + label = manage (new Label (_("Hardware output latency (samples)"))); + label->set_alignment (1.0, 0.5); + device_packer.attach (*label, 0, 1, row, row+1, FILL|EXPAND, (AttachOptions) 0); + device_packer.attach (output_latency, 1, 2, row, row+1, FILL|EXPAND, (AttachOptions) 0); + ++row; + + basic_hbox.pack_start (basic_packer, false, false); + options_hbox.pack_start (options_packer, false, false); + + device_packer.set_border_width (12); + options_packer.set_border_width (12); + basic_packer.set_border_width (12); + + notebook.pages().push_back (TabElem (basic_hbox, _("Device"))); + notebook.pages().push_back (TabElem (options_hbox, _("Options"))); + notebook.pages().push_back (TabElem (device_packer, _("Advanced"))); + notebook.set_border_width (12); + + set_border_width (12); + pack_start (notebook); +} + +EngineControl::~EngineControl () +{ + +} + +void +EngineControl::build_command_line (vector<string>& cmd) +{ + string str; + string driver; + bool using_oss = false; + bool using_alsa = false; + bool using_coreaudio = false; + bool using_netjack = false; + bool using_ffado = false; + + /* first, path to jackd */ + + cmd.push_back (serverpath_combo.get_active_text ()); + + /* now jackd arguments */ + + str = timeout_combo.get_active_text (); + if (str != _("Ignore")) { + double secs; + uint32_t msecs; + atof (str); + msecs = (uint32_t) floor (secs * 1000.0); + cmd.push_back ("-t"); + cmd.push_back (to_string (msecs, std::dec)); + } + + if (no_memory_lock_button.get_active()) { + cmd.push_back ("-m"); /* no munlock */ + } + + cmd.push_back ("-p"); /* port max */ + cmd.push_back (to_string ((uint32_t) floor (ports_spinner.get_value()), std::dec)); + + if (realtime_button.get_active()) { + cmd.push_back ("-R"); + cmd.push_back ("-P"); + cmd.push_back (to_string ((uint32_t) floor (priority_spinner.get_value()), std::dec)); + } + + if (unlock_memory_button.get_active()) { + cmd.push_back ("-u"); + } + + if (verbose_output_button.get_active()) { + cmd.push_back ("-v"); + } + + /* now add fixed arguments (not user-selectable) */ + + cmd.push_back ("-T"); // temporary */ + + /* next the driver */ + + cmd.push_back ("-d"); + + driver = driver_combo.get_active_text (); + if (driver == X_("ALSA")) { + using_alsa = true; + cmd.push_back ("alsa"); + } else if (driver == X_("OSS")) { + using_oss = true; + cmd.push_back ("oss"); + } else if (driver == X_("CoreAudio")) { + using_coreaudio = true; + cmd.push_back ("coreaudio"); + } else if (driver == X_("NetJACK")) { + using_netjack = true; + cmd.push_back ("netjack"); + } else if (driver == X_("FFADO")) { + using_ffado = true; + cmd.push_back ("ffado"); + } + + /* driver arguments */ + + if (!using_coreaudio) { + str = audio_mode_combo.get_active_text(); + + if (str == _("Playback/Recording on 1 Device")) { + + /* relax */ + + } else if (str == _("Playback/Recording on 2 Devices")) { + + cmd.push_back ("-C"); + cmd.push_back (get_device_name (driver, input_device_combo.get_active_text())); + cmd.push_back ("-P"); + cmd.push_back (get_device_name (driver, output_device_combo.get_active_text())); + + } else if (str == _("Playback only")) { + cmd.push_back ("-P"); + } else if (str == _("Recording only")) { + cmd.push_back ("-C"); + } + + cmd.push_back ("-n"); + cmd.push_back (to_string ((uint32_t) floor (periods_spinner.get_value()), std::dec)); + } + + cmd.push_back ("-r"); + cmd.push_back (to_string (get_rate(), std::dec)); + + cmd.push_back ("-p"); + cmd.push_back (period_size_combo.get_active_text()); + + if (using_alsa) { + + if (audio_mode_combo.get_active_text() != _("Playback/Recording on 2 Devices")) { + cmd.push_back ("-d"); + cmd.push_back (get_device_name (driver, interface_combo.get_active_text())); + } + + if (hw_meter_button.get_active()) { + cmd.push_back ("-M"); + } + + if (hw_monitor_button.get_active()) { + cmd.push_back ("-H"); + } + + str = dither_mode_combo.get_active_text(); + + if (str == _("None")) { + } else if (str == _("Triangular")) { + cmd.push_back ("-z triangular"); + } else if (str == _("Rectangular")) { + cmd.push_back ("-z rectangular"); + } else if (str == _("Shaped")) { + cmd.push_back ("-z shaped"); + } + + if (force16bit_button.get_active()) { + cmd.push_back ("-S"); + } + + if (soft_mode_button.get_active()) { + cmd.push_back ("-s"); + } + + } else if (using_coreaudio) { + +#ifdef __APPLE__ + // note: older versions of the CoreAudio JACK backend use -n instead of -d here + cmd.push_back ("-d"); + cmd.push_back (get_device_name (driver, interface_combo.get_active_text())); +#endif + + } else if (using_oss) { + + } else if (using_netjack) { + + } +} + +bool +EngineControl::engine_running () +{ + jack_status_t status; + jack_client_t* c = jack_client_open ("ardourprobe", JackNoStartServer, &status); + + if (status == 0) { + jack_client_close (c); + return true; + } + return false; +} + +int +EngineControl::setup_engine () +{ + vector<string> args; + std::string cwd = "/tmp"; + + build_command_line (args); + + Glib::ustring jackdrc_path = Glib::get_home_dir(); + jackdrc_path += "/.jackdrc"; + + ofstream jackdrc (jackdrc_path.c_str()); + if (!jackdrc) { + error << string_compose (_("cannot open JACK rc file %1 to store parameters"), jackdrc_path) << endmsg; + return -1; + } + + for (vector<string>::iterator i = args.begin(); i != args.end(); ++i) { + jackdrc << (*i) << ' '; + } + jackdrc << endl; + jackdrc.close (); + + _used = true; + + return 0; +} + +void +EngineControl::realtime_changed () +{ +#ifndef __APPLE__ + priority_spinner.set_sensitive (realtime_button.get_active()); +#endif +} + +void +EngineControl::enumerate_devices () +{ + /* note: case matters for the map keys */ + +#ifdef __APPLE__ + devices["CoreAudio"] = enumerate_coreaudio_devices (); +#else + devices["ALSA"] = enumerate_alsa_devices (); + devices["FFADO"] = enumerate_ffado_devices (); + devices["OSS"] = enumerate_oss_devices (); + devices["Dummy"] = enumerate_dummy_devices (); + devices["NetJACK"] = enumerate_netjack_devices (); +#endif +} + +#ifdef __APPLE__ +static OSStatus +getDeviceUIDFromID( AudioDeviceID id, char *name, size_t nsize) +{ + UInt32 size = sizeof(CFStringRef); + CFStringRef UI; + OSStatus res = AudioDeviceGetProperty(id, 0, false, + kAudioDevicePropertyDeviceUID, &size, &UI); + if (res == noErr) + CFStringGetCString(UI,name,nsize,CFStringGetSystemEncoding()); + CFRelease(UI); + return res; +} + +vector<string> +EngineControl::enumerate_coreaudio_devices () +{ + vector<string> devs; + + // Find out how many Core Audio devices are there, if any... + // (code snippet gently "borrowed" from St?hane Letz jackdmp;) + OSStatus err; + Boolean isWritable; + size_t outSize = sizeof(isWritable); + + backend_devs.clear (); + + err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, + &outSize, &isWritable); + if (err == noErr) { + // Calculate the number of device available... + int numCoreDevices = outSize / sizeof(AudioDeviceID); + // Make space for the devices we are about to get... + AudioDeviceID *coreDeviceIDs = new AudioDeviceID [numCoreDevices]; + err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, + &outSize, (void *) coreDeviceIDs); + if (err == noErr) { + // Look for the CoreAudio device name... + char coreDeviceName[256]; + size_t nameSize; + for (int i = 0; i < numCoreDevices; i++) { + + nameSize = sizeof (coreDeviceName); + + err = AudioDeviceGetPropertyInfo(coreDeviceIDs[i], + 0, true, kAudioDevicePropertyDeviceName, + &outSize, &isWritable); + if (err == noErr) { + err = AudioDeviceGetProperty(coreDeviceIDs[i], + 0, true, kAudioDevicePropertyDeviceName, + &nameSize, (void *) coreDeviceName); + if (err == noErr) { + char drivername[128]; + + // this returns the unique id for the device + // that must be used on the commandline for jack + + if (getDeviceUIDFromID(coreDeviceIDs[i], drivername, sizeof (drivername)) == noErr) { + devs.push_back (coreDeviceName); + backend_devs.push_back (drivername); + } + } + } + } + } + delete [] coreDeviceIDs; + } + + return devs; +} +#else +vector<string> +EngineControl::enumerate_alsa_devices () +{ + vector<string> devs; + + snd_ctl_t *handle; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + string devname; + int cardnum = -1; + int device = -1; + + backend_devs.clear (); + + while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) { + + devname = "hw:"; + devname += to_string (cardnum, std::dec); + + if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) { + + while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) { + + bool have_playback = false; + bool have_capture = false; + + /* find duplex devices only */ + + snd_pcm_info_set_device (pcminfo, device); + snd_pcm_info_set_subdevice (pcminfo, 0); + snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_CAPTURE); + + if (snd_ctl_pcm_info (handle, pcminfo) >= 0) { + have_capture = true; + } + + snd_pcm_info_set_device (pcminfo, device); + snd_pcm_info_set_subdevice (pcminfo, 0); + snd_pcm_info_set_stream (pcminfo, SND_PCM_STREAM_PLAYBACK); + + if (snd_ctl_pcm_info (handle, pcminfo) >= 0) { + have_playback = true; + } + + if (have_capture && have_playback) { + devs.push_back (snd_pcm_info_get_name (pcminfo)); + devname += ','; + devname += to_string (device, std::dec); + backend_devs.push_back (devname); + } + } + + snd_ctl_close(handle); + } + } + + return devs; +} + +vector<string> +EngineControl::enumerate_ffado_devices () +{ + vector<string> devs; + return devs; +} +vector<string> +EngineControl::enumerate_oss_devices () +{ + vector<string> devs; + return devs; +} +vector<string> +EngineControl::enumerate_dummy_devices () +{ + vector<string> devs; + return devs; +} +vector<string> +EngineControl::enumerate_netjack_devices () +{ + vector<string> devs; + return devs; +} +#endif + +void +EngineControl::driver_changed () +{ + string driver = driver_combo.get_active_text(); + vector<string>& strings = devices[driver]; + string::size_type maxlen = 0; + int maxindex = -1; + int n = 0; + + for (vector<string>::iterator i = strings.begin(); i != strings.end(); ++i, ++n) { + if ((*i).length() > maxlen) { + maxlen = (*i).length(); + maxindex = n; + } + } + + set_popdown_strings (interface_combo, strings); + set_popdown_strings (input_device_combo, strings); + set_popdown_strings (output_device_combo, strings); + + if (!strings.empty()) { + interface_combo.set_active_text (strings.front()); + input_device_combo.set_active_text (strings.front()); + output_device_combo.set_active_text (strings.front()); + } + + if (driver == "ALSA") { + soft_mode_button.set_sensitive (true); + force16bit_button.set_sensitive (true); + hw_monitor_button.set_sensitive (true); + hw_meter_button.set_sensitive (true); + monitor_button.set_sensitive (true); + } else { + soft_mode_button.set_sensitive (false); + force16bit_button.set_sensitive (false); + hw_monitor_button.set_sensitive (false); + hw_meter_button.set_sensitive (false); + monitor_button.set_sensitive (false); + } +} + +uint32_t +EngineControl::get_rate () +{ + return atoi (sample_rate_combo.get_active_text ()); +} + +void +EngineControl::redisplay_latency () +{ + uint32_t rate = get_rate(); +#ifdef __APPLE_ + float periods = 2; +#else + float periods = periods_adjustment.get_value(); +#endif + float period_size = atof (period_size_combo.get_active_text()); + + char buf[32]; + snprintf (buf, sizeof(buf), "%.1fmsec", (periods * period_size) / (rate/1000.0)); + + latency_label.set_text (buf); +} + +void +EngineControl::audio_mode_changed () +{ + Glib::ustring str = audio_mode_combo.get_active_text(); + + if (str == _("Playback/Recording on 1 Device")) { + input_device_combo.set_sensitive (false); + output_device_combo.set_sensitive (false); + } else if (str == _("Playback/Recording on 2 Devices")) { + input_device_combo.set_sensitive (true); + output_device_combo.set_sensitive (true); + } else if (str == _("Playback only")) { + output_device_combo.set_sensitive (true); + } else if (str == _("Recording only")) { + input_device_combo.set_sensitive (true); + } +} + +void +EngineControl::find_jack_servers (vector<string>& strings) +{ +#ifdef __APPLE__ + /* this magic lets us finds the path to the OSX bundle, and then + we infer JACK's location from there + */ + + char execpath[MAXPATHLEN+1]; + uint32_t pathsz = sizeof (execpath); + + _NSGetExecutablePath (execpath, &pathsz); + + cerr << " execpath = " << execpath << endl; + + Glib::ustring path (Glib::path_get_dirname (execpath)); + path += "/jackd"; + + if (Glib::file_test (path, FILE_TEST_EXISTS)) { + strings.push_back (path); + cerr << "Found jack in " << path << endl; + } + + if (getenv ("ARDOUR_WITH_JACK")) { + /* no other options - only use the JACK we supply */ + if (strings.empty()) { + fatal << _("JACK appears to be missing from the Ardour bundle") << endmsg; + /*NOTREACHED*/ + } + return; + } +#endif + + if (Glib::file_test ("/usr/bin/jackd", FILE_TEST_EXISTS)) { + strings.push_back ("/usr/bin/jackd"); + } + if (Glib::file_test ("/usr/local/bin/jackd", FILE_TEST_EXISTS)) { + strings.push_back ("/usr/local/bin/jackd"); + } + if (Glib::file_test ("/opt/bin/jackd", FILE_TEST_EXISTS)) { + strings.push_back ("/opt/bin/jackd"); + } + if (Glib::file_test ("/usr/bin/jackdmp", FILE_TEST_EXISTS)) { + strings.push_back ("/usr/bin/jackd"); + } + if (Glib::file_test ("/usr/local/bin/jackdmp", FILE_TEST_EXISTS)) { + strings.push_back ("/usr/local/bin/jackd"); + } + if (Glib::file_test ("/opt/bin/jackdmp", FILE_TEST_EXISTS)) { + strings.push_back ("/opt/bin/jackd"); + } + +} + +string +EngineControl::get_device_name (const string& driver, const string& human_readable) +{ + vector<string>::iterator n; + vector<string>::iterator i; + + if (backend_devs.empty()) { + return human_readable; + } + + for (i = devices[driver].begin(), n = backend_devs.begin(); i != devices[driver].end(); ++i, ++n) { + if (human_readable == (*i)) { + return (*n); + } + } + + if (i == devices[driver].end()) { + fatal << string_compose (_("programming error: %1"), "true hardware name for ID missing") << endmsg; + /*NOTREACHED*/ + } + + /* keep gcc happy */ + + return string(); +} + +XMLNode& +EngineControl::get_state () +{ + XMLNode* root = new XMLNode ("AudioSetup"); + XMLNode* child; + Glib::ustring path; + + child = new XMLNode ("periods"); + child->add_property ("val", to_string (periods_adjustment.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("priority"); + child->add_property ("val", to_string (priority_adjustment.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("ports"); + child->add_property ("val", to_string (ports_adjustment.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("inchannels"); + child->add_property ("val", to_string (input_channels.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("outchannels"); + child->add_property ("val", to_string (output_channels.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("inlatency"); + child->add_property ("val", to_string (input_latency.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("outlatency"); + child->add_property ("val", to_string (output_latency.get_value(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("realtime"); + child->add_property ("val", to_string (realtime_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("nomemorylock"); + child->add_property ("val", to_string (no_memory_lock_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("unlockmemory"); + child->add_property ("val", to_string (unlock_memory_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("softmode"); + child->add_property ("val", to_string (soft_mode_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("force16bit"); + child->add_property ("val", to_string (force16bit_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("hwmonitor"); + child->add_property ("val", to_string (hw_monitor_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("hwmeter"); + child->add_property ("val", to_string (hw_meter_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("verbose"); + child->add_property ("val", to_string (verbose_output_button.get_active(), std::dec)); + root->add_child_nocopy (*child); + + child = new XMLNode ("samplerate"); + child->add_property ("val", sample_rate_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("periodsize"); + child->add_property ("val", period_size_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("serverpath"); + child->add_property ("val", serverpath_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("driver"); + child->add_property ("val", driver_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("interface"); + child->add_property ("val", interface_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("timeout"); + child->add_property ("val", timeout_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("dither"); + child->add_property ("val", dither_mode_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("audiomode"); + child->add_property ("val", audio_mode_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("inputdevice"); + child->add_property ("val", input_device_combo.get_active_text()); + root->add_child_nocopy (*child); + + child = new XMLNode ("outputdevice"); + child->add_property ("val", output_device_combo.get_active_text()); + root->add_child_nocopy (*child); + + return *root; +} + +void +EngineControl::set_state (const XMLNode& root) +{ + XMLNodeList clist; + XMLNodeConstIterator citer; + XMLNode* child; + XMLProperty* prop; + + int val; + string strval; + + clist = root.children(); + + for (citer = clist.begin(); citer != clist.end(); ++citer) { + + child = *citer; + + prop = child->property ("val"); + + if (!prop || prop->value().empty()) { + error << string_compose (_("AudioSetup value for %1 is missing data"), child->name()) << endmsg; + continue; + } + + strval = prop->value(); + + /* adjustments/spinners */ + + if (child->name() == "periods") { + val = atoi (strval); + periods_adjustment.set_value(val); + } else if (child->name() == "priority") { + val = atoi (strval); + priority_adjustment.set_value(val); + } else if (child->name() == "ports") { + val = atoi (strval); + ports_adjustment.set_value(val); + } else if (child->name() == "inchannels") { + val = atoi (strval); + input_channels.set_value(val); + } else if (child->name() == "outchannels") { + val = atoi (strval); + output_channels.set_value(val); + } else if (child->name() == "inlatency") { + val = atoi (strval); + input_latency.set_value(val); + } else if (child->name() == "outlatency") { + val = atoi (strval); + output_latency.set_value(val); + } + + /* buttons */ + + else if (child->name() == "realtime") { + val = atoi (strval); + realtime_button.set_active(val); + } else if (child->name() == "nomemorylock") { + val = atoi (strval); + no_memory_lock_button.set_active(val); + } else if (child->name() == "unlockmemory") { + val = atoi (strval); + unlock_memory_button.set_active(val); + } else if (child->name() == "softmode") { + val = atoi (strval); + soft_mode_button.set_active(val); + } else if (child->name() == "force16bit") { + val = atoi (strval); + force16bit_button.set_active(val); + } else if (child->name() == "hwmonitor") { + val = atoi (strval); + hw_monitor_button.set_active(val); + } else if (child->name() == "hwmeter") { + val = atoi (strval); + hw_meter_button.set_active(val); + } else if (child->name() == "verbose") { + val = atoi (strval); + verbose_output_button.set_active(val); + } + + /* combos */ + + else if (child->name() == "samplerate") { + sample_rate_combo.set_active_text(strval); + } else if (child->name() == "periodsize") { + period_size_combo.set_active_text(strval); + } else if (child->name() == "serverpath") { + /* do not allow us to use a server path that doesn't + exist on this system. this handles cases where + the user has an RC file listing a serverpath + from some other machine. + */ + vector<string>::iterator x; + for (x = server_strings.begin(); x != server_strings.end(); ++x) { + if (*x == strval) { + break; + } + } + if (x != server_strings.end()) { + serverpath_combo.set_active_text (strval); + } else { + warning << string_compose (_("configuration files contain a JACK server path that doesn't exist (%1)"), + strval) + << endmsg; + } + } else if (child->name() == "driver") { + driver_combo.set_active_text(strval); + } else if (child->name() == "interface") { + interface_combo.set_active_text(strval); + } else if (child->name() == "timeout") { + timeout_combo.set_active_text(strval); + } else if (child->name() == "dither") { + dither_mode_combo.set_active_text(strval); + } else if (child->name() == "audiomode") { + audio_mode_combo.set_active_text(strval); + } else if (child->name() == "inputdevice") { + input_device_combo.set_active_text(strval); + } else if (child->name() == "outputdevice") { + output_device_combo.set_active_text(strval); + } + } +} diff --git a/gtk2_ardour/engine_dialog.h b/gtk2_ardour/engine_dialog.h new file mode 100644 index 0000000000..925d12acbb --- /dev/null +++ b/gtk2_ardour/engine_dialog.h @@ -0,0 +1,106 @@ +#ifndef __gtk2_ardour_engine_dialog_h__ +#define __gtk2_ardour_engine_dialog_h__ + +#include <map> +#include <vector> +#include <string> + +#include <gtkmm/checkbutton.h> +#include <gtkmm/spinbutton.h> +#include <gtkmm/notebook.h> +#include <gtkmm/comboboxtext.h> +#include <gtkmm/table.h> +#include <gtkmm/expander.h> +#include <gtkmm/box.h> +#include <gtkmm/buttonbox.h> +#include <gtkmm/button.h> + +class EngineControl : public Gtk::VBox { + public: + EngineControl (); + ~EngineControl (); + + static bool engine_running (); + int setup_engine (); + + bool was_used() const { return _used; } + XMLNode& get_state (); + void set_state (const XMLNode&); + + private: + Gtk::Adjustment periods_adjustment; + Gtk::SpinButton periods_spinner; + Gtk::Adjustment priority_adjustment; + Gtk::SpinButton priority_spinner; + Gtk::Adjustment ports_adjustment; + Gtk::SpinButton ports_spinner; + Gtk::SpinButton input_channels; + Gtk::SpinButton output_channels; + Gtk::SpinButton input_latency; + Gtk::SpinButton output_latency; + Gtk::Label latency_label; + + Gtk::CheckButton realtime_button; + Gtk::CheckButton no_memory_lock_button; + Gtk::CheckButton unlock_memory_button; + Gtk::CheckButton soft_mode_button; + Gtk::CheckButton monitor_button; + Gtk::CheckButton force16bit_button; + Gtk::CheckButton hw_monitor_button; + Gtk::CheckButton hw_meter_button; + Gtk::CheckButton verbose_output_button; + + Gtk::Button start_button; + Gtk::Button stop_button; + Gtk::HButtonBox button_box; + + Gtk::ComboBoxText sample_rate_combo; + Gtk::ComboBoxText period_size_combo; + + Gtk::ComboBoxText preset_combo; + Gtk::ComboBoxText serverpath_combo; + Gtk::ComboBoxText driver_combo; + Gtk::ComboBoxText interface_combo; + Gtk::ComboBoxText timeout_combo; + Gtk::ComboBoxText dither_mode_combo; + Gtk::ComboBoxText audio_mode_combo; + Gtk::ComboBoxText input_device_combo; + Gtk::ComboBoxText output_device_combo; + + Gtk::Table basic_packer; + Gtk::Table options_packer; + Gtk::Table device_packer; + Gtk::HBox basic_hbox; + Gtk::HBox options_hbox; + Gtk::HBox device_hbox; + Gtk::Notebook notebook; + + bool _used; + + void realtime_changed (); + void driver_changed (); + void build_command_line (std::vector<std::string>&); + + std::map<std::string,std::vector<std::string> > devices; + std::vector<std::string> backend_devs; + void enumerate_devices (); + +#ifdef __APPLE__ + std::vector<std::string> enumerate_coreaudio_devices (); +#else + std::vector<std::string> enumerate_alsa_devices (); + std::vector<std::string> enumerate_oss_devices (); + std::vector<std::string> enumerate_netjack_devices (); + std::vector<std::string> enumerate_ffado_devices (); + std::vector<std::string> enumerate_dummy_devices (); +#endif + + void redisplay_latency (); + uint32_t get_rate(); + void audio_mode_changed (); + std::vector<std::string> server_strings; + void find_jack_servers (std::vector<std::string>&); + std::string get_device_name (const std::string& driver, const std::string& human_readable_name); +}; + +#endif /* __gtk2_ardour_engine_dialog_h__ */ diff --git a/gtk2_ardour/enums.cc b/gtk2_ardour/enums.cc index e6c81598fb..c33b2965c3 100644 --- a/gtk2_ardour/enums.cc +++ b/gtk2_ardour/enums.cc @@ -20,11 +20,13 @@ #include <pbd/enumwriter.h> #include "audio_clock.h" +#include "editing.h" #include "enums.h" using namespace std; using namespace PBD; using namespace ARDOUR; +using namespace Editing; void setup_gtk_ardour_enums () @@ -35,6 +37,7 @@ setup_gtk_ardour_enums () AudioClock::Mode clock_mode; Width width; + ImportMode import_mode; #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() @@ -51,4 +54,10 @@ setup_gtk_ardour_enums () REGISTER_ENUM (Wide); REGISTER_ENUM (Narrow); REGISTER (width); + + REGISTER_ENUM (ImportAsTrack); + REGISTER_ENUM (ImportToTrack); + REGISTER_ENUM (ImportAsRegion); + REGISTER_ENUM (ImportAsTapeTrack); + REGISTER (import_mode); } diff --git a/gtk2_ardour/keyboard.cc b/gtk2_ardour/keyboard.cc index 90fea321de..e6eaffefcb 100644 --- a/gtk2_ardour/keyboard.cc +++ b/gtk2_ardour/keyboard.cc @@ -48,14 +48,14 @@ uint32_t Keyboard::Shift = GDK_SHIFT_MASK; uint32_t Keyboard::Alt = GDK_MOD1_MASK; uint32_t Keyboard::Meta; -Keyboard* Keyboard::_the_keyboard = 0; +Keyboard* Keyboard::_the_keyboard = 0; +Gtk::Window* Keyboard::current_window = 0; +bool Keyboard::_some_magic_widget_has_focus = false; /* set this to initially contain the modifiers we care about, then track changes in ::set_edit_modifier() etc. */ GdkModifierType Keyboard::RelevantModifierKeyMask; -bool Keyboard::_some_magic_widget_has_focus = false; - void Keyboard::magic_widget_grab_focus () { @@ -199,6 +199,13 @@ Keyboard::snooper (GtkWidget *widget, GdkEventKey *event) } + if (event->type == GDK_KEY_RELEASE && event->keyval == GDK_w && modifier_state_equals (event->state, Control)) { + if (current_window) { + current_window->hide (); + current_window = 0; + } + } + return false; } @@ -211,6 +218,7 @@ Keyboard::key_is_down (uint32_t keyval) bool Keyboard::enter_window (GdkEventCrossing *ev, Gtk::Window* win) { + current_window = win; return false; } @@ -236,6 +244,7 @@ Keyboard::leave_window (GdkEventCrossing *ev, Gtk::Window* win) cerr << "clearing current target\n"; } state.clear (); + current_window = 0; } return false; diff --git a/gtk2_ardour/keyboard.h b/gtk2_ardour/keyboard.h index 3d9e1c9f41..fb22f2eca9 100644 --- a/gtk2_ardour/keyboard.h +++ b/gtk2_ardour/keyboard.h @@ -105,6 +105,7 @@ class Keyboard : public sigc::trackable, PBD::Stateful static guint delete_but; static guint delete_mod; static guint snap_mod; + static Gtk::Window* current_window; static gint _snooper (GtkWidget*, GdkEventKey*, gpointer); gint snooper (GtkWidget*, GdkEventKey*); diff --git a/gtk2_ardour/main.cc b/gtk2_ardour/main.cc index 67e59d4b80..1e9b27d8a7 100644 --- a/gtk2_ardour/main.cc +++ b/gtk2_ardour/main.cc @@ -47,7 +47,7 @@ #include "i18n.h" using namespace Gtk; -using namespace GTK_ARDOUR; +using namespace ARDOUR_COMMAND_LINE; using namespace ARDOUR; using namespace PBD; using namespace sigc; @@ -57,6 +57,7 @@ TextReceiver text_receiver ("ardour"); extern int curvetest (string); static ARDOUR_UI *ui = 0; +static char* localedir = LOCALEDIR; gint show_ui_callback (void *arg) @@ -98,65 +99,129 @@ Please consider the possibilities, and perhaps (re)start JACK.")); win.run (); } -static bool -maybe_load_session () -{ - /* If no session name is given: we're not loading a session yet, nor creating a new one */ - if (!session_name.length()) { - ui->hide_splash (); - if (!Config->get_no_new_session_dialog()) { - if (!ui->new_session ()) { - return false; - } - } - return true; +#ifdef __APPLE__ + +#include <mach-o/dyld.h> +#include <sys/param.h> +#include <fstream> + +void +fixup_bundle_environment () +{ + if (!getenv ("ARDOUR_BUNDLED")) { + return; } - /* Load session or start the new session dialog */ - string name, path; + char execpath[MAXPATHLEN+1]; + uint32_t pathsz = sizeof (execpath); - bool isnew; + _NSGetExecutablePath (execpath, &pathsz); - if (find_session (session_name, path, name, isnew)) { - error << string_compose(_("could not load command line session \"%1\""), session_name) << endmsg; - return false; + Glib::ustring exec_path (execpath); + Glib::ustring dir_path = Glib::path_get_dirname (exec_path); + Glib::ustring path; + const char *cstr = getenv ("PATH"); + + /* ensure that we find any bundled executables (e.g. JACK) */ + + path = dir_path; + if (cstr) { + path += ':'; + path += cstr; } + setenv ("PATH", path.c_str(), 1); + + path = dir_path; + path += "/../Resources"; + path += dir_path; + path += "/../Resources/Surfaces"; + path += dir_path; + path += "/../Resources/Panners"; + + setenv ("ARDOUR_MODULE_PATH", path.c_str(), 1); + + path = dir_path; + path += "/../Resources/icons:"; + path += dir_path; + path += "/../Resources/pixmaps:"; + path += dir_path; + path += "/../Resources/share:"; + path += dir_path; + path += "/../Resources"; + + setenv ("ARDOUR_PATH", path.c_str(), 1); + setenv ("ARDOUR_CONFIG_PATH", path.c_str(), 1); + setenv ("ARDOUR_DATA_PATH", path.c_str(), 1); + + cstr = getenv ("LADSPA_PATH"); + if (cstr) { + path = cstr; + path += ':'; + } + path = dir_path; + path += "/../Plugins"; + + setenv ("LADSPA_PATH", path.c_str(), 1); - if (!new_session) { - - /* Loading a session, but the session doesn't exist */ - if (isnew) { - error << string_compose (_("\n\nNo session named \"%1\" exists.\n" - "To create it from the command line, start ardour as \"ardour --new %1"), path) - << endmsg; - return false; - } + path = dir_path; + path += "/../Frameworks/clearlooks"; - if (ui->load_session (path, name)) { - /* it failed */ - return false; - } + setenv ("GTK_PATH", path.c_str(), 1); - } else { + path = dir_path; + path += "/../Resources/locale"; + + localedir = strdup (path.c_str()); + + /* write a pango.rc file and tell pango to use it */ - /* TODO: This bit of code doesn't work properly yet - Glib::signal_idle().connect (bind (mem_fun (*ui, &ARDOUR_UI::cmdline_new_session), path)); - ui->set_will_create_new_session_automatically (true); - */ + path = dir_path; + path += "/../Resources/pango.rc"; + + std::ofstream pangorc (path.c_str()); + if (!pangorc) { + error << string_compose (_("cannot open pango.rc file %1") , path) << endmsg; + } else { + pangorc << "[Pango]\nModuleFiles="; + Glib::ustring mpath = dir_path; + mpath += "/../Resources/pango.modules"; + pangorc << mpath << endl; - /* Show the NSD */ - ui->hide_splash (); - if (!Config->get_no_new_session_dialog()) { - if (!ui->new_session ()) { - return false; - } - } + pangorc.close (); + setenv ("PANGO_RC_FILE", path.c_str(), 1); } - return true; + // gettext charset aliases + + setenv ("CHARSETALIASDIR", path.c_str(), 1); + + // font config + + path = dir_path; + path += "/../Resources/fonts.conf"; + + setenv ("FONTCONFIG_FILE", path.c_str(), 1); + + // GDK Pixbuf loader module file + + path = dir_path; + path += "/../Resources/gdk-pixbuf.loaders"; + + setenv ("GDK_PIXBUF_MODULE_FILE", path.c_str(), 1); + + if (getenv ("ARDOUR_WITH_JACK")) { + // JACK driver dir + + path = dir_path; + path += "/../Frameworks"; + + setenv ("JACK_DRIVER_DIR", path.c_str(), 1); + } } +#endif + #ifdef VST_SUPPORT /* this is called from the entry point of a wine-compiled executable that is linked against gtk2_ardour built @@ -167,15 +232,17 @@ int ardour_main (int argc, char *argv[]) #else int main (int argc, char *argv[]) #endif - { - ARDOUR::AudioEngine *engine = NULL; vector<Glib::ustring> null_file_list; - Glib::thread_init(); +#ifdef __APPLE__ + fixup_bundle_environment (); +#endif + + Glib::thread_init(); gtk_set_locale (); - (void) bindtextdomain (PACKAGE, LOCALEDIR); + (void) bindtextdomain (PACKAGE, localedir); /* our i18n translations are all in UTF-8, so make sure that even if the user locale doesn't specify UTF-8, we use that when handling them. @@ -229,17 +296,13 @@ int main (int argc, char *argv[]) PBD::ID::init (); - try { + try { ui = new ARDOUR_UI (&argc, &argv); } catch (failed_constructor& err) { error << _("could not create ARDOUR GUI") << endmsg; exit (1); } - if (!keybindings_path.empty()) { - ui->set_keybindings_path (keybindings_path); - } - if (!no_splash) { ui->show_splash (); if (session_name.length()) { @@ -247,50 +310,17 @@ int main (int argc, char *argv[]) } } - try { - ARDOUR::init (use_vst, try_hw_optimization); - setup_gtk_ardour_enums (); - Config->set_current_owner (ConfigVariableBase::Interface); - ui->setup_profile (); - - try { - engine = new ARDOUR::AudioEngine (jack_client_name); - } catch (AudioEngine::NoBackendAvailable& err) { - gui_jack_error (); - error << string_compose (_("Could not connect to JACK server as \"%1\""), jack_client_name) << endmsg; - return -1; - } - - ARDOUR::setup_midi(*engine); - - ui->set_engine (*engine); - - } catch (failed_constructor& err) { - error << _("could not initialize Ardour.") << endmsg; - return -1; - } - - ui->start_engine (); - - if (maybe_load_session ()) { - ui->run (text_receiver); - ui = 0; + if (!keybindings_path.empty()) { + ui->set_keybindings_path (keybindings_path); } - delete engine; - ARDOUR::cleanup (); + ui->run (text_receiver); + ui = 0; - if (ui) { - ui->kill(); - } - + ARDOUR::cleanup (); pthread_cancel_all (); - - exit (0); - return 0; } #ifdef VST_SUPPORT } // end of extern C block #endif - diff --git a/gtk2_ardour/mixer_strip.cc b/gtk2_ardour/mixer_strip.cc index dfe5a268fa..f791314d75 100644 --- a/gtk2_ardour/mixer_strip.cc +++ b/gtk2_ardour/mixer_strip.cc @@ -1227,19 +1227,19 @@ MixerStrip::route_active_changed () } else if (is_audio_track()) { if (_route->active()) { set_name ("AudioTrackStripBase"); - gpm.set_meter_strip_name ("AudioTrackStripBase"); + gpm.set_meter_strip_name ("AudioTrackMetrics"); } else { set_name ("AudioTrackStripBaseInactive"); - gpm.set_meter_strip_name ("AudioTrackStripBaseInactive"); + gpm.set_meter_strip_name ("AudioTrackMetricsInactive"); } gpm.set_fader_name ("AudioTrackFader"); } else { if (_route->active()) { set_name ("AudioBusStripBase"); - gpm.set_meter_strip_name ("AudioBusStripBase"); + gpm.set_meter_strip_name ("AudioBusMetrics"); } else { set_name ("AudioBusStripBaseInactive"); - gpm.set_meter_strip_name ("AudioBusStripBaseInactive"); + gpm.set_meter_strip_name ("AudioBusMetricsInactive"); } gpm.set_fader_name ("AudioBusFader"); diff --git a/gtk2_ardour/mixer_ui.cc b/gtk2_ardour/mixer_ui.cc index 89d7ef5cc4..e3f6cae6ac 100644 --- a/gtk2_ardour/mixer_ui.cc +++ b/gtk2_ardour/mixer_ui.cc @@ -61,12 +61,17 @@ using PBD::atoi; Mixer_UI::Mixer_UI () : Window (Gtk::WINDOW_TOPLEVEL) { + session = 0; _strip_width = Wide; track_menu = 0; mix_group_context_menu = 0; no_track_list_redisplay = false; in_group_row_change = false; _visible = false; + ignore_route_reorder = false; + ignore_sync = false; + + Route::SyncOrderKeys.connect (mem_fun (*this, &Mixer_UI::sync_order_keys)); scroller_base.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK); scroller_base.set_name ("MixerWindow"); @@ -92,6 +97,7 @@ Mixer_UI::Mixer_UI () track_model->signal_row_deleted().connect (mem_fun (*this, &Mixer_UI::track_list_delete)); track_model->signal_row_changed().connect (mem_fun (*this, &Mixer_UI::track_list_change)); + track_model->signal_rows_reordered().connect (mem_fun (*this, &Mixer_UI::track_list_reorder)); CellRendererToggle* track_list_visible_cell = dynamic_cast<CellRendererToggle*>(track_display.get_column_cell_renderer (1)); track_list_visible_cell->property_activatable() = true; @@ -318,6 +324,8 @@ Mixer_UI::add_strip (Session::RouteList& routes) row[track_columns.route] = route; row[track_columns.strip] = strip; + strip->set_old_order_key (track_model->children().size() - 1); + no_track_list_redisplay = false; redisplay_track_list (); @@ -349,6 +357,42 @@ Mixer_UI::remove_strip (MixerStrip* strip) } } +const char* +Mixer_UI::get_order_key() +{ + if (Config->get_sync_all_route_ordering()) { + return X_("editor"); + } else { + return X_("signal"); + } +} + +void +Mixer_UI::sync_order_keys () +{ + vector<int> neworder; + TreeModel::Children rows = track_model->children(); + TreeModel::Children::iterator ri; + + if (ignore_sync || !session || (session->state_of_the_state() & Session::Loading) || rows.empty()) { + return; + } + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + neworder.push_back (0); + } + + for (ri = rows.begin(); ri != rows.end(); ++ri) { + boost::shared_ptr<Route> route = (*ri)[track_columns.route]; + MixerStrip* strip = (*ri)[track_columns.strip]; + neworder[route->order_key (get_order_key())] = strip->old_order_key (); + } + + ignore_route_reorder = true; + track_model->reorder (neworder); + ignore_route_reorder = false; +} + void Mixer_UI::follow_strip_selection () { @@ -594,6 +638,13 @@ Mixer_UI::hide_all_audiotracks () } void +Mixer_UI::track_list_reorder (const TreeModel::Path& path, const TreeModel::iterator& iter, int* new_order) +{ + session->set_remote_control_ids(); + redisplay_track_list (); +} + +void Mixer_UI::track_list_change (const Gtk::TreeModel::Path& path,const Gtk::TreeModel::iterator& iter) { session->set_remote_control_ids(); @@ -632,6 +683,10 @@ Mixer_UI::redisplay_track_list () strip->set_marked_for_display (true); strip->route()->set_order_key (N_("signal"), order); + if (!ignore_route_reorder) { + strip->route()->set_order_key (get_order_key(), order); + } + if (strip->packed()) { if (strip->route()->is_master() || strip->route()->is_control()) { @@ -663,12 +718,18 @@ Mixer_UI::redisplay_track_list () } } } + + if (Config->get_sync_all_route_ordering() && !ignore_route_reorder) { + ignore_sync = true; + Route::SyncOrderKeys (); // EMIT SIGNAL + ignore_sync = false; + } } struct SignalOrderRouteSorter { bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) { /* use of ">" forces the correct sort order */ - return a->order_key ("signal") < b->order_key ("signal"); + return a->order_key (Mixer_UI::get_order_key()) < b->order_key (Mixer_UI::get_order_key()); } }; diff --git a/gtk2_ardour/mixer_ui.h b/gtk2_ardour/mixer_ui.h index 3c9355efb7..5d00acff5b 100644 --- a/gtk2_ardour/mixer_ui.h +++ b/gtk2_ardour/mixer_ui.h @@ -77,6 +77,8 @@ class Mixer_UI : public Gtk::Window void ensure_float (Gtk::Window&); RouteRedirectSelection& selection() { return _selection; } + + static const char* get_order_key(); private: ARDOUR::Session *session; @@ -148,6 +150,7 @@ class Mixer_UI : public Gtk::Window void track_list_change (const Gtk::TreeModel::Path&,const Gtk::TreeModel::iterator&); void track_list_delete (const Gtk::TreeModel::Path&); + void track_list_reorder (const Gtk::TreeModel::Path& path, const Gtk::TreeModel::iterator& iter, int* new_order); void initial_track_display (); void show_track_list_menu (); @@ -234,6 +237,10 @@ class Mixer_UI : public Gtk::Window Width _strip_width; + void sync_order_keys (); + bool ignore_route_reorder; + bool ignore_sync; + static const int32_t default_width = 478; static const int32_t default_height = 765; }; diff --git a/gtk2_ardour/new_session_dialog.cc b/gtk2_ardour/new_session_dialog.cc index 132af4a86c..ebf520fba7 100644 --- a/gtk2_ardour/new_session_dialog.cc +++ b/gtk2_ardour/new_session_dialog.cc @@ -23,6 +23,8 @@ #include <ardour/recent_sessions.h> #include <ardour/session_state_utils.h> #include <ardour/template_utils.h> +#include <ardour/session.h> +#include <ardour/profile.h> #include <gtkmm/entry.h> #include <gtkmm/filechooserbutton.h> @@ -44,9 +46,10 @@ using namespace PBD; NewSessionDialog::NewSessionDialog() : ArdourDialog ("session control") { + in_destructor = false; session_name_label = Gtk::manage(new class Gtk::Label(_("Name :"))); m_name = Gtk::manage(new class Gtk::Entry()); - m_name->set_text(GTK_ARDOUR::session_name); + m_name->set_text(ARDOUR_COMMAND_LINE::session_name); chan_count_label_1 = Gtk::manage(new class Gtk::Label(_("channels"))); chan_count_label_2 = Gtk::manage(new class Gtk::Label(_("channels"))); @@ -308,8 +311,11 @@ NewSessionDialog::NewSessionDialog() new_session_table->attach(*m_folder, 1, 2, 1, 2, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0); new_session_table->attach(*session_template_label, 0, 1, 2, 3, Gtk::FILL, Gtk::FILL, 0, 0); new_session_table->attach(*m_template, 1, 2, 2, 3, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 0, 0); - new_session_table->attach(*advanced_expander, 0, 2, 3, 4, Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 6); + if (!ARDOUR::Profile->get_sae()) { + new_session_table->attach(*advanced_expander, 0, 2, 3, 4, Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 0, 6); + } + open_session_hbox->pack_start(*open_session_file_label, false, false, 12); open_session_hbox->pack_start(*m_open_filechooser, true, true, 12); m_treeview->set_flags(Gtk::CAN_FOCUS); @@ -358,7 +364,7 @@ NewSessionDialog::NewSessionDialog() // add_button(Gtk::Stock::HELP, Gtk::RESPONSE_HELP); add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CANCEL); add_button(Gtk::Stock::CLEAR, Gtk::RESPONSE_NONE); - m_okbutton = add_button(Gtk::Stock::NEW, Gtk::RESPONSE_OK); + m_okbutton = add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); recent_model = Gtk::TreeStore::create (recent_columns); m_treeview->set_model (recent_model); @@ -401,7 +407,7 @@ NewSessionDialog::NewSessionDialog() set_default_response (Gtk::RESPONSE_OK); - if (!GTK_ARDOUR::session_name.length()) { + if (!ARDOUR_COMMAND_LINE::session_name.length()) { set_response_sensitive (Gtk::RESPONSE_OK, false); set_response_sensitive (Gtk::RESPONSE_NONE, false); } else { @@ -426,12 +432,39 @@ NewSessionDialog::NewSessionDialog() m_name->grab_focus(); } +NewSessionDialog::~NewSessionDialog() +{ + in_destructor = true; +} + +void +NewSessionDialog::set_have_engine (bool yn) +{ + if (yn) { + m_notebook->remove_page (engine_control); + } else { + // XXX this is a bit of crude hack. if we ever add or remove + // pages from the notebook, this is going to break. + if (m_notebook->get_n_pages () != 3) { + m_notebook->append_page (engine_control, _("Audio Setup")); + m_notebook->show_all_children(); + } + } +} + + void NewSessionDialog::set_session_name(const Glib::ustring& name) { m_name->set_text(name); } +void +NewSessionDialog::set_session_folder(const Glib::ustring& dir) +{ + // XXX DO SOMETHING +} + std::string NewSessionDialog::session_name() const { @@ -450,7 +483,7 @@ NewSessionDialog::session_name() const } */ - if (on_new_session_page ()) { + if (on_newable_page()) { return Glib::filename_from_utf8(m_name->get_text()); } else { if (m_treeview->get_selection()->count_selected_rows() == 0) { @@ -464,10 +497,9 @@ NewSessionDialog::session_name() const std::string NewSessionDialog::session_folder() const { - if (on_new_session_page ()) { + if (on_newable_page()) { return Glib::filename_from_utf8(m_folder->get_filename()); } else { - if (m_treeview->get_selection()->count_selected_rows() == 0) { std::string str = Glib::filename_from_utf8(m_open_filechooser->get_filename()); return Glib::path_get_dirname(str); @@ -563,9 +595,16 @@ NewSessionDialog::connect_outs_to_physical() const } bool -NewSessionDialog::on_new_session_page() const +NewSessionDialog::on_newable_page() const { - return (m_notebook->get_current_page() == 0); + return (m_notebook->get_current_page() == 0 || + m_notebook->get_current_page() == 2); +} + +int +NewSessionDialog::get_current_page() const +{ + return m_notebook->get_current_page(); } void @@ -595,7 +634,11 @@ NewSessionDialog::on_new_session_name_entry_changed () void NewSessionDialog::notebook_page_changed (GtkNotebookPage* np, uint pagenum) { - if (!on_new_session_page ()) { + if (in_destructor) { + return; + } + + if (!on_newable_page ()) { m_okbutton->set_label(_("Open")); set_response_sensitive (Gtk::RESPONSE_NONE, false); m_okbutton->set_image (*(new Gtk::Image (Gtk::Stock::OPEN, Gtk::ICON_SIZE_BUTTON))); @@ -635,7 +678,7 @@ NewSessionDialog::treeview_selection_changed () void NewSessionDialog::file_chosen () { - if (on_new_session_page ()) return; + if (on_newable_page ()) return; m_treeview->get_selection()->unselect_all(); diff --git a/gtk2_ardour/new_session_dialog.h b/gtk2_ardour/new_session_dialog.h index 2a2081e56a..53ec3eabf2 100644 --- a/gtk2_ardour/new_session_dialog.h +++ b/gtk2_ardour/new_session_dialog.h @@ -38,6 +38,7 @@ #include <glibmm/refptr.h> #include "ardour_dialog.h" +#include "engine_dialog.h" namespace Gtk { class Entry; @@ -54,8 +55,10 @@ class NewSessionDialog : public ArdourDialog public: NewSessionDialog(); + ~NewSessionDialog (); void set_session_name(const Glib::ustring& name); + void set_session_folder(const Glib::ustring& folder); std::string session_name() const; std::string session_folder() const; @@ -82,14 +85,17 @@ public: bool connect_outs_to_master() const; bool connect_outs_to_physical() const ; - bool on_new_session_page () const; - + bool on_newable_page() const; + int get_current_page () const; void set_current_page (int); void reset_recent(); // reset everything to default values. void reset(); + EngineControl engine_control; + void set_have_engine (bool yn); + protected: void reset_name(); @@ -173,6 +179,8 @@ protected: RecentSessionModelColumns recent_columns; Glib::RefPtr<Gtk::TreeStore> recent_model; + bool in_destructor; + void recent_session_selection_changed (); void nsd_redisplay_recent_sessions(); void nsd_recent_session_row_activated (const Gtk::TreePath& path, Gtk::TreeViewColumn* col); @@ -194,6 +202,7 @@ protected: void master_bus_button_clicked (); void monitor_bus_button_clicked (); + bool have_engine; }; #endif // NEW_SESSION_DIALOG_H diff --git a/gtk2_ardour/option_editor.cc b/gtk2_ardour/option_editor.cc index 513e46448e..e49b410668 100644 --- a/gtk2_ardour/option_editor.cc +++ b/gtk2_ardour/option_editor.cc @@ -27,7 +27,6 @@ #include <ardour/crossfade.h> #include <midi++/manager.h> #include <midi++/factory.h> -#include <midi++/port_request.h> #include <gtkmm2ext/stop_signal.h> #include <gtkmm2ext/utils.h> #include <gtkmm2ext/window_title.h> @@ -43,6 +42,7 @@ #include "editing.h" #include "option_editor.h" #include "midi_port_dialog.h" +#include "gui_thread.h" #include "i18n.h" @@ -56,7 +56,7 @@ using namespace std; static vector<string> positional_sync_strings; OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) - : Dialog ("options editor"), + : ArdourDialog ("options editor", false), ui (uip), editor (ed), mixer (mixui), @@ -64,24 +64,33 @@ OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) /* Paths */ path_table (11, 2), - /* Fades */ + /* misc */ short_xfade_adjustment (0, 1.0, 500.0, 5.0, 100.0), short_xfade_slider (short_xfade_adjustment), destructo_xfade_adjustment (1.0, 1.0, 500.0, 1.0, 100.0), destructo_xfade_slider (destructo_xfade_adjustment), + history_depth (20, -1, 100, 1.0, 10.0), + saved_history_depth (20, 0, 100, 1.0, 10.0), + history_depth_spinner (history_depth), + saved_history_depth_spinner (saved_history_depth), + limit_history_button (_("Limit undo history")), + save_history_button (_("Save undo history")), /* Sync */ smpte_offset_clock (X_("smpteoffset"), false, X_("SMPTEOffsetClock"), true, true), smpte_offset_negative_button (_("SMPTE offset is negative")), + synced_timecode_button (_("Timecode source is sample-clock synced")), /* MIDI */ + midi_port_table (4, 11), mmc_receive_device_id_adjustment (0.0, 0.0, (double) 0x7f, 1.0, 16.0), mmc_receive_device_id_spinner (mmc_receive_device_id_adjustment), mmc_send_device_id_adjustment (0.0, 0.0, (double) 0x7f, 1.0, 16.0), mmc_send_device_id_spinner (mmc_send_device_id_adjustment), + add_midi_port_button (_("Add new MIDI port")), /* Click */ @@ -105,13 +114,13 @@ OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) session = 0; WindowTitle title(Glib::get_application_name()); - title += _("Options Editor"); + title += _("Preferences"); set_title(title.get_string()); set_default_size (300, 300); - set_wmclass (X_("ardour_option_editor"), "Ardour"); + set_wmclass (X_("ardour_preferences"), "Ardour"); - set_name ("OptionsWindow"); + set_name ("Preferences"); add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK); VBox *vbox = get_vbox(); @@ -128,7 +137,7 @@ OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) setup_sync_options(); setup_path_options(); - setup_fade_options (); + setup_misc_options (); setup_keyboard_options (); setup_auditioner_editor (); @@ -137,15 +146,16 @@ OptionEditor::OptionEditor (ARDOUR_UI& uip, PublicEditor& ed, Mixer_UI& mixui) notebook.pages().push_back (TabElem (keyboard_mouse_table, _("Kbd/Mouse"))); notebook.pages().push_back (TabElem (click_packer, _("Click"))); notebook.pages().push_back (TabElem (audition_packer, _("Audition"))); - notebook.pages().push_back (TabElem (fade_packer, _("Layers & Fades"))); + notebook.pages().push_back (TabElem (misc_packer, _("Misc"))); - if (!MIDI::Manager::instance()->get_midi_ports().empty()) { - setup_midi_options (); - notebook.pages().push_back (TabElem (midi_packer, _("MIDI"))); - } + setup_midi_options (); + notebook.pages().push_back (TabElem (midi_packer, _("MIDI"))); set_session (0); show_all_children(); + + Config->map_parameters (mem_fun (*this, &OptionEditor::parameter_changed)); + Config->ParameterChanged.connect (mem_fun (*this, &OptionEditor::parameter_changed)); } void @@ -182,27 +192,7 @@ OptionEditor::set_session (Session *s) smpte_offset_negative_button.set_active (session->smpte_offset_negative()); - /* set up port assignments */ - - std::map<MIDI::Port*,vector<RadioButton*> >::iterator res; - - if (session->mtc_port()) { - if ((res = port_toggle_buttons.find (session->mtc_port())) != port_toggle_buttons.end()) { - (*res).second[MtcIndex]->set_active (true); - } - } - - if (session->mmc_port ()) { - if ((res = port_toggle_buttons.find (session->mmc_port())) != port_toggle_buttons.end()) { - (*res).second[MmcIndex]->set_active (true); - } - } - - if (session->midi_port()) { - if ((res = port_toggle_buttons.find (session->midi_port())) != port_toggle_buttons.end()) { - (*res).second[MidiIndex]->set_active (true); - } - } + redisplay_midi_ports (); setup_click_editor (); connect_audition_editor (); @@ -260,7 +250,7 @@ OptionEditor::add_session_paths () } void -OptionEditor::setup_fade_options () +OptionEditor::setup_misc_options () { Gtk::HBox* hbox; @@ -272,7 +262,7 @@ OptionEditor::setup_fade_options () hbox->set_spacing (10); hbox->pack_start (*label, false, false); hbox->pack_start (short_xfade_slider, true, true); - fade_packer.pack_start (*hbox, false, false); + misc_packer.pack_start (*hbox, false, false); short_xfade_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::short_xfade_adjustment_changed)); @@ -284,16 +274,94 @@ OptionEditor::setup_fade_options () hbox->set_spacing (10); hbox->pack_start (*label, false, false); hbox->pack_start (destructo_xfade_slider, true, true); - fade_packer.pack_start (*hbox, false, false); + misc_packer.pack_start (*hbox, false, false); + destructo_xfade_adjustment.signal_value_changed().connect (mem_fun(*this, &OptionEditor::destructo_xfade_adjustment_changed)); + hbox = manage (new HBox); + hbox->set_border_width (5); + hbox->set_spacing (10); + hbox->pack_start (limit_history_button, false, false); + misc_packer.pack_start (*hbox, false, false); + + label = manage (new Label (_("History depth (commands)"))); + label->set_name ("OptionsLabel"); + + hbox = manage (new HBox); + hbox->set_border_width (5); + hbox->set_spacing (10); + hbox->pack_start (*label, false, false); + hbox->pack_start (history_depth_spinner, false, false); + misc_packer.pack_start (*hbox, false, false); + + history_depth.signal_value_changed().connect (mem_fun (*this, &OptionEditor::history_depth_changed)); + saved_history_depth.signal_value_changed().connect (mem_fun (*this, &OptionEditor::saved_history_depth_changed)); + save_history_button.signal_toggled().connect (mem_fun (*this, &OptionEditor::save_history_toggled)); + limit_history_button.signal_toggled().connect (mem_fun (*this, &OptionEditor::limit_history_toggled)); + + hbox = manage (new HBox); + hbox->set_border_width (5); + hbox->set_spacing (10); + hbox->pack_start (save_history_button, false, false); + misc_packer.pack_start (*hbox, false, false); + + label = manage (new Label (_("Saved history depth (commands)"))); + label->set_name ("OptionsLabel"); + + hbox = manage (new HBox); + hbox->set_border_width (5); + hbox->set_spacing (10); + hbox->pack_start (*label, false, false); + hbox->pack_start (saved_history_depth_spinner, false, false); + misc_packer.pack_start (*hbox, false, false); + short_xfade_slider.set_update_policy (UPDATE_DISCONTINUOUS); destructo_xfade_slider.set_update_policy (UPDATE_DISCONTINUOUS); destructo_xfade_adjustment.set_value (Config->get_destructive_xfade_msecs()); - fade_packer.show_all (); + misc_packer.show_all (); +} + +void +OptionEditor::limit_history_toggled () +{ + bool x = limit_history_button.get_active(); + + if (!x) { + Config->set_history_depth (0); + history_depth_spinner.set_sensitive (false); + } else { + if (Config->get_history_depth() == 0) { + /* get back to a sane default */ + Config->set_history_depth (20); + } + history_depth_spinner.set_sensitive (true); + } +} + +void +OptionEditor::save_history_toggled () +{ + bool x = save_history_button.get_active(); + + if (x != Config->get_save_history()) { + Config->set_save_history (x); + saved_history_depth_spinner.set_sensitive (x); + } +} + +void +OptionEditor::history_depth_changed() +{ + Config->set_history_depth ((int32_t) floor (history_depth.get_value())); +} + +void +OptionEditor::saved_history_depth_changed() +{ + Config->set_saved_history_depth ((int32_t) floor (saved_history_depth.get_value())); } void @@ -347,8 +415,10 @@ OptionEditor::setup_sync_options () hbox->pack_start (smpte_offset_negative_button, false, false); sync_packer.pack_start (*hbox, false, false); + sync_packer.pack_start (synced_timecode_button, false, false); smpte_offset_negative_button.signal_clicked().connect (mem_fun(*this, &OptionEditor::smpte_offset_negative_clicked)); + synced_timecode_button.signal_toggled().connect (mem_fun(*this, &OptionEditor::synced_timecode_toggled)); } void @@ -360,6 +430,17 @@ OptionEditor::smpte_offset_negative_clicked () } void +OptionEditor::synced_timecode_toggled () +{ + bool x; + + if ((x = synced_timecode_button.get_active()) != Config->get_timecode_source_is_synced()) { + Config->set_timecode_source_is_synced (x); + Config->save_state(); + } +} + +void OptionEditor::smpte_offset_chosen() { if (session) { @@ -368,6 +449,7 @@ OptionEditor::smpte_offset_chosen() } } + void OptionEditor::setup_midi_options () { @@ -379,6 +461,9 @@ OptionEditor::setup_midi_options () redisplay_midi_ports (); + mmc_receive_device_id_adjustment.set_value (Config->get_mmc_receive_device_id()); + mmc_send_device_id_adjustment.set_value (Config->get_mmc_send_device_id()); + mmc_receive_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_receive_device_id_adjusted)); mmc_send_device_id_adjustment.signal_value_changed().connect (mem_fun (*this, &OptionEditor::mmc_send_device_id_adjusted)); @@ -396,7 +481,9 @@ OptionEditor::setup_midi_options () label = (manage (new Label (_("Inbound MMC Device ID")))); hbox->pack_start (mmc_receive_device_id_spinner, false, false); hbox->pack_start (*label, false, false); - midi_packer.pack_start (*hbox, false, false); + midi_packer.pack_start (*hbox, false, false); + + mmc_receive_device_id_spinner.set_value(Config->get_mmc_receive_device_id ()); hbox = manage (new HBox); hbox->set_border_width (6); @@ -406,6 +493,8 @@ OptionEditor::setup_midi_options () hbox->pack_start (*label, false, false); midi_packer.pack_start (*hbox, false, false); + mmc_send_device_id_spinner.set_value(Config->get_mmc_send_device_id ()); + add_midi_port_button.signal_clicked().connect (mem_fun (*this, &OptionEditor::add_midi_port)); } @@ -624,12 +713,15 @@ OptionEditor::add_midi_port () smod = "duplex"; } - MIDI::PortRequest req (X_("ardour"), - dialog.port_name.get_text(), - smod, - MIDI::PortFactory::default_port_type()); - if (MIDI::Manager::instance()->add_port (req) != 0) { + XMLNode node (X_("MIDI-port")); + + node.add_property ("tag", dialog.port_name.get_text()); + node.add_property ("device", X_("ardour")); // XXX this can't be right for all types + node.add_property ("type", MIDI::PortFactory::default_port_type()); + node.add_property ("mode", smod); + + if (MIDI::Manager::instance()->add_port (node) != 0) { redisplay_midi_ports (); } } @@ -696,23 +788,27 @@ OptionEditor::port_online_toggled (MIDI::Port* port, ToggleButton* tb) { bool wanted = tb->get_active(); - if (wanted != port->input()->offline()) { - port->input()->set_offline (wanted); - } + if (port->input()) { + if (wanted != port->input()->offline()) { + port->input()->set_offline (wanted); + } + } } void OptionEditor::map_port_online (MIDI::Port* port, ToggleButton* tb) { bool bstate = tb->get_active (); - - if (bstate != port->input()->offline()) { - if (port->input()->offline()) { - tb->set_label (_("offline")); - tb->set_active (false); - } else { - tb->set_label (_("online")); - tb->set_active (true); + + if (port->input()) { + if (bstate != port->input()->offline()) { + if (port->input()->offline()) { + tb->set_label (_("offline")); + tb->set_active (false); + } else { + tb->set_label (_("online")); + tb->set_active (true); + } } } } @@ -721,20 +817,14 @@ void OptionEditor::mmc_receive_device_id_adjusted () { uint8_t id = (uint8_t) mmc_receive_device_id_spinner.get_value(); - - if (id != Config->get_mmc_receive_device_id()) { - Config->set_mmc_receive_device_id (id); - } + Config->set_mmc_receive_device_id (id); } void OptionEditor::mmc_send_device_id_adjusted () { uint8_t id = (uint8_t) mmc_send_device_id_spinner.get_value(); - - if (id != Config->get_mmc_send_device_id()) { - Config->set_mmc_send_device_id (id); - } + Config->set_mmc_send_device_id (id); } void @@ -742,8 +832,10 @@ OptionEditor::port_trace_in_toggled (MIDI::Port* port, ToggleButton* tb) { bool trace = tb->get_active(); - if (port->input()->tracing() != trace) { - port->input()->trace (trace, &cerr, string (port->name()) + string (" input: ")); + if (port->input()) { + if (port->input()->tracing() != trace) { + port->input()->trace (trace, &cerr, string (port->name()) + string (" input: ")); + } } } @@ -752,8 +844,10 @@ OptionEditor::port_trace_out_toggled (MIDI::Port* port, ToggleButton* tb) { bool trace = tb->get_active(); - if (port->output()->tracing() != trace) { - port->output()->trace (trace, &cerr, string (port->name()) + string (" output: ")); + if (port->output()) { + if (port->output()->tracing() != trace) { + port->output()->trace (trace, &cerr, string (port->name()) + string (" output: ")); + } } } @@ -784,10 +878,13 @@ OptionEditor::raid_path_changed () void OptionEditor::click_browse_clicked () { - SoundFileChooser sfdb (_("Choose Click"), session); + SoundFileChooser sfdb (*this, _("Choose Click"), session); - int result = sfdb.run (); + sfdb.show_all (); + sfdb.present (); + int result = sfdb.run (); + if (result == Gtk::RESPONSE_OK) { click_chosen(sfdb.get_filename()); } @@ -803,7 +900,10 @@ OptionEditor::click_chosen (const string & path) void OptionEditor::click_emphasis_browse_clicked () { - SoundFileChooser sfdb (_("Choose Click Emphasis"), session); + SoundFileChooser sfdb (*this, _("Choose Click Emphasis"), session); + + sfdb.show_all (); + sfdb.present (); int result = sfdb.run (); @@ -1167,3 +1267,31 @@ OptionEditor::fixup_combo_size (Gtk::ComboBoxText& combo, vector<string>& string set_size_request_to_display_given_text (combo, maxstring.c_str(), 10 + FUDGE, 10); } +void +OptionEditor::parameter_changed (const char* parameter_name) +{ + ENSURE_GUI_THREAD (bind (mem_fun (*this, &OptionEditor::parameter_changed), parameter_name)); + +#define PARAM_IS(x) (!strcmp (parameter_name, (x))) + + if (PARAM_IS ("timecode-source-is-synced")) { + synced_timecode_button.set_active (Config->get_timecode_source_is_synced()); + } else if (PARAM_IS ("history-depth")) { + int32_t depth = Config->get_history_depth(); + + history_depth.set_value (depth); + history_depth_spinner.set_sensitive (depth != 0); + limit_history_button.set_active (depth != 0); + + } else if (PARAM_IS ("saved-history-depth")) { + + saved_history_depth.set_value (Config->get_saved_history_depth()); + + } else if (PARAM_IS ("save-history")) { + + bool x = Config->get_save_history(); + + save_history_button.set_active (x); + saved_history_depth_spinner.set_sensitive (x); + } +} diff --git a/gtk2_ardour/option_editor.h b/gtk2_ardour/option_editor.h index a234f1d752..82bb4db79b 100644 --- a/gtk2_ardour/option_editor.h +++ b/gtk2_ardour/option_editor.h @@ -45,7 +45,7 @@ class IOSelector; class GainMeter; class PannerUI; -class OptionEditor : public Gtk::Dialog +class OptionEditor : public ArdourDialog { public: OptionEditor (ARDOUR_UI&, PublicEditor&, Mixer_UI&); @@ -66,6 +66,7 @@ class OptionEditor : public Gtk::Dialog gint wm_close (GdkEventAny *); bool focus_out_event_handler (GdkEventFocus*, void (OptionEditor::*pmf)()); + void parameter_changed (const char* name); /* paths */ @@ -77,18 +78,32 @@ class OptionEditor : public Gtk::Dialog void remove_session_paths (); void raid_path_changed (); - /* fades */ + /* misc */ + + Gtk::VBox misc_packer; - Gtk::VBox fade_packer; Gtk::Adjustment short_xfade_adjustment; Gtk::HScale short_xfade_slider; Gtk::Adjustment destructo_xfade_adjustment; Gtk::HScale destructo_xfade_slider; - void setup_fade_options(); + void setup_misc_options(); + void short_xfade_adjustment_changed (); void destructo_xfade_adjustment_changed (); + Gtk::Adjustment history_depth; + Gtk::Adjustment saved_history_depth; + Gtk::SpinButton history_depth_spinner; + Gtk::SpinButton saved_history_depth_spinner; + Gtk::CheckButton limit_history_button; + Gtk::CheckButton save_history_button; + + void history_depth_changed(); + void saved_history_depth_changed(); + void save_history_toggled (); + void limit_history_toggled (); + /* Sync */ Gtk::VBox sync_packer; @@ -96,11 +111,13 @@ class OptionEditor : public Gtk::Dialog Gtk::ComboBoxText slave_type_combo; AudioClock smpte_offset_clock; Gtk::CheckButton smpte_offset_negative_button; + Gtk::CheckButton synced_timecode_button; void setup_sync_options (); void smpte_offset_chosen (); void smpte_offset_negative_clicked (); + void synced_timecode_toggled (); /* MIDI */ diff --git a/gtk2_ardour/opts.cc b/gtk2_ardour/opts.cc index 608f684fc9..8da0fb9ca1 100644 --- a/gtk2_ardour/opts.cc +++ b/gtk2_ardour/opts.cc @@ -27,18 +27,19 @@ using namespace std; -string GTK_ARDOUR::session_name = ""; -string GTK_ARDOUR::jack_client_name = "ardour"; -bool GTK_ARDOUR::show_key_actions = false; -bool GTK_ARDOUR::no_splash = true; -bool GTK_ARDOUR::just_version = false; -bool GTK_ARDOUR::use_vst = true; -bool GTK_ARDOUR::new_session = false; -char* GTK_ARDOUR::curvetest_file = 0; -bool GTK_ARDOUR::try_hw_optimization = true; -string GTK_ARDOUR::keybindings_path = ""; /* empty means use builtin default */ - -using namespace GTK_ARDOUR; +string ARDOUR_COMMAND_LINE::session_name = ""; +string ARDOUR_COMMAND_LINE::jack_client_name = "ardour"; +bool ARDOUR_COMMAND_LINE::show_key_actions = false; +bool ARDOUR_COMMAND_LINE::no_splash = true; +bool ARDOUR_COMMAND_LINE::just_version = false; +bool ARDOUR_COMMAND_LINE::use_vst = true; +bool ARDOUR_COMMAND_LINE::new_session = false; +char* ARDOUR_COMMAND_LINE::curvetest_file = 0; +bool ARDOUR_COMMAND_LINE::try_hw_optimization = true; +string ARDOUR_COMMAND_LINE::keybindings_path = ""; /* empty means use builtin default */ +Glib::ustring ARDOUR_COMMAND_LINE::menus_file = "ardour.menus"; + +using namespace ARDOUR_COMMAND_LINE; int print_help (const char *execname) @@ -49,9 +50,10 @@ print_help (const char *execname) << _(" -b, --bindings Print all possible keyboard binding names\n") << _(" -n, --show-splash Show splash screen\n") << _(" -c, --name name Use a specific jack client name, default is ardour\n") + << _(" -m, --menus file Use \"file\" for Ardour menus\n") << _(" -N, --new session-name Create a new session from the command line\n") << _(" -O, --no-hw-optimizations Disable h/w specific optimizations\n") - << _(" -S, --sync Draw the gui synchronously \n") + << _(" -S, --sync Draw the gui synchronously \n") #ifdef VST_SUPPORT << _(" -V, --novst Do not use VST support\n") #endif @@ -64,12 +66,16 @@ print_help (const char *execname) } int -GTK_ARDOUR::parse_opts (int argc, char *argv[]) +ARDOUR_COMMAND_LINE::parse_opts (int argc, char *argv[]) { - const char *optstring = "U:hSbvVnOc:C:N:k:"; + const char *optstring = "U:hSbvVnOc:C:m:N:k:"; const char *execname = strrchr (argv[0], '/'); + if (getenv ("ARDOUR_SAE")) { + menus_file = "ardour-sae.menus"; + } + if (execname == 0) { execname = argv[0]; } else { @@ -81,11 +87,12 @@ GTK_ARDOUR::parse_opts (int argc, char *argv[]) { "help", 0, 0, 'h' }, { "bindings", 0, 0, 'b' }, { "show-splash", 0, 0, 'n' }, + { "menus", 1, 0, 'm' }, { "name", 1, 0, 'c' }, { "novst", 0, 0, 'V' }, { "new", 1, 0, 'N' }, { "no-hw-optimizations", 0, 0, 'O' }, - { "sync", 0, 0, 'O' }, + { "sync", 0, 0, 'S' }, { "curvetest", 1, 0, 'C' }, { 0, 0, 0, 0 } }; @@ -116,6 +123,11 @@ GTK_ARDOUR::parse_opts (int argc, char *argv[]) show_key_actions = true; break; + + case 'm': + menus_file = optarg; + break; + case 'n': no_splash = false; break; @@ -133,6 +145,11 @@ GTK_ARDOUR::parse_opts (int argc, char *argv[]) try_hw_optimization = false; break; + + case 'p': + //undocumented OS X finder -psn_XXXXX argument + break; + case 'V': #ifdef VST_SUPPORT use_vst = false; diff --git a/gtk2_ardour/opts.h b/gtk2_ardour/opts.h index fb780fc8aa..c1b3f062d4 100644 --- a/gtk2_ardour/opts.h +++ b/gtk2_ardour/opts.h @@ -21,10 +21,11 @@ #define __ardour_opts_h__ #include <string> +#include <glibmm/ustring.h> using std::string; -namespace GTK_ARDOUR { +namespace ARDOUR_COMMAND_LINE { extern string session_name; extern bool show_key_actions; @@ -37,6 +38,7 @@ extern char* curvetest_file; extern bool try_hw_optimization; extern bool use_gtk_theme; extern string keybindings_path; +extern Glib::ustring menus_file; extern int32_t parse_opts (int argc, char *argv[]); diff --git a/gtk2_ardour/processor_box.cc b/gtk2_ardour/processor_box.cc index d093fda99f..12e30e59c0 100644 --- a/gtk2_ardour/processor_box.cc +++ b/gtk2_ardour/processor_box.cc @@ -338,7 +338,7 @@ ProcessorBox::processor_button_release_event (GdkEventButton *ev) show_processor_menu(ev->time); ret = true; - } else if (processor && (ev->button == 2) && (ev->state == Gdk::BUTTON2_MASK)) { + } else if (processor && (ev->button == 2) && (Keyboard::no_modifier_keys_pressed (ev) && ((ev->state & Gdk::BUTTON2_MASK) == Gdk::BUTTON2_MASK))) { processor->set_active (!processor->active()); ret = true; @@ -394,13 +394,13 @@ ProcessorBox::processor_plugin_chosen (boost::shared_ptr<Plugin> plugin) boost::shared_ptr<Processor> processor (new PluginInsert (_session, plugin, _placement)); - processor->ActiveChanged.connect (bind (mem_fun (*this, &ProcessorBox::show_processor_active), boost::weak_ptr<Processor>(processor))); - Route::ProcessorStreams err; if (_route->add_processor (processor, &err)) { weird_plugin_dialog (*plugin, err, _route); // XXX SHAREDPTR delete plugin here .. do we even need to care? + } else { + processor->ActiveChanged.connect (bind (mem_fun (*this, &ProcessorBox::show_processor_active), boost::weak_ptr<Processor>(processor))); } } } @@ -482,36 +482,39 @@ ProcessorBox::choose_send () boost::shared_ptr<Send> send (new Send (_session, _placement)); //send->set_default_type(_route->default_type()); - /* XXX need redirect lock on route */ + ChanCount outs; + + /* make an educated guess at the initial number of outputs for the send */ + + if (_session.master_out()) { + outs = _session.master_out()->n_outputs(); + } else { + outs = _route->n_outputs(); + } + + send->io()->ensure_io (ChanCount::ZERO, outs, false, this); - // This will be set properly in route->add_processor - send->configure_io (_route->max_processor_outs(), _route->max_processor_outs()); + SendUIWindow* gui = new SendUIWindow (send, _session); - IOSelectorWindow *ios = new IOSelectorWindow (_session, send->io(), false, true); + /* let the user adjust the output setup (number and connections) before passing + it along to the Route + */ - ios->show_all (); + gui->show_all (); + gui->present (); - ios->selector().Finished.connect (bind (mem_fun(*this, &ProcessorBox::send_io_finished), send, ios)); + /* pass shared_ptr, it will go out of scope when the GUI is deleted */ + /* also, connect it *before* existing handlers so that its definitely executed */ + + gui->signal_delete_event().connect (bind (mem_fun(*this, &ProcessorBox::send_io_finished), send, gui), false); } -void -ProcessorBox::send_io_finished (IOSelector::Result r, boost::shared_ptr<Send> send, IOSelectorWindow* ios) +bool +ProcessorBox::send_io_finished (GdkEventAny* ev, boost::shared_ptr<Send> send, SendUIWindow* sui) { - if (!send) { - return; - } - - switch (r) { - case IOSelector::Cancelled: - // send will go away when all shared_ptrs to it vanish - break; - - case IOSelector::Accepted: - _route->add_processor (send); - break; - } - - delete_when_idle (ios); + _route->add_processor (send); + delete sui; + return false; } void diff --git a/gtk2_ardour/processor_box.h b/gtk2_ardour/processor_box.h index 1ed0bcb39e..b6bb3ae3d7 100644 --- a/gtk2_ardour/processor_box.h +++ b/gtk2_ardour/processor_box.h @@ -50,6 +50,7 @@ class MotionController; class PluginSelector; class PluginUIWindow; class RouteRedirectSelection; +class SendUIWindow; namespace ARDOUR { class Bundle; @@ -142,7 +143,7 @@ class ProcessorBox : public Gtk::HBox void show_processor_menu (gint arg); void choose_send (); - void send_io_finished (IOSelector::Result, boost::shared_ptr<ARDOUR::Send>, IOSelectorWindow*); + bool send_io_finished (GdkEventAny*,boost::shared_ptr<ARDOUR::Send>, SendUIWindow*); void choose_processor (); void choose_plugin (); void processor_plugin_chosen (boost::shared_ptr<ARDOUR::Plugin>); diff --git a/gtk2_ardour/send_ui.cc b/gtk2_ardour/send_ui.cc index 7dda653894..c91068f97c 100644 --- a/gtk2_ardour/send_ui.cc +++ b/gtk2_ardour/send_ui.cc @@ -54,7 +54,7 @@ SendUI::SendUI (boost::shared_ptr<Send> s, Session& se) _send->set_metering (true); - _send->io()->output_changed.connect (mem_fun (*this, &SendUI::ins_changed)); + _send->io()->input_changed.connect (mem_fun (*this, &SendUI::ins_changed)); _send->io()->output_changed.connect (mem_fun (*this, &SendUI::outs_changed)); panners.set_width (Wide); diff --git a/gtk2_ardour/sfdb_ui.cc b/gtk2_ardour/sfdb_ui.cc index 5415cd70cb..67bedb6182 100644 --- a/gtk2_ardour/sfdb_ui.cc +++ b/gtk2_ardour/sfdb_ui.cc @@ -21,90 +21,181 @@ #include <cerrno> #include <sstream> +#include <unistd.h> #include <sys/stat.h> +#include <sys/param.h> #include <gtkmm/box.h> #include <gtkmm/stock.h> +#include <glibmm/fileutils.h> #include <pbd/convert.h> #include <pbd/tokenizer.h> +#include <pbd/enumwriter.h> #include <gtkmm2ext/utils.h> #include <ardour/audio_library.h> +#include <ardour/auditioner.h> #include <ardour/audioregion.h> #include <ardour/audiofilesource.h> #include <ardour/region_factory.h> #include <ardour/source_factory.h> +#include <ardour/session.h> +#include <ardour/session_directory.h> +#include <ardour/profile.h> #include "ardour_ui.h" #include "editing.h" #include "gui_thread.h" #include "prompter.h" #include "sfdb_ui.h" +#include "editing.h" #include "utils.h" +#include "gain_meter.h" #include "i18n.h" using namespace ARDOUR; using namespace PBD; using namespace std; +using namespace Gtk; +using namespace Gtkmm2ext; +using namespace Editing; + +using Glib::ustring; -Glib::ustring SoundFileBrowser::persistent_folder; +ustring SoundFileBrowser::persistent_folder; -SoundFileBox::SoundFileBox () - : - _session(0), - current_pid(0), - main_box (false, 3), - bottom_box (true, 4), - play_btn(_("Play")), - stop_btn(_("Stop")), - apply_btn(_("Apply")) +static ImportMode +string2importmode (string str) { - set_name (X_("SoundFileBox")); + if (str == "as new tracks") { + return ImportAsTrack; + } else if (str == "to selected tracks") { + return ImportToTrack; + } else if (str == "to region list") { + return ImportAsRegion; + } else if (str == "as new tape tracks") { + return ImportAsTapeTrack; + } + + warning << string_compose (_("programming error: unknown import mode string %1"), str) << endmsg; - set_size_request (250, 500); + return ImportAsTrack; +} + +static string +importmode2string (ImportMode mode) +{ + switch (mode) { + case ImportAsTrack: + return _("as new tracks"); + case ImportToTrack: + return _("to selected tracks"); + case ImportAsRegion: + return _("to region list"); + case ImportAsTapeTrack: + return _("as new tape tracks"); + } + /*NOTREACHED*/ + return _("as new tracks"); +} + +SoundFileBox::SoundFileBox (bool persistent) + : _session(0), + table (6, 2), + length_clock ("sfboxLengthClock", !persistent, "EditCursorClock", false, true, false), + timecode_clock ("sfboxTimecodeClock", !persistent, "EditCursorClock", false, false, false), + main_box (false, 6), + autoplay_btn (_("Auto-play")) - border_frame.set_label (_("Soundfile Info")); +{ + HBox* hbox; + VBox* vbox; + + set_name (X_("SoundFileBox")); + set_size_request (300, -1); + + preview_label.set_markup (_("<b>Soundfile Info</b>")); + + border_frame.set_label_widget (preview_label); border_frame.add (main_box); - Gtk::Label* tag_label = manage(new Gtk::Label(_("comma seperated tags"))); + pack_start (border_frame, true, true); + set_border_width (6); - pack_start (border_frame); - set_border_width (4); + main_box.set_border_width (6); + main_box.set_spacing (12); - main_box.set_border_width (4); + length.set_text (_("Length:")); + timecode.set_text (_("Timestamp:")); + format.set_text (_("Format:")); + channels.set_text (_("Channels:")); + samplerate.set_text (_("Sample rate:")); - main_box.pack_start(length, false, false); - main_box.pack_start(format, false, false); - main_box.pack_start(channels, false, false); - main_box.pack_start(samplerate, false, false); - main_box.pack_start(timecode, false, false); - main_box.pack_start(*tag_label, false, false); - main_box.pack_start(tags_entry, false, false); - main_box.pack_start(apply_btn, false, false); - main_box.pack_start(bottom_box, false, false); + table.set_col_spacings (6); + table.set_homogeneous (false); + table.set_row_spacings (6); + + table.attach (channels, 0, 1, 0, 1, FILL|EXPAND, (AttachOptions) 0); + table.attach (samplerate, 0, 1, 1, 2, FILL|EXPAND, (AttachOptions) 0); + table.attach (format, 0, 1, 2, 4, FILL|EXPAND, (AttachOptions) 0); + table.attach (length, 0, 1, 4, 5, FILL|EXPAND, (AttachOptions) 0); + table.attach (timecode, 0, 1, 5, 6, FILL|EXPAND, (AttachOptions) 0); + + table.attach (channels_value, 1, 2, 0, 1, FILL, (AttachOptions) 0); + table.attach (samplerate_value, 1, 2, 1, 2, FILL, (AttachOptions) 0); + table.attach (format_text, 1, 2, 2, 4, FILL, AttachOptions (0)); + table.attach (length_clock, 1, 2, 4, 5, FILL, (AttachOptions) 0); + table.attach (timecode_clock, 1, 2, 5, 6, FILL, (AttachOptions) 0); + + length_clock.set_mode (ARDOUR_UI::instance()->secondary_clock.mode()); + timecode_clock.set_mode (AudioClock::SMPTE); + + hbox = manage (new HBox); + hbox->pack_start (table, false, false); + main_box.pack_start (*hbox, false, false); - bottom_box.set_homogeneous(true); - bottom_box.pack_start(play_btn); - bottom_box.pack_start(stop_btn); + tags_entry.set_editable (true); + tags_entry.signal_focus_out_event().connect (mem_fun (*this, &SoundFileBox::tags_entry_left)); + hbox = manage (new HBox); + hbox->pack_start (tags_entry, true, true); - play_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::play_btn_clicked)); - stop_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::stop_btn_clicked)); - apply_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::apply_btn_clicked)); - tags_entry.signal_activate().connect (mem_fun (*this, &SoundFileBox::apply_btn_clicked)); + vbox = manage (new VBox); - length.set_alignment (0.0f, 0.0f); - format.set_alignment (0.0f, 0.0f); - channels.set_alignment (0.0f, 0.0f); - samplerate.set_alignment (0.0f, 0.0f); - timecode.set_alignment (0.0f, 0.0f); + Label* label = manage (new Label (_("Tags:"))); + label->set_alignment (0.0f, 0.5f); + vbox->set_spacing (6); + vbox->pack_start(*label, false, false); + vbox->pack_start(*hbox, true, true); - stop_btn.set_no_show_all (true); - stop_btn.hide(); + main_box.pack_start(*vbox, true, true); + main_box.pack_start(bottom_box, false, false); + + play_btn.set_image (*(manage (new Image (Stock::MEDIA_PLAY, ICON_SIZE_BUTTON)))); + play_btn.set_label (_("Play (double click)")); + + stop_btn.set_image (*(manage (new Image (Stock::MEDIA_STOP, ICON_SIZE_BUTTON)))); + stop_btn.set_label (_("Stop")); - show_all(); + bottom_box.set_homogeneous (false); + bottom_box.set_spacing (6); + bottom_box.pack_start(play_btn, true, true); + bottom_box.pack_start(stop_btn, true, true); + bottom_box.pack_start(autoplay_btn, false, false); + + play_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::audition)); + stop_btn.signal_clicked().connect (mem_fun (*this, &SoundFileBox::stop_audition)); + + length.set_alignment (0.0f, 0.5f); + format.set_alignment (0.0f, 0.5f); + channels.set_alignment (0.0f, 0.5f); + samplerate.set_alignment (0.0f, 0.5f); + timecode.set_alignment (0.0f, 0.5f); + + channels_value.set_alignment (0.0f, 0.5f); + samplerate_value.set_alignment (0.0f, 0.5f); } void @@ -113,41 +204,66 @@ SoundFileBox::set_session(Session* s) _session = s; if (!_session) { - play_btn.set_sensitive(false); - } else { - _session->AuditionActive.connect(mem_fun (*this, &SoundFileBox::audition_status_changed)); - } + play_btn.set_sensitive (false); + stop_btn.set_sensitive (false); + } + + + length_clock.set_session (s); + timecode_clock.set_session (s); } bool -SoundFileBox::setup_labels (string filename) +SoundFileBox::setup_labels (const ustring& filename) { + if (!path.empty()) { + // save existing tags + tags_changed (); + } + path = filename; string error_msg; if(!AudioFileSource::get_soundfile_info (filename, sf_info, error_msg)) { - length.set_text (_("Length: n/a")); - format.set_text (_("Format: n/a")); - channels.set_text (_("Channels: n/a")); - samplerate.set_text (_("Samplerate: n/a")); - timecode.set_text (_("Timecode: n/a")); - tags_entry.set_text (""); + + preview_label.set_markup (_("<b>Soundfile Info</b>")); + format_text.set_text (_("n/a")); + channels_value.set_text (_("n/a")); + samplerate_value.set_text (_("n/a")); + tags_entry.get_buffer()->set_text (""); + + length_clock.set (0); + timecode_clock.set (0); tags_entry.set_sensitive (false); play_btn.set_sensitive (false); - apply_btn.set_sensitive (false); return false; } - length.set_text (string_compose(_("Length: %1"), length2string(sf_info.length, sf_info.samplerate))); - format.set_text (sf_info.format_name); - channels.set_text (string_compose(_("Channels: %1"), sf_info.channels)); - samplerate.set_text (string_compose(_("Samplerate: %1"), sf_info.samplerate)); - timecode.set_text (string_compose (_("Timecode: %1"), length2string(sf_info.timecode, sf_info.samplerate))); + preview_label.set_markup (string_compose ("<b>%1</b>", Glib::path_get_basename (filename))); + format_text.set_text (sf_info.format_name); + channels_value.set_text (to_string (sf_info.channels, std::dec)); + + if (_session && sf_info.samplerate != _session->frame_rate()) { + samplerate.set_markup (string_compose ("<b>%1</b>", _("Sample rate:"))); + samplerate_value.set_markup (string_compose (X_("<b>%1 Hz</b>"), sf_info.samplerate)); + samplerate_value.set_name ("NewSessionSR1Label"); + samplerate.set_name ("NewSessionSR1Label"); + } else { + samplerate.set_text (_("Sample rate:")); + samplerate_value.set_text (string_compose (X_("%1 Hz"), sf_info.samplerate)); + samplerate_value.set_name ("NewSessionSR2Label"); + samplerate.set_name ("NewSessionSR2Label"); + } + + length_clock.set (sf_info.length, true); + timecode_clock.set (sf_info.timecode, true); + + // this is a hack that is fixed in trunk, i think (august 26th, 2007) - vector<string> tags = Library->get_tags (filename); + vector<string> tags = Library->get_tags (string ("//") + filename); stringstream tag_string; for (vector<string>::iterator i = tags.begin(); i != tags.end(); ++i) { @@ -156,158 +272,164 @@ SoundFileBox::setup_labels (string filename) } tag_string << *i; } - tags_entry.set_text (tag_string.str()); + tags_entry.get_buffer()->set_text (tag_string.str()); tags_entry.set_sensitive (true); if (_session) { play_btn.set_sensitive (true); } - apply_btn.set_sensitive (true); return true; } bool -SoundFileBox::tags_entry_left (GdkEventFocus* event) -{ - apply_btn_clicked (); - - return true; +SoundFileBox::autoplay() const +{ + return autoplay_btn.get_active(); +} + +bool +SoundFileBox::audition_oneshot() +{ + audition (); + return false; } void -SoundFileBox::play_btn_clicked () +SoundFileBox::audition () { if (!_session) { return; } - + _session->cancel_audition(); - if (access(path.c_str(), R_OK)) { + if (!Glib::file_test (path, Glib::FILE_TEST_EXISTS)) { warning << string_compose(_("Could not read file: %1 (%2)."), path, strerror(errno)) << endmsg; return; } - typedef std::map<string, boost::shared_ptr<AudioRegion> > RegionCache; - static RegionCache region_cache; - RegionCache::iterator the_region; + boost::shared_ptr<Region> r; + SourceList srclist; + boost::shared_ptr<AudioFileSource> afs; + bool old_sbp = AudioSource::get_build_peakfiles (); - if ((the_region = region_cache.find (path)) == region_cache.end()) { - SourceList srclist; - boost::shared_ptr<AudioFileSource> afs; - - for (int n = 0; n < sf_info.channels; ++n) { - try { - afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (DataType::AUDIO, *_session, path, n, AudioFileSource::Flag (0))); - srclist.push_back(afs); - - } catch (failed_constructor& err) { - error << _("Could not access soundfile: ") << path << endmsg; - return; - } - } + /* don't even think of building peakfiles for these files */ + + AudioSource::set_build_peakfiles (false); - if (srclist.empty()) { + for (int n = 0; n < sf_info.channels; ++n) { + try { + afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (DataType::AUDIO, *_session, path, + n, AudioFileSource::Flag (0), false)); + + srclist.push_back(afs); + + } catch (failed_constructor& err) { + error << _("Could not access soundfile: ") << path << endmsg; + AudioSource::set_build_peakfiles (old_sbp); return; } - - string rname; - - _session->region_name (rname, Glib::path_get_basename(srclist[0]->name()), false); - - pair<string,boost::shared_ptr<AudioRegion> > newpair; - pair<RegionCache::iterator,bool> res; - - newpair.first = path; - newpair.second = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, 0, srclist[0]->length(), rname, 0, Region::DefaultFlags, false)); - - res = region_cache.insert (newpair); - the_region = res.first; } - play_btn.hide(); - stop_btn.show(); - - boost::shared_ptr<Region> r = boost::static_pointer_cast<Region> (the_region->second); + AudioSource::set_build_peakfiles (old_sbp); + + if (srclist.empty()) { + return; + } + + afs = boost::dynamic_pointer_cast<AudioFileSource> (srclist[0]); + string rname = region_name_from_path (afs->path(), false); + r = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (srclist, 0, srclist[0]->length(), rname, 0, Region::DefaultFlags, false)); _session->audition_region(r); } void -SoundFileBox::stop_btn_clicked () +SoundFileBox::stop_audition () { if (_session) { _session->cancel_audition(); - play_btn.show(); - stop_btn.hide(); } } +bool +SoundFileBox::tags_entry_left (GdkEventFocus *ev) +{ + tags_changed (); + return false; +} + void -SoundFileBox::apply_btn_clicked () +SoundFileBox::tags_changed () { - string tag_string = tags_entry.get_text (); + string tag_string = tags_entry.get_buffer()->get_text (); + + if (tag_string.empty()) { + return; + } vector<string> tags; - if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) { + if (!PBD::tokenize (tag_string, string(",\n"), std::back_inserter (tags), true)) { warning << _("SoundFileBox: Could not tokenize string: ") << tag_string << endmsg; return; } - - Library->set_tags (path, tags); - Library->save_changes (); + + save_tags (tags); } void -SoundFileBox::audition_status_changed (bool active) +SoundFileBox::save_tags (const vector<string>& tags) { - ENSURE_GUI_THREAD(bind (mem_fun (*this, &SoundFileBox::audition_status_changed), active)); - - if (!active) { - stop_btn_clicked (); - } + Library->set_tags (string ("//") + path, tags); + Library->save_changes (); } -// this needs to be kept in sync with the ImportMode enum defined in editing.h and editing_syms.h. -static const char *import_mode_strings[] = { - N_("Add to Region list"), - N_("Add to selected Track(s)"), - N_("Add as new Track(s)"), - N_("Add as new Tape Track(s)"), - 0 -}; - -SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s) - : ArdourDialog (title, false), - chooser (Gtk::FILE_CHOOSER_ACTION_OPEN), - found_list (Gtk::ListStore::create(found_list_columns)), +SoundFileBrowser::SoundFileBrowser (Gtk::Window& parent, string title, ARDOUR::Session* s, bool persistent) + : ArdourDialog (parent, title, false, false), + found_list (ListStore::create(found_list_columns)), + chooser (FILE_CHOOSER_ACTION_OPEN), found_list_view (found_list), + preview (persistent), found_search_btn (_("Search")) + { - set_default_size (700, 500); - Gtk::HBox* hbox = manage(new Gtk::HBox); - hbox->pack_start(notebook); - hbox->pack_start(preview, Gtk::PACK_SHRINK); - get_vbox()->pack_start(*hbox); + VBox* vbox; + HBox* hbox; + + gm = 0; + + set_session (s); + resetting_ourselves = false; + + hpacker.set_spacing (6); + hpacker.pack_start (notebook, true, true); + hpacker.pack_start (preview, false, false); + + get_vbox()->pack_start (hpacker, true, true); - hbox = manage(new Gtk::HBox); + hbox = manage(new HBox); hbox->pack_start (found_entry); hbox->pack_start (found_search_btn); - Gtk::VBox* vbox = manage(new Gtk::VBox); - vbox->pack_start (*hbox, Gtk::PACK_SHRINK); + vbox = manage(new VBox); + vbox->pack_start (*hbox, PACK_SHRINK); vbox->pack_start (found_list_view); found_list_view.append_column(_("Paths"), found_list_columns.pathname); - notebook.append_page (chooser, _("Files")); - notebook.append_page (*vbox, _("Tags")); + chooser.set_border_width (12); + + notebook.append_page (chooser, _("Browse Files")); + notebook.append_page (*vbox, _("Search Tags")); - found_list_view.get_selection()->set_mode (Gtk::SELECTION_MULTIPLE); + notebook.set_size_request (500, -1); - custom_filter.add_custom (Gtk::FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_custom)); - custom_filter.set_name (_("Probable audio files")); + found_list_view.get_selection()->set_mode (SELECTION_MULTIPLE); + found_list_view.signal_row_activated().connect (mem_fun (*this, &SoundFileBrowser::found_list_view_activated)); + + custom_filter.add_custom (FILE_FILTER_FILENAME, mem_fun(*this, &SoundFileBrowser::on_custom)); + custom_filter.set_name (_("Audio files")); matchall_filter.add_pattern ("*.*"); matchall_filter.set_name (_("All files")); @@ -316,6 +438,7 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s) chooser.add_filter (matchall_filter); chooser.set_select_multiple (true); chooser.signal_update_preview().connect(mem_fun(*this, &SoundFileBrowser::update_preview)); + chooser.signal_file_activated().connect (mem_fun (*this, &SoundFileBrowser::chooser_file_activated)); if (!persistent_folder.empty()) { chooser.set_current_folder (persistent_folder); @@ -325,10 +448,11 @@ SoundFileBrowser::SoundFileBrowser (string title, ARDOUR::Session* s) found_search_btn.signal_clicked().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked)); found_entry.signal_activate().connect(mem_fun(*this, &SoundFileBrowser::found_search_clicked)); + + add_button (Stock::CANCEL, RESPONSE_CANCEL); + add_button (Stock::APPLY, RESPONSE_APPLY); + add_button (Stock::OK, RESPONSE_OK); - show_all (); - - set_session (s); } SoundFileBrowser::~SoundFileBrowser () @@ -336,37 +460,129 @@ SoundFileBrowser::~SoundFileBrowser () persistent_folder = chooser.get_current_folder(); } + +void +SoundFileBrowser::on_show () +{ + ArdourDialog::on_show (); + start_metering (); +} + +void +SoundFileBrowser::clear_selection () +{ + chooser.unselect_all (); + found_list_view.get_selection()->unselect_all (); +} + +void +SoundFileBrowser::chooser_file_activated () +{ + preview.audition (); +} + +void +SoundFileBrowser::found_list_view_activated (const TreeModel::Path& path, TreeViewColumn* col) +{ + preview.audition (); +} + void SoundFileBrowser::set_session (Session* s) { - preview.set_session(s); + ArdourDialog::set_session (s); + preview.set_session (s); + if (s) { + add_gain_meter (); + } else { + remove_gain_meter (); + } +} + +void +SoundFileBrowser::add_gain_meter () +{ + if (gm) { + delete gm; + } + + gm = new GainMeter (session->the_auditioner(), *session); + + meter_packer.set_border_width (12); + meter_packer.pack_start (*gm, false, true); + hpacker.pack_end (meter_packer, false, false); + meter_packer.show_all (); + start_metering (); +} + +void +SoundFileBrowser::remove_gain_meter () +{ + if (gm) { + meter_packer.remove (*gm); + hpacker.remove (meter_packer); + delete gm; + gm = 0; + } +} + +void +SoundFileBrowser::start_metering () +{ + metering_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (mem_fun(*this, &SoundFileBrowser::meter)); +} + +void +SoundFileBrowser::stop_metering () +{ + metering_connection.disconnect(); +} + +void +SoundFileBrowser::meter () +{ + if (is_mapped () && session && gm) { + gm->update_meters (); + } } bool -SoundFileBrowser::on_custom (const Gtk::FileFilter::Info& filter_info) +SoundFileBrowser::on_custom (const FileFilter::Info& filter_info) { - return AudioFileSource::safe_file_extension(filter_info.filename); + return AudioFileSource::safe_file_extension (filter_info.filename); } void SoundFileBrowser::update_preview () { - preview.setup_labels(chooser.get_filename()); + preview.setup_labels (chooser.get_filename()); + + if (preview.autoplay()) { + Glib::signal_idle().connect (mem_fun (preview, &SoundFileBox::audition_oneshot)); + } } void SoundFileBrowser::found_list_view_selected () { - string file; - - Gtk::TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows (); - - if (!rows.empty()) { - Gtk::TreeIter iter = found_list->get_iter(*rows.begin()); - file = (*iter)[found_list_columns.pathname]; - chooser.set_filename (file); + if (!reset_options ()) { + set_response_sensitive (RESPONSE_OK, false); + } else { + ustring file; + + TreeView::Selection::ListHandle_Path rows = found_list_view.get_selection()->get_selected_rows (); + + if (!rows.empty()) { + TreeIter iter = found_list->get_iter(*rows.begin()); + file = (*iter)[found_list_columns.pathname]; + chooser.set_filename (file); + set_response_sensitive (RESPONSE_OK, true); + } else { + set_response_sensitive (RESPONSE_OK, false); + } + + preview.setup_labels (file); } - preview.setup_labels (file); } void @@ -376,194 +592,608 @@ SoundFileBrowser::found_search_clicked () vector<string> tags; - if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) { + if (!PBD::tokenize (tag_string, string(","), std::back_inserter (tags), true)) { warning << _("SoundFileBrowser: Could not tokenize string: ") << tag_string << endmsg; return; } - + vector<string> results; Library->search_members_and (results, tags); found_list->clear(); for (vector<string>::iterator i = results.begin(); i != results.end(); ++i) { - Gtk::TreeModel::iterator new_row = found_list->append(); - Gtk::TreeModel::Row row = *new_row; - row[found_list_columns.pathname] = *i; + TreeModel::iterator new_row = found_list->append(); + TreeModel::Row row = *new_row; + string path = Glib::filename_from_uri (string ("file:") + *i); + row[found_list_columns.pathname] = path; } } -SoundFileChooser::SoundFileChooser (string title, ARDOUR::Session* s) - : - SoundFileBrowser(title, s) +vector<ustring> +SoundFileBrowser::get_paths () { - add_button (Gtk::Stock::OPEN, Gtk::RESPONSE_OK); - add_button (Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + vector<ustring> results; - chooser.set_select_multiple (false); - found_list_view.get_selection()->set_mode (Gtk::SELECTION_SINGLE); - show_all (); + int n = notebook.get_current_page (); + + if (n == 0) { + vector<ustring> filenames = chooser.get_filenames(); + vector<ustring>::iterator i; + + for (i = filenames.begin(); i != filenames.end(); ++i) { + struct stat buf; + if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) { + results.push_back (*i); + } + } + + } else { + + typedef TreeView::Selection::ListHandle_Path ListPath; + + ListPath rows = found_list_view.get_selection()->get_selected_rows (); + for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) { + TreeIter iter = found_list->get_iter(*i); + ustring str = (*iter)[found_list_columns.pathname]; + + results.push_back (str); + } + } + + return results; } -string -SoundFileChooser::get_filename () +void +SoundFileOmega::reset_options_noret () { - Gtk::TreeModel::iterator iter; - Gtk::TreeModel::Row row; - - string filename; - switch (notebook.get_current_page()) { - case 0: - filename = chooser.get_filename(); - case 1: - iter = found_list_view.get_selection()->get_selected(); - row = *iter; - filename = row[found_list_columns.pathname]; - default: - /* NOT REACHED */ - return ""; + if (!resetting_ourselves) { + (void) reset_options (); } - - struct stat buf; - if (stat (filename.c_str(), &buf) || !S_ISREG(buf.st_mode)) { - return ""; - } - - return filename; } -vector<string> SoundFileOmega::mode_strings; - -SoundFileOmega::SoundFileOmega (string title, ARDOUR::Session* s) - : SoundFileBrowser (title, s), - split_check (_("Split Channels")) +bool +SoundFileOmega::reset_options () { - ARDOUR_UI::instance()->tooltips().set_tip(split_check, - _("Create a region for each channel")); + vector<ustring> paths = get_paths (); - Gtk::Button* btn = add_button (_("Embed"), ResponseEmbed); - ARDOUR_UI::instance()->tooltips().set_tip(*btn, - _("Link to an external file")); + if (paths.empty()) { - btn = add_button (_("Import"), ResponseImport); - ARDOUR_UI::instance()->tooltips().set_tip(*btn, - _("Copy a file to the session folder")); + channel_combo.set_sensitive (false); + action_combo.set_sensitive (false); + where_combo.set_sensitive (false); + copy_files_btn.set_sensitive (false); - add_button (Gtk::Stock::CLOSE, Gtk::RESPONSE_CLOSE); + return false; + + } else { + + channel_combo.set_sensitive (true); + action_combo.set_sensitive (true); + where_combo.set_sensitive (true); + + /* if we get through this function successfully, this may be + reset at the end, once we know if we can use hard links + to do embedding + */ + + if (Config->get_only_copy_imported_files()) { + copy_files_btn.set_sensitive (false); + } else { + copy_files_btn.set_sensitive (false); + } + } + + bool same_size; + bool src_needed; + bool selection_includes_multichannel; + bool selection_can_be_embedded_with_links = check_link_status (*session, paths); + ImportMode mode; + + if (check_info (paths, same_size, src_needed, selection_includes_multichannel)) { + Glib::signal_idle().connect (mem_fun (*this, &SoundFileOmega::bad_file_message)); + return false; + } + + ustring existing_choice; + vector<string> action_strings; + + if (selected_track_cnt > 0) { + if (channel_combo.get_active_text().length()) { + ImportDisposition id = get_channel_disposition(); + + switch (id) { + case Editing::ImportDistinctFiles: + if (selected_track_cnt == paths.size()) { + action_strings.push_back (importmode2string (ImportToTrack)); + } + break; + + case Editing::ImportDistinctChannels: + /* XXX it would be nice to allow channel-per-selected track + but its too hard we don't want to deal with all the + different per-file + per-track channel configurations. + */ + break; + + default: + action_strings.push_back (importmode2string (ImportToTrack)); + break; + } + } + } + + action_strings.push_back (importmode2string (ImportAsTrack)); + action_strings.push_back (importmode2string (ImportAsRegion)); + action_strings.push_back (importmode2string (ImportAsTapeTrack)); + + resetting_ourselves = true; + + existing_choice = action_combo.get_active_text(); + + set_popdown_strings (action_combo, action_strings); + + /* preserve any existing choice, if possible */ + + + if (existing_choice.length()) { + vector<string>::iterator x; + for (x = action_strings.begin(); x != action_strings.end(); ++x) { + if (*x == existing_choice) { + action_combo.set_active_text (existing_choice); + break; + } + } + if (x == action_strings.end()) { + action_combo.set_active_text (action_strings.front()); + } + } else { + action_combo.set_active_text (action_strings.front()); + } + + resetting_ourselves = false; + + if ((mode = get_mode()) == ImportAsRegion) { + where_combo.set_sensitive (false); + } else { + where_combo.set_sensitive (true); + } + + vector<string> channel_strings; - if (mode_strings.empty()) { - mode_strings = I18N (import_mode_strings); + if (mode == ImportAsTrack || mode == ImportAsTapeTrack || mode == ImportToTrack) { + channel_strings.push_back (_("one track per file")); + + if (selection_includes_multichannel) { + channel_strings.push_back (_("one track per channel")); + } + + if (paths.size() > 1) { + /* tape tracks are a single region per track, so we cannot + sequence multiple files. + */ + if (mode != ImportAsTapeTrack) { + channel_strings.push_back (_("sequence files")); + } + if (same_size) { + channel_strings.push_back (_("all files in one region")); + } + + } + + } else { + channel_strings.push_back (_("one region per file")); + + if (selection_includes_multichannel) { + channel_strings.push_back (_("one region per channel")); + } + + if (paths.size() > 1) { + if (same_size) { + channel_strings.push_back (_("all files in one region")); + } + } + } + + existing_choice = channel_combo.get_active_text(); + + set_popdown_strings (channel_combo, channel_strings); + + /* preserve any existing choice, if possible */ + + if (existing_choice.length()) { + vector<string>::iterator x; + for (x = channel_strings.begin(); x != channel_strings.end(); ++x) { + if (*x == existing_choice) { + channel_combo.set_active_text (existing_choice); + break; + } + } + if (x == channel_strings.end()) { + channel_combo.set_active_text (channel_strings.front()); + } + } else { + channel_combo.set_active_text (channel_strings.front()); } - Gtkmm2ext::set_popdown_strings (mode_combo, mode_strings); - set_mode (Editing::ImportAsRegion); + if (src_needed) { + src_combo.set_sensitive (true); + } else { + src_combo.set_sensitive (false); + } + + if (Config->get_only_copy_imported_files()) { - get_action_area()->pack_start (split_check); - get_action_area()->pack_start (mode_combo); + if (selection_can_be_embedded_with_links) { + copy_files_btn.set_sensitive (true); + } else { + copy_files_btn.set_sensitive (false); + } - mode_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::mode_changed)); + } else { + + copy_files_btn.set_sensitive (true); + } - show_all (); + return true; +} + + +bool +SoundFileOmega::bad_file_message() +{ + MessageDialog msg (*this, + _("One or more of the selected files\ncannot be used by Ardour"), + true, + Gtk::MESSAGE_INFO, + Gtk::BUTTONS_OK); + msg.run (); + resetting_ourselves = true; + chooser.unselect_uri (chooser.get_preview_uri()); + resetting_ourselves = false; + + return false; } bool -SoundFileOmega::get_split () +SoundFileOmega::check_info (const vector<ustring>& paths, bool& same_size, bool& src_needed, bool& multichannel) { - return split_check.get_active(); + SNDFILE* sf; + SF_INFO info; + nframes64_t sz = 0; + bool err = false; + + same_size = true; + src_needed = false; + multichannel = false; + + for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) { + + info.format = 0; // libsndfile says to clear this before sf_open(). + + if ((sf = sf_open ((char*) (*i).c_str(), SFM_READ, &info)) != 0) { + sf_close (sf); + + if (info.channels > 1) { + multichannel = true; + } + + if (sz == 0) { + sz = info.frames; + } else { + if (sz != info.frames) { + same_size = false; + } + } + + if ((nframes_t) info.samplerate != session->frame_rate()) { + src_needed = true; + } + + } else { + err = true; + } + } + + return err; } -vector<Glib::ustring> -SoundFileOmega::get_paths () + +bool +SoundFileOmega::check_link_status (const Session& s, const vector<ustring>& paths) { - vector<Glib::ustring> results; - - int n = notebook.get_current_page (); + sys::path path = s.session_directory().sound_path() / "linktest"; + string tmpdir = path.to_string(); + bool ret = false; + + if (mkdir (tmpdir.c_str(), 0744)) { + if (errno != EEXIST) { + return false; + } + } - if (n == 0) { - vector<Glib::ustring> filenames = chooser.get_filenames(); - vector<Glib::ustring>::iterator i; - for (i = filenames.begin(); i != filenames.end(); ++i) { - struct stat buf; - if ((!stat((*i).c_str(), &buf)) && S_ISREG(buf.st_mode)) { - results.push_back (*i); - } + for (vector<ustring>::const_iterator i = paths.begin(); i != paths.end(); ++i) { + + char tmpc[MAXPATHLEN+1]; + + snprintf (tmpc, sizeof(tmpc), "%s/%s", tmpdir.c_str(), Glib::path_get_basename (*i).c_str()); + + /* can we link ? */ + + if (link ((*i).c_str(), tmpc)) { + goto out; } - return results; - - } else { - typedef Gtk::TreeView::Selection::ListHandle_Path ListPath; - - ListPath rows = found_list_view.get_selection()->get_selected_rows (); - for (ListPath::iterator i = rows.begin() ; i != rows.end(); ++i) { - Gtk::TreeIter iter = found_list->get_iter(*i); - string str = (*iter)[found_list_columns.pathname]; - - results.push_back (str); - } - return results; + unlink (tmpc); } + + ret = true; + + out: + rmdir (tmpdir.c_str()); + return ret; +} + +SoundFileChooser::SoundFileChooser (Gtk::Window& parent, string title, ARDOUR::Session* s) + : SoundFileBrowser (parent, title, s, false) +{ + set_size_request (780, 300); + chooser.set_select_multiple (false); + found_list_view.get_selection()->set_mode (SELECTION_SINGLE); } void -SoundFileOmega::set_mode (Editing::ImportMode mode) +SoundFileChooser::on_hide () { - mode_combo.set_active_text (mode_strings[(int)mode]); + ArdourDialog::on_hide(); + stop_metering (); - switch (mode) { - case Editing::ImportAsRegion: - split_check.set_sensitive (true); - break; - case Editing::ImportAsTrack: - split_check.set_sensitive (true); - break; - case Editing::ImportToTrack: - split_check.set_sensitive (false); - break; - case Editing::ImportAsTapeTrack: - split_check.set_sensitive (true); - break; - } -} - -Editing::ImportMode -SoundFileOmega::get_mode () -{ - vector<string>::iterator i; - uint32_t n; - string str = mode_combo.get_active_text (); - - for (n = 0, i = mode_strings.begin (); i != mode_strings.end(); ++i, ++n) { - if (str == (*i)) { - break; - } + if (session) { + session->cancel_audition(); + } +} + +ustring +SoundFileChooser::get_filename () +{ + vector<ustring> paths; + + paths = get_paths (); + + if (paths.empty()) { + return ustring (); + } + + if (!Glib::file_test (paths.front(), Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) { + return ustring(); } - if (i == mode_strings.end()) { - fatal << string_compose (_("programming error: %1"), X_("unknown import mode string")) << endmsg; + return paths.front(); +} + +SoundFileOmega::SoundFileOmega (Gtk::Window& parent, string title, ARDOUR::Session* s, int selected_tracks, bool persistent, + Editing::ImportMode mode_hint) + : SoundFileBrowser (parent, title, s, persistent), + copy_files_btn ( _("Copy files to session")), + selected_track_cnt (selected_tracks) +{ + VBox* vbox; + HBox* hbox; + vector<string> str; + + set_size_request (-1, 450); + + block_two.set_border_width (12); + block_three.set_border_width (12); + block_four.set_border_width (12); + + options.set_spacing (12); + + str.clear (); + str.push_back (_("use file timestamp")); + str.push_back (_("at edit cursor")); + str.push_back (_("at playhead")); + str.push_back (_("at session start")); + set_popdown_strings (where_combo, str); + where_combo.set_active_text (str.front()); + + Label* l = manage (new Label); + l->set_text (_("Add files:")); + + hbox = manage (new HBox); + hbox->set_border_width (12); + hbox->set_spacing (6); + hbox->pack_start (*l, false, false); + hbox->pack_start (action_combo, false, false); + vbox = manage (new VBox); + vbox->pack_start (*hbox, false, false); + options.pack_start (*vbox, false, false); + + /* dummy entry for action combo so that it doesn't look odd if we + come up with no tracks selected. + */ + + str.clear (); + str.push_back (importmode2string (mode_hint)); + set_popdown_strings (action_combo, str); + action_combo.set_active_text (str.front()); + action_combo.set_sensitive (false); + + l = manage (new Label); + l->set_text (_("Insert:")); + + hbox = manage (new HBox); + hbox->set_border_width (12); + hbox->set_spacing (6); + hbox->pack_start (*l, false, false); + hbox->pack_start (where_combo, false, false); + vbox = manage (new VBox); + vbox->pack_start (*hbox, false, false); + options.pack_start (*vbox, false, false); + + + l = manage (new Label); + l->set_text (_("Mapping:")); + + hbox = manage (new HBox); + hbox->set_border_width (12); + hbox->set_spacing (6); + hbox->pack_start (*l, false, false); + hbox->pack_start (channel_combo, false, false); + vbox = manage (new VBox); + vbox->pack_start (*hbox, false, false); + options.pack_start (*vbox, false, false); + + str.clear (); + str.push_back (_("one track per file")); + set_popdown_strings (channel_combo, str); + channel_combo.set_active_text (str.front()); + channel_combo.set_sensitive (false); + + l = manage (new Label); + l->set_text (_("Conversion Quality:")); + + hbox = manage (new HBox); + hbox->set_border_width (12); + hbox->set_spacing (6); + hbox->pack_start (*l, false, false); + hbox->pack_start (src_combo, false, false); + vbox = manage (new VBox); + vbox->pack_start (*hbox, false, false); + options.pack_start (*vbox, false, false); + + str.clear (); + str.push_back (_("Best")); + str.push_back (_("Good")); + str.push_back (_("Quick")); + str.push_back (_("Fast")); + str.push_back (_("Fastest")); + + set_popdown_strings (src_combo, str); + src_combo.set_active_text (str.front()); + src_combo.set_sensitive (false); + + reset_options (); + + action_combo.signal_changed().connect (mem_fun (*this, &SoundFileOmega::reset_options_noret)); + + copy_files_btn.set_active (true); + + block_four.pack_start (copy_files_btn, false, false); + + options.pack_start (block_four, false, false); + + get_vbox()->pack_start (options, false, false); + + /* setup disposition map */ + + disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per file"), ImportDistinctFiles)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("one track per channel"), ImportDistinctChannels)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("merge files"), ImportMergeFiles)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("sequence files"), ImportSerializeFiles)); + + disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per file"), ImportDistinctFiles)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("one region per channel"), ImportDistinctChannels)); + disposition_map.insert (pair<ustring,ImportDisposition>(_("all files in one region"), ImportMergeFiles)); + + chooser.signal_selection_changed().connect (mem_fun (*this, &SoundFileOmega::file_selection_changed)); +} + +void +SoundFileOmega::set_mode (ImportMode mode) +{ + action_combo.set_active_text (importmode2string (mode)); +} + +ImportMode +SoundFileOmega::get_mode () const +{ + return string2importmode (action_combo.get_active_text()); +} + +void +SoundFileOmega::on_hide () +{ + ArdourDialog::on_hide(); + if (session) { + session->cancel_audition(); + } +} + +ImportPosition +SoundFileOmega::get_position() const +{ + ustring str = where_combo.get_active_text(); + + if (str == _("use file timestamp")) { + return ImportAtTimestamp; + } else if (str == _("at edit cursor")) { + return ImportAtEditCursor; + } else if (str == _("at playhead")) { + return ImportAtPlayhead; + } else { + return ImportAtStart; + } +} + +SrcQuality +SoundFileOmega::get_src_quality() const +{ + ustring str = where_combo.get_active_text(); + + if (str == _("Best")) { + return SrcBest; + } else if (str == _("Good")) { + return SrcGood; + } else if (str == _("Quick")) { + return SrcQuick; + } else if (str == _("Fast")) { + return SrcFast; + } else { + return SrcFastest; + } +} + +ImportDisposition +SoundFileOmega::get_channel_disposition () const +{ + /* we use a map here because the channel combo can contain different strings + depending on the state of the other combos. the map contains all possible strings + and the ImportDisposition enum that corresponds to it. + */ + + ustring str = channel_combo.get_active_text(); + DispositionMap::const_iterator x = disposition_map.find (str); + + if (x == disposition_map.end()) { + fatal << string_compose (_("programming error: %1 (%2)"), "unknown string for import disposition", str) << endmsg; /*NOTREACHED*/ } - return (Editing::ImportMode) (n); + return x->second; } void -SoundFileOmega::mode_changed () +SoundFileOmega::reset (int selected_tracks) { - Editing::ImportMode mode = get_mode(); + selected_track_cnt = selected_tracks; + reset_options (); +} - switch (mode) { - case Editing::ImportAsRegion: - split_check.set_sensitive (true); - break; - case Editing::ImportAsTrack: - split_check.set_sensitive (true); - break; - case Editing::ImportToTrack: - split_check.set_sensitive (false); - break; - case Editing::ImportAsTapeTrack: - split_check.set_sensitive (true); - break; +void +SoundFileOmega::file_selection_changed () +{ + if (resetting_ourselves) { + return; + } + + if (!reset_options ()) { + set_response_sensitive (RESPONSE_OK, false); + } else { + if (chooser.get_filenames().size() > 0) { + set_response_sensitive (RESPONSE_OK, true); + } else { + set_response_sensitive (RESPONSE_OK, false); + } } } diff --git a/gtk2_ardour/sfdb_ui.h b/gtk2_ardour/sfdb_ui.h index 34c3f558bb..43f76a9ea2 100644 --- a/gtk2_ardour/sfdb_ui.h +++ b/gtk2_ardour/sfdb_ui.h @@ -22,6 +22,8 @@ #include <string> #include <vector> +#include <map> +#include <glibmm/ustring.h> #include <sigc++/signal.h> @@ -41,32 +43,50 @@ #include "ardour_dialog.h" #include "editing.h" +namespace ARDOUR { + class Session; +}; + +class GainMeter; + class SoundFileBox : public Gtk::VBox { public: - SoundFileBox (); + SoundFileBox (bool persistent); virtual ~SoundFileBox () {}; void set_session (ARDOUR::Session* s); - bool setup_labels (std::string filename); + bool setup_labels (const Glib::ustring& filename); + + void audition(); + bool audition_oneshot(); + bool autoplay () const; protected: ARDOUR::Session* _session; - std::string path; + Glib::ustring path; ARDOUR::SoundFileInfo sf_info; - pid_t current_pid; + Gtk::Table table; Gtk::Label length; Gtk::Label format; Gtk::Label channels; Gtk::Label samplerate; Gtk::Label timecode; + + Gtk::Label channels_value; + Gtk::Label samplerate_value; + Gtk::Label format_text; + AudioClock length_clock; + AudioClock timecode_clock; + Gtk::Frame border_frame; - - Gtk::Entry tags_entry; + Gtk::Label preview_label; + + Gtk::TextView tags_entry; Gtk::VBox main_box; Gtk::VBox path_box; @@ -74,91 +94,137 @@ class SoundFileBox : public Gtk::VBox Gtk::Button play_btn; Gtk::Button stop_btn; + Gtk::CheckButton autoplay_btn; Gtk::Button apply_btn; - + bool tags_entry_left (GdkEventFocus* event); - void play_btn_clicked (); - void stop_btn_clicked (); - void apply_btn_clicked (); - - void audition_status_changed (bool state); + void tags_changed (); + void save_tags (const std::vector<std::string>&); + void stop_audition (); }; class SoundFileBrowser : public ArdourDialog { + private: + class FoundTagColumns : public Gtk::TreeModel::ColumnRecord + { + public: + Gtk::TreeModelColumn<Glib::ustring> pathname; + + FoundTagColumns() { add(pathname); } + }; + + FoundTagColumns found_list_columns; + Glib::RefPtr<Gtk::ListStore> found_list; + public: - SoundFileBrowser (std::string title, ARDOUR::Session* _s = 0); + SoundFileBrowser (Gtk::Window& parent, std::string title, ARDOUR::Session* _s, bool persistent); virtual ~SoundFileBrowser (); virtual void set_session (ARDOUR::Session*); + std::vector<Glib::ustring> get_paths (); + + void clear_selection (); - protected: Gtk::FileChooserWidget chooser; + Gtk::TreeView found_list_view; + + protected: + bool resetting_ourselves; + Gtk::FileFilter custom_filter; Gtk::FileFilter matchall_filter; SoundFileBox preview; + Gtk::HBox hpacker; static Glib::ustring persistent_folder; - class FoundTagColumns : public Gtk::TreeModel::ColumnRecord - { - public: - Gtk::TreeModelColumn<string> pathname; - - FoundTagColumns() { add(pathname); } - }; - - FoundTagColumns found_list_columns; - Glib::RefPtr<Gtk::ListStore> found_list; - Gtk::TreeView found_list_view; Gtk::Entry found_entry; Gtk::Button found_search_btn; - Gtk::Notebook notebook; - + + GainMeter* gm; + Gtk::VBox meter_packer; + void add_gain_meter (); + void remove_gain_meter (); + void meter (); + void start_metering (); + void stop_metering (); + sigc::connection metering_connection; + void update_preview (); void found_list_view_selected (); + void found_list_view_activated (const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn*); void found_search_clicked (); + + void chooser_file_activated (); bool on_custom (const Gtk::FileFilter::Info& filter_info); + + virtual bool reset_options() { return true; } + + protected: + void on_show(); + }; class SoundFileChooser : public SoundFileBrowser { public: - SoundFileChooser (std::string title, ARDOUR::Session* _s = 0); + SoundFileChooser (Gtk::Window& parent, std::string title, ARDOUR::Session* _s = 0); virtual ~SoundFileChooser () {}; - std::string get_filename (); + Glib::ustring get_filename (); + + protected: + void on_hide(); }; class SoundFileOmega : public SoundFileBrowser { + public: - SoundFileOmega (std::string title, ARDOUR::Session* _s); - virtual ~SoundFileOmega () {}; - - /* these are returned by the Dialog::run() method. note - that builtin GTK responses are all negative, leaving - positive values for application-defined responses. - */ - - const static int ResponseImport = 1; - const static int ResponseEmbed = 2; + SoundFileOmega (Gtk::Window& parent, std::string title, ARDOUR::Session* _s, int selected_tracks, bool persistent, + Editing::ImportMode mode_hint = Editing::ImportAsTrack); - std::vector<Glib::ustring> get_paths (); - bool get_split (); + void reset (int selected_tracks); + Gtk::ComboBoxText action_combo; + Gtk::ComboBoxText where_combo; + Gtk::ComboBoxText channel_combo; + Gtk::ComboBoxText src_combo; + + Gtk::CheckButton copy_files_btn; + void set_mode (Editing::ImportMode); - Editing::ImportMode get_mode (); + Editing::ImportMode get_mode() const; + Editing::ImportPosition get_position() const; + Editing::ImportDisposition get_channel_disposition() const; + ARDOUR::SrcQuality get_src_quality() const; protected: - Gtk::CheckButton split_check; - Gtk::ComboBoxText mode_combo; - - void mode_changed (); - - static std::vector<std::string> mode_strings; + void on_hide(); + + private: + uint32_t selected_track_cnt; + + typedef std::map<Glib::ustring,Editing::ImportDisposition> DispositionMap; + DispositionMap disposition_map; + + Gtk::HBox options; + Gtk::VBox block_two; + Gtk::VBox block_three; + Gtk::VBox block_four; + + bool check_info (const std::vector<Glib::ustring>& paths, + bool& same_size, bool& src_needed, bool& multichannel); + + static bool check_link_status (const ARDOUR::Session&, const std::vector<Glib::ustring>& paths); + + void file_selection_changed (); + bool reset_options (); + void reset_options_noret (); + bool bad_file_message (); }; #endif // __ardour_sfdb_ui_h__ diff --git a/gtk2_ardour/streamview.cc b/gtk2_ardour/streamview.cc index 560dcd503f..1a3fb9a084 100644 --- a/gtk2_ardour/streamview.cc +++ b/gtk2_ardour/streamview.cc @@ -153,6 +153,8 @@ StreamView::set_samples_per_unit (gdouble spp) void StreamView::add_region_view (boost::shared_ptr<Region> r) { + // ENSURE_GUI_THREAD (bind (mem_fun (*this, &AudioStreamView::add_region_view), r)); + add_region_view_internal (r, true); } diff --git a/gtk2_ardour/utils.cc b/gtk2_ardour/utils.cc index 83a95fb71f..570a481c11 100644 --- a/gtk2_ardour/utils.cc +++ b/gtk2_ardour/utils.cc @@ -337,6 +337,12 @@ set_color (Gdk::Color& c, int rgb) c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256); } +#ifdef GTKOSX +extern "C" { + gboolean gdk_quartz_possibly_forward (GdkEvent*); +} +#endif + bool key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) { @@ -348,7 +354,6 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) #ifdef DEBUG_ACCELERATOR_HANDLING bool debug = (getenv ("ARDOUR_DEBUG_ACCELERATOR_HANDLING") != 0); #endif - if (focus) { if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) { special_handling_of_unmodified_accelerators = true; @@ -444,6 +449,11 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) cerr << "\tactivate, then propagate\n"; } #endif +#ifdef GTKOSX + if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) { + return true; + } +#endif if (!gtk_window_activate_key (win, ev)) { return gtk_window_propagate_key_event (win, ev); } else { @@ -469,6 +479,11 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) cerr << "\tpropagation didn't handle, so activate\n"; } #endif +#ifdef GTKOSX + if (gdk_quartz_possibly_forward ((GdkEvent*) ev)) { + return true; + } +#endif return gtk_window_activate_key (win, ev); } else { #ifdef DEBUG_ACCELERATOR_HANDLING @@ -490,23 +505,18 @@ key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev) Glib::RefPtr<Gdk::Pixbuf> get_xpm (std::string name) { - if (!xpm_map[name]) { - - SearchPath spath(ARDOUR::ardour_search_path()); - spath += ARDOUR::system_data_search_path(); - - spath.add_subdirectory_to_paths("pixmaps"); + SearchPath spath(ARDOUR::ardour_search_path()); + spath += ARDOUR::system_data_search_path(); - sys::path data_file_path; + spath.add_subdirectory_to_paths("pixmaps"); - if(!find_file_in_search_path (spath, name, data_file_path)) { - fatal << string_compose (_("cannot find pixmap %1"), name) << endmsg; - } + sys::path data_file_path; - xpm_map[name] = Gdk::Pixbuf::create_from_file (data_file_path.to_string()); + if(!find_file_in_search_path (spath, name, data_file_path)) { + fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg; } - - return (xpm_map[name]); + + return Gdk::Pixbuf::create_from_file (data_file_path.to_string()); } Glib::RefPtr<Gdk::Pixbuf> diff --git a/libs/ardour/SConscript b/libs/ardour/SConscript index 3892af6e9b..465138d90d 100644 --- a/libs/ardour/SConscript +++ b/libs/ardour/SConscript @@ -12,7 +12,7 @@ ardour = env.Copy() # this defines the version number of libardour # -domain = 'libardour' +domain = 'libardour2' ardour.Append(DOMAIN = domain, MAJOR = 2, MINOR = 0, MICRO = 0) ardour.Append(CXXFLAGS = "-DPACKAGE=\\\"" + domain + "\\\"") @@ -101,6 +101,7 @@ recent_sessions.cc region.cc region_factory.cc reverse.cc +resampled_source.cc quantize.cc route.cc route_group.cc diff --git a/libs/ardour/ardour/ardour.h b/libs/ardour/ardour/ardour.h index 1b9725a04c..6e7b494441 100644 --- a/libs/ardour/ardour/ardour.h +++ b/libs/ardour/ardour/ardour.h @@ -47,8 +47,6 @@ namespace ARDOUR { int init (bool with_vst, bool try_optimization); int cleanup (); - int setup_midi(AudioEngine& engine); - std::string get_ardour_revision (); microseconds_t get_microseconds (); diff --git a/libs/ardour/ardour/audiofilesource.h b/libs/ardour/ardour/audiofilesource.h index 78d10f9d64..4d80c8ddf5 100644 --- a/libs/ardour/ardour/audiofilesource.h +++ b/libs/ardour/ardour/audiofilesource.h @@ -61,7 +61,8 @@ class AudioFileSource : public AudioSource { Glib::ustring path() const { return _path; } Glib::ustring peak_path (Glib::ustring audio_path); - Glib::ustring old_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; } @@ -122,7 +123,7 @@ class AudioFileSource : public AudioSource { to cause issues. */ - virtual void handle_header_position_change (); + virtual void handle_header_position_change () {} protected: @@ -166,6 +167,10 @@ class AudioFileSource : public AudioSource { bool find (Glib::ustring& path, bool must_exist, bool& is_new, uint16_t& chan); bool removable() const; bool writable() const { return _flags & Writable; } + + private: + Glib::ustring old_peak_path (Glib::ustring audio_path); + Glib::ustring broken_peak_path (Glib::ustring audio_path); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/audiosource.h b/libs/ardour/ardour/audiosource.h index 812c30e8c2..7b22528bd1 100644 --- a/libs/ardour/ardour/audiosource.h +++ b/libs/ardour/ardour/audiosource.h @@ -40,7 +40,6 @@ using std::list; using std::vector; -using Glib::ustring; namespace ARDOUR { @@ -49,10 +48,23 @@ const nframes_t frames_per_peak = 256; class AudioSource : public Source, public boost::enable_shared_from_this<ARDOUR::AudioSource> { public: - AudioSource (Session&, ustring name); + AudioSource (Session&, Glib::ustring name); AudioSource (Session&, const XMLNode&); virtual ~AudioSource (); + + /* one could argue that this should belong to Source, but other data types + generally do not come with a model of "offset along an audio timeline" + so its here in AudioSource for now. + */ + + virtual nframes_t natural_position() const { return 0; } + /* returns the number of items in this `audio_source' */ + + virtual nframes_t length() const { + return _length; + } + virtual nframes_t available_peaks (double zoom) const; virtual nframes_t read (Sample *dst, nframes_t start, nframes_t cnt) const; @@ -65,8 +77,8 @@ const nframes_t frames_per_peak = 256; virtual bool can_truncate_peaks() const { return true; } - void set_captured_for (ustring str) { _captured_for = str; } - ustring captured_for() const { return _captured_for; } + 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; } @@ -81,7 +93,7 @@ const nframes_t frames_per_peak = 256; XMLNode& get_state (); int set_state (const XMLNode&); - int rename_peakfile (ustring newpath); + int rename_peakfile (Glib::ustring newpath); void touch_peakfile (); static void set_build_missing_peakfiles (bool yn) { @@ -92,35 +104,43 @@ const nframes_t frames_per_peak = 256; _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 (); + 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; - ustring peakpath; - ustring _captured_for; + bool _peaks_built; + mutable Glib::Mutex _lock; + mutable Glib::Mutex _peaks_ready_lock; + nframes_t _length; + 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, ustring path); + 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); + 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 ustring peak_path(ustring audio_path) = 0; - virtual ustring old_peak_path(ustring audio_path) = 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); + private: int peakfile; nframes_t peak_leftover_cnt; @@ -128,7 +148,7 @@ const nframes_t frames_per_peak = 256; Sample* peak_leftovers; nframes_t peak_leftover_frame; - bool file_changed (ustring path); + bool file_changed (Glib::ustring path); }; } diff --git a/libs/ardour/ardour/automatable.h b/libs/ardour/ardour/automatable.h index fe47614a1f..f96ecc0bd1 100644 --- a/libs/ardour/ardour/automatable.h +++ b/libs/ardour/ardour/automatable.h @@ -83,6 +83,14 @@ public: 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); @@ -102,6 +110,7 @@ protected: std::set<Parameter> _can_automate_list; nframes_t _last_automation_snapshot; + static nframes_t _automation_interval; }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/configuration.h b/libs/ardour/ardour/configuration.h index 70b7e166c1..7b890500d8 100644 --- a/libs/ardour/ardour/configuration.h +++ b/libs/ardour/ardour/configuration.h @@ -42,17 +42,7 @@ class Configuration : public PBD::Stateful Configuration(); virtual ~Configuration(); - struct MidiPortDescriptor { - std::string tag; - std::string device; - std::string type; - std::string mode; - - MidiPortDescriptor (const XMLNode&); - XMLNode& get_state(); - }; - - std::map<std::string,MidiPortDescriptor *> midi_ports; + std::map<std::string,XMLNode> midi_ports; void map_parameters (sigc::slot<void,const char*> theSlot); diff --git a/libs/ardour/ardour/configuration_vars.h b/libs/ardour/ardour/configuration_vars.h index 4d5579a9a0..b592a9f721 100644 --- a/libs/ardour/ardour/configuration_vars.h +++ b/libs/ardour/ardour/configuration_vars.h @@ -138,11 +138,15 @@ CONFIG_VARIABLE (bool, verify_remove_last_capture, "verify-remove-last-capture", 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 (uint32_t, saved_history_depth, "save-history-depth", 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 (string, possible_audio_file_regexp, "possible-audio-file-regexp", "\\.(wav|aiff|caf|w64|L|R)$") +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) /* denormal management */ diff --git a/libs/ardour/ardour/io.h b/libs/ardour/ardour/io.h index 6c040be63e..6e68c01d8c 100644 --- a/libs/ardour/ardour/io.h +++ b/libs/ardour/ardour/io.h @@ -219,7 +219,7 @@ class IO : public Automatable, public Latent public: /* automation */ - + struct GainControl : public AutomationControl { GainControl (std::string name, IO& i, boost::shared_ptr<AutomationList> al) : AutomationControl (i._session, al, name) diff --git a/libs/ardour/ardour/profile.h b/libs/ardour/ardour/profile.h index 3347447915..b016063c4d 100644 --- a/libs/ardour/ardour/profile.h +++ b/libs/ardour/ardour/profile.h @@ -29,6 +29,8 @@ class RuntimeProfile { public: enum Element { SmallScreen, + SAE, + SinglePackage, LastElement }; @@ -38,6 +40,12 @@ class 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; diff --git a/libs/ardour/ardour/resampled_source.h b/libs/ardour/ardour/resampled_source.h new file mode 100644 index 0000000000..9a88ca9644 --- /dev/null +++ b/libs/ardour/ardour/resampled_source.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_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 (SNDFILE* sf, SF_INFO* info, nframes_t rate, SrcQuality); + ~ResampledImportableSource (); + + nframes_t read (Sample* buffer, nframes_t nframes); + + float ratio() const { return src_data.src_ratio; } + + static const uint32_t blocksize; + + private: + float* input; + SRC_STATE* src_state; + SRC_DATA src_data; +}; + +} + +#endif /* __ardour_resampled_source_h__ */ diff --git a/libs/ardour/ardour/route.h b/libs/ardour/ardour/route.h index 1fd6eff0f8..fc17af06ee 100644 --- a/libs/ardour/ardour/route.h +++ b/libs/ardour/ardour/route.h @@ -258,6 +258,9 @@ class Route : public IO uint32_t remote_control_id () const; sigc::signal<void> RemoteControlIDChanged; + void sync_order_keys (); + static sigc::signal<void> SyncOrderKeys; + protected: friend class Session; diff --git a/libs/ardour/ardour/session.h b/libs/ardour/ardour/session.h index 94caf8a242..bbcae6e91d 100644 --- a/libs/ardour/ardour/session.h +++ b/libs/ardour/ardour/session.h @@ -261,6 +261,8 @@ class Session : public PBD::StatefulDestructible std::string automation_dir () const; + 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); @@ -422,6 +424,7 @@ class Session : public PBD::StatefulDestructible 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; @@ -568,13 +571,14 @@ class Session : public PBD::StatefulDestructible string doing_what; /* control info */ - bool multichan; bool sample_convert; + SrcQuality quality; volatile bool freeze; std::vector<Glib::ustring> paths; /* result */ - std::vector<boost::shared_ptr<Region> > new_regions; + SourceList sources; + }; int import_audiofile (import_status&); @@ -650,8 +654,6 @@ class Session : public PBD::StatefulDestructible void add_curve(Curve*); void add_automation_list(AutomationList*); - nframes_t automation_interval () const { return _automation_interval; } - /* fade curves */ float get_default_fade_length () const { return default_fade_msecs; } @@ -1650,8 +1652,6 @@ class Session : public PBD::StatefulDestructible void allocate_pan_automation_buffers (nframes_t nframes, uint32_t howmany, bool force); uint32_t _npan_buffers; - nframes_t _automation_interval; - /* VST support */ long _vst_callback (VSTPlugin*, @@ -1668,7 +1668,6 @@ class Session : public PBD::StatefulDestructible uint32_t n_physical_outputs; uint32_t n_physical_inputs; - void remove_pending_capture_state (); 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); @@ -1688,6 +1687,8 @@ class Session : public PBD::StatefulDestructible XMLNode& get_control_protocol_state (); + void set_history_depth (uint32_t depth); + void sync_order_keys (); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/sndfilesource.h b/libs/ardour/ardour/sndfilesource.h index 916e9da49e..4fd71a4c96 100644 --- a/libs/ardour/ardour/sndfilesource.h +++ b/libs/ardour/ardour/sndfilesource.h @@ -76,9 +76,6 @@ class SndFileSource : public AudioFileSource { SF_INFO _info; SF_BROADCAST_INFO *_broadcast_info; - mutable float *interleave_buf; - mutable nframes_t interleave_bufsize; - void init (); int open(); void close(); @@ -105,6 +102,7 @@ class SndFileSource : public AudioFileSource { void handle_header_position_change (); static int64_t get_timecode_info (SNDFILE* sf, SF_BROADCAST_INFO* binfo, bool& exists); + static Sample* get_interleave_buffer (nframes_t size); }; } // namespace ARDOUR diff --git a/libs/ardour/ardour/source_factory.h b/libs/ardour/ardour/source_factory.h index fb591216bf..01f50126a4 100644 --- a/libs/ardour/ardour/source_factory.h +++ b/libs/ardour/ardour/source_factory.h @@ -36,16 +36,23 @@ 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); + 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); - static boost::shared_ptr<Source> createWritable (DataType type, Session&, std::string name, bool destructive, nframes_t rate, bool announce = true); + 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 = true); + + static Glib::Cond* PeaksToBuild; + static Glib::StaticMutex peak_building_lock; + static std::list<boost::weak_ptr<AudioSource> > files_with_peaks; - private: - static int setup_peakfile (boost::shared_ptr<Source>); + static int setup_peakfile (boost::shared_ptr<Source>, bool async); }; } diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index e13bd09d83..5b50713313 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -370,6 +370,15 @@ namespace ARDOUR { }; typedef std::vector<boost::shared_ptr<Source> > SourceList; + + enum SrcQuality { + SrcBest, + SrcGood, + SrcQuick, + SrcFast, + SrcFastest + }; + } // namespace ARDOUR std::istream& operator>>(std::istream& o, ARDOUR::SampleFormat& sf); diff --git a/libs/ardour/ardour/utils.h b/libs/ardour/ardour/utils.h index cde17d8b49..e52274eb1f 100644 --- a/libs/ardour/ardour/utils.h +++ b/libs/ardour/ardour/utils.h @@ -50,7 +50,7 @@ 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); +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); diff --git a/libs/ardour/audio_playlist.cc b/libs/ardour/audio_playlist.cc index d28d88488e..d5c6120946 100644 --- a/libs/ardour/audio_playlist.cc +++ b/libs/ardour/audio_playlist.cc @@ -379,7 +379,9 @@ AudioPlaylist::check_dependents (boost::shared_ptr<Region> r, bool norefresh) bottom = region; } - + if (!top->opaque()) { + continue; + } OverlapType c = top->coverage (bottom->position(), bottom->last_frame()); diff --git a/libs/ardour/audioengine.cc b/libs/ardour/audioengine.cc index a4ce5f291d..2a4b36f7d8 100644 --- a/libs/ardour/audioengine.cc +++ b/libs/ardour/audioengine.cc @@ -156,7 +156,7 @@ AudioEngine::start () _has_run = true; Running(); /* EMIT SIGNAL */ } else { - error << _("cannot activate JACK client") << endmsg; + // error << _("cannot activate JACK client") << endmsg; } } @@ -1078,7 +1078,7 @@ AudioEngine::connect_to_jack (string client_name) error << _("Unable to connect to JACK server") << endmsg; } - error << string_compose (_("Could not connect to JACK server as \"%1\""), jack_client_name) << endmsg; + // error message is not useful here return -1; } diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 1044dd10d2..1284dd343b 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -21,10 +21,13 @@ #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> @@ -40,6 +43,7 @@ #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> @@ -153,7 +157,60 @@ AudioFileSource::init (ustring pathstr, bool must_exist) ustring AudioFileSource::peak_path (ustring audio_path) { - return _session.peak_path_from_audio_path (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 @@ -171,9 +228,9 @@ AudioFileSource::old_peak_path (ustring audio_path) char buf[32]; #ifdef __APPLE__ - snprintf (buf, sizeof (buf), "%u-%u-%d", stat_mount.st_ino, stat_file.st_ino, _channel); + 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", stat_mount.st_ino, stat_file.st_ino, _channel); + snprintf (buf, sizeof (buf), "%ld-%ld-%d.peak", stat_mount.st_ino, stat_file.st_ino, _channel); #endif ustring res = peak_dir; @@ -227,7 +284,7 @@ AudioFileSource::set_state (const XMLNode& node) } if ((prop = node.property (X_("channel"))) != 0) { - _channel = atoi (prop->value().c_str()); + _channel = atoi (prop->value()); } else { _channel = 0; } @@ -265,6 +322,10 @@ 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); @@ -432,7 +493,7 @@ AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew, uint16_t& fullpath += shorter; if (Glib::file_test (pathstr, Glib::FILE_TEST_EXISTS|Glib::FILE_TEST_IS_REGULAR)) { - chan = atoi (pathstr.substr (pos+1).c_str()); + chan = atoi (pathstr.substr (pos+1)); pathstr = shorter; keeppath = fullpath; ++cnt; @@ -484,7 +545,7 @@ AudioFileSource::find (ustring& pathstr, bool must_exist, bool& isnew, uint16_t& 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).c_str()); + chan = atoi (pathstr.substr (pos+1)); pathstr = shorter; } } @@ -542,15 +603,6 @@ AudioFileSource::set_header_position_offset (nframes_t offset) HeaderPositionOffsetChanged (); } -void -AudioFileSource::handle_header_position_change () -{ - if (writable()) { - set_header_timeline_position (); - flush_header (); - } -} - void AudioFileSource::set_timeline_position (int64_t pos) { @@ -603,15 +655,15 @@ AudioFileSource::set_source_name (ustring newname, bool destructive) bool AudioFileSource::is_empty (Session& s, ustring path) { - bool ret = false; + SoundFileInfo info; + string err; - boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createReadable (DataType::AUDIO, s, path, 0, NoPeakFile, false)); - - if (afs) { - ret = (afs->length() == 0); + if (!get_soundfile_info (path, info, err)) { + /* dangerous: we can't get info, so assume that its not empty */ + return false; } - return ret; + return info.length == 0; } int diff --git a/libs/ardour/audiosource.cc b/libs/ardour/audiosource.cc index d09a9b29cd..a2ce7209f6 100644 --- a/libs/ardour/audiosource.cc +++ b/libs/ardour/audiosource.cc @@ -20,6 +20,7 @@ #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> +#include <poll.h> #include <float.h> #include <utime.h> #include <cerrno> @@ -29,18 +30,21 @@ #include <algorithm> #include <vector> +#include <glibmm/fileutils.h> + #include <pbd/xml++.h> #include <pbd/pthread_utils.h> #include <ardour/audiosource.h> #include <ardour/cycle_timer.h> -#include <ardour/runtime_functions.h> +#include <ardour/session.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; @@ -126,12 +130,12 @@ bool AudioSource::peaks_ready (sigc::slot<void> the_slot, sigc::connection& conn) const { bool ret; - Glib::Mutex::Lock lm (_lock); + 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); } @@ -182,15 +186,10 @@ AudioSource::initialize_peakfile (bool newfile, ustring audio_path) peakpath = peak_path (audio_path); - /* Nasty band-aid for older sessions that were created before we - used libsndfile for all audio files. - */ + /* if the peak file should be there, but isn't .... */ - if (!newfile && access (peakpath.c_str(), R_OK) != 0) { - ustring str = old_peak_path (audio_path); - if (access (str.c_str(), R_OK) == 0) { - peakpath = str; - } + if (!newfile && !Glib::file_test (peakpath.c_str(), Glib::FILE_TEST_EXISTS)) { + peakpath = find_broken_peakfile (peakpath, audio_path); } if (newfile) { @@ -332,7 +331,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr /* open, read, close */ if ((_peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) { - error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; + error << string_compose(_("AudioSource: cannot open peakpath (a) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; return -1; } @@ -406,7 +405,7 @@ AudioSource::read_peaks (PeakData *peaks, nframes_t npeaks, nframes_t start, nfr /* open ... close during out: handling */ if ((_peakfile = ::open (peakpath.c_str(), O_RDONLY, 0664)) < 0) { - error << string_compose(_("AudioSource: cannot open peakpath \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; + error << string_compose(_("AudioSource: cannot open peakpath (b) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; return 0; } @@ -581,9 +580,11 @@ AudioSource::build_peaks_from_scratch () { nframes_t current_frame; nframes_t cnt; - Sample buf[frames_per_peak]; + 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; { @@ -598,39 +599,41 @@ AudioSource::build_peaks_from_scratch () current_frame = 0; cnt = _length; _peaks_built = false; + buf = new Sample[bufsize]; while (cnt) { - frames_to_read = min (frames_per_peak, 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 (); + done_with_peakfile_writes (false); goto out; } - if (compute_and_write_peaks (buf, current_frame, frames_read, true)) { + if (compute_and_write_peaks (buf, current_frame, frames_read, true, false)) { break; } current_frame += frames_read; cnt -= frames_read; } - + if (cnt == 0) { /* success */ truncate_peakfile(); - _peaks_built = true; } - done_with_peakfile_writes (); + done_with_peakfile_writes ((cnt == 0)); } - - /* lock no longer held, safe to signal */ - - if (_peaks_built) { - PeaksReady (); /* EMIT SIGNAL */ - ret = 0; + + { + Glib::Mutex::Lock lm (_peaks_ready_lock); + + if (_peaks_built) { + PeaksReady (); /* EMIT SIGNAL */ + ret = 0; + } } out: @@ -638,6 +641,10 @@ AudioSource::build_peaks_from_scratch () unlink (peakpath.c_str()); } + if (buf) { + delete [] buf; + } + return ret; } @@ -645,17 +652,21 @@ 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 \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; + error << string_compose(_("AudioSource: cannot open peakpath (c) \"%1\" (%2)"), peakpath, strerror (errno)) << endmsg; return -1; } return 0; } void -AudioSource::done_with_peakfile_writes () +AudioSource::done_with_peakfile_writes (bool done) { if (peak_leftover_cnt) { - compute_and_write_peaks (0, 0, 0, true); + compute_and_write_peaks (0, 0, 0, true, false); + } + + if (done) { + _peaks_built = true; } if (peakfile >= 0) { @@ -665,7 +676,7 @@ AudioSource::done_with_peakfile_writes () } int -AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force) +AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframes_t cnt, bool force, bool intermediate_peaks_ready) { Sample* buf2 = 0; nframes_t to_do; @@ -695,7 +706,8 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe x.min = peak_leftovers[0]; x.max = peak_leftovers[0]; - find_peaks (peak_leftovers + 1, peak_leftover_cnt - 1, &x.min, &x.max); + + ARDOUR::find_peaks (peak_leftovers + 1, peak_leftover_cnt - 1, &x.min, &x.max); off_t byte = (peak_leftover_frame / frames_per_peak) * sizeof (PeakData); @@ -706,8 +718,13 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe _peak_byte_max = max (_peak_byte_max, (off_t) (byte + sizeof(PeakData))); - PeakRangeReady (peak_leftover_frame, peak_leftover_cnt); /* EMIT SIGNAL */ - PeaksReady (); /* EMIT SIGNAL */ + { + 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 */ @@ -778,7 +795,7 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe peakbuf[peaks_computed].max = buf[0]; peakbuf[peaks_computed].min = buf[0]; - find_peaks (buf+1, this_time-1, &peakbuf[peaks_computed].min, &peakbuf[peaks_computed].max); + ARDOUR::find_peaks (buf+1, this_time-1, &peakbuf[peaks_computed].min, &peakbuf[peaks_computed].max); peaks_computed++; buf += this_time; @@ -814,8 +831,11 @@ AudioSource::compute_and_write_peaks (Sample* buf, nframes_t first_frame, nframe _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 */ - PeaksReady (); /* EMIT SIGNAL */ + if (intermediate_peaks_ready) { + PeaksReady (); /* EMIT SIGNAL */ + } } ret = 0; @@ -882,3 +902,11 @@ AudioSource::available_peaks (double zoom_factor) const return (end/sizeof(PeakData)) * frames_per_peak; } +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 index e344d5f2a6..c509997b2e 100644 --- a/libs/ardour/auditioner.cc +++ b/libs/ardour/auditioner.cc @@ -137,7 +137,7 @@ Auditioner::audition_region (boost::shared_ptr<Region> region) boost::shared_ptr<AudioRegion> the_region (boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (region))); the_region->set_position (0, this); - _diskstream->playlist()->clear (); + _diskstream->playlist()->drop_regions (); _diskstream->playlist()->add_region (the_region, 0, 1); if (_diskstream->n_channels().n_audio() < the_region->n_channels()) { diff --git a/libs/ardour/automatable.cc b/libs/ardour/automatable.cc index 0609b8d380..45b19d1997 100644 --- a/libs/ardour/automatable.cc +++ b/libs/ardour/automatable.cc @@ -34,6 +34,8 @@ 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) @@ -422,7 +424,7 @@ Automatable::protect_automation () void Automatable::automation_snapshot (nframes_t now) { - if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _session.automation_interval()) { + if (_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()) { diff --git a/libs/ardour/configuration.cc b/libs/ardour/configuration.cc index 5faab7c0ab..1460491180 100644 --- a/libs/ardour/configuration.cc +++ b/libs/ardour/configuration.cc @@ -25,6 +25,9 @@ #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> @@ -195,9 +198,12 @@ Configuration::get_state () LocaleGuard lg (X_("POSIX")); root = new XMLNode("Ardour"); - typedef map<string, MidiPortDescriptor*>::const_iterator CI; - for(CI m = midi_ports.begin(); m != midi_ports.end(); ++m){ - root->add_child_nocopy(m->second->get_state()); + + 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")); @@ -250,10 +256,13 @@ Configuration::set_state (const XMLNode& root) if (node->name() == "MIDI-port") { try { - pair<string,MidiPortDescriptor*> newpair; - newpair.second = new MidiPortDescriptor (*node); - newpair.first = newpair.second->tag; - midi_ports.insert (newpair); + + 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) { @@ -296,53 +305,6 @@ Configuration::set_variables (const XMLNode& node, ConfigVariableBase::Owner own #undef CONFIG_VARIABLE_SPECIAL } - -Configuration::MidiPortDescriptor::MidiPortDescriptor (const XMLNode& node) -{ - const XMLProperty *prop; - bool have_tag = false; - bool have_device = false; - bool have_type = false; - bool have_mode = false; - - if ((prop = node.property ("tag")) != 0) { - tag = prop->value(); - have_tag = true; - } - - if ((prop = node.property ("device")) != 0) { - device = prop->value(); - have_device = true; - } - - if ((prop = node.property ("type")) != 0) { - type = prop->value(); - have_type = true; - } - - if ((prop = node.property ("mode")) != 0) { - mode = prop->value(); - have_mode = true; - } - - if (!have_tag || !have_device || !have_type || !have_mode) { - throw failed_constructor(); - } -} - -XMLNode& -Configuration::MidiPortDescriptor::get_state() -{ - XMLNode* root = new XMLNode("MIDI-port"); - - root->add_property("tag", tag); - root->add_property("device", device); - root->add_property("type", type); - root->add_property("mode", mode); - - return *root; -} - void Configuration::map_parameters (sigc::slot<void,const char*> theSlot) { diff --git a/libs/ardour/crossfade.cc b/libs/ardour/crossfade.cc index 556f11125e..d45d5efa9f 100644 --- a/libs/ardour/crossfade.cc +++ b/libs/ardour/crossfade.cc @@ -294,6 +294,13 @@ Crossfade::read_at (Sample *buf, Sample *mixdown_buffer, 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); _in->read_at (crossfade_buffer_in, mixdown_buffer, gain_buffer, start, to_write, chan_n); @@ -358,6 +365,13 @@ Crossfade::refresh () 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()); diff --git a/libs/ardour/globals.cc b/libs/ardour/globals.cc index 52be946f1e..c3eae5ad86 100644 --- a/libs/ardour/globals.cc +++ b/libs/ardour/globals.cc @@ -39,7 +39,6 @@ #include <pbd/fpu.h> #include <midi++/port.h> -#include <midi++/port_request.h> #include <midi++/manager.h> #include <midi++/mmc.h> @@ -51,6 +50,7 @@ #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> @@ -114,47 +114,25 @@ setup_osc () #endif int -ARDOUR::setup_midi (AudioEngine& engine) +setup_midi () { - std::map<string,Configuration::MidiPortDescriptor*>::iterator i; - int nports; - - if ((nports = Config->midi_ports.size()) == 0) { + if (Config->midi_ports.size() == 0) { warning << _("no MIDI ports specified: no MMC or MTC control possible") << endmsg; return 0; } - MIDI::Manager::instance()->set_api_data(engine.jack()); - - for (i = Config->midi_ports.begin(); i != Config->midi_ports.end(); ++i) { - Configuration::MidiPortDescriptor* port_descriptor; - - port_descriptor = (*i).second; - - MIDI::PortRequest request (port_descriptor->device, - port_descriptor->tag, - port_descriptor->mode, - port_descriptor->type); - - if (request.status != MIDI::PortRequest::OK) { - error << string_compose(_("MIDI port specifications for \"%1\" are not understandable."), port_descriptor->tag) << endmsg; - continue; - } - - MIDI::Manager::instance()->add_port (request); - - nports++; + 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(); - first = ports.begin()->second; - if (nports > 1) { + if (ports.size() > 1) { - /* More than one port, so try using specific names for each port */ + first = ports.begin()->second; - map<string,Configuration::MidiPortDescriptor *>::iterator i; + /* 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()); @@ -182,11 +160,13 @@ ARDOUR::setup_midi (AudioEngine& engine) default_midi_port = first; } - } else { + } else if (ports.size() == 1) { + + first = ports.begin()->second; /* Only one port described, so use it for both MTC and MMC */ - default_mmc_port = MIDI::Manager::instance()->port (""); + default_mmc_port = first; default_mtc_port = default_mmc_port; default_midi_port = default_mmc_port; } @@ -209,15 +189,16 @@ ARDOUR::setup_midi (AudioEngine& engine) return 0; } - + void setup_hardware_optimization (bool try_optimization) { bool generic_mix_functions = true; - FPU fpu; if (try_optimization) { + FPU fpu; + #if defined (ARCH_X86) && defined (BUILD_SSE_OPTIMIZATIONS) if (fpu.has_sse()) { @@ -253,6 +234,10 @@ setup_hardware_optimization (bool try_optimization) 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) { @@ -265,9 +250,6 @@ setup_hardware_optimization (bool try_optimization) info << "No H/W specific optimizations in use" << endmsg; } - - setup_fpu (); - } int @@ -306,6 +288,8 @@ ARDOUR::init (bool use_vst, bool try_optimization) setup_hardware_optimization (try_optimization); + SourceFactory::init (); + /* singleton - first object is "it" */ new PluginManager (); @@ -387,6 +371,13 @@ ARDOUR::LocaleGuard::~LocaleGuard () 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; diff --git a/libs/ardour/import.cc b/libs/ardour/import.cc index bc16cde156..bd6351cf05 100644 --- a/libs/ardour/import.cc +++ b/libs/ardour/import.cc @@ -32,9 +32,9 @@ #include <glibmm.h> #include <pbd/basename.h> +#include <pbd/convert.h> #include <ardour/ardour.h> -#include <ardour/types.h> #include <ardour/session.h> #include <ardour/session_directory.h> #include <ardour/audio_diskstream.h> @@ -43,82 +43,22 @@ #include <ardour/audioregion.h> #include <ardour/region_factory.h> #include <ardour/source_factory.h> - +#include <ardour/resampled_source.h> #include "i18n.h" using namespace ARDOUR; using namespace PBD; -#define BLOCKSIZE 4096U - -class ImportableSource { - public: - ImportableSource (SNDFILE* sf, SF_INFO* info) : in (sf), sf_info (info) {} - virtual ~ImportableSource() {} - - virtual nframes_t read (Sample* buffer, nframes_t nframes) { - nframes_t per_channel = nframes / sf_info->channels; - per_channel = sf_readf_float (in, buffer, per_channel); - return per_channel * sf_info->channels; - } - - virtual float ratio() const { return 1.0f; } - -protected: - SNDFILE* in; - SF_INFO* sf_info; -}; - -class ResampledImportableSource : public ImportableSource { - public: - ResampledImportableSource (SNDFILE* sf, SF_INFO* info, nframes_t rate) : ImportableSource (sf, info) { - int err; - - sf_seek (in, 0, SEEK_SET) ; - - /* Initialize the sample rate converter. */ - - if ((src_state = src_new (SRC_SINC_BEST_QUALITY, sf_info->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) / sf_info->samplerate ; - - } - - ~ResampledImportableSource () { - src_state = src_delete (src_state) ; - } - - nframes_t read (Sample* buffer, nframes_t nframes); - - float ratio() const { return src_data.src_ratio; } - - private: - float input[BLOCKSIZE]; - SRC_STATE* src_state; - SRC_DATA src_data; -}; - int Session::import_audiofile (import_status& status) { SNDFILE *in; vector<boost::shared_ptr<AudioFileSource> > newfiles; - SourceList sources; SF_INFO info; float *data = 0; Sample **channel_data = 0; - long nfiles = 0; + int nfiles = 0; string basepath; string sounds_dir; nframes_t so_far; @@ -127,178 +67,166 @@ Session::import_audiofile (import_status& status) vector<string> new_paths; struct tm* now; ImportableSource* importable = 0; - const nframes_t nframes = BLOCKSIZE; - - status.new_regions.clear (); - - if ((in = sf_open (status.paths.front().c_str(), SFM_READ, &info)) == 0) { - error << string_compose(_("Import: cannot open input sound file \"%1\""), status.paths.front()) << endmsg; - status.done = 1; - status.cancel = 1; - return -1; - } - - if ((nframes_t) info.samplerate != frame_rate()) { - importable = new ResampledImportableSource (in, &info, frame_rate()); - } else { - importable = new ImportableSource (in, &info); - } - - for (int n = 0; n < info.channels; ++n) { - newfiles.push_back (boost::shared_ptr<AudioFileSource>()); - } + const nframes_t nframes = ResampledImportableSource::blocksize; + uint32_t cnt = 1; - SessionDirectory sdir(get_best_session_directory_for_new_source ()); - sounds_dir = sdir.sound_path().to_string(); - - basepath = PBD::basename_nosuffix (status.paths.front()); + status.sources.clear (); + + for (vector<Glib::ustring>::iterator p = status.paths.begin(); p != status.paths.end(); ++p, ++cnt) { - for (int n = 0; n < info.channels; ++n) { + if ((in = sf_open ((*p).c_str(), SFM_READ, &info)) == 0) { + error << string_compose(_("Import: cannot open input sound file \"%1\""), (*p)) << endmsg; + status.done = 1; + status.cancel = 1; + return -1; + } + + if ((nframes_t) info.samplerate != frame_rate()) { + importable = new ResampledImportableSource (in, &info, frame_rate(), status.quality); + } else { + importable = new ImportableSource (in, &info); + } + + newfiles.clear (); - bool goodfile = false; + for (int n = 0; n < info.channels; ++n) { + newfiles.push_back (boost::shared_ptr<AudioFileSource>()); + } + + SessionDirectory sdir(get_best_session_directory_for_new_source ()); + sounds_dir = sdir.sound_path().to_string(); - do { - if (info.channels == 2) { - if (n == 0) { - snprintf (buf, sizeof(buf), "%s/%s-L.wav", sounds_dir.c_str(), basepath.c_str()); + basepath = PBD::basename_nosuffix ((*p)); + + for (int n = 0; n < info.channels; ++n) { + + bool goodfile = false; + + do { + if (info.channels == 2) { + if (n == 0) { + snprintf (buf, sizeof(buf), "%s/%s-L.wav", sounds_dir.c_str(), basepath.c_str()); + } else { + snprintf (buf, sizeof(buf), "%s/%s-R.wav", sounds_dir.c_str(), basepath.c_str()); + } + } else if (info.channels > 1) { + snprintf (buf, sizeof(buf), "%s/%s-c%d.wav", sounds_dir.c_str(), basepath.c_str(), n+1); } else { - snprintf (buf, sizeof(buf), "%s/%s-R.wav", sounds_dir.c_str(), basepath.c_str()); + snprintf (buf, sizeof(buf), "%s/%s.wav", sounds_dir.c_str(), basepath.c_str()); } - } else if (info.channels > 1) { - snprintf (buf, sizeof(buf), "%s/%s-c%d.wav", sounds_dir.c_str(), basepath.c_str(), n+1); - } else { - snprintf (buf, sizeof(buf), "%s/%s.wav", sounds_dir.c_str(), basepath.c_str()); - } - if (Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) { + if (Glib::file_test (buf, 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 basepath - */ + /* if the file already exists, we must come up with + * a new name for it. for now we just keep appending + * _ to basepath + */ - basepath += "_"; + basepath += "_"; - } else { + } else { - goodfile = true; - } + goodfile = true; + } - } while ( !goodfile); + } while ( !goodfile); - try { - newfiles[n] = boost::dynamic_pointer_cast<AudioFileSource> ( - SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); - } + try { + newfiles[n] = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate())); + } - catch (failed_constructor& err) { - error << string_compose(_("Session::import_audiofile: cannot open new file source for channel %1"), n+1) << endmsg; - goto out; - } + catch (failed_constructor& err) { + error << string_compose(_("Session::import_audiofile: cannot open new file source for channel %1"), n+1) << endmsg; + goto out; + } - new_paths.push_back (buf); - nfiles++; - } + new_paths.push_back (buf); + newfiles[n]->prepare_for_peakfile_writes (); + nfiles++; + } - data = new float[nframes * info.channels]; - channel_data = new Sample * [ info.channels ]; + data = new float[nframes * info.channels]; + channel_data = new Sample * [ info.channels ]; - for (int n = 0; n < info.channels; ++n) { - channel_data[n] = new Sample[nframes]; - } + for (int n = 0; n < info.channels; ++n) { + channel_data[n] = new Sample[nframes]; + } - so_far = 0; + so_far = 0; - status.doing_what = _("converting audio"); - status.progress = 0.0; + if ((nframes_t) info.samplerate != frame_rate()) { + status.doing_what = string_compose (_("converting %1\n(resample from %2KHz to %3KHz)\n(%4 of %5)"), + basepath, + info.samplerate/1000.0f, + frame_rate()/1000.0f, + cnt, status.paths.size()); + + } else { + status.doing_what = string_compose (_("converting %1\n(%2 of %3)"), + basepath, + cnt, status.paths.size()); - while (!status.cancel) { + } + + status.progress = 0.0; + + while (!status.cancel) { - nframes_t nread, nfread; - long x; - long chn; + nframes_t nread, nfread; + long x; + long chn; - if ((nread = importable->read (data, nframes)) == 0) { - break; - } - nfread = nread / info.channels; + if ((nread = importable->read (data, nframes)) == 0) { + break; + } + nfread = nread / info.channels; - /* de-interleave */ + /* de-interleave */ - for (chn = 0; chn < info.channels; ++chn) { + for (chn = 0; chn < info.channels; ++chn) { - nframes_t n; - for (x = chn, n = 0; n < nfread; x += info.channels, ++n) { - channel_data[chn][n] = (Sample) data[x]; + nframes_t n; + for (x = chn, n = 0; n < nfread; x += info.channels, ++n) { + channel_data[chn][n] = (Sample) data[x]; + } } - } - /* flush to disk */ + /* flush to disk */ - for (chn = 0; chn < info.channels; ++chn) { - newfiles[chn]->write (channel_data[chn], nfread); - } + for (chn = 0; chn < info.channels; ++chn) { + newfiles[chn]->write (channel_data[chn], nfread); + } - so_far += nread; - status.progress = so_far / (importable->ratio () * info.frames * info.channels); - } + so_far += nread; + status.progress = so_far / (importable->ratio () * info.frames * info.channels); + } - if (status.cancel) { - goto out; - } + if (status.cancel) { + goto out; + } + + for (int n = 0; n < info.channels; ++n) { + status.sources.push_back (newfiles[n]); + } - if (status.multichan) { - status.doing_what = _("building region"); - } else { - status.doing_what = _("building regions"); + if (status.cancel) { + goto out; + } } - + status.freeze = true; time_t xnow; time (&xnow); now = localtime (&xnow); - if (status.multichan) { - /* all sources are used in a single multichannel region */ + /* flush the final length(s) to the header(s) */ - for (int n = 0; n < nfiles && !status.cancel; ++n) { - /* flush the final length to the header */ - newfiles[n]->update_header(0, *now, xnow); - sources.push_back(newfiles[n]); - } - - bool strip_paired_suffixes = (newfiles.size() > 1); - - boost::shared_ptr<AudioRegion> r (boost::dynamic_pointer_cast<AudioRegion> - (RegionFactory::create (sources, 0, - newfiles[0]->length(), - region_name_from_path (basepath, strip_paired_suffixes), - 0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile)))); - - status.new_regions.push_back (r); - - } else { - for (int n = 0; n < nfiles && !status.cancel; ++n) { - - /* flush the final length to the header */ - - newfiles[n]->update_header(0, *now, xnow); - - /* The sources had zero-length when created, which means that the Session - did not bother to create whole-file AudioRegions for them. Do it now. - - Note: leave any trailing paired indicators from the file names as part - of the region name. - */ - - status.new_regions.push_back (boost::dynamic_pointer_cast<AudioRegion> - (RegionFactory::create (boost::static_pointer_cast<Source> (newfiles[n]), 0, newfiles[n]->length(), - region_name_from_path (newfiles[n]->name(), false), - 0, AudioRegion::Flag (AudioRegion::DefaultFlags | AudioRegion::WholeFile | AudioRegion::Import)))); - } + for (SourceList::iterator x = status.sources.begin(); x != status.sources.end() && !status.cancel; ++x) { + boost::dynamic_pointer_cast<AudioFileSource>(*x)->update_header(0, *now, xnow); + boost::dynamic_pointer_cast<AudioSource>(*x)->done_with_peakfile_writes (); } - + /* save state so that we don't lose these new Sources */ if (!status.cancel) { @@ -321,7 +249,8 @@ Session::import_audiofile (import_status& status) } if (status.cancel) { - status.new_regions.clear (); + + status.sources.clear (); for (vector<string>::iterator i = new_paths.begin(); i != new_paths.end(); ++i) { unlink ((*i).c_str()); @@ -337,50 +266,3 @@ Session::import_audiofile (import_status& status) return ret; } - -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 = ImportableSource::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 = SF_TRUE ; - } - - src_data.input_frames /= sf_info->channels; - src_data.data_in = input ; - } - - src_data.data_out = output; - - if (!src_data.end_of_input) { - src_data.output_frames = nframes / sf_info->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 * sf_info->channels ; - src_data.input_frames -= src_data.input_frames_used ; - - return src_data.output_frames_gen * sf_info->channels; -} - diff --git a/libs/ardour/io.cc b/libs/ardour/io.cc index a708821c20..bff8c18bc9 100644 --- a/libs/ardour/io.cc +++ b/libs/ardour/io.cc @@ -1307,7 +1307,7 @@ IO::state (bool full_state) 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, "%zd,%d,%zd,%d", _input_minimum.get(_default_type), in_max, _output_minimum.get(_default_type), out_max); + 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); @@ -2335,7 +2335,7 @@ IO::automation_snapshot (nframes_t now) { Automatable::automation_snapshot (now); - if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _session.automation_interval()) { + if (_last_automation_snapshot > now || (now - _last_automation_snapshot) > _automation_interval) { _panner->snapshot (now); } } diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index 4af7e2b907..09b54000d8 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -1176,8 +1176,8 @@ Playlist::region_changed (Change what_changed, boost::shared_ptr<Region> region) save = !(_splicing || _nudging); } - if ((what_changed & Region::MuteChanged) && - !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) { + if ((what_changed & our_interests) && + !(what_changed & Change (ARDOUR::PositionChanged|ARDOUR::LengthChanged))) { check_dependents (region, false); } diff --git a/libs/ardour/resampled_source.cc b/libs/ardour/resampled_source.cc new file mode 100644 index 0000000000..38aa3832b9 --- /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 = 4096U; + +ResampledImportableSource::ResampledImportableSource (SNDFILE* sf, SF_INFO* info, nframes_t rate, SrcQuality srcq) + : ImportableSource (sf, info) +{ + int err; + + sf_seek (in, 0, SEEK_SET) ; + + /* Initialize the sample rate converter. */ + + int src_type; + + 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, sf_info->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) / sf_info->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 = ImportableSource::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 = SF_TRUE ; + } + + src_data.input_frames /= sf_info->channels; + src_data.data_in = input ; + } + + src_data.data_out = output; + + if (!src_data.end_of_input) { + src_data.output_frames = nframes / sf_info->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 * sf_info->channels ; + src_data.input_frames -= src_data.input_frames_used ; + + return src_data.output_frames_gen * sf_info->channels; +} + diff --git a/libs/ardour/route.cc b/libs/ardour/route.cc index 19095425f2..be7dfb8469 100644 --- a/libs/ardour/route.cc +++ b/libs/ardour/route.cc @@ -53,7 +53,7 @@ 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), @@ -161,10 +161,35 @@ 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; + } +} + +void Route::inc_gain (gain_t fraction, void *src) { IO::inc_gain (fraction, src); @@ -460,13 +485,9 @@ Route::process_output_buffers (BufferSet& bufs, // OR recording - // h/w monitoring not in use - - (!Config->get_monitoring_model() == HardwareMonitoring && - // AND software monitoring required - Config->get_monitoring_model() == SoftwareMonitoring)) { + Config->get_monitoring_model() == SoftwareMonitoring) { if (apply_gain_automation) { diff --git a/libs/ardour/session.cc b/libs/ardour/session.cc index 742b17af2c..e7f2c542e6 100644 --- a/libs/ardour/session.cc +++ b/libs/ardour/session.cc @@ -124,8 +124,7 @@ Session::Session (AudioEngine &eng, routes (new RouteList), auditioner ((Auditioner*) 0), _click_io ((IO*) 0), - main_outs (0), - _automation_interval (0) + main_outs (0) { if (!eng.connected()) { throw failed_constructor(); @@ -224,8 +223,7 @@ Session::Session (AudioEngine &eng, _send_smpte_update (false), diskstreams (new DiskstreamList), routes (new RouteList), - main_outs (0), - _automation_interval (0) + main_outs (0) { if (!eng.connected()) { @@ -1257,8 +1255,10 @@ Session::set_frame_rate (nframes_t frames_per_second) sync_time_vars(); - _automation_interval = ((nframes_t) ceil ((double) frames_per_second * 0.25)); + 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); @@ -2755,13 +2755,11 @@ Session::source_by_path_and_channel (const Glib::ustring& path, uint16_t chn) return boost::shared_ptr<Source>(); } -string -Session::peak_path_from_audio_path (string audio_path) const +Glib::ustring +Session::peak_path (Glib::ustring base) const { sys::path peakfile_path(_session_dir->peak_path()); - - peakfile_path /= basename_nosuffix (audio_path) + peakfile_suffix; - + peakfile_path /= basename_nosuffix (base) + peakfile_suffix; return peakfile_path.to_string(); } @@ -3387,8 +3385,8 @@ Session::remove_empty_sounds () try { sys::remove (audio_file_path); - const string peak_path = peak_path_from_audio_path (audio_file_path.to_string()); - sys::remove (peak_path); + const string peakfile = peak_path (audio_file_path.to_string()); + sys::remove (peakfile); } catch (const sys::filesystem_error& err) { @@ -4124,6 +4122,23 @@ Session::compute_initial_length () } 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); diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc index 35c1019c0e..aa5a1b87d4 100644 --- a/libs/ardour/session_events.cc +++ b/libs/ardour/session_events.cc @@ -410,6 +410,8 @@ Session::process_event (Event* ev) case Event::Audition: set_audition (ev->region); + // drop reference to region + ev->region.reset (); break; case Event::InputConfigurationChange: diff --git a/libs/ardour/session_state.cc b/libs/ardour/session_state.cc index f2f396cbee..0534da6c89 100644 --- a/libs/ardour/session_state.cc +++ b/libs/ardour/session_state.cc @@ -138,10 +138,14 @@ Session::first_stage_init (string fullpath, string snapshot_name) _name = _current_snapshot_name = snapshot_name; + set_history_depth (Config->get_history_depth()); + _current_frame_rate = _engine.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; @@ -501,7 +505,7 @@ 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 @@ -2668,8 +2672,6 @@ Session::save_history (string snapshot_name) { XMLTree tree; - tree.set_root (&_history.get_state (Config->get_saved_history_depth())); - if (snapshot_name.empty()) { snapshot_name = _current_snapshot_name; } @@ -2691,6 +2693,13 @@ Session::save_history (string snapshot_name) } } + + 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; @@ -2977,6 +2986,10 @@ Session::config_changed (const char* parameter_name) 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 (); @@ -2984,3 +2997,9 @@ Session::config_changed (const char* parameter_name) #undef PARAM_IS } + +void +Session::set_history_depth (uint32_t d) +{ + _history.set_depth (d); +} diff --git a/libs/ardour/sndfilesource.cc b/libs/ardour/sndfilesource.cc index 6977eef6bd..7c4859aa55 100644 --- a/libs/ardour/sndfilesource.cc +++ b/libs/ardour/sndfilesource.cc @@ -25,7 +25,7 @@ #include <sys/stat.h> #include <glibmm/miscutils.h> - +#include <glibmm/thread.h> #include <ardour/sndfilesource.h> #include <ardour/sndfile_helpers.h> #include <ardour/utils.h> @@ -45,6 +45,21 @@ const AudioFileSource::Flag SndFileSource::default_writable_flags = AudioFileSou AudioFileSource::RemovableIfEmpty| AudioFileSource::CanRename); +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; + SndFileSource::SndFileSource (Session& s, const XMLNode& node) : AudioFileSource (s, node) { @@ -186,8 +201,6 @@ SndFileSource::init () // lets try to keep the object initalizations here at the top xfade_buf = 0; - interleave_buf = 0; - interleave_bufsize = 0; sf = 0; _broadcast_info = 0; @@ -272,10 +285,6 @@ SndFileSource::~SndFileSource () touch_peakfile (); } - if (interleave_buf) { - delete [] interleave_buf; - } - if (_broadcast_info) { delete _broadcast_info; } @@ -341,14 +350,7 @@ SndFileSource::read_unlocked (Sample *dst, nframes_t start, nframes_t cnt) const real_cnt = cnt * _info.channels; - if (interleave_bufsize < real_cnt) { - - if (interleave_buf) { - delete [] interleave_buf; - } - interleave_bufsize = real_cnt; - interleave_buf = new float[interleave_bufsize]; - } + Sample* interleave_buf = get_interleave_buffer (real_cnt); nread = sf_read_float (sf, interleave_buf, real_cnt); ptr = interleave_buf + _channel; @@ -401,7 +403,7 @@ SndFileSource::nondestructive_write_unlocked (Sample *data, nframes_t cnt) update_length (oldlen, cnt); if (_build_peakfiles) { - compute_and_write_peaks (data, frame_pos, cnt, false); + compute_and_write_peaks (data, frame_pos, cnt, false, true); } _write_data_count = cnt; @@ -493,7 +495,7 @@ SndFileSource::destructive_write_unlocked (Sample* data, nframes_t cnt) update_length (file_pos, cnt); if (_build_peakfiles) { - compute_and_write_peaks (data, file_pos, cnt, false); + compute_and_write_peaks (data, file_pos, cnt, false, true); } file_pos += cnt; @@ -904,3 +906,21 @@ SndFileSource::one_of_several_channels () const { return _info.channels > 1; } + +Sample* +SndFileSource::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/source_factory.cc b/libs/ardour/source_factory.cc index 148f737551..c6b19c8600 100644 --- a/libs/ardour/source_factory.cc +++ b/libs/ardour/source_factory.cc @@ -19,6 +19,8 @@ */ #include <pbd/error.h> +#include <pbd/convert.h> +#include <pbd/pthread_utils.h> #include <ardour/source_factory.h> #include <ardour/sndfilesource.h> @@ -37,15 +39,69 @@ 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; +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) +SourceFactory::setup_peakfile (boost::shared_ptr<Source> s, bool async) { boost::shared_ptr<AudioSource> as (boost::dynamic_pointer_cast<AudioSource> (s)); + if (as) { - if (as->setup_peakfile ()) { - error << string_compose("SourceFactory: could not set up peakfile for %1", as->name()) << endmsg; - return -1; + + 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; + } } } @@ -60,84 +116,85 @@ SourceFactory::createSilent (Session& s, const XMLNode& node, nframes_t nframes, return ret; } +#ifdef USE_COREAUDIO_FOR_FILES boost::shared_ptr<Source> -SourceFactory::create (Session& s, const XMLNode& node) +SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) { - /* this is allowed to throw */ - - DataType type = DataType::AUDIO; - const XMLProperty* prop = node.property("type"); - if (prop) { - type = DataType(prop->value()); - } - - if (type == DataType::AUDIO) { - -#ifdef HAVE_COREAUDIO - try { - boost::shared_ptr<Source> ret (new CoreAudioSource (s, node)); - if (setup_peakfile (ret)) { + try { + boost::shared_ptr<Source> ret (new CoreAudioSource (s, node)); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { return boost::shared_ptr<Source>(); } - SourceCreated (ret); - return ret; - } + } + SourceCreated (ret); + return ret; + } - catch (failed_constructor& err) { - - /* this is allowed to throw */ - - boost::shared_ptr<Source> ret (new SndFileSource (s, node)); - if (setup_peakfile (ret)) { + + catch (failed_constructor& err) { + + /* this is allowed to throw */ + + boost::shared_ptr<Source> ret (new SndFileSource (s, node)); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { return boost::shared_ptr<Source>(); } - SourceCreated (ret); - return ret; } -#else - boost::shared_ptr<Source> ret (new SndFileSource (s, node)); - - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); - } - SourceCreated (ret); return ret; -#endif + } - } else if (type == DataType::MIDI) { + return boost::shared_ptr<Source>(); +} - boost::shared_ptr<Source> ret (new SMFSource (s, node)); - - SourceCreated (ret); - return ret; +#else + +boost::shared_ptr<Source> +SourceFactory::create (Session& s, const XMLNode& node, bool defer_peaks) +{ + /* this is allowed to throw */ + boost::shared_ptr<Source> ret (new SndFileSource (s, node)); + + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } - return boost::shared_ptr<Source> (); + SourceCreated (ret); + return ret; } +#endif // USE_COREAUDIO_FOR_FILES + boost::shared_ptr<Source> -SourceFactory::createReadable (DataType type, Session& s, string path, int chn, AudioFileSource::Flag flags, bool announce) +SourceFactory::createReadable (DataType type, Session& s, string path, int chn, AudioFileSource::Flag flags, bool announce, bool defer_peaks) { if (type == DataType::AUDIO) { #ifdef HAVE_COREAUDIO try { boost::shared_ptr<Source> ret (new CoreAudioSource (s, path, chn, flags)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } if (announce) { SourceCreated (ret); } return ret; } - + catch (failed_constructor& err) { boost::shared_ptr<Source> ret (new SndFileSource (s, path, chn, flags)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } if (announce) { SourceCreated (ret); @@ -147,8 +204,10 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn, #else boost::shared_ptr<Source> ret (new SndFileSource (s, path, chn, flags)); - if (setup_peakfile (ret)) { - return boost::shared_ptr<Source>(); + if (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } if (announce) { @@ -157,7 +216,7 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn, return ret; #endif - + } else if (type == DataType::MIDI) { // FIXME: flags? @@ -174,21 +233,23 @@ SourceFactory::createReadable (DataType type, Session& s, string path, int chn, } boost::shared_ptr<Source> -SourceFactory::createWritable (DataType type, Session& s, std::string path, bool destructive, nframes_t rate, bool announce) +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)) { - return boost::shared_ptr<Source>(); + (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 (!defer_peaks) { + if (setup_peakfile (ret, false)) { + return boost::shared_ptr<Source>(); + } } if (announce) { SourceCreated (ret); diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index 01e070e920..cfd38c5099 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -143,7 +143,7 @@ touch_file (ustring path) } ustring -region_name_from_path (ustring path, bool strip_channels) +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); @@ -160,6 +160,17 @@ region_name_from_path (ustring path, bool strip_channels) } } + if (add_channel_suffix) { + + path += '%'; + + if (total > 2) { + path += (char) ('a' + this_one); + } else { + path += (char) (this_one == 0 ? 'L' : 'R'); + } + } + return path; } diff --git a/libs/clearlooks/SConscript b/libs/clearlooks/SConscript index 110bfe41be..2676f63022 100644 --- a/libs/clearlooks/SConscript +++ b/libs/clearlooks/SConscript @@ -17,6 +17,9 @@ clearlooks = env.Copy() clearlooks.Replace(CCFLAGS = ' `pkg-config --cflags gtk+-2.0` ', LINKFLAGS = ' `pkg-config --libs gtk+-2.0` ') +if env['GTKOSX']: + clearlooks.Append (CCFLAGS = '-DGTKOSX') + libclearlooks = clearlooks.SharedLibrary('clearlooks', libclearlooks_files) usable_libclearlooks = clearlooks.Install ('engines', libclearlooks) diff --git a/libs/clearlooks/clearlooks_style.c b/libs/clearlooks/clearlooks_style.c index 241f14c6e4..074f1604b1 100644 --- a/libs/clearlooks/clearlooks_style.c +++ b/libs/clearlooks/clearlooks_style.c @@ -1677,14 +1677,18 @@ draw_option (DRAW_ARGS) x += (width - RADIO_SIZE)/2; y += (height - RADIO_SIZE)/2; +#ifndef GTKOSX gdk_gc_set_clip_mask (gc, clearlooks_style->radio_pixmap_mask); gdk_gc_set_clip_origin (gc, x, y); +#endif gdk_draw_drawable (window, gc, pixmap, 0, 0, x, y, RADIO_SIZE, RADIO_SIZE); +#ifndef GTKOSX gdk_gc_set_clip_origin (gc, 0, 0); gdk_gc_set_clip_mask (gc, NULL); +#endif if (area) gdk_gc_set_clip_rectangle (gc, NULL); diff --git a/libs/gtkmm2/gtk/gtkmm/toolbar.cc b/libs/gtkmm2/gtk/gtkmm/toolbar.cc index a8d10a7dde..8e51d70501 100644 --- a/libs/gtkmm2/gtk/gtkmm/toolbar.cc +++ b/libs/gtkmm2/gtk/gtkmm/toolbar.cc @@ -30,7 +30,7 @@ //but the GtkToolbar compatibility system is particularly unpleasant, so we just removed it in gtkmm 2.4. murrayc. //In future, this GTK_DISABLE_DEPRECATED might be inappropriate because it might cover extra GTK+ API. Just remove it then. -#define GTK_DISABLE_DEPRECATED +// #define GTK_DISABLE_DEPRECATED #include <glib.h> #include <gtkmm/button.h> diff --git a/libs/midi++2/SConscript b/libs/midi++2/SConscript index 04b027b6a3..769d888903 100644 --- a/libs/midi++2/SConscript +++ b/libs/midi++2/SConscript @@ -7,7 +7,7 @@ import glob Import('env libraries install_prefix') midi2 = env.Copy() -midi2.Merge([ libraries['sigc2'], libraries['xml'], libraries['glib2'], libraries['pbd'], libraries['jack'] ]) +midi2.Merge([ libraries['sigc2'], libraries['xml'], libraries['glibmm2'], libraries['glib2'], libraries['pbd'], libraries['jack'] ]) domain = 'midipp' @@ -24,7 +24,6 @@ midiparser.cc midiport.cc mmc.cc mtc.cc -port_request.cc version.cc """) diff --git a/libs/midi++2/alsa_sequencer_midiport.cc b/libs/midi++2/alsa_sequencer_midiport.cc index bf891108c6..bae8aff2ab 100644 --- a/libs/midi++2/alsa_sequencer_midiport.cc +++ b/libs/midi++2/alsa_sequencer_midiport.cc @@ -23,10 +23,12 @@ #include <pbd/failed_constructor.h> #include <pbd/error.h> +#include <pbd/xml++.h> #include <midi++/types.h> #include <midi++/alsa_sequencer.h> -#include <midi++/port_request.h> + +#include "i18n.h" //#define DOTRACE 1 @@ -44,31 +46,31 @@ using namespace PBD; snd_seq_t* ALSA_SequencerMidiPort::seq = 0; -ALSA_SequencerMidiPort::ALSA_SequencerMidiPort (PortRequest &req) - : Port (req) +ALSA_SequencerMidiPort::ALSA_SequencerMidiPort (const XMLNode& node) + : Port (node) , decoder (0) , encoder (0) , port_id (-1) { TR_FN(); int err; + Descriptor desc (node); - if (!seq && init_client (req.devname) < 0) { + if (!seq && init_client (desc.device) < 0) { _ok = false; } else { - if (0 <= (err = CreatePorts (req)) && + if (0 <= (err = create_ports (desc)) && 0 <= (err = snd_midi_event_new (1024, &decoder)) && // Length taken from ARDOUR::Session::midi_read () 0 <= (err = snd_midi_event_new (64, &encoder))) { // Length taken from ARDOUR::Session::mmc_buffer snd_midi_event_init (decoder); snd_midi_event_init (encoder); _ok = true; - req.status = PortRequest::OK; - } else { - req.status = PortRequest::Unknown; - } + } } + + set_state (node); } ALSA_SequencerMidiPort::~ALSA_SequencerMidiPort () @@ -94,7 +96,8 @@ ALSA_SequencerMidiPort::selectable () const return -1; } -int ALSA_SequencerMidiPort::write (byte *msg, size_t msglen, timestamp_t timestamp) +int +ALSA_SequencerMidiPort::write (byte *msg, size_t msglen, timestamp_t ignored) { TR_FN (); int R; @@ -133,7 +136,8 @@ int ALSA_SequencerMidiPort::write (byte *msg, size_t msglen, timestamp_t timesta return totwritten; } -int ALSA_SequencerMidiPort::read (byte *buf, size_t max, timestamp_t timestamp) +int +ALSA_SequencerMidiPort::read (byte *buf, size_t max, timestamp_t ignored) { TR_FN(); int err; @@ -158,17 +162,20 @@ int ALSA_SequencerMidiPort::read (byte *buf, size_t max, timestamp_t timestamp) } int -ALSA_SequencerMidiPort::CreatePorts (PortRequest &req) +ALSA_SequencerMidiPort::create_ports (const Port::Descriptor& desc) { int err; unsigned int caps = 0; - if (req.mode == O_WRONLY || req.mode == O_RDWR) + if (desc.mode == O_WRONLY || desc.mode == O_RDWR) caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE; - if (req.mode == O_RDONLY || req.mode == O_RDWR) + if (desc.mode == O_RDONLY || desc.mode == O_RDWR) caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ; - if (0 <= (err = snd_seq_create_simple_port (seq, req.tagname, caps, SND_SEQ_PORT_TYPE_MIDI_GENERIC))) { + if (0 <= (err = snd_seq_create_simple_port (seq, desc.tag.c_str(), caps, + (SND_SEQ_PORT_TYPE_MIDI_GENERIC| + SND_SEQ_PORT_TYPE_SOFTWARE| + SND_SEQ_PORT_TYPE_APPLICATION)))) { port_id = err; @@ -203,3 +210,216 @@ ALSA_SequencerMidiPort::init_client (std::string name) return -1; } } + +int +ALSA_SequencerMidiPort::discover (vector<PortSet>& ports) +{ + int n = 0; + + snd_seq_client_info_t *client_info; + snd_seq_port_info_t *port_info; + + snd_seq_client_info_alloca (&client_info); + snd_seq_port_info_alloca (&port_info); + snd_seq_client_info_set_client (client_info, -1); + + while (snd_seq_query_next_client(seq, client_info) >= 0) { + + int alsa_client; + + if ((alsa_client = snd_seq_client_info_get_client(client_info)) <= 0) { + break; + } + + snd_seq_port_info_set_client(port_info, alsa_client); + snd_seq_port_info_set_port(port_info, -1); + + char client[256]; + snprintf (client, sizeof (client), "%d:%s", alsa_client, snd_seq_client_info_get_name(client_info)); + + ports.push_back (PortSet (client)); + + while (snd_seq_query_next_port(seq, port_info) >= 0) { + +#if 0 + int type = snd_seq_port_info_get_type(pinfo); + if (!(type & SND_SEQ_PORT_TYPE_PORT)) { + continue; + } +#endif + + unsigned int port_capability = snd_seq_port_info_get_capability(port_info); + + if ((port_capability & SND_SEQ_PORT_CAP_NO_EXPORT) == 0) { + + int alsa_port = snd_seq_port_info_get_port(port_info); + + char port[256]; + snprintf (port, sizeof (port), "%d:%s", alsa_port, snd_seq_port_info_get_name(port_info)); + + std::string mode; + + if (port_capability & SND_SEQ_PORT_CAP_READ) { + if (port_capability & SND_SEQ_PORT_CAP_WRITE) { + mode = "duplex"; + } else { + mode = "output"; + } + } else if (port_capability & SND_SEQ_PORT_CAP_WRITE) { + if (port_capability & SND_SEQ_PORT_CAP_READ) { + mode = "duplex"; + } else { + mode = "input"; + } + } + + XMLNode node (X_("MIDI-port")); + node.add_property ("device", client); + node.add_property ("tag", port); + node.add_property ("mode", mode); + node.add_property ("type", "alsa/sequencer"); + + ports.back().ports.push_back (node); + ++n; + } + } + } + + return n; +} + +void +ALSA_SequencerMidiPort::get_connections (vector<SequencerPortAddress>& connections, int dir) const +{ + snd_seq_query_subscribe_t *subs; + snd_seq_addr_t seq_addr; + + snd_seq_query_subscribe_alloca (&subs); + + // Get port connections... + + if (dir) { + snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_WRITE); + } else { + snd_seq_query_subscribe_set_type(subs, SND_SEQ_QUERY_SUBS_READ); + } + + snd_seq_query_subscribe_set_index(subs, 0); + seq_addr.client = snd_seq_client_id (seq); + seq_addr.port = port_id; + snd_seq_query_subscribe_set_root(subs, &seq_addr); + + while (snd_seq_query_port_subscribers(seq, subs) >= 0) { + + seq_addr = *snd_seq_query_subscribe_get_addr (subs); + + connections.push_back (SequencerPortAddress (seq_addr.client, + seq_addr.port)); + + snd_seq_query_subscribe_set_index(subs, snd_seq_query_subscribe_get_index(subs) + 1); + } +} + +XMLNode& +ALSA_SequencerMidiPort::get_state () const +{ + XMLNode& root (Port::get_state ()); + vector<SequencerPortAddress> connections; + XMLNode* sub = 0; + char buf[256]; + + get_connections (connections, 1); + + if (!connections.empty()) { + if (!sub) { + sub = new XMLNode (X_("connections")); + } + for (vector<SequencerPortAddress>::iterator i = connections.begin(); i != connections.end(); ++i) { + XMLNode* cnode = new XMLNode (X_("read")); + snprintf (buf, sizeof (buf), "%d:%d", i->first, i->second); + cnode->add_property ("dest", buf); + sub->add_child_nocopy (*cnode); + } + } + + connections.clear (); + get_connections (connections, 0); + + if (!connections.empty()) { + if (!sub) { + sub = new XMLNode (X_("connections")); + } + for (vector<SequencerPortAddress>::iterator i = connections.begin(); i != connections.end(); ++i) { + XMLNode* cnode = new XMLNode (X_("write")); + snprintf (buf, sizeof (buf), "%d:%d", i->first, i->second); + cnode->add_property ("dest", buf); + sub->add_child_nocopy (*cnode); + } + } + + if (sub) { + root.add_child_nocopy (*sub); + } + + return root; +} + +void +ALSA_SequencerMidiPort::set_state (const XMLNode& node) +{ + Port::set_state (node); + + XMLNodeList children (node.children()); + XMLNodeIterator iter; + + for (iter = children.begin(); iter != children.end(); ++iter) { + + if ((*iter)->name() == X_("connections")) { + + XMLNodeList gchildren ((*iter)->children()); + XMLNodeIterator gciter; + + for (gciter = gchildren.begin(); gciter != gchildren.end(); ++gciter) { + XMLProperty* prop; + + if ((prop = (*gciter)->property ("dest")) != 0) { + int client; + int port; + + if (sscanf (prop->value().c_str(), "%d:%d", &client, &port) == 2) { + + snd_seq_port_subscribe_t *sub; + snd_seq_addr_t seq_addr; + + snd_seq_port_subscribe_alloca(&sub); + + if ((*gciter)->name() == X_("write")) { + + seq_addr.client = snd_seq_client_id (seq); + seq_addr.port = port_id; + snd_seq_port_subscribe_set_sender(sub, &seq_addr); + + seq_addr.client = client; + seq_addr.port = port; + snd_seq_port_subscribe_set_dest(sub, &seq_addr); + + } else { + + seq_addr.client = snd_seq_client_id (seq); + seq_addr.port = port_id; + snd_seq_port_subscribe_set_dest(sub, &seq_addr); + + seq_addr.client = client; + seq_addr.port = port; + snd_seq_port_subscribe_set_sender(sub, &seq_addr); + } + + snd_seq_subscribe_port (seq, sub); + } + } + } + + break; + } + } +} diff --git a/libs/midi++2/coremidi_midiport.cc b/libs/midi++2/coremidi_midiport.cc index 2cd98239ec..14020a6f35 100644 --- a/libs/midi++2/coremidi_midiport.cc +++ b/libs/midi++2/coremidi_midiport.cc @@ -23,7 +23,6 @@ #include <midi++/coremidi_midiport.h> #include <midi++/types.h> -#include <midi++/port_request.h> #include <mach/mach_time.h> #include <pbd/pthread_utils.h> @@ -36,15 +35,15 @@ MIDITimeStamp CoreMidi_MidiPort::MIDIGetCurrentHostTime() return mach_absolute_time(); } -CoreMidi_MidiPort::CoreMidi_MidiPort (PortRequest &req) : Port (req) +CoreMidi_MidiPort::CoreMidi_MidiPort (const XMLNode& node) : Port (node) { + Descriptor desc (node); + firstrecv = true; int err; - if (0 == (err = Open(req))) { + if (0 == (err = Open(desc))) { _ok = true; - req.status = PortRequest::OK; - } else - req.status = PortRequest::Unknown; + } } CoreMidi_MidiPort::~CoreMidi_MidiPort () {Close();} @@ -56,10 +55,10 @@ void CoreMidi_MidiPort::Close () if (midi_client) MIDIClientDispose(midi_client); } -int CoreMidi_MidiPort::write (byte *msg, size_t msglen, timestamp_t timestamp) +int CoreMidi_MidiPort::write (byte *msg, size_t msglen, timestamp_t ignored) { OSStatus err; - MIDIPacketList* pktlist = (MIDIPacketList*)midi_buffer; + MIDIPacketList* pktlist = (MIDIPacketList*)midi_buffer; MIDIPacket* packet = MIDIPacketListInit(pktlist); packet = MIDIPacketListAdd(pktlist,sizeof(midi_buffer),packet,MIDIGetCurrentHostTime(),msglen,msg); @@ -77,21 +76,21 @@ int CoreMidi_MidiPort::write (byte *msg, size_t msglen, timestamp_t timestamp) } } -int CoreMidi_MidiPort::Open (PortRequest &req) +int CoreMidi_MidiPort::Open (const Descriptor& desc) { OSStatus err; CFStringRef coutputStr; string str; - - coutputStr = CFStringCreateWithCString(0, req.devname, CFStringGetSystemEncoding()); + + coutputStr = CFStringCreateWithCString(0, desc.device.c_str(), CFStringGetSystemEncoding()); err = MIDIClientCreate(coutputStr, 0, 0, &midi_client); CFRelease(coutputStr); - if (!midi_client) { + if (!midi_client) { //error << "Cannot open CoreMidi client : " << err << endmsg. - goto error; - } + goto error; + } - str = req.tagname + string("_in"); + str = desc.tag + string("_in"); coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding()); err = MIDIDestinationCreate(midi_client, coutputStr, read_proc, this, &midi_destination); CFRelease(coutputStr); @@ -100,7 +99,7 @@ int CoreMidi_MidiPort::Open (PortRequest &req) goto error; } - str = req.tagname + string("_out"); + str = desc.tag + string("_out"); coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding()); err = MIDISourceCreate(midi_client, coutputStr, &midi_source); CFRelease(coutputStr); @@ -142,3 +141,10 @@ void CoreMidi_MidiPort::read_proc (const MIDIPacketList *pktlist, void *refCon, } } +int +CoreMidi_MidiPort::discover (vector<PortSet>& ports) +{ + /* XXX do dynamic port discovery here */ + + return 0; +} diff --git a/libs/midi++2/fd_midiport.cc b/libs/midi++2/fd_midiport.cc index 81d81c8558..17a7eff367 100644 --- a/libs/midi++2/fd_midiport.cc +++ b/libs/midi++2/fd_midiport.cc @@ -34,40 +34,38 @@ using namespace PBD; string *FD_MidiPort::midi_dirpath = 0; string *FD_MidiPort::midi_filename_pattern = 0; -FD_MidiPort::FD_MidiPort (PortRequest &req, +FD_MidiPort::FD_MidiPort (const XMLNode& node, const string &dirpath, const string &pattern) - : Port (req) + : Port (node) { - open (req); + Descriptor desc (node); + + open (desc); if (_fd < 0) { switch (errno) { case EBUSY: error << "MIDI: port device in use" << endmsg; - req.status = PortRequest::Busy; break; case ENOENT: error << "MIDI: no such port device" << endmsg; - req.status = PortRequest::NoSuchFile; break; case EACCES: error << "MIDI: access to port denied" << endmsg; - req.status = PortRequest::NotAllowed; break; default: - req.status = PortRequest::Unknown; + break; } } else { _ok = true; - req.status = PortRequest::OK; if (midi_dirpath == 0) { midi_dirpath = new string (dirpath); midi_filename_pattern = new string (pattern); } - if (req.mode & O_NONBLOCK == 0) { + if (desc.mode & O_NONBLOCK == 0) { /* we unconditionally set O_NONBLOCK during open, but the request didn't ask for it, so remove it. @@ -80,11 +78,11 @@ FD_MidiPort::FD_MidiPort (PortRequest &req, } void -FD_MidiPort::open (PortRequest &req) +FD_MidiPort::open (const Descriptor& desc) { - int mode = req.mode | O_NONBLOCK; - _fd = ::open (req.devname, mode); + int mode = desc.mode | O_NONBLOCK; + _fd = ::open (desc.device.c_str(), mode); } vector<string *> * @@ -152,7 +150,7 @@ FD_MidiPort::do_slow_write (byte *msg, unsigned int msglen) } int -FD_MidiPort::read (byte* buf, size_t max, timestamp_t timestamp) +FD_MidiPort::read (byte* buf, size_t max, timestamp_t ignored) { int nread; diff --git a/libs/midi++2/fifomidi.cc b/libs/midi++2/fifomidi.cc index 7bb126ddeb..a81520bb95 100644 --- a/libs/midi++2/fifomidi.cc +++ b/libs/midi++2/fifomidi.cc @@ -26,19 +26,19 @@ using namespace MIDI; -FIFO_MidiPort::FIFO_MidiPort (PortRequest &req) - : FD_MidiPort (req, ".", "midi") +FIFO_MidiPort::FIFO_MidiPort (const XMLNode& node) + : FD_MidiPort (node, ".", "midi") { } void -FIFO_MidiPort::open (PortRequest &req) +FIFO_MidiPort::open (const Port::Descriptor& desc) { /* This is a placeholder for the fun-and-games I think we will need to do with FIFO's. */ - _fd = ::open (req.devname, req.mode|O_NDELAY); + _fd = ::open (desc.device.c_str(), desc.mode|O_NDELAY); } diff --git a/libs/midi++2/jack_midiport.cc b/libs/midi++2/jack_midiport.cc index 4b2808a698..11cd70a051 100644 --- a/libs/midi++2/jack_midiport.cc +++ b/libs/midi++2/jack_midiport.cc @@ -23,26 +23,22 @@ #include <midi++/types.h> #include <midi++/jack.h> -#include <midi++/port_request.h> using namespace std; using namespace MIDI; -JACK_MidiPort::JACK_MidiPort(PortRequest & req, jack_client_t* jack_client) - : Port(req) +JACK_MidiPort::JACK_MidiPort(const XMLNode& node, jack_client_t* jack_client) + : Port(node) , _jack_client(jack_client) , _jack_input_port(NULL) , _jack_output_port(NULL) , _last_read_index(0) { - int err = create_ports(req); + int err = create_ports (node); if (!err) { - req.status = PortRequest::OK; _ok = true; - } else { - req.status = PortRequest::Unknown; - } + } } JACK_MidiPort::~JACK_MidiPort() @@ -94,8 +90,10 @@ JACK_MidiPort::read(byte * buf, size_t max, timestamp_t timestamp) } int -JACK_MidiPort::create_ports(PortRequest & req) +JACK_MidiPort::create_ports(const XMLNode& node) { + Descriptor desc (node); + assert(!_jack_input_port); assert(!_jack_output_port); @@ -103,24 +101,33 @@ JACK_MidiPort::create_ports(PortRequest & req) bool ret = true; - if (req.mode == O_RDWR || req.mode == O_WRONLY) { + if (desc.mode == O_RDWR || desc.mode == O_WRONLY) { _jack_output_port = jack_port_register(_jack_client, - string(req.tagname).append("_out").c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); - jack_midi_clear_buffer( - jack_port_get_buffer(_jack_output_port, nframes)); + string(desc.tag).append("_out").c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); + jack_midi_clear_buffer(jack_port_get_buffer(_jack_output_port, nframes)); ret = ret && (_jack_output_port != NULL); } - - if (req.mode == O_RDWR || req.mode == O_RDONLY) { + + if (desc.mode == O_RDWR || desc.mode == O_RDONLY) { _jack_input_port = jack_port_register(_jack_client, - string(req.tagname).append("_in").c_str(), - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); - jack_midi_clear_buffer( - jack_port_get_buffer(_jack_input_port, nframes)); + string(desc.tag).append("_in").c_str(), + JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); + jack_midi_clear_buffer(jack_port_get_buffer(_jack_input_port, nframes)); ret = ret && (_jack_input_port != NULL); } return ret ? 0 : -1; } +XMLNode& +JACK_MidiPort::get_state () const +{ + XMLNode& root (Port::get_state ()); + return root; +} + +void +JACK_MidiPort::set_state (const XMLNode& node) +{ +} diff --git a/libs/midi++2/midi++/alsa_rawmidi.h b/libs/midi++2/midi++/alsa_rawmidi.h index 54b86edd70..e5abc2832f 100644 --- a/libs/midi++2/midi++/alsa_rawmidi.h +++ b/libs/midi++2/midi++/alsa_rawmidi.h @@ -34,8 +34,8 @@ class ALSA_RawMidiPort : public MIDI::FD_MidiPort { public: - ALSA_RawMidiPort (MIDI::PortRequest &req) - : FD_MidiPort (req, "/dev/snd", "midi") {} + ALSA_RawMidiPort (const XMLNode& node) + : FD_MidiPort (node, "/dev/snd", "midi") {} virtual ~ALSA_RawMidiPort () {} static std::string typestring; @@ -46,7 +46,7 @@ class ALSA_RawMidiPort : public MIDI::FD_MidiPort } }; -} // namespace MIDI +} #endif // __alsa_rawmidi_h__ diff --git a/libs/midi++2/midi++/alsa_sequencer.h b/libs/midi++2/midi++/alsa_sequencer.h index b54486416a..a95f9c476f 100644 --- a/libs/midi++2/midi++/alsa_sequencer.h +++ b/libs/midi++2/midi++/alsa_sequencer.h @@ -30,39 +30,45 @@ namespace MIDI { -class PortRequest; - class ALSA_SequencerMidiPort : public Port + { public: - ALSA_SequencerMidiPort (PortRequest &req); + ALSA_SequencerMidiPort (const XMLNode&); virtual ~ALSA_SequencerMidiPort (); /* select(2)/poll(2)-based I/O */ virtual int selectable() const; - + + static int discover (std::vector<PortSet>&); static std::string typestring; - protected: - std::string get_typestring () const { - return typestring; - } + XMLNode& get_state() const; + void set_state (const XMLNode&); protected: /* Direct I/O */ + int write (byte *msg, size_t msglen, timestamp_t timestamp); int read (byte *buf, size_t max, timestamp_t timestamp); + std::string get_typestring () const { + return typestring; + } + private: snd_midi_event_t *decoder, *encoder; int port_id; snd_seq_event_t SEv; - int CreatePorts(PortRequest &req); + int create_ports (const Port::Descriptor&); static int init_client (std::string name); static snd_seq_t* seq; + + typedef std::pair<int,int> SequencerPortAddress; + void get_connections (std::vector<SequencerPortAddress>&, int dir) const; }; }; /* namespace MIDI */ diff --git a/libs/midi++2/midi++/coremidi_midiport.h b/libs/midi++2/midi++/coremidi_midiport.h index 91eccea4a5..d5d3c23ede 100644 --- a/libs/midi++2/midi++/coremidi_midiport.h +++ b/libs/midi++2/midi++/coremidi_midiport.h @@ -22,6 +22,7 @@ #include <list> #include <string> +#include <vector> #include <fcntl.h> #include <unistd.h> @@ -32,45 +33,47 @@ namespace MIDI { -namespace PortRequest; + class CoreMidi_MidiPort:public Port { + public: + CoreMidi_MidiPort(const XMLNode& node); + virtual ~ CoreMidi_MidiPort(); -class CoreMidi_MidiPort:public Port -{ - public: - CoreMidi_MidiPort(PortRequest & req); - virtual ~ CoreMidi_MidiPort(); - - virtual int selectable() const { - return -1; - } - static std::string typestring; + virtual int selectable() const { + return -1; + } + + static int discover (std::vector<PortSet>&); + static std::string typestring; + + protected: + /* Direct I/O */ + int write (byte * msg, size_t msglen, timestamp_t timestamp); - protected: + int read (byte * buf, size_t max, timestamp_t timestamp) { + return 0; + } + + /* CoreMidi callback */ + static void read_proc(const MIDIPacketList * pktlist, + void *refCon, void *connRefCon); + std::string get_typestring () const { return typestring; } - protected: - /* Direct I/O */ - int write (byte *msg, size_t msglen, timestamp_t timestamp); - int read (byte *buf, size_t max, timestamp_t timestamp); - - /* CoreMidi callback */ - static void read_proc(const MIDIPacketList * pktlist, - void *refCon, void *connRefCon); - - private: - byte midi_buffer[1024]; - MIDIClientRef midi_client; - MIDIEndpointRef midi_destination; - MIDIEndpointRef midi_source; + private: + byte midi_buffer[1024]; + MIDIClientRef midi_client; + MIDIEndpointRef midi_destination; + MIDIEndpointRef midi_source; - int Open(PortRequest & req); - void Close(); - static MIDITimeStamp MIDIGetCurrentHostTime(); + int Open(const Port::Descriptor&); + void Close(); + static MIDITimeStamp MIDIGetCurrentHostTime(); - bool firstrecv; -}; + bool firstrecv; + + }; } // namespace MIDI diff --git a/libs/midi++2/midi++/factory.h b/libs/midi++2/midi++/factory.h index 9954ea72fe..f3402546e9 100644 --- a/libs/midi++2/midi++/factory.h +++ b/libs/midi++2/midi++/factory.h @@ -23,13 +23,12 @@ #include <string> #include <midi++/port.h> -#include <midi++/port_request.h> namespace MIDI { class PortFactory { public: - Port *create_port (PortRequest &req, void* data); + Port *create_port (const XMLNode&, void* data); static bool ignore_duplicate_devices (Port::Type); static int get_known_ports (std::vector<PortSet>&); diff --git a/libs/midi++2/midi++/fd_midiport.h b/libs/midi++2/midi++/fd_midiport.h index 34e2e27a1a..ec5a9f8af4 100644 --- a/libs/midi++2/midi++/fd_midiport.h +++ b/libs/midi++2/midi++/fd_midiport.h @@ -29,7 +29,6 @@ #include <unistd.h> #include <midi++/port.h> -#include <midi++/port_request.h> namespace MIDI { @@ -37,7 +36,7 @@ class FD_MidiPort : public Port { public: - FD_MidiPort (PortRequest &req, + FD_MidiPort (const XMLNode& node, const std::string &dirpath, const std::string &pattern); @@ -46,23 +45,14 @@ class FD_MidiPort : public Port } virtual int selectable() const; - static std::vector<std::string *> *list_devices (); - - static std::string typestring; - protected: - std::string get_typestring () const { - return typestring; - } + static std::vector<std::string *> *list_devices (); protected: int _fd; - virtual void open (PortRequest &req); + virtual void open (const Port::Descriptor&); - /* Direct I/O */ - - virtual int write (byte *msg, size_t msglen, - timestamp_t timestamp) { + virtual int write (byte *msg, size_t msglen, timestamp_t ignored) { int nwritten; if ((_mode & O_ACCMODE) == O_RDONLY) { @@ -89,8 +79,7 @@ class FD_MidiPort : public Port return nwritten; } - virtual int read (byte *buf, size_t max, - timestamp_t timestamp); + virtual int read (byte *buf, size_t max, timestamp_t ignored); private: static std::string *midi_dirpath; diff --git a/libs/midi++2/midi++/fifomidi.h b/libs/midi++2/midi++/fifomidi.h index ea644dde06..3439c27dcf 100644 --- a/libs/midi++2/midi++/fifomidi.h +++ b/libs/midi++2/midi++/fifomidi.h @@ -25,7 +25,6 @@ #include <unistd.h> #include <midi++/port.h> -#include <midi++/port_request.h> #include <midi++/fd_midiport.h> namespace MIDI { @@ -34,7 +33,7 @@ class FIFO_MidiPort : public MIDI::FD_MidiPort { public: - FIFO_MidiPort (PortRequest &req); + FIFO_MidiPort (const XMLNode&); ~FIFO_MidiPort () {}; static std::string typestring; @@ -45,7 +44,7 @@ class FIFO_MidiPort : public MIDI::FD_MidiPort } private: - void open (PortRequest &req); + void open (const Port::Descriptor&); }; } // namespace MIDI diff --git a/libs/midi++2/midi++/jack.h b/libs/midi++2/midi++/jack.h index 1f25609aac..845dd0c229 100644 --- a/libs/midi++2/midi++/jack.h +++ b/libs/midi++2/midi++/jack.h @@ -39,7 +39,7 @@ namespace MIDI class JACK_MidiPort : public Port { public: - JACK_MidiPort (PortRequest &req, jack_client_t* jack_client); + JACK_MidiPort (const XMLNode& node, jack_client_t* jack_client); virtual ~JACK_MidiPort (); /* No select(2)/poll(2)-based I/O */ @@ -49,6 +49,9 @@ public: static std::string typestring; + virtual XMLNode& get_state () const; + virtual void set_state (const XMLNode&); + protected: std::string get_typestring () const { return typestring; @@ -60,7 +63,7 @@ protected: int read(byte *buf, size_t max, timestamp_t timestamp); private: - int create_ports(PortRequest &req); + int create_ports(const XMLNode&); jack_client_t* _jack_client; jack_port_t* _jack_input_port; diff --git a/libs/midi++2/midi++/manager.h b/libs/midi++2/midi++/manager.h index eef52abe52..bb3bf9b999 100644 --- a/libs/midi++2/midi++/manager.h +++ b/libs/midi++2/midi++/manager.h @@ -21,6 +21,8 @@ #define __midi_manager_h__ #include <map> +#include <vector> + #include <string> #include <midi++/types.h> @@ -28,20 +30,18 @@ namespace MIDI { -/** Creates, stores, and manages system MIDI ports. - */ class Manager { public: ~Manager (); void set_api_data(void* data) { api_data = data; } - + /** Signal the start of an audio cycle. * This MUST be called before any reading/writing for this cycle. * Realtime safe. */ void cycle_start(nframes_t nframes); - + /** Signal the end of an audio cycle. * This signifies that the cycle began with @ref cycle_start has ended. * This MUST be called at the end of each cycle. @@ -49,13 +49,25 @@ class Manager { */ void cycle_end(); - Port *add_port (PortRequest &); + Port *add_port (const XMLNode& node); int remove_port (Port*); Port *port (std::string name); size_t nports () { return ports_by_device.size(); } + /* defaults for clients who are not picky */ + + Port *inputPort; + Port *outputPort; + channel_t inputChannelNumber; + channel_t outputChannelNumber; + + int set_input_port (std::string); + int set_output_port (std::string); + int set_input_channel (channel_t); + int set_output_channel (channel_t); + int foreach_port (int (*func)(const Port &, size_t n, void *), void *arg); @@ -70,7 +82,7 @@ class Manager { return theManager; } - static int parse_port_request (std::string str, Port::Type type); + int get_known_ports (std::vector<PortSet>&); private: /* This is a SINGLETON pattern */ @@ -81,7 +93,7 @@ class Manager { PortMap ports_by_device; /* canonical */ PortMap ports_by_tag; /* may contain duplicate Ports */ - void *api_data; + void* api_data; void close_ports (); }; diff --git a/libs/midi++2/midi++/nullmidi.h b/libs/midi++2/midi++/nullmidi.h index 6ed94db71c..8f36e6aed8 100644 --- a/libs/midi++2/midi++/nullmidi.h +++ b/libs/midi++2/midi++/nullmidi.h @@ -24,7 +24,6 @@ #include <string> #include <midi++/port.h> -#include <midi++/port_request.h> namespace MIDI { diff --git a/libs/midi++2/midi++/port.h b/libs/midi++2/midi++/port.h index e4338cf952..dcae446c42 100644 --- a/libs/midi++2/midi++/port.h +++ b/libs/midi++2/midi++/port.h @@ -20,10 +20,11 @@ #define __libmidi_port_h__ #include <string> +#include <iostream> #include <sigc++/sigc++.h> +#include <pbd/xml++.h> -#include <pbd/selectable.h> #include <midi++/types.h> #include <midi++/parser.h> @@ -44,64 +45,82 @@ class Port : public sigc::trackable { FIFO }; - Port (PortRequest &); + + Port (const XMLNode&); virtual ~Port (); + virtual XMLNode& get_state () const; + virtual void set_state (const XMLNode&); + // FIXME: make Manager a friend of port so these can be hidden? - + /* Only for use by MidiManager. Don't ever call this. */ virtual void cycle_start(nframes_t nframes); - /* Only for use by MidiManager. Don't ever call this. */ virtual void cycle_end(); - /* Direct I/O */ - - /** Read a message from port. - * @param buf Raw MIDI message to send - * @param max Max size to write to @a buf - * @param timestamp Time stamp in frames of this message (relative to cycle start) - * @return number of bytes successfully written to \a buf - */ - virtual int read(byte *buf, size_t max, timestamp_t timestamp) = 0; - /** Write a message to port. * @param msg Raw MIDI message to send * @param msglen Size of @a msg * @param timestamp Time stamp in frames of this message (relative to cycle start) * @return number of bytes successfully written */ - virtual int write(byte *msg, size_t msglen, timestamp_t timestamp) = 0; + virtual int write (byte *msg, size_t msglen, timestamp_t timestamp) = 0; + + /** Read a message from port. + * @param buf Raw MIDI message to send + * @param max Max size to write to @a buf + * @param timestamp Time stamp in frames of this message (relative to cycle start) + * @return number of bytes successfully written to \a buf + */ + virtual int read (byte *buf, size_t max, timestamp_t timestamp) = 0; /** Write a message to port. * @return true on success. * FIXME: describe semantics here */ - bool midimsg (byte *msg, size_t len, timestamp_t timestamp) { + int midimsg (byte *msg, size_t len, timestamp_t timestamp) { return !(write (msg, len, timestamp) == (int) len); - } + } + + int three_byte_msg (byte a, byte b, byte c, timestamp_t timestamp) { + byte msg[3]; + msg[0] = a; + msg[1] = b; + msg[2] = c; + + return !(write (msg, 3, timestamp) == 3); + } + bool clock (timestamp_t timestamp); + + /* slowdown i/o to a loop of single byte emissions + interspersed with a busy loop of 10000 * this value. + + This may be ignored by a particular instance + of this virtual class. See FD_MidiPort for an + example of where it used. + */ - /** Slow down I/O to a loop of single byte emissions - * interspersed with a busy loop of 10000 * this value. - * - * This may be ignored by a particular instance of this virtual - * class. See FD_MidiPort for an example of where it used. */ void set_slowdown (size_t n) { slowdown = n; } /* select(2)/poll(2)-based I/O */ /** Get the file descriptor for port. - * @return File descriptor, or -1 if not selectable. */ + * @return File descriptor, or -1 if not selectable. + */ virtual int selectable() const = 0; + static void gtk_read_callback (void *ptr, int fd, int cond); + static void write_callback (byte *msg, unsigned int len, void *); + Channel *channel (channel_t chn) { return _channel[chn&0x7F]; } - Parser *input() { return input_parser; } - Parser *output() { return output_parser; } + Parser *input() { return input_parser; } + Parser *output() { return output_parser; } void iostat (int *written, int *read, const size_t **in_counts, @@ -121,14 +140,21 @@ class Port : public sigc::trackable { } } - bool clock (); - const char *device () const { return _devname.c_str(); } - const char *name () const { return _tagname.c_str(); } - Type type () const { return _type; } - int mode () const { return _mode; } - bool ok () const { return _ok; } - size_t number () const { return _number; } + const char *name () const { return _tagname.c_str(); } + Type type () const { return _type; } + int mode () const { return _mode; } + bool ok () const { return _ok; } + + struct Descriptor { + std::string tag; + std::string device; + int mode; + Port::Type type; + + Descriptor (const XMLNode&); + XMLNode& get_state(); + }; protected: bool _ok; @@ -147,10 +173,21 @@ class Port : public sigc::trackable { Parser *output_parser; size_t slowdown; + virtual std::string get_typestring () const = 0; + private: static size_t nports; }; +struct PortSet { + PortSet (std::string str) : owner (str) { } + + std::string owner; + std::list<XMLNode> ports; +}; + +std::ostream & operator << ( std::ostream & os, const Port & port ); + } // namespace MIDI #endif // __libmidi_port_h__ diff --git a/libs/midi++2/midi++/port_request.h b/libs/midi++2/midi++/port_request.h deleted file mode 100644 index dfde87a63c..0000000000 --- a/libs/midi++2/midi++/port_request.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - Copyright (C) 1999 Paul Barton-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 __midi_port_request_h__ -#define __midi_port_request_h__ - -#include <string> - -namespace MIDI { - -struct PortRequest { - enum Status { - Unknown, - OK, - Busy, - NoSuchFile, - TypeUnsupported, - NotAllowed - }; - const char *devname; - const char *tagname; - int mode; - Port::Type type; - Status status; - - PortRequest () { - devname = 0; - tagname = 0; - mode = 0; - type = Port::Unknown; - status = Unknown; - } - - PortRequest (const std::string &xdev, - const std::string &xtag, - const std::string &xmode, - const std::string &xtype); -}; - -struct PortSet { - PortSet (std::string str) : owner (str) { } - - std::string owner; - std::list<PortRequest> ports; -}; - -} // namespace MIDI - -#endif // __midi_port_request_h__ - diff --git a/libs/midi++2/midifactory.cc b/libs/midi++2/midifactory.cc index 9d98d9f6a7..d893cc9f47 100644 --- a/libs/midi++2/midifactory.cc +++ b/libs/midi++2/midifactory.cc @@ -24,7 +24,6 @@ #include <midi++/types.h> #include <midi++/factory.h> -#include <midi++/nullmidi.h> #include <midi++/fifomidi.h> #ifdef WITH_JACK_MIDI @@ -33,7 +32,6 @@ std::string MIDI::JACK_MidiPort::typestring = "jack"; #endif // WITH_JACK_MIDI -std::string MIDI::Null_MidiPort::typestring = "null"; std::string MIDI::FIFO_MidiPort::typestring = "fifo"; #ifdef WITH_ALSA @@ -58,50 +56,44 @@ using namespace PBD; // FIXME: void* data pointer, filthy Port * -PortFactory::create_port (PortRequest &req, void* data) +PortFactory::create_port (const XMLNode& node, void* data) { + Port::Descriptor desc (node); Port *port; - switch (req.type) { + switch (desc.type) { #ifdef WITH_JACK_MIDI case Port::JACK_Midi: assert(data != NULL); - port = new JACK_MidiPort (req, (jack_client_t*)data); + port = new JACK_MidiPort (node, (jack_client_t*) data); break; #endif // WITH_JACK_MIDI #ifdef WITH_ALSA case Port::ALSA_RawMidi: - port = new ALSA_RawMidiPort (req); + port = new ALSA_RawMidiPort (node); break; case Port::ALSA_Sequencer: - port = new ALSA_SequencerMidiPort (req); + port = new ALSA_SequencerMidiPort (node); break; #endif // WITH_ALSA #if WITH_COREMIDI case Port::CoreMidi_MidiPort: - port = new CoreMidi_MidiPort (req); + port = new CoreMidi_MidiPort (node); break; #endif // WITH_COREMIDI - case Port::Null: - port = new Null_MidiPort (req); - break; - case Port::FIFO: - port = new FIFO_MidiPort (req); + port = new FIFO_MidiPort (node); break; default: - req.status = PortRequest::TypeUnsupported; return 0; } - req.status = PortRequest::OK; - return port; } @@ -179,8 +171,6 @@ PortFactory::string_to_type (const string& xtype) } else if (strings_equal_ignore_case (xtype, CoreMidi_MidiPort::typestring)) { return Port::CoreMidi_MidiPort; #endif - } else if (strings_equal_ignore_case (xtype, Null_MidiPort::typestring)) { - return Port::Null; } else if (strings_equal_ignore_case (xtype, FIFO_MidiPort::typestring)) { return Port::FIFO; #ifdef WITH_JACK_MIDI diff --git a/libs/midi++2/midimanager.cc b/libs/midi++2/midimanager.cc index ee73bdad86..8a358c3183 100644 --- a/libs/midi++2/midimanager.cc +++ b/libs/midi++2/midimanager.cc @@ -27,21 +27,25 @@ #include <midi++/manager.h> #include <midi++/factory.h> #include <midi++/channel.h> -#include <midi++/port_request.h> using namespace std; using namespace MIDI; using namespace PBD; +/* XXX check for strdup leaks */ + Manager *Manager::theManager = 0; Manager::Manager () - : api_data(NULL) { + inputPort = 0; + outputPort = 0; + inputChannelNumber = 0; + outputChannelNumber = 0; + api_data = 0; } Manager::~Manager () - { PortMap::iterator i; @@ -58,27 +62,27 @@ Manager::~Manager () } Port * -Manager::add_port (PortRequest &req) - +Manager::add_port (const XMLNode& node) { + Port::Descriptor desc (node); PortFactory factory; Port *port; PortMap::iterator existing; pair<string, Port *> newpair; - if (!PortFactory::ignore_duplicate_devices (req.type)) { + if (!PortFactory::ignore_duplicate_devices (desc.type)) { - if ((existing = ports_by_device.find (req.devname)) != ports_by_device.end()) { + if ((existing = ports_by_device.find (desc.device)) != ports_by_device.end()) { port = (*existing).second; - if (port->mode() == req.mode) { + if (port->mode() == desc.mode) { /* Same mode - reuse the port, and just create a new tag entry. */ - newpair.first = req.tagname; + newpair.first = desc.tag; newpair.second = port; ports_by_tag.insert (newpair); @@ -91,10 +95,10 @@ Manager::add_port (PortRequest &req) operation. */ - if ((req.mode == O_RDWR && port->mode() != O_RDWR) || - (req.mode != O_RDWR && port->mode() == O_RDWR)) { + if ((desc.mode == O_RDWR && port->mode() != O_RDWR) || + (desc.mode != O_RDWR && port->mode() == O_RDWR)) { error << "MIDIManager: port tagged \"" - << req.tagname + << desc.tag << "\" cannot be opened duplex and non-duplex" << endmsg; return 0; @@ -103,7 +107,8 @@ Manager::add_port (PortRequest &req) /* modes must be different or complementary */ } } - port = factory.create_port (req, api_data); + + port = factory.create_port (node, api_data); if (port == 0) { return 0; @@ -122,6 +127,18 @@ Manager::add_port (PortRequest &req) newpair.second = port; ports_by_device.insert (newpair); + /* first port added becomes the default input + port. + */ + + if (inputPort == 0) { + inputPort = port; + } + + if (outputPort == 0) { + outputPort = port; + } + return port; } @@ -156,119 +173,88 @@ Manager::remove_port (Port* port) return 0; } -Port * -Manager::port (string name) +int +Manager::set_input_port (string tag) { PortMap::iterator res; + bool found = false; for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) { - if (name == (*res).first) { - return (*res).second; + if (tag == (*res).first) { + found = true; + break; } } + + if (!found) { + return -1; + } + + inputPort = (*res).second; return 0; } int -Manager::foreach_port (int (*func)(const Port &, size_t, void *), - void *arg) +Manager::set_output_port (string tag) { - PortMap::const_iterator i; - int retval; - int n; - - for (n = 0, i = ports_by_device.begin(); - i != ports_by_device.end(); i++, n++) { + PortMap::iterator res; + bool found = false; - if ((retval = func (*((*i).second), n, arg)) != 0) { - return retval; + for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) { + if (tag == (*res).first) { + found = true; + break; } } - - return 0; -} - -int -Manager::parse_port_request (string str, Port::Type type) -{ - PortRequest *req; - string::size_type colon; - string tag; - - if (str.length() == 0) { - error << "MIDI: missing port specification" << endmsg; + + if (!found) { return -1; } - /* Port specifications look like: + // XXX send a signal to say we're about to change output ports - devicename - devicename:tagname - devicename:tagname:mode + if (outputPort) { + for (channel_t chan = 0; chan < 16; chan++) { + outputPort->channel (chan)->all_notes_off (0); + } + } + outputPort = (*res).second; - where + // XXX send a signal to say we've changed output ports - "devicename" is the full path to the requested file - - "tagname" (optional) is the name used to refer to the - port. If not given, g_path_get_basename (devicename) - will be used. + return 0; +} - "mode" (optional) is either "r" or "w" or something else. - if it is "r", the port will be opened - read-only, if "w", the port will be opened - write-only. Any other value, or no mode - specification at all, will cause the port to - be opened for reading and writing. - */ - - req = new PortRequest; - colon = str.find_first_of (':'); +Port * +Manager::port (string name) +{ + PortMap::iterator res; - if (colon != string::npos) { - req->devname = strdup (str.substr (0, colon).c_str()); - } else { - req->devname = strdup (str.c_str()); + for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) { + if (name == (*res).first) { + return (*res).second; + } } - if (colon < str.length()) { - - tag = str.substr (colon+1); + return 0; +} - /* see if there is a mode specification in the tag part */ +int +Manager::foreach_port (int (*func)(const Port &, size_t, void *), + void *arg) +{ + PortMap::const_iterator i; + int retval; + int n; - colon = tag.find_first_of (':'); - - if (colon != string::npos) { - string modestr; - - req->tagname = strdup (tag.substr (0, colon).c_str()); - - modestr = tag.substr (colon+1); - if (modestr == "r") { - req->mode = O_RDONLY; - } else if (modestr == "w") { - req->mode = O_WRONLY; - } else { - req->mode = O_RDWR; - } + for (n = 0, i = ports_by_device.begin(); + i != ports_by_device.end(); i++, n++) { - } else { - req->tagname = strdup (tag.c_str()); - req->mode = O_RDWR; + if ((retval = func (*((*i).second), n, arg)) != 0) { + return retval; } - - } else { - req->tagname = g_path_get_basename (req->devname); - req->mode = O_RDWR; - } - - req->type = type; - - if (MIDI::Manager::instance()->add_port (*req) == 0) { - return -1; } return 0; @@ -277,16 +263,22 @@ Manager::parse_port_request (string str, Port::Type type) void Manager::cycle_start(nframes_t nframes) { - for (PortMap::iterator i = ports_by_device.begin(); - i != ports_by_device.end(); i++) - (*i).second->cycle_start(nframes); + for (PortMap::iterator i = ports_by_device.begin(); i != ports_by_device.end(); i++) { + (*i).second->cycle_start (nframes); + } } void Manager::cycle_end() { - for (PortMap::iterator i = ports_by_device.begin(); - i != ports_by_device.end(); i++) - (*i).second->cycle_end(); + for (PortMap::iterator i = ports_by_device.begin(); i != ports_by_device.end(); i++) { + (*i).second->cycle_end (); + } } + +int +Manager::get_known_ports (vector<PortSet>& ports) +{ + return PortFactory::get_known_ports (ports); +} diff --git a/libs/midi++2/midiport.cc b/libs/midi++2/midiport.cc index 7f31b909d3..2e3d36c19c 100644 --- a/libs/midi++2/midiport.cc +++ b/libs/midi++2/midiport.cc @@ -17,24 +17,29 @@ $Id$ */ - +#include <iostream> #include <cstdio> #include <fcntl.h> +#include <pbd/xml++.h> +#include <pbd/failed_constructor.h> + #include <midi++/types.h> #include <midi++/port.h> #include <midi++/channel.h> -#include <midi++/port_request.h> +#include <midi++/factory.h> -using namespace Select; using namespace MIDI; +using namespace std; size_t Port::nports = 0; -Port::Port (PortRequest &req) +Port::Port (const XMLNode& node) : _currently_in_cycle(false) , _nframes_this_cycle(0) { + Descriptor desc (node); + _ok = false; /* derived class must set to true if constructor succeeds. */ @@ -45,10 +50,9 @@ Port::Port (PortRequest &req) output_parser = 0; slowdown = 0; - _devname = req.devname; - _tagname = req.tagname; - _mode = req.mode; - _number = nports++; + _devname = desc.device; + _tagname = desc.tag; + _mode = desc.mode; if (_mode == O_RDONLY || _mode == O_RDWR) { input_parser = new Parser (*this); @@ -77,7 +81,6 @@ Port::Port (PortRequest &req) Port::~Port () - { for (int i = 0; i < 16; i++) { delete _channel[i]; @@ -113,3 +116,87 @@ Port::cycle_end () _nframes_this_cycle = 0; } +XMLNode& +Port::get_state () const +{ + XMLNode* node = new XMLNode ("MIDI-port"); + node->add_property ("tag", _tagname); + node->add_property ("device", _devname); + node->add_property ("mode", PortFactory::mode_to_string (_mode)); + node->add_property ("type", get_typestring()); + + return *node; +} + +void +Port::set_state (const XMLNode& node) +{ + // relax +} + +void +Port::gtk_read_callback (void *ptr, int fd, int cond) +{ + byte buf[64]; + + ((Port *)ptr)->read (buf, sizeof (buf), 0); +} + +void +Port::write_callback (byte *msg, unsigned int len, void *ptr) + +{ + ((Port *)ptr)->write (msg, len, 0); +} + +std::ostream & MIDI::operator << ( std::ostream & os, const MIDI::Port & port ) +{ + using namespace std; + os << "MIDI::Port { "; + os << "device: " << port.device(); + os << "; "; + os << "name: " << port.name(); + os << "; "; + os << "type: " << port.type(); + os << "; "; + os << "mode: " << port.mode(); + os << "; "; + os << "ok: " << port.ok(); + os << "; "; + os << " }"; + return os; +} + +Port::Descriptor::Descriptor (const XMLNode& node) +{ + const XMLProperty *prop; + bool have_tag = false; + bool have_device = false; + bool have_type = false; + bool have_mode = false; + + if ((prop = node.property ("tag")) != 0) { + tag = prop->value(); + have_tag = true; + } + + if ((prop = node.property ("device")) != 0) { + device = prop->value(); + have_device = true; + } + + if ((prop = node.property ("type")) != 0) { + type = PortFactory::string_to_type (prop->value()); + have_type = true; + } + + if ((prop = node.property ("mode")) != 0) { + mode = PortFactory::string_to_mode (prop->value()); + have_mode = true; + } + + if (!have_tag || !have_device || !have_type || !have_mode) { + throw failed_constructor(); + } +} + diff --git a/libs/midi++2/miditrace.cc b/libs/midi++2/miditrace.cc index d7c65d9f29..fafe822f82 100644 --- a/libs/midi++2/miditrace.cc +++ b/libs/midi++2/miditrace.cc @@ -11,7 +11,6 @@ Transmitter fatal (Transmitter::Fatal); TextReceiver text_receiver ("mmctest"); #include "midi++/port.h" -#include "midi++/port_request.h" #include "midi++/manager.h" using namespace MIDI; diff --git a/libs/midi++2/mmctest.cc b/libs/midi++2/mmctest.cc index 36fbd61124..062f6e8d32 100644 --- a/libs/midi++2/mmctest.cc +++ b/libs/midi++2/mmctest.cc @@ -11,7 +11,6 @@ Transmitter fatal (Transmitter::Fatal); TextReceiver text_receiver ("mmctest"); #include "midi++/port.h" -#include "midi++/port_request.h" #include "midi++/manager.h" #include "midi++/mmc.h" diff --git a/libs/midi++2/port_request.cc b/libs/midi++2/port_request.cc deleted file mode 100644 index d209f02574..0000000000 --- a/libs/midi++2/port_request.cc +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright (C) 2000 Paul Barton-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 <fcntl.h> -#include <string.h> -#include <midi++/port.h> -#include <midi++/port_request.h> - -using namespace std; -using namespace MIDI; - -PortRequest::PortRequest (const string &xdev, - const string &xtag, - const string &xmode, - const string &xtype) - -{ - status = OK; - - devname = strdup (xdev.c_str()); - tagname = strdup (xtag.c_str()); - - if (xmode == "output" || - xmode == "out" || - xmode == "OUTPUT" || - xmode == "OUT") { - mode = O_WRONLY; - - } else if (xmode == "input" || - xmode == "in" || - xmode == "INPUT" || - xmode == "IN") { - mode = O_RDONLY; - - } else if (xmode == "duplex" || - xmode == "DUPLEX" || - xmode == "inout" || - xmode == "INOUT") { - mode = O_RDWR; - } else { - status = Unknown; - } - - if (xtype == "JACK" || - xtype == "jack") { - type = Port::JACK_Midi; - } else if (xtype == "ALSA/RAW" || - xtype == "alsa/raw") { - type = Port::ALSA_RawMidi; - } else if (xtype == "ALSA/SEQUENCER" || - xtype == "alsa/sequencer") { - type = Port::ALSA_Sequencer; - } else if (xtype == "COREMIDI" || - xtype == "coremidi") { - type = Port::CoreMidi_MidiPort; - } else if (xtype == "NULL" || - xtype == "null") { - type = Port::Null; - } else if (xtype == "FIFO" || - xtype == "fifo") { - type = Port::FIFO; - } else { - status = Unknown; - } -} - diff --git a/libs/pbd/SConscript b/libs/pbd/SConscript index 303ac84552..d513dfc762 100644 --- a/libs/pbd/SConscript +++ b/libs/pbd/SConscript @@ -31,6 +31,7 @@ filesystem_paths.cc file_utils.cc fpu.cc id.cc +misc.c mountpoint.cc pathscanner.cc pool.cc diff --git a/libs/pbd/convert.cc b/libs/pbd/convert.cc index 07fcc09ace..2ce99ba631 100644 --- a/libs/pbd/convert.cc +++ b/libs/pbd/convert.cc @@ -30,6 +30,7 @@ using std::string; using std::vector; +using Glib::ustring; namespace PBD { @@ -194,6 +195,52 @@ url_decode (string& url) } } +void +url_decode (ustring& url) +{ + ustring::iterator last; + ustring::iterator next; + + for (ustring::iterator i = url.begin(); i != url.end(); ++i) { + if ((*i) == '+') { + next = i; + ++next; + url.replace (i, next, 1, ' '); + } + } + + if (url.length() <= 3) { + return; + } + + last = url.end(); + + --last; /* points at last char */ + --last; /* points at last char - 1 */ + + for (ustring::iterator i = url.begin(); i != last; ) { + + if (*i == '%') { + + next = i; + + url.erase (i); + + i = next; + ++next; + + if (isxdigit (*i) && isxdigit (*next)) { + /* replace first digit with char */ + url.replace (i, next, 1, (gunichar) int_from_hex (*i,*next)); + ++i; /* points at 2nd of 2 digits */ + url.erase (i); + } + } else { + ++i; + } + } +} + #if 0 string length2string (const int32_t frames, const float sample_rate) diff --git a/libs/pbd/file_utils.cc b/libs/pbd/file_utils.cc index efb065fbd4..f8dfe269c5 100644 --- a/libs/pbd/file_utils.cc +++ b/libs/pbd/file_utils.cc @@ -102,14 +102,6 @@ find_file_in_search_path(const SearchPath& search_path, if (tmp.size() == 0) { - info << string_compose - ( - "Found no file named %1 in search path %2", - filename, - search_path.to_string () - ) - << endmsg; - return false; } diff --git a/libs/pbd/misc.c b/libs/pbd/misc.c new file mode 100644 index 0000000000..797be5de45 --- /dev/null +++ b/libs/pbd/misc.c @@ -0,0 +1,21 @@ +#include <pbd/misc.h> + +#ifdef GTKOSX +#include <AppKit/AppKit.h> +#endif + +void +disable_screen_updates () +{ +#ifdef GTKOSX + NSDisableScreenUpdates (); +#endif +} + +void +enable_screen_updates () +{ +#ifdef GTKOSX + NSEnableScreenUpdates(); +#endif +} diff --git a/libs/pbd/pbd/convert.h b/libs/pbd/pbd/convert.h index 00176659cf..83cd285098 100644 --- a/libs/pbd/pbd/convert.h +++ b/libs/pbd/pbd/convert.h @@ -22,6 +22,9 @@ #include <string> #include <vector> +#include <sstream> +#include <iostream> +#include <glibmm/ustring.h> namespace PBD { @@ -30,6 +33,7 @@ std::string short_version (std::string, std::string::size_type target_length); int atoi (const std::string&); double atof (const std::string&); void url_decode (std::string&); +void url_decode (Glib::ustring&); // std::string length2string (const int32_t frames, const float sample_rate); std::string length2string (const int64_t frames, const double sample_rate); @@ -37,6 +41,14 @@ std::string length2string (const int64_t frames, const double sample_rate); std::vector<std::string> internationalize (const char *, const char **); bool strings_equal_ignore_case (const std::string& a, const std::string& b); +template <class T> std::string +to_string (T t, std::ios_base & (*f)(std::ios_base&)) +{ + std::ostringstream oss; + oss << f << t; + return oss.str(); +} + } //namespace PBD #endif /* __pbd_convert_h__ */ diff --git a/libs/pbd/pbd/functor_command.h b/libs/pbd/pbd/functor_command.h new file mode 100644 index 0000000000..e335f4418e --- /dev/null +++ b/libs/pbd/pbd/functor_command.h @@ -0,0 +1,121 @@ +/* + 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 __lib_pbd_functor_command_h__ +#define __lib_pbd_functor_command_h__ + +#include <iostream> +#include <sstream> +#include <string> +#include <map> + +#include <pbd/xml++.h> +#include <pbd/shiva.h> +#include <pbd/command.h> +#include <pbd/failed_constructor.h> + +/** This command class is initialized + */ + +namespace PBD { + +template <class obj_type, class arg_type> +class FunctorCommand : public Command +{ + private: + typedef void (obj_type::*functor_type)(arg_type); + typedef std::map< std::string, functor_type > FunctorMap; + typedef typename FunctorMap::iterator FunctorMapIterator; + + public: + FunctorCommand( + std::string functor, + obj_type object, + arg_type b, + arg_type a + ) : functor_name(functor), + object(object), + before(b), + after(a) + { + method = find_functor(functor); + + /* catch destruction of the object */ + new PBD::Shiva< obj_type, FunctorCommand<obj_type, arg_type> > (object, *this); + } + + ~FunctorCommand() { + GoingAway(); + } + + void operator() () { + (object.*method) (after); + } + + void undo() { + (object.*method) (before); + } + + virtual XMLNode &get_state() { + std::stringstream ss; + + XMLNode *node = new XMLNode("FunctorCommand"); + node->add_property("functor", functor_name); + ss << before; + node->add_property("before", ss.str()); + ss.clear (); + ss << after; + node->add_property("after", ss.str()); + + return *node; + } + + static void register_functor(std::string name, functor_type f) { + functor_map[name] = f; + } + + private: + static functor_type find_functor(std::string name) { + FunctorMapIterator iter; + + if((iter = functor_map.find(name)) == functor_map.end()) { + throw failed_constructor(); + } + + return iter->second; + } + + protected: + std::string functor_name; + obj_type &object; + arg_type before; + arg_type after; + functor_type method; + static FunctorMap functor_map; +}; + +// static initialization of functor_map... +template <class obj_type, class arg_type> +typename FunctorCommand<obj_type, arg_type>::FunctorMap +FunctorCommand<obj_type, arg_type>::functor_map; + +}; + +#endif // __lib_pbd_functor_command_h__ + diff --git a/libs/pbd/pbd/misc.h b/libs/pbd/pbd/misc.h new file mode 100644 index 0000000000..306c00683e --- /dev/null +++ b/libs/pbd/pbd/misc.h @@ -0,0 +1,15 @@ +#ifndef __pbd_misc_h__ +#define __pbd_misc_h__ + +#ifdef __cplusplus +extern "C" { +#endif + + void disable_screen_updates (); + void enable_screen_updates (); + +#ifdef __cplusplus +} +#endif + +#endif /* __pbd_misc_h__ */ diff --git a/libs/pbd/pbd/undo.h b/libs/pbd/pbd/undo.h index 5bfccf5a06..8f1716d09f 100644 --- a/libs/pbd/pbd/undo.h +++ b/libs/pbd/pbd/undo.h @@ -80,20 +80,24 @@ class UndoHistory : public sigc::trackable unsigned long undo_depth() const { return UndoList.size(); } unsigned long redo_depth() const { return RedoList.size(); } - std::string next_undo() const { return (UndoList.empty() ? std::string("") : UndoList.back()->name()); } - std::string next_redo() const { return (RedoList.empty() ? std::string("") : RedoList.back()->name()); } + std::string next_undo() const { return (UndoList.empty() ? std::string() : UndoList.back()->name()); } + std::string next_redo() const { return (RedoList.empty() ? std::string() : RedoList.back()->name()); } void clear (); void clear_undo (); void clear_redo (); - XMLNode &get_state(uint32_t depth = 0); - void save_state(); + XMLNode &get_state(int32_t depth = 0); + void save_state(); - sigc::signal<void> Changed; + void set_depth (int32_t); + int32_t get_depth() const { return _depth; } + sigc::signal<void> Changed; + private: bool _clearing; + int32_t _depth; std::list<UndoTransaction*> UndoList; std::list<UndoTransaction*> RedoList; diff --git a/libs/pbd/undo.cc b/libs/pbd/undo.cc index 6db85e6ab3..aeff37cce7 100644 --- a/libs/pbd/undo.cc +++ b/libs/pbd/undo.cc @@ -148,12 +148,28 @@ XMLNode &UndoTransaction::get_state() UndoHistory::UndoHistory () { _clearing = false; + _depth = 0; +} + +void +UndoHistory::set_depth (int32_t d) +{ + _depth = d; + + while (_depth > 0 && UndoList.size() > (uint32_t) _depth) { + UndoList.pop_front (); + } } void UndoHistory::add (UndoTransaction* const ut) { ut->GoingAway.connect (bind (mem_fun (*this, &UndoHistory::remove), ut)); + + while (_depth > 0 && UndoList.size() > (uint32_t) _depth) { + UndoList.pop_front (); + } + UndoList.push_back (ut); /* we are now owners of the transaction */ @@ -240,17 +256,22 @@ UndoHistory::clear () } XMLNode& -UndoHistory::get_state (uint32_t depth) +UndoHistory::get_state (int32_t depth) { XMLNode *node = new XMLNode ("UndoHistory"); if (depth == 0) { + + return (*node); + + } else if (depth < 0) { + /* everything */ for (list<UndoTransaction*>::iterator it = UndoList.begin(); it != UndoList.end(); ++it) { node->add_child_nocopy((*it)->get_state()); } - + } else { /* just the last "depth" transactions */ @@ -268,3 +289,5 @@ UndoHistory::get_state (uint32_t depth) return *node; } + + diff --git a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc index 3906b53e36..93cbf088c7 100644 --- a/libs/surfaces/generic_midi/generic_midi_control_protocol.cc +++ b/libs/surfaces/generic_midi/generic_midi_control_protocol.cc @@ -27,7 +27,6 @@ #include <midi++/port.h> #include <midi++/manager.h> -#include <midi++/port_request.h> #include <ardour/route.h> #include <ardour/session.h> diff --git a/libs/surfaces/mackie/mackie_control_protocol_poll.cc b/libs/surfaces/mackie/mackie_control_protocol_poll.cc index 05681c0c25..fa7134fa95 100644 --- a/libs/surfaces/mackie/mackie_control_protocol_poll.cc +++ b/libs/surfaces/mackie/mackie_control_protocol_poll.cc @@ -9,7 +9,6 @@ #include <midi++/types.h> #include <midi++/port.h> #include <midi++/manager.h> -#include <midi++/port_request.h> #include "i18n.h" #include <unistd.h> diff --git a/libs/surfaces/powermate/powermate.cc b/libs/surfaces/powermate/powermate.cc index 139313f3f8..8b3051af20 100644 --- a/libs/surfaces/powermate/powermate.cc +++ b/libs/surfaces/powermate/powermate.cc @@ -14,12 +14,15 @@ #include <i18n.h> #include <pbd/xml++.h> +#include <pbd/error.h> +#include <glibmm.h> #include "powermate.h" using namespace ARDOUR; using namespace std; using namespace sigc; +using namespace PBD; #define NUM_VALID_PREFIXES 2 @@ -32,17 +35,22 @@ static const char *valid_prefix[NUM_VALID_PREFIXES] = { int open_powermate(const char *dev, int mode) { - int fd = open(dev, mode); - int i; - char name[255]; - - if(fd < 0){ - fprintf(stderr, "Unable to open \"%s\": %s\n", dev, strerror(errno)); - return -1; - } + if (!Glib::file_test (dev, Glib::FILE_TEST_EXISTS)) { + return -1; + } + int fd = open(dev, mode); + int i; + char name[255]; + + if(fd < 0){ + if (errno != EACCES) { + error << string_compose ("Unable to open \"%1\": %2", dev, strerror(errno)) << endmsg; + } + return -1; + } if(ioctl(fd, EVIOCGNAME(sizeof(name)), name) < 0){ - fprintf(stderr, "\"%s\": EVIOCGNAME failed: %s\n", dev, strerror(errno)); + error << string_compose ("\"%1\": EVIOCGNAME failed: %2", dev, strerror(errno)) << endmsg; close(fd); return -1; } |