RelativePath="..\gtk2_ardour\group_tabs.cc"
>
</File>
- <File
- RelativePath="..\gtk2_ardour\gtk-custom-hruler.c"
- >
- <FileConfiguration
- Name="Debug 32|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- CompileAs="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release 32|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- CompileAs="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release 32 with Debugging Capability|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- CompileAs="1"
- />
- </FileConfiguration>
- </File>
- <File
- RelativePath="..\gtk2_ardour\gtk-custom-ruler.c"
- >
- <FileConfiguration
- Name="Debug 32|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- CompileAs="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release 32|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- CompileAs="1"
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release 32 with Debugging Capability|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- CompileAs="1"
- />
- </FileConfiguration>
- </File>
<File
RelativePath="..\gtk2_ardour\gtk_pianokeyboard.c"
>
RelativePath="..\gtk2_ardour\route_ui.cc"
>
</File>
+ <File
+ RelativePath="..\gtk2_ardour\ruler_dialog.cc"
+ >
+ </File>
<File
RelativePath="..\gtk2_ardour\search_path_option.cc"
>
RelativePath="..\gtk2_ardour\shuttle_control.cc"
>
</File>
+ <File
+ RelativePath="..\gtk2_ardour\soundcloud_export_selector.cc"
+ >
+ </File>
<File
RelativePath="..\gtk2_ardour\speaker_dialog.cc"
>
RelativePath="..\gtk2_ardour\group_tabs.h"
>
</File>
- <File
- RelativePath="..\gtk2_ardour\gtk-custom-hruler.h"
- >
- </File>
- <File
- RelativePath="..\gtk2_ardour\gtk-custom-ruler.h"
- >
- </File>
<File
RelativePath="..\gtk2_ardour\gtk_pianokeyboard.h"
>
RelativePath="..\gtk2_ardour\route_ui_selection.h"
>
</File>
+ <File
+ RelativePath="..\gtk2_ardour\ruler_dialog.h"
+ >
+ </File>
<File
RelativePath="..\gtk2_ardour\search_path_option.h"
>
RelativePath="..\gtk2_ardour\shuttle_control.h"
>
</File>
+ <File
+ RelativePath="..\gtk2_ardour\soundcloud_export_selector.h"
+ >
+ </File>
<File
RelativePath="..\gtk2_ardour\speaker_dialog.h"
>
#include "ardour/version.h"
#include "ardour/filesystem_paths.h"
-#include "utils.h"
#include "version.h"
#include "about.h"
Searchpath spath(ardour_data_search_path());
- if (find_file_in_search_path (spath, "splash.png", splash_file)) {
+ if (find_file (spath, "splash.png", splash_file)) {
set_logo (Gdk::Pixbuf::create_from_file (splash_file));
} else {
error << "Could not find splash file" << endmsg;
#include "gtkmm2ext/actions.h"
-#include "utils.h"
#include "actions.h"
#include "i18n.h"
{
std::string ui_file;
- find_file_in_search_path (ardour_config_search_path(), menus_file, ui_file);
+ find_file (ardour_config_search_path(), menus_file, ui_file);
bool loaded = false;
using namespace std;
using namespace PBD;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
std::vector<std::string> AddRouteDialog::channel_combo_strings;
#include "ardour/session.h"
#include "ardour_ui.h"
-#include "utils.h"
#include "add_video_dialog.h"
#include "utils_videotl.h"
#include "i18n.h"
#include <string>
+#ifdef interface
+#undef interface
+#endif
+
#include <gtkmm.h>
#include "ardour/types.h"
export ARDOUR_MIDIMAPS_PATH=$TOP/midi_maps:.
export ARDOUR_MCP_PATH=$TOP/mcp:.
export ARDOUR_EXPORT_FORMATS_PATH=$TOP/export:.
-export ARDOUR_BACKEND_PATH=$libs/backends/jack:$libs/backends/wavesaudio:$libs/backends/dummy
+export ARDOUR_BACKEND_PATH=$libs/backends/jack:$libs/backends/wavesaudio:$libs/backends/dummy:$libs/backends/alsa
+export ARDOUR_TEST_PATH=$libs/ardour/test/data
#
# even though we set the above variables, ardour requires that these
export GTK_PATH=~/.ardour3:$libs/clearlooks-newer
export VAMP_PATH=$libs/vamp-plugins${VAMP_PATH:+:$VAMP_PATH}
-export LD_LIBRARY_PATH=$libs/qm-dsp:$libs/vamp-sdk:$libs/surfaces:$libs/surfaces/control_protocol:$libs/ardour:$libs/midi++2:$libs/pbd:$libs/rubberband:$libs/soundtouch:$libs/gtkmm2ext:$libs/gnomecanvas:$libs/libsndfile:$libs/appleutility:$libs/taglib:$libs/evoral:$libs/evoral/src/libsmf:$libs/audiographer:$libs/timecode:$libs/libltc:$libs/canvas${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+export LD_LIBRARY_PATH=$libs/qm-dsp:$libs/vamp-sdk:$libs/surfaces:$libs/surfaces/control_protocol:$libs/ardour:$libs/midi++2:$libs/pbd:$libs/rubberband:$libs/soundtouch:$libs/gtkmm2ext:$libs/gnomecanvas:$libs/libsndfile:$libs/appleutility:$libs/taglib:$libs/evoral:$libs/evoral/src/libsmf:$libs/audiographer:$libs/timecode:$libs/libltc:$libs/canvas:$libs/ardouralsautil${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
# DYLD_LIBRARY_PATH is for darwin.
export DYLD_FALLBACK_LIBRARY_PATH=$LD_LIBRARY_PATH
<accelerator action='track-mute-toggle'/>
<accelerator action='toggle-edit-mode'/>
<accelerator action='toggle-midi-input-active'/>
+#ifdef GTKOSX
+ <accelerator action='Quit'/>
+#endif
<menubar name='Main' action='MainMenu'>
<menu name='Session' action='Session'>
<menuitem action='toggle-about'/>
<menuitem action='toggle-rc-options-editor'/>
#endif
+ <separator/>
+ <menuitem action='lock'/>
#ifndef GTKOSX
<separator/>
<menuitem action='Quit'/>
<menuitem action='combine-regions'/>
<menuitem action='uncombine-regions'/>
<menuitem action='analyze-region'/>
- <menuitem action='toggle-opaque-region'/>
- <menuitem action='toggle-region-mute'/>
<menuitem action='pitch-shift-region'/>
<menuitem action='split-region'/>
<menuitem action='split-multichannel-region'/>
<menuitem action='show-region-list-editor'/>
</menu>
<menu action='RegionMenuGain'>
+ <menuitem action='toggle-opaque-region'/>
+ <menuitem action='toggle-region-mute'/>
<menuitem action='normalize-region'/>
<menuitem action='boost-region-gain'/>
<menuitem action='cut-region-gain'/>
<menuitem action='uncombine-regions'/>
<menuitem action='split-region'/>
<menuitem action='split-multichannel-region'/>
- <menuitem action='toggle-opaque-region'/>
- <menuitem action='toggle-region-mute'/>
<menuitem action='pitch-shift-region'/>
<menuitem action='reverse-region'/>
<menuitem action='close-region-gaps'/>
<menuitem action='set-selection-from-region'/>
</menu>
<menu action='RegionMenuGain'>
+ <menuitem action='toggle-opaque-region'/>
+ <menuitem action='toggle-region-mute'/>
<menuitem action='normalize-region'/>
<menuitem action='boost-region-gain'/>
<menuitem action='cut-region-gain'/>
# and does a few checks before exec'ing the real executable.
#
-export GTK_PATH=@SYSCONFDIR@/ardour3:@LIBDIR@/ardour3${GTK_PATH:+:$GTK_PATH}
+export GTK_PATH=@CONFDIR@:@LIBDIR@${GTK_PATH:+:$GTK_PATH}
-export LD_LIBRARY_PATH=@LIBDIR@/ardour3${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+export LD_LIBRARY_PATH=@LIBDIR@${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
## Memlock check
## Glib atomic test
-GLIB=$(ldd @LIBDIR@/ardour3/ardour-@VERSION@ 2> /dev/null | grep glib-2.0 | sed 's/.*=> \([^ ]*\) .*/\1/')
+GLIB=$(ldd @LIBDIR@/ardour-@VERSION@ 2> /dev/null | grep glib-2.0 | sed 's/.*=> \([^ ]*\) .*/\1/')
if [ "$GLIB" = "" ]; then
echo "WARNING: Could not check your glib-2.0 for mutex locking atomic operations."
# Running Ardour requires these 3 variables to be set
#
-export ARDOUR_DATA_PATH=@DATADIR@/ardour3
-export ARDOUR_CONFIG_PATH=@SYSCONFDIR@/ardour3
-export ARDOUR_DLL_PATH=@LIBDIR@/ardour3
+export ARDOUR_DATA_PATH=@DATADIR@
+export ARDOUR_CONFIG_PATH=@CONFDIR@
+export ARDOUR_DLL_PATH=@LIBDIR@
#
# VAMP has its own lookup path
#
-export VAMP_PATH=@LIBDIR@/ardour3/vamp
+export VAMP_PATH=@LIBDIR@/vamp
if [ $# -gt 0 ] ; then
case $1 in
esac
fi
-exec $GDB @LIBDIR@/ardour3/ardour-@VERSION@ "$@"
+exec $GDB @LIBDIR@/ardour-@VERSION@ "$@"
{
}
+style "midi_device" = "very_small_text"
+{
+}
+
style "solo_isolate" = "very_small_text"
{
}
{
}
+style "tracknumber_label" = "medium_monospace_text"
+{
+}
+
style "solo_button" = "small_button"
{
bg[NORMAL] = mix(0.1,@@COLPREFIX@_solo,@@COLPREFIX@_bg)
bg[ACTIVE] = @@COLPREFIX@_base
}
-style "editor_time_ruler" = "small_text"
-{
- fg[NORMAL] = @@COLPREFIX@_fg
- bg[NORMAL] = @@COLPREFIX@_base
- ythickness = 0
-}
-
style "audio_bus_base" = "very_small_text"
{
fg[NORMAL] = @@COLPREFIX@_fg
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<Ardour>
- <UI>
- <Option name="ui-rc-file" value="ardour3_ui_dark.rc"/>
- <Option name="flat-buttons" value="00000000"/>
- <Option name="waveform-gradient-depth" value="0"/>
- <Option name="timeline-item-gradient-depth" value="00000.95"/>
- <Option name="all-floating-windows-are-dialogs" value="00000000"/>
- <Option name="color-regions-using-track-color" value="00000000"/>
- <Option name="show-waveform-clipping" value="00000001"/>
- </UI>
- <Canvas>
- <Option name="active crossfade" value="20b2af2e"/>
- <Option name="audio bus base" value="73829968"/>
- <Option name="audio master bus base" value="00000000"/>
- <Option name="audio track base" value="9daac468"/>
- <Option name="automation line" value="44bc59ff"/>
- <Option name="automation track fill" value="a0a0ce68"/>
- <Option name="automation track outline" value="282828ff"/>
- <Option name="cd marker bar" value="9496a3cc"/>
- <Option name="crossfade editor base" value="282d49ff"/>
- <Option name="crossfade editor line" value="000000ff"/>
- <Option name="crossfade editor line shading" value="00a0d154"/>
- <Option name="crossfade editor point fill" value="00ff00ff"/>
- <Option name="crossfade editor point outline" value="0000ffff"/>
- <Option name="crossfade editor wave" value="ffffff28"/>
- <Option name="selected crossfade editor wave fill" value="00000000"/>
- <Option name="crossfade line" value="000000ff"/>
- <Option name="edit point" value="0000ffff"/>
- <Option name="entered automation line" value="dd6363ff"/>
- <Option name="control point fill" value="ffffff66"/>
- <Option name="control point outline" value="ff0000ee"/>
- <Option name="control point selected" value="55ccccff"/>
- <Option name="entered gain line" value="dd6363ff"/>
- <Option name="entered marker" value="dd6363ff"/>
- <Option name="frame handle" value="7c00ff96"/>
- <Option name="gain line" value="00bc20ff"/>
- <Option name="gain line inactive" value="9fbca4c5"/>
- <Option name="ghost track base" value="44007c7f"/>
- <Option name="ghost track midi outline" value="00000000"/>
- <Option name="ghost track wave" value="02fd004c"/>
- <Option name="ghost track wave fill" value="00000000"/>
- <Option name="ghost track wave clip" value="ff000000"/>
- <Option name="ghost track zero line" value="e500e566"/>
- <Option name="image track" value="ddddd8ff"/>
- <Option name="inactive crossfade" value="e8ed3d77"/>
- <Option name="inactive fade handle" value="bbbbbbaa"/>
- <Option name="location cd marker" value="1ee8c4ff"/>
- <Option name="location loop" value="35964fff"/>
- <Option name="location marker" value="c4f411ff"/>
- <Option name="location punch" value="7c3a3aff"/>
- <Option name="location range" value="497a59ff"/>
- <Option name="marker bar" value="99a1adcc"/>
- <Option name="marker bar separator" value="555555ff"/>
- <Option name="marker drag line" value="004f00f9"/>
- <Option name="marker label" value="000000ff"/>
- <Option name="marker track" value="ddddd8ff"/>
- <Option name="measure line bar" value="ffffff9c"/>
- <Option name="measure line beat" value="a29e9e76"/>
- <Option name="meter bar" value="626470cc"/>
- <Option name="meter fill: 0" value="008800ff"/>
- <Option name="meter fill: 1" value="008800ff"/>
- <Option name="meter fill: 2" value="00ff00ff"/>
- <Option name="meter fill: 3" value="00ff00ff"/>
- <Option name="meter fill: 4" value="fff000ff"/>
- <Option name="meter fill: 5" value="fff000ff"/>
- <Option name="meter fill: 6" value="ff8000ff"/>
- <Option name="meter fill: 7" value="ff8000ff"/>
- <Option name="meter fill: 8" value="ff0000ff"/>
- <Option name="meter fill: 9" value="ff0000ff"/>
- <Option name="meter background: bottom" value="333333ff"/>
- <Option name="meter background: top" value="444444ff"/>
- <Option name="midi meter fill: 0" value="effaa1ff"/>
- <Option name="midi meter fill: 1" value="f2c97dff"/>
- <Option name="midi meter fill: 2" value="f2c97dff"/>
- <Option name="midi meter fill: 3" value="f48f52ff"/>
- <Option name="midi meter fill: 4" value="f48f52ff"/>
- <Option name="midi meter fill: 5" value="f83913ff"/>
- <Option name="midi meter fill: 6" value="f83913ff"/>
- <Option name="midi meter fill: 7" value="8fc78eff"/>
- <Option name="midi meter fill: 8" value="8fc78eff"/>
- <Option name="midi meter fill: 9" value="00f45600"/>
- <Option name="meter background: bottom" value="333333ff"/>
- <Option name="meter background: top" value="444444ff"/>
- <Option name="meterbridge peakindicator: fill start" value="444444ff"/>
- <Option name="meterbridge peakindicator: fill end" value="333333ff"/>
- <Option name="meterbridge peakindicator on: fill start" value="ff0000ff"/>
- <Option name="meterbridge peakindicator on: fill end" value="880000ff"/>
- <Option name="meterbridge label: fill start" value="444444ff"/>
- <Option name="meterbridge label: fill end" value="333333ff"/>
- <Option name="meterbridge label: text" value="c7c7d8ff"/>
- <Option name="meter marker" value="f2425bff"/>
- <Option name="midi bus base" value="00000000"/>
- <Option name="midi frame base" value="393d3766"/>
- <Option name="midi note inactive channel" value="00000000"/>
- <Option name="midi note color min" value="3f542aff"/>
- <Option name="midi note color mid" value="7ea854ff"/>
- <Option name="midi note color max" value="bfff80ff"/>
- <Option name="selected midi note color min" value="1e1e33ff"/>
- <Option name="selected midi note color mid" value="51518aff"/>
- <Option name="selected midi note color max" value="8383deff"/>
- <Option name="midi note selected" value="b2b2ffff"/>
- <Option name="midi note velocity text" value="f4f214bc"/>
- <Option name="midi patch change fill" value="50555aa0"/>
- <Option name="midi patch change outline" value="c0c5caff"/>
- <Option name="midi patch change inactive channel fill" value="50555ac0"/>
- <Option name="midi patch change inactive channel outline" value="20252ac0"/>
- <Option name="midi sysex fill" value="f1e139a0"/>
- <Option name="midi sysex outline" value="a7a7d4ff"/>
- <Option name="midi select rect fill" value="8888ff88"/>
- <Option name="midi select rect outline" value="5555ffff"/>
- <Option name="midi track base" value="b3cca35f"/>
- <Option name="name highlight fill" value="0000ffff"/>
- <Option name="name highlight outline" value="7c00ff96"/>
- <Option name="piano roll black outline" value="f4f4f476"/>
- <Option name="piano roll black" value="6c6e6a6b"/>
- <Option name="piano roll white" value="979b9565"/>
- <Option name="play head" value="ff0000ff"/>
- <Option name="processor automation line" value="7aa3f9ff"/>
- <Option name="punch line" value="a80000ff"/>
- <Option name="range drag bar rect" value="969696c6"/>
- <Option name="range drag rect" value="82c696c6"/>
- <Option name="range marker bar" value="7d7f8ccc"/>
- <Option name="recording rect" value="cc2828ff"/>
- <Option name="recorded waveform fill" value="ffffffff"/>
- <Option name="recorded waveform outline" value="0f0f1fff"/>
- <Option name="rubber band rect" value="c6c6c659"/>
- <Option name="selected crossfade editor line" value="00dbdbff"/>
- <Option name="selected crossfade editor wave" value="f9ea14a0"/>
- <Option name="selected region base" value="51518a97"/>
- <Option name="selected waveform fill" value="25e2e9c8"/>
- <Option name="selected waveform outline" value="0f0f0fcc"/>
- <Option name="selection rect" value="e8f4d377"/>
- <Option name="selection" value="636363b2"/>
- <Option name="shuttle" value="6bb620ff"/>
- <Option name="silence" value="9efffd7a"/>
- <Option name="silence text" value="0e066cff"/>
- <Option name="mono panner outline" value="33445eff"/>
- <Option name="mono panner fill" value="7a9bccc9"/>
- <Option name="mono panner text" value="000000ff"/>
- <Option name="mono panner bg" value="2e2929ff"/>
- <Option name="mono panner position fill" value="7a89b3ff"/>
- <Option name="mono panner position outline" value="33445eff"/>
- <Option name="stereo panner outline" value="33445eff"/>
- <Option name="stereo panner fill" value="7a9accc9"/>
- <Option name="stereo panner text" value="000000ff"/>
- <Option name="stereo panner bg" value="2e2929ff"/>
- <Option name="stereo panner rule" value="455c7fff"/>
- <Option name="stereo panner mono outline" value="a05600ff"/>
- <Option name="stereo panner mono fill" value="e99668ca"/>
- <Option name="stereo panner mono text" value="000000ff"/>
- <Option name="stereo panner mono bg" value="2e2929ff"/>
- <Option name="stereo panner inverted outline" value="bf0a00ff"/>
- <Option name="stereo panner inverted fill" value="e4a19cc9"/>
- <Option name="stereo panner inverted text" value="000000ff"/>
- <Option name="stereo panner inverted bg" value="2e2929ff"/>
- <Option name="tempo bar" value="70727fcc"/>
- <Option name="tempo marker" value="f2425bff"/>
- <Option name="time axis frame" value="000000ff"/>
- <Option name="selected time axis frame" value="000000ff"/>
- <Option name="time stretch fill" value="e2b5b596"/>
- <Option name="time stretch outline" value="63636396"/>
- <Option name="transport drag rect" value="969696c6"/>
- <Option name="transport loop rect" value="1e7728f9"/>
- <Option name="transport marker bar" value="8c8e98cc"/>
- <Option name="transport punch rect" value="6d2828e5"/>
- <Option name="trim handle locked" value="ea0f0f28"/>
- <Option name="trim handle" value="1900ff44"/>
- <Option name="verbose canvas cursor" value="fffd2ebc"/>
- <Option name="vestigial frame" value="0000000f"/>
- <Option name="video timeline bar" value="303030ff"/>
- <Option name="region base" value="99a7b5a0"/>
- <Option name="region area covered by another region" value="505050b0"/>
- <Option name="waveform outline" value="000000ff"/>
- <Option name="clipped waveform" value="ff0000e5"/>
- <Option name="waveform fill" value="ffffffff"/>
- <Option name="zero line" value="7f7f7f58"/>
- <Option name="zoom rect" value="c6d1b26d"/>
- <Option name="monitor knob" value="329edfff"/>
- <Option name="button border" value="000000f0"/>
- <Option name="border color" value="00000000"/>
- <Option name="processor prefader: fill start" value="873c3cff"/>
- <Option name="processor prefader: fill end" value="542525ff"/>
- <Option name="processor prefader: fill start active" value="774c4cff"/>
- <Option name="processor prefader: fill end active" value="603535ff"/>
- <Option name="processor prefader: led" value="26550eff"/>
- <Option name="processor prefader: led active" value="78cb4eff"/>
- <Option name="processor prefader: text" value="aaaaa3ff"/>
- <Option name="processor prefader: text active" value="eeeeecff"/>
- <Option name="processor fader: fill start" value="5d90b0ff"/>
- <Option name="processor fader: fill end" value="154c6eff"/>
- <Option name="processor fader: fill start active" value="5d90b0ff"/>
- <Option name="processor fader: fill end active" value="256d8fff"/>
- <Option name="processor fader: led" value="26550eff"/>
- <Option name="processor fader: led active" value="78cb4eff"/>
- <Option name="processor fader: text" value="aaaaa3ff"/>
- <Option name="processor fader: text active" value="eeeeecff"/>
- <Option name="processor postfader: fill start" value="354537ff"/>
- <Option name="processor postfader: fill end" value="202823ff"/>
- <Option name="processor postfader: fill start active" value="466452ff"/>
- <Option name="processor postfader: fill end active" value="254528ff"/>
- <Option name="processor postfader: led" value="26550eff"/>
- <Option name="processor postfader: led active" value="78cb4eff"/>
- <Option name="processor postfader: text" value="aaaaa3ff"/>
- <Option name="processor postfader: text active" value="eeeeecff"/>
- <Option name="processor control button: fill start" value="222222ff"/>
- <Option name="processor control button: fill end" value="333333ff"/>
- <Option name="processor control button: fill start active" value="444444ff"/>
- <Option name="processor control button: fill end active" value="333333ff"/>
- <Option name="processor control button: led" value="224400ff"/>
- <Option name="processor control button: led active" value="99cc00ff"/>
- <Option name="processor control button: text" value="ffffffff"/>
- <Option name="processor control button: text active" value="ffffffff"/>
- <Option name="monitor button: fill start" value="5f5a58ff"/>
- <Option name="monitor button: fill end" value="4f4a48ff"/>
- <Option name="monitor button: fill start active" value="553500ff"/>
- <Option name="monitor button: fill end active" value="e58505ff"/>
- <Option name="monitor button: led" value="660000ff"/>
- <Option name="monitor button: led active" value="ff0000ff"/>
- <Option name="monitor button: text" value="aaaaa3ff"/>
- <Option name="monitor button: text active" value="1a1a1aff"/>
- <Option name="meterbridge label: fill start" value="444444ff"/>
- <Option name="meterbridge label: fill end" value="333333ff"/>
- <Option name="meterbridge label: text" value="c7c7d8ff"/>
- <Option name="solo isolate: fill start" value="5f5a58ff"/>
- <Option name="solo isolate: fill end" value="504442ff"/>
- <Option name="solo isolate: fill start active" value="5d5856ff"/>
- <Option name="solo isolate: fill end active" value="564d48ff"/>
- <Option name="solo isolate: led" value="660000ff"/>
- <Option name="solo isolate: led active" value="ff0000ff"/>
- <Option name="solo isolate: text" value="c7c7d8ff"/>
- <Option name="solo isolate: text active" value="c8c8d9ff"/>
- <Option name="solo safe: fill start" value="5f5a58ff"/>
- <Option name="solo safe: fill end" value="504442ff"/>
- <Option name="solo safe: fill start active" value="5d5856ff"/>
- <Option name="solo safe: fill end active" value="564d48ff"/>
- <Option name="solo safe: led" value="660000ff"/>
- <Option name="solo safe: led active" value="ff0000ff"/>
- <Option name="solo safe: text" value="c7c7d8ff"/>
- <Option name="solo safe: text active" value="c8c8d9ff"/>
- <Option name="meterbridge peaklabel" value="ff1111ff"/>
- <Option name="meter color BBC" value="ffa500ff"/>
- <Option name="meterbridge peakindicator: fill start" value="444444ff"/>
- <Option name="meterbridge peakindicator: fill end" value="333333ff"/>
- <Option name="meterbridge peakindicator on: fill start" value="ff0000ff"/>
- <Option name="meterbridge peakindicator on: fill end" value="880000ff"/>
- <Option name="monitor section cut: fill start" value="5f5a58ff"/>
- <Option name="monitor section cut: fill end" value="4f4a48ff"/>
- <Option name="monitor section cut: fill start active" value="5f4943ff"/>
- <Option name="monitor section cut: fill end active" value="ffa500ff"/>
- <Option name="monitor section cut: led" value="473812ff"/>
- <Option name="monitor section cut: led active" value="78cb4eff"/>
- <Option name="monitor section cut: text" value="c7c7d8ff"/>
- <Option name="monitor section cut: text active" value="000000ff"/>
- <Option name="monitor section dim: fill start" value="5f5a58ff"/>
- <Option name="monitor section dim: fill end" value="4f4a48ff"/>
- <Option name="monitor section dim: fill start active" value="553500ff"/>
- <Option name="monitor section dim: fill end active" value="e58505ff"/>
- <Option name="monitor section dim: led" value="00000000"/>
- <Option name="monitor section dim: led active" value="78cb4eff"/>
- <Option name="monitor section dim: text" value="c8c8d9ff"/>
- <Option name="monitor section dim: text active" value="c8c8d9ff"/>
- <Option name="monitor section solo: fill start" value="5f5a58ff"/>
- <Option name="monitor section solo: fill end" value="4f4a48ff"/>
- <Option name="monitor section solo: fill start active" value="104506ff"/>
- <Option name="monitor section solo: fill end active" value="4dbb00ff"/>
- <Option name="monitor section solo: led" value="473812ff"/>
- <Option name="monitor section solo: led active" value="ffa500ff"/>
- <Option name="monitor section solo: text" value="00000000"/>
- <Option name="monitor section solo: text active" value="00000000"/>
- <Option name="monitor section invert: fill start" value="5f5a58ff"/>
- <Option name="monitor section invert: fill end" value="4f4a48ff"/>
- <Option name="monitor section invert: fill start active" value="222260ff"/>
- <Option name="monitor section invert: fill end active" value="4242d0ff"/>
- <Option name="monitor section invert: led" value="473812ff"/>
- <Option name="monitor section invert: led active" value="78cb4eff"/>
- <Option name="monitor section invert: text" value="00000000"/>
- <Option name="monitor section invert: text active" value="00000000"/>
- <Option name="monitor section mono: fill start" value="5f5a58ff"/>
- <Option name="monitor section mono: fill end" value="4f4a48ff"/>
- <Option name="monitor section mono: fill start active" value="222260ff"/>
- <Option name="monitor section mono: fill end active" value="3232c0ff"/>
- <Option name="monitor section mono: led" value="473812ff"/>
- <Option name="monitor section mono: led active" value="78cb4eff"/>
- <Option name="monitor section mono: text" value="c7c7d8ff"/>
- <Option name="monitor section mono: text active" value="c8c8d9ff"/>
- <Option name="monitor section solo model: fill start" value="5d5856ff"/>
- <Option name="monitor section solo model: fill end" value="564d48ff"/>
- <Option name="monitor section solo model: fill start active" value="5d5856ff"/>
- <Option name="monitor section solo model: fill end active" value="564d48ff"/>
- <Option name="monitor section solo model: led" value="4f3300ff"/>
- <Option name="monitor section solo model: led active" value="ffa500ff"/>
- <Option name="monitor section solo model: text" value="c7c7d8ff"/>
- <Option name="monitor section solo model: text active" value="c8c8d9ff"/>
- <Option name="monitor solo override: fill start" value="5d5856ff"/>
- <Option name="monitor solo override: fill end" value="564d48ff"/>
- <Option name="monitor solo override: fill start active" value="5d5856ff"/>
- <Option name="monitor solo override: fill end active" value="564d48ff"/>
- <Option name="monitor solo override: led" value="4f3300ff"/>
- <Option name="monitor solo override: led active" value="ffa500ff"/>
- <Option name="monitor solo override: text" value="c7c7d8ff"/>
- <Option name="monitor solo override: text active" value="c8c8d9ff"/>
- <Option name="monitor solo exclusive: fill start" value="5d5856ff"/>
- <Option name="monitor solo exclusive: fill end" value="564d48ff"/>
- <Option name="monitor solo exclusive: fill start active" value="5d5856ff"/>
- <Option name="monitor solo exclusive: fill end active" value="564c47ff"/>
- <Option name="monitor solo exclusive: led" value="4f3300ff"/>
- <Option name="monitor solo exclusive: led active" value="ffa500ff"/>
- <Option name="monitor solo exclusive: text" value="c7c7d8ff"/>
- <Option name="monitor solo exclusive: text active" value="c8c8d9ff"/>
- <Option name="rude solo: fill start" value="684d4dff"/>
- <Option name="rude solo: fill end" value="513c3cff"/>
- <Option name="rude solo: fill start active" value="ff1f1fff"/>
- <Option name="rude solo: fill end active" value="e21b1bff"/>
- <Option name="rude solo: led" value="00000000"/>
- <Option name="rude solo: led active" value="00000000"/>
- <Option name="rude solo: text" value="969696ff"/>
- <Option name="rude solo: text active" value="e5e5e5ff"/>
- <Option name="rude isolate: fill start" value="21414fff"/>
- <Option name="rude isolate: fill end" value="192930ff"/>
- <Option name="rude isolate: fill start active" value="e5f7ffff"/>
- <Option name="rude isolate: fill end active" value="b6e5fdff"/>
- <Option name="rude isolate: led" value="00000000"/>
- <Option name="rude isolate: led active" value="000000ff"/>
- <Option name="rude isolate: text" value="979797ff"/>
- <Option name="rude isolate: text active" value="000000ff"/>
- <Option name="rude audition: fill start" value="684d4dff"/>
- <Option name="rude audition: fill end" value="513c3cff"/>
- <Option name="rude audition: fill start active" value="ff1f1fff"/>
- <Option name="rude audition: fill end active" value="e21b1bff"/>
- <Option name="rude audition: led" value="00000000"/>
- <Option name="rude audition: led active" value="00000000"/>
- <Option name="rude audition: text" value="979797ff"/>
- <Option name="rude audition: text active" value="ffffffff"/>
- <Option name="feedback alert: fill start" value="684d4dff"/>
- <Option name="feedback alert: fill end" value="513c3cff"/>
- <Option name="feedback alert: fill start active" value="ff1f1fff"/>
- <Option name="feedback alert: fill end active" value="e21b1bff"/>
- <Option name="feedback alert: led" value="00000000"/>
- <Option name="feedback alert: led active" value="00000000"/>
- <Option name="feedback alert: text" value="969696ff"/>
- <Option name="feedback alert: text active" value="e5e5e5ff"/>
- <Option name="mute button: fill start" value="565659ff"/>
- <Option name="mute button: fill end" value="484853ff"/>
- <Option name="mute button: fill start active" value="5f4943ff"/>
- <Option name="mute button: fill end active" value="ffff00ff"/>
- <Option name="mute button: led" value="00000000"/>
- <Option name="mute button: led active" value="00000000"/>
- <Option name="mute button: text" value="bfbfafff"/>
- <Option name="mute button: text active" value="191919ff"/>
- <Option name="solo button: fill start" value="565659ff"/>
- <Option name="solo button: fill end" value="484853ff"/>
- <Option name="solo button: fill start active" value="1d7a05ff"/>
- <Option name="solo button: fill end active" value="4dbb00ff"/>
- <Option name="solo button: led" value="00000000"/>
- <Option name="solo button: led active" value="00000000"/>
- <Option name="solo button: text" value="afbfafff"/>
- <Option name="solo button: text active" value="191919ff"/>
- <Option name="invert button: fill start" value="565659ff"/>
- <Option name="invert button: fill end" value="484853ff"/>
- <Option name="invert button: fill start active" value="222260ff"/>
- <Option name="invert button: fill end active" value="4242d0ff"/>
- <Option name="invert button: led" value="473812ff"/>
- <Option name="invert button: led active" value="78cb4eff"/>
- <Option name="invert button: text" value="bfbfbfff"/>
- <Option name="invert button: text active" value="bfbfbfff"/>
- <Option name="record enable button: fill start" value="3e312fff"/>
- <Option name="record enable button: fill end" value="3f312fff"/>
- <Option name="record enable button: fill start active" value="c10b0bff"/>
- <Option name="record enable button: fill end active" value="fd0000ff"/>
- <Option name="record enable button: led" value="7b3541ff"/>
- <Option name="record enable button: led active" value="ffa3b3ff"/>
- <Option name="record enable button: text" value="a5a5a5ff"/>
- <Option name="record enable button: text active" value="000000ff"/>
- <Option name="generic button: fill start" value="3e312fff"/>
- <Option name="generic button: fill end" value="3f312fff"/>
- <Option name="generic button: fill start active" value="c10b0bff"/>
- <Option name="generic button: fill end active" value="fd0000ff"/>
- <Option name="generic button: led" value="7b3541ff"/>
- <Option name="generic button: led active" value="ffa3b3ff"/>
- <Option name="generic button: text" value="ff0000ff"/>
- <Option name="generic button: text active" value="000000ff"/>
- <Option name="send alert button: fill start" value="4e5647ff"/>
- <Option name="send alert button: fill end" value="43493cff"/>
- <Option name="send alert button: fill start active" value="91f928ff"/>
- <Option name="send alert button: fill end active" value="85e524ff"/>
- <Option name="send alert button: led" value="00000000"/>
- <Option name="send alert button: led active" value="00000000"/>
- <Option name="send alert button: text" value="ccccccff"/>
- <Option name="send alert button: text active" value="000000ff"/>
- <Option name="transport button: fill start" value="616268ff"/>
- <Option name="transport button: fill end" value="505159ff"/>
- <Option name="transport button: fill start active" value="1d7a05ff"/>
- <Option name="transport button: fill end active" value="00a300ff"/>
- <Option name="transport button: led" value="00000000"/>
- <Option name="transport button: led active" value="00000000"/>
- <Option name="transport button: text" value="00000000"/>
- <Option name="transport button: text active" value="00000000"/>
- <Option name="transport recenable button: fill start" value="5f3f3fff"/>
- <Option name="transport recenable button: fill end" value="3d2828ff"/>
- <Option name="transport recenable button: fill start active" value="6a0404ff"/>
- <Option name="transport recenable button: fill end active" value="b50e0eff"/>
- <Option name="transport recenable button: led" value="00000000"/>
- <Option name="transport recenable button: led active" value="00000000"/>
- <Option name="transport recenable button: text" value="00000000"/>
- <Option name="transport recenable button: text active" value="00000000"/>
- <Option name="transport option button: fill start" value="636470ff"/>
- <Option name="transport option button: fill end" value="54555dff"/>
- <Option name="transport option button: fill start active" value="636470ff"/>
- <Option name="transport option button: fill end active" value="4a4b51ff"/>
- <Option name="transport option button: led" value="4f3300ff"/>
- <Option name="transport option button: led active" value="ffa500ff"/>
- <Option name="transport option button: text" value="c7c7d8ff"/>
- <Option name="transport option button: text active" value="c8c8d9ff"/>
- <Option name="transport active option button: fill start" value="606b60ff"/>
- <Option name="transport active option button: fill end" value="495348ff"/>
- <Option name="transport active option button: fill start active" value="154515ff"/>
- <Option name="transport active option button: fill end active" value="20a320ff"/>
- <Option name="transport active option button: led" value="4f3300ff"/>
- <Option name="transport active option button: led active" value="ffa500ff"/>
- <Option name="transport active option button: text" value="c7c7d8ff"/>
- <Option name="transport active option button: text active" value="000000ff"/>
- <Option name="plugin bypass button: fill start" value="5d5856ff"/>
- <Option name="plugin bypass button: fill end" value="564d48ff"/>
- <Option name="plugin bypass button: fill start active" value="5d5856ff"/>
- <Option name="plugin bypass button: fill end active" value="564d48ff"/>
- <Option name="plugin bypass button: led" value="660000ff"/>
- <Option name="plugin bypass button: led active" value="ff0000ff"/>
- <Option name="plugin bypass button: text" value="c7c7d8ff"/>
- <Option name="plugin bypass button: text active" value="c8c8d9ff"/>
- <Option name="punch button: fill start" value="603f3fff"/>
- <Option name="punch button: fill end" value="3d2828ff"/>
- <Option name="punch button: fill start active" value="503010ff"/>
- <Option name="punch button: fill end active" value="f03020ff"/>
- <Option name="punch button: led" value="00000000"/>
- <Option name="punch button: led active" value="00000000"/>
- <Option name="punch button: text" value="a5a5a5ff"/>
- <Option name="punch button: text active" value="d8d8d8ff"/>
- <Option name="mouse mode button: fill start" value="6d7ab460"/>
- <Option name="mouse mode button: fill end" value="54555dff"/>
- <Option name="mouse mode button: fill start active" value="1d7a05ff"/>
- <Option name="mouse mode button: fill end active" value="14ae08ff"/>
- <Option name="mouse mode button: led" value="4f3300ff"/>
- <Option name="mouse mode button: led active" value="ffa500ff"/>
- <Option name="mouse mode button: text" value="f2f2f2ff"/>
- <Option name="mouse mode button: text active" value="000000ff"/>
- <Option name="nudge button: fill start" value="785754dd"/>
- <Option name="nudge button: fill end" value="564242dd"/>
- <Option name="nudge button: fill start active" value="202025ff"/>
- <Option name="nudge button: fill end active" value="404045ff"/>
- <Option name="nudge button: led" value="4f3300ff"/>
- <Option name="nudge button: led active" value="ffa500ff"/>
- <Option name="nudge button: text" value="c7c7d8ff"/>
- <Option name="nudge button: text active" value="c8c8d9ff"/>
- <Option name="zoom menu: fill start" value="99997950"/>
- <Option name="zoom menu: fill end" value="99996999"/>
- <Option name="zoom menu: fill start active" value="202025ff"/>
- <Option name="zoom menu: fill end active" value="404045ff"/>
- <Option name="zoom menu: led" value="4f3300ff"/>
- <Option name="zoom menu: led active" value="ffa500ff"/>
- <Option name="zoom menu: text" value="c7c7d8ff"/>
- <Option name="zoom menu: text active" value="c8c8d9ff"/>
- <Option name="zoom button: fill start" value="d4d0a090"/>
- <Option name="zoom button: fill end" value="a4a07090"/>
- <Option name="zoom button: fill start active" value="202025ff"/>
- <Option name="zoom button: fill end active" value="404045ff"/>
- <Option name="zoom button: led" value="4f3300ff"/>
- <Option name="zoom button: led active" value="ffa500ff"/>
- <Option name="zoom button: text" value="c7c7d8ff"/>
- <Option name="zoom button: text active" value="c8c8d9ff"/>
- <Option name="route button: fill start" value="565659ff"/>
- <Option name="route button: fill end" value="484853ff"/>
- <Option name="route button: fill start active" value="4d4d4dff"/>
- <Option name="route button: fill end active" value="121212ff"/>
- <Option name="route button: led" value="4f3300ff"/>
- <Option name="route button: led active" value="ffa500ff"/>
- <Option name="route button: text" value="bfbfbfff"/>
- <Option name="route button: text active" value="191919ff"/>
- <Option name="mixer strip button: fill start" value="565659ff"/>
- <Option name="mixer strip button: fill end" value="484853ff"/>
- <Option name="mixer strip button: fill start active" value="5f4943ff"/>
- <Option name="mixer strip button: fill end active" value="ffa500ff"/>
- <Option name="mixer strip button: led" value="4f3300ff"/>
- <Option name="mixer strip button: led active" value="ffa500ff"/>
- <Option name="mixer strip button: text" value="c7c7d8ff"/>
- <Option name="mixer strip button: text active" value="000000ff"/>
- <Option name="mixer strip name button: fill start" value="565659ff"/>
- <Option name="mixer strip name button: fill end" value="484853ff"/>
- <Option name="mixer strip name button: fill start active" value="4d4d4dff"/>
- <Option name="mixer strip name button: fill end active" value="121212ff"/>
- <Option name="mixer strip name button: led" value="4f3300ff"/>
- <Option name="mixer strip name button: led active" value="ffa500ff"/>
- <Option name="mixer strip name button: text" value="c7c7d8ff"/>
- <Option name="mixer strip name button: text active" value="c8c8d9ff"/>
- <Option name="midi input button: fill start" value="656867ff"/>
- <Option name="midi input button: fill end" value="333333ff"/>
- <Option name="midi input button: fill start active" value="a1ff43ff"/>
- <Option name="midi input button: fill end active" value="00a300ff"/>
- <Option name="midi input button: led" value="00000000"/>
- <Option name="midi input button: led active" value="00000000"/>
- <Option name="midi input button: text" value="00000000"/>
- <Option name="midi input button: text active" value="00000000"/>
- <Option name="transport clock: background" value="262626ff"/>
- <Option name="transport clock: text" value="8df823ff"/>
- <Option name="transport clock: edited text" value="ffa500ff"/>
- <Option name="transport clock: cursor" value="ffa500ff"/>
- <Option name="secondary clock: background" value="262626ff"/>
- <Option name="secondary clock: text" value="8df823ff"/>
- <Option name="secondary clock: edited text" value="ffa500ff"/>
- <Option name="secondary clock: cursor" value="ffa500ff"/>
- <Option name="transport delta clock: background" value="000000ff"/>
- <Option name="transport delta clock: edited text" value="ff0000ff"/>
- <Option name="transport delta clock: cursor" value="f11000ff"/>
- <Option name="transport delta clock: text" value="8ce1f8ff"/>
- <Option name="secondary delta clock: edited text" value="ff0000ff"/>
- <Option name="secondary delta clock: cursor" value="f11000ff"/>
- <Option name="secondary delta clock: background" value="000000ff"/>
- <Option name="secondary delta clock: text" value="8ce1f8ff"/>
- <Option name="big clock: background" value="020202ff"/>
- <Option name="big clock: text" value="f0f0f0ff"/>
- <Option name="big clock: edited text" value="ffa500ff"/>
- <Option name="big clock: cursor" value="ffa500ff"/>
- <Option name="big clock active: background" value="020202ff"/>
- <Option name="big clock active: text" value="f11000ff"/>
- <Option name="big clock active: edited text" value="ffa500ff"/>
- <Option name="big clock active: cursor" value="ffa500ff"/>
- <Option name="punch clock: background" value="000000ff"/>
- <Option name="punch clock: text" value="6bb620ff"/>
- <Option name="punch clock: edited text" value="ff0000ff"/>
- <Option name="punch clock: cursor" value="f11000ff"/>
- <Option name="selection clock: background" value="000000ff"/>
- <Option name="selection clock: text" value="6bb620ff"/>
- <Option name="selection clock: edited text" value="ff0000ff"/>
- <Option name="selection clock: cursor" value="f11000ff"/>
- <Option name="nudge clock: background" value="262626ff"/>
- <Option name="nudge clock: text" value="6bb620ff"/>
- <Option name="nudge clock: edited text" value="ffa500ff"/>
- <Option name="nudge clock: cursor" value="ffa500ff"/>
- <Option name="clock: background" value="000000ff"/>
- <Option name="clock: text" value="6bb620ff"/>
- <Option name="clock: edited text" value="ffa500ff"/>
- <Option name="clock: cursor" value="ffa500ff"/>
- </Canvas>
-</Ardour>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<Ardour>
+ <UI>
+ <Option name="ui-rc-file" value="ardour3_ui_dark.rc"/>
+ <Option name="flat-buttons" value="00000000"/>
+ <Option name="waveform-gradient-depth" value="0"/>
+ <Option name="timeline-item-gradient-depth" value="00000.5"/>
+ <Option name="all-floating-windows-are-dialogs" value="00000000"/>
+ <Option name="color-regions-using-track-color" value="00000000"/>
+ <Option name="show-waveform-clipping" value="00000001"/>
+ </UI>
+ <Canvas>
+ <Option name="active crossfade" value="20b2af2e"/>
+ <Option name="audio bus base" value="73829968"/>
+ <Option name="audio master bus base" value="00000000"/>
+ <Option name="audio track base" value="9daac468"/>
+ <Option name="automation line" value="44bc59ff"/>
+ <Option name="automation track fill" value="a0a0ce68"/>
+ <Option name="automation track outline" value="282828ff"/>
+ <Option name="cd marker bar" value="9496a3cc"/>
+ <Option name="crossfade editor base" value="282d49ff"/>
+ <Option name="crossfade editor line" value="000000ff"/>
+ <Option name="crossfade editor line shading" value="00a0d154"/>
+ <Option name="crossfade editor point fill" value="00ff00ff"/>
+ <Option name="crossfade editor point outline" value="0000ffff"/>
+ <Option name="crossfade editor wave" value="ffffff28"/>
+ <Option name="selected crossfade editor wave fill" value="00000000"/>
+ <Option name="crossfade line" value="000000ff"/>
+ <Option name="edit point" value="0000ffff"/>
+ <Option name="entered automation line" value="dd6363ff"/>
+ <Option name="control point fill" value="ffffff66"/>
+ <Option name="control point outline" value="ff0000ee"/>
+ <Option name="control point selected" value="55ccccff"/>
+ <Option name="entered gain line" value="dd6363ff"/>
+ <Option name="entered marker" value="dd6363ff"/>
+ <Option name="frame handle" value="7c00ff96"/>
+ <Option name="gain line" value="00bc20ff"/>
+ <Option name="gain line inactive" value="9fbca4c5"/>
+ <Option name="ghost track base" value="44007c7f"/>
+ <Option name="ghost track midi outline" value="00000000"/>
+ <Option name="ghost track wave" value="02fd004c"/>
+ <Option name="ghost track wave fill" value="00000000"/>
+ <Option name="ghost track wave clip" value="ff000000"/>
+ <Option name="ghost track zero line" value="e500e566"/>
+ <Option name="image track" value="ddddd8ff"/>
+ <Option name="inactive crossfade" value="e8ed3d77"/>
+ <Option name="inactive fade handle" value="bbbbbbaa"/>
+ <Option name="inactive group tab" value="434343ff"/>
+ <Option name="location cd marker" value="1ee8c4ff"/>
+ <Option name="location loop" value="35964fff"/>
+ <Option name="location marker" value="c4f411ff"/>
+ <Option name="location punch" value="7c3a3aff"/>
+ <Option name="location range" value="497a59ff"/>
+ <Option name="marker bar" value="99a1adcc"/>
+ <Option name="marker bar separator" value="555555ff"/>
+ <Option name="marker drag line" value="004f00f9"/>
+ <Option name="marker label" value="000000ff"/>
+ <Option name="marker track" value="ddddd8ff"/>
+ <Option name="measure line bar" value="ffffff9c"/>
+ <Option name="measure line beat" value="a29e9e76"/>
+ <Option name="meter bar" value="626470cc"/>
+ <Option name="meter fill: 0" value="008800ff"/>
+ <Option name="meter fill: 1" value="008800ff"/>
+ <Option name="meter fill: 2" value="00ff00ff"/>
+ <Option name="meter fill: 3" value="00ff00ff"/>
+ <Option name="meter fill: 4" value="fff000ff"/>
+ <Option name="meter fill: 5" value="fff000ff"/>
+ <Option name="meter fill: 6" value="ff8000ff"/>
+ <Option name="meter fill: 7" value="ff8000ff"/>
+ <Option name="meter fill: 8" value="ff0000ff"/>
+ <Option name="meter fill: 9" value="ff0000ff"/>
+ <Option name="meter background: bottom" value="333333ff"/>
+ <Option name="meter background: top" value="444444ff"/>
+ <Option name="midi meter fill: 0" value="effaa1ff"/>
+ <Option name="midi meter fill: 1" value="f2c97dff"/>
+ <Option name="midi meter fill: 2" value="f2c97dff"/>
+ <Option name="midi meter fill: 3" value="f48f52ff"/>
+ <Option name="midi meter fill: 4" value="f48f52ff"/>
+ <Option name="midi meter fill: 5" value="f83913ff"/>
+ <Option name="midi meter fill: 6" value="f83913ff"/>
+ <Option name="midi meter fill: 7" value="8fc78eff"/>
+ <Option name="midi meter fill: 8" value="8fc78eff"/>
+ <Option name="midi meter fill: 9" value="00f45600"/>
+ <Option name="meterbridge peakindicator: fill start" value="444444ff"/>
+ <Option name="meterbridge peakindicator: fill end" value="333333ff"/>
+ <Option name="meterbridge peakindicator on: fill start" value="ff0000ff"/>
+ <Option name="meterbridge peakindicator on: fill end" value="880000ff"/>
+ <Option name="meterbridge label: fill start" value="444444ff"/>
+ <Option name="meterbridge label: fill end" value="333333ff"/>
+ <Option name="meterbridge label: text" value="c7c7d8ff"/>
+ <Option name="meter marker" value="f2425bff"/>
+ <Option name="midi bus base" value="00000000"/>
+ <Option name="midi frame base" value="393d3766"/>
+ <Option name="midi note inactive channel" value="00000000"/>
+ <Option name="midi note color min" value="3f542aff"/>
+ <Option name="midi note color mid" value="7ea854ff"/>
+ <Option name="midi note color max" value="bfff80ff"/>
+ <Option name="selected midi note color min" value="1e1e33ff"/>
+ <Option name="selected midi note color mid" value="51518aff"/>
+ <Option name="selected midi note color max" value="8383deff"/>
+ <Option name="midi note selected" value="b2b2ffff"/>
+ <Option name="midi note velocity text" value="f4f214bc"/>
+ <Option name="midi patch change fill" value="50555aa0"/>
+ <Option name="midi patch change outline" value="c0c5caff"/>
+ <Option name="midi patch change inactive channel fill" value="50555ac0"/>
+ <Option name="midi patch change inactive channel outline" value="20252ac0"/>
+ <Option name="midi sysex fill" value="f1e139a0"/>
+ <Option name="midi sysex outline" value="a7a7d4ff"/>
+ <Option name="midi select rect fill" value="8888ff88"/>
+ <Option name="midi select rect outline" value="5555ffff"/>
+ <Option name="midi track base" value="b3cca35f"/>
+ <Option name="name highlight fill" value="0000ffff"/>
+ <Option name="name highlight outline" value="7c00ff96"/>
+ <Option name="piano roll black outline" value="f4f4f476"/>
+ <Option name="piano roll black" value="6c6e6a6b"/>
+ <Option name="piano roll white" value="979b9565"/>
+ <Option name="play head" value="ff0000ff"/>
+ <Option name="processor automation line" value="7aa3f9ff"/>
+ <Option name="punch line" value="a80000ff"/>
+ <Option name="range drag bar rect" value="969696c6"/>
+ <Option name="range drag rect" value="82c696c6"/>
+ <Option name="range marker bar" value="7d7f8ccc"/>
+ <Option name="recording rect" value="cc2828ff"/>
+ <Option name="recorded waveform fill" value="ffffffff"/>
+ <Option name="recorded waveform outline" value="0f0f1fff"/>
+ <Option name="rubber band rect" value="c6c6c659"/>
+ <Option name="ruler base" value="2c2121ff"/>
+ <Option name="ruler text" value="e5e5e5ff"/>
+ <Option name="selected crossfade editor line" value="00dbdbff"/>
+ <Option name="selected crossfade editor wave" value="f9ea14a0"/>
+ <Option name="selected region base" value="596559ff"/>
+ <Option name="selected waveform fill" value="ffa500ff"/>
+ <option name="selected waveform outline" value="0f0f0fcc"/>
+ <Option name="selection rect" value="e8f4d377"/>
+ <Option name="selection" value="636363b2"/>
+ <Option name="shuttle" value="6bb620ff"/>
+ <Option name="silence" value="9efffd7a"/>
+ <Option name="silence text" value="0e066cff"/>
+ <Option name="mono panner outline" value="33445eff"/>
+ <Option name="mono panner fill" value="7a9bccc9"/>
+ <Option name="mono panner text" value="000000ff"/>
+ <Option name="mono panner bg" value="2e2929ff"/>
+ <Option name="mono panner position fill" value="7a89b3ff"/>
+ <Option name="mono panner position outline" value="33445eff"/>
+ <Option name="stereo panner outline" value="33445eff"/>
+ <Option name="stereo panner fill" value="7a9accc9"/>
+ <Option name="stereo panner text" value="000000ff"/>
+ <Option name="stereo panner bg" value="2e2929ff"/>
+ <Option name="stereo panner rule" value="455c7fff"/>
+ <Option name="stereo panner mono outline" value="a05600ff"/>
+ <Option name="stereo panner mono fill" value="e99668ca"/>
+ <Option name="stereo panner mono text" value="000000ff"/>
+ <Option name="stereo panner mono bg" value="2e2929ff"/>
+ <Option name="stereo panner inverted outline" value="bf0a00ff"/>
+ <Option name="stereo panner inverted fill" value="e4a19cc9"/>
+ <Option name="stereo panner inverted text" value="000000ff"/>
+ <Option name="stereo panner inverted bg" value="2e2929ff"/>
+ <Option name="tempo bar" value="70727fcc"/>
+ <Option name="tempo marker" value="f2425bff"/>
+ <Option name="time axis frame" value="000000ff"/>
+ <Option name="selected time axis frame" value="000000ff"/>
+ <Option name="time stretch fill" value="e2b5b596"/>
+ <Option name="time stretch outline" value="63636396"/>
+ <Option name="tracknumber label: fill start" value="444444ff"/>
+ <Option name="tracknumber label: fill end" value="333333ff"/>
+ <Option name="tracknumber label: text" value="c7c7d8ff"/>
+ <Option name="transport drag rect" value="969696c6"/>
+ <Option name="transport loop rect" value="1e7728f9"/>
+ <Option name="transport marker bar" value="8c8e98cc"/>
+ <Option name="transport punch rect" value="6d2828e5"/>
+ <Option name="trim handle locked" value="ea0f0f28"/>
+ <Option name="trim handle" value="1900ff44"/>
+ <Option name="verbose canvas cursor" value="fffd2ebc"/>
+ <Option name="vestigial frame" value="0000000f"/>
+ <Option name="video timeline bar" value="303030ff"/>
+ <Option name="region base" value="b1c9b1ff"/>
+ <Option name="region area covered by another region" value="505050b0"/>
+ <Option name="waveform outline" value="000000ff"/>
+ <Option name="clipped waveform" value="ff0000e5"/>
+ <Option name="waveform fill" value="ffffffff"/>
+ <Option name="zero line" value="7f7f7fe0"/>
+ <Option name="zoom rect" value="c6d1b26d"/>
+ <Option name="monitor knob" value="329edfff"/>
+ <Option name="button border" value="000000f0"/>
+ <Option name="border color" value="00000000"/>
+ <Option name="processor prefader: fill start" value="873c3cff"/>
+ <Option name="processor prefader: fill end" value="542525ff"/>
+ <Option name="processor prefader: fill start active" value="774c4cff"/>
+ <Option name="processor prefader: fill end active" value="603535ff"/>
+ <Option name="processor prefader: led" value="26550eff"/>
+ <Option name="processor prefader: led active" value="78cb4eff"/>
+ <Option name="processor prefader: text" value="aaaaa3ff"/>
+ <Option name="processor prefader: text active" value="eeeeecff"/>
+ <Option name="processor fader: fill start" value="5d90b0ff"/>
+ <Option name="processor fader: fill end" value="154c6eff"/>
+ <Option name="processor fader: fill start active" value="5d90b0ff"/>
+ <Option name="processor fader: fill end active" value="256d8fff"/>
+ <Option name="processor fader: led" value="26550eff"/>
+ <Option name="processor fader: led active" value="78cb4eff"/>
+ <Option name="processor fader: text" value="aaaaa3ff"/>
+ <Option name="processor fader: text active" value="eeeeecff"/>
+ <Option name="processor postfader: fill start" value="354537ff"/>
+ <Option name="processor postfader: fill end" value="202823ff"/>
+ <Option name="processor postfader: fill start active" value="466452ff"/>
+ <Option name="processor postfader: fill end active" value="254528ff"/>
+ <Option name="processor postfader: led" value="26550eff"/>
+ <Option name="processor postfader: led active" value="78cb4eff"/>
+ <Option name="processor postfader: text" value="aaaaa3ff"/>
+ <Option name="processor postfader: text active" value="eeeeecff"/>
+ <Option name="processor control button: fill start" value="222222ff"/>
+ <Option name="processor control button: fill end" value="333333ff"/>
+ <Option name="processor control button: fill start active" value="444444ff"/>
+ <Option name="processor control button: fill end active" value="333333ff"/>
+ <Option name="processor control button: led" value="224400ff"/>
+ <Option name="processor control button: led active" value="99cc00ff"/>
+ <Option name="processor control button: text" value="ffffffff"/>
+ <Option name="processor control button: text active" value="ffffffff"/>
+ <Option name="midi device: fill start" value="54555dff"/>
+ <Option name="midi device: fill end" value="54555dff"/>
+ <Option name="midi device: fill start active" value="3a3a40ff"/>
+ <Option name="midi device: fill end active" value="45464cff"/>
+ <Option name="midi device: led" value="006600ff"/>
+ <Option name="midi device: led active" value="00ff00ff"/>
+ <Option name="midi device: text" value="c7c7d8ff"/>
+ <Option name="midi device: text active" value="eeeeecff"/>
+ <Option name="monitor button: fill start" value="5f5a58ff"/>
+ <Option name="monitor button: fill end" value="4f4a48ff"/>
+ <Option name="monitor button: fill start active" value="553500ff"/>
+ <Option name="monitor button: fill end active" value="e58505ff"/>
+ <Option name="monitor button: led" value="660000ff"/>
+ <Option name="monitor button: led active" value="ff0000ff"/>
+ <Option name="monitor button: text" value="aaaaa3ff"/>
+ <Option name="monitor button: text active" value="1a1a1aff"/>
+ <Option name="meterbridge label: fill start" value="444444ff"/>
+ <Option name="meterbridge label: fill end" value="333333ff"/>
+ <Option name="meterbridge label: text" value="c7c7d8ff"/>
+ <Option name="solo isolate: fill start" value="5f5a58ff"/>
+ <Option name="solo isolate: fill end" value="504442ff"/>
+ <Option name="solo isolate: fill start active" value="5d5856ff"/>
+ <Option name="solo isolate: fill end active" value="564d48ff"/>
+ <Option name="solo isolate: led" value="660000ff"/>
+ <Option name="solo isolate: led active" value="ff0000ff"/>
+ <Option name="solo isolate: text" value="c7c7d8ff"/>
+ <Option name="solo isolate: text active" value="c8c8d9ff"/>
+ <Option name="solo safe: fill start" value="5f5a58ff"/>
+ <Option name="solo safe: fill end" value="504442ff"/>
+ <Option name="solo safe: fill start active" value="5d5856ff"/>
+ <Option name="solo safe: fill end active" value="564d48ff"/>
+ <Option name="solo safe: led" value="660000ff"/>
+ <Option name="solo safe: led active" value="ff0000ff"/>
+ <Option name="solo safe: text" value="c7c7d8ff"/>
+ <Option name="solo safe: text active" value="c8c8d9ff"/>
+ <Option name="meterbridge peaklabel" value="ff1111ff"/>
+ <Option name="meter color BBC" value="ffa500ff"/>
+ <Option name="meterbridge peakindicator: fill start" value="444444ff"/>
+ <Option name="meterbridge peakindicator: fill end" value="333333ff"/>
+ <Option name="meterbridge peakindicator on: fill start" value="ff0000ff"/>
+ <Option name="meterbridge peakindicator on: fill end" value="880000ff"/>
+ <Option name="monitor section cut: fill start" value="5f5a58ff"/>
+ <Option name="monitor section cut: fill end" value="4f4a48ff"/>
+ <Option name="monitor section cut: fill start active" value="5f4943ff"/>
+ <Option name="monitor section cut: fill end active" value="ffa500ff"/>
+ <Option name="monitor section cut: led" value="473812ff"/>
+ <Option name="monitor section cut: led active" value="78cb4eff"/>
+ <Option name="monitor section cut: text" value="c7c7d8ff"/>
+ <Option name="monitor section cut: text active" value="000000ff"/>
+ <Option name="monitor section dim: fill start" value="5f5a58ff"/>
+ <Option name="monitor section dim: fill end" value="4f4a48ff"/>
+ <Option name="monitor section dim: fill start active" value="553500ff"/>
+ <Option name="monitor section dim: fill end active" value="e58505ff"/>
+ <Option name="monitor section dim: led" value="00000000"/>
+ <Option name="monitor section dim: led active" value="78cb4eff"/>
+ <Option name="monitor section dim: text" value="c8c8d9ff"/>
+ <Option name="monitor section dim: text active" value="c8c8d9ff"/>
+ <Option name="monitor section solo: fill start" value="5f5a58ff"/>
+ <Option name="monitor section solo: fill end" value="4f4a48ff"/>
+ <Option name="monitor section solo: fill start active" value="104506ff"/>
+ <Option name="monitor section solo: fill end active" value="4dbb00ff"/>
+ <Option name="monitor section solo: led" value="473812ff"/>
+ <Option name="monitor section solo: led active" value="ffa500ff"/>
+ <Option name="monitor section solo: text" value="00000000"/>
+ <Option name="monitor section solo: text active" value="00000000"/>
+ <Option name="monitor section invert: fill start" value="5f5a58ff"/>
+ <Option name="monitor section invert: fill end" value="4f4a48ff"/>
+ <Option name="monitor section invert: fill start active" value="222260ff"/>
+ <Option name="monitor section invert: fill end active" value="4242d0ff"/>
+ <Option name="monitor section invert: led" value="473812ff"/>
+ <Option name="monitor section invert: led active" value="78cb4eff"/>
+ <Option name="monitor section invert: text" value="00000000"/>
+ <Option name="monitor section invert: text active" value="00000000"/>
+ <Option name="monitor section mono: fill start" value="5f5a58ff"/>
+ <Option name="monitor section mono: fill end" value="4f4a48ff"/>
+ <Option name="monitor section mono: fill start active" value="222260ff"/>
+ <Option name="monitor section mono: fill end active" value="3232c0ff"/>
+ <Option name="monitor section mono: led" value="473812ff"/>
+ <Option name="monitor section mono: led active" value="78cb4eff"/>
+ <Option name="monitor section mono: text" value="c7c7d8ff"/>
+ <Option name="monitor section mono: text active" value="c8c8d9ff"/>
+ <Option name="monitor section solo model: fill start" value="5d5856ff"/>
+ <Option name="monitor section solo model: fill end" value="564d48ff"/>
+ <Option name="monitor section solo model: fill start active" value="5d5856ff"/>
+ <Option name="monitor section solo model: fill end active" value="564d48ff"/>
+ <Option name="monitor section solo model: led" value="4f3300ff"/>
+ <Option name="monitor section solo model: led active" value="ffa500ff"/>
+ <Option name="monitor section solo model: text" value="c7c7d8ff"/>
+ <Option name="monitor section solo model: text active" value="c8c8d9ff"/>
+ <Option name="monitor solo override: fill start" value="5d5856ff"/>
+ <Option name="monitor solo override: fill end" value="564d48ff"/>
+ <Option name="monitor solo override: fill start active" value="5d5856ff"/>
+ <Option name="monitor solo override: fill end active" value="564d48ff"/>
+ <Option name="monitor solo override: led" value="4f3300ff"/>
+ <Option name="monitor solo override: led active" value="ffa500ff"/>
+ <Option name="monitor solo override: text" value="c7c7d8ff"/>
+ <Option name="monitor solo override: text active" value="c8c8d9ff"/>
+ <Option name="monitor solo exclusive: fill start" value="5d5856ff"/>
+ <Option name="monitor solo exclusive: fill end" value="564d48ff"/>
+ <Option name="monitor solo exclusive: fill start active" value="5d5856ff"/>
+ <Option name="monitor solo exclusive: fill end active" value="564c47ff"/>
+ <Option name="monitor solo exclusive: led" value="4f3300ff"/>
+ <Option name="monitor solo exclusive: led active" value="ffa500ff"/>
+ <Option name="monitor solo exclusive: text" value="c7c7d8ff"/>
+ <Option name="monitor solo exclusive: text active" value="c8c8d9ff"/>
+ <Option name="rude solo: fill start" value="684d4dff"/>
+ <Option name="rude solo: fill end" value="513c3cff"/>
+ <Option name="rude solo: fill start active" value="ff1f1fff"/>
+ <Option name="rude solo: fill end active" value="e21b1bff"/>
+ <Option name="rude solo: led" value="00000000"/>
+ <Option name="rude solo: led active" value="00000000"/>
+ <Option name="rude solo: text" value="969696ff"/>
+ <Option name="rude solo: text active" value="e5e5e5ff"/>
+ <Option name="rude isolate: fill start" value="21414fff"/>
+ <Option name="rude isolate: fill end" value="192930ff"/>
+ <Option name="rude isolate: fill start active" value="e5f7ffff"/>
+ <Option name="rude isolate: fill end active" value="b6e5fdff"/>
+ <Option name="rude isolate: led" value="00000000"/>
+ <Option name="rude isolate: led active" value="000000ff"/>
+ <Option name="rude isolate: text" value="979797ff"/>
+ <Option name="rude isolate: text active" value="000000ff"/>
+ <Option name="rude audition: fill start" value="684d4dff"/>
+ <Option name="rude audition: fill end" value="513c3cff"/>
+ <Option name="rude audition: fill start active" value="ff1f1fff"/>
+ <Option name="rude audition: fill end active" value="e21b1bff"/>
+ <Option name="rude audition: led" value="00000000"/>
+ <Option name="rude audition: led active" value="00000000"/>
+ <Option name="rude audition: text" value="979797ff"/>
+ <Option name="rude audition: text active" value="ffffffff"/>
+ <Option name="feedback alert: fill start" value="684d4dff"/>
+ <Option name="feedback alert: fill end" value="513c3cff"/>
+ <Option name="feedback alert: fill start active" value="ff1f1fff"/>
+ <Option name="feedback alert: fill end active" value="e21b1bff"/>
+ <Option name="feedback alert: led" value="00000000"/>
+ <Option name="feedback alert: led active" value="00000000"/>
+ <Option name="feedback alert: text" value="969696ff"/>
+ <Option name="feedback alert: text active" value="e5e5e5ff"/>
+ <Option name="mute button: fill start" value="565659ff"/>
+ <Option name="mute button: fill end" value="484853ff"/>
+ <Option name="mute button: fill start active" value="5f4943ff"/>
+ <Option name="mute button: fill end active" value="ffff00ff"/>
+ <Option name="mute button: led" value="00000000"/>
+ <Option name="mute button: led active" value="00000000"/>
+ <Option name="mute button: text" value="bfbfafff"/>
+ <Option name="mute button: text active" value="191919ff"/>
+ <Option name="solo button: fill start" value="565659ff"/>
+ <Option name="solo button: fill end" value="484853ff"/>
+ <Option name="solo button: fill start active" value="1d7a05ff"/>
+ <Option name="solo button: fill end active" value="4dbb00ff"/>
+ <Option name="solo button: led" value="00000000"/>
+ <Option name="solo button: led active" value="00000000"/>
+ <Option name="solo button: text" value="afbfafff"/>
+ <Option name="solo button: text active" value="191919ff"/>
+ <Option name="invert button: fill start" value="565659ff"/>
+ <Option name="invert button: fill end" value="484853ff"/>
+ <Option name="invert button: fill start active" value="222260ff"/>
+ <Option name="invert button: fill end active" value="4242d0ff"/>
+ <Option name="invert button: led" value="473812ff"/>
+ <Option name="invert button: led active" value="78cb4eff"/>
+ <Option name="invert button: text" value="bfbfbfff"/>
+ <Option name="invert button: text active" value="bfbfbfff"/>
+ <Option name="record enable button: fill start" value="3e312fff"/>
+ <Option name="record enable button: fill end" value="3f312fff"/>
+ <Option name="record enable button: fill start active" value="c10b0bff"/>
+ <Option name="record enable button: fill end active" value="fd0000ff"/>
+ <Option name="record enable button: led" value="7b3541ff"/>
+ <Option name="record enable button: led active" value="ffa3b3ff"/>
+ <Option name="record enable button: text" value="a5a5a5ff"/>
+ <Option name="record enable button: text active" value="000000ff"/>
+ <Option name="generic button: fill start" value="3e312fff"/>
+ <Option name="generic button: fill end" value="3f312fff"/>
+ <Option name="generic button: fill start active" value="c10b0bff"/>
+ <Option name="generic button: fill end active" value="fd0000ff"/>
+ <Option name="generic button: led" value="7b3541ff"/>
+ <Option name="generic button: led active" value="ffa3b3ff"/>
+ <Option name="generic button: text" value="ff0000ff"/>
+ <Option name="generic button: text active" value="000000ff"/>
+ <Option name="send alert button: fill start" value="4e5647ff"/>
+ <Option name="send alert button: fill end" value="43493cff"/>
+ <Option name="send alert button: fill start active" value="91f928ff"/>
+ <Option name="send alert button: fill end active" value="85e524ff"/>
+ <Option name="send alert button: led" value="00000000"/>
+ <Option name="send alert button: led active" value="00000000"/>
+ <Option name="send alert button: text" value="ccccccff"/>
+ <Option name="send alert button: text active" value="000000ff"/>
+ <Option name="transport button: fill start" value="616268ff"/>
+ <Option name="transport button: fill end" value="505159ff"/>
+ <Option name="transport button: fill start active" value="1d7a05ff"/>
+ <Option name="transport button: fill end active" value="00a300ff"/>
+ <Option name="transport button: led" value="00000000"/>
+ <Option name="transport button: led active" value="00000000"/>
+ <Option name="transport button: text" value="00000000"/>
+ <Option name="transport button: text active" value="00000000"/>
+ <Option name="transport recenable button: fill start" value="5f3f3fff"/>
+ <Option name="transport recenable button: fill end" value="3d2828ff"/>
+ <Option name="transport recenable button: fill start active" value="6a0404ff"/>
+ <Option name="transport recenable button: fill end active" value="b50e0eff"/>
+ <Option name="transport recenable button: led" value="00000000"/>
+ <Option name="transport recenable button: led active" value="00000000"/>
+ <Option name="transport recenable button: text" value="00000000"/>
+ <Option name="transport recenable button: text active" value="00000000"/>
+ <Option name="transport option button: fill start" value="636470ff"/>
+ <Option name="transport option button: fill end" value="54555dff"/>
+ <Option name="transport option button: fill start active" value="636470ff"/>
+ <Option name="transport option button: fill end active" value="4a4b51ff"/>
+ <Option name="transport option button: led" value="4f3300ff"/>
+ <Option name="transport option button: led active" value="ffa500ff"/>
+ <Option name="transport option button: text" value="c7c7d8ff"/>
+ <Option name="transport option button: text active" value="c8c8d9ff"/>
+ <Option name="transport active option button: fill start" value="606b60ff"/>
+ <Option name="transport active option button: fill end" value="495348ff"/>
+ <Option name="transport active option button: fill start active" value="154515ff"/>
+ <Option name="transport active option button: fill end active" value="20a320ff"/>
+ <Option name="transport active option button: led" value="4f3300ff"/>
+ <Option name="transport active option button: led active" value="ffa500ff"/>
+ <Option name="transport active option button: text" value="c7c7d8ff"/>
+ <Option name="transport active option button: text active" value="000000ff"/>
+ <Option name="plugin bypass button: fill start" value="5d5856ff"/>
+ <Option name="plugin bypass button: fill end" value="564d48ff"/>
+ <Option name="plugin bypass button: fill start active" value="5d5856ff"/>
+ <Option name="plugin bypass button: fill end active" value="564d48ff"/>
+ <Option name="plugin bypass button: led" value="660000ff"/>
+ <Option name="plugin bypass button: led active" value="ff0000ff"/>
+ <Option name="plugin bypass button: text" value="c7c7d8ff"/>
+ <Option name="plugin bypass button: text active" value="c8c8d9ff"/>
+ <Option name="punch button: fill start" value="603f3fff"/>
+ <Option name="punch button: fill end" value="3d2828ff"/>
+ <Option name="punch button: fill start active" value="503010ff"/>
+ <Option name="punch button: fill end active" value="f03020ff"/>
+ <Option name="punch button: led" value="00000000"/>
+ <Option name="punch button: led active" value="00000000"/>
+ <Option name="punch button: text" value="a5a5a5ff"/>
+ <Option name="punch button: text active" value="d8d8d8ff"/>
+ <Option name="mouse mode button: fill start" value="6d7ab460"/>
+ <Option name="mouse mode button: fill end" value="54555dff"/>
+ <Option name="mouse mode button: fill start active" value="1d7a05ff"/>
+ <Option name="mouse mode button: fill end active" value="14ae08ff"/>
+ <Option name="mouse mode button: led" value="4f3300ff"/>
+ <Option name="mouse mode button: led active" value="ffa500ff"/>
+ <Option name="mouse mode button: text" value="f2f2f2ff"/>
+ <Option name="mouse mode button: text active" value="000000ff"/>
+ <Option name="nudge button: fill start" value="785754dd"/>
+ <Option name="nudge button: fill end" value="564242dd"/>
+ <Option name="nudge button: fill start active" value="202025ff"/>
+ <Option name="nudge button: fill end active" value="404045ff"/>
+ <Option name="nudge button: led" value="4f3300ff"/>
+ <Option name="nudge button: led active" value="ffa500ff"/>
+ <Option name="nudge button: text" value="c7c7d8ff"/>
+ <Option name="nudge button: text active" value="c8c8d9ff"/>
+ <Option name="zoom menu: fill start" value="99997950"/>
+ <Option name="zoom menu: fill end" value="99996999"/>
+ <Option name="zoom menu: fill start active" value="202025ff"/>
+ <Option name="zoom menu: fill end active" value="404045ff"/>
+ <Option name="zoom menu: led" value="4f3300ff"/>
+ <Option name="zoom menu: led active" value="ffa500ff"/>
+ <Option name="zoom menu: text" value="c7c7d8ff"/>
+ <Option name="zoom menu: text active" value="c8c8d9ff"/>
+ <Option name="zoom button: fill start" value="d4d0a090"/>
+ <Option name="zoom button: fill end" value="a4a07090"/>
+ <Option name="zoom button: fill start active" value="202025ff"/>
+ <Option name="zoom button: fill end active" value="404045ff"/>
+ <Option name="zoom button: led" value="4f3300ff"/>
+ <Option name="zoom button: led active" value="ffa500ff"/>
+ <Option name="zoom button: text" value="c7c7d8ff"/>
+ <Option name="zoom button: text active" value="c8c8d9ff"/>
+ <Option name="route button: fill start" value="565659ff"/>
+ <Option name="route button: fill end" value="484853ff"/>
+ <Option name="route button: fill start active" value="4d4d4dff"/>
+ <Option name="route button: fill end active" value="121212ff"/>
+ <Option name="route button: led" value="4f3300ff"/>
+ <Option name="route button: led active" value="ffa500ff"/>
+ <Option name="route button: text" value="bfbfbfff"/>
+ <Option name="route button: text active" value="191919ff"/>
+ <Option name="mixer strip button: fill start" value="565659ff"/>
+ <Option name="mixer strip button: fill end" value="484853ff"/>
+ <Option name="mixer strip button: fill start active" value="5f4943ff"/>
+ <Option name="mixer strip button: fill end active" value="ffa500ff"/>
+ <Option name="mixer strip button: led" value="4f3300ff"/>
+ <Option name="mixer strip button: led active" value="ffa500ff"/>
+ <Option name="mixer strip button: text" value="c7c7d8ff"/>
+ <Option name="mixer strip button: text active" value="000000ff"/>
+ <Option name="mixer strip name button: fill start" value="565659ff"/>
+ <Option name="mixer strip name button: fill end" value="484853ff"/>
+ <Option name="mixer strip name button: fill start active" value="4d4d4dff"/>
+ <Option name="mixer strip name button: fill end active" value="121212ff"/>
+ <Option name="mixer strip name button: led" value="4f3300ff"/>
+ <Option name="mixer strip name button: led active" value="ffa500ff"/>
+ <Option name="mixer strip name button: text" value="c7c7d8ff"/>
+ <Option name="mixer strip name button: text active" value="c8c8d9ff"/>
+ <Option name="midi input button: fill start" value="656867ff"/>
+ <Option name="midi input button: fill end" value="333333ff"/>
+ <Option name="midi input button: fill start active" value="a1ff43ff"/>
+ <Option name="midi input button: fill end active" value="00a300ff"/>
+ <Option name="midi input button: led" value="00000000"/>
+ <Option name="midi input button: led active" value="00000000"/>
+ <Option name="midi input button: text" value="00000000"/>
+ <Option name="midi input button: text active" value="00000000"/>
+ <Option name="transport clock: background" value="262626ff"/>
+ <Option name="transport clock: text" value="8df823ff"/>
+ <Option name="transport clock: edited text" value="ffa500ff"/>
+ <Option name="transport clock: cursor" value="ffa500ff"/>
+ <Option name="secondary clock: background" value="262626ff"/>
+ <Option name="secondary clock: text" value="8df823ff"/>
+ <Option name="secondary clock: edited text" value="ffa500ff"/>
+ <Option name="secondary clock: cursor" value="ffa500ff"/>
+ <Option name="transport delta clock: background" value="000000ff"/>
+ <Option name="transport delta clock: edited text" value="ff0000ff"/>
+ <Option name="transport delta clock: cursor" value="f11000ff"/>
+ <Option name="transport delta clock: text" value="8ce1f8ff"/>
+ <Option name="secondary delta clock: edited text" value="ff0000ff"/>
+ <Option name="secondary delta clock: cursor" value="f11000ff"/>
+ <Option name="secondary delta clock: background" value="000000ff"/>
+ <Option name="secondary delta clock: text" value="8ce1f8ff"/>
+ <Option name="big clock: background" value="020202ff"/>
+ <Option name="big clock: text" value="f0f0f0ff"/>
+ <Option name="big clock: edited text" value="ffa500ff"/>
+ <Option name="big clock: cursor" value="ffa500ff"/>
+ <Option name="big clock active: background" value="020202ff"/>
+ <Option name="big clock active: text" value="f11000ff"/>
+ <Option name="big clock active: edited text" value="ffa500ff"/>
+ <Option name="big clock active: cursor" value="ffa500ff"/>
+ <Option name="punch clock: background" value="000000ff"/>
+ <Option name="punch clock: text" value="6bb620ff"/>
+ <Option name="punch clock: edited text" value="ff0000ff"/>
+ <Option name="punch clock: cursor" value="f11000ff"/>
+ <Option name="selection clock: background" value="000000ff"/>
+ <Option name="selection clock: text" value="6bb620ff"/>
+ <Option name="selection clock: edited text" value="ff0000ff"/>
+ <Option name="selection clock: cursor" value="f11000ff"/>
+ <Option name="nudge clock: background" value="262626ff"/>
+ <Option name="nudge clock: text" value="6bb620ff"/>
+ <Option name="nudge clock: edited text" value="ffa500ff"/>
+ <Option name="nudge clock: cursor" value="ffa500ff"/>
+ <Option name="clock: background" value="000000ff"/>
+ <Option name="clock: text" value="6bb620ff"/>
+ <Option name="clock: edited text" value="ffa500ff"/>
+ <Option name="clock: cursor" value="ffa500ff"/>
+ <Option name="lock button: fill start" value="ff2714dd"/>
+ <Option name="lock button: fill end" value="8b0000ff"/>
+ <Option name="lock button: fill start active" value="202025ff"/>
+ <Option name="lock button: fill end active" value="404045ff"/>
+ <Option name="lock button: led" value="00000000"/>
+ <Option name="lock button: led active" value="00000000"/>
+ <Option name="lock button: text" value="000024ff"/>
+ <Option name="lock button: text active" value="c8c8d9ff"/>
+
+ <Option name="small font" value="@FONT_SMALL@"/>
+ <Option name="smaller font" value="@FONT_SMALLER@"/>
+ <Option name="normal font" value="@FONT_NORMAL@"/>
+ <Option name="big font" value="@FONT_BIG@"/>
+ <Option name="large font" value="@FONT_LARGE@"/>
+ <Option name="larger font" value="@FONT_LARGER@"/>
+ <Option name="huger font" value="@FONT_HUGER@"/>
+ <Option name="massive font" value="@FONT_MASSIVE@"/>
+ <Option name="small bold font" value="bold @FONT_SMALL@"/>
+ <Option name="smaller bold font" value="bold @FONT_SMALLER@"/>
+ <Option name="normal bold font" value="bold @FONT_NORMAL@"/>
+ <Option name="big bold font" value="bold @FONT_BIG@"/>
+ <Option name="large bold font" value="bold @FONT_LARGE@"/>
+ <Option name="larger bold font" value="bold @FONT_LARGER@"/>
+ <Option name="huger bold font" value="bold @FONT_HUGER@"/>
+ <Option name="massive bold font" value="bold @FONT_MASSIVE@"/>
+ <Option name="small italic font" value="italic @FONT_SMALL@"/>
+ <Option name="smaller italic font" value="italic @FONT_SMALLER@"/>
+ <Option name="normal italic font" value="italic @FONT_NORMAL@"/>
+ <Option name="big italic font" value="italic @FONT_BIG@"/>
+ <Option name="large italic font" value="italic @FONT_LARGE@"/>
+ <Option name="larger italic font" value="italic @FONT_LARGER@"/>
+ <Option name="huger italic font" value="italic @FONT_HUGER@"/>
+ <Option name="massive italic font" value="italic @FONT_MASSIVE@"/>
+ <Option name="small monospace font" value="@MONOSPACE@ @FONT_SMALL@"/>
+ <Option name="smaller monospace font" value="@MONOSPACE@ @FONT_SMALLER@"/>
+ <Option name="normal monospace font" value="@MONOSPACE@ @FONT_NORMAL@"/>
+ <Option name="big monospace font" value="@MONOSPACE@ @FONT_BIG@"/>
+ <Option name="large monospace font" value="@MONOSPACE@ @FONT_LARGE@"/>
+ <Option name="larger monospace font" value="@MONOSPACE@ @FONT_LARGER@"/>
+ <Option name="huger monospace font" value="@MONOSPACE@ @FONT_HUGER@"/>
+ <Option name="massive monospace font" value="@MONOSPACE@ @FONT_MASSIVE@"/>
+ <Option name="small bold monospace font" value="bold @MONOSPACE@ @FONT_SMALL@"/>
+ <Option name="smaller bold monospace font" value="bold @MONOSPACE@ @FONT_SMALLER@"/>
+ <Option name="normal bold monospace font" value="bold @MONOSPACE@ @FONT_NORMAL@"/>
+ <Option name="big bold monospace font" value="bold @MONOSPACE@ @FONT_BIG@"/>
+ <Option name="large bold monospace font" value="bold @MONOSPACE@ @FONT_LARGE@"/>
+ <Option name="larger bold monospace font" value="bold @MONOSPACE@ @FONT_LARGER@"/>
+ <Option name="huger bold monospace font" value="bold @MONOSPACE@ @FONT_HUGER@"/>
+ <Option name="massive bold monospace font" value="bold @MONOSPACE@ @FONT_MASSIVE@"/>
+ </Canvas>
+</Ardour>
widget "*LocationSelector" style:highest "medium_entry"
widget "*TakeSelector" style:highest "medium_entry"
widget "*RegionSelector" style:highest "medium_entry"
-widget "*TimecodeRuler" style:highest "editor_time_ruler"
-widget "*BBTRuler" style:highest "editor_time_ruler"
-widget "*SamplesRuler" style:highest "editor_time_ruler"
-widget "*TimecodeRuler" 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 "*SendStripBase" style:highest "send_strip_base"
widget "*MidiListView*" style:highest "treeview_display"
widget "*ProcessorList*" style:highest "processor_list"
widget "*PortMatrixLabel*" style:highest "small_text"
+widget "*midi device" style:highest "midi_device"
widget "*MidiTracerTextView" style:highest "midi_tracer_textview"
widget "*solo isolate" style:highest "solo_isolate"
widget "*meterbridge label" style:highest "meterbridge_label"
widget "*TimeInfoPunchTitle" style:highest "very_small_text"
widget "*TimeInfoPunchButton" style:highest "very_small_text"
widget "*TimeInfoBox" style:highest "time_info_box"
+widget "*tracknumber label" style:highest "tracknumber_label"
widget "*StatusBarBox" style:highest "status_bar_box"
widget "*RouteNameEditorEntry" style:highest "text_cell_entry"
widget "*RegionNameEditorEntry" style:highest "text_cell_entry"
, _fixed_diameter (true)
, _distinct_led_click (false)
, _hovering (false)
+ , _focused (false)
{
- ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
+ ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &ArdourButton::color_handler));
}
ArdourButton::ArdourButton (const std::string& str, Element e)
, _fixed_diameter (true)
, _distinct_led_click (false)
, _hovering (false)
+ , _focused (false)
{
set_text (str);
}
cairo_fill (cr);
}
}
+ if (_focused) {
+ rounded_function (cr, 1.5, 1.5, get_width() - 3, get_height() - 3, _corner_radius);
+ cairo_set_source_rgba (cr, 0.905, 0.917, 0.925, 0.8);
+ double dashes = 1;
+ cairo_set_dash (cr, &dashes, 1, 0);
+ cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_width (cr, 1.0);
+ cairo_stroke (cr);
+ cairo_set_dash (cr, 0, 0, 0);
+ }
}
void
}
}
+
+bool
+ArdourButton::on_focus_in_event (GdkEventFocus* ev)
+{
+ _focused = true;
+ queue_draw ();
+ return CairoWidget::on_focus_in_event (ev);
+}
+
+bool
+ArdourButton::on_focus_out_event (GdkEventFocus* ev)
+{
+ _focused = false;
+ queue_draw ();
+ return CairoWidget::on_focus_out_event (ev);
+}
+
+bool
+ArdourButton::on_key_release_event (GdkEventKey *ev) {
+ if (_focused &&
+ (ev->keyval == GDK_KEY_space || ev->keyval == GDK_Return))
+ {
+ signal_clicked();
+ if (_action) {
+ _action->activate ();
+ }
+ return true;
+ }
+ return CairoWidget::on_key_release_event (ev);
+}
+
bool
ArdourButton::on_enter_notify_event (GdkEventCrossing* ev)
{
void on_name_changed ();
bool on_enter_notify_event (GdkEventCrossing*);
bool on_leave_notify_event (GdkEventCrossing*);
+ bool on_focus_in_event (GdkEventFocus*);
+ bool on_focus_out_event (GdkEventFocus*);
+ bool on_key_release_event (GdkEventKey *);
void controllable_changed ();
PBD::ScopedConnection watch_connection;
bool _fixed_diameter;
bool _distinct_led_click;
bool _hovering;
+ bool _focused;
static bool _flat_buttons;
using namespace std;
using namespace Gtk;
using namespace Gtkmm2ext;
+using namespace ARDOUR_UI_UTILS;
ArdourDialog::ArdourDialog (string title, bool modal, bool use_seperator)
: Dialog (title, modal, use_seperator)
bool
ArdourDialog::on_key_press_event (GdkEventKey* ev)
{
- return relay_key_press (ev, this);
+ if (!relay_key_press (ev, this)) {
+ return Gtk::Window::on_key_press_event(ev);
+ }
+ return true;
}
bool
#include "pbd/enumwriter.h"
#include "pbd/memento_command.h"
#include "pbd/openuri.h"
+#include "pbd/stl_delete.h"
#include "pbd/file_utils.h"
#include "pbd/localtime_r.h"
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtkmm2ext;
using namespace Gtk;
try {
audio_midi_setup.get (true);
} catch (...) {
+ std::cerr << "audio-midi engine setup failed."<< std::endl;
return -1;
}
const bool new_session_required = (ARDOUR_COMMAND_LINE::new_session || brand_new_user);
if (get_session_parameters (false, new_session_required, ARDOUR_COMMAND_LINE::load_template)) {
+ std::cerr << "Cannot get session parameters."<< std::endl;
return -1;
}
}
get_state_files_in_directory (*i, state_file_paths);
- vector<string*>* states;
+ vector<string> states;
vector<const gchar*> item;
string fullpath = *i;
}
/* now get available states for this session */
+ states = Session::possible_states (fullpath);
- if ((states = Session::possible_states (fullpath)) == 0) {
+ if (states.empty()) {
/* no state file? */
continue;
}
Gtk::TreeModel::Row row = *(recent_session_model->append());
- row[recent_session_columns.visible_name] = Glib::path_get_basename (fullpath);
row[recent_session_columns.fullpath] = fullpath;
row[recent_session_columns.tip] = Glib::Markup::escape_text (fullpath);
if (state_file_names.size() > 1) {
+ // multiple session files in the session directory - show the directory name.
+ row[recent_session_columns.visible_name] = Glib::path_get_basename (fullpath);
// add the children
-
for (std::vector<std::string>::iterator i2 = state_file_names.begin();
i2 != state_file_names.end(); ++i2)
{
child_row[recent_session_columns.fullpath] = fullpath;
child_row[recent_session_columns.tip] = Glib::Markup::escape_text (fullpath);
}
+ } else {
+ // only a single session file in the directory - show its actual name.
+ row[recent_session_columns.visible_name] = state_file_names.front ();
}
}
setup_order_hint();
- PBD::ScopedConnection idle_connection;
-
string template_path = add_route_dialog->track_template();
+ DisplaySuspender ds;
if (!template_path.empty()) {
if (add_route_dialog->name_template_is_default()) {
session_add_audio_bus (input_chan.n_audio(), output_chan.n_audio(), route_group, count, name_template);
break;
}
-
- /* idle connection will end at scope end */
}
void
struct RecentSessionsSorter {
bool operator() (std::pair<std::string,std::string> a, std::pair<std::string,std::string> b) const {
- return cmp_nocase(a.first, b.first) == -1;
+ return ARDOUR::cmp_nocase(a.first, b.first) == -1;
}
};
using namespace Gtkmm2ext;
using namespace Gtk;
using namespace Glib;
+using namespace ARDOUR_UI_UTILS;
int
ARDOUR_UI::setup_windows ()
#include "ardour/profile.h"
#include "ardour/session.h"
-#ifdef interface
-#undef interface
-#endif
-
#include "actions.h"
#include "add_route_dialog.h"
#include "add_video_dialog.h"
#include "actions.h"
#include "mixer_ui.h"
#include "startup.h"
-#include "utils.h"
#include "window_manager.h"
#include "global_port_matrix.h"
#include "location_ui.h"
editor_meter_peak_display.show();
}
}
+ } else if (p == "waveform-scale") {
+ ArdourCanvas::WaveView::set_global_logscaled (Config->get_waveform_scale() == Logarithmic);
+ } else if (p == "waveform-shape") {
+ ArdourCanvas::WaveView::set_global_shape (Config->get_waveform_shape() == Rectified
+ ? ArdourCanvas::WaveView::Rectified : ArdourCanvas::WaveView::Normal);
+ } else if (p == "show-waveform-clipping") {
+ ArdourCanvas::WaveView::set_global_show_waveform_clipping (ARDOUR_UI::config()->get_show_waveform_clipping());
}
}
using namespace std;
using namespace Gtk;
using namespace Gtkmm2ext;
+using namespace ARDOUR_UI_UTILS;
ArdourWindow::ArdourWindow (string title)
: Window ()
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace std;
#include "audio_region_editor.h"
#include "audio_region_view.h"
#include "ardour_ui.h"
-#include "utils.h"
#include "gui_thread.h"
#include "i18n.h"
#include "canvas/poly_line.h"
#include "canvas/line.h"
#include "canvas/text.h"
-#include "canvas/curve.h"
+#include "canvas/xfade_curve.h"
#include "canvas/debug.h"
#include "canvas/utils.h"
#include "control_point.h"
#include "ghostregion.h"
#include "audio_time_axis.h"
-#include "utils.h"
#include "rgb_macros.h"
#include "gui_thread.h"
#include "ardour_ui.h"
using namespace ArdourCanvas;
static const int32_t sync_mark_width = 9;
-static double const handle_size = 15; /* height of fade handles */
+static double const handle_size = 10; /* height of fade handles */
-AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
- Gdk::Color const & basic_color)
+AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
+ uint32_t basic_color)
: RegionView (parent, tv, r, spu, basic_color)
, sync_mark(0)
, fade_in_handle(0)
, fade_out_handle(0)
- , start_xfade_in (0)
- , start_xfade_out (0)
+ , fade_in_trim_handle(0)
+ , fade_out_trim_handle(0)
+ , start_xfade_curve (0)
, start_xfade_rect (0)
, _start_xfade_visible (false)
- , end_xfade_in (0)
- , end_xfade_out (0)
+ , end_xfade_curve (0)
, end_xfade_rect (0)
, _end_xfade_visible (false)
, _amplitude_above_axis(1.0)
- , fade_color(0)
+ , trim_fade_in_drag_active(false)
+ , trim_fade_out_drag_active(false)
{
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&AudioRegionView::parameter_changed, this, _1), gui_context());
}
-AudioRegionView::AudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
- Gdk::Color const & basic_color, bool recording, TimeAxisViewItem::Visibility visibility)
+AudioRegionView::AudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv, boost::shared_ptr<AudioRegion> r, double spu,
+ uint32_t basic_color, bool recording, TimeAxisViewItem::Visibility visibility)
: RegionView (parent, tv, r, spu, basic_color, recording, visibility)
, sync_mark(0)
, fade_in_handle(0)
, fade_out_handle(0)
- , start_xfade_in (0)
- , start_xfade_out (0)
+ , fade_in_trim_handle(0)
+ , fade_out_trim_handle(0)
+ , start_xfade_curve (0)
, start_xfade_rect (0)
, _start_xfade_visible (false)
- , end_xfade_in (0)
- , end_xfade_out (0)
+ , end_xfade_curve (0)
, end_xfade_rect (0)
, _end_xfade_visible (false)
, _amplitude_above_axis(1.0)
- , fade_color(0)
+ , trim_fade_in_drag_active(false)
+ , trim_fade_out_drag_active(false)
{
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&AudioRegionView::parameter_changed, this, _1), gui_context());
}
: RegionView (other, boost::shared_ptr<Region> (other_region))
, fade_in_handle(0)
, fade_out_handle(0)
- , start_xfade_in (0)
- , start_xfade_out (0)
+ , fade_in_trim_handle(0)
+ , fade_out_trim_handle(0)
+ , start_xfade_curve (0)
, start_xfade_rect (0)
, _start_xfade_visible (false)
- , end_xfade_in (0)
- , end_xfade_out (0)
+ , end_xfade_curve (0)
, end_xfade_rect (0)
, _end_xfade_visible (false)
, _amplitude_above_axis (other._amplitude_above_axis)
- , fade_color(0)
+ , trim_fade_in_drag_active(false)
+ , trim_fade_out_drag_active(false)
{
- Gdk::Color c;
- int r,g,b,a;
-
- UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
- c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
- init (c, true);
+ init (true);
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&AudioRegionView::parameter_changed, this, _1), gui_context());
}
void
-AudioRegionView::init (Gdk::Color const & basic_color, bool wfd)
+AudioRegionView::init (bool wfd)
{
// FIXME: Some redundancy here with RegionView::init. Need to figure out
// where order is important and where it isn't...
- RegionView::init (basic_color, wfd);
+ RegionView::init (wfd);
_amplitude_above_axis = 1.0;
- compute_colors (basic_color);
-
create_waves ();
if (!_recregion) {
fade_in_handle = new ArdourCanvas::Rectangle (group);
CANVAS_DEBUG_NAME (fade_in_handle, string_compose ("fade in handle for %1", region()->name()));
- fade_in_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 255));
+ fade_in_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
fade_in_handle->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
fade_in_handle->set_data ("regionview", this);
fade_in_handle->hide ();
fade_out_handle = new ArdourCanvas::Rectangle (group);
CANVAS_DEBUG_NAME (fade_out_handle, string_compose ("fade out handle for %1", region()->name()));
- fade_out_handle->set_outline_color (RGBA_TO_UINT (0, 0, 0, 255));
+ fade_out_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
fade_out_handle->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
fade_out_handle->set_data ("regionview", this);
fade_out_handle->hide ();
+
+ fade_in_trim_handle = new ArdourCanvas::Rectangle (group);
+ CANVAS_DEBUG_NAME (fade_in_handle, string_compose ("fade in trim handle for %1", region()->name()));
+ fade_in_trim_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
+ fade_in_trim_handle->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
+ fade_in_trim_handle->set_data ("regionview", this);
+ fade_in_trim_handle->hide ();
+
+ fade_out_trim_handle = new ArdourCanvas::Rectangle (group);
+ CANVAS_DEBUG_NAME (fade_out_handle, string_compose ("fade out trim handle for %1", region()->name()));
+ fade_out_trim_handle->set_outline_color (ArdourCanvas::rgba_to_color (0, 0, 0, 1.0));
+ fade_out_trim_handle->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
+ fade_out_trim_handle->set_data ("regionview", this);
+ fade_out_trim_handle->hide ();
}
setup_fade_handle_positions ();
reset_width_dependent_items (_pixel_width);
if (fade_in_handle) {
- fade_in_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_handle_event), fade_in_handle, this));
+ fade_in_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_handle_event), fade_in_handle, this, false));
}
if (fade_out_handle) {
- fade_out_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_handle_event), fade_out_handle, this));
+ fade_out_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_handle_event), fade_out_handle, this, false));
+ }
+
+ if (fade_in_trim_handle) {
+ fade_in_trim_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_in_handle_event), fade_in_trim_handle, this, true));
+ }
+
+ if (fade_out_trim_handle) {
+ fade_out_trim_handle->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_fade_out_handle_event), fade_out_trim_handle, this, true));
}
set_colors ();
setup_waveform_visibility ();
- setup_waveform_shape ();
if (frame_handle_start) {
frame_handle_start->raise_to_top ();
{
if (start_xfade_rect) {
if (audio_region()->fade_in_active()) {
- start_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+ start_xfade_rect->set_fill (false);
} else {
start_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveCrossfade());
+ start_xfade_rect->set_fill (true);
}
}
}
{
if (end_xfade_rect) {
if (audio_region()->fade_out_active()) {
- end_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+ end_xfade_rect->set_fill (false);
} else {
end_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveCrossfade());
+ end_xfade_rect->set_fill (true);
}
}
}
RegionView::reset_width_dependent_items(pixel_width);
assert(_pixel_width == pixel_width);
- if (fade_in_handle) {
- if (pixel_width <= 6.0 || _height < 5.0 || !trackview.session()->config.get_show_region_fades()) {
- fade_in_handle->hide();
- fade_out_handle->hide();
- } else {
- //fade_in_handle->show();
- //fade_out_handle->show();
- }
+ if (pixel_width <= 20.0 || _height < 5.0 || !trackview.session()->config.get_show_region_fades()) {
+ if (fade_in_handle) { fade_in_handle->hide(); }
+ if (fade_out_handle) { fade_out_handle->hide(); }
+ if (fade_in_trim_handle) { fade_in_trim_handle->hide(); }
+ if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
+ if (start_xfade_rect) { start_xfade_rect->set_outline (false); }
+ if (end_xfade_rect) { end_xfade_rect->set_outline (false); }
}
AnalysisFeatureList analysis_features = _region->transients();
fade_out_handle->set_y0 (handle_pos);
fade_out_handle->set_y1 (handle_pos + handle_size);
}
+
+ if (fade_in_trim_handle) {
+ fade_in_trim_handle->set_y0 (_height - handle_size);
+ fade_in_trim_handle->set_y1 (_height);
+ }
+
+ if (fade_out_trim_handle) {
+ fade_out_trim_handle->set_y0 (_height - handle_size );
+ fade_out_trim_handle->set_y1 (_height);
+ }
}
void
if (name_text) {
name_text->raise_to_top();
}
+
+ setup_fade_handle_positions();
}
void
AudioRegionView::reset_fade_shapes ()
{
- reset_fade_in_shape ();
- reset_fade_out_shape ();
+ if (!trim_fade_in_drag_active) { reset_fade_in_shape (); }
+ if (!trim_fade_out_drag_active) { reset_fade_out_shape (); }
}
void
}
void
-AudioRegionView::reset_fade_in_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width)
+AudioRegionView::reset_fade_in_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width, bool drag_active)
{
+ trim_fade_in_drag_active = drag_active;
if (fade_in_handle == 0) {
return;
}
fade_in_handle->set_x0 (handle_left);
fade_in_handle->set_x1 (handle_left + handle_size);
+ if (fade_in_trim_handle) {
+ fade_in_trim_handle->set_x0 (0);
+ fade_in_trim_handle->set_x1 (handle_size);
+ }
+
+ if (fade_in_handle->visible()) {
+ //see comment for drag_start
+ entered(false);
+ }
+
if (pwidth < 5) {
hide_start_xfade();
return;
/* points *MUST* be in anti-clockwise order */
Points points;
- Points::size_type npoints;
Points::size_type pi;
boost::shared_ptr<const Evoral::ControlList> list (audio_region()->fade_in());
Evoral::ControlList::const_iterator x;
double length = list->length();
- npoints = list->size();
points.assign (list->size(), Duple());
}
void
-AudioRegionView::reset_fade_out_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width)
+AudioRegionView::reset_fade_out_shape_width (boost::shared_ptr<AudioRegion> ar, framecnt_t width, bool drag_active)
{
+ trim_fade_out_drag_active = drag_active;
if (fade_out_handle == 0) {
return;
}
width = std::max ((framecnt_t) 64, width);
- double const pwidth = trackview.editor().sample_to_pixel (width);
+ double const pwidth = rint(trackview.editor().sample_to_pixel (width));
/* the right edge should be right on the region frame is the pixel
* width is zero. Hence the additional + 1.0 at the end.
*/
- double const handle_right = trackview.editor().sample_to_pixel (_region->length()) + TimeAxisViewItem::RIGHT_EDGE_SHIFT - pwidth;
+ double const handle_right = rint(trackview.editor().sample_to_pixel (_region->length()) + TimeAxisViewItem::RIGHT_EDGE_SHIFT - pwidth);
+ double const trim_handle_right = rint(trackview.editor().sample_to_pixel (_region->length()) + TimeAxisViewItem::RIGHT_EDGE_SHIFT);
/* Put the fade out handle so that its right side is at the end-of-fade line;
*/
- fade_out_handle->set_x0 (handle_right - handle_size);
- fade_out_handle->set_x1 (handle_right);
+ fade_out_handle->set_x0 (1 + handle_right - handle_size);
+ fade_out_handle->set_x1 (1 + handle_right);
+ if (fade_out_trim_handle) {
+ fade_out_trim_handle->set_x0 (1 + trim_handle_right - handle_size);
+ fade_out_trim_handle->set_x1 (1 + trim_handle_right);
+ }
+ if (fade_out_handle->visible()) {
+ //see comment for drag_start
+ entered(false);
+ }
/* don't show shape if its too small */
if (pwidth < 5) {
/* points *MUST* be in anti-clockwise order */
Points points;
- Points::size_type npoints;
Points::size_type pi;
boost::shared_ptr<const Evoral::ControlList> list (audio_region()->fade_out());
Evoral::ControlList::const_iterator x;
double length = list->length();
- npoints = list->size();
points.assign (list->size(), Duple());
return;
}
- if (!start_xfade_in) {
- start_xfade_in = new ArdourCanvas::Curve (group);
- CANVAS_DEBUG_NAME (start_xfade_in, string_compose ("xfade start in line for %1", region()->name()));
- start_xfade_in->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
- start_xfade_in->set_outline_width (1.5);
- start_xfade_in->set_ignore_events (true);
+ if (!start_xfade_curve) {
+ start_xfade_curve = new ArdourCanvas::XFadeCurve (group, ArdourCanvas::XFadeCurve::Start);
+ CANVAS_DEBUG_NAME (start_xfade_curve, string_compose ("xfade start out line for %1", region()->name()));
+ start_xfade_curve->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+ start_xfade_curve->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
+ start_xfade_curve->set_ignore_events (true);
}
-
- if (!start_xfade_out) {
- start_xfade_out = new ArdourCanvas::Curve (group);
- CANVAS_DEBUG_NAME (start_xfade_out, string_compose ("xfade start out line for %1", region()->name()));
- uint32_t col = UINT_RGBA_CHANGE_A (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine(), 128);
- start_xfade_out->set_outline_color (col);
- start_xfade_out->set_outline_width (2.0);
- start_xfade_out->set_ignore_events (true);
- }
-
if (!start_xfade_rect) {
start_xfade_rect = new ArdourCanvas::Rectangle (group);
CANVAS_DEBUG_NAME (start_xfade_rect, string_compose ("xfade start rect for %1", region()->name()));
- start_xfade_rect->set_fill (true);
- start_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+ start_xfade_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
+ start_xfade_rect->set_fill (false);
start_xfade_rect->set_outline (false);
+ start_xfade_rect->set_outline_what (ArdourCanvas::Rectangle::What (ArdourCanvas::Rectangle::RIGHT));
+ start_xfade_rect->set_outline_width (0.5);
start_xfade_rect->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_start_xfade_event), start_xfade_rect, this));
start_xfade_rect->set_data ("regionview", this);
}
- start_xfade_rect->set (ArdourCanvas::Rect (1.0, 0.0, rect_width, effective_height));
-
- start_xfade_in->set (points);
+ start_xfade_rect->set (ArdourCanvas::Rect (0.0, 0.0, rect_width, effective_height));
/* fade out line */
}
}
- start_xfade_out->set (ipoints);
+ start_xfade_curve->set_inout (points, ipoints);
show_start_xfade();
}
return;
}
- if (!end_xfade_in) {
- end_xfade_in = new ArdourCanvas::Curve (group);
- CANVAS_DEBUG_NAME (end_xfade_in, string_compose ("xfade end in line for %1", region()->name()));
- uint32_t col UINT_RGBA_CHANGE_A (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine(), 128);
- end_xfade_in->set_outline_color (col);
- end_xfade_in->set_outline_width (1.5);
- end_xfade_in->set_ignore_events (true);
-}
-
- if (!end_xfade_out) {
- end_xfade_out = new ArdourCanvas::Curve (group);
- CANVAS_DEBUG_NAME (end_xfade_out, string_compose ("xfade end out line for %1", region()->name()));
- end_xfade_out->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
- end_xfade_out->set_outline_width (2.0);
- end_xfade_out->set_ignore_events (true);
+ if (!end_xfade_curve) {
+ end_xfade_curve = new ArdourCanvas::XFadeCurve (group, ArdourCanvas::XFadeCurve::End);
+ CANVAS_DEBUG_NAME (end_xfade_curve, string_compose ("xfade end out line for %1", region()->name()));
+ end_xfade_curve->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+ end_xfade_curve->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
+ end_xfade_curve->set_ignore_events (true);
}
if (!end_xfade_rect) {
end_xfade_rect = new ArdourCanvas::Rectangle (group);
CANVAS_DEBUG_NAME (end_xfade_rect, string_compose ("xfade end rect for %1", region()->name()));
- end_xfade_rect->set_fill (true);
- end_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
- end_xfade_rect->set_outline (0);
+ end_xfade_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
+ end_xfade_rect->set_fill (false);
+ end_xfade_rect->set_outline (false);
+ end_xfade_rect->set_outline_what (ArdourCanvas::Rectangle::What (ArdourCanvas::Rectangle::LEFT));
+ end_xfade_rect->set_outline_width (0.5);
end_xfade_rect->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_end_xfade_event), end_xfade_rect, this));
end_xfade_rect->set_data ("regionview", this);
}
end_xfade_rect->set (ArdourCanvas::Rect (rect_edge, 0.0, rect_edge + rect_width + TimeAxisViewItem::RIGHT_EDGE_SHIFT, effective_height));
- end_xfade_in->set (points);
-
/* fade in line */
boost::shared_ptr<AutomationList> inverse = ar->inverse_fade_out ();
}
}
- end_xfade_out->set (ipoints);
+ end_xfade_curve->set_inout (ipoints, points);
show_end_xfade();
}
void
AudioRegionView::hide_start_xfade ()
{
- if (start_xfade_in) {
- start_xfade_in->hide();
- }
- if (start_xfade_out) {
- start_xfade_out->hide();
+ if (start_xfade_curve) {
+ start_xfade_curve->hide();
}
if (start_xfade_rect) {
start_xfade_rect->hide ();
void
AudioRegionView::hide_end_xfade ()
{
- if (end_xfade_in) {
- end_xfade_in->hide();
- }
- if (end_xfade_out) {
- end_xfade_out->hide();
+ if (end_xfade_curve) {
+ end_xfade_curve->hide();
}
if (end_xfade_rect) {
end_xfade_rect->hide ();
void
AudioRegionView::show_start_xfade ()
{
- if (start_xfade_in) {
- start_xfade_in->show();
- }
- if (start_xfade_out) {
- start_xfade_out->show();
+ if (start_xfade_curve) {
+ start_xfade_curve->show();
}
if (start_xfade_rect) {
start_xfade_rect->show ();
void
AudioRegionView::show_end_xfade ()
{
- if (end_xfade_in) {
- end_xfade_in->show();
- }
- if (end_xfade_out) {
- end_xfade_out->show();
+ if (end_xfade_curve) {
+ end_xfade_curve->show();
}
if (end_xfade_rect) {
end_xfade_rect->show ();
}
}
-void
-AudioRegionView::compute_colors (Gdk::Color const & basic_color)
-{
- RegionView::compute_colors (basic_color);
-
- /* gain color computed in envelope_active_changed() */
-
- fade_color = UINT_RGBA_CHANGE_A (fill_color, 120);
-}
-
void
AudioRegionView::set_colors ()
{
set_waveform_colors ();
- if (start_xfade_in) {
- start_xfade_in->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
- }
- if (start_xfade_out) {
- uint32_t col UINT_RGBA_CHANGE_A (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine(), 128);
- start_xfade_out->set_outline_color (col);
+ if (start_xfade_curve) {
+ start_xfade_curve->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+ start_xfade_curve->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
}
- if (end_xfade_in) {
- end_xfade_in->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
- }
- if (end_xfade_out) {
- uint32_t col UINT_RGBA_CHANGE_A (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine(), 128);
- end_xfade_out->set_outline_color (col);
+ if (end_xfade_curve) {
+ end_xfade_curve->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+ end_xfade_curve->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
}
if (start_xfade_rect) {
- start_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+ start_xfade_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
}
if (end_xfade_rect) {
- end_xfade_rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_ActiveCrossfade());
+ end_xfade_rect->set_outline_color (ARDOUR_UI::config()->get_canvasvar_CrossfadeLine());
}
}
wave->set_height (ht);
wave->set_samples_per_pixel (samples_per_pixel);
wave->set_show_zero_line (true);
+ wave->set_clip_level (Config->get_waveform_clip_level ());
+
+ wave->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_wave_view_event), wave, this));
switch (Config->get_waveform_shape()) {
case Rectified:
wave->set_logscaled (Config->get_waveform_scale() == Logarithmic);
+ vector<ArdourCanvas::WaveView*> v;
+ v.push_back (wave);
+ set_some_waveform_colors (v);
+
if (!Config->get_show_waveforms ()) {
wave->hide();
}
audio_region()->envelope()->erase (cp->model());
}
-void
-AudioRegionView::setup_waveform_shape ()
-{
- WaveView::Shape shape;
-
- switch (Config->get_waveform_shape()) {
- case Rectified:
- shape = WaveView::Rectified;
- break;
- default:
- shape = WaveView::Normal;
- }
- for (vector<WaveView *>::iterator wave = waves.begin(); wave != waves.end() ; ++wave) {
- (*wave)->set_shape (shape);
- }
-}
-
-void
-AudioRegionView::setup_waveform_scale ()
-{
- WaveView::set_global_logscaled (Config->get_waveform_scale() == Logarithmic);
-}
-
-void
-AudioRegionView::setup_waveform_clipping ()
-{
- WaveView::set_global_show_waveform_clipping (ARDOUR_UI::config()->get_show_waveform_clipping());
-}
-
GhostRegion*
AudioRegionView::add_ghost (TimeAxisView& tv)
{
gain_line->add_visibility (AutomationLine::ControlPoints);
}
- if (fade_in_handle && !internal_editing) {
- fade_in_handle->show ();
- fade_out_handle->show ();
- fade_out_handle->raise_to_top ();
- fade_in_handle->raise_to_top ();
+ if (!internal_editing) {
+ if (start_xfade_rect) {
+ start_xfade_rect->set_outline (true);
+ }
+ if (end_xfade_rect) {
+ end_xfade_rect->set_outline (true);
+ }
+ if (fade_in_handle) {
+ fade_in_handle->show ();
+ fade_in_handle->raise_to_top ();
+ }
+ if (fade_out_handle) {
+ fade_out_handle->show ();
+ fade_out_handle->raise_to_top ();
+ }
+ if (fade_in_trim_handle) {
+ boost::shared_ptr<AudioRegion> ar (audio_region());
+ if (!ar->locked() && (ar->fade_in()->back()->when > 64 || (ar->can_trim() & Trimmable::FrontTrimEarlier))) {
+ fade_in_trim_handle->show ();
+ fade_in_trim_handle->raise_to_top ();
+ } else {
+ fade_in_trim_handle->hide ();
+ }
+ }
+ if (fade_out_trim_handle) {
+ boost::shared_ptr<AudioRegion> ar (audio_region());
+ if (!ar->locked() && (ar->fade_out()->back()->when > 64 || (ar->can_trim() & Trimmable::EndTrimLater))) {
+ fade_out_trim_handle->show ();
+ fade_out_trim_handle->raise_to_top ();
+ } else {
+ fade_out_trim_handle->hide ();
+ }
+ }
}
}
gain_line->remove_visibility (AutomationLine::ControlPoints);
}
- if (fade_in_handle) {
- fade_in_handle->hide ();
- fade_out_handle->hide ();
- }
+ if (fade_in_handle) { fade_in_handle->hide(); }
+ if (fade_out_handle) { fade_out_handle->hide(); }
+ if (fade_in_trim_handle) { fade_in_trim_handle->hide(); }
+ if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
+ if (start_xfade_rect) { start_xfade_rect->set_outline (false); }
+ if (end_xfade_rect) { end_xfade_rect->set_outline (false); }
}
void
void
AudioRegionView::set_waveform_colors ()
{
- for (vector<ArdourCanvas::WaveView*>::iterator w = waves.begin(); w != waves.end(); ++w) {
- set_one_waveform_color (*w);
- }
+ set_some_waveform_colors (waves);
}
void
-AudioRegionView::set_one_waveform_color (ArdourCanvas::WaveView* wave)
+AudioRegionView::set_some_waveform_colors (vector<ArdourCanvas::WaveView*>& waves_to_color)
{
ArdourCanvas::Color fill;
ArdourCanvas::Color outline;
-
+ ArdourCanvas::Color clip = ARDOUR_UI::config()->get_canvasvar_WaveFormClip();
+ ArdourCanvas::Color zero = ARDOUR_UI::config()->get_canvasvar_ZeroLine();
+
if (_selected) {
if (_region->muted()) {
/* hide outline with zero alpha */
} else {
outline = ARDOUR_UI::config()->get_canvasvar_SelectedWaveForm();
fill = ARDOUR_UI::config()->get_canvasvar_SelectedWaveFormFill();
+
+ if (ARDOUR_UI::config()->get_color_regions_using_track_color()) {
+ /* just use a slightly transparent version of the selected
+ * color so that some of the track color bleeds through
+ */
+ fill = UINT_RGBA_CHANGE_A (fill, 217);
+ }
}
} else {
if (_recregion) {
} else {
outline = ARDOUR_UI::config()->get_canvasvar_WaveForm();
fill = ARDOUR_UI::config()->get_canvasvar_WaveFormFill();
+
+ if (ARDOUR_UI::config()->get_color_regions_using_track_color()) {
+ /* just use a slightly transparent version of the selected
+ * color so that some of the track color bleeds through
+ */
+ fill = UINT_RGBA_CHANGE_A (fill, 217);
+ }
}
}
}
- if (ARDOUR_UI::config()->get_color_regions_using_track_color()) {
-
- /* just use a slightly transparent version of the selected
- * color so that some of the track color bleeds through
- */
-
- double r, g, b, a;
- ArdourCanvas::color_to_rgba (fill, r, g, b, a);
- fill = ArdourCanvas::rgba_to_color (r, g, b, 0.85); /* magic number, not user controllable */
- outline = ARDOUR_UI::config()->get_canvasvar_WaveForm();
-
- if (!Config->get_show_name_highlight()) {
- /* recolor name text because it needs to contrast with
- the waveform background, not the name highlight.
- */
- }
+ for (vector<ArdourCanvas::WaveView*>::iterator w = waves_to_color.begin(); w != waves_to_color.end(); ++w) {
+ (*w)->set_fill_color (fill);
+ (*w)->set_outline_color (outline);
+ (*w)->set_clip_color (clip);
+ (*w)->set_zero_color (zero);
}
-
- wave->set_fill_color (fill);
- wave->set_outline_color (outline);
- wave->set_clip_color (ARDOUR_UI::config()->get_canvasvar_WaveFormClip());
- wave->set_zero_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
}
void
return;
}
- if (_region->opaque()) {
- fill_opacity = 130;
- } else {
- fill_opacity = 0;
- }
-
- TimeAxisViewItem::set_frame_color ();
+ RegionView::set_frame_color ();
set_waveform_colors ();
}
AudioRegionView::set_fade_visibility (bool yn)
{
if (yn) {
- if (fade_in_handle) {
- fade_in_handle->show ();
- }
- if (fade_out_handle) {
- fade_out_handle->show ();
- }
- } else {
- if (fade_in_handle) {
- fade_in_handle->hide ();
- }
- if (fade_out_handle) {
- fade_out_handle->hide ();
- }
+ if (start_xfade_curve) { start_xfade_curve->show (); }
+ if (end_xfade_curve) { end_xfade_curve->show (); }
+ if (start_xfade_rect) { start_xfade_rect->show (); }
+ if (end_xfade_rect) { end_xfade_rect->show (); }
+ } else {
+ if (start_xfade_curve) { start_xfade_curve->hide(); }
+ if (end_xfade_curve) { end_xfade_curve->hide(); }
+ if (fade_in_handle) { fade_in_handle->hide(); }
+ if (fade_out_handle) { fade_out_handle->hide(); }
+ if (fade_in_trim_handle) { fade_in_trim_handle->hide(); }
+ if (fade_out_trim_handle) { fade_out_trim_handle->hide(); }
+ if (start_xfade_rect) { start_xfade_rect->hide (); }
+ if (end_xfade_rect) { end_xfade_rect->hide (); }
+ if (start_xfade_rect) { start_xfade_rect->set_outline (false); }
+ if (end_xfade_rect) { end_xfade_rect->set_outline (false); }
}
}
{
RegionView::update_coverage_frames (d);
- if (fade_in_handle) {
- fade_in_handle->raise_to_top ();
- fade_out_handle->raise_to_top ();
- }
+ if (fade_in_handle) { fade_in_handle->raise_to_top (); }
+ if (fade_out_handle) { fade_out_handle->raise_to_top (); }
+ if (fade_in_trim_handle) { fade_in_trim_handle->raise_to_top (); }
+ if (fade_out_trim_handle) { fade_out_trim_handle->raise_to_top (); }
}
void
AudioRegionView::drag_end ()
{
TimeAxisViewItem::drag_end ();
-
//see comment for drag_start
+
+ if (fade_in_handle && fade_in_handle->visible()) {
+ // lenght of region or fade changed, re-check
+ // if fade_in_trim_handle or fade_out_trim_handle should
+ // be visible. -- If the fade_in_handle is visible
+ // we have focus and are not in internal edit mode.
+ entered(false);
+ }
}
void
{
if (p == "show-waveforms") {
setup_waveform_visibility ();
- } else if (p == "waveform-scale") {
- setup_waveform_scale ();
- } else if (p == "waveform-shape") {
- setup_waveform_shape ();
- } else if (p == "show-waveform-clipping") {
- setup_waveform_clipping ();
}
}
#include "canvas/fwd.h"
#include "canvas/wave_view.h"
+#include "canvas/xfade_curve.h"
#include "region_view.h"
#include "time_axis_view_item.h"
class AudioRegionView : public RegionView
{
public:
- AudioRegionView (ArdourCanvas::Group *,
+ AudioRegionView (ArdourCanvas::Container *,
RouteTimeAxisView&,
boost::shared_ptr<ARDOUR::AudioRegion>,
double initial_samples_per_pixel,
- Gdk::Color const & basic_color);
+ uint32_t base_color);
- AudioRegionView (ArdourCanvas::Group *,
+ AudioRegionView (ArdourCanvas::Container *,
RouteTimeAxisView&,
boost::shared_ptr<ARDOUR::AudioRegion>,
double samples_per_pixel,
- Gdk::Color const & basic_color,
+ uint32_t base_color,
bool recording,
TimeAxisViewItem::Visibility);
~AudioRegionView ();
- virtual void init (Gdk::Color const & base_color, bool wait_for_data);
+ void init (bool wait_for_data);
boost::shared_ptr<ARDOUR::AudioRegion> audio_region() const;
GhostRegion* add_ghost (TimeAxisView&);
- void reset_fade_in_shape_width (boost::shared_ptr<ARDOUR::AudioRegion> ar, framecnt_t);
- void reset_fade_out_shape_width (boost::shared_ptr<ARDOUR::AudioRegion> ar, framecnt_t);
+ void reset_fade_in_shape_width (boost::shared_ptr<ARDOUR::AudioRegion> ar, framecnt_t, bool drag_active = false);
+ void reset_fade_out_shape_width (boost::shared_ptr<ARDOUR::AudioRegion> ar, framecnt_t, bool drag_active = false);
framepos_t get_fade_in_shape_width ();
framepos_t get_fade_out_shape_width ();
ArdourCanvas::Polygon* sync_mark; ///< polgyon for sync position
ArdourCanvas::Rectangle* fade_in_handle; ///< fade in handle, or 0
ArdourCanvas::Rectangle* fade_out_handle; ///< fade out handle, or 0
+ ArdourCanvas::Rectangle* fade_in_trim_handle; ///< fade in trim handle, or 0
+ ArdourCanvas::Rectangle* fade_out_trim_handle; ///< fade out trim handle, or 0
- ArdourCanvas::Curve* start_xfade_in;
- ArdourCanvas::Curve* start_xfade_out;
- ArdourCanvas::Rectangle* start_xfade_rect;
+ ArdourCanvas::XFadeCurve* start_xfade_curve;
+ ArdourCanvas::Rectangle* start_xfade_rect;
bool _start_xfade_visible;
- ArdourCanvas::Curve *end_xfade_in;
- ArdourCanvas::Curve *end_xfade_out;
- ArdourCanvas::Rectangle* end_xfade_rect;
+ ArdourCanvas::XFadeCurve* end_xfade_curve;
+ ArdourCanvas::Rectangle* end_xfade_rect;
bool _end_xfade_visible;
boost::shared_ptr<AudioRegionGainLine> gain_line;
double _amplitude_above_axis;
- uint32_t fade_color;
-
void reset_fade_shapes ();
void reset_fade_in_shape ();
void reset_fade_out_shape ();
void set_colors ();
void set_waveform_colors ();
- void set_one_waveform_color (ArdourCanvas::WaveView*);
- void compute_colors (Gdk::Color const &);
void reset_width_dependent_items (double pixel_width);
void set_frame_color ();
void parameter_changed (std::string const &);
void setup_waveform_visibility ();
- void setup_waveform_shape ();
- void setup_waveform_scale ();
- void setup_waveform_clipping ();
+ void set_some_waveform_colors (std::vector<ArdourCanvas::WaveView*>& waves_to_color);
/** A ScopedConnection for each PeaksReady callback (one per channel). Each member
* may be 0 if no connection exists.
* first list is for start xfades, second list is for end xfades.
*/
std::pair<std::list<AudioRegionView*>, std::list<AudioRegionView*> > _hidden_xfades;
+
+ bool trim_fade_in_drag_active;
+ bool trim_fade_out_drag_active;
};
#endif /* __gtk_ardour_audio_region_view_h__ */
#include "ardour_ui.h"
#include "rgb_macros.h"
#include "gui_thread.h"
-#include "utils.h"
#include "i18n.h"
break;
case Destructive:
region_view = new TapeAudioRegionView (_canvas_group, _trackview, region,
- _samples_per_pixel, region_color);
+ _samples_per_pixel, region_color);
break;
default:
fatal << string_compose (_("programming error: %1"), "illegal track mode in ::add_region_view_internal") << endmsg;
}
- region_view->init (region_color, wait_for_waves);
+ region_view->init (wait_for_waves);
region_view->set_amplitude_above_axis(_amplitude_above_axis);
region_view->set_height (child_height ());
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Editing;
RouteTimeAxisView::set_route (rt);
- _view->apply_color (color (), StreamView::RegionColor);
+ _view->apply_color (gdk_color_to_rgba (color()), StreamView::RegionColor);
// Make sure things are sane...
assert(!is_track() || is_audio_track());
#include "ardour/session.h"
#include "ardour_ui.h"
-#include "utils.h"
#include "automation_controller.h"
#include "gui_thread.h"
#include "ardour/automation_list.h"
#include "ardour/dB.h"
#include "ardour/debug.h"
+#include "ardour/tempo.h"
#include "evoral/Curve.hpp"
#include "rgb_macros.h"
#include "ardour_ui.h"
#include "public_editor.h"
-#include "utils.h"
#include "selection.h"
#include "time_axis_view.h"
#include "point_selection.h"
/** @param converter A TimeConverter whose origin_b is the start time of the AutomationList in session frames.
* This will not be deleted by AutomationLine.
*/
-AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Group& parent,
+AutomationLine::AutomationLine (const string& name, TimeAxisView& tv, ArdourCanvas::Item& parent,
boost::shared_ptr<AutomationList> al,
Evoral::TimeConverter<double, framepos_t>* converter)
: trackview (tv)
terminal_points_can_slide = true;
_height = 0;
- group = new ArdourCanvas::Group (&parent);
+ group = new ArdourCanvas::Container (&parent);
CANVAS_DEBUG_NAME (group, "region gain envelope group");
line = new ArdourCanvas::PolyLine (group);
}
void
-AutomationLine::ContiguousControlPoints::compute_x_bounds ()
+AutomationLine::ContiguousControlPoints::compute_x_bounds (PublicEditor& e)
{
uint32_t sz = size();
if (sz > 0 && sz < line.npoints()) {
+ const TempoMap& map (e.session()->tempo_map());
/* determine the limits on x-axis motion for this
contiguous range of control points
if (front()->view_index() > 0) {
before_x = line.nth (front()->view_index() - 1)->get_x();
+
+ const framepos_t pos = e.pixel_to_sample(before_x);
+ const Meter& meter = map.meter_at (pos);
+ const framecnt_t len = ceil (meter.frames_per_bar (map.tempo_at (pos), e.session()->frame_rate())
+ / (Timecode::BBT_Time::ticks_per_beat * meter.divisions_per_bar()) );
+ const double one_tick_in_pixels = e.sample_to_pixel_unrounded (len);
+
+ before_x += one_tick_in_pixels;
}
/* if our last point has a point after it in the line,
we have an "after" bound
*/
- if (back()->view_index() < (line.npoints() - 2)) {
+ if (back()->view_index() < (line.npoints() - 1)) {
after_x = line.nth (back()->view_index() + 1)->get_x();
+
+ const framepos_t pos = e.pixel_to_sample(after_x);
+ const Meter& meter = map.meter_at (pos);
+ const framecnt_t len = ceil (meter.frames_per_bar (map.tempo_at (pos), e.session()->frame_rate())
+ / (Timecode::BBT_Time::ticks_per_beat * meter.divisions_per_bar()));
+ const double one_tick_in_pixels = e.sample_to_pixel_unrounded (len);
+
+ after_x -= one_tick_in_pixels;
}
}
}
}
for (vector<CCP>::iterator ccp = contiguous_points.begin(); ccp != contiguous_points.end(); ++ccp) {
- (*ccp)->compute_x_bounds ();
+ (*ccp)->compute_x_bounds (trackview.editor());
}
}
#include "ardour/types.h"
#include "canvas/types.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
#include "canvas/poly_line.h"
class AutomationLine;
class AutomationTimeAxisView;
class Selectable;
class Selection;
+class PublicEditor;
/** A GUI representation of an ARDOUR::AutomationList */
SelectedControlPoints = 0x4
};
- AutomationLine (const std::string& name, TimeAxisView&, ArdourCanvas::Group&,
+ AutomationLine (const std::string& name, TimeAxisView&, ArdourCanvas::Item&,
boost::shared_ptr<ARDOUR::AutomationList>,
Evoral::TimeConverter<double, ARDOUR::framepos_t>* converter = 0);
virtual ~AutomationLine ();
TimeAxisView& trackview;
- ArdourCanvas::Group& canvas_group() const { return *group; }
+ ArdourCanvas::Container& canvas_group() const { return *group; }
ArdourCanvas::Item& parent_group() const { return _parent_group; }
ArdourCanvas::Item& grab_item() const { return *line; }
/** true if we did a push at any point during the current drag */
bool did_push;
- ArdourCanvas::Group& _parent_group;
- ArdourCanvas::Group* group;
+ ArdourCanvas::Item& _parent_group;
+ ArdourCanvas::Container* group;
ArdourCanvas::PolyLine* line; /* line */
ArdourCanvas::Points line_points; /* coordinates for canvas line */
std::vector<ControlPoint*> control_points; /* visible control points */
ContiguousControlPoints (AutomationLine& al);
double clamp_dx (double dx);
void move (double dx, double dy);
- void compute_x_bounds ();
+ void compute_x_bounds (PublicEditor& e);
private:
AutomationLine& line;
double before_x;
#include "i18n.h"
-AutomationRegionView::AutomationRegionView (ArdourCanvas::Group* parent,
+AutomationRegionView::AutomationRegionView (ArdourCanvas::Container* parent,
AutomationTimeAxisView& time_axis,
boost::shared_ptr<ARDOUR::Region> region,
const Evoral::Parameter& param,
boost::shared_ptr<ARDOUR::AutomationList> list,
double spu,
- Gdk::Color const & basic_color)
+ uint32_t basic_color)
: RegionView(parent, time_axis, region, spu, basic_color, true)
, _parameter(param)
{
}
void
-AutomationRegionView::init (Gdk::Color const & basic_color, bool /*wfd*/)
+AutomationRegionView::init (bool /*wfd*/)
{
_enable_display = false;
- RegionView::init(basic_color, false);
-
- compute_colors (basic_color);
+ RegionView::init (false);
reset_width_dependent_items ((double) _region->length() / samples_per_pixel);
class AutomationRegionView : public RegionView
{
public:
- AutomationRegionView(ArdourCanvas::Group*,
+ AutomationRegionView(ArdourCanvas::Container*,
AutomationTimeAxisView&,
boost::shared_ptr<ARDOUR::Region>,
const Evoral::Parameter& parameter,
boost::shared_ptr<ARDOUR::AutomationList>,
double initial_samples_per_pixel,
- Gdk::Color const & basic_color);
+ uint32_t basic_color);
~AutomationRegionView();
- void init (Gdk::Color const & basic_color, bool wfd);
+ void init (bool wfd);
inline AutomationTimeAxisView* automation_view() const
{ return dynamic_cast<AutomationTimeAxisView*>(&trackview); }
#include "ardour_ui.h"
#include "rgb_macros.h"
#include "gui_thread.h"
-#include "utils.h"
using namespace std;
using namespace ARDOUR;
_samples_per_pixel, region_color
);
- region_view->init (region_color, false);
+ region_view->init (false);
region_views.push_front (region_view);
/* follow global waveform setting */
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
using namespace Gtk;
using namespace Gtkmm2ext;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
list<Gdk::Color> AxisView::used_colors;
string
AxisView::gui_property (const string& property_name) const
{
- return gui_object_state().get_string (state_id(), property_name);
+ if (property_hashtable.count(property_name)) {
+ return property_hashtable[property_name];
+ } else {
+ string rv = gui_object_state().get_string (state_id(), property_name);
+ property_hashtable.erase(property_name);
+ property_hashtable.emplace(property_name, rv);
+ return rv;
+ }
}
bool
set_gui_property ("visible", yn);
return true; // things changed
}
-
return false;
}
#define __ardour_gtk_axis_view_h__
#include <list>
+#include <boost/unordered_map.hpp>
#include <gtkmm/label.h>
#include <gdkmm/color.h>
std::string gui_property (const std::string& property_name) const;
template<typename T> void set_gui_property (const std::string& property_name, const T& value) {
+ std::stringstream s;
+ s << value;
+ property_hashtable.erase(property_name);
+ property_hashtable.emplace(property_name, s.str());
gui_object_state().set_property<T> (state_id(), property_name, value);
}
*/
static Gdk::Color unique_random_color();
-
Gdk::Color _color;
static std::list<Gdk::Color> used_colors;
Gtk::Label name_label;
- bool _marked_for_display;
+ mutable boost::unordered_map<std::string, std::string> property_hashtable;
uint32_t _old_order_key;
}; /* class AxisView */
using std::min;
using std::string;
+using namespace ARDOUR_UI_UTILS;
BigClockWindow::BigClockWindow (AudioClock& c)
: ArdourWindow (_("Big Clock"))
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
std::string ardour_mono_file;
- if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
+ if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
cerr << _("Cannot find ArdourMono TrueType font") << endl;
}
{
std::string ardour_mono_file;
- if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
+ if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
cerr << _("Cannot find ArdourMono TrueType font") << endl;
}
fonts_conf_file += PROGRAM_NAME;
fonts_conf_file += FONTS_CONF_LOCATION;
#else
- if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "fonts.conf", fonts_conf_file)) {
+ if (PBD::find_file (ARDOUR::ardour_config_search_path(), "fonts.conf", fonts_conf_file)) {
#endif
Glib::setenv ("FONTCONFIG_FILE", fonts_conf_file, true);
#endif
pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
#else
- if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "pango.modules", pango_modules_file)) {
+ if (PBD::find_file (ARDOUR::ardour_config_search_path(), "pango.modules", pango_modules_file)) {
Glib::ustring pango_modules_path = pango_modules_file;
pango_modules_path.resize (pango_modules_path.size()-14); // Remove "/pango.modules" from the end
gdk_pixbuf_loaders_file += PROGRAM_NAME;
gdk_pixbuf_loaders_file += PIXBUFLOADERS_CONF_LOCATION;
#else
- if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "gdk-pixbuf.loaders", gdk_pixbuf_loaders_file)) {
+ if (PBD::find_file (ARDOUR::ardour_config_search_path(), "gdk-pixbuf.loaders", gdk_pixbuf_loaders_file)) {
#endif
// Set an environment variable so we can find our pixbuf modules.
Glib::setenv ("GDK_PIXBUF_MODULE_FILE", Glib::filename_from_utf8(gdk_pixbuf_loaders_file), true);
clearlooks_la_file += PROGRAM_NAME;
clearlooks_la_file += CLEARLOOKS_CONF_LOCATION;
#else
- if (PBD::find_file_in_search_path (ARDOUR::ardour_config_search_path(), "libclearlooks.la", clearlooks_la_file)) {
+ if (PBD::find_file (ARDOUR::ardour_config_search_path(), "libclearlooks.la", clearlooks_la_file)) {
#endif
// Set an environment variable so we can find our clearlooks engine.
// Note that this requires a modified version of libgtk (gtkthemes.c)
{
std::string ardour_mono_file;
- if (!find_file_in_search_path (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
+ if (!find_file (ardour_data_search_path(), "ArdourMono.ttf", ardour_mono_file)) {
cerr << _("Cannot find ArdourMono TrueType font") << endl;
}
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
BundleEditorMatrix::BundleEditorMatrix (Gtk::Window* parent, Session* session, boost::shared_ptr<Bundle> bundle)
: PortMatrix (parent, session, DataType::NIL)
CANVAS_VARIABLE(canvasvar_ImageTrack, "image track")
CANVAS_VARIABLE(canvasvar_InactiveCrossfade, "inactive crossfade")
CANVAS_VARIABLE(canvasvar_InactiveFadeHandle, "inactive fade handle")
+CANVAS_VARIABLE(canvasvar_InactiveGroupTab, "inactive group tab")
CANVAS_VARIABLE(canvasvar_LocationCDMarker, "location cd marker")
CANVAS_VARIABLE(canvasvar_LocationLoop, "location loop")
CANVAS_VARIABLE(canvasvar_LocationMarker, "location marker")
CANVAS_VARIABLE(canvasvar_RecWaveFormFill, "recorded waveform fill")
CANVAS_VARIABLE(canvasvar_RecWaveForm, "recorded waveform outline")
CANVAS_VARIABLE(canvasvar_RubberBandRect, "rubber band rect")
+CANVAS_VARIABLE(canvasvar_RulerBase, "ruler base")
+CANVAS_VARIABLE(canvasvar_RulerText, "ruler text")
CANVAS_VARIABLE(canvasvar_SelectedCrossfadeEditorLine, "selected crossfade editor line")
CANVAS_VARIABLE(canvasvar_SelectedCrossfadeEditorWave, "selected crossfade editor wave")
CANVAS_VARIABLE(canvasvar_SelectedFrameBase, "selected region base")
CANVAS_VARIABLE(canvasvar_SelectedTimeAxisFrame, "selected time axis frame")
CANVAS_VARIABLE(canvasvar_TimeStretchFill, "time stretch fill")
CANVAS_VARIABLE(canvasvar_TimeStretchOutline, "time stretch outline")
+CANVAS_VARIABLE(canvasvar_TrackNumberLabelFillStart, "tracknumber label: fill start")
+CANVAS_VARIABLE(canvasvar_TrackNumberLabelFillEnd, "tracknumber label: fill end")
+CANVAS_VARIABLE(canvasvar_TrackNumberLabelText, "tracknumber label: text")
CANVAS_VARIABLE(canvasvar_TransportDragRect, "transport drag rect")
CANVAS_VARIABLE(canvasvar_TransportLoopRect, "transport loop rect")
CANVAS_VARIABLE(canvasvar_TransportMarkerBar, "transport marker bar")
BUTTON_VARS(MonitorButton, "monitor button")
BUTTON_VARS(SoloIsolateButton, "solo isolate")
BUTTON_VARS(SoloSafeButton, "solo safe")
+BUTTON_VARS(MidiDeviceButton, "midi device")
BUTTON_VARS(MonitorSectionCutButton, "monitor section cut")
BUTTON_VARS(MonitorSectionDimButton, "monitor section dim")
BUTTON_VARS(MonitorSectionSoloButton, "monitor section solo")
BUTTON_VARS(MixerStripButton, "mixer strip button")
BUTTON_VARS(MixerStripNameButton, "mixer strip name button")
BUTTON_VARS(MidiInputButton, "midi input button")
+BUTTON_VARS(LockButton, "lock button")
BUTTON_VARS(GenericButton, "generic button")
#define CLOCK_VARS(root,name) \
CLOCK_VARS(SelectionClock, "selection clock")
CLOCK_VARS(NudgeClock, "nudge clock")
CLOCK_VARS(GenericClock, "clock")
+CANVAS_FONT_VARIABLE(canvasvar_SmallFont, "small font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerFont, "smaller font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalFont, "normal font")
+CANVAS_FONT_VARIABLE(canvasvar_BigFont, "big font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeFont, "large font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerFont, "larger font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerFont, "huger font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveFont, "massive font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallBoldFont, "small bold font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerBoldFont, "smaller bold font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalBoldFont, "normal bold font")
+CANVAS_FONT_VARIABLE(canvasvar_BigBoldFont, "big bold font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeBoldFont, "large bold font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerBoldFont, "larger bold font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerBoldFont, "huger bold font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveBoldFont, "massive bold font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallItalicFont, "small italic font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerItalicFont, "smaller italic font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalItalicFont, "normal italic font")
+CANVAS_FONT_VARIABLE(canvasvar_BigItalicFont, "big italic font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeItalicFont, "large italic font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerItalicFont, "larger italic font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerItalicFont, "huger italic font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveItalicFont, "massive italic font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallMonospaceFont, "small monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerMonospaceFont, "smaller monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalMonospaceFont, "normal monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_BigMonospaceFont, "big monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeMonospaceFont, "large monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerMonospaceFont, "larger monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerMonospaceFont, "huger monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveMonospaceFont, "massive monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallBoldMonospaceFont, "small bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_SmallerBoldMonospaceFont, "smaller bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_NormalBoldMonospaceFont, "normal bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_BigBoldMonospaceFont, "big bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_LargeBoldMonospaceFont, "large bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_LargerBoldMonospaceFont, "larger bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_HugerBoldMonospaceFont, "huger bold monospace font")
+CANVAS_FONT_VARIABLE(canvasvar_MassiveBoldMonospaceFont, "massive bold monospace font")
+
_shape = shape;
}
-void
-ControlPoint::i2w (double& x, double& y) const
+ArdourCanvas::Item&
+ControlPoint::item() const
{
- _item->item_to_canvas (x, y);
+ return *_item;
}
namespace ArdourCanvas {
class Rectangle;
class Diamond;
+ class Item;
}
class ControlPoint : public Selectable
uint32_t view_index() const { return _view_index; }
void set_view_index(uint32_t i) { _view_index = i; }
- void i2w (double &, double &) const;
+ ArdourCanvas::Item& item() const;
ARDOUR::AutomationList::iterator model() const { return _model; }
AutomationLine& line() const { return _line; }
#include "crossfade_edit.h"
#include "rgb_macros.h"
#include "keyboard.h"
-#include "utils.h"
#include "gui_thread.h"
#include "actions.h"
class CrossfadeView : public TimeAxisViewItem
{
public:
- CrossfadeView (ArdourCanvas::Group*,
+ CrossfadeView (ArdourCanvas::Container*,
RouteTimeAxisView&,
boost::shared_ptr<ARDOUR::Crossfade>,
double initial_samples_per_pixel,
#include "pbd/unknown_type.h"
#include "pbd/unwind.h"
#include "pbd/stacktrace.h"
+#include "pbd/timersub.h"
#include <glibmm/miscutils.h>
#include <glibmm/uriutils.h>
#include "tempo_lines.h"
#include "time_axis_view.h"
#include "utils.h"
+#include "verbose_cursor.h"
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Glib;
last_update_frame = 0;
pre_press_cursor = 0;
_drags = new DragManager (this);
+ lock_dialog = 0;
+ ruler_dialog = 0;
current_mixer_strip = 0;
tempo_lines = 0;
transport_mark_label.hide();
transport_mark_label.set_no_show_all();
- initialize_rulers ();
initialize_canvas ();
_summary = new EditorSummary (this);
controls_layout.add (*h);
controls_layout.set_name ("EditControlsBase");
- controls_layout.add_events (Gdk::SCROLL_MASK);
- controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
-
- controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
+ controls_layout.add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK|Gdk::SCROLL_MASK);
controls_layout.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::edit_controls_button_release));
+ controls_layout.signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::control_layout_scroll), false);
_cursors = new MouseCursors;
+ _cursors->set_cursor_set (ARDOUR_UI::config()->get_icon_set());
+ cerr << "Set cursor set to " << ARDOUR_UI::config()->get_icon_set() << endl;
ArdourCanvas::GtkCanvas* time_pad = manage (new ArdourCanvas::GtkCanvas ());
// CAIROCANVAS
time_pad->show();
- time_canvas_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars) + 2);
- time_canvas_vbox.set_size_request (-1, -1);
-
- ruler_label_event_box.add (ruler_label_vbox);
- ruler_label_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
- ruler_label_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
-
- time_bars_event_box.add (time_bars_vbox);
- time_bars_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
- time_bars_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
-
- time_canvas_event_box.add (time_canvas_vbox);
- time_canvas_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
-
edit_packer.set_col_spacings (0);
edit_packer.set_row_spacings (0);
edit_packer.set_homogeneous (false);
edit_packer.set_border_width (0);
edit_packer.set_name ("EditorWindow");
- /* labels for the rulers */
- edit_packer.attach (ruler_label_event_box, 1, 2, 0, 1, FILL, SHRINK, 0, 0);
- /* labels for the marker "tracks" (time bars) */
- edit_packer.attach (time_bars_event_box, 1, 2, 1, 2, FILL, SHRINK, 0, 0);
- /* the rulers */
- edit_packer.attach (time_canvas_event_box, 2, 3, 0, 1, FILL|EXPAND, FILL, 0, 0);
+ time_bars_event_box.add (time_bars_vbox);
+ time_bars_event_box.set_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK);
+ time_bars_event_box.signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_label_button_release));
+
+ /* labels for the time bars */
+ edit_packer.attach (time_bars_event_box, 0, 1, 0, 1, FILL, SHRINK, 0, 0);
/* track controls */
- edit_packer.attach (controls_layout, 0, 2, 2, 3, FILL, FILL|EXPAND, 0, 0);
- /* time bars canvas */
- edit_packer.attach (*_time_bars_canvas_viewport, 2, 3, 1, 2, FILL, FILL, 0, 0);
- /* track canvas */
- edit_packer.attach (*_track_canvas_viewport, 2, 3, 2, 3, FILL|EXPAND, FILL|EXPAND, 0, 0);
+ edit_packer.attach (controls_layout, 0, 1, 1, 2, FILL, FILL|EXPAND, 0, 0);
+ /* canvas */
+ edit_packer.attach (*_track_canvas_viewport, 1, 2, 0, 2, FILL|EXPAND, FILL|EXPAND, 0, 0);
bottom_hbox.set_border_width (2);
bottom_hbox.set_spacing (3);
Session::AskAboutPlaylistDeletion.connect_same_thread (*this, boost::bind (&Editor::playlist_deletion_dialog, this, _1));
Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&Editor::parameter_changed, this, _1), gui_context());
+ ARDOUR_UI::config()->ParameterChanged.connect (sigc::mem_fun (*this, &Editor::ui_parameter_changed));
TimeAxisView::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Editor::timeaxisview_deleted, this, _1), gui_context());
_popup_region_menu_item = 0;
_show_marker_lines = false;
- _over_region_trim_target = false;
/* Button bindings */
}
constructed = true;
- instant_save ();
+
+ /* grab current parameter state */
+ boost::function<void (string)> pc (boost::bind (&Editor::ui_parameter_changed, this, _1));
+ ARDOUR_UI::config()->map_parameters (pc);
setup_fade_images ();
+
+ instant_save ();
}
Editor::~Editor()
delete button_bindings;
delete _routes;
delete _route_groups;
- delete _time_bars_canvas_viewport;
delete _track_canvas_viewport;
delete _drags;
}
bool
Editor::get_smart_mode () const
{
- return ( (current_mouse_mode() == Editing::MouseObject) && smart_mode_action->get_active() );
+ return ((current_mouse_mode() == Editing::MouseObject) && smart_mode_action->get_active());
}
void
if (!_all_region_actions_sensitized) {
sensitize_all_region_actions (true);
}
-
- _over_region_trim_target = false;
}
void
entered_regionview->exited ();
}
- if ((entered_regionview = rv) != 0) {
+ entered_regionview = rv;
+
+ if (entered_regionview != 0) {
entered_regionview->entered (internal_editing ());
}
entered_track->exited ();
}
- if ((entered_track = tav) != 0) {
+ entered_track = tav;
+
+ if (entered_track) {
entered_track->entered ();
}
}
{
Window::on_realize ();
Realized ();
+
+ if (ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
+ start_lock_event_timing ();
+ }
+
+ signal_event().connect (sigc::mem_fun (*this, &Editor::generic_event_handler));
+}
+
+void
+Editor::start_lock_event_timing ()
+{
+ /* check if we should lock the GUI every 30 seconds */
+
+ Glib::signal_timeout().connect (sigc::mem_fun (*this, &Editor::lock_timeout_callback), 30 * 1000);
+}
+
+bool
+Editor::generic_event_handler (GdkEvent* ev)
+{
+ switch (ev->type) {
+ case GDK_BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ case GDK_MOTION_NOTIFY:
+ case GDK_KEY_PRESS:
+ case GDK_KEY_RELEASE:
+ gettimeofday (&last_event_time, 0);
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+bool
+Editor::lock_timeout_callback ()
+{
+ struct timeval now, delta;
+
+ gettimeofday (&now, 0);
+
+ timersub (&now, &last_event_time, &delta);
+
+ if (delta.tv_sec > ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
+ lock ();
+ /* don't call again. Returning false will effectively
+ disconnect us from the timer callback.
+
+ unlock() will call start_lock_event_timing() to get things
+ started again.
+ */
+ return false;
+ }
+
+ return true;
}
void
/** Pop up a context menu for when the user clicks on a start crossfade */
void
-Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* /*item*/, ItemType /*item_type*/)
+Editor::popup_xfade_in_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
{
using namespace Menu_Helpers;
+ AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
+ assert(arv);
MenuList& items (xfade_in_context_menu.items());
+ items.clear ();
- if (items.empty()) {
- fill_xfade_menu (items, true);
+ if (arv->audio_region()->fade_in_active()) {
+ items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
+ } else {
+ items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
}
+ items.push_back (SeparatorElem());
+ fill_xfade_menu (items, true);
+
xfade_in_context_menu.popup (button, time);
}
/** Pop up a context menu for when the user clicks on an end crossfade */
void
-Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* /*item*/, ItemType /*item_type*/)
-{
- using namespace Menu_Helpers;
-
- MenuList& items (xfade_out_context_menu.items());
-
- if (items.empty()) {
- fill_xfade_menu (items, false);
- }
-
- xfade_out_context_menu.popup (button, time);
-}
-
-
-/** Pop up a context menu for when the user clicks on a fade in or fade out */
-void
-Editor::popup_fade_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType item_type)
+Editor::popup_xfade_out_context_menu (int button, int32_t time, ArdourCanvas::Item* item, ItemType /*item_type*/)
{
using namespace Menu_Helpers;
AudioRegionView* arv = static_cast<AudioRegionView*> (item->get_data ("regionview"));
+ assert(arv);
- if (arv == 0) {
- fatal << _("programming error: fade in canvas item has no regionview data pointer!") << endmsg;
- /*NOTREACHED*/
- }
-
- MenuList& items (fade_context_menu.items());
+ MenuList& items (xfade_out_context_menu.items());
items.clear ();
- switch (item_type) {
- case FadeInItem:
- case FadeInHandleItem:
- if (arv->audio_region()->fade_in_active()) {
- items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), false)));
- } else {
- items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_active), true)));
- }
-
- items.push_back (SeparatorElem());
-
- if (Profile->get_sae()) {
-
- items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)));
- items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)));
-
- } else {
-
- items.push_back (
- ImageMenuElem (
- _("Linear"),
- *_fade_in_images[FadeLinear],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeLinear)
- )
- );
-
- dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-
- items.push_back (
- ImageMenuElem (
- _("Slow"),
- *_fade_in_images[FadeSlow],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSlow)
- ));
-
- dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-
- items.push_back (
- ImageMenuElem (
- _("Fast"),
- *_fade_in_images[FadeFast],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeFast)
- ));
-
- dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-
- items.push_back (
- ImageMenuElem (
- _("Symmetric"),
- *_fade_in_images[FadeSymmetric],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeSymmetric)
- ));
-
- items.push_back (
- ImageMenuElem (
- _("Constant power"),
- *_fade_in_images[FadeConstantPower],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_in_shape), FadeConstantPower)
- ));
-
- dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
- }
-
- break;
-
- case FadeOutItem:
- case FadeOutHandleItem:
- if (arv->audio_region()->fade_out_active()) {
- items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
- } else {
- items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
- }
-
- items.push_back (SeparatorElem());
-
- if (Profile->get_sae()) {
- items.push_back (MenuElem (_("Linear"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)));
- items.push_back (MenuElem (_("Slowest"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)));
- } else {
-
- items.push_back (
- ImageMenuElem (
- _("Linear"),
- *_fade_out_images[FadeLinear],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeLinear)
- )
- );
-
- dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-
- items.push_back (
- ImageMenuElem (
- _("Slow"),
- *_fade_out_images[FadeSlow],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSlow)
- ));
-
- dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-
- items.push_back (
- ImageMenuElem (
- _("Fast"),
- *_fade_out_images[FadeFast],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeFast)
- ));
-
- dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
-
- items.push_back (
- ImageMenuElem (
- _("Symmetric"),
- *_fade_out_images[FadeSymmetric],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeSymmetric)
- ));
-
- items.push_back (
- ImageMenuElem (
- _("Constant power"),
- *_fade_out_images[FadeConstantPower],
- sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_shape), FadeConstantPower)
- ));
-
- dynamic_cast<ImageMenuItem*>(&items.back())->set_always_show_image ();
- }
-
- break;
-
- default:
- fatal << _("programming error: ")
- << X_("non-fade canvas item passed to popup_fade_context_menu()")
- << endmsg;
- /*NOTREACHED*/
+ if (arv->audio_region()->fade_out_active()) {
+ items.push_back (MenuElem (_("Deactivate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), false)));
+ } else {
+ items.push_back (MenuElem (_("Activate"), sigc::bind (sigc::mem_fun (*this, &Editor::set_fade_out_active), true)));
}
- fade_context_menu.popup (button, time);
+ items.push_back (SeparatorElem());
+ fill_xfade_menu (items, false);
+
+ xfade_out_context_menu.popup (button, time);
}
void
edit_point_selector.set_text (str);
}
- set_canvas_cursor ();
+ reset_canvas_cursor ();
if (!force && !changed) {
return;
}
if ((prop = node.property ("zoom-focus"))) {
- set_zoom_focus ((ZoomFocus) string_2_enum (prop->value(), zoom_focus));
+ zoom_focus_selection_done ((ZoomFocus) string_2_enum (prop->value(), zoom_focus));
}
if ((prop = node.property ("zoom"))) {
}
if ((prop = node.property ("snap-to"))) {
- set_snap_to ((SnapType) string_2_enum (prop->value(), _snap_type));
+ snap_type_selection_done ((SnapType) string_2_enum (prop->value(), _snap_type));
}
if ((prop = node.property ("snap-mode"))) {
- set_snap_mode ((SnapMode) string_2_enum (prop->value(), _snap_mode));
+ snap_mode_selection_done((SnapMode) string_2_enum (prop->value(), _snap_mode));
}
if ((prop = node.property ("internal-snap-to"))) {
return *node;
}
-
-
-/** @param y y offset from the top of all trackviews.
+/** if @param trackview_relative_offset is true, @param y y is an offset into the trackview area, in pixel units
+ * if @param trackview_relative_offset is false, @param y y is a global canvas * coordinate, in pixel units
+ *
* @return pair: TimeAxisView that y is over, layer index.
+ *
* TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
* in stacked or expanded region display mode, otherwise 0.
*/
std::pair<TimeAxisView *, double>
-Editor::trackview_by_y_position (double y)
+Editor::trackview_by_y_position (double y, bool trackview_relative_offset) const
{
- for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+ if (!trackview_relative_offset) {
+ y -= _trackview_group->canvas_origin().y;
+ }
+ if (y < 0) {
+ return std::make_pair ( (TimeAxisView *) 0, 0);
+ }
+
+ for (TrackViewList::const_iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
+
std::pair<TimeAxisView*, double> const r = (*iter)->covers_y_position (y);
+
if (r.first) {
return r;
}
bool
Editor::control_layout_scroll (GdkEventScroll* ev)
{
- if (Keyboard::some_magic_widget_has_focus()) {
- return false;
- }
-
- switch (ev->direction) {
- case GDK_SCROLL_UP:
- scroll_tracks_up_line ();
- return true;
- break;
-
- case GDK_SCROLL_DOWN:
- scroll_tracks_down_line ();
- return true;
-
- default:
- /* no left/right handling yet */
- break;
- }
+ /* Just forward to the normal canvas scroll method. The coordinate
+ systems are different but since the canvas is always larger than the
+ track headers, and aligned with the trackview area, this will work.
- return false;
+ In the not too distant future this layout is going away anyway and
+ headers will be on the canvas.
+ */
+ return canvas_scroll_event (ev, false);
}
void
Editor::use_visual_state (VisualState& vs)
{
PBD::Unwinder<bool> nsv (no_save_visual, true);
-
- _routes->suspend_redisplay ();
+ DisplaySuspender ds;
vertical_adjustment.set_value (vs.y_position);
}
_routes->update_visibility ();
- _routes->resume_redisplay ();
}
/** This is the core function that controls the zoom level of the canvas. It is called
ArdourCanvas::GtkCanvasViewport* c;
- c = get_time_bars_canvas();
- if (c) {
- c->canvas()->zoomed ();
- }
c = get_track_canvas();
if (c) {
c->canvas()->zoomed ();
void
Editor::region_view_added (RegionView *)
{
- _summary->set_dirty ();
+ _summary->set_background_dirty ();
}
void
Editor::region_view_removed ()
{
- _summary->set_dirty ();
+ _summary->set_background_dirty ();
}
-TimeAxisView*
+RouteTimeAxisView*
Editor::axis_view_from_route (boost::shared_ptr<Route> r) const
{
TrackViewList::const_iterator j = track_views.begin ();
return t;
}
+void
+Editor::suspend_route_redisplay ()
+{
+ if (_routes) {
+ _routes->suspend_redisplay();
+ }
+}
+
+void
+Editor::resume_route_redisplay ()
+{
+ if (_routes) {
+ _routes->resume_redisplay();
+ }
+}
+
void
Editor::add_routes (RouteList& routes)
{
void
Editor::timeaxisview_deleted (TimeAxisView *tv)
{
+ if (tv == entered_track) {
+ entered_track = 0;
+ }
+
if (_session && _session->deletion_in_progress()) {
/* the situation is under control */
return;
_routes->route_removed (tv);
- if (tv == entered_track) {
- entered_track = 0;
- }
-
TimeAxisView::Children c = tv->get_child_list ();
for (TimeAxisView::Children::const_iterator i = c.begin(); i != c.end(); ++i) {
if (entered_track == i->get()) {
Editor::setup_fade_images ()
{
_fade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
- _fade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-short-cut")));
- _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
- _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
- _fade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-long-cut")));
+ _fade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
+ _fade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
+ _fade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
+ _fade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
_fade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
- _fade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut")));
- _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
- _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
- _fade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut")));
+ _fade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
+ _fade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
+ _fade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
+ _fade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
- _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
- _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut")));
- _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
- _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
- _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut")));
+ _xfade_in_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadein-linear")));
+ _xfade_in_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadein-symmetric")));
+ _xfade_in_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadein-fast-cut")));
+ _xfade_in_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadein-slow-cut")));
+ _xfade_in_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadein-constant-power")));
_xfade_out_images[FadeLinear] = new Gtk::Image (get_icon_path (X_("fadeout-linear")));
- _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-short-cut")));
- _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
- _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
- _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-long-cut")));
+ _xfade_out_images[FadeSymmetric] = new Gtk::Image (get_icon_path (X_("fadeout-symmetric")));
+ _xfade_out_images[FadeFast] = new Gtk::Image (get_icon_path (X_("fadeout-fast-cut")));
+ _xfade_out_images[FadeSlow] = new Gtk::Image (get_icon_path (X_("fadeout-slow-cut")));
+ _xfade_out_images[FadeConstantPower] = new Gtk::Image (get_icon_path (X_("fadeout-constant-power")));
}
{
_stepping_axis_view = 0;
}
+
+void
+Editor::ui_parameter_changed (string parameter)
+{
+ if (parameter == "icon-set") {
+ while (!_cursor_stack.empty()) {
+ _cursor_stack.pop();
+ }
+ _cursors->set_cursor_set (ARDOUR_UI::config()->get_icon_set());
+ } else if (parameter == "draggable-playhead") {
+ if (_verbose_cursor) {
+ playhead_cursor->set_sensitive (ARDOUR_UI::config()->get_draggable_playhead());
+ }
+ }
+}
#include <list>
#include <map>
#include <set>
+#include <stack>
#include <string>
#include <sys/time.h>
#include <cmath>
#include "ardour/types.h"
#include "canvas/fwd.h"
+#include "canvas/ruler.h"
-#include "gtk-custom-ruler.h"
#include "ardour_button.h"
#include "ardour_dialog.h"
#include "ardour_dropdown.h"
class PluginSelector;
class ProgressReporter;
class RhythmFerret;
+class RulerDialog;
class Selection;
class SoundFileOmega;
class StreamView;
void set_internal_edit (bool yn);
bool toggle_internal_editing_from_double_click (GdkEvent*);
- void _ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top);
+ void _ensure_time_axis_view_is_visible (TimeAxisView const & tav, bool at_top);
void foreach_time_axis_view (sigc::slot<void,TimeAxisView&>);
void add_to_idle_resize (TimeAxisView*, int32_t);
void center_screen (framepos_t);
TrackViewList axis_views_from_routes (boost::shared_ptr<ARDOUR::RouteList>) const;
+
Gtkmm2ext::TearOff* mouse_mode_tearoff () const { return _mouse_mode_tearoff; }
Gtkmm2ext::TearOff* tools_tearoff () const { return _tools_tearoff; }
Gdk::Cursor* get_canvas_cursor () const { return current_canvas_cursor; }
void set_canvas_cursor (Gdk::Cursor*, bool save=false);
+
+ void push_canvas_cursor (Gdk::Cursor*);
+ void pop_canvas_cursor ();
+
void set_current_trimmable (boost::shared_ptr<ARDOUR::Trimmable>);
void set_current_movable (boost::shared_ptr<ARDOUR::Movable>);
_stepping_axis_view = v;
}
- ArdourCanvas::Group* get_trackview_group () const { return _trackview_group; }
- ArdourCanvas::Group* get_time_bars_group () const;
- ArdourCanvas::Group* get_track_canvas_group () const;
- ArdourCanvas::GtkCanvasViewport* get_time_bars_canvas () const;
+ ArdourCanvas::Container* get_trackview_group () const { return _trackview_group; }
+ ArdourCanvas::Container* get_noscroll_group () const { return no_scroll_group; }
+ ArdourCanvas::ScrollGroup* get_hscroll_group () const { return h_scroll_group; }
+ ArdourCanvas::ScrollGroup* get_vscroll_group () const { return v_scroll_group; }
+ ArdourCanvas::ScrollGroup* get_hvscroll_group () const { return hv_scroll_group; }
+
ArdourCanvas::GtkCanvasViewport* get_track_canvas () const;
void override_visible_track_count ();
+ /* Ruler metrics methods */
+
+ void metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>&, gdouble, gdouble, gint);
+ void metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>&, gdouble, gdouble, gint);
+ void metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>&, gdouble, gdouble, gint);
+ void metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>&, gdouble, gdouble, gint);
+
protected:
void map_transport_state ();
void map_position_change (framepos_t);
void on_realize();
+ void suspend_route_redisplay ();
+ void resume_route_redisplay ();
+
private:
void color_handler ();
JoinObjectRangeState _join_object_range_state;
- void update_join_object_range_location (double, double);
+ void update_join_object_range_location (double);
boost::optional<int> pre_notebook_shrink_pane_width;
void refresh_location_display ();
void refresh_location_display_internal (ARDOUR::Locations::LocationList&);
void add_new_location (ARDOUR::Location *);
- ArdourCanvas::Group* add_new_location_internal (ARDOUR::Location *);
+ ArdourCanvas::Container* add_new_location_internal (ARDOUR::Location *);
void location_gone (ARDOUR::Location *);
void remove_marker (ArdourCanvas::Item&, GdkEvent*);
gint really_remove_marker (ARDOUR::Location* loc);
LocationMarkerMap location_markers;
void update_marker_labels ();
- void update_marker_labels (ArdourCanvas::Group *);
+ void update_marker_labels (ArdourCanvas::Container *);
void check_marker_label (Marker *);
/** A set of lists of Markers that are in each of the canvas groups
* a marker has moved we can decide whether we need to update the labels
* for all markers or for just a few.
*/
- std::map<ArdourCanvas::Group *, std::list<Marker *> > _sorted_marker_lists;
+ std::map<ArdourCanvas::Container *, std::list<Marker *> > _sorted_marker_lists;
void remove_sorted_marker (Marker *);
void hide_marker (ArdourCanvas::Item*, GdkEvent*);
Gtk::VBox global_vpacker;
Gtk::VBox vpacker;
+ std::stack<Gdk::Cursor*> _cursor_stack;
Gdk::Cursor* current_canvas_cursor;
- Gdk::Cursor* which_grabber_cursor ();
- void set_canvas_cursor ();
+ Gdk::Cursor* which_grabber_cursor () const;
+ Gdk::Cursor* which_track_cursor () const;
+ Gdk::Cursor* which_mode_cursor () const;
+ Gdk::Cursor* which_trim_cursor (bool left_side) const;
+ bool reset_canvas_cursor ();
+ void choose_canvas_cursor_on_entry (GdkEventCrossing*, ItemType);
ArdourCanvas::GtkCanvas* _track_canvas;
ArdourCanvas::GtkCanvasViewport* _track_canvas_viewport;
- ArdourCanvas::GtkCanvas* _time_bars_canvas;
- ArdourCanvas::GtkCanvasViewport* _time_bars_canvas_viewport;
-
bool within_track_canvas;
friend class VerboseCursor;
VerboseCursor* _verbose_cursor;
void parameter_changed (std::string);
+ void ui_parameter_changed (std::string);
- bool track_canvas_motion (GdkEvent*);
-
- Gtk::EventBox time_canvas_event_box;
Gtk::EventBox time_bars_event_box;
- Gtk::EventBox ruler_label_event_box;
+ Gtk::VBox time_bars_vbox;
- ArdourCanvas::Group *minsec_group;
ArdourCanvas::Pixbuf *logo_item;
- ArdourCanvas::Group *bbt_group;
- ArdourCanvas::Group *timecode_group;
- ArdourCanvas::Group *frame_group;
- ArdourCanvas::Group *tempo_group;
- ArdourCanvas::Group *meter_group;
- ArdourCanvas::Group *marker_group;
- ArdourCanvas::Group *range_marker_group;
- ArdourCanvas::Group *transport_marker_group;
- ArdourCanvas::Group* cd_marker_group;
+#if 0
+ /* these will be needed when we have canvas rulers */
+ ArdourCanvas::Container *minsec_group;
+ ArdourCanvas::Container *bbt_group;
+ ArdourCanvas::Container *timecode_group;
+ ArdourCanvas::Container *frame_group;
+#endif
+
+ ArdourCanvas::Container *tempo_group;
+ ArdourCanvas::Container *meter_group;
+ ArdourCanvas::Container *marker_group;
+ ArdourCanvas::Container *range_marker_group;
+ ArdourCanvas::Container *transport_marker_group;
+ ArdourCanvas::Container* cd_marker_group;
/* parent for groups which themselves contain time markers */
- ArdourCanvas::Group* _time_markers_group;
+ ArdourCanvas::Container* _time_markers_group;
+
+ /* The group containing all other groups that are scrolled vertically
+ and horizontally.
+ */
+ ArdourCanvas::ScrollGroup* hv_scroll_group;
- ArdourCanvas::Group* meter_bar_group;
- ArdourCanvas::Group* tempo_bar_group;
- ArdourCanvas::Group* marker_bar_group;
- ArdourCanvas::Group* range_marker_bar_group;
- ArdourCanvas::Group* transport_marker_bar_group;
- ArdourCanvas::Group* cd_marker_bar_group;
+ /* The group containing all other groups that are scrolled vertically ONLY
+ */
+ ArdourCanvas::ScrollGroup* v_scroll_group;
+
+ /* The group containing all other groups that are scrolled horizontally ONLY
+ */
+ ArdourCanvas::ScrollGroup* h_scroll_group;
- /* The group containing all trackviews. Only scrolled vertically. */
- ArdourCanvas::Group* _trackview_group;
+ /* The group containing all trackviews. */
+ ArdourCanvas::Container* no_scroll_group;
- /* The group used for region motion. Sits on top of _trackview_group */
- ArdourCanvas::Group* _region_motion_group;
+ /* The group containing all trackviews. */
+ ArdourCanvas::Container* _trackview_group;
+
+ /* The group holding things (mostly regions) while dragging so they
+ * are on top of everything else
+ */
+ ArdourCanvas::Container* _drag_motion_group;
/* a rect that sits at the bottom of all tracks to act as a drag-no-drop/clickable
* target area.
*/
- ArdourCanvas::Rectangle* _canvas_bottom_rect;
- bool canvas_bottom_rect_event (GdkEvent* event);
+ ArdourCanvas::Rectangle* _canvas_drop_zone;
+ bool canvas_drop_zone_event (GdkEvent* event);
enum RulerType {
ruler_metric_timecode = 0,
ruler_video_timeline = 10,
};
- static GtkCustomMetric ruler_metrics[4];
Glib::RefPtr<Gtk::ToggleAction> ruler_timecode_action;
Glib::RefPtr<Gtk::ToggleAction> ruler_bbt_action;
Glib::RefPtr<Gtk::ToggleAction> ruler_samples_action;
Glib::RefPtr<Gtk::ToggleAction> ruler_cd_marker_action;
bool no_ruler_shown_update;
- bool ruler_button_press (GdkEventButton*);
- bool ruler_button_release (GdkEventButton*);
- bool ruler_mouse_motion (GdkEventMotion*);
- bool ruler_scroll (GdkEventScroll* event);
-
Gtk::Widget * ruler_grabbed_widget;
+ RulerDialog* ruler_dialog;
+
void initialize_rulers ();
void update_just_timecode ();
void compute_fixed_ruler_scale (); //calculates the RulerScale of the fixed rulers
void store_ruler_visibility ();
void restore_ruler_visibility ();
- static gint _metric_get_timecode (GtkCustomRulerMark **, gdouble, gdouble, gint);
- static gint _metric_get_bbt (GtkCustomRulerMark **, gdouble, gdouble, gint);
- static gint _metric_get_samples (GtkCustomRulerMark **, gdouble, gdouble, gint);
- static gint _metric_get_minsec (GtkCustomRulerMark **, gdouble, gdouble, gint);
-
enum MinsecRulerScale {
minsec_show_seconds,
minsec_show_minutes,
ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin,
ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end);
- gint metric_get_timecode (GtkCustomRulerMark **, gdouble, gdouble, gint);
- gint metric_get_bbt (GtkCustomRulerMark **, gdouble, gdouble, gint);
- gint metric_get_samples (GtkCustomRulerMark **, gdouble, gdouble, gint);
- gint metric_get_minsec (GtkCustomRulerMark **, gdouble, gdouble, gint);
-
- Gtk::Widget *_ruler_separator;
- GtkWidget *_timecode_ruler;
- GtkWidget *_bbt_ruler;
- GtkWidget *_samples_ruler;
- GtkWidget *_minsec_ruler;
- Gtk::Widget *timecode_ruler;
- Gtk::Widget *bbt_ruler;
- Gtk::Widget *samples_ruler;
- Gtk::Widget *minsec_ruler;
- static Editor *ruler_editor;
+ ArdourCanvas::Ruler* timecode_ruler;
+ ArdourCanvas::Ruler* bbt_ruler;
+ ArdourCanvas::Ruler* samples_ruler;
+ ArdourCanvas::Ruler* minsec_ruler;
static const double timebar_height;
guint32 visible_timebars;
/* videtimline related actions */
Gtk::Label videotl_label;
- ArdourCanvas::Group* videotl_group;
+ ArdourCanvas::Container* videotl_group;
Glib::RefPtr<Gtk::ToggleAction> ruler_video_action;
Glib::RefPtr<Gtk::ToggleAction> xjadeo_proc_action;
Glib::RefPtr<Gtk::ToggleAction> xjadeo_ontop_action;
void export_video (bool range = false);
void toggle_region_video_lock ();
- Gtk::VBox time_bars_vbox;
-
friend class EditorCursor;
EditorCursor* playhead_cursor;
Gtk::Menu *edit_controls_left_menu;
Gtk::Menu *edit_controls_right_menu;
- Gtk::VBox ruler_label_vbox;
Gtk::VBox track_canvas_vbox;
- Gtk::VBox time_canvas_vbox;
Gtk::VBox edit_controls_vbox;
Gtk::HBox edit_controls_hbox;
/* track views */
TrackViewList track_views;
- std::pair<TimeAxisView*, double> trackview_by_y_position (double);
- TimeAxisView* axis_view_from_route (boost::shared_ptr<ARDOUR::Route>) const;
+ std::pair<TimeAxisView*, double> trackview_by_y_position (double, bool trackview_relative_offset = true) const;
+ RouteTimeAxisView* axis_view_from_route (boost::shared_ptr<ARDOUR::Route>) const;
TrackViewList get_tracks_for_range_action () const;
void temporal_zoom_by_frame (framepos_t start, framepos_t end);
void temporal_zoom_to_frame (bool coarser, framepos_t frame);
- void insert_region_list_drag (boost::shared_ptr<ARDOUR::Region>, int x, int y);
void insert_region_list_selection (float times);
- void insert_route_list_drag (boost::shared_ptr<ARDOUR::Route>, int x, int y);
-
/* import & embed */
void add_external_audio_action (Editing::ImportMode);
int target_regions, int target_tracks, boost::shared_ptr<ARDOUR::Track>&, bool add_channel_suffix);
int finish_bringing_in_material (boost::shared_ptr<ARDOUR::Region> region, uint32_t, uint32_t, framepos_t& pos, Editing::ImportMode mode,
- boost::shared_ptr<ARDOUR::Track>& existing_track);
+ boost::shared_ptr<ARDOUR::Track>& existing_track, const std::string& new_track_name);
boost::shared_ptr<ARDOUR::AudioTrack> get_nth_selected_audio_track (int nth) const;
boost::shared_ptr<ARDOUR::MidiTrack> get_nth_selected_midi_track (int nth) const;
DragManager* _drags;
void escape ();
+ void lock ();
+ void unlock ();
+ Gtk::Dialog* lock_dialog;
+
+ struct timeval last_event_time;
+ bool generic_event_handler (GdkEvent*);
+ bool lock_timeout_callback ();
+ void start_lock_event_timing ();
Gtk::Menu fade_context_menu;
- void popup_fade_context_menu (int, int, ArdourCanvas::Item*, ItemType);
Gtk::Menu xfade_in_context_menu;
Gtk::Menu xfade_out_context_menu;
/* Canvas event handlers */
+ bool canvas_scroll_event (GdkEventScroll* event, bool from_canvas);
bool canvas_control_point_event (GdkEvent* event,ArdourCanvas::Item*, ControlPoint*);
bool canvas_line_event (GdkEvent* event,ArdourCanvas::Item*, AutomationLine*);
bool canvas_selection_rect_event (GdkEvent* event,ArdourCanvas::Item*, SelectionRect*);
bool canvas_start_xfade_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
bool canvas_end_xfade_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
bool canvas_fade_in_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
- bool canvas_fade_in_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
+ bool canvas_fade_in_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*, bool trim = false);
bool canvas_fade_out_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
- bool canvas_fade_out_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*);
+ bool canvas_fade_out_handle_event (GdkEvent* event,ArdourCanvas::Item*, AudioRegionView*, bool trim = false);
bool canvas_region_view_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
+ bool canvas_wave_view_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
bool canvas_frame_handle_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
bool canvas_region_view_name_highlight_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
bool canvas_region_view_name_event (GdkEvent* event,ArdourCanvas::Item*, RegionView*);
bool canvas_automation_track_event(GdkEvent* event, ArdourCanvas::Item*, AutomationTimeAxisView*);
bool canvas_note_event (GdkEvent* event, ArdourCanvas::Item *);
+ bool canvas_ruler_event (GdkEvent* event, ArdourCanvas::Item *, ItemType);
bool canvas_tempo_bar_event (GdkEvent* event, ArdourCanvas::Item*);
bool canvas_meter_bar_event (GdkEvent* event, ArdourCanvas::Item*);
bool canvas_marker_bar_event (GdkEvent* event, ArdourCanvas::Item*);
friend class EditorRouteGroups;
friend class EditorRegions;
- /** true if the mouse is over a place where region trim can happen */
- bool _over_region_trim_target;
-
/* non-public event handlers */
bool canvas_playhead_cursor_event (GdkEvent* event, ArdourCanvas::Item*);
bool track_canvas_scroll (GdkEventScroll* event);
- bool track_canvas_scroll_event (GdkEventScroll* event);
bool track_canvas_button_press_event (GdkEventButton* event);
bool track_canvas_button_release_event (GdkEventButton* event);
bool track_canvas_motion_notify_event (GdkEventMotion* event);
TempoLines* tempo_lines;
- ArdourCanvas::Group* global_rect_group;
- ArdourCanvas::Group* time_line_group;
+ ArdourCanvas::Container* global_rect_group;
+ ArdourCanvas::Container* time_line_group;
void hide_measures ();
void draw_measures (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
bool entered_track_canvas (GdkEventCrossing*);
void set_entered_track (TimeAxisView*);
void set_entered_regionview (RegionView*);
- void ensure_track_visible (TimeAxisView*);
gint left_automation_track ();
void reset_canvas_action_sensitivity (bool);
Gtk::MenuItem& action_menu_item (std::string const &);
void action_pre_activated (Glib::RefPtr<Gtk::Action> const &);
- void set_canvas_cursor_for_region_view (double, RegionView *);
-
MouseCursors* _cursors;
void follow_mixer_selection ();
using namespace Glib;
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Editing;
ActionManager::register_action (editor_actions, "escape", _("Break drag or deselect all"), sigc::mem_fun (*this, &Editor::escape));
+ /* We don't bother registering "unlock" because it would be insensitive
+ when required. Editor::unlock() must be invoked directly.
+ */
+ ActionManager::register_action (editor_actions, "lock", _("Lock"), sigc::mem_fun (*this, &Editor::lock));
+
toggle_reg_sens (editor_actions, "show-editor-mixer", _("Show Editor Mixer"), sigc::mem_fun (*this, &Editor::editor_mixer_button_toggled));
toggle_reg_sens (editor_actions, "show-editor-list", _("Show Editor List"), sigc::mem_fun (*this, &Editor::editor_list_button_toggled));
std::string binding_file;
- if (find_file_in_search_path (ardour_config_search_path(), "editor.bindings", binding_file)) {
+ if (find_file (ardour_config_search_path(), "editor.bindings", binding_file)) {
key_bindings.load (binding_file);
info << string_compose (_("Loaded editor bindings from %1"), binding_file) << endmsg;
} else {
#include "audio_time_axis.h"
#include "midi_time_axis.h"
#include "session_import_dialog.h"
-#include "utils.h"
#include "gui_thread.h"
#include "interthread_progress_window.h"
#include "mouse_cursors.h"
SoundFileInfo finfo;
int ret = 0;
- set_canvas_cursor (_cursors->wait);
- gdk_flush ();
+ push_canvas_cursor (_cursors->wait);
+ gdk_flush ();
for (vector<string>::iterator p = paths.begin(); p != paths.end(); ++p) {
}
}
- set_canvas_cursor (_cursors->wait);
-
for (int n = 0; n < finfo.channels; ++n) {
try {
boost::shared_ptr<Source> s;
- if ((s = _session->source_by_path_and_channel (path, n)) == 0) {
+ if ((s = _session->audio_source_by_path_and_channel (path, n)) == 0) {
source = boost::dynamic_pointer_cast<AudioFileSource> (
SourceFactory::createExternal (DataType::AUDIO, *_session,
ret = add_sources (paths, sources, pos, disposition, mode, target_regions, target_tracks, track, true);
out:
- set_canvas_cursor (current_canvas_cursor);
+ pop_canvas_cursor ();
return ret;
}
uint32_t input_chan = 0;
uint32_t output_chan = 0;
bool use_timestamp;
-
+ vector<string> track_names;
+
use_timestamp = (pos == -1);
// kludge (for MIDI we're abusing "channel" for "track" here)
regions.push_back (r);
+ /* if we're creating a new track, name it after the cleaned-up
+ * and "merged" region name.
+ */
+
+ track_names.push_back (region_name);
} else if (target_regions == -1 || target_regions > 1) {
region_name = (*x)->name();
}
- switch (sources.size()) {
- /* zero and one channel handled
- by previous if() condition
- */
- case 2:
+ if (sources.size() == 2) {
if (n == 0) {
region_name += "-L";
} else {
region_name += "-R";
}
- break;
- default:
- region_name += (char) '-';
- region_name += (char) ('1' + n);
- break;
+ } else if (sources.size() > 2) {
+ region_name += string_compose ("-%1", n+1);
}
+ track_names.push_back (region_name);
+
} else {
if (fs) {
region_name = region_name_from_path (fs->path(), false, false, sources.size(), n);
- } else{
+ } else {
region_name = (*x)->name();
}
+
+ track_names.push_back (PBD::basename_nosuffix (paths[n]));
}
PropertyList plist;
framepos_t rlen = 0;
begin_reversible_command (Operations::insert_file);
+
+ /* we only use tracks names when importing to new tracks, but we
+ * require that one is defined for every region, just to keep
+ * the API simpler.
+ */
+ assert (regions.size() == track_names.size());
for (vector<boost::shared_ptr<Region> >::iterator r = regions.begin(); r != regions.end(); ++r, ++n) {
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion> (*r);
pos = get_preferred_edit_position ();
}
}
-
-
- finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track);
+
+ finish_bringing_in_material (*r, input_chan, output_chan, pos, mode, track, track_names[n]);
rlen = (*r)->length();
int
Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t in_chans, uint32_t out_chans, framepos_t& pos,
- ImportMode mode, boost::shared_ptr<Track>& existing_track)
+ ImportMode mode, boost::shared_ptr<Track>& existing_track, const string& new_track_name)
{
boost::shared_ptr<AudioRegion> ar = boost::dynamic_pointer_cast<AudioRegion>(region);
boost::shared_ptr<MidiRegion> mr = boost::dynamic_pointer_cast<MidiRegion>(region);
existing_track = mt.front();
}
- existing_track->set_name (region->name());
+ if (!new_track_name.empty()) {
+ existing_track->set_name (new_track_name);
+ } else {
+ existing_track->set_name (region->name());
+ }
}
boost::shared_ptr<Playlist> playlist = existing_track->playlist();
#include "canvas/canvas.h"
#include "canvas/rectangle.h"
#include "canvas/pixbuf.h"
+#include "canvas/scroll_group.h"
#include "canvas/text.h"
#include "canvas/debug.h"
#include "ardour_ui.h"
+#include "automation_time_axis.h"
#include "editor.h"
#include "global_signals.h"
#include "editing.h"
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Glib;
using namespace Gtkmm2ext;
using namespace Editing;
-/* XXX this is a hack. it ought to be the maximum value of an framepos_t */
-
-const double max_canvas_coordinate = (double) UINT32_MAX;
-
void
Editor::initialize_canvas ()
{
_track_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, vertical_adjustment);
_track_canvas = _track_canvas_viewport->canvas ();
- _time_bars_canvas_viewport = new ArdourCanvas::GtkCanvasViewport (horizontal_adjustment, unused_adjustment);
- _time_bars_canvas = _time_bars_canvas_viewport->canvas ();
-
+ /* scroll group for items that should not automatically scroll
+ * (e.g verbose cursor). It shares the canvas coordinate space.
+ */
+ no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
+
+ ArdourCanvas::ScrollGroup* hsg;
+ ArdourCanvas::ScrollGroup* hg;
+ ArdourCanvas::ScrollGroup* vg;
+
+ hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
+ ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
+ ArdourCanvas::ScrollGroup::ScrollsHorizontally));
+ CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
+ _track_canvas->add_scroller (*hsg);
+
+ v_scroll_group = vg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsVertically);
+ CANVAS_DEBUG_NAME (v_scroll_group, "canvas v scroll");
+ _track_canvas->add_scroller (*vg);
+
+ h_scroll_group = hg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), ArdourCanvas::ScrollGroup::ScrollsHorizontally);
+ CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
+ _track_canvas->add_scroller (*hg);
+
_verbose_cursor = new VerboseCursor (this);
/* on the bottom, an image */
// logo_item->property_width_set() = true;
// logo_item->show ();
}
-
+
/*a group to hold global rects like punch/loop indicators */
- global_rect_group = new ArdourCanvas::Group (_track_canvas->root());
+ global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
transport_loop_range_rect = new ArdourCanvas::Rectangle (global_rect_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, ArdourCanvas::COORD_MAX));
transport_punch_range_rect->hide();
/*a group to hold time (measure) lines */
- time_line_group = new ArdourCanvas::Group (_track_canvas->root());
+ time_line_group = new ArdourCanvas::Container (hv_scroll_group);
CANVAS_DEBUG_NAME (time_line_group, "time line group");
- _trackview_group = new ArdourCanvas::Group (_track_canvas->root());
+ _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
- _region_motion_group = new ArdourCanvas::Group (_trackview_group);
- CANVAS_DEBUG_NAME (_region_motion_group, "Canvas Region Motion");
+
+ // used to show zoom mode active zooming
+ zoom_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
+ zoom_rect->hide();
+ zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
+
+ // used as rubberband rect
+ rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
+ rubberband_rect->hide();
+
+ /* a group to hold stuff while it gets dragged around. Must be the
+ * uppermost (last) group with hv_scroll_group as a parent
+ */
+ _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
+ CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
+
+ /* TIME BAR CANVAS */
+
+ _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
+ CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
+
+ cd_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
+ CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
+ /* the vide is temporarily placed a the same location as the
+ cd_marker_group, but is moved later.
+ */
+ videotl_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
+ CANVAS_DEBUG_NAME (videotl_group, "videotl group");
+ marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
+ CANVAS_DEBUG_NAME (marker_group, "marker group");
+ transport_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
+ CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
+ range_marker_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
+ CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
+ tempo_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
+ CANVAS_DEBUG_NAME (tempo_group, "tempo group");
+ meter_group = new ArdourCanvas::Container (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
+ CANVAS_DEBUG_NAME (meter_group, "meter group");
- meter_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
- meter_bar = new ArdourCanvas::Rectangle (meter_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+ meter_bar = new ArdourCanvas::Rectangle (meter_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
CANVAS_DEBUG_NAME (meter_bar, "meter Bar");
meter_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
- tempo_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
- tempo_bar = new ArdourCanvas::Rectangle (tempo_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+ tempo_bar = new ArdourCanvas::Rectangle (tempo_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
CANVAS_DEBUG_NAME (tempo_bar, "Tempo Bar");
tempo_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
- range_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
- range_marker_bar = new ArdourCanvas::Rectangle (range_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+ range_marker_bar = new ArdourCanvas::Rectangle (range_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
CANVAS_DEBUG_NAME (range_marker_bar, "Range Marker Bar");
range_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
- transport_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
- transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+ transport_marker_bar = new ArdourCanvas::Rectangle (transport_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
CANVAS_DEBUG_NAME (transport_marker_bar, "transport Marker Bar");
transport_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
- marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
- marker_bar = new ArdourCanvas::Rectangle (marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+ marker_bar = new ArdourCanvas::Rectangle (marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
CANVAS_DEBUG_NAME (marker_bar, "Marker Bar");
marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
- cd_marker_bar_group = new ArdourCanvas::Group (_time_bars_canvas->root ());
- cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_bar_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
+ cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
-
- _time_markers_group = new ArdourCanvas::Group (_time_bars_canvas->root());
-
- cd_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, 0.0));
- CANVAS_DEBUG_NAME (cd_marker_group, "cd marker group");
- /* the vide is temporarily placed a the same location as the
- cd_marker_group, but is moved later.
- */
- videotl_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple(0.0, 0.0));
- CANVAS_DEBUG_NAME (videotl_group, "videotl group");
- marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, timebar_height + 1.0));
- CANVAS_DEBUG_NAME (marker_group, "marker group");
- transport_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 2.0) + 1.0));
- CANVAS_DEBUG_NAME (transport_marker_group, "transport marker group");
- range_marker_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 3.0) + 1.0));
- CANVAS_DEBUG_NAME (range_marker_group, "range marker group");
- tempo_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 4.0) + 1.0));
- CANVAS_DEBUG_NAME (tempo_group, "tempo group");
- meter_group = new ArdourCanvas::Group (_time_markers_group, ArdourCanvas::Duple (0.0, (timebar_height * 5.0) + 1.0));
- CANVAS_DEBUG_NAME (meter_group, "meter group");
ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
-
+
cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
cd_marker_bar_drag_rect->set_outline (false);
transport_bar_drag_rect->set_outline (false);
transport_bar_drag_rect->hide ();
- transport_punchin_line = new ArdourCanvas::Line (_track_canvas->root());
+ transport_punchin_line = new ArdourCanvas::Line (hv_scroll_group);
transport_punchin_line->set_x0 (0);
transport_punchin_line->set_y0 (0);
transport_punchin_line->set_x1 (0);
transport_punchin_line->set_y1 (ArdourCanvas::COORD_MAX);
transport_punchin_line->hide ();
- transport_punchout_line = new ArdourCanvas::Line (_track_canvas->root());
+ transport_punchout_line = new ArdourCanvas::Line (hv_scroll_group);
transport_punchout_line->set_x0 (0);
transport_punchout_line->set_y0 (0);
transport_punchout_line->set_x1 (0);
transport_punchout_line->set_y1 (ArdourCanvas::COORD_MAX);
transport_punchout_line->hide();
- // used to show zoom mode active zooming
- zoom_rect = new ArdourCanvas::Rectangle (_track_canvas->root(), ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
- zoom_rect->hide();
- zoom_rect->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_zoom_rect_event), (ArdourCanvas::Item*) 0));
-
- // used as rubberband rect
- rubberband_rect = new ArdourCanvas::Rectangle (_trackview_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
- rubberband_rect->hide();
-
tempo_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_tempo_bar_event), tempo_bar));
meter_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_meter_bar_event), meter_bar));
marker_bar->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_marker_bar_event), marker_bar));
}
- _canvas_bottom_rect = new ArdourCanvas::Rectangle (_track_canvas->root(), ArdourCanvas::Rect (0.0, 0.0, max_canvas_coordinate, 20));
+ _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
/* this thing is transparent */
- _canvas_bottom_rect->set_fill (false);
- _canvas_bottom_rect->set_outline (false);
- _canvas_bottom_rect->Event.connect (sigc::mem_fun (*this, &Editor::canvas_bottom_rect_event));
+ _canvas_drop_zone->set_fill (false);
+ _canvas_drop_zone->set_outline (false);
+ _canvas_drop_zone->Event.connect (sigc::mem_fun (*this, &Editor::canvas_drop_zone_event));
/* these signals will initially be delivered to the canvas itself, but if they end up remaining unhandled, they are passed to Editor-level
handlers.
*/
- _track_canvas->signal_scroll_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_scroll_event));
+ _track_canvas->signal_scroll_event().connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_scroll_event), true));
_track_canvas->signal_motion_notify_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_motion_notify_event));
_track_canvas->signal_button_press_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_press_event));
_track_canvas->signal_button_release_event().connect (sigc::mem_fun (*this, &Editor::track_canvas_button_release_event));
_track_canvas_viewport->signal_size_allocate().connect (sigc::mem_fun(*this, &Editor::track_canvas_viewport_allocate));
+ initialize_rulers ();
+
ColorsChanged.connect (sigc::mem_fun (*this, &Editor::color_handler));
color_handler();
_visible_canvas_width = _canvas_viewport_allocation.get_width ();
_visible_canvas_height = _canvas_viewport_allocation.get_height ();
+ _canvas_drop_zone->set_y1 (_canvas_drop_zone->y0() + (_visible_canvas_height - 20.0));
+
// SHOWTRACKS
if (height_changed) {
* (the drag-n-drop zone) is, in fact, at the bottom.
*/
- _canvas_bottom_rect->set_position (ArdourCanvas::Duple (0, h));
+ _canvas_drop_zone->set_position (ArdourCanvas::Duple (0, h));
/* track controls layout must span the full height of "h" (all tracks)
* plus the bottom rect.
*/
- h += _canvas_bottom_rect->height ();
+ h += _canvas_drop_zone->height ();
/* set the height of the scrollable area (i.e. the sum of all contained widgets)
* for the controls layout. The size request is set elsewhere.
}
- std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos);
+ std::pair<TimeAxisView*, int> const tvp = trackview_by_y_position (ypos, false);
if (tvp.first == 0) {
/* drop onto canvas background: create new tracks */
if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
- /* D-n-D coordinates are window-relative, so convert to "world" coordinates
+ /* D-n-D coordinates are window-relative, so convert to canvas coordinates
*/
ev.type = GDK_BUTTON_RELEASE;
void
Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
{
- if (!Config->get_autoscroll_editor ()) {
+ if (!Config->get_autoscroll_editor () || autoscroll_active ()) {
return;
}
+ /* define a rectangular boundary for scrolling. If the mouse moves
+ * outside of this area and/or continue to be outside of this area,
+ * then we will continuously auto-scroll the canvas in the appropriate
+ * direction(s)
+ *
+ * the boundary is defined in coordinates relative to the toplevel
+ * window since that is what we're going to call ::get_pointer() on
+ * during autoscrolling to determine if we're still outside the
+ * boundary or not.
+ */
ArdourCanvas::Rect scrolling_boundary;
Gtk::Allocation alloc;
-
+ int cx, cy;
+
if (from_headers) {
alloc = controls_layout.get_allocation ();
- } else {
+ } else {
alloc = _track_canvas_viewport->get_allocation ();
+ cx = alloc.get_x();
+ cy = alloc.get_y();
+
+ /* reduce height by the height of the timebars, which happens
+ to correspond to the position of the hv_scroll_group.
+ */
+ alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
+ alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
+
+ /* now reduce it again so that we start autoscrolling before we
+ * move off the top or bottom of the canvas
+ */
+
+ alloc.set_height (alloc.get_height() - 20);
+ alloc.set_y (alloc.get_y() + 10);
+
/* the effective width of the autoscroll boundary so
that we start scrolling before we hit the edge.
alloc.set_width (alloc.get_width() - 20);
alloc.set_x (alloc.get_x() + 10);
}
+
}
- scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(),
- alloc.get_x() + alloc.get_width(),
- alloc.get_y() + alloc.get_height());
+ scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
int x, y;
Gdk::ModifierType mask;
get_window()->get_pointer (x, y, mask);
- if ((allow_horiz && (x < scrolling_boundary.x0 || x >= scrolling_boundary.x1)) ||
- (allow_vert && (y < scrolling_boundary.y0 || y >= scrolling_boundary.y1))) {
+ if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
+ (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
}
}
get_window()->get_pointer (x, y, mask);
VisualChange vc;
+ bool vertical_motion = false;
if (autoscroll_horizontal_allowed) {
if (autoscroll_vertical_allowed) {
- const double vertical_pos = vertical_adjustment.get_value();
- double new_pixel = vertical_pos;
- const int speed_factor = 20;
+ // const double vertical_pos = vertical_adjustment.get_value();
+ const int speed_factor = 10;
/* vertical */
- new_pixel = vertical_pos;
-
if (y < autoscroll_boundary.y0) {
/* scroll to make higher tracks visible */
if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
y_motion = scroll_up_one_track ();
+ vertical_motion = true;
}
} else if (y > autoscroll_boundary.y1) {
if (autoscroll_cnt && (autoscroll_cnt % speed_factor == 0)) {
y_motion = scroll_down_one_track ();
-
+ vertical_motion = true;
}
}
no_stop = true;
}
- if (vc.pending) {
+ if (vc.pending || vertical_motion) {
/* change horizontal first */
/* the motion handler expects events in canvas coordinate space */
- /* first convert from Editor window coordinates to canvas
- * window coordinates
+ /* we asked for the mouse position above (::get_pointer()) via
+ * our own top level window (we being the Editor). Convert into
+ * coordinates within the canvas window.
*/
int cx;
int cy;
- /* clamp x and y to remain within the visible area */
+ translate_coordinates (*_track_canvas, x, y, cx, cy);
- x = min (max ((ArdourCanvas::Coord) x, autoscroll_boundary.x0), autoscroll_boundary.x1);
- y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
+ /* clamp x and y to remain within the autoscroll boundary,
+ * which is defined in window coordinates
+ */
- translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
+ x = min (max ((ArdourCanvas::Coord) cx, autoscroll_boundary.x0), autoscroll_boundary.x1);
+ y = min (max ((ArdourCanvas::Coord) cy, autoscroll_boundary.y0), autoscroll_boundary.y1);
+
+ /* now convert from Editor window coordinates to canvas
+ * window coordinates
+ */
ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
ev.x = d.x;
ev.y = d.y;
motion_handler (0, (GdkEvent*) &ev, true);
-
+
} else if (no_stop) {
/* not changing visual state but pointer is outside the scrolling boundary
ev.y = d.y;
motion_handler (0, (GdkEvent*) &ev, true);
-
+
} else {
stop_canvas_autoscroll ();
return false;
}
void
-Editor::_ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top)
+Editor::_ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_top)
{
- double begin = tav.y_position();
- double v = vertical_adjustment.get_value ();
-
- if (!at_top && (begin < v || begin + tav.current_height() > v + _visible_canvas_height)) {
- /* try to put the TimeAxisView roughly central */
- if (begin >= _visible_canvas_height/2.0) {
- begin -= _visible_canvas_height/2.0;
- }
+ if (track.hidden()) {
+ return;
}
- /* Clamp the y pos so that we do not extend beyond the canvas full
- * height.
+ /* compute visible area of trackview group, as offsets from top of
+ * trackview group.
*/
- if (_full_canvas_height - begin < _visible_canvas_height){
- begin = _full_canvas_height - _visible_canvas_height;
+
+ double const current_view_min_y = vertical_adjustment.get_value();
+ double const current_view_max_y = current_view_min_y + vertical_adjustment.get_page_size();
+
+ double const track_min_y = track.y_position ();
+ double const track_max_y = track.y_position () + track.effective_height ();
+
+ if (!at_top &&
+ (track_min_y > current_view_min_y &&
+ track_max_y <= current_view_max_y)) {
+ return;
}
- vertical_adjustment.set_value (begin);
+ double new_value;
+
+ if (track_min_y < current_view_min_y) {
+ // Track is above the current view
+ new_value = track_min_y;
+ } else {
+ // Track is below the current view
+ new_value = track.y_position () + track.effective_height() - vertical_adjustment.get_page_size();
+ }
+
+ vertical_adjustment.set_value(new_value);
}
/** Called when the main vertical_adjustment has changed */
void
Editor::color_handler()
{
+ ArdourCanvas::Color base = ARDOUR_UI::config()->get_canvasvar_RulerBase();
+ ArdourCanvas::Color text = ARDOUR_UI::config()->get_canvasvar_RulerText();
+ timecode_ruler->set_fill_color (base);
+ timecode_ruler->set_outline_color (text);
+ minsec_ruler->set_fill_color (base);
+ minsec_ruler->set_outline_color (text);
+ samples_ruler->set_fill_color (base);
+ samples_ruler->set_outline_color (text);
+ bbt_ruler->set_fill_color (base);
+ bbt_ruler->set_outline_color (text);
+
playhead_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_PlayHead());
- _verbose_cursor->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor());
meter_bar->set_fill_color (ARDOUR_UI::config()->get_canvasvar_MeterBar());
meter_bar->set_outline_color (ARDOUR_UI::config()->get_canvasvar_MarkerBarSeparator());
return sample_to_pixel (leftmost_frame);
}
-void
-Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
-{
- if (save) {
- current_canvas_cursor = cursor;
- }
-
- Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
-
- if (win) {
- _track_canvas->get_window()->set_cursor (*cursor);
- }
-}
-
bool
Editor::track_canvas_key_press (GdkEventKey*)
{
return y;
}
-ArdourCanvas::Group*
-Editor::get_time_bars_group () const
+ArdourCanvas::GtkCanvasViewport*
+Editor::get_track_canvas() const
{
- return _time_bars_canvas->root();
+ return _track_canvas_viewport;
}
-ArdourCanvas::Group*
-Editor::get_track_canvas_group() const
+void
+Editor::set_canvas_cursor (Gdk::Cursor* cursor, bool save)
{
- return _track_canvas->root();
+ if (save) {
+ current_canvas_cursor = cursor;
+ }
+
+ Glib::RefPtr<Gdk::Window> win = _track_canvas->get_window();
+
+ if (win && cursor) {
+ win->set_cursor (*cursor);
+ }
}
-ArdourCanvas::GtkCanvasViewport*
-Editor::get_time_bars_canvas() const
+void
+Editor::push_canvas_cursor (Gdk::Cursor* cursor)
{
- return _time_bars_canvas_viewport;
+ if (cursor) {
+ _cursor_stack.push (cursor);
+ set_canvas_cursor (cursor, false);
+ }
}
-ArdourCanvas::GtkCanvasViewport*
-Editor::get_track_canvas() const
+void
+Editor::pop_canvas_cursor ()
{
- return _track_canvas_viewport;
+ if (!_cursor_stack.empty()) {
+ Gdk::Cursor* cursor = _cursor_stack.top ();
+ _cursor_stack.pop ();
+ set_canvas_cursor (cursor, false);
+ }
+}
+
+Gdk::Cursor*
+Editor::which_grabber_cursor () const
+{
+ Gdk::Cursor* c = _cursors->grabber;
+
+ if (_internal_editing) {
+ switch (mouse_mode) {
+ case MouseDraw:
+ c = _cursors->midi_pencil;
+ break;
+
+ case MouseObject:
+ c = _cursors->grabber_note;
+ break;
+
+ case MouseTimeFX:
+ c = _cursors->midi_resize;
+ break;
+
+ case MouseRange:
+ c = _cursors->grabber_note;
+ break;
+
+ default:
+ break;
+ }
+
+ } else {
+
+ switch (_edit_point) {
+ case EditAtMouse:
+ c = _cursors->grabber_edit_point;
+ break;
+ default:
+ boost::shared_ptr<Movable> m = _movable.lock();
+ if (m && m->locked()) {
+ c = _cursors->speaker;
+ }
+ break;
+ }
+ }
+
+ return c;
+}
+
+Gdk::Cursor*
+Editor::which_trim_cursor (bool left) const
+{
+ if (!entered_regionview) {
+ return 0;
+ }
+
+ Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
+
+ if (left) {
+
+ if (ct & Trimmable::FrontTrimEarlier) {
+ return _cursors->left_side_trim;
+ } else {
+ return _cursors->left_side_trim_right_only;
+ }
+ } else {
+ if (ct & Trimmable::EndTrimLater) {
+ return _cursors->right_side_trim;
+ } else {
+ return _cursors->right_side_trim_left_only;
+ }
+ }
+}
+
+Gdk::Cursor*
+Editor::which_mode_cursor () const
+{
+ Gdk::Cursor* mode_cursor = 0;
+
+ switch (mouse_mode) {
+ case MouseRange:
+ mode_cursor = _cursors->selector;
+ if (_internal_editing) {
+ mode_cursor = which_grabber_cursor();
+ }
+ break;
+
+ case MouseObject:
+ /* don't use mode cursor, pick a grabber cursor based on the item */
+ break;
+
+ case MouseDraw:
+ mode_cursor = _cursors->midi_pencil;
+ break;
+
+ case MouseGain:
+ mode_cursor = _cursors->cross_hair;
+ break;
+
+ case MouseZoom:
+ if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
+ mode_cursor = _cursors->zoom_out;
+ } else {
+ mode_cursor = _cursors->zoom_in;
+ }
+ break;
+
+ case MouseTimeFX:
+ mode_cursor = _cursors->time_fx; // just use playhead
+ break;
+
+ case MouseAudition:
+ mode_cursor = _cursors->speaker;
+ break;
+ }
+
+ /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
+ if (!_internal_editing && get_smart_mode() ) {
+
+ double x, y;
+ get_pointer_position (x, y);
+
+ if (x >= 0 && y >= 0) {
+
+ vector<ArdourCanvas::Item const *> items;
+
+ /* Note how we choose a specific scroll group to get
+ * items from. This could be problematic.
+ */
+
+ hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
+
+ // first item will be the upper most
+
+ if (!items.empty()) {
+ const ArdourCanvas::Item* i = items.front();
+
+ if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
+ pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
+ if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
+ mode_cursor = _cursors->up_down;
+ }
+ }
+ }
+ }
+ }
+
+ return mode_cursor;
+}
+
+Gdk::Cursor*
+Editor::which_track_cursor () const
+{
+ Gdk::Cursor* cursor = 0;
+
+ assert (mouse_mode == MouseObject || get_smart_mode());
+
+ if (!_internal_editing) {
+ switch (_join_object_range_state) {
+ case JOIN_OBJECT_RANGE_NONE:
+ case JOIN_OBJECT_RANGE_OBJECT:
+ cursor = which_grabber_cursor ();
+ break;
+ case JOIN_OBJECT_RANGE_RANGE:
+ cursor = _cursors->selector;
+ break;
+ }
+ }
+
+ return cursor;
+}
+
+bool
+Editor::reset_canvas_cursor ()
+{
+ if (!is_drawable()) {
+ return false;
+ }
+
+ Gdk::Cursor* cursor = which_mode_cursor ();
+
+ if (cursor) {
+ set_canvas_cursor (cursor);
+ return true;
+ }
+
+ return false;
+}
+
+void
+Editor::choose_canvas_cursor_on_entry (GdkEventCrossing* /*event*/, ItemType type)
+{
+ Gdk::Cursor* cursor = 0;
+
+ if (_drags->active()) {
+ return;
+ }
+
+ cursor = which_mode_cursor ();
+
+ if (mouse_mode == MouseObject || get_smart_mode ()) {
+
+ /* find correct cursor to use in object/smart mode */
+
+ switch (type) {
+ case RegionItem:
+ case RegionViewNameHighlight:
+ case RegionViewName:
+ case WaveItem:
+ case StreamItem:
+ case AutomationTrackItem:
+ cursor = which_track_cursor ();
+ break;
+ case PlayheadCursorItem:
+ switch (_edit_point) {
+ case EditAtMouse:
+ cursor = _cursors->grabber_edit_point;
+ break;
+ default:
+ cursor = _cursors->grabber;
+ break;
+ }
+ break;
+ case SelectionItem:
+ cursor = _cursors->selector;
+ break;
+ case ControlPointItem:
+ cursor = _cursors->fader;
+ break;
+ case GainLineItem:
+ cursor = _cursors->fader;
+ break;
+ case AutomationLineItem:
+ cursor = _cursors->cross_hair;
+ break;
+ case StartSelectionTrimItem:
+ cursor = _cursors->left_side_trim;
+ break;
+ case EndSelectionTrimItem:
+ cursor = _cursors->right_side_trim;
+ break;
+ case FadeInItem:
+ cursor = _cursors->fade_in;
+ break;
+ case FadeInHandleItem:
+ cursor = _cursors->fade_in;
+ break;
+ case FadeInTrimHandleItem:
+ cursor = _cursors->fade_in;
+ break;
+ case FadeOutItem:
+ cursor = _cursors->fade_out;
+ break;
+ case FadeOutHandleItem:
+ cursor = _cursors->fade_out;
+ break;
+ case FadeOutTrimHandleItem:
+ cursor = _cursors->fade_out;
+ break;
+ case NoteItem:
+ cursor = which_grabber_cursor();
+ break;
+ case FeatureLineItem:
+ cursor = _cursors->cross_hair;
+ break;
+ case LeftFrameHandle:
+ cursor = which_trim_cursor (true);
+ break;
+ case RightFrameHandle:
+ cursor = which_trim_cursor (false);
+ break;
+ case StartCrossFadeItem:
+ cursor = _cursors->fade_in;
+ break;
+ case EndCrossFadeItem:
+ cursor = _cursors->fade_out;
+ break;
+ case CrossfadeViewItem:
+ cursor = _cursors->cross_hair;
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (type) {
+ /* These items use the timebar cursor at all times */
+ case TimecodeRulerItem:
+ case MinsecRulerItem:
+ case BBTRulerItem:
+ case SamplesRulerItem:
+ cursor = _cursors->timebar;
+ break;
+
+ /* These items use the grabber cursor at all times */
+ case MeterMarkerItem:
+ case TempoMarkerItem:
+ case MeterBarItem:
+ case TempoBarItem:
+ case MarkerItem:
+ case MarkerBarItem:
+ case RangeMarkerBarItem:
+ case CdMarkerBarItem:
+ case VideoBarItem:
+ case TransportMarkerBarItem:
+ cursor = which_grabber_cursor();
+ break;
+
+ default:
+ break;
+ }
+
+ if (cursor) {
+ set_canvas_cursor (cursor, false);
+ }
}
#include "canvas/canvas.h"
#include "canvas/text.h"
+#include "canvas/scroll_group.h"
#include "editor.h"
#include "keyboard.h"
bool
Editor::track_canvas_scroll (GdkEventScroll* ev)
{
+ if (Keyboard::some_magic_widget_has_focus()) {
+ return false;
+ }
+
framepos_t xdelta;
int direction = ev->direction;
*/
Duple event_coords = _track_canvas->window_to_canvas (Duple (ev->x, ev->y));
-
+
retry:
switch (direction) {
case GDK_SCROLL_UP:
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
if (!current_stepping_trackview) {
step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500);
- std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y);
+ std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y, false);
current_stepping_trackview = p.first;
if (!current_stepping_trackview) {
return false;
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
if (!current_stepping_trackview) {
step_timeout = Glib::signal_timeout().connect (sigc::mem_fun(*this, &Editor::track_height_step_timeout), 500);
- std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y);
+ std::pair<TimeAxisView*, int> const p = trackview_by_y_position (event_coords.y, false);
current_stepping_trackview = p.first;
if (!current_stepping_trackview) {
return false;
}
bool
-Editor::track_canvas_scroll_event (GdkEventScroll *event)
+Editor::canvas_scroll_event (GdkEventScroll *event, bool from_canvas)
{
+ if (from_canvas) {
+ boost::optional<ArdourCanvas::Rect> rulers = _time_markers_group->bounding_box();
+ if (rulers && rulers->contains (Duple (event->x, event->y))) {
+ return canvas_ruler_event ((GdkEvent*) event, timecode_ruler, TimecodeRulerItem);
+ }
+ }
+
_track_canvas->grab_focus();
return track_canvas_scroll (event);
}
return false;
}
-bool
-Editor::track_canvas_motion (GdkEvent *ev)
-{
- if (_verbose_cursor->visible ()) {
- _verbose_cursor->set_position (ev->motion.x + 10, ev->motion.y + 10);
- }
-
- return false;
-}
-
bool
Editor::typed_event (ArdourCanvas::Item* item, GdkEvent *event, ItemType type)
{
break;
case GDK_ENTER_NOTIFY:
- if (event->crossing.detail != GDK_NOTIFY_INFERIOR) {
- set_entered_regionview (rv);
- ret = true;
- }
+ set_entered_regionview (rv);
+ ret = enter_handler (item, event, RegionItem);
break;
case GDK_LEAVE_NOTIFY:
if (event->crossing.detail != GDK_NOTIFY_INFERIOR) {
set_entered_regionview (0);
- ret = true;
+ ret = leave_handler (item, event, RegionItem);
}
break;
return ret;
}
+bool
+Editor::canvas_wave_view_event (GdkEvent *event, ArdourCanvas::Item* item, RegionView* rv)
+{
+ /* we only care about enter events here, required for mouse/cursor
+ * tracking. there is a non-linear (non-child/non-parent) relationship
+ * between various components of a regionview and so when we leave one
+ * of them (e.g. a trim handle) and enter another (e.g. the waveview)
+ * no other items get notified. enter/leave handling does not propagate
+ * in the same way as other events, so we need to catch this because
+ * entering (and leaving) the waveview is equivalent to
+ * entering/leaving the regionview (which is why it is passed in as a
+ * third argument).
+ *
+ * And in fact, we really only care about enter events.
+ */
+
+ bool ret = false;
+
+ if (!rv->sensitive ()) {
+ return false;
+ }
+
+ switch (event->type) {
+ case GDK_ENTER_NOTIFY:
+ set_entered_regionview (rv);
+ ret = enter_handler (item, event, WaveItem);
+ break;
+
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+
bool
Editor::canvas_stream_view_event (GdkEvent *event, ArdourCanvas::Item* item, RouteTimeAxisView *tv)
{
case GDK_ENTER_NOTIFY:
set_entered_track (tv);
- ret = true;
+ ret = enter_handler (item, event, StreamItem);
break;
case GDK_LEAVE_NOTIFY:
- set_entered_track (0);
+ if (event->crossing.detail != GDK_NOTIFY_INFERIOR) {
+ set_entered_track (0);
+ }
+ ret = leave_handler (item, event, StreamItem);
break;
default:
}
bool
-Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv)
+Editor::canvas_fade_in_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv, bool trim)
{
bool ret = false;
clicked_control_point = 0;
clicked_axisview = &rv->get_time_axis_view();
clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
- ret = button_press_handler (item, event, FadeInHandleItem);
+ ret = button_press_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
break;
case GDK_BUTTON_RELEASE:
- ret = button_release_handler (item, event, FadeInHandleItem);
+ ret = button_release_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
maybe_locate_with_edit_preroll ( rv->region()->position() );
break;
break;
case GDK_ENTER_NOTIFY:
- ret = enter_handler (item, event, FadeInHandleItem);
+ ret = enter_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
break;
case GDK_LEAVE_NOTIFY:
- ret = leave_handler (item, event, FadeInHandleItem);
+ ret = leave_handler (item, event, trim ? FadeInTrimHandleItem : FadeInHandleItem);
break;
default:
}
bool
-Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv)
+Editor::canvas_fade_out_handle_event (GdkEvent *event, ArdourCanvas::Item* item, AudioRegionView *rv, bool trim)
{
bool ret = false;
clicked_control_point = 0;
clicked_axisview = &rv->get_time_axis_view();
clicked_routeview = dynamic_cast<RouteTimeAxisView*>(clicked_axisview);
- ret = button_press_handler (item, event, FadeOutHandleItem);
+ ret = button_press_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
break;
case GDK_BUTTON_RELEASE:
- ret = button_release_handler (item, event, FadeOutHandleItem);
+ ret = button_release_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
maybe_locate_with_edit_preroll ( rv->region()->last_frame() - rv->get_fade_out_shape_width() );
break;
break;
case GDK_ENTER_NOTIFY:
- ret = enter_handler (item, event, FadeOutHandleItem);
+ ret = enter_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
break;
case GDK_LEAVE_NOTIFY:
- ret = leave_handler (item, event, FadeOutHandleItem);
+ ret = leave_handler (item, event, trim ? FadeOutTrimHandleItem : FadeOutHandleItem);
break;
default:
return typed_event (item, event, MeterMarkerItem);
}
+bool
+Editor::canvas_ruler_event (GdkEvent *event, ArdourCanvas::Item* item, ItemType type)
+{
+ framepos_t xdelta;
+ bool handled = false;
+
+ if (event->type == GDK_SCROLL) {
+
+ /* scroll events in the rulers are handled a little differently from
+ scrolling elsewhere in the canvas.
+ */
+
+ switch (event->scroll.direction) {
+ case GDK_SCROLL_UP:
+ temporal_zoom_step (false);
+ handled = true;
+ break;
+
+ case GDK_SCROLL_DOWN:
+ temporal_zoom_step (true);
+ handled = true;
+ break;
+
+ case GDK_SCROLL_LEFT:
+ xdelta = (current_page_samples() / 2);
+ if (leftmost_frame > xdelta) {
+ reset_x_origin (leftmost_frame - xdelta);
+ } else {
+ reset_x_origin (0);
+ }
+ handled = true;
+ break;
+
+ case GDK_SCROLL_RIGHT:
+ xdelta = (current_page_samples() / 2);
+ if (max_framepos - xdelta > leftmost_frame) {
+ reset_x_origin (leftmost_frame + xdelta);
+ } else {
+ reset_x_origin (max_framepos - current_page_samples());
+ }
+ handled = true;
+ break;
+
+ default:
+ /* what? */
+ break;
+ }
+ return handled;
+ }
+
+ return typed_event (item, event, type);
+}
+
bool
Editor::canvas_tempo_bar_event (GdkEvent *event, ArdourCanvas::Item* item)
{
}
bool
-Editor::canvas_bottom_rect_event (GdkEvent* event)
+Editor::canvas_drop_zone_event (GdkEvent* event)
{
- cerr << "CBR event, type " << event << endl;
+ GdkEventScroll scroll;
+ ArdourCanvas::Duple winpos;
+
+ switch (event->type) {
+ case GDK_BUTTON_RELEASE:
+ if (event->button.button == 1) {
+ selection->clear_objects ();
+ selection->clear_tracks ();
+ }
+ break;
+
+ case GDK_SCROLL:
+ /* convert coordinates back into window space so that
+ we can just call canvas_scroll_event().
+ */
+ winpos = _track_canvas->canvas_to_window (Duple (event->scroll.x, event->scroll.y));
+ scroll = event->scroll;
+ scroll.x = winpos.x;
+ scroll.y = winpos.y;
+ return canvas_scroll_event (&scroll, true);
+ break;
+
+ default:
+ break;
+ }
+
return true;
}
framepos_t const pos = window_event_sample (&event, &px, &py);
- std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (py);
+ std::pair<TimeAxisView*, int> const tv = trackview_by_y_position (py, false);
if (tv.first != 0) {
#include "canvas/canvas.h"
#include "canvas/debug.h"
+#include "canvas/scroll_group.h"
-#include "utils.h"
#include "editor_cursors.h"
#include "editor.h"
EditorCursor::EditorCursor (Editor& ed, bool (Editor::*callbck)(GdkEvent*,ArdourCanvas::Item*))
: _editor (ed)
- , _time_bars_canvas_item (new ArdourCanvas::Arrow (_editor._time_bars_canvas->root ()))
- , _track_canvas_item (new ArdourCanvas::Line (_editor._track_canvas->root ()))
+ , _track_canvas_item (new ArdourCanvas::Arrow (_editor.get_hscroll_group()))
, _length (1.0)
{
- CANVAS_DEBUG_NAME (_time_bars_canvas_item, "timebars editor cursor");
CANVAS_DEBUG_NAME (_track_canvas_item, "track canvas editor cursor");
- _time_bars_canvas_item->set_show_head (0, true);
- _time_bars_canvas_item->set_head_height (0, 9);
- _time_bars_canvas_item->set_head_width (0, 16);
- _time_bars_canvas_item->set_head_outward (0, false);
- _time_bars_canvas_item->set_show_head (1, false); // head only
-
- _time_bars_canvas_item->set_data ("cursor", this);
+ _track_canvas_item->set_show_head (0, true);
+ _track_canvas_item->set_head_height (0, 9);
+ _track_canvas_item->set_head_width (0, 16);
+ _track_canvas_item->set_head_outward (0, false);
+ _track_canvas_item->set_show_head (1, false); // head only
_track_canvas_item->set_data ("cursor", this);
- _time_bars_canvas_item->Event.connect (sigc::bind (sigc::mem_fun (ed, callbck), _time_bars_canvas_item));
_track_canvas_item->Event.connect (sigc::bind (sigc::mem_fun (ed, callbck), _track_canvas_item));
- _time_bars_canvas_item->set_y1 (ArdourCanvas::COORD_MAX);
_track_canvas_item->set_y1 (ArdourCanvas::COORD_MAX);
_current_frame = 1; /* force redraw at 0 */
double const new_pos = _editor.sample_to_pixel_unrounded (frame);
- if (new_pos != _time_bars_canvas_item->x ()) {
- _time_bars_canvas_item->set_x (new_pos);
- }
-
- if (new_pos != _track_canvas_item->x0 ()) {
- _track_canvas_item->set_x (new_pos, new_pos);
+ if (new_pos != _track_canvas_item->x ()) {
+ _track_canvas_item->set_x (new_pos);
}
_current_frame = frame;
void
EditorCursor::show ()
{
- _time_bars_canvas_item->show ();
_track_canvas_item->show ();
}
void
EditorCursor::hide ()
{
- _time_bars_canvas_item->hide ();
_track_canvas_item->hide ();
}
void
EditorCursor::set_color (ArdourCanvas::Color color)
{
- _time_bars_canvas_item->set_color (color);
- _track_canvas_item->set_outline_color (color);
+ _track_canvas_item->set_color (color);
+}
+
+void
+EditorCursor::set_sensitive (bool yn)
+{
+ _track_canvas_item->set_ignore_events (!yn);
}
*/
#include "pbd/signals.h"
+#include "ardour/types.h"
#include "canvas/arrow.h"
#include "canvas/line.h"
void show ();
void hide ();
void set_color (ArdourCanvas::Color);
+ void set_sensitive (bool);
framepos_t current_frame () const {
return _current_frame;
}
- ArdourCanvas::Line& track_canvas_item () {
+ ArdourCanvas::Arrow& track_canvas_item () {
return *_track_canvas_item;
}
- ArdourCanvas::Arrow& time_bar_canvas_item () {
- return *_time_bars_canvas_item;
- }
-
PBD::Signal1<void, framepos_t> PositionChanged;
private:
Editor& _editor;
- ArdourCanvas::Arrow* _time_bars_canvas_item;
- ArdourCanvas::Line* _track_canvas_item;
+ ArdourCanvas::Arrow* _track_canvas_item;
framepos_t _current_frame;
double _length;
};
#include "ardour/audioengine.h"
#include "ardour/audioregion.h"
+#include "ardour/audio_track.h"
#include "ardour/dB.h"
#include "ardour/midi_region.h"
+#include "ardour/midi_track.h"
#include "ardour/operations.h"
#include "ardour/region_factory.h"
#include "ardour/session.h"
+#include "canvas/scroll_group.h"
+
#include "editor.h"
#include "i18n.h"
#include "keyboard.h"
#include "ardour_ui.h"
#include "gui_thread.h"
#include "control_point.h"
-#include "utils.h"
#include "region_gain_line.h"
#include "editor_drag.h"
#include "audio_time_axis.h"
{
bool r = false;
- _current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
-
- for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
- bool const t = (*i)->motion_handler (e, from_autoscroll);
- if (t) {
- r = true;
- }
-
- }
-
- return r;
-}
-
-bool
-DragManager::window_motion_handler (GdkEvent* e, bool from_autoscroll)
-{
- bool r = false;
+ /* calling this implies that we expect the event to have canvas
+ * coordinates
+ *
+ * Can we guarantee that this is true?
+ */
_current_pointer_frame = _editor->canvas_event_sample (e, &_current_pointer_x, &_current_pointer_y);
for (list<Drag*>::iterator i = _drags.begin(); i != _drags.end(); ++i) {
bool const t = (*i)->motion_handler (e, from_autoscroll);
+ /* run all handlers; return true if at least one of them
+ returns true (indicating that the event has been handled).
+ */
if (t) {
r = true;
}
return j != _drags.end ();
}
-Drag::Drag (Editor* e, ArdourCanvas::Item* i)
+Drag::Drag (Editor* e, ArdourCanvas::Item* i, bool trackview_only)
: _editor (e)
, _item (i)
, _pointer_frame_offset (0)
+ , _trackview_only (trackview_only)
, _move_threshold_passed (false)
, _was_double_click (false)
, _raw_grab_frame (0)
_grab_frame = adjusted_frame (_raw_grab_frame, event);
_last_pointer_frame = _grab_frame;
_last_pointer_x = _grab_x;
+
+ if (_trackview_only) {
+ _grab_y = _grab_y - _editor->get_trackview_group()->canvas_origin().y;
+ }
+
_last_pointer_y = _grab_y;
if (cursor == 0) {
_item->grab ();
-
} else {
/* CAIROCANVAS need a variant here that passes *cursor */
_item->grab ();
-
+ _editor->push_canvas_cursor (cursor);
}
if (_editor->session() && _editor->session()->transport_rolling()) {
finished (event, _move_threshold_passed);
_editor->verbose_cursor()->hide ();
+ _editor->pop_canvas_cursor ();
return _move_threshold_passed;
}
return adjusted_frame (_drags->current_pointer_frame (), event, snap);
}
+double
+Drag::current_pointer_y () const
+{
+ if (!_trackview_only) {
+ return _drags->current_pointer_y ();
+ }
+
+ return _drags->current_pointer_y () - _editor->get_trackview_group()->canvas_origin().y;
+}
+
bool
Drag::motion_handler (GdkEvent* event, bool from_autoscroll)
{
/* check to see if we have moved in any way that matters since the last motion event */
if (_move_threshold_passed &&
(!x_movement_matters() || _last_pointer_frame == adjusted_current_frame (event)) &&
- (!y_movement_matters() || _last_pointer_y == _drags->current_pointer_y ()) ) {
+ (!y_movement_matters() || _last_pointer_y == current_pointer_y ()) ) {
return false;
}
if (!from_autoscroll && !_move_threshold_passed) {
bool const xp = (::llabs (_drags->current_pointer_frame () - _raw_grab_frame) >= threshold.first);
- bool const yp = (::fabs ((_drags->current_pointer_y () - _grab_y)) >= threshold.second);
+ bool const yp = (::fabs ((current_pointer_y () - _grab_y)) >= threshold.second);
_move_threshold_passed = ((xp && x_movement_matters()) || (yp && y_movement_matters()));
}
motion (event, _move_threshold_passed != old_move_threshold_passed);
_last_pointer_x = _drags->current_pointer_x ();
- _last_pointer_y = _drags->current_pointer_y ();
+ _last_pointer_y = current_pointer_y ();
_last_pointer_frame = adjusted_current_frame (event);
}
return true;
}
}
+
return false;
}
void
Drag::show_verbose_cursor_time (framepos_t frame)
{
- _editor->verbose_cursor()->set_time (
- frame,
- _drags->current_pointer_x() + 10,
- _drags->current_pointer_y() + 10
- );
-
+ _editor->verbose_cursor()->set_time (frame);
_editor->verbose_cursor()->show ();
}
void
-Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double xoffset)
+Drag::show_verbose_cursor_duration (framepos_t start, framepos_t end, double /*xoffset*/)
{
- _editor->verbose_cursor()->show (xoffset);
-
- _editor->verbose_cursor()->set_duration (
- start, end,
- _drags->current_pointer_x() + 10,
- _drags->current_pointer_y() + 10
- );
+ _editor->verbose_cursor()->set_duration (start, end);
+ _editor->verbose_cursor()->show ();
}
void
Drag::show_verbose_cursor_text (string const & text)
{
+ _editor->verbose_cursor()->set (text);
_editor->verbose_cursor()->show ();
-
- _editor->verbose_cursor()->set (
- text,
- _drags->current_pointer_x() + 10,
- _drags->current_pointer_y() + 10
- );
}
boost::shared_ptr<Region>
*/
for (list<RegionView*>::const_iterator i = v.begin(); i != v.end(); ++i) {
- _views.push_back (DraggingView (*i, this));
+ _views.push_back (DraggingView (*i, this, &(*i)->get_time_axis_view()));
}
RegionView::RegionViewGoingAway.connect (death_connection, invalidator (*this), boost::bind (&RegionDrag::region_going_away, this, _1), gui_context());
show_verbose_cursor_time (_last_frame_position);
- pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (current_pointer_y ());
if (tv.first) {
_last_pointer_time_axis_view = find_time_axis_view (tv.first);
_last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
void
RegionMotionDrag::motion (GdkEvent* event, bool first_move)
{
- assert (!_views.empty ());
+ double delta_layer = 0;
+ int delta_time_axis_view = 0;
- /* Find the TimeAxisView that the pointer is now over */
- pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ assert (!_views.empty ());
- /* Bail early if we're not over a track */
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
+ /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
- if (!rtv || !rtv->is_track()) {
- _editor->verbose_cursor()->hide ();
- return;
- }
+ /* Find the TimeAxisView that the pointer is now over */
+ pair<TimeAxisView*, double> const r = _editor->trackview_by_y_position (current_pointer_y ());
+ TimeAxisView* tv = r.first;
- if (first_move && tv.first->view()->layer_display() == Stacked) {
- tv.first->view()->set_layer_display (Expanded);
- }
+ if (tv && tv->view()) {
+ double layer = r.second;
- /* Note: time axis views in this method are often expressed as an index into the _time_axis_views vector */
+ if (first_move && tv->view()->layer_display() == Stacked) {
+ tv->view()->set_layer_display (Expanded);
+ }
- /* Here's the current pointer position in terms of time axis view and layer */
- int const current_pointer_time_axis_view = find_time_axis_view (tv.first);
- double const current_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
+ /* Here's the current pointer position in terms of time axis view and layer */
+ int const current_pointer_time_axis_view = find_time_axis_view (tv);
+ double const current_pointer_layer = tv->layer_display() == Overlaid ? 0 : layer;
+
+ /* Work out the change in y */
+ delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
+ delta_layer = current_pointer_layer - _last_pointer_layer;
+ }
+
/* Work out the change in x */
framepos_t pending_region_position;
double const x_delta = compute_x_delta (event, &pending_region_position);
_last_frame_position = pending_region_position;
- /* Work out the change in y */
-
- int delta_time_axis_view = current_pointer_time_axis_view - _last_pointer_time_axis_view;
- double delta_layer = current_pointer_layer - _last_pointer_layer;
-
+ /* Verify change in y */
if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
/* this y movement is not allowed, so do no y movement this time */
delta_time_axis_view = 0;
delta_layer = 0;
}
- if (x_delta == 0 && delta_time_axis_view == 0 && delta_layer == 0 && !first_move) {
+ if (x_delta == 0 && (tv && tv->view() && delta_time_axis_view == 0) && delta_layer == 0 && !first_move) {
/* haven't reached next snap point, and we're not switching
trackviews nor layers. nothing to do.
*/
}
if (first_move) {
-
rv->drag_start ();
- /* Reparent to a non scrolling group so that we can keep the
- region selection above all time axis views.
- Reparenting means that we will have to move the region view
- within its new parent, as the two parent groups have different coordinates.
+ /* reparent the regionview into a group above all
+ * others
+ */
+
+ ArdourCanvas::Item* rvg = rv->get_canvas_group();
+ Duple rv_canvas_offset = rvg->parent()->canvas_origin ();
+ Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
+ rv->get_canvas_group()->reparent (_editor->_drag_motion_group);
+ /* move the item so that it continues to appear at the
+ same location now that its parent has changed.
*/
-
- ArdourCanvas::Group* rvg = rv->get_canvas_group();
- Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
-
- rv->get_canvas_group()->reparent (_editor->_region_motion_group);
-
- rv->fake_set_opaque (true);
- rvg->set_position (rv_canvas_offset);
+ rvg->move (rv_canvas_offset - dmg_canvas_offset);
}
/* If we have moved tracks, we'll fudge the layer delta so that the
this_delta_layer = - i->layer;
}
- /* The TimeAxisView that this region is now on */
- TimeAxisView* tv = _time_axis_views[i->time_axis_view + delta_time_axis_view];
+ if (tv) {
- /* Ensure it is moved from stacked -> expanded if appropriate */
- if (tv->view()->layer_display() == Stacked) {
- tv->view()->set_layer_display (Expanded);
- }
+ int track_index;
+
+ if (i->time_axis_view >= 0) {
+ track_index = i->time_axis_view + delta_time_axis_view;
+ } else {
+ track_index = _time_axis_views.size() - 1 + delta_time_axis_view;
+ }
+
+ if (track_index < 0 || track_index >= (int) _time_axis_views.size()) {
+ continue;
+ }
+
+ /* The TimeAxisView that this region is now over */
+ TimeAxisView* current_tv = _time_axis_views[track_index];
+
+ /* Ensure it is moved from stacked -> expanded if appropriate */
+ if (current_tv->view()->layer_display() == Stacked) {
+ current_tv->view()->set_layer_display (Expanded);
+ }
- /* We're only allowed to go -ve in layer on Expanded views */
- if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
- this_delta_layer = - i->layer;
- }
+ /* We're only allowed to go -ve in layer on Expanded views */
+ if (current_tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
+ this_delta_layer = - i->layer;
+ }
- /* Set height */
- rv->set_height (tv->view()->child_height ());
+ /* Set height */
+ rv->set_height (current_tv->view()->child_height ());
- /* Update show/hidden status as the region view may have come from a hidden track,
- or have moved to one.
- */
- if (tv->hidden ()) {
- rv->get_canvas_group()->hide ();
- } else {
- rv->get_canvas_group()->show ();
- }
+ /* Update show/hidden status as the region view may have come from a hidden track,
+ or have moved to one.
+ */
+ if (current_tv->hidden ()) {
+ rv->get_canvas_group()->hide ();
+ } else {
+ rv->get_canvas_group()->show ();
+ }
- /* Update the DraggingView */
- i->time_axis_view += delta_time_axis_view;
- i->layer += this_delta_layer;
+ /* Update the DraggingView */
+ i->time_axis_view = track_index;
+ i->layer += this_delta_layer;
- if (_brushing) {
- _editor->mouse_brush_insert_region (rv, pending_region_position);
- } else {
- double x = 0;
- double y = 0;
+ if (_brushing) {
+ _editor->mouse_brush_insert_region (rv, pending_region_position);
+ } else {
+ Duple track_origin;
- /* Get the y coordinate of the top of the track that this region is now on */
- tv->canvas_display()->item_to_canvas (x, y);
+ /* Get the y coordinate of the top of the track that this region is now over */
+ track_origin = current_tv->canvas_display()->item_to_canvas (track_origin);
+
+ /* And adjust for the layer that it should be on */
+ StreamView* cv = current_tv->view ();
+ switch (cv->layer_display ()) {
+ case Overlaid:
+ break;
+ case Stacked:
+ track_origin.y += (cv->layers() - i->layer - 1) * cv->child_height ();
+ break;
+ case Expanded:
+ track_origin.y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
+ break;
+ }
- /* And adjust for the layer that it should be on */
- StreamView* cv = tv->view ();
- switch (cv->layer_display ()) {
- case Overlaid:
- break;
- case Stacked:
- y += (cv->layers() - i->layer - 1) * cv->child_height ();
- break;
- case Expanded:
- y += (cv->layers() - i->layer - 0.5) * 2 * cv->child_height ();
- break;
+ /* need to get the parent of the regionview
+ * canvas group and get its position in
+ * equivalent coordinate space as the trackview
+ * we are now dragging over.
+ */
+
+ /* Now move the region view */
+ rv->move (x_delta, track_origin.y - rv->get_canvas_group()->canvas_origin().y);
}
+ } else {
- /* Now move the region view */
- rv->move (x_delta, y - rv->get_canvas_group()->position().y);
- }
+ /* Only move the region into the empty dropzone at the bottom if the pointer
+ * is down there.
+ */
+
+ if (current_pointer_y() >= 0) {
+
+ Coord last_track_bottom_edge;
+ if (!_time_axis_views.empty()) {
+ TimeAxisView* last = _time_axis_views.back();
+ last_track_bottom_edge = last->canvas_display()->canvas_origin ().y + last->effective_height();
+ } else {
+ last_track_bottom_edge = 0;
+ }
+ rv->move (x_delta, last_track_bottom_edge - rv->get_canvas_group()->canvas_origin().y);
+ i->time_axis_view = -1;
+ }
+ }
+
} /* foreach region */
_total_x_delta += x_delta;
}
nrv->get_canvas_group()->show ();
- new_regionviews.push_back (DraggingView (nrv, this));
+ new_regionviews.push_back (DraggingView (nrv, this, i->initial_time_axis_view));
/* swap _primary to the copy */
_editor->maybe_locate_with_edit_preroll (_editor->get_selection().regions.start());
}
+RouteTimeAxisView*
+RegionMoveDrag::create_destination_time_axis (boost::shared_ptr<Region> region, TimeAxisView* original)
+{
+ /* Add a new track of the correct type, and return the RouteTimeAxisView that is created to display the
+ new track.
+ */
+
+ try {
+ if (boost::dynamic_pointer_cast<AudioRegion> (region)) {
+ list<boost::shared_ptr<AudioTrack> > audio_tracks;
+ audio_tracks = _editor->session()->new_audio_track (region->n_channels(), region->n_channels(), ARDOUR::Normal, 0, 1, region->name());
+ RouteTimeAxisView* rtav = _editor->axis_view_from_route (audio_tracks.front());
+ if (rtav) {
+ rtav->set_height (original->current_height());
+ }
+ return rtav;
+ } else {
+ ChanCount one_midi_port (DataType::MIDI, 1);
+ list<boost::shared_ptr<MidiTrack> > midi_tracks;
+ midi_tracks = _editor->session()->new_midi_track (one_midi_port, one_midi_port, boost::shared_ptr<ARDOUR::PluginInfo>(), ARDOUR::Normal, 0, 1, region->name());
+ RouteTimeAxisView* rtav = _editor->axis_view_from_route (midi_tracks.front());
+ if (rtav) {
+ rtav->set_height (original->current_height());
+ }
+ return rtav;
+ }
+ } catch (...) {
+ error << _("Could not create new track after region placed in the drop zone") << endmsg;
+ return 0;
+ }
+}
+
void
RegionMoveDrag::finished_copy (bool const changed_position, bool const /*changed_tracks*/, framecnt_t const drag_delta)
{
RegionSelection new_views;
PlaylistSet modified_playlists;
- list<RegionView*> views_to_delete;
+ RouteTimeAxisView* new_time_axis_view = 0;
if (_brushing) {
/* all changes were made during motion event handlers */
}
/* insert the regions into their new playlists */
- for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end();) {
+
+ RouteTimeAxisView* dest_rtv = 0;
if (i->view->region()->locked() || i->view->region()->video_locked()) {
continue;
} else {
where = i->view->region()->position();
}
-
- RegionView* new_view = insert_region_into_playlist (
- i->view->region(), dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]), i->layer, where, modified_playlists
- );
-
- if (new_view == 0) {
- continue;
+
+ if (i->time_axis_view < 0) {
+ if (!new_time_axis_view) {
+ new_time_axis_view = create_destination_time_axis (i->view->region(), i->initial_time_axis_view);
+ }
+ dest_rtv = new_time_axis_view;
+ } else {
+ dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
+ }
+
+ if (dest_rtv != 0) {
+ RegionView* new_view = insert_region_into_playlist (i->view->region(), dest_rtv, i->layer, where, modified_playlists);
+ if (new_view != 0) {
+ new_views.push_back (new_view);
+ }
}
+
+ /* Delete the copy of the view that was used for dragging. Need to play safe with the iterator
+ since deletion will automagically remove it from _views, thus invalidating i as an iterator.
+ */
- new_views.push_back (new_view);
-
- /* we don't need the copied RegionView any more */
- views_to_delete.push_back (i->view);
- }
-
- /* Delete views that are no longer needed; we can't do this directly in the iteration over _views
- because when views are deleted they are automagically removed from _views, which messes
- up the iteration.
- */
- for (list<RegionView*>::iterator i = views_to_delete.begin(); i != views_to_delete.end(); ++i) {
- delete *i;
+ list<DraggingView>::const_iterator next = i;
+ ++next;
+ delete i->view;
+ i = next;
}
/* If we've created new regions either by copying or moving
PlaylistSet modified_playlists;
PlaylistSet frozen_playlists;
set<RouteTimeAxisView*> views_to_update;
+ RouteTimeAxisView* new_time_axis_view = 0;
if (_brushing) {
/* all changes were made during motion event handlers */
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ) {
RegionView* rv = i->view;
-
- RouteTimeAxisView* const dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
- double const dest_layer = i->layer;
+ RouteTimeAxisView* dest_rtv = 0;
if (rv->region()->locked() || rv->region()->video_locked()) {
++i;
continue;
}
+
+ if (i->time_axis_view < 0) {
+ if (!new_time_axis_view) {
+ new_time_axis_view = create_destination_time_axis (rv->region(), i->initial_time_axis_view);
+ }
+ dest_rtv = new_time_axis_view;
+ } else {
+ dest_rtv = dynamic_cast<RouteTimeAxisView*> (_time_axis_views[i->time_axis_view]);
+ }
+
+ assert (dest_rtv);
+ double const dest_layer = i->layer;
+
views_to_update.insert (dest_rtv);
framepos_t where;
visible.
*/
rv->hide_region_editor();
- rv->fake_set_opaque (false);
+
remove_region_from_playlist (rv->region(), i->initial_playlist, modified_playlists);
rv->get_canvas_group()->reparent (rtv->view()->canvas_item());
rv->get_canvas_group()->set_y_position (0);
rv->drag_end ();
- rv->fake_set_opaque (false);
rv->move (-_total_x_delta, 0);
rv->set_height (rtv->view()->child_height ());
}
_primary->get_canvas_group()->show ();
_primary->set_position (pos, 0);
- _views.push_back (DraggingView (_primary, this));
+ _views.push_back (DraggingView (_primary, this, v));
_last_frame_position = pos;
{
/* Which trackview is this ? */
- pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
/* The region motion is only processed if the pointer is over
*/
_editor->verbose_cursor()->hide ();
return;
+ } else {
+ _editor->verbose_cursor()->show ();
}
int dir;
(*i)->drag_start();
ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
- rvg->reparent (_editor->_region_motion_group);
- (*i)->fake_set_opaque (true);
- rvg->set_position (rv_canvas_offset);
+ Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
+ rvg->reparent (_editor->_drag_motion_group);
+
+ // XXX without the following, things jump in the y direction during drags
+ // with it, they jump in the x direction
+ // so we need to do the move in the y direction only
+ // rvg->move (rv_canvas_offset - dmg_canvas_offset);
+ std::cerr << "rv_canvas_offset = " << rv_canvas_offset << ", dmg_canvas_offset = " << dmg_canvas_offset << std::endl;
+ Duple fudge = rv_canvas_offset - dmg_canvas_offset;
+ fudge.x = 0;
+ rvg->move (fudge);
+
}
- _views.push_back (DraggingView (*i, this));
+ _views.push_back (DraggingView (*i, this, tav));
}
}
}
{
/* Which trackview is this ? */
- pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ());
+ pair<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
/* The region motion is only processed if the pointer is over
Timecode::Time timecode;
_editor->session()->sample_to_timecode(abs(_startdrag_video_offset), timecode, true /* use_offset */, false /* use_subframes */ );
snprintf (buf, sizeof (buf), "Video Start:\n%c%02" PRId32 ":%02" PRId32 ":%02" PRId32 ":%02" PRId32, (_startdrag_video_offset<0?'-':' '), timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
- _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
- _editor->verbose_cursor()->show ();
+ show_verbose_cursor_text (buf);
}
void
DEBUG_TRACE (DEBUG::Drags, string_compose("SHIFT REGION at %1 by %2\n", i->initial_position, dt));
if (first_move) {
rv->drag_start ();
- rv->fake_set_opaque (true);
rv->region()->clear_changes ();
rv->region()->suspend_property_changes();
}
, _("Diff:"),
(dt<0?'-':' '), timediff.hours, timediff.minutes, timediff.seconds, timediff.frames
);
- _editor->verbose_cursor()->set(buf, event->button.x + 10, event->button.y + 10);
- _editor->verbose_cursor()->show ();
+ show_verbose_cursor_text (buf);
}
void
for (list<AVDraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
i->view->drag_end();
- i->view->fake_set_opaque (false);
i->view->region()->resume_property_changes ();
_editor->session()->add_command (new StatefulDiffCommand (i->view->region()));
if (pf < (region_start + region_length/2)) {
/* closer to front */
_operation = StartTrim;
- Drag::start_grab (event, _editor->cursors()->left_side_trim);
+
+ if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+ Drag::start_grab (event, _editor->cursors()->anchored_left_side_trim);
+ } else {
+ Drag::start_grab (event, _editor->cursors()->left_side_trim);
+ }
} else {
/* closer to end */
_operation = EndTrim;
- Drag::start_grab (event, _editor->cursors()->right_side_trim);
+ if (Keyboard::modifier_state_equals (event->button.state, Keyboard::TertiaryModifier)) {
+ Drag::start_grab (event, _editor->cursors()->anchored_right_side_trim);
+ } else {
+ Drag::start_grab (event, _editor->cursors()->right_side_trim);
+ }
}
}
speed = tv->track()->speed();
}
- framecnt_t const dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
+ framecnt_t dt = adjusted_current_frame (event) - raw_grab_frame () + _pointer_frame_offset;
if (first_move) {
case ContentsTrim:
trim_type = "Region content trim";
break;
+ default:
+ assert(0);
+ break;
}
_editor->begin_reversible_command (trim_type);
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
RegionView* rv = i->view;
- rv->fake_set_opaque (false);
rv->enable_display (false);
rv->region()->playlist()->clear_owned_changes ();
non_overlap_trim = true;
}
+ /* contstrain trim to fade length */
+ if (_preserve_fade_anchor) {
+ switch (_operation) {
+ case StartTrim:
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+ if (!arv) continue;
+ boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+ if (ar->locked()) continue;
+ framecnt_t len = ar->fade_in()->back()->when;
+ if (len < dt) dt = min(dt, len);
+ }
+ break;
+ case EndTrim:
+ for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
+ if (!arv) continue;
+ boost::shared_ptr<AudioRegion> ar (arv->audio_region());
+ if (ar->locked()) continue;
+ framecnt_t len = ar->fade_out()->back()->when;
+ if (len < -dt) dt = max(dt, -len);
+ }
+ break;
+ case ContentsTrim:
+ break;
+ }
+ }
+
+
switch (_operation) {
case StartTrim:
- for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
bool changed = i->view->trim_front (i->initial_position + dt, non_overlap_trim);
if (changed && _preserve_fade_anchor) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
if (arv) {
- double distance;
- double new_length;
- framecnt_t len;
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
- distance = _drags->current_pointer_x() - grab_x();
- len = ar->fade_in()->back()->when;
- new_length = len - _editor->pixel_to_sample (distance);
- new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
- arv->reset_fade_in_shape_width (ar, new_length); //the grey shape
+ framecnt_t len = ar->fade_in()->back()->when;
+ framecnt_t diff = ar->first_frame() - i->initial_position;
+ framepos_t new_length = len - diff;
+ i->anchored_fade_length = min (ar->length(), new_length);
+ //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
+ arv->reset_fade_in_shape_width (ar, i->anchored_fade_length, true);
}
}
}
break;
case EndTrim:
- for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
+ for (list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ++i) {
bool changed = i->view->trim_end (i->initial_end + dt, non_overlap_trim);
if (changed && _preserve_fade_anchor) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
if (arv) {
- double distance;
- double new_length;
- framecnt_t len;
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
- distance = grab_x() - _drags->current_pointer_x();
- len = ar->fade_out()->back()->when;
- new_length = len - _editor->pixel_to_sample (distance);
- new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
- arv->reset_fade_out_shape_width (ar, new_length); //the grey shape
+ framecnt_t len = ar->fade_out()->back()->when;
+ framecnt_t diff = 1 + ar->last_frame() - i->initial_end;
+ framepos_t new_length = len + diff;
+ i->anchored_fade_length = min (ar->length(), new_length);
+ //i->anchored_fade_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
+ arv->reset_fade_out_shape_width (ar, i->anchored_fade_length, true);
}
}
}
if (_preserve_fade_anchor) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
if (arv) {
- double distance;
- double new_length;
- framecnt_t len;
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
- distance = _drags->current_pointer_x() - grab_x();
- len = ar->fade_in()->back()->when;
- new_length = len - _editor->pixel_to_sample (distance);
- new_length = ar->verify_xfade_bounds (new_length, true /*START*/ );
- ar->set_fade_in_length(new_length);
+ arv->reset_fade_in_shape_width (ar, i->anchored_fade_length);
+ ar->set_fade_in_length(i->anchored_fade_length);
+ ar->set_fade_in_active(true);
}
}
if (_jump_position_when_done) {
if (_preserve_fade_anchor) {
AudioRegionView* arv = dynamic_cast<AudioRegionView*> (i->view);
if (arv) {
- double distance;
- double new_length;
- framecnt_t len;
boost::shared_ptr<AudioRegion> ar (arv->audio_region());
- distance = _drags->current_pointer_x() - grab_x();
- len = ar->fade_out()->back()->when;
- new_length = len - _editor->pixel_to_sample (distance);
- new_length = ar->verify_xfade_bounds (new_length, false /*END*/ );
- ar->set_fade_out_length(new_length);
+ arv->reset_fade_out_shape_width (ar, i->anchored_fade_length);
+ ar->set_fade_out_length(i->anchored_fade_length);
+ ar->set_fade_out_active(true);
}
}
if (_jump_position_when_done) {
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
i->view->thaw_after_trim ();
i->view->enable_display (true);
- i->view->fake_set_opaque (true);
/* Trimming one region may affect others on the playlist, so we need
to get undo Commands from the whole playlist rather than just the
}
CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
- : Drag (e, &c.time_bar_canvas_item())
+ : Drag (e, &c.track_canvas_item(), false)
, _cursor (c)
, _stop (s)
{
_point->line().start_drag_single (_point, _fixed_grab_x, fraction);
- _editor->verbose_cursor()->set (_point->line().get_verbose_cursor_string (fraction),
- event->button.x + 10, event->button.y + 10);
-
- _editor->verbose_cursor()->show ();
+ show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
_pushing = Keyboard::modifier_state_contains (event->button.state, Keyboard::PrimaryModifier);
ControlPointDrag::motion (GdkEvent* event, bool)
{
double dx = _drags->current_pointer_x() - last_pointer_x();
- double dy = _drags->current_pointer_y() - last_pointer_y();
+ double dy = current_pointer_y() - last_pointer_y();
if (event->button.state & Keyboard::SecondaryModifier) {
dx *= 0.1;
_point->line().drag_motion (_editor->sample_to_pixel_unrounded (cx_frames), fraction, false, _pushing, _final_index);
- _editor->verbose_cursor()->set_text (_point->line().get_verbose_cursor_string (fraction));
+ show_verbose_cursor_text (_point->line().get_verbose_cursor_string (fraction));
}
void
_line->start_drag_line (before, after, fraction);
- _editor->verbose_cursor()->set (_line->get_verbose_cursor_string (fraction),
- event->button.x + 10, event->button.y + 10);
-
- _editor->verbose_cursor()->show ();
+ show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
}
void
LineDrag::motion (GdkEvent* event, bool)
{
- double dy = _drags->current_pointer_y() - last_pointer_y();
+ double dy = current_pointer_y() - last_pointer_y();
if (event->button.state & Keyboard::SecondaryModifier) {
dy *= 0.1;
/* we are ignoring x position for this drag, so we can just pass in anything */
_line->drag_motion (0, fraction, true, false, ignored);
- _editor->verbose_cursor()->set_text (_line->get_verbose_cursor_string (fraction));
+ show_verbose_cursor_text (_line->get_verbose_cursor_string (fraction));
}
void
start = grab;
}
- if (_drags->current_pointer_y() < grab_y()) {
- y1 = _drags->current_pointer_y();
+ if (current_pointer_y() < grab_y()) {
+ y1 = current_pointer_y();
y2 = grab_y();
} else {
- y2 = _drags->current_pointer_y();
+ y2 = current_pointer_y();
y1 = grab_y();
}
-
if (start != end || y1 != y2) {
double x1 = _editor->sample_to_pixel (start);
double x2 = _editor->sample_to_pixel (end);
const double min_dimension = 2.0;
- _editor->rubberband_rect->set_x0 (x1);
if (_vertical_only) {
/* fixed 10 pixel width */
- _editor->rubberband_rect->set_x1 (x1 + 10);
+ x2 = x1 + 10;
} else {
if (x2 < x1) {
x2 = min (x1 - min_dimension, x2);
} else {
x2 = max (x1 + min_dimension, x2);
}
- _editor->rubberband_rect->set_x1 (x2);
}
- _editor->rubberband_rect->set_y0 (y1);
if (y2 < y1) {
y2 = min (y1 - min_dimension, y2);
} else {
y2 = max (y1 + min_dimension, y2);
}
- _editor->rubberband_rect->set_y1 (y2);
-
+ /* translate rect into item space and set */
+
+ ArdourCanvas::Rect r (x1, y1, x2, y2);
+
+ /* this drag is a _trackview_only == true drag, so the y1 and
+ * y2 (computed using current_pointer_y() and grab_y()) will be
+ * relative to the top of the trackview group). The
+ * rubberband rect has the same parent/scroll offset as the
+ * the trackview group, so we can use the "r" rect directly
+ * to set the shape of the rubberband.
+ */
+
+ _editor->rubberband_rect->set (r);
_editor->rubberband_rect->show();
_editor->rubberband_rect->raise_to_top();
double y1;
double y2;
- if (_drags->current_pointer_y() < grab_y()) {
- y1 = _drags->current_pointer_y();
+ if (current_pointer_y() < grab_y()) {
+ y1 = current_pointer_y();
y2 = grab_y();
} else {
- y2 = _drags->current_pointer_y();
+ y2 = current_pointer_y();
y1 = grab_y();
}
show_verbose_cursor_time (adjusted_current_frame (event));
}
- _original_pointer_time_axis = _editor->trackview_by_y_position (_drags->current_pointer_y ()).first->order ();
+ _original_pointer_time_axis = _editor->trackview_by_y_position (current_pointer_y ()).first->order ();
}
void
TrackViewList to_be_removed_from_selection;
TrackViewList& all_tracks (_editor->track_views);
- for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
+ ArdourCanvas::Coord const top = grab_y();
+ ArdourCanvas::Coord const bottom = current_pointer_y();
+
+ if (top >= 0 && bottom >= 0) {
+
+ for (TrackViewList::const_iterator i = all_tracks.begin(); i != all_tracks.end(); ++i) {
- if ((*i)->covered_by_y_range (grab_y(), _drags->current_pointer_y())) {
- if (!(*i)->get_selected()) {
- to_be_added_to_selection.push_back (*i);
- }
- } else {
- if ((*i)->get_selected()) {
- to_be_removed_from_selection.push_back (*i);
+ if ((*i)->covered_by_y_range (top, bottom)) {
+ if (!(*i)->get_selected()) {
+ to_be_added_to_selection.push_back (*i);
+ }
+ } else {
+ if ((*i)->get_selected()) {
+ to_be_removed_from_selection.push_back (*i);
+ }
}
}
- }
- if (!to_be_added_to_selection.empty()) {
- _editor->selection->add (to_be_added_to_selection);
- }
-
- if (!to_be_removed_from_selection.empty()) {
- _editor->selection->remove (to_be_removed_from_selection);
+ if (!to_be_added_to_selection.empty()) {
+ _editor->selection->add (to_be_added_to_selection);
+ }
+
+ if (!to_be_removed_from_selection.empty()) {
+ _editor->selection->remove (to_be_removed_from_selection);
+ }
}
}
break;
break;
}
- _editor->maybe_autoscroll (true, false, false);
-
if (start != end) {
switch (_operation) {
case SelectionMove:
}
RangeMarkerBarDrag::RangeMarkerBarDrag (Editor* e, ArdourCanvas::Item* i, Operation o)
- : Drag (e, i),
+ : Drag (e, i, false),
_operation (o),
_copy (false)
{
}
}
- _editor->maybe_autoscroll (true, false, false);
-
if (start != end) {
_editor->temp_location->set (start, end);
MidiStreamView* msv = _region->midi_stream_view ();
double const y = _region->midi_view()->y_position ();
/* new current note */
- uint8_t n = msv->y_to_note (_drags->current_pointer_y () - y);
+ uint8_t n = msv->y_to_note (current_pointer_y () - y);
/* clamp */
n = max (msv->lowest_note(), n);
n = min (msv->highest_note(), n);
/* Get line states before we start changing things */
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
i->state = &i->line->get_state ();
- i->original_fraction = y_fraction (i->line, _drags->current_pointer_y());
+ i->original_fraction = y_fraction (i->line, current_pointer_y());
}
if (_ranges.empty()) {
}
for (list<Line>::iterator i = _lines.begin(); i != _lines.end(); ++i) {
- i->line->start_drag_multiple (i->points, y_fraction (i->line, _drags->current_pointer_y()), i->state);
+ i->line->start_drag_multiple (i->points, y_fraction (i->line, current_pointer_y()), i->state);
}
}
}
for (list<Line>::iterator l = _lines.begin(); l != _lines.end(); ++l) {
- float const f = y_fraction (l->line, _drags->current_pointer_y());
+ float const f = y_fraction (l->line, current_pointer_y());
/* we are ignoring x position for this drag, so we can just pass in anything */
uint32_t ignored;
l->line->drag_motion (0, f, true, false, ignored);
}
}
-DraggingView::DraggingView (RegionView* v, RegionDrag* parent)
+DraggingView::DraggingView (RegionView* v, RegionDrag* parent, TimeAxisView* itav)
: view (v)
+ , initial_time_axis_view (itav)
{
+ /* note that time_axis_view may be null if the regionview was created
+ * as part of a copy operation.
+ */
time_axis_view = parent->find_time_axis_view (&v->get_time_axis_view ());
layer = v->region()->layer ();
initial_y = v->get_canvas_group()->position().y;
~DragManager ();
bool motion_handler (GdkEvent *, bool);
- bool window_motion_handler (GdkEvent *, bool);
void abort ();
void add (Drag *);
class Drag
{
public:
- Drag (Editor *, ArdourCanvas::Item *);
+ Drag (Editor *, ArdourCanvas::Item *, bool trackview_only = true);
virtual ~Drag () {}
void set_manager (DragManager* m) {
return _last_pointer_frame;
}
+ double current_pointer_y () const;
+
boost::shared_ptr<ARDOUR::Region> add_midi_region (MidiTimeAxisView*);
void show_verbose_cursor_time (framepos_t);
bool _was_rolling; ///< true if the session was rolling before the drag started, otherwise false
private:
-
+ bool _trackview_only; ///< true if pointer y value should always be relative to the top of the trackview group
bool _move_threshold_passed; ///< true if the move threshold has been passed, otherwise false
bool _was_double_click; ///< true if drag initiated by a double click event
double _grab_x; ///< trackview x of the grab start position
- double _grab_y; ///< trackview y of the grab start position
+ double _grab_y; ///< y of the grab start position, possibly adjusted if _trackview_only is true
double _last_pointer_x; ///< trackview x of the pointer last time a motion occurred
double _last_pointer_y; ///< trackview y of the pointer last time a motion occurred
ARDOUR::framepos_t _raw_grab_frame; ///< unsnapped frame that the mouse was at when start_grab was called, or 0
class DraggingView
{
public:
- DraggingView (RegionView *, RegionDrag *);
+ DraggingView (RegionView *, RegionDrag *, TimeAxisView* original_tav);
RegionView* view; ///< the view
/** index into RegionDrag::_time_axis_views of the view that this region is currently being displayed on,
double initial_y; ///< the initial y position of the view before any reparenting
framepos_t initial_position; ///< initial position of the region
framepos_t initial_end; ///< initial end position of the region
+ framepos_t anchored_fade_length; ///< fade_length when anchored during drag
boost::shared_ptr<ARDOUR::Playlist> initial_playlist;
+ TimeAxisView* initial_time_axis_view;
};
/** Abstract base class for drags that involve region(s) */
void collect_new_region_view (RegionView *);
+ RouteTimeAxisView* create_destination_time_axis (boost::shared_ptr<ARDOUR::Region>, TimeAxisView* original);
bool _copy;
RegionView* _new_region_view;
#include "gtkmm2ext/utils.h"
#include "ardour/route_group.h"
-#include "editor_group_tabs.h"
+
+#include "canvas/utils.h"
+
+#include "ardour_ui.h"
#include "editor.h"
-#include "route_time_axis.h"
-#include "utils.h"
+#include "editor_group_tabs.h"
#include "editor_route_groups.h"
#include "editor_routes.h"
+#include "rgb_macros.h"
+#include "route_time_axis.h"
+#include "utils.h"
+
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
EditorGroupTabs::EditorGroupTabs (Editor* e)
: EditorComponent (e)
EditorGroupTabs::draw_tab (cairo_t* cr, Tab const & tab) const
{
double const arc_radius = get_width();
-
+ double r, g, b, a;
+
if (tab.group && tab.group->is_active()) {
- cairo_set_source_rgba (cr, tab.color.get_red_p (), tab.color.get_green_p (), tab.color.get_blue_p (), 1);
+ ArdourCanvas::color_to_rgba (tab.color, r, g, b, a);
} else {
- cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
+ ArdourCanvas::color_to_rgba (ARDOUR_UI::config()->get_canvasvar_InactiveGroupTab(), r, g, b, a);
}
+ a = 1.0;
+
+ cairo_set_source_rgba (cr, r, g, b, a);
cairo_move_to (cr, 0, tab.from + arc_radius);
cairo_arc (cr, get_width(), tab.from + arc_radius, arc_radius, M_PI, 3 * M_PI / 2);
cairo_line_to (cr, get_width(), tab.to);
cairo_text_extents_t ext;
cairo_text_extents (cr, tab.group->name().c_str(), &ext);
- cairo_set_source_rgb (cr, 1, 1, 1);
+ ArdourCanvas::Color c = ArdourCanvas::contrasting_text_color (ArdourCanvas::rgba_to_color (r, g, b, a));
+ ArdourCanvas::color_to_rgba (c, r, g, b, a);
+
+ cairo_set_source_rgb (cr, r, g, b);
cairo_move_to (cr, get_width() - ext.height / 2, tab.from + (f.second + tab.to - tab.from) / 2);
cairo_save (cr);
cairo_rotate (cr, - M_PI / 2);
enum ItemType {
RegionItem,
StreamItem,
+ WaveItem,
PlayheadCursorItem,
MarkerItem,
MarkerBarItem,
AutomationTrackItem,
FadeInItem,
FadeInHandleItem,
+ FadeInTrimHandleItem,
FadeOutItem,
FadeOutHandleItem,
+ FadeOutTrimHandleItem,
NoteItem,
FeatureLineItem,
- LeftFrameHandle,
- RightFrameHandle,
+ LeftFrameHandle,
+ RightFrameHandle,
StartCrossFadeItem,
EndCrossFadeItem,
CrossfadeViewItem,
-
+ TimecodeRulerItem,
+ MinsecRulerItem,
+ BBTRulerItem,
+ SamplesRulerItem,
+
/* don't remove this */
NoItem
{
ENSURE_GUI_THREAD (*this, &Editor::add_new_location, location);
- ArdourCanvas::Group* group = add_new_location_internal (location);
+ ArdourCanvas::Container* group = add_new_location_internal (location);
/* Do a full update of the markers in this group */
update_marker_labels (group);
* the caller must call update_marker_labels () after calling this.
* @return canvas group that the location's marker was added to.
*/
-ArdourCanvas::Group*
+ArdourCanvas::Container*
Editor::add_new_location_internal (Location* location)
{
LocationMarkers *lam = new LocationMarkers;
uint32_t color;
/* make a note here of which group this marker ends up in */
- ArdourCanvas::Group* group = 0;
+ ArdourCanvas::Container* group = 0;
if (location->is_cd_marker()) {
color = location_cd_marker_color;
void
Editor::update_marker_labels ()
{
- for (std::map<ArdourCanvas::Group *, std::list<Marker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
+ for (std::map<ArdourCanvas::Container *, std::list<Marker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
update_marker_labels (i->first);
}
}
/** Look at all markers in a group and update label widths */
void
-Editor::update_marker_labels (ArdourCanvas::Group* group)
+Editor::update_marker_labels (ArdourCanvas::Container* group)
{
list<Marker*>& sorted = _sorted_marker_lists[group];
Location* tpl;
if ((_session->config.get_punch_in() || _session->config.get_punch_out()) && ((tpl = transport_punch_location()) != 0)) {
- ArdourCanvas::Rect const v = _track_canvas->visible_area ();
+ double pixel_start;
+ double pixel_end;
+
if (_session->config.get_punch_in()) {
- transport_punch_range_rect->set_x0 (sample_to_pixel (tpl->start()));
- transport_punch_range_rect->set_x1 (_session->config.get_punch_out() ? sample_to_pixel (tpl->end()) : sample_to_pixel (max_framepos));
+ pixel_start = sample_to_pixel (tpl->start());
+ } else {
+ pixel_start = 0;
+ }
+ if (_session->config.get_punch_out()) {
+ pixel_end = sample_to_pixel (tpl->end());
} else {
- transport_punch_range_rect->set_x0 (0);
- transport_punch_range_rect->set_x1 (_session->config.get_punch_out() ? sample_to_pixel (tpl->end()) : v.width ());
+ pixel_end = sample_to_pixel (max_framepos);
}
+ transport_punch_range_rect->set_x0 (pixel_start);
+ transport_punch_range_rect->set_x1 (pixel_end);
transport_punch_range_rect->show();
} else {
void
Editor::remove_sorted_marker (Marker* m)
{
- for (std::map<ArdourCanvas::Group *, std::list<Marker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
+ for (std::map<ArdourCanvas::Container *, std::list<Marker *> >::iterator i = _sorted_marker_lists.begin(); i != _sorted_marker_lists.end(); ++i) {
i->second.remove (m);
}
}
Editor::ensure_all_elements_drawn ()
{
controls_layout.queue_draw ();
- ruler_label_event_box.queue_draw ();
time_bars_event_box.queue_draw ();
}
#endif
#include "automation_time_axis.h"
#include "control_point.h"
#include "prompter.h"
-#include "utils.h"
#include "selection.h"
#include "keyboard.h"
#include "editing.h"
return false;
}
- if (pointer_window != canvas_window && pointer_window != _time_bars_canvas->get_window()) {
+ if (pointer_window != canvas_window) {
in_track_canvas = false;
return false;
}
framepos_t
Editor::window_event_sample (GdkEvent const * event, double* pcx, double* pcy) const
{
- double x;
- double y;
+ ArdourCanvas::Duple d;
- if (!gdk_event_get_coords (event, &x, &y)) {
+ if (!gdk_event_get_coords (event, &d.x, &d.y)) {
return 0;
}
/* event coordinates are in window units, so convert to canvas
- * (i.e. account for scrolling)
*/
- ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (x, y));
+ d = _track_canvas->window_to_canvas (d);
if (pcx) {
*pcx = d.x;
return pixel_to_sample_from_event (x);
}
-Gdk::Cursor*
-Editor::which_grabber_cursor ()
-{
- Gdk::Cursor* c = _cursors->grabber;
-
- if (_internal_editing) {
- switch (mouse_mode) {
- case MouseDraw:
- c = _cursors->midi_pencil;
- break;
-
- case MouseObject:
- c = _cursors->grabber_note;
- break;
-
- case MouseTimeFX:
- c = _cursors->midi_resize;
- break;
-
- case MouseRange:
- c = _cursors->grabber_note;
- break;
-
- default:
- break;
- }
-
- } else {
-
- switch (_edit_point) {
- case EditAtMouse:
- c = _cursors->grabber_edit_point;
- break;
- default:
- boost::shared_ptr<Movable> m = _movable.lock();
- if (m && m->locked()) {
- c = _cursors->speaker;
- }
- break;
- }
- }
-
- return c;
-}
-
void
Editor::set_current_trimmable (boost::shared_ptr<Trimmable> t)
{
if (!st || st == t) {
_trimmable = t;
- set_canvas_cursor ();
}
}
if (!sm || sm != m) {
_movable = m;
- set_canvas_cursor ();
- }
-}
-
-void
-Editor::set_canvas_cursor ()
-{
- switch (mouse_mode) {
- case MouseRange:
- current_canvas_cursor = _cursors->selector;
- if (_internal_editing) {
- current_canvas_cursor = which_grabber_cursor();
- }
- break;
-
- case MouseObject:
- current_canvas_cursor = which_grabber_cursor();
- break;
-
- case MouseDraw:
- current_canvas_cursor = _cursors->midi_pencil;
- break;
-
- case MouseGain:
- current_canvas_cursor = _cursors->cross_hair;
- break;
-
- case MouseZoom:
- if (Keyboard::the_keyboard().key_is_down (GDK_Control_L)) {
- current_canvas_cursor = _cursors->zoom_out;
- } else {
- current_canvas_cursor = _cursors->zoom_in;
- }
- break;
-
- case MouseTimeFX:
- current_canvas_cursor = _cursors->time_fx; // just use playhead
- break;
-
- case MouseAudition:
- current_canvas_cursor = _cursors->speaker;
- break;
- }
-
- if (!_internal_editing) {
- switch (_join_object_range_state) {
- case JOIN_OBJECT_RANGE_NONE:
- break;
- case JOIN_OBJECT_RANGE_OBJECT:
- current_canvas_cursor = which_grabber_cursor ();
- break;
- case JOIN_OBJECT_RANGE_RANGE:
- current_canvas_cursor = _cursors->selector;
- break;
- }
- }
-
- /* up-down cursor as a cue that automation can be dragged up and down when in join object/range mode */
- if (!_internal_editing && get_smart_mode() ) {
-
- double x, y;
- get_pointer_position (x, y);
-
- if (x >= 0 && y >= 0) {
-
- vector<ArdourCanvas::Item const *> items;
-
- _track_canvas->root()->add_items_at_point (ArdourCanvas::Duple (x,y), items);
-
- // first item will be the upper most
-
- if (!items.empty()) {
- const ArdourCanvas::Item* i = items.front();
-
- if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
- pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
- if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
- current_canvas_cursor = _cursors->up_down;
- }
- }
- }
- }
}
-
- set_canvas_cursor (current_canvas_cursor, true);
}
void
}
*/
- set_canvas_cursor ();
+ reset_canvas_cursor ();
set_gain_envelope_visibility ();
update_time_selection_display ();
to cut notes or regions.
*/
- MouseMode eff_mouse_mode = mouse_mode;
+ MouseMode eff_mouse_mode = effective_mouse_mode ();
if (get_smart_mode() && eff_mouse_mode == MouseRange && event->button.button == 3 && item_type == RegionItem) {
/* context clicks are always about object properties, even if
eff_mouse_mode = MouseObject;
}
+ /* special case: allow drag of region fade in/out in object mode with join object/range enabled */
+ if (get_smart_mode()) {
+ switch (item_type) {
+ case FadeInHandleItem:
+ case FadeInTrimHandleItem:
+ case FadeOutHandleItem:
+ case FadeOutTrimHandleItem:
+ eff_mouse_mode = MouseObject;
+ break;
+ default:
+ break;
+ }
+ }
+
if (((mouse_mode != MouseObject) &&
(mouse_mode != MouseAudition || item_type != RegionItem) &&
(mouse_mode != MouseTimeFX || item_type != RegionItem) &&
case RegionViewName:
case LeftFrameHandle:
case RightFrameHandle:
- if (eff_mouse_mode != MouseRange) {
- set_selected_regionview_from_click (press, op);
- } else if (event->type == GDK_BUTTON_PRESS) {
- set_selected_track_as_side_effect (op);
- }
- break;
-
case FadeInHandleItem:
+ case FadeInTrimHandleItem:
case FadeInItem:
case FadeOutHandleItem:
+ case FadeOutTrimHandleItem:
case FadeOutItem:
case StartCrossFadeItem:
case EndCrossFadeItem:
- if (eff_mouse_mode != MouseRange) {
- cerr << "Should be setting selected regionview\n";
+ if (get_smart_mode() || eff_mouse_mode != MouseRange) {
set_selected_regionview_from_click (press, op);
} else if (event->type == GDK_BUTTON_PRESS) {
set_selected_track_as_side_effect (op);
case MarkerBarItem:
case TempoBarItem:
case MeterBarItem:
+ case TimecodeRulerItem:
+ case SamplesRulerItem:
+ case MinsecRulerItem:
+ case BBTRulerItem:
if (!Keyboard::modifier_state_equals (event->button.state, Keyboard::PrimaryModifier)) {
_drags->set (new CursorDrag (this, *playhead_cursor, false), event);
}
Editing::MouseMode eff = effective_mouse_mode ();
/* special case: allow drag of region fade in/out in object mode with join object/range enabled */
- if (item_type == FadeInHandleItem || item_type == FadeOutHandleItem) {
- eff = MouseObject;
+ if (get_smart_mode()) {
+ switch (item_type) {
+ case FadeInHandleItem:
+ case FadeInTrimHandleItem:
+ case FadeOutHandleItem:
+ case FadeOutTrimHandleItem:
+ eff = MouseObject;
+ break;
+ default:
+ break;
+ }
}
/* there is no Range mode when in internal edit mode */
case RegionViewNameHighlight:
case LeftFrameHandle:
- case RightFrameHandle:
+ case RightFrameHandle:
if (!clicked_regionview->region()->locked()) {
_drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
return true;
}
break;
+ case FadeInTrimHandleItem:
+ case FadeOutTrimHandleItem:
+ if (!clicked_regionview->region()->locked()) {
+ _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer(), true), event);
+ return true;
+ }
+ break;
+
case RegionViewName:
{
/* rename happens on edit clicks */
- _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
- return true;
+ if (clicked_regionview->get_name_highlight()) {
+ _drags->set (new TrimDrag (this, clicked_regionview->get_name_highlight(), clicked_regionview, selection->regions.by_layer()), event);
+ return true;
+ }
break;
}
scrub_reverse_distance = 0;
last_scrub_x = event->button.x;
scrubbing_direction = 0;
- set_canvas_cursor (_cursors->transparent);
+ push_canvas_cursor (_cursors->transparent);
return true;
break;
switch (item_type) {
case RegionViewNameHighlight:
- _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
- return true;
- break;
+ _drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
+ return true;
+ break;
- case LeftFrameHandle:
- case RightFrameHandle:
+ case LeftFrameHandle:
+ case RightFrameHandle:
if (!internal_editing ()) {
_drags->set (new TrimDrag (this, item, clicked_regionview, selection->regions.by_layer()), event);
}
return false;
}
- Glib::RefPtr<Gdk::Window> canvas_window = const_cast<Editor*>(this)->_track_canvas_viewport->get_window();
-
- if (canvas_window) {
- Glib::RefPtr<const Gdk::Window> pointer_window;
- int x, y;
- double wx, wy;
- Gdk::ModifierType mask;
-
- pointer_window = canvas_window->get_pointer (x, y, mask);
-
- if (pointer_window == _track_canvas->get_window()) {
- _track_canvas->window_to_canvas (x, y, wx, wy);
- }
- }
-
pre_press_cursor = current_canvas_cursor;
_track_canvas->grab_focus();
case CdMarkerBarItem:
case TransportMarkerBarItem:
case StreamItem:
- /* button press on these events never does anything to
+ case TimecodeRulerItem:
+ case SamplesRulerItem:
+ case MinsecRulerItem:
+ case BBTRulerItem:
+ /* button press on these items never does anything to
change the editing mode.
*/
break;
switch (item_type) {
case FadeInItem:
case FadeInHandleItem:
- case FadeOutItem:
- case FadeOutHandleItem:
- popup_fade_context_menu (1, event->button.time, item, item_type);
- break;
-
+ case FadeInTrimHandleItem:
case StartCrossFadeItem:
+ case LeftFrameHandle:
popup_xfade_in_context_menu (1, event->button.time, item, item_type);
break;
+ case FadeOutItem:
+ case FadeOutHandleItem:
+ case FadeOutTrimHandleItem:
case EndCrossFadeItem:
+ case RightFrameHandle:
popup_xfade_out_context_menu (1, event->button.time, item, item_type);
break;
case RegionItem:
case RegionViewNameHighlight:
- case LeftFrameHandle:
- case RightFrameHandle:
case RegionViewName:
popup_track_context_menu (1, event->button.time, item_type, false);
break;
case TempoBarItem:
case MeterBarItem:
case VideoBarItem:
+ case TimecodeRulerItem:
+ case SamplesRulerItem:
+ case MinsecRulerItem:
+ case BBTRulerItem:
popup_ruler_menu (where, item_type);
break;
return true;
break;
+ case TimecodeRulerItem:
+ case SamplesRulerItem:
+ case MinsecRulerItem:
+ case BBTRulerItem:
+ return true;
+ break;
+
default:
break;
}
break;
case MouseAudition:
- set_canvas_cursor (current_canvas_cursor);
+ pop_canvas_cursor ();
if (scrubbing_direction == 0) {
/* no drag, just a click */
switch (item_type) {
double fraction;
bool ret = true;
+ /* by the time we reach here, entered_regionview and entered trackview
+ * will have already been set as appropriate. Things are done this
+ * way because this method isn't passed a pointer to a variable type of
+ * thing that is entered (which may or may not be canvas item).
+ * (e.g. the actual entered regionview)
+ */
+
+ choose_canvas_cursor_on_entry (&event->crossing, item_type);
+
switch (item_type) {
case ControlPointItem:
if (mouse_mode == MouseGain || mouse_mode == MouseObject) {
cp = static_cast<ControlPoint*>(item->get_data ("control_point"));
cp->show ();
- double at_x, at_y;
- at_x = cp->get_x();
- at_y = cp->get_y ();
- cp->i2w (at_x, at_y);
- at_x += 10.0;
- at_y += 10.0;
-
fraction = 1.0 - (cp->get_y() / cp->line().height());
- if (is_drawable() && !_drags->active ()) {
- set_canvas_cursor (_cursors->fader);
- }
-
- _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction), at_x, at_y);
+ _verbose_cursor->set (cp->line().get_verbose_cursor_string (fraction));
_verbose_cursor->show ();
}
break;
if (line) {
line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredGainLine());
}
- if (is_drawable()) {
- set_canvas_cursor (_cursors->fader);
- }
}
break;
if (line) {
line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EnteredAutomationLine());
}
- if (is_drawable()) {
- set_canvas_cursor (_cursors->fader);
- }
- }
- break;
-
- case RegionViewNameHighlight:
- if (is_drawable() && effective_mouse_mode() == MouseObject && entered_regionview) {
- set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
- _over_region_trim_target = true;
}
break;
- case LeftFrameHandle:
- case RightFrameHandle:
- if (is_drawable() && effective_mouse_mode() == MouseObject && !internal_editing() && entered_regionview) {
- set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
- }
- break;
-
- case RegionItem:
- switch (effective_mouse_mode()) {
- case MouseRange:
- set_canvas_cursor (_cursors->selector);
- break;
- default:
- set_canvas_cursor (which_grabber_cursor());
- break;
- }
- break;
-
- case StartSelectionTrimItem:
- if (is_drawable()) {
- set_canvas_cursor (_cursors->left_side_trim);
- }
- break;
- case EndSelectionTrimItem:
- if (is_drawable()) {
- set_canvas_cursor (_cursors->right_side_trim);
- }
- break;
-
- case PlayheadCursorItem:
- if (is_drawable()) {
- switch (_edit_point) {
- case EditAtMouse:
- set_canvas_cursor (_cursors->grabber_edit_point);
- break;
- default:
- set_canvas_cursor (_cursors->grabber);
- break;
- }
- }
- break;
-
-
- case RegionViewName:
-
- /* when the name is not an active item, the entire name highlight is for trimming */
-
- if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
- if (mouse_mode == MouseObject && is_drawable()) {
- set_canvas_cursor_for_region_view (event->crossing.x, entered_regionview);
- _over_region_trim_target = true;
- }
- }
- break;
-
-
case AutomationTrackItem:
- if (is_drawable()) {
- Gdk::Cursor *cursor;
- switch (mouse_mode) {
- case MouseRange:
- cursor = _cursors->selector;
- break;
- case MouseZoom:
- cursor = _cursors->zoom_in;
- break;
- default:
- cursor = _cursors->cross_hair;
- break;
- }
-
- set_canvas_cursor (cursor);
-
- AutomationTimeAxisView* atv;
- if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
- clear_entered_track = false;
- set_entered_track (atv);
- }
- }
- break;
-
- case MarkerBarItem:
- case RangeMarkerBarItem:
- case TransportMarkerBarItem:
- case CdMarkerBarItem:
- case MeterBarItem:
- case TempoBarItem:
- if (is_drawable()) {
- set_canvas_cursor (_cursors->timebar);
+ AutomationTimeAxisView* atv;
+ if ((atv = static_cast<AutomationTimeAxisView*>(item->get_data ("trackview"))) != 0) {
+ clear_entered_track = false;
+ set_entered_track (atv);
}
break;
// fall through
case MeterMarkerItem:
case TempoMarkerItem:
- if (is_drawable()) {
- set_canvas_cursor (_cursors->timebar);
- }
break;
case FadeInHandleItem:
+ case FadeInTrimHandleItem:
if (mouse_mode == MouseObject && !internal_editing()) {
ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
if (rect) {
RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
rect->set_fill_color (rv->get_fill_color());
- set_canvas_cursor (_cursors->fade_in);
}
}
break;
case FadeOutHandleItem:
+ case FadeOutTrimHandleItem:
if (mouse_mode == MouseObject && !internal_editing()) {
ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
if (rect) {
RegionView* rv = static_cast<RegionView*>(item->get_data ("regionview"));
rect->set_fill_color (rv->get_fill_color ());
- set_canvas_cursor (_cursors->fade_out);
}
}
break;
+
case FeatureLineItem:
{
ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
break;
case SelectionItem:
- if ( get_smart_mode() ) {
- set_canvas_cursor ();
- }
break;
default:
break;
}
- /* second pass to handle entered track status in a comprehensible way.
+ /* third pass to handle entered track status in a comprehensible way.
*/
switch (item_type) {
break;
default:
- set_entered_track (0);
+
break;
}
AutomationLine* al;
Marker *marker;
Location *loc;
- RegionView* rv;
bool is_start;
bool ret = true;
switch (item_type) {
case ControlPointItem:
- if (is_drawable()) {
- set_canvas_cursor (current_canvas_cursor);
- }
-
- _verbose_cursor->hide ();
- break;
-
- case RegionViewNameHighlight:
- case LeftFrameHandle:
- case RightFrameHandle:
- case StartSelectionTrimItem:
- case EndSelectionTrimItem:
- case PlayheadCursorItem:
-
- _over_region_trim_target = false;
-
- if (is_drawable()) {
- set_canvas_cursor (current_canvas_cursor);
- }
+ _verbose_cursor->hide ();
break;
case GainLineItem:
line->set_outline_color (al->get_line_color());
}
}
- if (is_drawable()) {
- set_canvas_cursor (current_canvas_cursor);
- }
- break;
-
- case RegionViewName:
- /* see enter_handler() for notes */
- _over_region_trim_target = false;
-
- if (!reinterpret_cast<RegionView *> (item->get_data ("regionview"))->name_active()) {
- if (is_drawable() && mouse_mode == MouseObject) {
- set_canvas_cursor (current_canvas_cursor);
- }
- }
- break;
-
- case RangeMarkerBarItem:
- case TransportMarkerBarItem:
- case CdMarkerBarItem:
- case MeterBarItem:
- case TempoBarItem:
- case MarkerBarItem:
- if (is_drawable()) {
- set_canvas_cursor (current_canvas_cursor);
- }
break;
case MarkerItem:
// fall through
case MeterMarkerItem:
case TempoMarkerItem:
-
- if (is_drawable()) {
- set_canvas_cursor (current_canvas_cursor);
- }
-
break;
+ case FadeInTrimHandleItem:
+ case FadeOutTrimHandleItem:
case FadeInHandleItem:
case FadeOutHandleItem:
- rv = static_cast<RegionView*>(item->get_data ("regionview"));
- {
- ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
- if (rect) {
- rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
- }
+ {
+ ArdourCanvas::Rectangle *rect = dynamic_cast<ArdourCanvas::Rectangle *> (item);
+ if (rect) {
+ rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_InactiveFadeHandle());
}
- set_canvas_cursor (current_canvas_cursor);
- break;
+ }
+ break;
case AutomationTrackItem:
- if (is_drawable()) {
- set_canvas_cursor (current_canvas_cursor);
- clear_entered_track = true;
- Glib::signal_idle().connect (sigc::mem_fun(*this, &Editor::left_automation_track));
- }
break;
+
case FeatureLineItem:
- {
- ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
- line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
- }
- break;
+ {
+ ArdourCanvas::Line *line = dynamic_cast<ArdourCanvas::Line *> (item);
+ line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_ZeroLine());
+ }
+ break;
default:
break;
return ret;
}
-gint
-Editor::left_automation_track ()
-{
- if (clear_entered_track) {
- set_entered_track (0);
- clear_entered_track = false;
- }
- return false;
-}
-
void
Editor::scrub (framepos_t frame, double current_x)
{
current_stepping_trackview = 0;
step_timeout.disconnect ();
}
-
+
if (_session && _session->actively_recording()) {
/* Sorry. no dragging stuff around while we record */
return true;
}
-
- JoinObjectRangeState const old = _join_object_range_state;
- update_join_object_range_location (event->motion.x, event->motion.y);
-
- if (!_internal_editing && _join_object_range_state != old) {
- set_canvas_cursor ();
- }
-
- if (!_internal_editing && _over_region_trim_target) {
- set_canvas_cursor_for_region_view (event->motion.x, entered_regionview);
- }
-
- bool handled = false;
+
+ update_join_object_range_location (event->motion.y);
+
if (_drags->active ()) {
- handled = _drags->motion_handler (event, from_autoscroll);
+ return _drags->motion_handler (event, from_autoscroll);
}
- if (!handled) {
- return false;
- }
-
- track_canvas_motion (event);
- return true;
+ return false;
}
bool
if (s.empty ()) {
return;
}
-
+
EditNoteDialog* d = new EditNoteDialog (&(*s.begin())->region_view(), s);
- d->show_all ();
+ d->show_all ();
ensure_float (*d);
- d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
+ d->signal_response().connect (sigc::bind (sigc::mem_fun (*this, &Editor::note_edit_done), d));
}
void
Editor::note_edit_done (int r, EditNoteDialog* d)
{
- d->done (r);
- delete d;
+ d->done (r);
+ delete d;
}
void
void
Editor::collect_and_select_new_region_view (RegionView* rv)
{
- selection->add(rv);
+ selection->add(rv);
latest_regionviews.push_back (rv);
}
void
Editor::cancel_time_selection ()
{
- for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+ for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
(*i)->hide_selection ();
}
selection->time.clear ();
return;
}
- _region_motion_group->raise_to_top ();
-
switch (Config->get_edit_mode()) {
case Splice:
_drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer()));
default:
_drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false));
break;
+
}
}
return;
}
- _region_motion_group->raise_to_top ();
-
_drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, true));
}
}
_internal_editing = yn;
-
+
if (yn) {
- pre_internal_mouse_mode = mouse_mode;
+ pre_internal_mouse_mode = mouse_mode;
pre_internal_snap_type = _snap_type;
pre_internal_snap_mode = _snap_mode;
- for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
- (*i)->enter_internal_edit_mode ();
- }
+ for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+ (*i)->enter_internal_edit_mode ();
+ }
set_snap_to (internal_snap_type);
set_snap_mode (internal_snap_mode);
internal_snap_mode = _snap_mode;
internal_snap_type = _snap_type;
- for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
- (*i)->leave_internal_edit_mode ();
- }
+ for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+ (*i)->leave_internal_edit_mode ();
+ }
- if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
- /* we were drawing .. flip back to something sensible */
- set_mouse_mode (pre_internal_mouse_mode);
- }
+ if (mouse_mode == MouseDraw && pre_internal_mouse_mode != MouseDraw) {
+ /* we were drawing .. flip back to something sensible */
+ set_mouse_mode (pre_internal_mouse_mode);
+ }
set_snap_to (pre_internal_snap_type);
set_snap_mode (pre_internal_snap_mode);
}
-
- set_canvas_cursor ();
+
+ reset_canvas_cursor ();
}
-/** Update _join_object_range_state which indicate whether we are over the top or bottom half of a region view,
- * used by the `join object/range' tool mode.
+/** Update _join_object_range_state which indicate whether we are over the top
+ * or bottom half of a route view, used by the `join object/range' tool
+ * mode. Coordinates in canvas space.
*/
void
-Editor::update_join_object_range_location (double /*x*/, double y)
+Editor::update_join_object_range_location (double y)
{
- /* XXX: actually, this decides based on whether the mouse is in the top
- or bottom half of a the waveform part RouteTimeAxisView;
-
- Note that entered_{track,regionview} is not always setup (e.g. if
- the mouse is over a TimeSelection), and to get a Region
- that we're over requires searching the playlist.
- */
-
- if ( !get_smart_mode() ) {
+ if (_internal_editing || !get_smart_mode()) {
_join_object_range_state = JOIN_OBJECT_RANGE_NONE;
return;
}
+ JoinObjectRangeState const old = _join_object_range_state;
+
if (mouse_mode == MouseObject) {
_join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
} else if (mouse_mode == MouseRange) {
_join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
}
- /* XXX: maybe we should make entered_track work in all cases, rather than resorting to this */
- pair<TimeAxisView*, int> tvp = trackview_by_y_position (y);
-
- if (tvp.first) {
- RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tvp.first);
- if (rtv) {
+ if (entered_regionview) {
- double cx = 0;
- double cy = y;
- rtv->canvas_display()->canvas_to_item (cx, cy);
+ ArdourCanvas::Duple const item_space = entered_regionview->get_canvas_group()->canvas_to_item (ArdourCanvas::Duple (0, y));
+ double const c = item_space.y / entered_regionview->height();
+
+ _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
+
+ if (_join_object_range_state != old) {
+ set_canvas_cursor (which_track_cursor ());
+ }
- double const c = cy / (rtv->view()->child_height() - TimeAxisViewItem::NAME_HIGHLIGHT_SIZE);
+ } else if (entered_track) {
- _join_object_range_state = c <= 0.5 ? JOIN_OBJECT_RANGE_RANGE : JOIN_OBJECT_RANGE_OBJECT;
+ RouteTimeAxisView* entered_route_view = dynamic_cast<RouteTimeAxisView*> (entered_track);
+
+ if (entered_route_view) {
+ /* track/bus ... but not in a region ... use range mode */
+ _join_object_range_state = JOIN_OBJECT_RANGE_RANGE;
+ if (_join_object_range_state != old) {
+ set_canvas_cursor (which_track_cursor ());
+ }
+ } else {
+ /* Other kinds of tracks use object mode */
+ _join_object_range_state = JOIN_OBJECT_RANGE_OBJECT;
+ if (_join_object_range_state != old) {
+ set_canvas_cursor (which_track_cursor ());
+ }
}
}
}
e->region_view().delete_note (e->note ());
}
-void
-Editor::set_canvas_cursor_for_region_view (double x, RegionView* rv)
-{
- /* XXX: this check should not be necessary */
- if (rv == 0) {
- return;
- }
-
- ArdourCanvas::Group* g = rv->get_canvas_group ();
- ArdourCanvas::Group* p = g->parent ();
-
- /* Compute x in region view parent coordinates */
- double dy = 0;
- p->canvas_to_item (x, dy);
-
- boost::optional<ArdourCanvas::Rect> item_bbox = g->bounding_box ();
- assert (item_bbox);
- ArdourCanvas::Rect parent_bbox = g->item_to_parent (item_bbox.get ());
-
- /* First or last 10% of region is used for trimming, if the whole
- region is wider than 20 pixels at the current zoom level.
- */
-
- double const w = parent_bbox.width();
-
- if (w > 20.0 && x >= parent_bbox.x0 && x < parent_bbox.x1) {
-
- Trimmable::CanTrim ct = rv->region()->can_trim ();
-
- if (((x - parent_bbox.x0) / w) < 0.10) {
- if (ct & Trimmable::FrontTrimEarlier) {
- set_canvas_cursor (_cursors->left_side_trim, true);
- } else {
- set_canvas_cursor (_cursors->left_side_trim_right_only, true);
- }
- } else if (((parent_bbox.x1 - x) / w) < 0.10) {
- if (ct & Trimmable::EndTrimLater) {
- set_canvas_cursor (_cursors->right_side_trim, true);
- } else {
- set_canvas_cursor (_cursors->right_side_trim_left_only, true);
- }
- }
- }
-}
-
/** Obtain the pointer position in canvas coordinates */
void
Editor::get_pointer_position (double& x, double& y) const
#include "canvas/canvas.h"
+#include "actions.h"
#include "ardour_ui.h"
#include "audio_region_view.h"
#include "audio_streamview.h"
#include "editor_drag.h"
#include "editor_regions.h"
#include "editor_routes.h"
-#include "gtk-custom-hruler.h"
#include "gui_thread.h"
#include "insert_time_dialog.h"
#include "interthread_progress_window.h"
#include "strip_silence_dialog.h"
#include "time_axis_view.h"
#include "transpose_dialog.h"
-#include "utils.h"
#include "i18n.h"
bool
Editor::scroll_down_one_track ()
{
- double vertical_pos = vertical_adjustment.get_value () + vertical_adjustment.get_page_size() - 1.0;
-
TrackViewList::reverse_iterator next = track_views.rend();
std::pair<TimeAxisView*,double> res;
+ const double bottom_of_trackviews = vertical_adjustment.get_value() + vertical_adjustment.get_page_size() - 1;
for (TrackViewList::reverse_iterator t = track_views.rbegin(); t != track_views.rend(); ++t) {
if ((*t)->hidden()) {
continue;
}
- res = (*t)->covers_y_position (vertical_pos);
+ /* If this is the bottom visible trackview, we want to display
+ the next one.
+ */
+
+ res = (*t)->covers_y_position (bottom_of_trackviews);
if (res.first) {
break;
}
- next = t;
+ ++next; // moves "next" towards the "front" since it is a reverse iterator
}
/* move to the track below the first one that covers the */
if (next != track_views.rend()) {
- ensure_track_visible (*next);
+ ensure_time_axis_view_is_visible (**next);
return true;
}
continue;
}
- res = (*t)->covers_y_position(vertical_pos);
+ /* find the trackview at the top of the trackview group */
+ res = (*t)->covers_y_position (vertical_pos);
if (res.first) {
+ cerr << res.first->name() << " covers the top\n";
break;
}
}
if (prev != track_views.end()) {
- ensure_track_visible (*prev);
+ ensure_time_axis_view_is_visible (**prev);
return true;
}
void
Editor::tav_zoom_step (bool coarser)
{
- _routes->suspend_redisplay ();
+ DisplaySuspender ds;
TrackViewList* ts;
TimeAxisView *tv = (static_cast<TimeAxisView*>(*i));
tv->step_height (coarser);
}
-
- _routes->resume_redisplay ();
}
void
Editor::tav_zoom_smooth (bool coarser, bool force_all)
{
- _routes->suspend_redisplay ();
+ DisplaySuspender ds;
TrackViewList* ts;
tv->set_height (h + 5);
}
}
-
- _routes->resume_redisplay ();
}
bool
clamped = true;
}
- if (max_framepos / fpp < 800) {
- fpp = max_framepos / 800;
+ framecnt_t sr;
+
+ if (_session) {
+ sr = _session->frame_rate ();
+ } else {
+ sr = 48000;
+ }
+
+ const framecnt_t three_days = 3 * 24 * 60 * 60 * sr;
+ const framecnt_t lots_of_pixels = 4000;
+
+ /* if the zoom level is greater than what you'd get trying to display 3
+ * days of audio on a really big screen, scale it down.
+ */
+
+ if (fpp * lots_of_pixels > three_days) {
+ fpp = three_days / _track_canvas->width();
clamped = true;
}
/* hide irrelevant tracks */
- _routes->suspend_redisplay ();
+ DisplaySuspender ds;
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
}
}
- _routes->resume_redisplay ();
-
vertical_adjustment.set_value (0.0);
}
if (!_session) {
return;
}
- double range_before = frame - leftmost_frame;
- double new_fpp;
- new_fpp = samples_per_pixel;
+ framecnt_t range_before = frame - leftmost_frame;
+ framecnt_t new_spp;
if (coarser) {
- new_fpp *= 1.61803399;
- range_before *= 1.61803399;
+ if (samples_per_pixel <= 1) {
+ new_spp = 2;
+ } else {
+ new_spp = samples_per_pixel + (samples_per_pixel/2);
+ }
+ range_before += range_before/2;
} else {
- new_fpp = max(1.0,(new_fpp/1.61803399));
- range_before /= 1.61803399;
+ if (samples_per_pixel >= 1) {
+ new_spp = samples_per_pixel - (samples_per_pixel/2);
+ } else {
+ /* could bail out here since we cannot zoom any finer,
+ but leave that to the clamp_samples_per_pixel() and
+ equality test below
+ */
+ new_spp = samples_per_pixel;
+ }
+
+ range_before -= range_before/2;
}
- if (new_fpp == samples_per_pixel) {
+ clamp_samples_per_pixel (new_spp);
+
+ if (new_spp == samples_per_pixel) {
return;
}
+ /* zoom focus is automatically taken as @param frame when this
+ method is used.
+ */
+
framepos_t new_leftmost = frame - (framepos_t)range_before;
if (new_leftmost > frame) {
new_leftmost = 0;
}
- reposition_and_zoom (new_leftmost, new_fpp);
+ reposition_and_zoom (new_leftmost, new_spp);
}
/* create event pool because we may need to talk to the session */
SessionEvent::create_per_thread_pool ("freeze events", 64);
/* create per-thread buffers for process() tree to use */
- current_interthread_info->process_thread.get_buffers ();
clicked_routeview->audio_track()->freeze_me (*current_interthread_info);
current_interthread_info->done = true;
- current_interthread_info->process_thread.drop_buffers();
return 0;
}
selection->set(current);
- ensure_track_visible(current);
+ ensure_time_axis_view_is_visible (*current);
}
void
selection->set (current);
- ensure_track_visible(current);
-}
-
-void
-Editor::ensure_track_visible(TimeAxisView *track)
-{
- if (track->hidden())
- return;
-
- double const current_view_min_y = vertical_adjustment.get_value();
- double const current_view_max_y = vertical_adjustment.get_value() + vertical_adjustment.get_page_size();
-
- double const track_min_y = track->y_position ();
- double const track_max_y = track->y_position () + track->effective_height ();
-
- if (track_min_y >= current_view_min_y &&
- track_max_y <= current_view_max_y) {
- return;
- }
-
- double new_value;
-
- if (track_min_y < current_view_min_y) {
- // Track is above the current view
- new_value = track_min_y;
- } else {
- // Track is below the current view
- new_value = track->y_position () + track->effective_height() - vertical_adjustment.get_page_size();
- }
-
- vertical_adjustment.set_value(new_value);
+ ensure_time_axis_view_is_visible (*current);
}
void
return;
}
- for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
- _session->remove_route (*x);
+ {
+ Session::StateProtector sp (_session);
+ for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
+ _session->remove_route (*x);
+ }
}
}
++visible_tracks;
}
- uint32_t h = (uint32_t) floor ((_visible_canvas_height - child_heights) / visible_tracks);
+ /* compute the per-track height from:
+
+ total canvas visible height -
+ height that will be taken by visible children of selected
+ tracks - height of the ruler/hscroll area
+ */
+ uint32_t h = (uint32_t) floor ((_visible_canvas_height - (child_heights + _trackview_group->canvas_origin().y)) / visible_tracks);
double first_y_pos = DBL_MAX;
if (h < TimeAxisView::preset_height (HeightSmall)) {
/* operate on all tracks, hide unselected ones that are in the middle of selected ones */
- bool prev_was_selected = false;
- bool is_selected = tracks.contains (all.front());
- bool next_is_selected;
+ bool within_selected = false;
for (TrackViewList::iterator t = all.begin(); t != all.end(); ++t) {
next = t;
++next;
-
- if (next != all.end()) {
- next_is_selected = tracks.contains (*next);
- } else {
- next_is_selected = false;
- }
-
+
if ((*t)->marked_for_display ()) {
- if (is_selected) {
+ if (tracks.contains (*t)) {
(*t)->set_height (h);
first_y_pos = std::min ((*t)->y_position (), first_y_pos);
- } else {
- if (prev_was_selected && next_is_selected) {
- hide_track_in_display (*t);
- }
+ within_selected = true;
+ } else if (within_selected) {
+ hide_track_in_display (*t);
}
}
-
- prev_was_selected = is_selected;
- is_selected = next_is_selected;
}
/*
_session->set_exclusive_input_active (rl, onoff, flip_others);
}
+
+void
+Editor::lock ()
+{
+ if (!lock_dialog) {
+ lock_dialog = new Gtk::Dialog (string_compose (_("%1: Locked"), PROGRAM_NAME), true);
+
+ Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
+ lock_dialog->get_vbox()->pack_start (*padlock);
+
+ ArdourButton* b = manage (new ArdourButton);
+ b->set_name ("lock button");
+ b->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Click to unlock")));
+ b->signal_clicked.connect (sigc::mem_fun (*this, &Editor::unlock));
+ lock_dialog->get_vbox()->pack_start (*b);
+
+ lock_dialog->get_vbox()->show_all ();
+ lock_dialog->set_size_request (200, 200);
+ }
+
+#ifdef __APPLE__
+ /* The global menu bar continues to be accessible to applications
+ with modal dialogs, which means that we need to desensitize
+ all items in the menu bar. Since those items are really just
+ proxies for actions, that means disabling all actions.
+ */
+ ActionManager::disable_all_actions ();
+#endif
+ lock_dialog->present ();
+}
+
+void
+Editor::unlock ()
+{
+ lock_dialog->hide ();
+
+#ifdef __APPLE__
+ ActionManager::pop_action_state ();
+#endif
+
+ if (ARDOUR_UI::config()->get_lock_gui_after_seconds()) {
+ start_lock_event_timing ();
+ }
+}
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Glib;
{ 3, _("Length"), _("Length of the region") },
{ 4, _("Sync"), _("Position of region sync point, relative to start of the region") },
{ 5, _("Fade In"), _("Length of region fade-in (units: secondary clock), () if disabled") },
- { 6, _("Fade Out"), _("Length of region fade-out (units: secondary clock), () if dsisabled") },
+ { 6, _("Fade Out"), _("Length of region fade-out (units: secondary clock), () if disabled") },
{ 7, _("L"), _("Region position locked?") },
{ 8, _("G"), _("Region position glued to Bars|Beats time?") },
{ 9, _("M"), _("Region muted?") },
c.set_rgb(0,65535,0); // FIXME: error color from style
} else {
- set_color(c, rgba_from_style ("RegionListWholeFile", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL, false ));
+ set_color_from_rgba (c, rgba_from_style ("RegionListWholeFile", 0xff, 0, 0, 0, "fg", Gtk::STATE_NORMAL, false ));
}
#include "gtkmm2ext/cell_renderer_color_selector.h"
#include "ardour/route_group.h"
+#include "ardour/route.h"
+#include "ardour/session.h"
+#include "ardour_ui.h"
#include "editor.h"
+#include "editor_group_tabs.h"
+#include "editor_route_groups.h"
+#include "editor_routes.h"
+#include "gui_thread.h"
#include "keyboard.h"
#include "marker.h"
-#include "time_axis_view.h"
#include "prompter.h"
-#include "gui_thread.h"
-#include "editor_group_tabs.h"
#include "route_group_dialog.h"
#include "route_time_axis.h"
-#include "editor_routes.h"
-#include "editor_route_groups.h"
-#include "ardour_ui.h"
-
-#include "ardour/route.h"
-#include "ardour/session.h"
+#include "time_axis_view.h"
+#include "utils.h"
#include "i18n.h"
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using Gtkmm2ext::Keyboard;
Gtkmm2ext::CellRendererColorSelector* color_renderer = manage (new Gtkmm2ext::CellRendererColorSelector);
TreeViewColumn* color_column = manage (new TreeViewColumn ("", *color_renderer));
+
color_column->add_attribute (color_renderer->property_color(), _columns.gdkcolor);
_display.append_column (*color_column);
switch (GPOINTER_TO_UINT (column->get_data (X_("colnum")))) {
case 0:
- c = (*iter)[_columns.gdkcolor];
-
+ c = (*iter)[_columns.gdkcolor];
color_dialog.get_colorsel()->set_previous_color (c);
color_dialog.get_colorsel()->set_current_color (c);
break;
case RESPONSE_ACCEPT:
c = color_dialog.get_colorsel()->get_current_color();
- GroupTabs::set_group_color (group, c);
+ GroupTabs::set_group_color (group, gdk_color_to_rgba (c));
ARDOUR_UI::config()->set_dirty ();
break;
group->apply_changes (plist);
- GroupTabs::set_group_color ((*iter)[_columns.routegroup], (*iter)[_columns.gdkcolor]);
+ GroupTabs::set_group_color ((*iter)[_columns.routegroup], gdk_color_to_rgba ((*iter)[_columns.gdkcolor]));
}
void
row[_columns.active_shared] = group->is_route_active ();
row[_columns.active_state] = group->is_active ();
row[_columns.is_visible] = !group->is_hidden();
- row[_columns.gdkcolor] = GroupTabs::group_color (group);
+
+ Gdk::Color c;
+ set_color_from_rgba (c, GroupTabs::group_color (group));
+ row[_columns.gdkcolor] = c;
_in_row_change = true;
(*iter)[_columns.active_shared] = group->is_route_active ();
(*iter)[_columns.active_state] = group->is_active ();
(*iter)[_columns.is_visible] = !group->is_hidden();
- (*iter)[_columns.gdkcolor] = GroupTabs::group_color (group);
+
+ Gdk::Color c;
+ set_color_from_rgba (c, GroupTabs::group_color (group));
+ (*iter)[_columns.gdkcolor] = c;
break;
}
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
, _ignore_reorder (false)
, _no_redisplay (false)
, _adding_routes (false)
+ , _route_deletion_in_progress (false)
, _menu (0)
, old_focus (0)
, selection_countdown (0)
active_col->set_fixed_width (30);
active_col->set_alignment (ALIGN_CENTER);
- _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::route_deleted));
+ _model->signal_row_deleted().connect (sigc::mem_fun (*this, &EditorRoutes::row_deleted));
_model->signal_rows_reordered().connect (sigc::mem_fun (*this, &EditorRoutes::reordered));
_display.signal_button_press_event().connect (sigc::mem_fun (*this, &EditorRoutes::button_press), false);
/* show or hide the TimeAxisView */
if (visible) {
position += tv->show_at (position, n, &_editor->edit_controls_vbox);
- // SHOWTRACKS
} else {
tv->hide ();
}
}
void
-EditorRoutes::route_deleted (Gtk::TreeModel::Path const &)
+EditorRoutes::row_deleted (Gtk::TreeModel::Path const &)
{
- /* this happens as the second step of a DnD within the treeview as well
- as when a row/route is actually deleted.
+ /* this happens as the second step of a DnD within the treeview, and
+ when a route is actually removed. we don't differentiate between
+ the two cases.
+
+ note that the sync_orders_keys() step may not actually change any
+ RID's (e.g. the last track may be removed, so all other tracks keep
+ the same RID), which means that no redisplay would happen. so we
+ have to force a redisplay.
*/
+
DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview row deleted\n");
+
+ DisplaySuspender ds;
sync_order_keys_from_treeview ();
}
void
EditorRoutes::reordered (TreeModel::Path const &, TreeModel::iterator const &, int* /*what*/)
{
+ /* reordering implies that RID's will change, so sync_order_keys() will
+ cause a redisplay.
+ */
+
DEBUG_TRACE (DEBUG::OrderKeys, "editor routes treeview reordered\n");
sync_order_keys_from_treeview ();
}
return;
}
+ DisplaySuspender ds;
TreeIter iter;
if ((iter = _model->get_iter (path))) {
_editor->selection->tracks.clear();
}
- suspend_redisplay ();
+ DisplaySuspender ds;
_display.set_model (Glib::RefPtr<ListStore>());
update_input_active_display ();
update_active_display ();
- resume_redisplay ();
_display.set_model (_model);
/* now update route order keys from the treeview/track display order */
TreeModel::Children rows = _model->children();
TreeModel::Children::iterator ri;
+ bool found = false;
for (ri = rows.begin(); ri != rows.end(); ++ri) {
if ((*ri)[_columns.tv] == tv) {
+ PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
_model->erase (ri);
+ found = true;
break;
}
}
TreeModel::Children rows = _model->children();
TreeModel::Children::iterator i;
- suspend_redisplay ();
+ DisplaySuspender ds ();
for (i = rows.begin(); i != rows.end(); ++i) {
TimeAxisView *tv = (*i)[_columns.tv];
*/
sync_order_keys_from_treeview ();
-
- resume_redisplay ();
}
void
TreeModel::Children rows = _model->children();
TreeModel::Children::iterator i;
- suspend_redisplay ();
+ DisplaySuspender ds;
for (i = rows.begin(); i != rows.end(); ++i) {
row[_columns.visible] = false;
}
-
- resume_redisplay ();
}
void
TreeModel::Children rows = _model->children();
TreeModel::Children::iterator i;
- suspend_redisplay ();
+ DisplaySuspender ds;
for (i = rows.begin(); i != rows.end(); ++i) {
*/
sync_order_keys_from_treeview ();
-
- resume_redisplay ();
}
void
TreeModel::Children rows = _model->children();
TreeModel::Children::iterator i;
- suspend_redisplay ();
+ DisplaySuspender ds;
for (i = rows.begin(); i != rows.end(); ++i) {
*/
sync_order_keys_from_treeview ();
-
- resume_redisplay ();
}
void
void
EditorRoutes::initial_display ()
{
- suspend_redisplay ();
+ DisplaySuspender ds;
_model->clear ();
if (!_session) {
- resume_redisplay ();
return;
}
_editor->add_routes (r);
}
-
- resume_redisplay ();
}
void
}
}
- suspend_redisplay ();
+ DisplaySuspender ds;
TreeModel::Children rows = _model->children ();
for (TreeModel::Children::iterator i = rows.begin(); i != rows.end(); ++i) {
TimeAxisView* tv = (*i)[_columns.tv];
(*i)[_columns.visible] = (show.find (tv) != show.end());
}
-
- resume_redisplay ();
}
-
_no_redisplay = true;
}
- void allow_redisplay () {
- _no_redisplay = false;
- }
-
void resume_redisplay () {
_no_redisplay = false;
redisplay ();
void build_menu ();
void show_menu ();
void sync_treeview_from_order_keys ();
- void route_deleted (Gtk::TreeModel::Path const &);
+ void row_deleted (Gtk::TreeModel::Path const &);
void visible_changed (std::string const &);
void active_changed (std::string const &);
void reordered (Gtk::TreeModel::Path const &, Gtk::TreeModel::iterator const &, int *);
bool _ignore_reorder;
bool _no_redisplay;
bool _adding_routes;
+ bool _route_deletion_in_progress;
Gtk::Menu* _menu;
Gtk::Widget* old_focus;
#include <gtk/gtkaction.h>
-#include "canvas/group.h"
+#include "canvas/container.h"
#include "canvas/canvas.h"
+#include "canvas/ruler.h"
+#include "canvas/debug.h"
+#include "canvas/scroll_group.h"
#include "ardour/session.h"
#include "ardour/tempo.h"
#include "ardour/profile.h"
#include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/keyboard.h"
+#include "ardour_ui.h"
#include "editor.h"
#include "editing.h"
#include "actions.h"
-#include "gtk-custom-hruler.h"
#include "gui_thread.h"
+#include "ruler_dialog.h"
#include "time_axis_view.h"
#include "editor_drag.h"
#include "editor_cursors.h"
using namespace Gtk;
using namespace Editing;
-Editor *Editor::ruler_editor;
-
/* the order here must match the "metric" enums in editor.h */
-GtkCustomMetric Editor::ruler_metrics[4] = {
- {1, Editor::_metric_get_timecode },
- {1, Editor::_metric_get_bbt },
- {1, Editor::_metric_get_samples },
- {1, Editor::_metric_get_minsec }
-};
-
-void
-Editor::initialize_rulers ()
+class TimecodeMetric : public ArdourCanvas::Ruler::Metric
{
- ruler_editor = this;
- ruler_grabbed_widget = 0;
-
- _ruler_separator = new Gtk::HSeparator();
- _ruler_separator->set_size_request(-1, 2);
- _ruler_separator->set_name("TimebarPadding");
- _ruler_separator->show();
-
- _minsec_ruler = gtk_custom_hruler_new ();
- minsec_ruler = Glib::wrap (_minsec_ruler);
- minsec_ruler->set_name ("MinSecRuler");
- minsec_ruler->set_size_request (-1, (int)timebar_height);
- gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_minsec_ruler), &ruler_metrics[ruler_metric_minsec]);
- minsec_ruler->hide ();
- minsec_ruler->set_no_show_all();
-
- _timecode_ruler = gtk_custom_hruler_new ();
- timecode_ruler = Glib::wrap (_timecode_ruler);
- timecode_ruler->set_name ("TimecodeRuler");
- timecode_ruler->set_size_request (-1, (int)timebar_height);
- gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_timecode_ruler), &ruler_metrics[ruler_metric_timecode]);
- timecode_ruler->hide ();
- timecode_ruler->set_no_show_all();
- timecode_nmarks = 0;
-
- _bbt_ruler = gtk_custom_hruler_new ();
- bbt_ruler = Glib::wrap (_bbt_ruler);
- bbt_ruler->set_name ("BBTRuler");
- bbt_ruler->set_size_request (-1, (int)timebar_height);
- gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_bbt_ruler), &ruler_metrics[ruler_metric_bbt]);
- bbt_ruler->hide ();
- bbt_ruler->set_no_show_all();
- bbt_nmarks = 0;
-
- _samples_ruler = gtk_custom_hruler_new ();
- samples_ruler = Glib::wrap (_samples_ruler);
- samples_ruler->set_name ("SamplesRuler");
- samples_ruler->set_size_request (-1, (int) timebar_height);
- gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER (_samples_ruler), &ruler_metrics[ruler_metric_samples]);
- samples_ruler->hide ();
- samples_ruler->set_no_show_all ();
-
- _bbt_ruler = gtk_custom_hruler_new ();
- bbt_ruler = Glib::wrap (_bbt_ruler);
- bbt_ruler->set_name ("BBTRuler");
- bbt_ruler->set_size_request (-1, (int)timebar_height);
- gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_bbt_ruler), &ruler_metrics[ruler_metric_bbt]);
- bbt_ruler->hide ();
- bbt_ruler->set_no_show_all();
- minsec_ruler->hide ();
- minsec_ruler->set_no_show_all();
- minsec_nmarks = 0;
-
- using namespace Box_Helpers;
- BoxList & ruler_lab_children = ruler_label_vbox.children();
- BoxList & ruler_children = time_canvas_vbox.children();
- BoxList & lab_children = time_bars_vbox.children();
-
- BoxList::iterator canvaspos = ruler_children.begin();
+ public:
+ TimecodeMetric (Editor* e) : _editor (e) {}
- lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
- lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
- lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
- lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
- lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
- lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
- lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
-
- ruler_lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
- ruler_children.insert (canvaspos, Element(*minsec_ruler, PACK_SHRINK, PACK_START));
- ruler_lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
- ruler_children.insert (canvaspos, Element(*timecode_ruler, PACK_SHRINK, PACK_START));
- ruler_lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
- ruler_children.insert (canvaspos, Element (*samples_ruler, PACK_SHRINK, PACK_START));
- ruler_lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
- ruler_children.insert (canvaspos, Element(*bbt_ruler, PACK_SHRINK, PACK_START));
-
- timecode_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
- bbt_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
- samples_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
- minsec_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
-
- timecode_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
- bbt_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
- samples_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
- minsec_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
-
- timecode_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
- bbt_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
- samples_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
- minsec_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
-
- timecode_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
- bbt_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
- samples_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
- minsec_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
-
- timecode_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
- bbt_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
- samples_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
- minsec_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
+ void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+ _editor->metric_get_timecode (marks, lower, upper, maxchars);
+ }
- visible_timebars = 0; /*this will be changed below */
-}
+ private:
+ Editor* _editor;
+};
-bool
-Editor::ruler_scroll (GdkEventScroll* event)
+class SamplesMetric : public ArdourCanvas::Ruler::Metric
{
- framepos_t xdelta;
- int direction = event->direction;
- bool handled = false;
-
- switch (direction) {
- case GDK_SCROLL_UP:
- temporal_zoom_step (false);
- handled = true;
- break;
+ public:
+ SamplesMetric (Editor* e) : _editor (e) {}
- case GDK_SCROLL_DOWN:
- temporal_zoom_step (true);
- handled = true;
- break;
-
- case GDK_SCROLL_LEFT:
- xdelta = (current_page_samples() / 2);
- if (leftmost_frame > xdelta) {
- reset_x_origin (leftmost_frame - xdelta);
- } else {
- reset_x_origin (0);
- }
- handled = true;
- break;
-
- case GDK_SCROLL_RIGHT:
- xdelta = (current_page_samples() / 2);
- if (max_framepos - xdelta > leftmost_frame) {
- reset_x_origin (leftmost_frame + xdelta);
- } else {
- reset_x_origin (max_framepos - current_page_samples());
- }
- handled = true;
- break;
-
- default:
- /* what? */
- break;
+ void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+ _editor->metric_get_samples (marks, lower, upper, maxchars);
}
- return handled;
-}
-
+ private:
+ Editor* _editor;
+};
-bool
-Editor::ruler_button_press (GdkEventButton* ev)
+class BBTMetric : public ArdourCanvas::Ruler::Metric
{
- if (_session == 0) {
- return false;
- }
-
- Widget * grab_widget = 0;
-
- if (timecode_ruler->is_realized() && ev->window == timecode_ruler->get_window()->gobj()) {
- grab_widget = timecode_ruler;
- } else if (bbt_ruler->is_realized() && ev->window == bbt_ruler->get_window()->gobj()) {
- grab_widget = bbt_ruler;
- } else if (samples_ruler->is_realized() && ev->window == samples_ruler->get_window()->gobj()) {
- grab_widget = samples_ruler;
- } else if (minsec_ruler->is_realized() && ev->window == minsec_ruler->get_window()->gobj()) {
- grab_widget = minsec_ruler;
- }
+ public:
+ BBTMetric (Editor* e) : _editor (e) {}
- if (grab_widget) {
- grab_widget->add_modal_grab ();
- ruler_grabbed_widget = grab_widget;
+ void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+ _editor->metric_get_bbt (marks, lower, upper, maxchars);
}
- if (ev->button == 1) {
- // Since we will locate the playhead on button release, cancel any running
- // auditions.
- if (_session->is_auditioning()) {
- _session->cancel_audition ();
- }
-
- /* playhead cursor drag: CursorDrag expects an event with
- * canvas coordinates, so convert from window coordinates,
- * since for now, rulers are still Gtk::Widgets.
- */
+ private:
+ Editor* _editor;
+};
- GdkEventButton canvas_ev = *ev;
- ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (ev->x, ev->y));
- canvas_ev.x = rint (d.x);
- canvas_ev.y = rint (d.y);
+class MinsecMetric : public ArdourCanvas::Ruler::Metric
+{
+ public:
+ MinsecMetric (Editor* e) : _editor (e) {}
- _drags->set (new CursorDrag (this, *playhead_cursor, false), reinterpret_cast<GdkEvent *> (&canvas_ev));
- _dragging_playhead = true;
+ void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+ _editor->metric_get_minsec (marks, lower, upper, maxchars);
}
- return true;
-}
+ private:
+ Editor* _editor;
+};
-bool
-Editor::ruler_button_release (GdkEventButton* ev)
+static ArdourCanvas::Ruler::Metric* _bbt_metric;
+static ArdourCanvas::Ruler::Metric* _timecode_metric;
+static ArdourCanvas::Ruler::Metric* _samples_metric;
+static ArdourCanvas::Ruler::Metric* _minsec_metric;
+
+void
+Editor::initialize_rulers ()
{
- if (_session == 0) {
- return false;
- }
+ ruler_grabbed_widget = 0;
+ Pango::FontDescription font (ARDOUR_UI::config()->get_canvasvar_SmallFont());
+
+ _timecode_metric = new TimecodeMetric (this);
+ _bbt_metric = new BBTMetric (this);
+ _minsec_metric = new MinsecMetric (this);
+ _samples_metric = new SamplesMetric (this);
+
+ timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
+ ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+ timecode_ruler->set_font_description (font);
+ CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
+ timecode_nmarks = 0;
- if (_drags->active ()) {
- GdkEventButton canvas_ev = *ev;
- ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (ev->x, ev->y));
- canvas_ev.x = rint (d.x);
- canvas_ev.x = rint (d.y);
- _drags->end_grab (reinterpret_cast<GdkEvent*> (&canvas_ev));
- _dragging_playhead = false;
- }
+ samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
+ ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+ samples_ruler->set_font_description (font);
+ CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
- if (ev->button == 3) {
-
- stop_canvas_autoscroll();
+ minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
+ ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+ minsec_ruler->set_font_description (font);
+ CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
+ minsec_nmarks = 0;
- framepos_t where = window_event_sample ((GdkEvent*) ev);
+ bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
+ ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+ bbt_ruler->set_font_description (font);
+ CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
+ timecode_nmarks = 0;
- snap_to (where);
- popup_ruler_menu (where);
- }
+ using namespace Box_Helpers;
+ BoxList & lab_children = time_bars_vbox.children();
- if (ruler_grabbed_widget) {
- ruler_grabbed_widget->remove_modal_grab();
- ruler_grabbed_widget = 0;
- }
+ lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
+ lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
- return true;
+ /* 1 event handler to bind them all ... */
+
+ timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
+ minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
+ bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
+ samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
+
+ visible_timebars = 0; /*this will be changed below */
}
bool
Editor::ruler_label_button_release (GdkEventButton* ev)
{
- if (ev->button == 3) {
- Gtk::Menu* m = dynamic_cast<Gtk::Menu*> (ActionManager::get_widget (X_("/RulerMenuPopup")));
- if (m) {
- m->popup (1, ev->time);
+ if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
+ if (!ruler_dialog) {
+ ruler_dialog = new RulerDialog ();
}
+ ruler_dialog->present ();
}
return true;
}
-
-bool
-Editor::ruler_mouse_motion (GdkEventMotion* ev)
-{
- if (_session == 0) {
- return false;
- }
-
- if (_drags->active ()) {
- GdkEventMotion canvas_ev = *ev;
- ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (ev->x, ev->y));
- canvas_ev.x = rint (d.x);
- canvas_ev.y = rint (d.y);
- _drags->window_motion_handler (reinterpret_cast<GdkEvent*> (&canvas_ev), false);
- }
-
- return true;
-}
-
-
void
Editor::popup_ruler_menu (framepos_t where, ItemType t)
{
case TempoBarItem:
ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
- ruler_items.push_back (SeparatorElem ());
break;
case MeterBarItem:
ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
- ruler_items.push_back (SeparatorElem ());
break;
case VideoBarItem:
ruler_items.push_back (CheckMenuElem (_("Lock")));
{
- Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
- vtl_lock->set_active(is_video_timeline_locked());
- vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
+ Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
+ vtl_lock->set_active(is_video_timeline_locked());
+ vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
}
-
- ruler_items.push_back (SeparatorElem ());
break;
default:
break;
}
- Glib::RefPtr<Action> action;
-
- action = ActionManager::get_action ("Rulers", "toggle-minsec-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
- if (!Profile->get_sae()) {
- action = ActionManager::get_action ("Rulers", "toggle-timecode-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
- }
- action = ActionManager::get_action ("Rulers", "toggle-samples-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
- action = ActionManager::get_action ("Rulers", "toggle-bbt-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
- action = ActionManager::get_action ("Rulers", "toggle-meter-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
- action = ActionManager::get_action ("Rulers", "toggle-tempo-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
+ if (!ruler_items.empty()) {
+ editor_ruler_menu->popup (1, gtk_get_current_event_time());
}
- if (!Profile->get_sae()) {
- action = ActionManager::get_action ("Rulers", "toggle-range-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
- }
- action = ActionManager::get_action ("Rulers", "toggle-loop-punch-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
- action = ActionManager::get_action ("Rulers", "toggle-cd-marker-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
- action = ActionManager::get_action ("Rulers", "toggle-marker-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
- action = ActionManager::get_action ("Rulers", "toggle-video-ruler");
- if (action) {
- ruler_items.push_back (MenuElem (*action->create_menu_item()));
- }
-
- editor_ruler_menu->popup (1, gtk_get_current_event_time());
no_ruler_shown_update = false;
}
void
Editor::update_ruler_visibility ()
{
- int visible_rulers = 0;
+ int visible_timebars = 0;
if (no_ruler_shown_update) {
return;
}
- visible_timebars = 0;
+ /* the order of the timebars is fixed, so we have to go through each one
+ * and adjust its position depending on what is shown.
+ *
+ * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
+ * loop/punch, cd markers, location markers
+ */
+
+ double tbpos = 0.0;
+ double tbgpos = 0.0;
+ double old_unit_pos;
+
+#ifdef GTKOSX
+ /* gtk update probs require this (damn) */
+ meter_label.hide();
+ tempo_label.hide();
+ range_mark_label.hide();
+ transport_mark_label.hide();
+ cd_mark_label.hide();
+ mark_label.hide();
+ videotl_label.hide();
+#endif
if (ruler_minsec_action->get_active()) {
- visible_rulers++;
- minsec_label.show ();
- minsec_ruler->show ();
+ old_unit_pos = minsec_ruler->position().y;
+ if (tbpos != old_unit_pos) {
+ minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+ }
+ minsec_ruler->show();
+ minsec_label.show();
+ tbpos += timebar_height;
+ tbgpos += timebar_height;
+ visible_timebars++;
} else {
- minsec_label.hide ();
- minsec_ruler->hide ();
+ minsec_ruler->hide();
+ minsec_label.hide();
}
if (ruler_timecode_action->get_active()) {
- visible_rulers++;
- timecode_label.show ();
- timecode_ruler->show ();
+ old_unit_pos = timecode_ruler->position().y;
+ if (tbpos != old_unit_pos) {
+ timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+ }
+ timecode_ruler->show();
+ timecode_label.show();
+ tbpos += timebar_height;
+ tbgpos += timebar_height;
+ visible_timebars++;
} else {
- timecode_label.hide ();
- timecode_ruler->hide ();
+ timecode_ruler->hide();
+ timecode_label.hide();
}
if (ruler_samples_action->get_active()) {
- visible_rulers++;
- samples_label.show ();
- samples_ruler->show ();
+ old_unit_pos = samples_ruler->position().y;
+ if (tbpos != old_unit_pos) {
+ samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+ }
+ samples_ruler->show();
+ samples_label.show();
+ tbpos += timebar_height;
+ tbgpos += timebar_height;
+ visible_timebars++;
} else {
- samples_label.hide ();
- samples_ruler->hide ();
+ samples_ruler->hide();
+ samples_label.hide();
}
if (ruler_bbt_action->get_active()) {
- visible_rulers++;
- bbt_label.show ();
- bbt_ruler->show ();
+ old_unit_pos = bbt_ruler->position().y;
+ if (tbpos != old_unit_pos) {
+ bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+ }
+ bbt_ruler->show();
+ bbt_label.show();
+ tbpos += timebar_height;
+ tbgpos += timebar_height;
+ visible_timebars++;
} else {
- bbt_label.hide ();
- bbt_ruler->hide ();
+ bbt_ruler->hide();
+ bbt_label.hide();
}
- double tbpos = 0.0;
- double tbgpos = 0.0;
- double old_unit_pos;
-
-#ifdef GTKOSX
- /* gtk update probs require this (damn) */
- meter_label.hide();
- tempo_label.hide();
- range_mark_label.hide();
- transport_mark_label.hide();
- cd_mark_label.hide();
- mark_label.hide();
- videotl_label.hide();
-#endif
if (ruler_meter_action->get_active()) {
old_unit_pos = meter_group->position().y;
if (tbpos != old_unit_pos) {
meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
}
- old_unit_pos = meter_bar_group->position().y;
- if (tbgpos != old_unit_pos) {
- meter_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
- }
- meter_bar_group->show();
meter_group->show();
meter_label.show();
tbpos += timebar_height;
tbgpos += timebar_height;
visible_timebars++;
} else {
- meter_bar_group->hide();
meter_group->hide();
meter_label.hide();
}
if (tbpos != old_unit_pos) {
tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
}
- old_unit_pos = tempo_bar_group->position().y;
- if (tbgpos != old_unit_pos) {
- tempo_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
- }
- tempo_bar_group->show();
tempo_group->show();
tempo_label.show();
tbpos += timebar_height;
tbgpos += timebar_height;
visible_timebars++;
} else {
- tempo_bar_group->hide();
tempo_group->hide();
tempo_label.hide();
}
if (tbpos != old_unit_pos) {
range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
}
- old_unit_pos = range_marker_bar_group->position().y;
- if (tbgpos != old_unit_pos) {
- range_marker_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
- }
- range_marker_bar_group->show();
range_marker_group->show();
range_mark_label.show();
tbgpos += timebar_height;
visible_timebars++;
} else {
- range_marker_bar_group->hide();
range_marker_group->hide();
range_mark_label.hide();
}
if (tbpos != old_unit_pos) {
transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
}
- old_unit_pos = transport_marker_bar_group->position().y;
- if (tbgpos != old_unit_pos) {
- transport_marker_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
- }
- transport_marker_bar_group->show();
transport_marker_group->show();
transport_mark_label.show();
tbpos += timebar_height;
tbgpos += timebar_height;
visible_timebars++;
} else {
- transport_marker_bar_group->hide();
transport_marker_group->hide();
transport_mark_label.hide();
}
if (tbpos != old_unit_pos) {
cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
}
- old_unit_pos = cd_marker_bar_group->position().y;
- if (tbgpos != old_unit_pos) {
- cd_marker_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
- }
- cd_marker_bar_group->show();
cd_marker_group->show();
cd_mark_label.show();
tbpos += timebar_height;
// make sure all cd markers show up in their respective places
update_cd_marker_display();
} else {
- cd_marker_bar_group->hide();
cd_marker_group->hide();
cd_mark_label.hide();
// make sure all cd markers show up in their respective places
if (tbpos != old_unit_pos) {
marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
}
- old_unit_pos = marker_bar_group->position().y;
- if (tbgpos != old_unit_pos) {
- marker_bar_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
- }
- marker_bar_group->show();
marker_group->show();
mark_label.show();
tbpos += timebar_height;
tbgpos += timebar_height;
visible_timebars++;
} else {
- marker_bar_group->hide();
marker_group->hide();
mark_label.hide();
}
if (tbpos != old_unit_pos) {
videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
}
- old_unit_pos = videotl_group->position().y;
- if (tbgpos != old_unit_pos) {
- videotl_group->move (ArdourCanvas::Duple (0.0, tbgpos - old_unit_pos));
- }
- videotl_group->show();
videotl_group->show();
videotl_label.show();
tbpos += timebar_height * videotl_bar_height;
visible_timebars+=videotl_bar_height;
queue_visual_videotimeline_update();
} else {
- videotl_group->hide();
videotl_group->hide();
videotl_label.hide();
update_video_timeline(true);
}
- ruler_label_vbox.set_size_request (-1, (int)(timebar_height * visible_rulers));
- time_canvas_vbox.set_size_request (-1,-1);
+ time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
+
+ /* move hv_scroll_group (trackviews) to the end of the timebars
+ */
+
+ hv_scroll_group->set_y_position (timebar_height * visible_timebars);
compute_fixed_ruler_scale ();
update_fixed_rulers();
framepos_t rightmost_frame = leftmost_frame + current_page_samples();
if (ruler_timecode_action->get_active()) {
- gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_timecode_ruler), leftmost_frame, rightmost_frame,
- leftmost_frame, _session->current_end_frame());
+ timecode_ruler->set_range (leftmost_frame, rightmost_frame);
}
}
compute_fixed_ruler_scale ();
- ruler_metrics[ruler_metric_timecode].units_per_pixel = samples_per_pixel;
- ruler_metrics[ruler_metric_samples].units_per_pixel = samples_per_pixel;
- ruler_metrics[ruler_metric_minsec].units_per_pixel = samples_per_pixel;
+ _timecode_metric->units_per_pixel = samples_per_pixel;
+ _samples_metric->units_per_pixel = samples_per_pixel;
+ _minsec_metric->units_per_pixel = samples_per_pixel;
rightmost_frame = leftmost_frame + current_page_samples();
*/
if (ruler_timecode_action->get_active()) {
- gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_timecode_ruler), leftmost_frame, rightmost_frame,
- leftmost_frame, _session->current_end_frame());
+ timecode_ruler->set_range (leftmost_frame, rightmost_frame);
}
if (ruler_samples_action->get_active()) {
- gtk_custom_ruler_set_range (GTK_CUSTOM_RULER (_samples_ruler), leftmost_frame, rightmost_frame,
- leftmost_frame, _session->current_end_frame());
+ samples_ruler->set_range (leftmost_frame, rightmost_frame);
}
if (ruler_minsec_action->get_active()) {
- gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_minsec_ruler), leftmost_frame, rightmost_frame,
- leftmost_frame, _session->current_end_frame());
+ minsec_ruler->set_range (leftmost_frame, rightmost_frame);
}
}
compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
begin, end);
- ruler_metrics[ruler_metric_bbt].units_per_pixel = samples_per_pixel;
+ _bbt_metric->units_per_pixel = samples_per_pixel;
if (ruler_bbt_action->get_active()) {
- gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_bbt_ruler), leftmost_frame, leftmost_frame+current_page_samples(),
- leftmost_frame, _session->current_end_frame());
+ bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
}
}
-/* Mark generation */
-
-gint
-Editor::_metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
- return ruler_editor->metric_get_timecode (marks, lower, upper, maxchars);
-}
-
-gint
-Editor::_metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
- return ruler_editor->metric_get_bbt (marks, lower, upper, maxchars);
-}
-
-gint
-Editor::_metric_get_samples (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
- return ruler_editor->metric_get_samples (marks, lower, upper, maxchars);
-}
-
-gint
-Editor::_metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
- return ruler_editor->metric_get_minsec (marks, lower, upper, maxchars);
-}
-
void
Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
{
}
-gint
-Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
+void
+Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
{
framepos_t pos;
framecnt_t spacer;
Timecode::Time timecode;
gchar buf[16];
gint n;
+ ArdourCanvas::Ruler::Mark mark;
if (_session == 0) {
- return 0;
+ return;
}
if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
pos = (framecnt_t) floor (lower);
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * timecode_nmarks);
switch (timecode_ruler_scale) {
case timecode_show_bits:
_session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
if ((timecode.subframes % timecode_mark_modulo) == 0) {
if (timecode.subframes == 0) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
}
} else {
snprintf (buf, sizeof(buf)," ");
- (*marks)[n].style = GtkCustomRulerMarkMicro;
-
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
+
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
+ mark.label = buf;
+ mark.position = pos;
+
+ marks.push_back (mark);
// Increment subframes by one
Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
_session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
if ((timecode.seconds % timecode_mark_modulo) == 0) {
if (timecode.seconds == 0) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
- (*marks)[n].position = pos;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
+ mark.position = pos;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
- (*marks)[n].position = pos;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
+ mark.position = pos;
}
snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
} else {
snprintf (buf, sizeof(buf)," ");
- (*marks)[n].style = GtkCustomRulerMarkMicro;
- (*marks)[n].position = pos;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
+ mark.position = pos;
}
- (*marks)[n].label = g_strdup (buf);
+ mark.label = buf;
+ marks.push_back (mark);
+
Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
}
break;
_session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
if ((timecode.minutes % timecode_mark_modulo) == 0) {
if (timecode.minutes == 0) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
}
snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
} else {
snprintf (buf, sizeof(buf)," ");
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
-
+ mark.label = buf;
+ mark.position = pos;
+ marks.push_back (mark);
Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
}
for (n = 0; n < timecode_nmarks; n++) {
_session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
if ((timecode.hours % timecode_mark_modulo) == 0) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
} else {
snprintf (buf, sizeof(buf)," ");
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
-
+ mark.label = buf;
+ mark.position = pos;
+ marks.push_back (mark);
Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
}
break;
_session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
if ((timecode.frames % timecode_mark_modulo) == 0) {
if (timecode.frames == 0) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
}
- (*marks)[n].position = pos;
+ mark.position = pos;
snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
} else {
snprintf (buf, sizeof(buf)," ");
- (*marks)[n].style = GtkCustomRulerMarkMicro;
- (*marks)[n].position = pos;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
+ mark.position = pos;
}
- (*marks)[n].label = g_strdup (buf);
+ mark.label = buf;
+ marks.push_back (mark);
Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
}
break;
}
-
- return timecode_nmarks;
}
+
void
Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
ARDOUR::TempoMap::BBTPointList::const_iterator begin,
} else {
bbt_ruler_scale = bbt_show_ticks_detail;
}
-
+
if ((bbt_ruler_scale == bbt_show_ticks_detail) && (lower_beat.beats == upper_beat.beats) && (upper_beat.ticks - lower_beat.ticks <= Timecode::BBT_Time::ticks_per_beat / 4)) {
bbt_ruler_scale = bbt_show_ticks_super_detail;
}
}
-gint
-Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint /*maxchars*/)
+static void
+edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
+{
+ ArdourCanvas::Ruler::Mark copy = marks.back();
+ copy.label = newlabel;
+ marks.pop_back ();
+ marks.push_back (copy);
+}
+
+void
+Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
{
if (_session == 0) {
- return 0;
+ return;
}
TempoMap::BBTPointList::const_iterator i;
Timecode::BBT_Time next_beat;
framepos_t next_beat_pos;
uint32_t beats = 0;
-
uint32_t tick = 0;
uint32_t skip;
uint32_t t;
double accumulated_error;
bool i_am_accented = false;
bool helper_active = false;
+ ArdourCanvas::Ruler::Mark mark;
ARDOUR::TempoMap::BBTPointList::const_iterator begin;
ARDOUR::TempoMap::BBTPointList::const_iterator end;
compute_current_bbt_points (lower, upper, begin, end);
if (distance (begin, end) == 0) {
- return 0;
+ return;
}
switch (bbt_ruler_scale) {
beats = distance (begin, end);
bbt_nmarks = beats + 2;
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
+ mark.label = "";
+ mark.position = lower;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
+ marks.push_back (mark);
- (*marks)[0].label = g_strdup(" ");
- (*marks)[0].position = lower;
- (*marks)[0].style = GtkCustomRulerMarkMicro;
-
for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
if ((*i).frame < lower && (bbt_bar_helper_on)) {
snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
- (*marks)[0].label = g_strdup (buf);
+ edit_last_mark_label (marks, buf);
helper_active = true;
} else {
if ((*i).is_bar()) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
} else if (((*i).beat % 2 == 1)) {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
- snprintf (buf, sizeof(buf), " ");
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
+ buf[0] = '\0';
} else {
- (*marks)[n].style = GtkCustomRulerMarkMicro;
- snprintf (buf, sizeof(buf), " ");
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
+ buf[0] = '\0';
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = (*i).frame;
+ mark.label = buf;
+ mark.position = (*i).frame;
+ marks.push_back (mark);
n++;
}
}
bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
+
+ // could do marks.assign() here to preallocate
- (*marks)[0].label = g_strdup(" ");
- (*marks)[0].position = lower;
- (*marks)[0].style = GtkCustomRulerMarkMicro;
+ mark.label = "";
+ mark.position = lower;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
+ marks.push_back (mark);
for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
if ((*i).frame < lower && (bbt_bar_helper_on)) {
snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
- (*marks)[0].label = g_strdup (buf);
+ edit_last_mark_label (marks, buf);
helper_active = true;
} else {
if ((*i).is_bar()) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
}
if (((*i).frame < bbt_position_of_helper) && helper_active) {
- snprintf (buf, sizeof(buf), " ");
+ buf[0] = '\0';
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = (*i).frame;
+ mark.label = buf;
+ mark.position = (*i).frame;
+ marks.push_back (mark);
n++;
}
i_am_accented = true;
}
- snprintf (buf, sizeof(buf), " ");
- (*marks)[n].label = g_strdup (buf);
+ mark.label = "";
/* Error compensation for float to framepos_t*/
accumulated_error += frame_skip_error;
accumulated_error -= 1.0f;
}
- (*marks)[n].position = pos;
+ mark.position = pos;
if ((bbt_beat_subdivision > 4) && i_am_accented) {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
i_am_accented = false;
+ marks.push_back (mark);
n++;
}
}
bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
- (*marks)[0].label = g_strdup(" ");
- (*marks)[0].position = lower;
- (*marks)[0].style = GtkCustomRulerMarkMicro;
+ mark.label = "";
+ mark.position = lower;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
+ marks.push_back (mark);
for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
if ((*i).frame < lower && (bbt_bar_helper_on)) {
snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
- (*marks)[0].label = g_strdup (buf);
+ edit_last_mark_label (marks, buf);
helper_active = true;
} else {
if ((*i).is_bar()) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
}
if (((*i).frame < bbt_position_of_helper) && helper_active) {
- snprintf (buf, sizeof(buf), " ");
+ buf[0] = '\0';
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = (*i).frame;
+ mark.label = buf;
+ mark.position = (*i).frame;
+ marks.push_back (mark);
n++;
}
if (i_am_accented && (pos > bbt_position_of_helper)){
snprintf (buf, sizeof(buf), "%" PRIu32, tick);
} else {
- snprintf (buf, sizeof(buf), " ");
+ buf[0] = '\0';
}
- (*marks)[n].label = g_strdup (buf);
+ mark.label = buf;
/* Error compensation for float to framepos_t*/
accumulated_error += frame_skip_error;
accumulated_error -= 1.0f;
}
- (*marks)[n].position = pos;
+ mark.position = pos;
if ((bbt_beat_subdivision > 4) && i_am_accented) {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
i_am_accented = false;
n++;
bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
- (*marks)[0].label = g_strdup(" ");
- (*marks)[0].position = lower;
- (*marks)[0].style = GtkCustomRulerMarkMicro;
+ mark.label = "";
+ mark.position = lower;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
+ marks.push_back (mark);
for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
if ((*i).frame < lower && (bbt_bar_helper_on)) {
snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
- (*marks)[0].label = g_strdup (buf);
+ edit_last_mark_label (marks, buf);
helper_active = true;
} else {
if ((*i).is_bar()) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
}
if (((*i).frame < bbt_position_of_helper) && helper_active) {
- snprintf (buf, sizeof(buf), " ");
+ buf[0] = '\0';
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = (*i).frame;
+ mark.label = buf;
+ mark.position = (*i).frame;
+ marks.push_back (mark);
n++;
}
if (pos > bbt_position_of_helper) {
snprintf (buf, sizeof(buf), "%" PRIu32, tick);
} else {
- snprintf (buf, sizeof(buf), " ");
+ buf[0] = '\0';
}
- (*marks)[n].label = g_strdup (buf);
+ mark.label = buf;
/* Error compensation for float to framepos_t*/
accumulated_error += frame_skip_error;
accumulated_error -= 1.0f;
}
- (*marks)[n].position = pos;
+ mark.position = pos;
if ((bbt_beat_subdivision > 4) && i_am_accented) {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
i_am_accented = false;
+ marks.push_back (mark);
n++;
}
}
case bbt_over:
bbt_nmarks = 1;
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
- (*marks)[0].style = GtkCustomRulerMarkMajor;
- (*marks)[0].label = g_strdup (buf);
- (*marks)[0].position = lower;
- n = 1;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
+ mark.label = buf;
+ mark.position = lower;
+ marks.push_back (mark);
break;
case bbt_show_64:
bbt_nmarks = (gint) (bbt_bars / 64) + 1;
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
if ((*i).is_bar()) {
if ((*i).bar % 64 == 1) {
if ((*i).bar % 256 == 1) {
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
- snprintf (buf, sizeof(buf), " ");
+ buf[0] = '\0';
if ((*i).bar % 256 == 129) {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = (*i).frame;
- n++;
+ mark.label = buf;
+ mark.position = (*i).frame;
+ marks.push_back (mark);
+ ++n;
}
}
}
case bbt_show_16:
bbt_nmarks = (bbt_bars / 16) + 1;
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
if ((*i).is_bar()) {
if ((*i).bar % 16 == 1) {
if ((*i).bar % 64 == 1) {
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
- snprintf (buf, sizeof(buf), " ");
+ buf[0] = '\0';
if ((*i).bar % 64 == 33) {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = (*i).frame;
- n++;
+ mark.label = buf;
+ mark.position = (*i).frame;
+ marks.push_back (mark);
+ ++n;
}
}
}
case bbt_show_4:
bbt_nmarks = (bbt_bars / 4) + 1;
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
if ((*i).is_bar()) {
if ((*i).bar % 4 == 1) {
if ((*i).bar % 16 == 1) {
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
- snprintf (buf, sizeof(buf), " ");
+ buf[0] = '\0';
if ((*i).bar % 16 == 9) {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = (*i).frame;
- n++;
+ mark.label = buf;
+ mark.position = (*i).frame;
+ marks.push_back (mark);
+ ++n;
}
}
}
break;
case bbt_show_1:
- // default:
+// default:
bbt_nmarks = bbt_bars + 2;
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks );
- for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
+ for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
if ((*i).is_bar()) {
if ((*i).bar % 4 == 1) {
snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
- snprintf (buf, sizeof(buf), " ");
- if ((*i).bar % 4 == 3) {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
- } else {
- (*marks)[n].style = GtkCustomRulerMarkMicro;
- }
+ buf[0] = '\0';
+ if ((*i).bar % 4 == 3) {
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
+ } else {
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
+ }
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = (*i).frame;
- n++;
+ mark.label = buf;
+ mark.position = (*i).frame;
+ marks.push_back (mark);
+ ++n;
}
}
-
- break;
+ break;
}
-
- return n; //return the actual number of marks made, since we might have skipped some from fractional time signatures
-
}
void
_samples_ruler_interval = (upper - lower) / 5;
}
-gint
-Editor::metric_get_samples (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
+void
+Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
{
framepos_t pos;
framepos_t const ilower = (framepos_t) floor (lower);
gchar buf[16];
gint nmarks;
gint n;
+ ArdourCanvas::Ruler::Mark mark;
if (_session == 0) {
- return 0;
+ return;
}
nmarks = 5;
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks);
for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
snprintf (buf, sizeof(buf), "%" PRIi64, pos);
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.label = buf;
+ mark.position = pos;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
+ marks.push_back (mark);
}
-
- return nmarks;
}
static void
long millisecs;
left = sample;
- hrs = left / (sample_rate * 60 * 60);
- left -= hrs * sample_rate * 60 * 60;
- mins = left / (sample_rate * 60);
- left -= mins * sample_rate * 60;
- secs = left / sample_rate;
- left -= secs * sample_rate;
- millisecs = left * 1000 / sample_rate;
+ hrs = left / (sample_rate * 60 * 60 * 1000);
+ left -= hrs * sample_rate * 60 * 60 * 1000;
+ mins = left / (sample_rate * 60 * 1000);
+ left -= mins * sample_rate * 60 * 1000;
+ secs = left / (sample_rate * 1000);
+ left -= secs * sample_rate * 1000;
+ millisecs = left / sample_rate;
*millisecs_p = millisecs;
*secs_p = secs;
return;
}
- fr = _session->frame_rate();
+ fr = _session->frame_rate() * 1000;
/* to prevent 'flashing' */
if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
lower = 0;
}
upper += spacer;
- framecnt_t const range = upper - lower;
+ framecnt_t const range = (upper - lower) * 1000;
if (range < (fr / 50)) {
minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
minsec_nmarks = 2 + (range / minsec_mark_interval);
}
-gint
-Editor::metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
+void
+Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
{
framepos_t pos;
framepos_t spacer;
long hrs, mins, secs, millisecs;
gchar buf[16];
gint n;
+ ArdourCanvas::Ruler::Mark mark;
if (_session == 0) {
- return 0;
+ return;
}
/* to prevent 'flashing' */
lower = 0;
}
- *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * minsec_nmarks);
- pos = ((((framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
+ pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
switch (minsec_ruler_scale) {
case minsec_show_seconds:
for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
if (secs % minsec_mark_modulo == 0) {
if (secs == 0) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
}
snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
} else {
- snprintf (buf, sizeof(buf), " ");
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ buf[0] = '\0';
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
+ mark.label = buf;
+ mark.position = pos/1000.0;
+ marks.push_back (mark);
}
break;
case minsec_show_minutes:
sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
if (mins % minsec_mark_modulo == 0) {
if (mins == 0) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
}
snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
} else {
- snprintf (buf, sizeof(buf), " ");
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ buf[0] = '\0';
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
+ mark.label = buf;
+ mark.position = pos/1000.0;
+ marks.push_back (mark);
}
break;
case minsec_show_hours:
for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
if (hrs % minsec_mark_modulo == 0) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
} else {
- snprintf (buf, sizeof(buf), " ");
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ buf[0] = '\0';
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
+ mark.label = buf;
+ mark.position = pos/1000.0;
+ marks.push_back (mark);
}
break;
case minsec_show_frames:
for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
if (millisecs % minsec_mark_modulo == 0) {
- if (secs == 0) {
- (*marks)[n].style = GtkCustomRulerMarkMajor;
+ if (millisecs == 0) {
+ mark.style = ArdourCanvas::Ruler::Mark::Major;
} else {
- (*marks)[n].style = GtkCustomRulerMarkMinor;
+ mark.style = ArdourCanvas::Ruler::Mark::Minor;
}
snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
} else {
- snprintf (buf, sizeof(buf), " ");
- (*marks)[n].style = GtkCustomRulerMarkMicro;
+ buf[0] = '\0';
+ mark.style = ArdourCanvas::Ruler::Mark::Micro;
}
- (*marks)[n].label = g_strdup (buf);
- (*marks)[n].position = pos;
+ mark.label = buf;
+ mark.position = pos/1000.0;
+ marks.push_back (mark);
}
break;
}
-
- return minsec_nmarks;
}
return;
}
+ /* XXX this is superficially inefficient. Hide the selection in all
+ * tracks, then show it in all selected tracks.
+ *
+ * However, if you investigate what this actually does, it isn't
+ * anywhere nearly as bad as it may appear. Remember: nothing is
+ * redrawn or even recomputed during these two loops - that only
+ * happens when we next render ...
+ */
+
for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
(*i)->hide_selection ();
}
using namespace PBD;
using namespace Gtk;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
EditorSnapshots::EditorSnapshots (Editor* e)
: EditorComponent (e)
_view_rectangle_x (0, 0),
_view_rectangle_y (0, 0),
_zoom_dragging (false),
- _old_follow_playhead (false)
+ _old_follow_playhead (false),
+ _background_dirty (true)
{
- Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
- Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
- _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
-
add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
set_flags (get_flags() | Gtk::CAN_FOCUS);
}
+/** Handle a size allocation.
+ * @param alloc GTK allocation.
+ */
+void
+EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
+{
+ Gtk::EventBox::on_size_allocate (alloc);
+ _background_dirty = true;
+ set_dirty ();
+}
+
+
/** Connect to a session.
* @param s Session.
*/
*/
if (_session) {
- _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
- _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
+ Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+ Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+ _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
+ _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+ _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+ _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
}
}
-/** Render the required regions to a cairo context.
- * @param cr Context.
- */
void
-EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
+EditorSummary::render_background_image ()
{
- /* background (really just the dividing lines between tracks */
+ int stride;
+ unsigned char *data;
+ stride = cairo_format_stride_for_width (CAIRO_FORMAT_RGB24, get_width ());
+ data = (unsigned char*) malloc (stride * get_height ());
+ _image = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_RGB24, get_width (), get_height (), stride);
+
+ cairo_t* cr = cairo_create (_image);
+
+ /* background (really just the dividing lines between tracks */
cairo_set_source_rgb (cr, 0, 0, 0);
cairo_rectangle (cr, 0, 0, get_width(), get_height());
cairo_fill (cr);
- if (_session == 0) {
- return;
- }
-
/* compute start and end points for the summary */
framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
const double p = (_session->current_start_frame() - _start) * _x_scale;
cairo_move_to (cr, p, 0);
cairo_line_to (cr, p, get_height());
- cairo_stroke (cr);
double const q = (_session->current_end_frame() - _start) * _x_scale;
cairo_move_to (cr, q, 0);
cairo_line_to (cr, q, get_height());
cairo_stroke (cr);
+ cairo_destroy (cr);
+}
+
+/** Render the required regions to a cairo context.
+ * @param cr Context.
+ */
+void
+EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
+{
+
+ if (_session == 0) {
+ return;
+ }
+
+ if (!_image || _background_dirty) {
+ render_background_image ();
+ _background_dirty = false;
+ }
+
+ cairo_push_group (cr);
+
+ /* Fill with the background image */
+
+ cairo_rectangle (cr, 0, 0, get_width(), get_height());
+ cairo_set_source_surface (cr, _image, 0, 0);
+ cairo_fill (cr);
+
/* Render the view rectangle. If there is an editor visual pending, don't update
the view rectangle now --- wait until the expose event that we'll get after
the visual change. This prevents a flicker.
get_editor (&_view_rectangle_x, &_view_rectangle_y);
}
- cairo_move_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
- cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.first);
- cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.second);
- cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.second);
- cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
+ int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
+ int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
+ cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
cairo_fill_preserve (cr);
cairo_set_line_width (cr, 1);
cairo_move_to (cr, ph, 0);
cairo_line_to (cr, ph, get_height());
cairo_stroke (cr);
+ cairo_pop_group_to_source (cr);
+ cairo_paint (cr);
_last_playhead = ph;
}
cairo_stroke (cr);
}
+void
+EditorSummary::set_background_dirty ()
+{
+ _background_dirty = true;
+ set_dirty ();
+}
+
/** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
void
EditorSummary::set_overlays_dirty ()
(*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
if (tr) {
- tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context ());
+ tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
}
}
+ _background_dirty = true;
set_dirty ();
}
EditorSummary::route_gui_changed (string c)
{
if (c == "color") {
+ _background_dirty = true;
set_dirty ();
}
}
void set_session (ARDOUR::Session *);
void set_overlays_dirty ();
+ void set_background_dirty ();
void routes_added (std::list<RouteTimeAxisView*> const &);
private:
+ void on_size_allocate (Gtk::Allocation& alloc);
enum Position {
LEFT,
Position _zoom_position;
bool _old_follow_playhead;
+ cairo_surface_t* _image;
+ void render_background_image ();
+ bool _background_dirty;
PBD::ScopedConnectionList position_connection;
PBD::ScopedConnection route_ctrl_id_connection;
#include "canvas/canvas.h"
#include "canvas/item.h"
+#include "canvas/line_set.h"
#include "editor.h"
#include "marker.h"
#include "time_axis_view.h"
#include "ardour_ui.h"
#include "tempo_lines.h"
-#include "utils.h"
#include "i18n.h"
}
if (tempo_lines == 0) {
- tempo_lines = new TempoLines (*_track_canvas, time_line_group, ArdourCanvas::COORD_MAX);
+ tempo_lines = new TempoLines (time_line_group, ArdourCanvas::LineSet::Vertical);
}
tempo_lines->draw (begin, end);
*/
#ifdef PLATFORM_WINDOWS
- Sleep(2000);
+ Glib::usleep(2 * G_USEC_PER_SEC);
#else
struct timespec t = { 2, 0 };
nanosleep (&t, 0);
#include "ardour/audio_backend.h"
#include "ardour/audioengine.h"
#include "ardour/mtdm.h"
+#include "ardour/mididm.h"
#include "ardour/rc_configuration.h"
#include "ardour/types.h"
using namespace Gtkmm2ext;
using namespace PBD;
using namespace Glib;
+using namespace ARDOUR_UI_UTILS;
-static const unsigned int midi_tab = -1; /* not currently in use */
+static const unsigned int midi_tab = 2;
static const unsigned int latency_tab = 1; /* zero-based, page zero is the main setup page */
-static const char* results_markup = X_("<span foreground=\"red\" style=\"italic\" size=\"larger\">%1</span>");
+static const char* results_markup = X_("<span weight=\"bold\" size=\"larger\">%1</span>");
EngineControl::EngineControl ()
: ArdourDialog (_("Audio/MIDI Setup"))
, ports_adjustment (128, 8, 1024, 1, 16)
, ports_spinner (ports_adjustment)
, control_app_button (_("Device Control Panel"))
+ , midi_devices_button (_("Midi Device Setup"))
, lm_measure_label (_("Measure"))
, lm_use_button (_("Use results"))
, lm_back_button (_("Back to settings ... (ignore results)"))
- , lm_button (_("Calibrate..."))
+ , lm_button_audio (_("Calibrate Audio"))
, lm_table (12, 3)
, have_lm_results (false)
, lm_running (false)
- , midi_refresh_button (_("Refresh list"))
+ , midi_back_button (_("Back to settings"))
, ignore_changes (0)
, _desired_sample_rate (0)
, started_at_least_once (false)
set_name (X_("AudioMIDISetup"));
- /* the backend combo is the one thing that is ALWAYS visible
- */
+ /* the backend combo is the one thing that is ALWAYS visible */
vector<const ARDOUR::AudioBackendInfo*> backends = ARDOUR::AudioEngine::instance()->available_backends();
basic_hbox.pack_start (basic_packer, false, false);
- /* latency tab */
-
/* latency measurement tab */
-
+
lm_title.set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("Latency Measurement Tool")));
-
+
row = 0;
lm_table.set_row_spacings (12);
lm_table.set_col_spacings (6);
lm_table.set_homogeneous (false);
-
+
lm_table.attach (lm_title, 0, 3, row, row+1, xopt, (AttachOptions) 0);
row++;
- Gtk::Label* preamble;
-
- preamble = manage (new Label);
- preamble->set_width_chars (60);
- preamble->set_line_wrap (true);
- preamble->set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
+ lm_preamble.set_width_chars (60);
+ lm_preamble.set_line_wrap (true);
+ lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
- lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
+ lm_table.attach (lm_preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
row++;
+ Gtk::Label* preamble;
preamble = manage (new Label);
preamble->set_width_chars (60);
preamble->set_line_wrap (true);
lm_measure_button.add (lm_measure_label);
lm_measure_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::latency_button_clicked));
lm_use_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::use_latency_button_clicked));
- lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
-
+ lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
+
lm_use_button.set_sensitive (false);
/* Increase the default spacing around the labels of these three
preamble->set_markup (_("When satisfied with the results, click the \"Use results\" button."));
lm_table.attach (*preamble, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
- ++row; // skip a row in the table
- ++row; // skip a row in the table
+ ++row; // skip a row in the table
+ ++row; // skip a row in the table
lm_table.attach (lm_results, 0, 3, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
- ++row; // skip a row in the table
- ++row; // skip a row in the table
+ ++row; // skip a row in the table
+ ++row; // skip a row in the table
lm_table.attach (lm_measure_button, 0, 1, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
lm_table.attach (lm_use_button, 1, 2, row, row+1, AttachOptions(FILL|EXPAND), (AttachOptions) 0);
lm_vbox.set_border_width (12);
lm_vbox.pack_start (lm_table, false, false);
+ midi_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
+
/* pack it all up */
notebook.pages().push_back (TabElem (basic_vbox, _("Audio")));
- // notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
notebook.pages().push_back (TabElem (lm_vbox, _("Latency")));
+ notebook.pages().push_back (TabElem (midi_vbox, _("MIDI")));
notebook.set_border_width (12);
notebook.set_show_tabs (false);
input_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &input_channels));
output_channels.signal_output().connect (sigc::bind (sigc::ptr_fun (&EngineControl::print_channel_count), &output_channels));
+ midi_devices_button.signal_clicked.connect (mem_fun (*this, &EngineControl::configure_midi_devices));
+ midi_devices_button.set_sensitive (false);
+ midi_devices_button.set_name ("generic button");
+ midi_devices_button.set_can_focus(true);
+
control_app_button.signal_clicked().connect (mem_fun (*this, &EngineControl::control_app_button_clicked));
manage_control_app_sensitivity ();
ARDOUR::AudioEngine::instance()->Stopped.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
ARDOUR::AudioEngine::instance()->Halted.connect (stopped_connection, MISSING_INVALIDATOR, boost::bind (&EngineControl::engine_stopped, this), gui_context());
- backend_changed ();
-
- if (audio_setup) {
+ if (audio_setup)
+ {
set_state (*audio_setup);
}
+ {
+ /* ignore: don't save state */
+ PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+ backend_changed ();
+ }
/* Connect to signals */
output_channels.signal_changed().connect (sigc::mem_fun (*this, &EngineControl::parameter_changed));
notebook.signal_switch_page().connect (sigc::mem_fun (*this, &EngineControl::on_switch_page));
- }
-
- void
- EngineControl::on_response (int response_id)
- {
- ArdourDialog::on_response (response_id);
-
- switch (response_id) {
- case RESPONSE_APPLY:
- push_state_to_backend (true);
- break;
- case RESPONSE_OK:
- push_state_to_backend (true);
- hide ();
- break;
- case RESPONSE_DELETE_EVENT: {
- GdkEventButton ev;
- ev.type = GDK_BUTTON_PRESS;
- ev.button = 1;
- on_delete_event ((GdkEventAny*) &ev);
- break;
- }
- default:
- hide ();
- }
- }
-
- void
- EngineControl::build_notebook ()
- {
- Label* label;
- AttachOptions xopt = AttachOptions (FILL|EXPAND);
-
- /* clear the table */
-
- Gtkmm2ext::container_clear (basic_vbox);
- Gtkmm2ext::container_clear (basic_packer);
-
- if (control_app_button.get_parent()) {
- control_app_button.get_parent()->remove (control_app_button);
- }
-
- label = manage (left_aligned_label (_("Audio System:")));
- basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
- basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
-
- lm_button.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_latency));
- lm_button.set_name ("generic button");
- if (_have_control) {
- build_full_control_notebook ();
- } else {
- build_no_control_notebook ();
- }
-
- basic_vbox.pack_start (basic_hbox, false, false);
-
- if (_have_control) {
- Gtk::HBox* hpacker = manage (new HBox);
- hpacker->set_border_width (12);
- hpacker->pack_start (control_app_button, false, false);
- hpacker->show ();
- control_app_button.show();
- basic_vbox.pack_start (*hpacker);
- }
-
- basic_vbox.show_all ();
- }
-
- void
- EngineControl::build_full_control_notebook ()
- {
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
- assert (backend);
-
- using namespace Notebook_Helpers;
- Label* label;
- vector<string> strings;
- AttachOptions xopt = AttachOptions (FILL|EXPAND);
- int row = 1; // row zero == backend combo
-
- /* start packing it up */
-
- if (backend->requires_driver_selection()) {
- label = manage (left_aligned_label (_("Driver:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
- basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
- row++;
- }
-
- label = manage (left_aligned_label (_("Device:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
- basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
- row++;
-
- label = manage (left_aligned_label (_("Sample rate:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
- basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
- row++;
-
-
- label = manage (left_aligned_label (_("Buffer size:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
- basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
- buffer_size_duration_label.set_alignment (0.0); /* left-align */
- basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
- row++;
-
- input_channels.set_name ("InputChannels");
- input_channels.set_flags(Gtk::CAN_FOCUS);
- input_channels.set_digits(0);
- input_channels.set_wrap(false);
- output_channels.set_editable (true);
-
- label = manage (left_aligned_label (_("Input Channels:")));
- basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
- basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
- ++row;
-
- output_channels.set_name ("OutputChannels");
- output_channels.set_flags(Gtk::CAN_FOCUS);
- output_channels.set_digits(0);
- output_channels.set_wrap(false);
- output_channels.set_editable (true);
-
- label = manage (left_aligned_label (_("Output Channels:")));
- basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
- basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
- ++row;
-
- input_latency.set_name ("InputLatency");
- input_latency.set_flags(Gtk::CAN_FOCUS);
- input_latency.set_digits(0);
- input_latency.set_wrap(false);
- input_latency.set_editable (true);
-
- label = manage (left_aligned_label (_("Hardware input latency:")));
- basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
- basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
- label = manage (left_aligned_label (_("samples")));
- basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
- ++row;
-
- output_latency.set_name ("OutputLatency");
- output_latency.set_flags(Gtk::CAN_FOCUS);
- output_latency.set_digits(0);
- output_latency.set_wrap(false);
- output_latency.set_editable (true);
-
- label = manage (left_aligned_label (_("Hardware output latency:")));
- basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
- basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
- label = manage (left_aligned_label (_("samples")));
- basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
-
- /* button spans 2 rows */
-
- basic_packer.attach (lm_button, 3, 4, row-1, row+1, xopt, xopt);
- ++row;
-
- label = manage (left_aligned_label (_("MIDI System")));
- basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
- basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
- row++;
- }
-
- void
- EngineControl::build_no_control_notebook ()
- {
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
- assert (backend);
-
- using namespace Notebook_Helpers;
- Label* label;
- vector<string> strings;
- AttachOptions xopt = AttachOptions (FILL|EXPAND);
- int row = 1; // row zero == backend combo
- const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
-
- label = manage (new Label);
- label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
- basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
- row++;
-
- if (backend->can_change_sample_rate_when_running()) {
- label = manage (left_aligned_label (_("Sample rate:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
- basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
- row++;
- }
-
- if (backend->can_change_buffer_size_when_running()) {
- label = manage (left_aligned_label (_("Buffer size:")));
- basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
- basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
- buffer_size_duration_label.set_alignment (0.0); /* left-align */
- basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
- row++;
- }
-
- connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
-
- basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
- row++;
- }
-
- EngineControl::~EngineControl ()
- {
- ignore_changes = true;
- }
-
- void
- EngineControl::disable_latency_tab ()
- {
- vector<string> empty;
- set_popdown_strings (lm_output_channel_combo, empty);
- set_popdown_strings (lm_input_channel_combo, empty);
- lm_measure_button.set_sensitive (false);
- lm_use_button.set_sensitive (false);
- }
-
- void
- EngineControl::enable_latency_tab ()
- {
- vector<string> outputs;
- vector<string> inputs;
-
- ARDOUR::AudioEngine::instance()->get_physical_outputs (ARDOUR::DataType::AUDIO, outputs);
- ARDOUR::AudioEngine::instance()->get_physical_inputs (ARDOUR::DataType::AUDIO, inputs);
-
- if (inputs.empty() || outputs.empty()) {
- MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
- lm_measure_button.set_sensitive (false);
- notebook.set_current_page (0);
- msg.run ();
- return;
- }
-
- if (!outputs.empty()) {
- set_popdown_strings (lm_output_channel_combo, outputs);
- lm_output_channel_combo.set_active_text (outputs.front());
- lm_output_channel_combo.set_sensitive (true);
- } else {
- lm_output_channel_combo.set_sensitive (false);
- }
-
- if (!inputs.empty()) {
- set_popdown_strings (lm_input_channel_combo, inputs);
- lm_input_channel_combo.set_active_text (inputs.front());
- lm_input_channel_combo.set_sensitive (true);
- } else {
- lm_input_channel_combo.set_sensitive (false);
- }
-
- lm_measure_button.set_sensitive (true);
- }
-
- void
- EngineControl::setup_midi_tab_for_backend ()
- {
- string backend = backend_combo.get_active_text ();
-
- Gtkmm2ext::container_clear (midi_vbox);
-
- midi_vbox.set_border_width (12);
- midi_device_table.set_border_width (12);
-
- if (backend == "JACK") {
- setup_midi_tab_for_jack ();
- }
-
- midi_vbox.pack_start (midi_device_table, true, true);
- midi_vbox.pack_start (midi_refresh_button, false, false);
- midi_vbox.show_all ();
-
- midi_refresh_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::refresh_midi_display));
- }
-
- void
- EngineControl::setup_midi_tab_for_jack ()
- {
- }
-
- void
- EngineControl::refresh_midi_display ()
- {
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
- assert (backend);
-
- vector<string> midi_inputs;
- vector<string> midi_outputs;
- int row = 0;
- AttachOptions xopt = AttachOptions (FILL|EXPAND);
- Gtk::Label* l;
-
- Gtkmm2ext::container_clear (midi_device_table);
-
- backend->get_physical_inputs (ARDOUR::DataType::MIDI, midi_inputs);
- backend->get_physical_outputs (ARDOUR::DataType::MIDI, midi_outputs);
-
- midi_device_table.set_spacings (6);
- midi_device_table.set_homogeneous (true);
- midi_device_table.resize (midi_inputs.size() + midi_outputs.size() + 3, 1);
-
- l = manage (new Label);
- l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Inputs")));
- midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
- l->set_alignment (0, 0.5);
- row++;
- l->show ();
-
- for (vector<string>::iterator p = midi_inputs.begin(); p != midi_inputs.end(); ++p) {
- l = manage (new Label ((*p).substr ((*p).find_last_of (':') + 1)));
- l->set_alignment (0, 0.5);
- midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
- l->show ();
- row++;
- }
-
- row++; // extra row of spacing
-
- l = manage (new Label);
- l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Outputs")));
- midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
- l->set_alignment (0, 0.5);
- row++;
- l->show ();
-
- for (vector<string>::iterator p = midi_outputs.begin(); p != midi_outputs.end(); ++p) {
- l = manage (new Label ((*p).substr ((*p).find_last_of (':') + 1)));
- l->set_alignment (0, 0.5);
- midi_device_table.attach (*l, 0, 1, row, row + 1, xopt, AttachOptions (0));
- l->show ();
- row++;
- }
- }
-
- void
- EngineControl::update_sensitivity ()
- {
- }
-
- void
- EngineControl::backend_changed ()
- {
- if (ignore_changes) {
- return;
- }
-
- string backend_name = backend_combo.get_active_text();
- boost::shared_ptr<ARDOUR::AudioBackend> backend;
-
- if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
- /* eh? setting the backend failed... how ? */
- return;
- }
-
- _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
-
- build_notebook ();
- setup_midi_tab_for_backend ();
-
- if (backend->requires_driver_selection()) {
- vector<string> drivers = backend->enumerate_drivers();
-
- if (!drivers.empty()) {
- {
- PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
- set_popdown_strings (driver_combo, drivers);
- driver_combo.set_active_text (drivers.front());
- }
-
- driver_changed ();
- }
-
- } else {
- driver_combo.set_sensitive (false);
- /* this will change the device text which will cause a call to
- * device changed which will set up parameters
- */
- list_devices ();
- }
-
- vector<string> midi_options = backend->enumerate_midi_options();
-
- if (midi_options.size() == 1) {
- /* only contains the "none" option */
- midi_option_combo.set_sensitive (false);
- } else {
- if (_have_control) {
- set_popdown_strings (midi_option_combo, midi_options);
- midi_option_combo.set_active_text (midi_options.front());
- midi_option_combo.set_sensitive (true);
- } else {
- midi_option_combo.set_sensitive (false);
- }
- }
-
- maybe_display_saved_state ();
- }
-
- bool
- EngineControl::print_channel_count (Gtk::SpinButton* sb)
- {
- uint32_t cnt = (uint32_t) sb->get_value();
- if (cnt == 0) {
- sb->set_text (_("all available channels"));
- } else {
- char buf[32];
- snprintf (buf, sizeof (buf), "%d", cnt);
- sb->set_text (buf);
- }
- return true;
- }
-
- void
- EngineControl::list_devices ()
- {
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
- assert (backend);
-
- /* now fill out devices, mark sample rates, buffer sizes insensitive */
-
- vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
-
- /* NOTE: Ardour currently does not display the "available" field of the
- * returned devices.
- *
- * Doing so would require a different GUI widget than the combo
- * box/popdown that we currently use, since it has no way to list
- * items that are not selectable. Something more like a popup menu,
- * which could have unselectable items, would be appropriate.
- */
-
- vector<string> available_devices;
-
- for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
- available_devices.push_back (i->name);
- }
-
- if (!available_devices.empty()) {
-
- update_sensitivity ();
-
- {
- PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
- set_popdown_strings (device_combo, available_devices);
- device_combo.set_active_text (available_devices.front());
- }
-
- device_changed ();
-
- ok_button->set_sensitive (true);
- apply_button->set_sensitive (true);
-
- } else {
- sample_rate_combo.set_sensitive (false);
- buffer_size_combo.set_sensitive (false);
- input_latency.set_sensitive (false);
- output_latency.set_sensitive (false);
- input_channels.set_sensitive (false);
- output_channels.set_sensitive (false);
- ok_button->set_sensitive (false);
- apply_button->set_sensitive (false);
- }
- }
-
- void
- EngineControl::driver_changed ()
- {
- if (ignore_changes) {
- return;
- }
-
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
- assert (backend);
-
- backend->set_driver (driver_combo.get_active_text());
- list_devices ();
-
- maybe_display_saved_state ();
- }
-
- void
- EngineControl::device_changed ()
- {
- if (ignore_changes) {
- return;
- }
-
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
- assert (backend);
- string device_name = device_combo.get_active_text ();
- vector<string> s;
-
- {
- PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-
- /* don't allow programmatic change to combos to cause a
- recursive call to this method.
- */
-
- /* sample rates */
-
- string desired;
-
- vector<float> sr;
-
- if (_have_control) {
- sr = backend->available_sample_rates (device_name);
- } else {
-
- sr.push_back (8000.0f);
- sr.push_back (16000.0f);
- sr.push_back (32000.0f);
- sr.push_back (44100.0f);
- sr.push_back (48000.0f);
- sr.push_back (88200.0f);
- sr.push_back (96000.0f);
- sr.push_back (192000.0f);
- sr.push_back (384000.0f);
- }
-
- for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
- s.push_back (rate_as_string (*x));
- if (*x == _desired_sample_rate) {
- desired = s.back();
- }
- }
-
- if (!s.empty()) {
- sample_rate_combo.set_sensitive (true);
- set_popdown_strings (sample_rate_combo, s);
-
- if (desired.empty()) {
- sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
- } else {
- sample_rate_combo.set_active_text (desired);
- }
-
- } else {
- sample_rate_combo.set_sensitive (false);
- }
-
- /* buffer sizes */
-
- vector<uint32_t> bs;
-
- if (_have_control) {
- bs = backend->available_buffer_sizes(device_name);
- } else if (backend->can_change_buffer_size_when_running()) {
- bs.push_back (8);
- bs.push_back (16);
- bs.push_back (32);
- bs.push_back (64);
- bs.push_back (128);
- bs.push_back (256);
- bs.push_back (512);
- bs.push_back (1024);
- bs.push_back (2048);
- bs.push_back (4096);
- bs.push_back (8192);
- }
- s.clear ();
- for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
- s.push_back (bufsize_as_string (*x));
- }
-
- if (!s.empty()) {
- buffer_size_combo.set_sensitive (true);
- set_popdown_strings (buffer_size_combo, s);
-
- buffer_size_combo.set_active_text (bufsize_as_string (backend->default_buffer_size()));
- show_buffer_duration ();
- } else {
- buffer_size_combo.set_sensitive (false);
- }
-
- /* XXX theoretically need to set min + max channel counts here
- */
-
- manage_control_app_sensitivity ();
- }
-
- /* pick up any saved state for this device */
-
- maybe_display_saved_state ();
- }
-
- string
- EngineControl::bufsize_as_string (uint32_t sz)
- {
- /* Translators: "samples" is always plural here, so no
- need for plural+singular forms.
- */
- char buf[32];
- snprintf (buf, sizeof (buf), _("%u samples"), sz);
- return buf;
- }
-
- void
- EngineControl::sample_rate_changed ()
- {
- if (ignore_changes) {
- return;
- }
-
- /* reset the strings for buffer size to show the correct msec value
- (reflecting the new sample rate).
- */
+}
- show_buffer_duration ();
- save_state ();
+void
+EngineControl::on_response (int response_id)
+{
+ ArdourDialog::on_response (response_id);
+
+ switch (response_id) {
+ case RESPONSE_APPLY:
+ push_state_to_backend (true);
+ break;
+ case RESPONSE_OK:
+ push_state_to_backend (true);
+ hide ();
+ break;
+ case RESPONSE_DELETE_EVENT:
+ {
+ GdkEventButton ev;
+ ev.type = GDK_BUTTON_PRESS;
+ ev.button = 1;
+ on_delete_event ((GdkEventAny*) &ev);
+ break;
+ }
+ default:
+ hide ();
+ }
+}
- }
+void
+EngineControl::build_notebook ()
+{
+ Label* label;
+ AttachOptions xopt = AttachOptions (FILL|EXPAND);
- void
- EngineControl::buffer_size_changed ()
- {
- if (ignore_changes) {
- return;
- }
+ /* clear the table */
- show_buffer_duration ();
- save_state ();
- }
+ Gtkmm2ext::container_clear (basic_vbox);
+ Gtkmm2ext::container_clear (basic_packer);
- void
- EngineControl::show_buffer_duration ()
- {
+ if (control_app_button.get_parent()) {
+ control_app_button.get_parent()->remove (control_app_button);
+ }
- /* buffer sizes - convert from just samples to samples + msecs for
- * the displayed string
- */
+ label = manage (left_aligned_label (_("Audio System:")));
+ basic_packer.attach (*label, 0, 1, 0, 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (backend_combo, 1, 2, 0, 1, xopt, (AttachOptions) 0);
- string bs_text = buffer_size_combo.get_active_text ();
- uint32_t samples = atoi (bs_text); /* will ignore trailing text */
- uint32_t rate = get_rate();
+ lm_button_audio.signal_clicked.connect (sigc::mem_fun (*this, &EngineControl::calibrate_audio_latency));
+ lm_button_audio.set_name ("generic button");
+ lm_button_audio.set_can_focus(true);
- /* Translators: "msecs" is ALWAYS plural here, so we do not
- need singular form as well.
- */
- /* Developers: note the hard-coding of a double buffered model
- in the (2 * samples) computation of latency. we always start
- the audiobackend in this configuration.
- */
- char buf[32];
- snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
- buffer_size_duration_label.set_text (buf);
- }
-
- void
- EngineControl::midi_option_changed ()
- {
- if (!ignore_changes) {
- save_state ();
- }
- }
-
- void
- EngineControl::parameter_changed ()
- {
- if (!ignore_changes) {
- save_state ();
- }
- }
-
- EngineControl::State*
- EngineControl::get_matching_state (const string& backend,
- const string& driver,
- const string& device)
- {
- for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
- if ((*i).backend == backend &&
- (*i).driver == driver &&
- (*i).device == device) {
- return &(*i);
- }
- }
- return 0;
- }
-
- EngineControl::State*
- EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
- {
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-
- if (backend) {
- return get_matching_state (backend_combo.get_active_text(),
- (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
- device_combo.get_active_text());
- }
-
-
- return get_matching_state (backend_combo.get_active_text(),
- string(),
- device_combo.get_active_text());
- }
-
- EngineControl::State*
- EngineControl::save_state ()
- {
- if (!_have_control) {
- return 0;
- }
-
- bool existing = true;
- State* state = get_saved_state_for_currently_displayed_backend_and_device ();
-
- if (!state) {
- existing = false;
- state = new State;
- }
-
- store_state (*state);
-
- if (!existing) {
- states.push_back (*state);
- }
-
- return state;
- }
-
- void
- EngineControl::store_state (State& state)
- {
- state.backend = get_backend ();
- state.driver = get_driver ();
- state.device = get_device_name ();
- state.sample_rate = get_rate ();
- state.buffer_size = get_buffer_size ();
- state.input_latency = get_input_latency ();
- state.output_latency = get_output_latency ();
- state.input_channels = get_input_channels ();
- state.output_channels = get_output_channels ();
- state.midi_option = get_midi_option ();
- }
-
- void
- EngineControl::maybe_display_saved_state ()
- {
- if (!_have_control) {
- return;
- }
-
- State* state = get_saved_state_for_currently_displayed_backend_and_device ();
-
- if (state) {
- PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-
- if (!_desired_sample_rate) {
- sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
- }
- buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
- /* call this explicitly because we're ignoring changes to
- the controls at this point.
- */
- show_buffer_duration ();
- input_latency.set_value (state->input_latency);
- output_latency.set_value (state->output_latency);
-
- if (!state->midi_option.empty()) {
- midi_option_combo.set_active_text (state->midi_option);
- }
- }
- }
+ if (_have_control) {
+ build_full_control_notebook ();
+ } else {
+ build_no_control_notebook ();
+ }
- XMLNode&
- EngineControl::get_state ()
- {
- XMLNode* root = new XMLNode ("AudioMIDISetup");
- std::string path;
-
- if (!states.empty()) {
- XMLNode* state_nodes = new XMLNode ("EngineStates");
-
- for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
-
- XMLNode* node = new XMLNode ("State");
-
- node->add_property ("backend", (*i).backend);
- node->add_property ("driver", (*i).driver);
- node->add_property ("device", (*i).device);
- node->add_property ("sample-rate", (*i).sample_rate);
- node->add_property ("buffer-size", (*i).buffer_size);
- node->add_property ("input-latency", (*i).input_latency);
- node->add_property ("output-latency", (*i).output_latency);
- node->add_property ("input-channels", (*i).input_channels);
- node->add_property ("output-channels", (*i).output_channels);
- node->add_property ("active", (*i).active ? "yes" : "no");
- node->add_property ("midi-option", (*i).midi_option);
-
- state_nodes->add_child_nocopy (*node);
- }
-
- root->add_child_nocopy (*state_nodes);
- }
-
- return *root;
- }
-
- void
- EngineControl::set_state (const XMLNode& root)
- {
- XMLNodeList clist, cclist;
- XMLNodeConstIterator citer, cciter;
- XMLNode* child;
- XMLNode* grandchild;
- XMLProperty* prop = NULL;
-
- if (root.name() != "AudioMIDISetup") {
- return;
- }
-
- clist = root.children();
-
- states.clear ();
-
- for (citer = clist.begin(); citer != clist.end(); ++citer) {
-
- child = *citer;
-
- if (child->name() != "EngineStates") {
- continue;
- }
-
- cclist = child->children();
-
- for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
- State state;
-
- grandchild = *cciter;
-
- if (grandchild->name() != "State") {
- continue;
- }
-
- if ((prop = grandchild->property ("backend")) == 0) {
- continue;
- }
- state.backend = prop->value ();
-
- if ((prop = grandchild->property ("driver")) == 0) {
- continue;
- }
- state.driver = prop->value ();
-
- if ((prop = grandchild->property ("device")) == 0) {
- continue;
- }
- state.device = prop->value ();
-
- if ((prop = grandchild->property ("sample-rate")) == 0) {
- continue;
- }
- state.sample_rate = atof (prop->value ());
-
- if ((prop = grandchild->property ("buffer-size")) == 0) {
- continue;
- }
- state.buffer_size = atoi (prop->value ());
-
- if ((prop = grandchild->property ("input-latency")) == 0) {
- continue;
- }
- state.input_latency = atoi (prop->value ());
-
- if ((prop = grandchild->property ("output-latency")) == 0) {
- continue;
- }
- state.output_latency = atoi (prop->value ());
-
- if ((prop = grandchild->property ("input-channels")) == 0) {
- continue;
- }
- state.input_channels = atoi (prop->value ());
-
- if ((prop = grandchild->property ("output-channels")) == 0) {
- continue;
- }
- state.output_channels = atoi (prop->value ());
-
- if ((prop = grandchild->property ("active")) == 0) {
- continue;
- }
- state.active = string_is_affirmative (prop->value ());
-
- if ((prop = grandchild->property ("midi-option")) == 0) {
- continue;
- }
- state.midi_option = prop->value ();
-
- states.push_back (state);
- }
- }
-
- /* now see if there was an active state and switch the setup to it */
-
- for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
-
- if ((*i).active) {
- ignore_changes++;
- backend_combo.set_active_text ((*i).backend);
- driver_combo.set_active_text ((*i).driver);
- device_combo.set_active_text ((*i).device);
- sample_rate_combo.set_active_text (rate_as_string ((*i).sample_rate));
- buffer_size_combo.set_active_text (bufsize_as_string ((*i).buffer_size));
- input_latency.set_value ((*i).input_latency);
- output_latency.set_value ((*i).output_latency);
- midi_option_combo.set_active_text ((*i).midi_option);
- ignore_changes--;
- break;
- }
- }
- }
-
- int
- EngineControl::push_state_to_backend (bool start)
- {
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-
- if (!backend) {
- return 0;
- }
-
- /* figure out what is going to change */
-
- bool restart_required = false;
- bool was_running = ARDOUR::AudioEngine::instance()->running();
- bool change_driver = false;
- bool change_device = false;
- bool change_rate = false;
- bool change_bufsize = false;
- bool change_latency = false;
- bool change_channels = false;
- bool change_midi = false;
-
- uint32_t ochan = get_output_channels ();
- uint32_t ichan = get_input_channels ();
-
- if (_have_control) {
-
- if (started_at_least_once) {
-
- /* we can control the backend */
-
- if (backend->requires_driver_selection()) {
- if (get_driver() != backend->driver_name()) {
- change_driver = true;
- }
- }
-
- if (get_device_name() != backend->device_name()) {
- change_device = true;
- }
-
- if (get_rate() != backend->sample_rate()) {
- change_rate = true;
- }
-
- if (get_buffer_size() != backend->buffer_size()) {
- change_bufsize = true;
- }
-
- if (get_midi_option() != backend->midi_option()) {
- change_midi = true;
- }
-
- /* zero-requested channels means "all available" */
-
- if (ichan == 0) {
- ichan = backend->input_channels();
- }
-
- if (ochan == 0) {
- ochan = backend->output_channels();
- }
-
- if (ichan != backend->input_channels()) {
- change_channels = true;
- }
-
- if (ochan != backend->output_channels()) {
- change_channels = true;
- }
-
- if (get_input_latency() != backend->systemic_input_latency() ||
- get_output_latency() != backend->systemic_output_latency()) {
- change_latency = true;
- }
- } else {
- /* backend never started, so we have to force a group
- of settings.
- */
- change_device = true;
- if (backend->requires_driver_selection()) {
- change_driver = true;
- }
- change_rate = true;
- change_bufsize = true;
- change_channels = true;
- change_latency = true;
- change_midi = true;
- }
-
- } else {
-
- /* we have no control over the backend, meaning that we can
- * only possibly change sample rate and buffer size.
- */
-
-
- if (get_rate() != backend->sample_rate()) {
- change_bufsize = true;
- }
-
- if (get_buffer_size() != backend->buffer_size()) {
- change_bufsize = true;
- }
- }
-
- if (!_have_control) {
-
- /* We do not have control over the backend, so the best we can
- * do is try to change the sample rate and/or bufsize and get
- * out of here.
- */
-
- if (change_rate && !backend->can_change_sample_rate_when_running()) {
- return 1;
- }
-
- if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
- return 1;
- }
-
- if (change_rate) {
- backend->set_sample_rate (get_rate());
- }
-
- if (change_bufsize) {
- backend->set_buffer_size (get_buffer_size());
- }
-
- if (start) {
- if (ARDOUR::AudioEngine::instance()->start ()) {
- error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
- return -1;
- }
- }
-
- post_push ();
-
- return 0;
- }
-
- /* determine if we need to stop the backend before changing parameters */
-
- if (change_driver || change_device || change_channels || change_latency ||
- (change_rate && !backend->can_change_sample_rate_when_running()) ||
- change_midi ||
- (change_bufsize && !backend->can_change_buffer_size_when_running())) {
- restart_required = true;
- } else {
- restart_required = false;
- }
-
- if (was_running) {
-
- if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
- /* no changes in any parameters that absolutely require a
- * restart, so check those that might be changeable without a
- * restart
- */
-
- if (change_rate && !backend->can_change_sample_rate_when_running()) {
- /* can't do this while running ... */
- restart_required = true;
- }
-
- if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
- /* can't do this while running ... */
- restart_required = true;
- }
- }
- }
-
- if (was_running) {
- if (restart_required) {
- if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
- return -1;
- }
- }
- }
-
-
- if (change_driver && backend->set_driver (get_driver())) {
- error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
- return -1;
- }
- if (change_device && backend->set_device_name (get_device_name())) {
- error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
- return -1;
- }
- if (change_rate && backend->set_sample_rate (get_rate())) {
- error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
- return -1;
- }
- if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
- error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
- return -1;
- }
-
- if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
- if (backend->set_input_channels (get_input_channels())) {
- error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
- return -1;
- }
- if (backend->set_output_channels (get_output_channels())) {
- error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
- return -1;
- }
- }
- if (change_latency) {
- if (backend->set_systemic_input_latency (get_input_latency())) {
- error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
- return -1;
- }
- if (backend->set_systemic_output_latency (get_output_latency())) {
- error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
- return -1;
- }
- }
-
- if (change_midi) {
- backend->set_midi_option (get_midi_option());
- }
-
- if (start || (was_running && restart_required)) {
- if (ARDOUR_UI::instance()->reconnect_to_engine()) {
- return -1;
- }
- }
-
- post_push ();
-
- return 0;
- }
-
- void
- EngineControl::post_push ()
- {
- /* get a pointer to the current state object, creating one if
- * necessary
- */
-
- if (_have_control) {
- State* state = get_saved_state_for_currently_displayed_backend_and_device ();
-
- if (!state) {
- state = save_state ();
- assert (state);
- }
-
- /* all off */
-
- for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
- (*i).active = false;
- }
-
- /* mark this one active (to be used next time the dialog is
- * shown)
- */
-
- state->active = true;
-
- manage_control_app_sensitivity ();
- }
-
- /* schedule a redisplay of MIDI ports */
-
- Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
- }
-
-
- float
- EngineControl::get_rate () const
- {
- float r = atof (sample_rate_combo.get_active_text ());
- /* the string may have been translated with an abbreviation for
- * thousands, so use a crude heuristic to fix this.
- */
- if (r < 1000.0) {
- r *= 1000.0;
- }
- return r;
- }
-
-
- uint32_t
- EngineControl::get_buffer_size () const
- {
- string txt = buffer_size_combo.get_active_text ();
- uint32_t samples;
-
- if (sscanf (txt.c_str(), "%d", &samples) != 1) {
- throw exception ();
- }
-
- return samples;
- }
-
- string
- EngineControl::get_midi_option () const
- {
- return midi_option_combo.get_active_text();
- }
-
- uint32_t
- EngineControl::get_input_channels() const
- {
- return (uint32_t) input_channels_adjustment.get_value();
- }
-
- uint32_t
- EngineControl::get_output_channels() const
- {
- return (uint32_t) output_channels_adjustment.get_value();
- }
-
- uint32_t
- EngineControl::get_input_latency() const
- {
- return (uint32_t) input_latency_adjustment.get_value();
- }
-
- uint32_t
- EngineControl::get_output_latency() const
- {
- return (uint32_t) output_latency_adjustment.get_value();
- }
-
- string
- EngineControl::get_backend () const
- {
- return backend_combo.get_active_text ();
- }
-
- string
- EngineControl::get_driver () const
- {
- return driver_combo.get_active_text ();
- }
-
- string
- EngineControl::get_device_name () const
- {
- return device_combo.get_active_text ();
- }
-
- void
- EngineControl::control_app_button_clicked ()
- {
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-
- if (!backend) {
- return;
- }
-
- backend->launch_control_app ();
- }
-
- void
- EngineControl::manage_control_app_sensitivity ()
- {
- boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
-
- if (!backend) {
- return;
- }
-
- string appname = backend->control_app_name();
-
- if (appname.empty()) {
- control_app_button.set_sensitive (false);
- } else {
- control_app_button.set_sensitive (true);
- }
- }
-
- void
- EngineControl::set_desired_sample_rate (uint32_t sr)
- {
- _desired_sample_rate = sr;
- device_changed ();
- }
-
- void
- EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
- {
- if (page_num == 0) {
- cancel_button->set_sensitive (true);
- ok_button->set_sensitive (true);
- apply_button->set_sensitive (true);
- } else {
- cancel_button->set_sensitive (false);
- ok_button->set_sensitive (false);
- apply_button->set_sensitive (false);
- }
-
- if (page_num == midi_tab) {
- /* MIDI tab */
- refresh_midi_display ();
- }
-
- if (page_num == latency_tab) {
- /* latency tab */
-
- if (!ARDOUR::AudioEngine::instance()->running()) {
-
- PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
-
- /* save any existing latency values */
-
- uint32_t il = (uint32_t) input_latency.get_value ();
- uint32_t ol = (uint32_t) input_latency.get_value ();
-
- /* reset to zero so that our new test instance
- will be clean of any existing latency measures.
- */
+ basic_vbox.pack_start (basic_hbox, false, false);
- input_latency.set_value (0);
- output_latency.set_value (0);
+ if (_have_control) {
+ Gtk::HBox* hpacker = manage (new HBox);
+ hpacker->set_border_width (12);
+ hpacker->pack_start (control_app_button, false, false);
+ hpacker->show ();
+ control_app_button.show();
+ basic_vbox.pack_start (*hpacker);
+ }
- /* reset control */
+ basic_vbox.show_all ();
+}
- input_latency.set_value (il);
- output_latency.set_value (ol);
+void
+EngineControl::build_full_control_notebook ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
- }
+ using namespace Notebook_Helpers;
+ Label* label;
+ vector<string> strings;
+ AttachOptions xopt = AttachOptions (FILL|EXPAND);
+ int row = 1; // row zero == backend combo
- if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
- disable_latency_tab ();
- }
+ /* start packing it up */
- enable_latency_tab ();
+ if (backend->requires_driver_selection()) {
+ label = manage (left_aligned_label (_("Driver:")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (driver_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+ row++;
+ }
- } else {
- if (lm_running) {
- ARDOUR::AudioEngine::instance()->stop_latency_detection();
- }
- }
- }
+ label = manage (left_aligned_label (_("Device:")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (device_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+ row++;
- /* latency measurement */
+ label = manage (left_aligned_label (_("Sample rate:")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+ row++;
- bool
- EngineControl::check_latency_measurement ()
- {
- MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
- if (mtdm->resolve () < 0) {
- lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
- return true;
- }
+ label = manage (left_aligned_label (_("Buffer size:")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+ buffer_size_duration_label.set_alignment (0.0); /* left-align */
+ basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
+ row++;
- if (mtdm->err () > 0.3) {
- mtdm->invert ();
- mtdm->resolve ();
- }
+ input_channels.set_name ("InputChannels");
+ input_channels.set_flags (Gtk::CAN_FOCUS);
+ input_channels.set_digits (0);
+ input_channels.set_wrap (false);
+ output_channels.set_editable (true);
- char buf[128];
- ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
+ label = manage (left_aligned_label (_("Input Channels:")));
+ basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ basic_packer.attach (input_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+ ++row;
- if (sample_rate == 0) {
- lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
- ARDOUR::AudioEngine::instance()->stop_latency_detection ();
- return false;
- }
+ output_channels.set_name ("OutputChannels");
+ output_channels.set_flags (Gtk::CAN_FOCUS);
+ output_channels.set_digits (0);
+ output_channels.set_wrap (false);
+ output_channels.set_editable (true);
- uint32_t frames_total = mtdm->del();
- uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
+ label = manage (left_aligned_label (_("Output Channels:")));
+ basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ basic_packer.attach (output_channels, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+ ++row;
- snprintf (buf, sizeof (buf), "%u samples / %.3lf ms", extra, extra * 1000.0f/sample_rate);
+ input_latency.set_name ("InputLatency");
+ input_latency.set_flags (Gtk::CAN_FOCUS);
+ input_latency.set_digits (0);
+ input_latency.set_wrap (false);
+ input_latency.set_editable (true);
+
+ label = manage (left_aligned_label (_("Hardware input latency:")));
+ basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ basic_packer.attach (input_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+ label = manage (left_aligned_label (_("samples")));
+ basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
+ ++row;
- bool solid = true;
+ output_latency.set_name ("OutputLatency");
+ output_latency.set_flags (Gtk::CAN_FOCUS);
+ output_latency.set_digits (0);
+ output_latency.set_wrap (false);
+ output_latency.set_editable (true);
- if (mtdm->err () > 0.2) {
- strcat (buf, " ");
- strcat (buf, _("(signal detection error)"));
- solid = false;
- }
+ label = manage (left_aligned_label (_("Hardware output latency:")));
+ basic_packer.attach (*label, 0, 1, row, row+1, xopt, (AttachOptions) 0);
+ basic_packer.attach (output_latency, 1, 2, row, row+1, xopt, (AttachOptions) 0);
+ label = manage (left_aligned_label (_("samples")));
+ basic_packer.attach (*label, 2, 3, row, row+1, SHRINK, (AttachOptions) 0);
- if (mtdm->inv ()) {
- strcat (buf, " ");
- strcat (buf, _("(inverted - bad wiring)"));
- solid = false;
- }
+ /* button spans 2 rows */
- if (solid) {
- end_latency_detection ();
- lm_use_button.set_sensitive (true);
- have_lm_results = true;
- }
-
- lm_results.set_markup (string_compose (results_markup, string_compose (_("Detected roundtrip latency: %1"), buf)));
+ basic_packer.attach (lm_button_audio, 3, 4, row-1, row+1, xopt, xopt);
+ ++row;
- return true;
+ label = manage (left_aligned_label (_("MIDI System")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (midi_option_combo, 1, 2, row, row + 1, SHRINK, (AttachOptions) 0);
+ basic_packer.attach (midi_devices_button, 3, 4, row, row+1, xopt, xopt);
+ row++;
}
void
-EngineControl::start_latency_detection ()
+EngineControl::build_no_control_notebook ()
{
- ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
- ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
- if (ARDOUR::AudioEngine::instance()->start_latency_detection () == 0) {
- lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
- latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_latency_measurement), 100);
- lm_measure_label.set_text (_("Cancel"));
- have_lm_results = false;
- lm_use_button.set_sensitive (false);
- lm_input_channel_combo.set_sensitive (false);
- lm_output_channel_combo.set_sensitive (false);
- lm_running = true;
+ using namespace Notebook_Helpers;
+ Label* label;
+ vector<string> strings;
+ AttachOptions xopt = AttachOptions (FILL|EXPAND);
+ int row = 1; // row zero == backend combo
+ const string msg = string_compose (_("The %1 audio backend was configured and started externally.\nThis limits your control over it."), backend->name());
+
+ label = manage (new Label);
+ label->set_markup (string_compose ("<span weight=\"bold\" foreground=\"red\">%1</span>", msg));
+ basic_packer.attach (*label, 0, 2, row, row + 1, xopt, (AttachOptions) 0);
+ row++;
+
+ if (backend->can_change_sample_rate_when_running()) {
+ label = manage (left_aligned_label (_("Sample rate:")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (sample_rate_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+ row++;
}
+
+ if (backend->can_change_buffer_size_when_running()) {
+ label = manage (left_aligned_label (_("Buffer size:")));
+ basic_packer.attach (*label, 0, 1, row, row + 1, xopt, (AttachOptions) 0);
+ basic_packer.attach (buffer_size_combo, 1, 2, row, row + 1, xopt, (AttachOptions) 0);
+ buffer_size_duration_label.set_alignment (0.0); /* left-align */
+ basic_packer.attach (buffer_size_duration_label, 2, 3, row, row+1, xopt, (AttachOptions) 0);
+ row++;
+ }
+
+ connect_disconnect_button.signal_clicked().connect (sigc::mem_fun (*this, &EngineControl::connect_disconnect_click));
+
+ basic_packer.attach (connect_disconnect_button, 0, 2, row, row+1, FILL, AttachOptions (0));
+ row++;
+}
+
+EngineControl::~EngineControl ()
+{
+ ignore_changes = true;
}
void
-EngineControl::end_latency_detection ()
+EngineControl::disable_latency_tab ()
{
- latency_timeout.disconnect ();
- ARDOUR::AudioEngine::instance()->stop_latency_detection ();
- lm_measure_label.set_text (_("Measure"));
- if (!have_lm_results) {
- lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
+ vector<string> empty;
+ set_popdown_strings (lm_output_channel_combo, empty);
+ set_popdown_strings (lm_input_channel_combo, empty);
+ lm_measure_button.set_sensitive (false);
+ lm_use_button.set_sensitive (false);
+}
+
+void
+EngineControl::enable_latency_tab ()
+{
+ vector<string> outputs;
+ vector<string> inputs;
+
+ ARDOUR::DataType const type = _measure_midi ? ARDOUR::DataType::MIDI : ARDOUR::DataType::AUDIO;
+ ARDOUR::AudioEngine::instance()->get_physical_outputs (type, outputs);
+ ARDOUR::AudioEngine::instance()->get_physical_inputs (type, inputs);
+
+ if (!ARDOUR::AudioEngine::instance()->running()) {
+ MessageDialog msg (_("Failed to start or connect to audio-engine.\n\nLatency calibration requires a working audio interface."));
+ notebook.set_current_page (0);
+ msg.run ();
+ return;
+ }
+ else if (inputs.empty() || outputs.empty()) {
+ MessageDialog msg (_("Your selected audio configuration is playback- or capture-only.\n\nLatency calibration requires playback and capture"));
+ notebook.set_current_page (0);
+ msg.run ();
+ return;
+ }
+
+ lm_back_button_signal.disconnect();
+ if (_measure_midi) {
+ lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), midi_tab));
+ lm_preamble.set_markup (_(""));
} else {
- lm_use_button.set_sensitive (false);
+ lm_back_button_signal = lm_back_button.signal_clicked().connect (sigc::bind (sigc::mem_fun (notebook, &Gtk::Notebook::set_current_page), 0));
+ lm_preamble.set_markup (_("<span weight=\"bold\">Turn down the volume on your audio equipment to a very low level.</span>"));
}
- lm_input_channel_combo.set_sensitive (true);
+
+ set_popdown_strings (lm_output_channel_combo, outputs);
+ lm_output_channel_combo.set_active_text (outputs.front());
lm_output_channel_combo.set_sensitive (true);
- lm_running = false;
+
+ set_popdown_strings (lm_input_channel_combo, inputs);
+ lm_input_channel_combo.set_active_text (inputs.front());
+ lm_input_channel_combo.set_sensitive (true);
+
+ lm_measure_button.set_sensitive (true);
}
void
-EngineControl::latency_button_clicked ()
+EngineControl::setup_midi_tab_for_backend ()
{
- if (!lm_running) {
- start_latency_detection ();
+ string backend = backend_combo.get_active_text ();
+
+ Gtkmm2ext::container_clear (midi_vbox);
+
+ midi_vbox.set_border_width (12);
+ midi_device_table.set_border_width (12);
+
+ if (backend == "JACK") {
+ setup_midi_tab_for_jack ();
+ }
+
+ midi_vbox.pack_start (midi_device_table, true, true);
+ midi_vbox.pack_start (midi_back_button, false, false);
+ midi_vbox.show_all ();
+}
+
+void
+EngineControl::setup_midi_tab_for_jack ()
+{
+}
+
+void
+EngineControl::midi_latency_adjustment_changed (Gtk::Adjustment *a, MidiDeviceSettings device, bool for_input) {
+ if (for_input) {
+ device->input_latency = a->get_value();
} else {
- end_latency_detection ();
- }
+ device->output_latency = a->get_value();
+ }
}
void
-EngineControl::use_latency_button_clicked ()
+EngineControl::midi_device_enabled_toggled (ArdourButton *b, MidiDeviceSettings device) {
+ b->set_active (!b->get_active());
+ device->enabled = b->get_active();
+ refresh_midi_display(device->name);
+}
+
+void
+EngineControl::refresh_midi_display (std::string focus)
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
+
+ int row = 0;
+ AttachOptions xopt = AttachOptions (FILL|EXPAND);
+ Gtk::Label* l;
+
+ Gtkmm2ext::container_clear (midi_device_table);
+
+ midi_device_table.set_spacings (6);
+
+ l = manage (new Label);
+ l->set_markup (string_compose ("<span size=\"large\" weight=\"bold\">%1</span>", _("MIDI Devices")));
+ midi_device_table.attach (*l, 0, 4, row, row + 1, xopt, AttachOptions (0));
+ l->set_alignment (0.5, 0.5);
+ row++;
+ l->show ();
+
+ l = manage (new Label (_("Device"))); l->show (); l->set_alignment (0.5, 0.5);
+ midi_device_table.attach (*l, 0, 1, row, row + 2, xopt, AttachOptions (0));
+ l = manage (new Label (_("Hardware Latencies"))); l->show (); l->set_alignment (0.5, 0.5);
+ midi_device_table.attach (*l, 1, 3, row, row + 1, xopt, AttachOptions (0));
+ row++;
+ l = manage (new Label (_("Input"))); l->show (); l->set_alignment (0.5, 0.5);
+ midi_device_table.attach (*l, 1, 2, row, row + 1, xopt, AttachOptions (0));
+ l = manage (new Label (_("Output"))); l->show (); l->set_alignment (0.5, 0.5);
+ midi_device_table.attach (*l, 2, 3, row, row + 1, xopt, AttachOptions (0));
+ row++;
+
+ for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
+ ArdourButton *m;
+ Gtk::Button* b;
+ Gtk::Adjustment *a;
+ Gtk::SpinButton *s;
+ bool enabled = (*p)->enabled;
+
+ m = manage (new ArdourButton ((*p)->name, ArdourButton::led_default_elements));
+ m->set_name ("midi device");
+ m->set_can_focus (Gtk::CAN_FOCUS);
+ m->add_events (Gdk::BUTTON_RELEASE_MASK);
+ m->set_active (enabled);
+ m->signal_clicked.connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_device_enabled_toggled), m, *p));
+ midi_device_table.attach (*m, 0, 1, row, row + 1, xopt, AttachOptions (0)); m->show ();
+ if ((*p)->name == focus) {
+ m->grab_focus();
+ }
+
+ a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
+ s = manage (new Gtk::SpinButton (*a));
+ a->set_value ((*p)->input_latency);
+ s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, true));
+ s->set_sensitive (_can_set_midi_latencies && enabled);
+ midi_device_table.attach (*s, 1, 2, row, row + 1, xopt, AttachOptions (0)); s->show ();
+
+ a = manage (new Gtk::Adjustment (0, 0, 99999, 1));
+ s = manage (new Gtk::SpinButton (*a));
+ a->set_value ((*p)->output_latency);
+ s->signal_value_changed().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::midi_latency_adjustment_changed), a, *p, false));
+ s->set_sensitive (_can_set_midi_latencies && enabled);
+ midi_device_table.attach (*s, 2, 3, row, row + 1, xopt, AttachOptions (0)); s->show ();
+
+ b = manage (new Button (_("Calibrate")));
+ b->signal_clicked().connect (sigc::bind (sigc::mem_fun (*this, &EngineControl::calibrate_midi_latency), *p));
+ b->set_sensitive (_can_set_midi_latencies && enabled);
+ midi_device_table.attach (*b, 3, 4, row, row + 1, xopt, AttachOptions (0)); b->show ();
+
+ row++;
+ }
+}
+
+void
+EngineControl::update_sensitivity ()
+{
+}
+
+void
+EngineControl::backend_changed ()
{
- MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
+ string backend_name = backend_combo.get_active_text();
+ boost::shared_ptr<ARDOUR::AudioBackend> backend;
- if (!mtdm) {
+ if (!(backend = ARDOUR::AudioEngine::instance()->set_backend (backend_name, "ardour", ""))) {
+ /* eh? setting the backend failed... how ? */
return;
}
- uint32_t frames_total = mtdm->del();
- uint32_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
- uint32_t one_way = extra/2;
+ _have_control = ARDOUR::AudioEngine::instance()->setup_required ();
+
+ build_notebook ();
+ setup_midi_tab_for_backend ();
+ _midi_devices.clear();
+
+ if (backend->requires_driver_selection()) {
+ vector<string> drivers = backend->enumerate_drivers();
+ driver_combo.set_sensitive (true);
+
+ if (!drivers.empty()) {
+ {
+ PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+ set_popdown_strings (driver_combo, drivers);
+ driver_combo.set_active_text (drivers.front());
+ }
+
+ driver_changed ();
+ }
+
+ } else {
+ driver_combo.set_sensitive (false);
+ /* this will change the device text which will cause a call to
+ * device changed which will set up parameters
+ */
+ list_devices ();
+ }
+
+ vector<string> midi_options = backend->enumerate_midi_options();
+
+ if (midi_options.size() == 1) {
+ /* only contains the "none" option */
+ midi_option_combo.set_sensitive (false);
+ } else {
+ if (_have_control) {
+ set_popdown_strings (midi_option_combo, midi_options);
+ midi_option_combo.set_active_text (midi_options.front());
+ midi_option_combo.set_sensitive (true);
+ } else {
+ midi_option_combo.set_sensitive (false);
+ }
+ }
- input_latency_adjustment.set_value (one_way);
- output_latency_adjustment.set_value (one_way);
+ midi_option_changed();
- /* back to settings page */
+ started_at_least_once = false;
- notebook.set_current_page (0);
+ if (!ignore_changes) {
+ maybe_display_saved_state ();
+ }
}
bool
-EngineControl::on_delete_event (GdkEventAny* ev)
+EngineControl::print_channel_count (Gtk::SpinButton* sb)
{
- if (notebook.get_current_page() == 2) {
- /* currently on latency tab - be sure to clean up */
- end_latency_detection ();
+ uint32_t cnt = (uint32_t) sb->get_value();
+ if (cnt == 0) {
+ sb->set_text (_("all available channels"));
+ } else {
+ char buf[32];
+ snprintf (buf, sizeof (buf), "%d", cnt);
+ sb->set_text (buf);
}
- return ArdourDialog::on_delete_event (ev);
+ return true;
}
void
-EngineControl::engine_running ()
+EngineControl::list_devices ()
{
boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
assert (backend);
- buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
- sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
+ /* now fill out devices, mark sample rates, buffer sizes insensitive */
- buffer_size_combo.set_sensitive (true);
- sample_rate_combo.set_sensitive (true);
+ vector<ARDOUR::AudioBackend::DeviceStatus> all_devices = backend->enumerate_devices ();
- connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
+ /* NOTE: Ardour currently does not display the "available" field of the
+ * returned devices.
+ *
+ * Doing so would require a different GUI widget than the combo
+ * box/popdown that we currently use, since it has no way to list
+ * items that are not selectable. Something more like a popup menu,
+ * which could have unselectable items, would be appropriate.
+ */
- started_at_least_once = true;
+ vector<string> available_devices;
+
+ for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = all_devices.begin(); i != all_devices.end(); ++i) {
+ available_devices.push_back (i->name);
+ }
+
+ if (!available_devices.empty()) {
+
+ update_sensitivity ();
+
+ {
+ PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+ set_popdown_strings (device_combo, available_devices);
+ device_combo.set_active_text (available_devices.front());
+ }
+
+ device_changed ();
+
+ ok_button->set_sensitive (true);
+ apply_button->set_sensitive (true);
+
+ } else {
+ device_combo.clear();
+ sample_rate_combo.set_sensitive (false);
+ buffer_size_combo.set_sensitive (false);
+ input_latency.set_sensitive (false);
+ output_latency.set_sensitive (false);
+ input_channels.set_sensitive (false);
+ output_channels.set_sensitive (false);
+ if (_have_control) {
+ ok_button->set_sensitive (false);
+ apply_button->set_sensitive (false);
+ } else {
+ ok_button->set_sensitive (true);
+ apply_button->set_sensitive (true);
+ }
+ }
}
void
-EngineControl::engine_stopped ()
+EngineControl::driver_changed ()
{
boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
assert (backend);
- buffer_size_combo.set_sensitive (false);
- connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
+ backend->set_driver (driver_combo.get_active_text());
+ list_devices ();
- sample_rate_combo.set_sensitive (true);
- buffer_size_combo.set_sensitive (true);
+ if (!ignore_changes) {
+ maybe_display_saved_state ();
+ }
}
-
+
void
-EngineControl::connect_disconnect_click()
+EngineControl::device_changed ()
{
- if (ARDOUR::AudioEngine::instance()->running()) {
- ARDOUR_UI::instance()->disconnect_from_engine ();
+
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
+ string device_name = device_combo.get_active_text ();
+ vector<string> s;
+
+ {
+ PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+
+ /* don't allow programmatic change to combos to cause a
+ recursive call to this method.
+ */
+
+ /* sample rates */
+
+ string desired;
+
+ vector<float> sr;
+
+ if (_have_control) {
+ sr = backend->available_sample_rates (device_name);
+ } else {
+
+ sr.push_back (8000.0f);
+ sr.push_back (16000.0f);
+ sr.push_back (32000.0f);
+ sr.push_back (44100.0f);
+ sr.push_back (48000.0f);
+ sr.push_back (88200.0f);
+ sr.push_back (96000.0f);
+ sr.push_back (192000.0f);
+ sr.push_back (384000.0f);
+ }
+
+ for (vector<float>::const_iterator x = sr.begin(); x != sr.end(); ++x) {
+ s.push_back (rate_as_string (*x));
+ if (*x == _desired_sample_rate) {
+ desired = s.back();
+ }
+ }
+
+ if (!s.empty()) {
+ sample_rate_combo.set_sensitive (true);
+ set_popdown_strings (sample_rate_combo, s);
+
+ if (desired.empty()) {
+ sample_rate_combo.set_active_text (rate_as_string (backend->default_sample_rate()));
+ } else {
+ sample_rate_combo.set_active_text (desired);
+ }
+
+ } else {
+ sample_rate_combo.set_sensitive (false);
+ }
+
+ /* buffer sizes */
+
+ vector<uint32_t> bs;
+
+ if (_have_control) {
+ bs = backend->available_buffer_sizes (device_name);
+ } else if (backend->can_change_buffer_size_when_running()) {
+ bs.push_back (8);
+ bs.push_back (16);
+ bs.push_back (32);
+ bs.push_back (64);
+ bs.push_back (128);
+ bs.push_back (256);
+ bs.push_back (512);
+ bs.push_back (1024);
+ bs.push_back (2048);
+ bs.push_back (4096);
+ bs.push_back (8192);
+ }
+ s.clear ();
+ for (vector<uint32_t>::const_iterator x = bs.begin(); x != bs.end(); ++x) {
+ s.push_back (bufsize_as_string (*x));
+ }
+
+ if (!s.empty()) {
+ buffer_size_combo.set_sensitive (true);
+ set_popdown_strings (buffer_size_combo, s);
+
+ buffer_size_combo.set_active_text (bufsize_as_string (backend->default_buffer_size()));
+ show_buffer_duration ();
+ } else {
+ buffer_size_combo.set_sensitive (false);
+ }
+
+ /* XXX theoretically need to set min + max channel counts here
+ */
+
+ manage_control_app_sensitivity ();
+ }
+
+ /* pick up any saved state for this device */
+
+ if (!ignore_changes) {
+ maybe_display_saved_state ();
+ }
+}
+
+string
+EngineControl::bufsize_as_string (uint32_t sz)
+{
+ /* Translators: "samples" is always plural here, so no
+ need for plural+singular forms.
+ */
+ char buf[32];
+ snprintf (buf, sizeof (buf), _("%u samples"), sz);
+ return buf;
+}
+
+void
+EngineControl::sample_rate_changed ()
+{
+ /* reset the strings for buffer size to show the correct msec value
+ (reflecting the new sample rate).
+ */
+
+ show_buffer_duration ();
+ if (!ignore_changes) {
+ save_state ();
+ }
+
+}
+
+void
+EngineControl::buffer_size_changed ()
+{
+ show_buffer_duration ();
+ if (!ignore_changes) {
+ save_state ();
+ }
+}
+
+void
+EngineControl::show_buffer_duration ()
+{
+
+ /* buffer sizes - convert from just samples to samples + msecs for
+ * the displayed string
+ */
+
+ string bs_text = buffer_size_combo.get_active_text ();
+ uint32_t samples = atoi (bs_text); /* will ignore trailing text */
+ uint32_t rate = get_rate();
+
+ /* Translators: "msecs" is ALWAYS plural here, so we do not
+ need singular form as well.
+ */
+ /* Developers: note the hard-coding of a double buffered model
+ in the (2 * samples) computation of latency. we always start
+ the audiobackend in this configuration.
+ */
+ char buf[32];
+ snprintf (buf, sizeof (buf), _("(%.1f msecs)"), (2 * samples) / (rate/1000.0));
+ buffer_size_duration_label.set_text (buf);
+}
+
+void
+EngineControl::midi_option_changed ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
+
+ backend->set_midi_option (get_midi_option());
+
+ vector<ARDOUR::AudioBackend::DeviceStatus> midi_devices = backend->enumerate_midi_devices();
+
+ //_midi_devices.clear(); // TODO merge with state-saved settings..
+ _can_set_midi_latencies = backend->can_set_systemic_midi_latencies();
+ std::vector<MidiDeviceSettings> new_devices;
+
+ for (vector<ARDOUR::AudioBackend::DeviceStatus>::const_iterator i = midi_devices.begin(); i != midi_devices.end(); ++i) {
+ MidiDeviceSettings mds = find_midi_device (i->name);
+ if (i->available && !mds) {
+ uint32_t input_latency = 0;
+ uint32_t output_latency = 0;
+ if (_can_set_midi_latencies) {
+ input_latency = backend->systemic_midi_input_latency (i->name);
+ output_latency = backend->systemic_midi_output_latency (i->name);
+ }
+ bool enabled = backend->midi_device_enabled (i->name);
+ MidiDeviceSettings ptr (new MidiDeviceSetting (i->name, enabled, input_latency, output_latency));
+ new_devices.push_back (ptr);
+ } else if (i->available) {
+ new_devices.push_back (mds);
+ }
+ }
+ _midi_devices = new_devices;
+
+ if (_midi_devices.empty()) {
+ midi_devices_button.set_sensitive (false);
} else {
- ARDOUR_UI::instance()->reconnect_to_engine ();
+ midi_devices_button.set_sensitive (true);
+ }
+
+ if (!ignore_changes) {
+ save_state ();
}
}
void
-EngineControl::calibrate_latency ()
+EngineControl::parameter_changed ()
{
- notebook.set_current_page (latency_tab);
+ if (!ignore_changes) {
+ save_state ();
+ }
+}
+
+EngineControl::State
+EngineControl::get_matching_state (
+ const string& backend,
+ const string& driver,
+ const string& device)
+{
+ for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
+ if ((*i)->backend == backend &&
+ (!_have_control || ((*i)->driver == driver && (*i)->device == device)))
+ {
+ return (*i);
+ }
+ }
+ return State();
}
+EngineControl::State
+EngineControl::get_saved_state_for_currently_displayed_backend_and_device ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+ if (backend) {
+ return get_matching_state (backend_combo.get_active_text(),
+ (backend->requires_driver_selection() ? (std::string) driver_combo.get_active_text() : string()),
+ device_combo.get_active_text());
+ }
+
+
+ return get_matching_state (backend_combo.get_active_text(),
+ string(),
+ device_combo.get_active_text());
+}
+
+EngineControl::State
+EngineControl::save_state ()
+{
+ State state;
+
+ if (!_have_control) {
+ state = get_matching_state (backend_combo.get_active_text(), string(), string());
+ if (state) {
+ return state;
+ }
+ state.reset(new StateStruct);
+ state->backend = get_backend ();
+ } else {
+ state.reset(new StateStruct);
+ store_state (state);
+ }
+
+ for (StateList::iterator i = states.begin(); i != states.end();) {
+ if ((*i)->backend == state->backend &&
+ (*i)->driver == state->driver &&
+ (*i)->device == state->device) {
+ i = states.erase(i);
+ } else {
+ ++i;
+ }
+ }
+
+ states.push_back (state);
+
+ return state;
+}
+
+void
+EngineControl::store_state (State state)
+{
+ state->backend = get_backend ();
+ state->driver = get_driver ();
+ state->device = get_device_name ();
+ state->sample_rate = get_rate ();
+ state->buffer_size = get_buffer_size ();
+ state->input_latency = get_input_latency ();
+ state->output_latency = get_output_latency ();
+ state->input_channels = get_input_channels ();
+ state->output_channels = get_output_channels ();
+ state->midi_option = get_midi_option ();
+ state->midi_devices = _midi_devices;
+}
+
+void
+EngineControl::maybe_display_saved_state ()
+{
+ if (!_have_control) {
+ return;
+ }
+
+ State state = get_saved_state_for_currently_displayed_backend_and_device ();
+
+ if (state) {
+ PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+
+ if (!_desired_sample_rate) {
+ sample_rate_combo.set_active_text (rate_as_string (state->sample_rate));
+ }
+ buffer_size_combo.set_active_text (bufsize_as_string (state->buffer_size));
+ /* call this explicitly because we're ignoring changes to
+ the controls at this point.
+ */
+ show_buffer_duration ();
+ input_latency.set_value (state->input_latency);
+ output_latency.set_value (state->output_latency);
+
+ if (!state->midi_option.empty()) {
+ midi_option_combo.set_active_text (state->midi_option);
+ _midi_devices = state->midi_devices;
+ }
+ }
+}
+
+XMLNode&
+EngineControl::get_state ()
+{
+ XMLNode* root = new XMLNode ("AudioMIDISetup");
+ std::string path;
+
+ if (!states.empty()) {
+ XMLNode* state_nodes = new XMLNode ("EngineStates");
+
+ for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
+
+ XMLNode* node = new XMLNode ("State");
+
+ node->add_property ("backend", (*i)->backend);
+ node->add_property ("driver", (*i)->driver);
+ node->add_property ("device", (*i)->device);
+ node->add_property ("sample-rate", (*i)->sample_rate);
+ node->add_property ("buffer-size", (*i)->buffer_size);
+ node->add_property ("input-latency", (*i)->input_latency);
+ node->add_property ("output-latency", (*i)->output_latency);
+ node->add_property ("input-channels", (*i)->input_channels);
+ node->add_property ("output-channels", (*i)->output_channels);
+ node->add_property ("active", (*i)->active ? "yes" : "no");
+ node->add_property ("midi-option", (*i)->midi_option);
+
+ XMLNode* midi_devices = new XMLNode ("MIDIDevices");
+ for (std::vector<MidiDeviceSettings>::const_iterator p = (*i)->midi_devices.begin(); p != (*i)->midi_devices.end(); ++p) {
+ XMLNode* midi_device_stuff = new XMLNode ("MIDIDevice");
+ midi_device_stuff->add_property (X_("name"), (*p)->name);
+ midi_device_stuff->add_property (X_("enabled"), (*p)->enabled);
+ midi_device_stuff->add_property (X_("input-latency"), (*p)->input_latency);
+ midi_device_stuff->add_property (X_("output-latency"), (*p)->output_latency);
+ midi_devices->add_child_nocopy (*midi_device_stuff);
+ }
+ node->add_child_nocopy (*midi_devices);
+
+ state_nodes->add_child_nocopy (*node);
+ }
+
+ root->add_child_nocopy (*state_nodes);
+ }
+
+ return *root;
+}
+
+void
+EngineControl::set_state (const XMLNode& root)
+{
+ XMLNodeList clist, cclist;
+ XMLNodeConstIterator citer, cciter;
+ XMLNode* child;
+ XMLNode* grandchild;
+ XMLProperty* prop = NULL;
+
+ if (root.name() != "AudioMIDISetup") {
+ return;
+ }
+
+ clist = root.children();
+
+ states.clear ();
+
+ for (citer = clist.begin(); citer != clist.end(); ++citer) {
+
+ child = *citer;
+
+ if (child->name() != "EngineStates") {
+ continue;
+ }
+
+ cclist = child->children();
+
+ for (cciter = cclist.begin(); cciter != cclist.end(); ++cciter) {
+ State state (new StateStruct);
+
+ grandchild = *cciter;
+
+ if (grandchild->name() != "State") {
+ continue;
+ }
+
+ if ((prop = grandchild->property ("backend")) == 0) {
+ continue;
+ }
+ state->backend = prop->value ();
+
+ if ((prop = grandchild->property ("driver")) == 0) {
+ continue;
+ }
+ state->driver = prop->value ();
+
+ if ((prop = grandchild->property ("device")) == 0) {
+ continue;
+ }
+ state->device = prop->value ();
+
+ if ((prop = grandchild->property ("sample-rate")) == 0) {
+ continue;
+ }
+ state->sample_rate = atof (prop->value ());
+
+ if ((prop = grandchild->property ("buffer-size")) == 0) {
+ continue;
+ }
+ state->buffer_size = atoi (prop->value ());
+
+ if ((prop = grandchild->property ("input-latency")) == 0) {
+ continue;
+ }
+ state->input_latency = atoi (prop->value ());
+
+ if ((prop = grandchild->property ("output-latency")) == 0) {
+ continue;
+ }
+ state->output_latency = atoi (prop->value ());
+
+ if ((prop = grandchild->property ("input-channels")) == 0) {
+ continue;
+ }
+ state->input_channels = atoi (prop->value ());
+
+ if ((prop = grandchild->property ("output-channels")) == 0) {
+ continue;
+ }
+ state->output_channels = atoi (prop->value ());
+
+ if ((prop = grandchild->property ("active")) == 0) {
+ continue;
+ }
+ state->active = string_is_affirmative (prop->value ());
+
+ if ((prop = grandchild->property ("midi-option")) == 0) {
+ continue;
+ }
+ state->midi_option = prop->value ();
+
+ state->midi_devices.clear();
+ XMLNode* midinode;
+ if ((midinode = ARDOUR::find_named_node (*grandchild, "MIDIDevices")) != 0) {
+ const XMLNodeList mnc = midinode->children();
+ for (XMLNodeList::const_iterator n = mnc.begin(); n != mnc.end(); ++n) {
+ if ((*n)->property (X_("name")) == 0
+ || (*n)->property (X_("enabled")) == 0
+ || (*n)->property (X_("input-latency")) == 0
+ || (*n)->property (X_("output-latency")) == 0
+ ) {
+ continue;
+ }
+
+ MidiDeviceSettings ptr (new MidiDeviceSetting(
+ (*n)->property (X_("name"))->value (),
+ string_is_affirmative ((*n)->property (X_("enabled"))->value ()),
+ atoi ((*n)->property (X_("input-latency"))->value ()),
+ atoi ((*n)->property (X_("output-latency"))->value ())
+ ));
+ state->midi_devices.push_back (ptr);
+ }
+ }
+
+#if 1
+ /* remove accumulated duplicates (due to bug in ealier version)
+ * this can be removed again before release
+ */
+ for (StateList::iterator i = states.begin(); i != states.end();) {
+ if ((*i)->backend == state->backend &&
+ (*i)->driver == state->driver &&
+ (*i)->device == state->device) {
+ i = states.erase(i);
+ } else {
+ ++i;
+ }
+ }
+#endif
+
+ states.push_back (state);
+ }
+ }
+
+ /* now see if there was an active state and switch the setup to it */
+
+ for (StateList::const_iterator i = states.begin(); i != states.end(); ++i) {
+
+ if ((*i)->active) {
+ ignore_changes++;
+ backend_combo.set_active_text ((*i)->backend);
+ driver_combo.set_active_text ((*i)->driver);
+ device_combo.set_active_text ((*i)->device);
+ sample_rate_combo.set_active_text (rate_as_string ((*i)->sample_rate));
+ buffer_size_combo.set_active_text (bufsize_as_string ((*i)->buffer_size));
+ input_latency.set_value ((*i)->input_latency);
+ output_latency.set_value ((*i)->output_latency);
+ midi_option_combo.set_active_text ((*i)->midi_option);
+ ignore_changes--;
+ break;
+ }
+ }
+}
+
+int
+EngineControl::push_state_to_backend (bool start)
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+ if (!backend) {
+ return 0;
+ }
+
+ /* figure out what is going to change */
+
+ bool restart_required = false;
+ bool was_running = ARDOUR::AudioEngine::instance()->running();
+ bool change_driver = false;
+ bool change_device = false;
+ bool change_rate = false;
+ bool change_bufsize = false;
+ bool change_latency = false;
+ bool change_channels = false;
+ bool change_midi = false;
+
+ uint32_t ochan = get_output_channels ();
+ uint32_t ichan = get_input_channels ();
+
+ if (_have_control) {
+
+ if (started_at_least_once) {
+
+ /* we can control the backend */
+
+ if (backend->requires_driver_selection()) {
+ if (get_driver() != backend->driver_name()) {
+ change_driver = true;
+ }
+ }
+
+ if (get_device_name() != backend->device_name()) {
+ change_device = true;
+ }
+
+ if (get_rate() != backend->sample_rate()) {
+ change_rate = true;
+ }
+
+ if (get_buffer_size() != backend->buffer_size()) {
+ change_bufsize = true;
+ }
+
+ if (get_midi_option() != backend->midi_option()) {
+ change_midi = true;
+ }
+
+ /* zero-requested channels means "all available" */
+
+ if (ichan == 0) {
+ ichan = backend->input_channels();
+ }
+
+ if (ochan == 0) {
+ ochan = backend->output_channels();
+ }
+
+ if (ichan != backend->input_channels()) {
+ change_channels = true;
+ }
+
+ if (ochan != backend->output_channels()) {
+ change_channels = true;
+ }
+
+ if (get_input_latency() != backend->systemic_input_latency() ||
+ get_output_latency() != backend->systemic_output_latency()) {
+ change_latency = true;
+ }
+ } else {
+ /* backend never started, so we have to force a group
+ of settings.
+ */
+ change_device = true;
+ if (backend->requires_driver_selection()) {
+ change_driver = true;
+ }
+ change_rate = true;
+ change_bufsize = true;
+ change_channels = true;
+ change_latency = true;
+ change_midi = true;
+ }
+
+ } else {
+
+ /* we have no control over the backend, meaning that we can
+ * only possibly change sample rate and buffer size.
+ */
+
+
+ if (get_rate() != backend->sample_rate()) {
+ change_bufsize = true;
+ }
+
+ if (get_buffer_size() != backend->buffer_size()) {
+ change_bufsize = true;
+ }
+ }
+
+ if (!_have_control) {
+
+ /* We do not have control over the backend, so the best we can
+ * do is try to change the sample rate and/or bufsize and get
+ * out of here.
+ */
+
+ if (change_rate && !backend->can_change_sample_rate_when_running()) {
+ return 1;
+ }
+
+ if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
+ return 1;
+ }
+
+ if (change_rate) {
+ backend->set_sample_rate (get_rate());
+ }
+
+ if (change_bufsize) {
+ backend->set_buffer_size (get_buffer_size());
+ }
+
+ if (start) {
+ if (ARDOUR::AudioEngine::instance()->start ()) {
+ error << string_compose (_("Could not start backend engine %1"), backend->name()) << endmsg;
+ return -1;
+ }
+ }
+
+ post_push ();
+
+ return 0;
+ }
+
+ /* determine if we need to stop the backend before changing parameters */
+
+ if (change_driver || change_device || change_channels || change_latency ||
+ (change_rate && !backend->can_change_sample_rate_when_running()) ||
+ change_midi ||
+ (change_bufsize && !backend->can_change_buffer_size_when_running())) {
+ restart_required = true;
+ } else {
+ restart_required = false;
+ }
+
+ if (was_running) {
+
+ if (!change_driver && !change_device && !change_channels && !change_latency && !change_midi) {
+ /* no changes in any parameters that absolutely require a
+ * restart, so check those that might be changeable without a
+ * restart
+ */
+
+ if (change_rate && !backend->can_change_sample_rate_when_running()) {
+ /* can't do this while running ... */
+ restart_required = true;
+ }
+
+ if (change_bufsize && !backend->can_change_buffer_size_when_running()) {
+ /* can't do this while running ... */
+ restart_required = true;
+ }
+ }
+ }
+
+ if (was_running) {
+ if (restart_required) {
+ if (ARDOUR_UI::instance()->disconnect_from_engine ()) {
+ return -1;
+ }
+ }
+ }
+
+
+ if (change_driver && backend->set_driver (get_driver())) {
+ error << string_compose (_("Cannot set driver to %1"), get_driver()) << endmsg;
+ return -1;
+ }
+ if (change_device && backend->set_device_name (get_device_name())) {
+ error << string_compose (_("Cannot set device name to %1"), get_device_name()) << endmsg;
+ return -1;
+ }
+ if (change_rate && backend->set_sample_rate (get_rate())) {
+ error << string_compose (_("Cannot set sample rate to %1"), get_rate()) << endmsg;
+ return -1;
+ }
+ if (change_bufsize && backend->set_buffer_size (get_buffer_size())) {
+ error << string_compose (_("Cannot set buffer size to %1"), get_buffer_size()) << endmsg;
+ return -1;
+ }
+
+ if (change_channels || get_input_channels() == 0 || get_output_channels() == 0) {
+ if (backend->set_input_channels (get_input_channels())) {
+ error << string_compose (_("Cannot set input channels to %1"), get_input_channels()) << endmsg;
+ return -1;
+ }
+ if (backend->set_output_channels (get_output_channels())) {
+ error << string_compose (_("Cannot set output channels to %1"), get_output_channels()) << endmsg;
+ return -1;
+ }
+ }
+ if (change_latency) {
+ if (backend->set_systemic_input_latency (get_input_latency())) {
+ error << string_compose (_("Cannot set input latency to %1"), get_input_latency()) << endmsg;
+ return -1;
+ }
+ if (backend->set_systemic_output_latency (get_output_latency())) {
+ error << string_compose (_("Cannot set output latency to %1"), get_output_latency()) << endmsg;
+ return -1;
+ }
+ }
+
+ if (change_midi) {
+ backend->set_midi_option (get_midi_option());
+ }
+
+ if (1 /* TODO */) {
+ for (vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
+ if (_measure_midi) {
+ if (*p == _measure_midi) {
+ backend->set_midi_device_enabled ((*p)->name, true);
+ } else {
+ backend->set_midi_device_enabled ((*p)->name, false);
+ }
+ continue;
+ }
+ backend->set_midi_device_enabled ((*p)->name, (*p)->enabled);
+ if (backend->can_set_systemic_midi_latencies()) {
+ backend->set_systemic_midi_input_latency ((*p)->name, (*p)->input_latency);
+ backend->set_systemic_midi_output_latency ((*p)->name, (*p)->output_latency);
+ }
+ }
+ }
+
+ if (start || (was_running && restart_required)) {
+ if (ARDOUR_UI::instance()->reconnect_to_engine()) {
+ return -1;
+ }
+ }
+
+ post_push ();
+
+ return 0;
+}
+
+void
+EngineControl::post_push ()
+{
+ /* get a pointer to the current state object, creating one if
+ * necessary
+ */
+
+ State state = get_saved_state_for_currently_displayed_backend_and_device ();
+
+ if (!state) {
+ state = save_state ();
+ assert (state);
+ }
+
+ /* all off */
+
+ for (StateList::iterator i = states.begin(); i != states.end(); ++i) {
+ (*i)->active = false;
+ }
+
+ /* mark this one active (to be used next time the dialog is
+ * shown)
+ */
+
+ state->active = true;
+
+ if (_have_control) { // XXX
+ manage_control_app_sensitivity ();
+ }
+
+ /* schedule a redisplay of MIDI ports */
+ //Glib::signal_timeout().connect (sigc::bind_return (sigc::mem_fun (*this, &EngineControl::refresh_midi_display), false), 1000);
+}
+
+
+float
+EngineControl::get_rate () const
+{
+ float r = atof (sample_rate_combo.get_active_text ());
+ /* the string may have been translated with an abbreviation for
+ * thousands, so use a crude heuristic to fix this.
+ */
+ if (r < 1000.0) {
+ r *= 1000.0;
+ }
+ return r;
+}
+
+
+uint32_t
+EngineControl::get_buffer_size () const
+{
+ string txt = buffer_size_combo.get_active_text ();
+ uint32_t samples;
+
+ if (sscanf (txt.c_str(), "%d", &samples) != 1) {
+ throw exception ();
+ }
+
+ return samples;
+}
+
+string
+EngineControl::get_midi_option () const
+{
+ return midi_option_combo.get_active_text();
+}
+
+uint32_t
+EngineControl::get_input_channels() const
+{
+ return (uint32_t) input_channels_adjustment.get_value();
+}
+
+uint32_t
+EngineControl::get_output_channels() const
+{
+ return (uint32_t) output_channels_adjustment.get_value();
+}
+
+uint32_t
+EngineControl::get_input_latency() const
+{
+ return (uint32_t) input_latency_adjustment.get_value();
+}
+
+uint32_t
+EngineControl::get_output_latency() const
+{
+ return (uint32_t) output_latency_adjustment.get_value();
+}
+
+string
+EngineControl::get_backend () const
+{
+ return backend_combo.get_active_text ();
+}
+
+string
+EngineControl::get_driver () const
+{
+ if (driver_combo.get_sensitive() && driver_combo.get_parent()) {
+ return driver_combo.get_active_text ();
+ } else {
+ return "";
+ }
+}
+
+string
+EngineControl::get_device_name () const
+{
+ return device_combo.get_active_text ();
+}
+
+void
+EngineControl::control_app_button_clicked ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+ if (!backend) {
+ return;
+ }
+
+ backend->launch_control_app ();
+}
+
+void
+EngineControl::manage_control_app_sensitivity ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+
+ if (!backend) {
+ return;
+ }
+
+ string appname = backend->control_app_name();
+
+ if (appname.empty()) {
+ control_app_button.set_sensitive (false);
+ } else {
+ control_app_button.set_sensitive (true);
+ }
+}
+
+void
+EngineControl::set_desired_sample_rate (uint32_t sr)
+{
+ _desired_sample_rate = sr;
+ device_changed ();
+}
+
+void
+EngineControl::on_switch_page (GtkNotebookPage*, guint page_num)
+{
+ if (page_num == 0) {
+ cancel_button->set_sensitive (true);
+ ok_button->set_sensitive (true);
+ apply_button->set_sensitive (true);
+ _measure_midi.reset();
+ } else {
+ cancel_button->set_sensitive (false);
+ ok_button->set_sensitive (false);
+ apply_button->set_sensitive (false);
+ }
+
+ if (page_num == midi_tab) {
+ /* MIDI tab */
+ refresh_midi_display ();
+ }
+
+ if (page_num == latency_tab) {
+ /* latency tab */
+
+ if (ARDOUR::AudioEngine::instance()->running()) {
+ // TODO - mark as 'stopped for latency
+ ARDOUR_UI::instance()->disconnect_from_engine ();
+ }
+
+ {
+ PBD::Unwinder<uint32_t> protect_ignore_changes (ignore_changes, ignore_changes + 1);
+
+ /* save any existing latency values */
+
+ uint32_t il = (uint32_t) input_latency.get_value ();
+ uint32_t ol = (uint32_t) input_latency.get_value ();
+
+ /* reset to zero so that our new test instance
+ will be clean of any existing latency measures.
+
+ NB. this should really be done by the backend
+ when stated for latency measurement.
+ */
+
+ input_latency.set_value (0);
+ output_latency.set_value (0);
+
+ push_state_to_backend (false);
+
+ /* reset control */
+
+ input_latency.set_value (il);
+ output_latency.set_value (ol);
+
+ }
+ // This should be done in push_state_to_backend()
+ if (ARDOUR::AudioEngine::instance()->prepare_for_latency_measurement()) {
+ disable_latency_tab ();
+ }
+
+ enable_latency_tab ();
+
+ } else {
+ if (lm_running) {
+ end_latency_detection ();
+ ARDOUR::AudioEngine::instance()->stop_latency_detection();
+ }
+ }
+}
+
+/* latency measurement */
+
+bool
+EngineControl::check_audio_latency_measurement ()
+{
+ MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
+
+ if (mtdm->resolve () < 0) {
+ lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
+ return true;
+ }
+
+ if (mtdm->err () > 0.3) {
+ mtdm->invert ();
+ mtdm->resolve ();
+ }
+
+ char buf[256];
+ ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
+
+ if (sample_rate == 0) {
+ lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
+ ARDOUR::AudioEngine::instance()->stop_latency_detection ();
+ return false;
+ }
+
+ int frames_total = mtdm->del();
+ int extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
+
+ snprintf (buf, sizeof (buf), "%s%d samples (%.3lf ms)\n%s%d samples (%.3lf ms)",
+ _("Detected roundtrip latency: "),
+ frames_total, frames_total * 1000.0f/sample_rate,
+ _("Systemic latency: "),
+ extra, extra * 1000.0f/sample_rate);
+
+ bool solid = true;
+
+ if (mtdm->err () > 0.2) {
+ strcat (buf, " ");
+ strcat (buf, _("(signal detection error)"));
+ solid = false;
+ }
+
+ if (mtdm->inv ()) {
+ strcat (buf, " ");
+ strcat (buf, _("(inverted - bad wiring)"));
+ solid = false;
+ }
+
+ if (solid) {
+ have_lm_results = true;
+ end_latency_detection ();
+ lm_use_button.set_sensitive (true);
+ return false;
+ }
+
+ lm_results.set_markup (string_compose (results_markup, buf));
+
+ return true;
+}
+
+bool
+EngineControl::check_midi_latency_measurement ()
+{
+ ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
+
+ if (!mididm->have_signal () || mididm->latency () == 0) {
+ lm_results.set_markup (string_compose (results_markup, _("No signal detected ")));
+ return true;
+ }
+
+ char buf[256];
+ ARDOUR::framecnt_t const sample_rate = ARDOUR::AudioEngine::instance()->sample_rate();
+
+ if (sample_rate == 0) {
+ lm_results.set_markup (string_compose (results_markup, _("Disconnected from audio engine")));
+ ARDOUR::AudioEngine::instance()->stop_latency_detection ();
+ return false;
+ }
+
+ ARDOUR::framecnt_t frames_total = mididm->latency();
+ ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
+ snprintf (buf, sizeof (buf), "%s%" PRId64" samples (%.1lf ms) dev: %.2f[spl]\n%s%" PRId64" samples (%.1lf ms)",
+ _("Detected roundtrip latency: "),
+ frames_total, frames_total * 1000.0f / sample_rate, mididm->deviation (),
+ _("Systemic latency: "),
+ extra, extra * 1000.0f / sample_rate);
+
+ bool solid = true;
+
+ if (!mididm->ok ()) {
+ strcat (buf, " ");
+ strcat (buf, _("(averaging)"));
+ solid = false;
+ }
+
+ if (mididm->deviation () > 50.0) {
+ strcat (buf, " ");
+ strcat (buf, _("(too large jitter)"));
+ solid = false;
+ } else if (mididm->deviation () > 10.0) {
+ strcat (buf, " ");
+ strcat (buf, _("(large jitter)"));
+ }
+
+ if (solid) {
+ have_lm_results = true;
+ end_latency_detection ();
+ lm_use_button.set_sensitive (true);
+ return false;
+ } else if (mididm->processed () > 400) {
+ have_lm_results = false;
+ end_latency_detection ();
+ lm_results.set_markup (string_compose (results_markup, _("Timeout - large MIDI jitter.")));
+ return false;
+ }
+
+ lm_results.set_markup (string_compose (results_markup, buf));
+
+ return true;
+}
+
+void
+EngineControl::start_latency_detection ()
+{
+ ARDOUR::AudioEngine::instance()->set_latency_input_port (lm_input_channel_combo.get_active_text());
+ ARDOUR::AudioEngine::instance()->set_latency_output_port (lm_output_channel_combo.get_active_text());
+
+ if (ARDOUR::AudioEngine::instance()->start_latency_detection (_measure_midi ? true : false) == 0) {
+ lm_results.set_markup (string_compose (results_markup, _("Detecting ...")));
+ if (_measure_midi) {
+ latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_midi_latency_measurement), 100);
+ } else {
+ latency_timeout = Glib::signal_timeout().connect (mem_fun (*this, &EngineControl::check_audio_latency_measurement), 100);
+ }
+ lm_measure_label.set_text (_("Cancel"));
+ have_lm_results = false;
+ lm_use_button.set_sensitive (false);
+ lm_input_channel_combo.set_sensitive (false);
+ lm_output_channel_combo.set_sensitive (false);
+ lm_running = true;
+ }
+}
+
+void
+EngineControl::end_latency_detection ()
+{
+ latency_timeout.disconnect ();
+ ARDOUR::AudioEngine::instance()->stop_latency_detection ();
+ lm_measure_label.set_text (_("Measure"));
+ if (!have_lm_results) {
+ lm_use_button.set_sensitive (false);
+ }
+ lm_input_channel_combo.set_sensitive (true);
+ lm_output_channel_combo.set_sensitive (true);
+ lm_running = false;
+}
+
+void
+EngineControl::latency_button_clicked ()
+{
+ if (!lm_running) {
+ start_latency_detection ();
+ } else {
+ end_latency_detection ();
+ }
+}
+
+void
+EngineControl::use_latency_button_clicked ()
+{
+ if (_measure_midi) {
+ ARDOUR::MIDIDM* mididm = ARDOUR::AudioEngine::instance()->mididm ();
+ if (!mididm) {
+ return;
+ }
+ ARDOUR::framecnt_t frames_total = mididm->latency();
+ ARDOUR::framecnt_t extra = frames_total - ARDOUR::AudioEngine::instance()->latency_signal_delay();
+ uint32_t one_way = max ((ARDOUR::framecnt_t) 0, extra / 2);
+ _measure_midi->input_latency = one_way;
+ _measure_midi->output_latency = one_way;
+ notebook.set_current_page (midi_tab);
+ } else {
+ MTDM* mtdm = ARDOUR::AudioEngine::instance()->mtdm ();
+
+ if (!mtdm) {
+ return;
+ }
+
+ double one_way = rint ((mtdm->del() - ARDOUR::AudioEngine::instance()->latency_signal_delay()) / 2.0);
+ one_way = std::max (0., one_way);
+
+ input_latency_adjustment.set_value (one_way);
+ output_latency_adjustment.set_value (one_way);
+
+ /* back to settings page */
+ notebook.set_current_page (0);
+}
+ }
+
+
+bool
+EngineControl::on_delete_event (GdkEventAny* ev)
+{
+ if (notebook.get_current_page() == 2) {
+ /* currently on latency tab - be sure to clean up */
+ end_latency_detection ();
+ }
+ return ArdourDialog::on_delete_event (ev);
+}
+
+void
+EngineControl::engine_running ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
+
+ buffer_size_combo.set_active_text (bufsize_as_string (backend->buffer_size()));
+ sample_rate_combo.set_active_text (rate_as_string (backend->sample_rate()));
+
+ buffer_size_combo.set_sensitive (true);
+ sample_rate_combo.set_sensitive (true);
+
+ connect_disconnect_button.set_label (string_compose (_("Disconnect from %1"), backend->name()));
+
+ started_at_least_once = true;
+}
+
+void
+EngineControl::engine_stopped ()
+{
+ boost::shared_ptr<ARDOUR::AudioBackend> backend = ARDOUR::AudioEngine::instance()->current_backend();
+ assert (backend);
+
+ buffer_size_combo.set_sensitive (false);
+ connect_disconnect_button.set_label (string_compose (_("Connect to %1"), backend->name()));
+
+ sample_rate_combo.set_sensitive (true);
+ buffer_size_combo.set_sensitive (true);
+}
+
+void
+EngineControl::connect_disconnect_click()
+{
+ if (ARDOUR::AudioEngine::instance()->running()) {
+ ARDOUR_UI::instance()->disconnect_from_engine ();
+ } else {
+ ARDOUR_UI::instance()->reconnect_to_engine ();
+ }
+}
+
+void
+EngineControl::calibrate_audio_latency ()
+{
+ _measure_midi.reset ();
+ have_lm_results = false;
+ lm_use_button.set_sensitive (false);
+ lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
+ notebook.set_current_page (latency_tab);
+}
+
+void
+EngineControl::calibrate_midi_latency (MidiDeviceSettings s)
+{
+ _measure_midi = s;
+ have_lm_results = false;
+ lm_use_button.set_sensitive (false);
+ lm_results.set_markup (string_compose (results_markup, _("No measurement results yet")));
+ notebook.set_current_page (latency_tab);
+}
+
+void
+EngineControl::configure_midi_devices ()
+{
+ notebook.set_current_page (midi_tab);
+}
public:
EngineControl ();
~EngineControl ();
-
+
static bool need_setup ();
-
+
XMLNode& get_state ();
void set_state (const XMLNode&);
-
+
void set_desired_sample_rate (uint32_t);
-
+
private:
Gtk::Notebook notebook;
Gtk::Label have_control_text;
Gtk::Button control_app_button;
+ ArdourButton midi_devices_button;
Gtk::Button connect_disconnect_button;
Gtk::Button lm_measure_button;
Gtk::Button lm_use_button;
Gtk::Button lm_back_button;
- ArdourButton lm_button;
+ ArdourButton lm_button_audio;
Gtk::Label lm_title;
+ Gtk::Label lm_preamble;
Gtk::Label lm_results;
Gtk::Table lm_table;
Gtk::VBox lm_vbox;
/* MIDI Tab */
Gtk::VBox midi_vbox;
- Gtk::Button midi_refresh_button;
+ Gtk::Button midi_back_button;
Gtk::Table midi_device_table;
/* MIDI ... JACK */
-
+
Gtk::CheckButton aj_button;
-
+
uint32_t ignore_changes;
uint32_t _desired_sample_rate;
bool started_at_least_once;
void setup_midi_tab_for_backend ();
void setup_midi_tab_for_jack ();
- void refresh_midi_display ();
-
+ void refresh_midi_display (std::string focus = "");
+
std::string bufsize_as_string (uint32_t);
float get_rate() const;
void list_devices ();
void show_buffer_duration ();
- struct State {
+ void configure_midi_devices ();
+
+ struct MidiDeviceSetting {
+ std::string name;
+ bool enabled;
+ uint32_t input_latency;
+ uint32_t output_latency;
+
+ MidiDeviceSetting (std::string n, bool en = true, uint32_t inl = 0, uint32_t oul = 0)
+ : name (n)
+ , enabled (en)
+ , input_latency (inl)
+ , output_latency (oul)
+ {}
+ };
+
+ typedef boost::shared_ptr<MidiDeviceSetting> MidiDeviceSettings;
+ bool _can_set_midi_latencies;
+ std::vector<MidiDeviceSettings> _midi_devices;
+
+ MidiDeviceSettings find_midi_device(std::string devicename) const {
+ for (std::vector<MidiDeviceSettings>::const_iterator p = _midi_devices.begin(); p != _midi_devices.end(); ++p) {
+ if ((*p)->name == devicename) {
+ return *p;
+ }
+ }
+ return MidiDeviceSettings();
+ }
+
+ struct StateStruct {
std::string backend;
std::string driver;
std::string device;
uint32_t output_channels;
bool active;
std::string midi_option;
+ std::vector<MidiDeviceSettings> midi_devices;
- State()
- : input_latency (0)
+ StateStruct()
+ : sample_rate (48000)
+ , buffer_size (1024)
+ , input_latency (0)
, output_latency (0)
, input_channels (0)
, output_channels (0)
, active (false) {}
};
-
+
+ typedef boost::shared_ptr<StateStruct> State;
typedef std::list<State> StateList;
StateList states;
- State* get_matching_state (const std::string& backend,
+ State get_matching_state (const std::string& backend,
const std::string& driver,
const std::string& device);
- State* get_saved_state_for_currently_displayed_backend_and_device ();
+ State get_saved_state_for_currently_displayed_backend_and_device ();
void maybe_display_saved_state ();
- State* save_state ();
- void store_state (State&);
+ State save_state ();
+ void store_state (State);
bool _have_control;
/* latency measurement */
void latency_button_clicked ();
- bool check_latency_measurement ();
+ bool check_audio_latency_measurement ();
+ bool check_midi_latency_measurement ();
sigc::connection latency_timeout;
void enable_latency_tab ();
void disable_latency_tab ();
void start_latency_detection ();
void end_latency_detection ();
-
+
void on_switch_page (GtkNotebookPage*, guint page_num);
bool on_delete_event (GdkEventAny*);
PBD::ScopedConnection stopped_connection;
void connect_disconnect_click ();
- void calibrate_latency ();
+ void calibrate_audio_latency ();
+ void calibrate_midi_latency (MidiDeviceSettings);
+
+ MidiDeviceSettings _measure_midi;
+ void midi_latency_adjustment_changed(Gtk::Adjustment *, MidiDeviceSettings, bool);
+ void midi_device_enabled_toggled(ArdourButton *, MidiDeviceSettings);
+ sigc::connection lm_back_button_signal;
};
#endif /* __gtk2_ardour_engine_dialog_h__ */
REGISTER (zoom_focus);
REGISTER_ENUM (RegionItem);
+ REGISTER_ENUM (WaveItem);
REGISTER_ENUM (StreamItem);
REGISTER_ENUM (PlayheadCursorItem);
REGISTER_ENUM (MarkerItem);
REGISTER_ENUM (StartCrossFadeItem);
REGISTER_ENUM (EndCrossFadeItem);
REGISTER_ENUM (CrossfadeViewItem);
+ REGISTER_ENUM (TimecodeRulerItem);
+ REGISTER_ENUM (MinsecRulerItem);
+ REGISTER_ENUM (BBTRulerItem);
+ REGISTER_ENUM (SamplesRulerItem);
REGISTER (item_type);
}
raw_button.set_label (string_compose (_("Region contents without fades nor region gain (channels: %1)"), region_chans));
raw_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
- vbox.pack_start (raw_button);
+ vbox.pack_start (raw_button, false, false);
fades_button.set_label (string_compose (_("Region contents with fades and region gain (channels: %1)"), region_chans));
fades_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
- vbox.pack_start (fades_button);
+ vbox.pack_start (fades_button, false, false);
processed_button.set_label (string_compose (_("Track output (channels: %1)"), track_chans));
processed_button.signal_toggled ().connect (sigc::mem_fun (*this, &RegionExportChannelSelector::handle_selection));
- vbox.pack_start (processed_button);
+ vbox.pack_start (processed_button, false, false);
sync_with_manager();
vbox.show_all_children ();
// Options
options_box.pack_start(region_contents_button);
options_box.pack_start(track_output_button);
- main_layout.pack_start(options_box);
+ main_layout.pack_start(options_box, false, false);
// Track scroller
track_scroller.add (track_view);
typedef Gtk::TreeModelColumn<Glib::RefPtr<Gtk::ListStore> > ComboCol;
ComboCol port_list_col;
- /* Channel struct, that represents the selected port and it's name */
+ /* Channel struct, that represents the selected port and its name */
struct Channel {
public:
cancel_button->signal_clicked().connect (sigc::mem_fun (*this, &ExportDialog::close_dialog));
export_button->signal_clicked().connect (sigc::mem_fun (*this, &ExportDialog::do_export));
+ file_notebook->soundcloud_export_selector = soundcloud_selector;
+
/* Done! */
show_all_children ();
progress_widget.hide_all();
}
-void
-ExportDialog::expanded_changed ()
-{
- set_resizable(advanced->get_expanded());
-}
-
void
ExportDialog::init_gui ()
{
Gtk::Alignment * preset_align = Gtk::manage (new Gtk::Alignment());
preset_align->add (*preset_selector);
preset_align->set_padding (0, 12, 0, 0);
- get_vbox()->pack_start (*preset_align, false, false, 0);
-
- Gtk::VPaned * advanced_paned = Gtk::manage (new Gtk::VPaned());
-
- Gtk::VBox* timespan_vbox = Gtk::manage (new Gtk::VBox());
- timespan_vbox->set_spacing (12);
- timespan_vbox->set_border_width (12);
-
- Gtk::Alignment * timespan_align = Gtk::manage (new Gtk::Alignment());
- timespan_label = Gtk::manage (new Gtk::Label (_("Time Span"), Gtk::ALIGN_LEFT));
- timespan_align->add (*timespan_selector);
- timespan_align->set_padding (0, 0, 18, 0);
- timespan_vbox->pack_start (*timespan_label, false, false, 0);
- timespan_vbox->pack_start (*timespan_align, true, true, 0);
- advanced_paned->pack1(*timespan_vbox, true, false);
-
- Gtk::VBox* channels_vbox = Gtk::manage (new Gtk::VBox());
- channels_vbox->set_spacing (12);
- channels_vbox->set_border_width (12);
-
- Gtk::Alignment * channels_align = Gtk::manage (new Gtk::Alignment());
- channels_label = Gtk::manage (new Gtk::Label (_("Channels"), Gtk::ALIGN_LEFT));
- channels_align->add (*channel_selector);
- channels_align->set_padding (0, 12, 18, 0);
- channels_vbox->pack_start (*channels_label, false, false, 0);
- channels_vbox->pack_start (*channels_align, true, true, 0);
- advanced_paned->pack2(*channels_vbox, channel_selector_is_expandable(), false);
-
- get_vbox()->pack_start (*file_notebook, false, false, 0);
- get_vbox()->pack_start (warning_widget, false, false, 0);
- get_vbox()->pack_start (progress_widget, false, false, 0);
-
- advanced = Gtk::manage (new Gtk::Expander (_("Time span and channel options")));
- advanced->property_expanded().signal_changed().connect(
- sigc::mem_fun(*this, &ExportDialog::expanded_changed));
- advanced->add (*advanced_paned);
-
- if (channel_selector_is_expandable()) {
- advanced_sizegroup = Gtk::SizeGroup::create(Gtk::SIZE_GROUP_VERTICAL);
- advanced_sizegroup->add_widget(*timespan_selector);
- advanced_sizegroup->add_widget(*channel_selector);
- }
- get_vbox()->pack_start (*advanced, true, true);
+ Gtk::VBox * file_format_selector = Gtk::manage (new Gtk::VBox());
+ file_format_selector->set_homogeneous (false);
+ file_format_selector->pack_start (*preset_align, false, false, 0);
+ file_format_selector->pack_start (*file_notebook, false, false, 0);
+ file_format_selector->pack_start (*soundcloud_selector, false, false, 0);
- Pango::AttrList bold;
- Pango::Attribute b = Pango::Attribute::create_attr_weight (Pango::WEIGHT_BOLD);
- bold.insert (b);
+ export_notebook.append_page (*file_format_selector, _("File format"));
+ export_notebook.append_page (*timespan_selector, _("Time Span"));
+ export_notebook.append_page (*channel_selector, _("Channels"));
+
+ get_vbox()->pack_start (export_notebook, true, true, 0);
+ get_vbox()->pack_end (warning_widget, false, false, 0);
+ get_vbox()->pack_end (progress_widget, false, false, 0);
- timespan_label->set_attributes (bold);
- channels_label->set_attributes (bold);
}
void
preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+ soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ());
}
dialog.run();
}
+void
+ExportDialog::soundcloud_upload_progress(double total, double now, std::string title)
+{
+ soundcloud_selector->do_progress_callback(total, now, title);
+
+}
+
void
ExportDialog::do_export ()
{
try {
profile_manager->prepare_for_export ();
+ handler->soundcloud_username = soundcloud_selector->username ();
+ handler->soundcloud_password = soundcloud_selector->password ();
+ handler->soundcloud_make_public = soundcloud_selector->make_public ();
+ handler->soundcloud_open_page = soundcloud_selector->open_page ();
+ handler->soundcloud_downloadable = soundcloud_selector->downloadable ();
+
+ handler->SoundcloudProgress.connect_same_thread(
+ *this,
+ boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3)
+ );
+#if 0
+ handler->SoundcloudProgress.connect(
+ *this, invalidator (*this),
+ boost::bind(&ExportDialog::soundcloud_upload_progress, this, _1, _2, _3),
+ gui_context()
+ );
+#endif
handler->do_export ();
show_progress ();
} catch(std::exception & e) {
preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, range_id));
channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+ soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ());
}
preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, X_("selection")));
channel_selector.reset (new PortExportChannelSelector (_session, profile_manager));
+ soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ());
}
ExportRegionDialog::init_gui ()
{
ExportDialog::init_gui ();
-
- channels_label->set_text (_("Source"));
+ export_notebook.set_tab_label_text(*export_notebook.get_nth_page(2), _("Source"));
}
void
preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorSingle (_session, profile_manager, loc_id));
channel_selector.reset (new RegionExportChannelSelector (_session, profile_manager, region, track));
+ soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ());
}
preset_selector.reset (new ExportPresetSelector ());
timespan_selector.reset (new ExportTimespanSelectorMultiple (_session, profile_manager));
channel_selector.reset (new TrackExportChannelSelector (_session, profile_manager));
+ soundcloud_selector.reset (new SoundcloudExportSelector ());
file_notebook.reset (new ExportFileNotebook ());
}
#include "export_file_notebook.h"
#include "export_preset_selector.h"
#include "ardour_dialog.h"
+#include "soundcloud_export_selector.h"
#include <gtkmm.h>
class ExportTimespanSelector;
class ExportChannelSelector;
-class ExportDialog : public ArdourDialog {
+class ExportDialog : public ArdourDialog, public PBD::ScopedConnectionList
+{
public:
// Must initialize all the shared_ptrs below
virtual void init_components ();
- // Override if the channel selector should not be grown
- virtual bool channel_selector_is_expandable() { return true; }
-
boost::scoped_ptr<ExportPresetSelector> preset_selector;
boost::scoped_ptr<ExportTimespanSelector> timespan_selector;
boost::scoped_ptr<ExportChannelSelector> channel_selector;
boost::scoped_ptr<ExportFileNotebook> file_notebook;
+ boost::shared_ptr<SoundcloudExportSelector> soundcloud_selector;
+
Gtk::VBox warning_widget;
Gtk::VBox progress_widget;
- Gtk::Label * timespan_label;
- Gtk::Label * channels_label;
+ /*** GUI components ***/
+ Gtk::Notebook export_notebook;
private:
void init ();
- void expanded_changed();
-
void notify_errors (bool force = false);
void close_dialog ();
PublicEditor & editor;
StatusPtr status;
- /*** GUI components ***/
- Glib::RefPtr<Gtk::SizeGroup> advanced_sizegroup;
- Gtk::Expander * advanced;
/* Warning area */
float previous_progress; // Needed for gtk bug workaround
+ void soundcloud_upload_progress(double total, double now, std::string title);
+
/* Buttons */
Gtk::Button * cancel_button;
public:
ExportRegionDialog (PublicEditor & editor, ARDOUR::AudioRegion const & region, ARDOUR::AudioTrack & track);
- protected:
- virtual bool channel_selector_is_expandable() { return false; }
-
private:
void init_gui ();
void init_components ();
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
ExportFileNotebook::ExportFileNotebook () :
}
set_current_page (0);
+ update_soundcloud_upload ();
CriticalSelectionChanged ();
}
void
-ExportFileNotebook::update_example_filenames()
+ExportFileNotebook::update_soundcloud_upload ()
{
- int i = 0;
- FilePage * page;
- while ((page = dynamic_cast<FilePage *> (get_nth_page (i++)))) {
- page->update_example_filename();
+ int i;
+ bool show_credentials_entry = false;
+ ExportProfileManager::FormatStateList const & formats = profile_manager->get_formats ();
+ ExportProfileManager::FormatStateList::const_iterator format_it;
+
+ for (i = 0, format_it = formats.begin(); format_it != formats.end(); ++i, ++format_it) {
+ FilePage * page;
+ if ((page = dynamic_cast<FilePage *> (get_nth_page (i)))) {
+ bool this_soundcloud_upload = page->get_soundcloud_upload ();
+ (*format_it)->format->set_soundcloud_upload (this_soundcloud_upload);
+ if (this_soundcloud_upload) {
+ show_credentials_entry = true;
+ }
+ }
}
+
+ soundcloud_export_selector->set_visible (show_credentials_entry);
+
}
-std::string
-ExportFileNotebook::get_nth_format_name (uint32_t n)
+void
+ExportFileNotebook::update_example_filenames ()
{
+ int i = 0;
FilePage * page;
- if ((page = dynamic_cast<FilePage *> (get_nth_page (n - 1)))) {
- return page->get_format_name();
+ while ((page = dynamic_cast<FilePage *> (get_nth_page (i++)))) {
+ page->update_example_filename();
}
- return "";
}
void
format_label (_("Format"), Gtk::ALIGN_LEFT),
filename_label (_("Location"), Gtk::ALIGN_LEFT),
+ soundcloud_upload_button (_("Upload to Soundcloud")),
tab_number (number)
{
set_border_width (12);
pack_start (format_align, false, false, 0);
pack_start (filename_label, false, false, 0);
pack_start (filename_align, false, false, 0);
+ pack_start (soundcloud_upload_button, false, false, 0);
format_align.add (format_selector);
format_align.set_padding (6, 12, 18, 0);
filename_selector.CriticalSelectionChanged.connect (
sigc::mem_fun (*this, &ExportFileNotebook::FilePage::critical_selection_changed));
+ soundcloud_upload_button.signal_toggled().connect (sigc::mem_fun (*parent, &ExportFileNotebook::update_soundcloud_upload));
/* Tab widget */
tab_close_button.add (*Gtk::manage (new Gtk::Image (::get_icon("close"))));
return _("No format!");
}
+bool
+ExportFileNotebook::FilePage::get_soundcloud_upload () const
+{
+ return soundcloud_upload_button.get_active ();
+}
+
void
ExportFileNotebook::FilePage::save_format_to_manager (FormatPtr format)
{
#include "export_format_selector.h"
#include "export_filename_selector.h"
+#include "soundcloud_export_selector.h"
class ExportFileNotebook : public Gtk::Notebook, public ARDOUR::SessionHandlePtr
{
void set_session_and_manager (ARDOUR::Session * s, boost::shared_ptr<ARDOUR::ExportProfileManager> manager);
void sync_with_manager ();
-
void update_example_filenames();
- std::string get_nth_format_name (uint32_t n);
+ boost::shared_ptr<SoundcloudExportSelector> soundcloud_export_selector;
sigc::signal<void> CriticalSelectionChanged;
void add_file_page (ARDOUR::ExportProfileManager::FormatStatePtr format_state, ARDOUR::ExportProfileManager::FilenameStatePtr filename_state);
void remove_file_page (FilePage * page);
void update_remove_file_page_sensitivity ();
+ void update_soundcloud_upload ();
sigc::connection page_change_connection;
void handle_page_change (GtkNotebookPage*, uint32_t page);
Gtk::Widget & get_tab_widget () { return tab_widget; }
void set_remove_sensitive (bool value);
std::string get_format_name () const;
+ bool get_soundcloud_upload () const;
void update_example_filename();
Gtk::Alignment filename_align;
ExportFilenameSelector filename_selector;
+ Gtk::CheckButton soundcloud_upload_button;
Gtk::HBox tab_widget;
Gtk::Label tab_label;
Gtk::Alignment tab_close_alignment;
silence_end_checkbox (_("Add silence at end:")),
silence_end_clock ("silence_end", true, "", true, false, true),
+ command_label(_("Command to run post-export\n(%f=full path & filename, %d=directory, %b=basename):")),
+
format_table (3, 4),
compatibility_label (_("Compatibility"), Gtk::ALIGN_LEFT),
quality_label (_("Quality"), Gtk::ALIGN_LEFT),
silence_table.attach (silence_end_checkbox, 1, 2, 2, 3);
silence_table.attach (silence_end_clock, 2, 3, 2, 3);
+ get_vbox()->pack_start (command_label, false, false);
+ get_vbox()->pack_start (command_entry, false, false);
+
/* Format table */
init_format_table();
with_cue.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_cue));
with_toc.signal_toggled().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_with_toc));
+ command_entry.signal_changed().connect (sigc::mem_fun (*this, &ExportFormatDialog::update_command));
cue_toc_vbox.pack_start (with_cue, false, false);
cue_toc_vbox.pack_start (with_toc, false, false);
}
tag_checkbox.set_active (spec->tag());
+ command_entry.set_text (spec->command());
}
void
manager.select_with_toc (with_toc.get_active());
}
+
+void
+ExportFormatDialog::update_command ()
+{
+ manager.set_command (command_entry.get_text());
+}
+
void
ExportFormatDialog::update_description()
{
Gtk::CheckButton silence_end_checkbox;
AudioClock silence_end_clock;
+ /* Post-export hook */
+
+ Gtk::Label command_label;
+ Gtk::Entry command_entry;
+
/* Format table */
struct CompatibilityCols : public Gtk::TreeModelColumnRecord
void update_with_toc ();
void update_with_cue ();
+ void update_command ();
Gtk::TreeView sample_format_view;
Gtk::TreeView dither_type_view;
-/*
- Copyright (C) 2006 Paul Davis
- Author: Andre Raue
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include <sys/stat.h>
-
-#include <sstream>
-
-#include "ardour/audioengine.h"
-#include "ardour/sndfile_helpers.h"
-
-#include "ardour_ui.h"
-#include "export_range_markers_dialog.h"
-
-#include "i18n.h"
-
-using namespace Gtk;
-using namespace ARDOUR;
-using namespace PBD;
-using namespace std;
-
-ExportRangeMarkersDialog::ExportRangeMarkersDialog (PublicEditor& editor)
- : ExportDialog(editor)
-{
- set_title (_("Export Ranges"));
- file_frame.set_label (_("Export to Directory"));
-
- do_not_allow_export_cd_markers();
-
- total_duration = 0;
- current_range_marker_index = 0;
-}
-
-Gtk::FileChooserAction
-ExportRangeMarkersDialog::browse_action () const
-{
- return Gtk::FILE_CHOOSER_ACTION_CREATE_FOLDER;
-}
-
-void
-ExportRangeMarkersDialog::export_data ()
-{
- getSession().locations()->apply(*this, &ExportRangeMarkersDialog::process_range_markers_export);
-}
-
-void
-ExportRangeMarkersDialog::process_range_markers_export(Locations::LocationList& locations)
-{
- Locations::LocationList::iterator locationIter;
- current_range_marker_index = 0;
- init_progress_computing(locations);
-
- for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) {
- Location *currentLocation = (*locationIter);
-
- if(currentLocation->is_range_marker()){
- // init filename
- string filepath = get_target_filepath(
- get_selected_file_name(),
- currentLocation->name(),
- get_selected_header_format());
-
- initSpec(filepath);
-
- spec.start_frame = currentLocation->start();
- spec.end_frame = currentLocation->end();
-
- if (getSession().start_export(spec)){
- // if export fails
- return;
- }
-
- // wait until export of this range finished
- gtk_main_iteration();
-
- while (spec.running){
- if(gtk_events_pending()){
- gtk_main_iteration();
- }else {
- Glib::usleep(10000);
- }
- }
-
- current_range_marker_index++;
-
- getSession().stop_export (spec);
- }
- }
-
- spec.running = false;
-}
-
-
-string
-ExportRangeMarkersDialog::get_target_filepath(string path, string filename, string postfix)
-{
- string target_path = path;
- if ((target_path.find_last_of ('/')) != string::npos) {
- target_path += '/';
- }
-
- string target_filepath = target_path + filename + postfix;
- struct stat statbuf;
-
- for(int counter=1; (stat (target_filepath.c_str(), &statbuf) == 0); counter++){
- // while file exists
- ostringstream scounter;
- scounter.flush();
- scounter << counter;
-
- target_filepath =
- target_path + filename + "_" + scounter.str() + postfix;
- }
-
- return target_filepath;
-}
-
-bool
-ExportRangeMarkersDialog::is_filepath_valid(string &filepath)
-{
- // sanity check file name first
- struct stat statbuf;
-
- if (filepath.empty()) {
- // warning dialog
- string txt = _("Please enter a valid target directory.");
- MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
- msg.run();
- return false;
- }
-
- if ( (stat (filepath.c_str(), &statbuf) != 0) ||
- (!S_ISDIR (statbuf.st_mode)) ) {
- string txt = _("Please select an existing target directory. Files are not allowed!");
- MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
- msg.run();
- return false;
- }
-
- // directory needs to exist and be writable
- string dirpath = Glib::path_get_dirname (filepath);
- if (!exists_and_writable (dirpath)) {
- string txt = _("Cannot write file in: ") + dirpath;
- MessageDialog msg (*this, txt, false, MESSAGE_ERROR, BUTTONS_OK, true);
- msg.run();
- return false;
- }
-
- return true;
-}
-
-void
-ExportRangeMarkersDialog::init_progress_computing(Locations::LocationList& locations)
-{
- // flush vector
- range_markers_durations_aggregated.resize(0);
-
- framecnt_t duration_before_current_location = 0;
- Locations::LocationList::iterator locationIter;
-
- for (locationIter = locations.begin(); locationIter != locations.end(); ++locationIter) {
- Location *currentLocation = (*locationIter);
-
- if(currentLocation->is_range_marker()){
- range_markers_durations_aggregated.push_back (duration_before_current_location);
-
- framecnt_t duration = currentLocation->end() - currentLocation->start();
-
- range_markers_durations.push_back (duration);
- duration_before_current_location += duration;
- }
- }
-
- total_duration = duration_before_current_location;
-}
-
-
-gint
-ExportRangeMarkersDialog::progress_timeout ()
-{
- double progress = 0.0;
-
- if (current_range_marker_index >= range_markers_durations.size()){
- progress = 1.0;
- } else{
- progress = ((double) range_markers_durations_aggregated[current_range_marker_index] +
- (spec.progress * (double) range_markers_durations[current_range_marker_index])) /
- (double) total_duration;
- }
-
- set_progress_fraction( progress );
- return TRUE;
-}
-/*
- Copyright (C) 2006 Andre Raue
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the 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 __export_range_markers_dialog_h__
-#define __export_range_markers_dialog_h__
-
-#include "ardour/location.h"
-
-#include "export_dialog.h"
-
-
-class ExportRangeMarkersDialog : public ExportDialog
-{
- public:
- ExportRangeMarkersDialog (PublicEditor&);
-
- Gtk::FileChooserAction browse_action() const;
-
- protected:
- virtual bool is_filepath_valid(string &filepath);
-
- void export_data();
-
- bool wants_dir() { return true; }
-
- private:
- // keeps the duration of all range_markers before the current
- vector<nframes_t> range_markers_durations_aggregated;
- vector<nframes_t> range_markers_durations;
- // duration of all range markers
- nframes_t total_duration;
- // index of range marker, that get's exported right now
- unsigned int current_range_marker_index;
-
- // sets value of progress bar
- virtual gint progress_timeout ();
-
- // initializes range_markers_durations_aggregated, range_markers_durations
- // and total_duration
- void init_progress_computing(ARDOUR::Locations::LocationList& locations);
-
- // searches for a filename like "<filename><nr>.<postfix>" in path, that
- // does not exist
- string get_target_filepath(string path, string filename, string postfix);
-
- void process_range_markers_export(ARDOUR::Locations::LocationList&);
-};
-
-
-#endif // __export_range_markers_dialog_h__
/* Range view */
range_list = Gtk::ListStore::create (range_cols);
+ // order by location start times
+ range_list->set_sort_column(range_cols.location, Gtk::SORT_ASCENDING);
+ range_list->set_sort_func(range_cols.location, sigc::mem_fun(*this, &ExportTimespanSelector::location_sorter));
range_view.set_model (range_list);
range_view.set_headers_visible (true);
}
}
+int
+ExportTimespanSelector::location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b)
+{
+ Location *l1 = (*a)[range_cols.location];
+ Location *l2 = (*b)[range_cols.location];
+ const Location *ls = _session->locations()->session_range_location();
+
+ // always sort session range first
+ if (l1 == ls)
+ return -1;
+ if (l2 == ls)
+ return +1;
+
+ return l1->start() - l2->start();
+}
+
void
ExportTimespanSelector::add_range_to_selection (ARDOUR::Location const * loc)
{
void update_range_name (std::string const & path, std::string const & new_text);
void set_selection_state_of_all_timespans (bool);
+ int location_sorter(Gtk::TreeModel::iterator a, Gtk::TreeModel::iterator b);
/*** GUI components ***/
Gtk::ScrolledWindow range_scroller;
};
-/// Allows seleting multiple timespans
+/// Allows selecting multiple timespans
class ExportTimespanSelectorMultiple : public ExportTimespanSelector
{
public:
#include "ardour/session_metadata.h"
#include "ardour/broadcast_info.h"
-#include "utils.h"
#include "opts.h"
#include "export_video_dialog.h"
#include "utils_videotl.h"
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtkmm2ext;
using namespace Gtk;
#include "ardour_ui.h"
#include "prompter.h"
#include "plugin_ui.h"
-#include "utils.h"
#include "gui_thread.h"
#include "automation_controller.h"
control_ui->clickbox = new ClickBox (adj, "PluginUIClickBox");
Gtkmm2ext::set_size_request_to_display_given_text (*control_ui->clickbox, "g9999999", 2, 2);
if (desc.midinote) {
- printf("MIDI NOTE\n");
control_ui->clickbox->set_printer (sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::midinote_printer), control_ui));
} else {
control_ui->clickbox->set_printer (sigc::bind (sigc::mem_fun (*this, &GenericPluginUI::integer_printer), control_ui));
*/
#include "evoral/Note.hpp"
-#include "canvas/group.h"
+#include "canvas/container.h"
#include "canvas/rectangle.h"
#include "canvas/wave_view.h"
#include "canvas/debug.h"
PBD::Signal1<void,GhostRegion*> GhostRegion::CatchDeletion;
-GhostRegion::GhostRegion (ArdourCanvas::Group* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_pos)
+GhostRegion::GhostRegion (ArdourCanvas::Container* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_pos)
: trackview (tv)
, source_trackview (source_tv)
{
- group = new ArdourCanvas::Group (parent);
+ group = new ArdourCanvas::Container (parent);
CANVAS_DEBUG_NAME (group, "ghost region");
group->set_position (ArdourCanvas::Duple (initial_pos, 0));
clear_events ();
}
-MidiGhostRegion::GhostEvent::GhostEvent (NoteBase* e, ArdourCanvas::Group* g)
+MidiGhostRegion::GhostEvent::GhostEvent (NoteBase* e, ArdourCanvas::Container* g)
: event (e)
{
rect = new ArdourCanvas::Rectangle (g, ArdourCanvas::Rect (e->x0(), e->y0(), e->x1(), e->y1()));
class GhostRegion : public sigc::trackable
{
public:
- GhostRegion(ArdourCanvas::Group* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos);
+ GhostRegion(ArdourCanvas::Container* parent, TimeAxisView& tv, TimeAxisView& source_tv, double initial_unit_pos);
virtual ~GhostRegion();
virtual void set_samples_per_pixel (double) = 0;
TimeAxisView& trackview;
/** TimeAxisView that we are a ghost for */
TimeAxisView& source_trackview;
- ArdourCanvas::Group* group;
+ ArdourCanvas::Container* group;
ArdourCanvas::Rectangle* base_rect;
static PBD::Signal1<void,GhostRegion*> CatchDeletion;
public:
class GhostEvent : public sigc::trackable {
public:
- GhostEvent(::NoteBase *, ArdourCanvas::Group *);
+ GhostEvent(::NoteBase *, ArdourCanvas::Container *);
virtual ~GhostEvent ();
NoteBase* event;
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
GlobalPortMatrix::GlobalPortMatrix (Gtk::Window* p, Session* s, DataType t)
: PortMatrix (p, s, t)
#include <sigc++/signal.h>
+namespace ARDOUR_UI_UTILS {
extern sigc::signal<void> ColorsChanged;
extern sigc::signal<void> DPIReset;
+} // namespace
#endif /* __gtk_ardour_global_signals_h__ */
#include "gui_thread.h"
#include "route_group_dialog.h"
+#include "global_signals.h"
#include "group_tabs.h"
#include "keyboard.h"
#include "i18n.h"
#include "ardour_ui.h"
+#include "rgb_macros.h"
#include "utils.h"
using namespace std;
using namespace Gtk;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using Gtkmm2ext::Keyboard;
list<Gdk::Color> GroupTabs::_used_colors;
, _dragging_new_tab (0)
{
add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::POINTER_MOTION_MASK);
+ ColorsChanged.connect (sigc::mem_fun (*this, &GroupTabs::queue_draw));
}
GroupTabs::~GroupTabs ()
/** Set the color of the tab of a route group */
void
-GroupTabs::set_group_color (RouteGroup* group, Gdk::Color color)
+GroupTabs::set_group_color (RouteGroup* group, uint32_t color)
{
assert (group);
+ uint32_t r, g, b, a;
+
+ UINT_TO_RGBA (color, &r, &g, &b, &a);
/* Hack to disallow black route groups; force a dark grey instead */
- if (color.get_red() == 0 && color.get_green() == 0 && color.get_blue() == 0) {
- color.set_grey_p (0.1);
+
+ if (r == 0 && g == 0 && b == 0) {
+ r = 25;
+ g = 25;
+ b = 25;
}
GUIObjectState& gui_state = *ARDOUR_UI::instance()->gui_object_state;
char buf[64];
- snprintf (buf, sizeof (buf), "%d:%d:%d", color.get_red(), color.get_green(), color.get_blue());
+
+ /* for historical reasons the colors must be stored as 16 bit color
+ * values. Ugh.
+ */
+
+ snprintf (buf, sizeof (buf), "%d:%d:%d", (r<<8), (g<<8), (b<<8));
gui_state.set_property (group_gui_id (group), "color", buf);
/* the group color change notification */
}
/** @return the color to use for a route group tab */
-Gdk::Color
+uint32_t
GroupTabs::group_color (RouteGroup* group)
{
assert (group);
GUIObjectState& gui_state = *ARDOUR_UI::instance()->gui_object_state;
-
string const gui_id = group_gui_id (group);
-
bool empty;
string const color = gui_state.get_string (gui_id, "color", &empty);
+
if (empty) {
/* no color has yet been set, so use a random one */
- Gdk::Color const color = unique_random_color (_used_colors);
- set_group_color (group, color);
- return color;
+ uint32_t c = gdk_color_to_rgba (unique_random_color (_used_colors));
+ set_group_color (group, c);
+ return c;
}
- Gdk::Color c;
-
int r, g, b;
+ /* for historical reasons, colors are stored as 16 bit values.
+ */
+
sscanf (color.c_str(), "%d:%d:%d", &r, &g, &b);
- c.set_red (r);
- c.set_green (g);
- c.set_blue (b);
-
- return c;
+ r /= 256;
+ g /= 256;
+ b /= 256;
+
+ return RGBA_TO_UINT (r, g, b, 255);
}
void
void run_new_group_dialog (ARDOUR::RouteList const &);
- static void set_group_color (ARDOUR::RouteGroup *, Gdk::Color);
+ static void set_group_color (ARDOUR::RouteGroup *, uint32_t);
static std::string group_gui_id (ARDOUR::RouteGroup *);
- static Gdk::Color group_color (ARDOUR::RouteGroup *);
+ static uint32_t group_color (ARDOUR::RouteGroup *);
protected:
double from;
double to;
- Gdk::Color color; ///< color
+ uint32_t color; ///< color
ARDOUR::RouteGroup* group; ///< route group
};
+++ /dev/null
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
- * file for a list of people on the GTK+ Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/* modified by andreas meyer <hexx3000@gmx.de> */
-/* subsequently specialized for audio time displays by paul davis <paul@linuxaudiosystems.com> */
-
-#include <math.h>
-#include <stdio.h>
-#include <string.h>
-#include "gtk-custom-hruler.h"
-
-#define RULER_HEIGHT 14
-#define MINIMUM_INCR 5
-#define MAXIMUM_SUBDIVIDE 5
-
-#define ROUND(x) ((int) ((x) + 0.5))
-
-static void gtk_custom_hruler_class_init (GtkCustomHRulerClass * klass);
-static void gtk_custom_hruler_init (GtkCustomHRuler * custom_hruler);
-static gint gtk_custom_hruler_motion_notify (GtkWidget * widget, GdkEventMotion * event);
-static void gtk_custom_hruler_draw_ticks (GtkCustomRuler * ruler);
-static void gtk_custom_hruler_draw_pos (GtkCustomRuler * ruler);
-
-GType gtk_custom_hruler_get_type (void)
-{
- static GType hruler_type = 0;
-
- if (!hruler_type) {
- static const GTypeInfo hruler_info =
- {
- sizeof (GtkCustomHRulerClass),
- NULL, /* base_init */
- NULL, /* base_finalize */
- (GClassInitFunc) gtk_custom_hruler_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (GtkCustomHRuler),
- 0, /* n_preallocs */
- (GInstanceInitFunc) gtk_custom_hruler_init,
- NULL /* value_table */
- };
-
- hruler_type = g_type_register_static (gtk_custom_ruler_get_type(), "GtkCustomHRuler",
- &hruler_info, (GTypeFlags)0);
- }
-
- return hruler_type;
-}
-
-static void
-gtk_custom_hruler_class_init (GtkCustomHRulerClass * klass)
-{
- GtkWidgetClass *widget_class;
- GtkCustomRulerClass *ruler_class;
-
- widget_class = (GtkWidgetClass *) klass;
- ruler_class = (GtkCustomRulerClass *) klass;
-
- widget_class->motion_notify_event = gtk_custom_hruler_motion_notify;
-
- ruler_class->draw_ticks = gtk_custom_hruler_draw_ticks;
- ruler_class->draw_pos = gtk_custom_hruler_draw_pos;
-}
-
-static void
-gtk_custom_hruler_init (GtkCustomHRuler * custom_hruler)
-{
- GtkWidget *widget;
-
- widget = GTK_WIDGET (custom_hruler);
- widget->requisition.width = widget->style->xthickness * 2 + 1;
- widget->requisition.height = widget->style->ythickness * 2 + RULER_HEIGHT;
-}
-
-
-GtkWidget *
-gtk_custom_hruler_new (void)
-{
- return GTK_WIDGET (gtk_type_new (gtk_custom_hruler_get_type ()));
-}
-
-static gint
-gtk_custom_hruler_motion_notify (GtkWidget * widget, GdkEventMotion * event)
-{
- GtkCustomRuler *ruler;
- gint x;
-
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_CUSTOM_HRULER (widget), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
-
- ruler = GTK_CUSTOM_RULER (widget);
-
- if (event->is_hint)
- gdk_window_get_pointer (widget->window, &x, NULL, NULL);
- else
- x = event->x;
-
- ruler->position = ruler->lower + ((ruler->upper - ruler->lower) * x) / widget->allocation.width;
-
- /* Make sure the ruler has been allocated already */
- if (ruler->backing_store != NULL)
- gtk_custom_ruler_draw_pos (ruler);
-
- return FALSE;
-}
-
-static void
-gtk_custom_hruler_draw_ticks (GtkCustomRuler * ruler)
-{
- GtkWidget *widget;
- GdkGC *gc;
- gint i;
- GtkCustomRulerMark *marks;
- gint ythickness;
- gint nmarks;
- gint max_chars;
- gint digit_offset;
- PangoLayout *layout;
- PangoRectangle logical_rect, ink_rect;
-
- g_return_if_fail (ruler != NULL);
- g_return_if_fail (GTK_IS_CUSTOM_HRULER (ruler));
-
- if (!GTK_WIDGET_DRAWABLE (ruler))
- return;
-
- widget = GTK_WIDGET (ruler);
-
- gc = widget->style->fg_gc[GTK_STATE_NORMAL];
-
- layout = gtk_widget_create_pango_layout (widget, "012456789");
- pango_layout_get_extents (layout, &ink_rect, &logical_rect);
-
- digit_offset = ink_rect.y;
-
- ythickness = widget->style->ythickness;
-
- gtk_paint_box (widget->style, ruler->backing_store,
- GTK_STATE_NORMAL, GTK_SHADOW_NONE,
- NULL, widget, "custom_hruler", 0, 0, widget->allocation.width, widget->allocation.height);
-
- gdk_draw_line (ruler->backing_store, gc, 0, widget->allocation.height - 1,
- widget->allocation.width, widget->allocation.height - 1);
-
- if ((ruler->upper - ruler->lower) == 0) {
- return;
- }
-
- /* we have to assume a fixed width font here */
-
- max_chars = widget->allocation.width / 12; // XXX FIX ME: pixel with of the char `8'
-
- nmarks = ruler->metric->get_marks (&marks, ruler->lower, ruler->upper, max_chars);
-
- for (i = 0; i < nmarks; i++) {
- gint pos;
- gint height;
-
- pos = ROUND ((marks[i].position - ruler->lower) / ruler->metric->units_per_pixel);
- height = widget->allocation.height;
-
- switch (marks[i].style) {
- case GtkCustomRulerMarkMajor:
- gdk_draw_line (ruler->backing_store, gc, pos, height, pos, 0);
- break;
- case GtkCustomRulerMarkMinor:
- gdk_draw_line (ruler->backing_store, gc, pos, height, pos, height - (height/2));
- break;
- case GtkCustomRulerMarkMicro:
- gdk_draw_line (ruler->backing_store, gc, pos, height, pos, height - 3);
- break;
- }
-
- pango_layout_set_text (layout, marks[i].label, -1);
- pango_layout_get_extents (layout, &logical_rect, NULL);
-
- gtk_paint_layout (widget->style,
- ruler->backing_store,
- GTK_WIDGET_STATE (widget),
- FALSE,
- NULL,
- widget,
- "hruler",
- pos + 2, ythickness + PANGO_PIXELS (logical_rect.y - digit_offset),
- layout);
-
- g_free (marks[i].label);
- }
-
- if (nmarks) {
- g_free (marks);
- }
-
- g_object_unref (layout);
-}
-
-static void
-gtk_custom_hruler_draw_pos (GtkCustomRuler * ruler)
-{
- GtkWidget *widget;
- GdkGC *gc;
- int i;
- gint x, y;
- gint width, height;
- gint bs_width, bs_height;
- gint xthickness;
- gint ythickness;
- gfloat increment;
-
- g_return_if_fail (ruler != NULL);
- g_return_if_fail (GTK_IS_CUSTOM_HRULER (ruler));
- if (GTK_WIDGET_DRAWABLE (ruler) && (ruler->upper - ruler->lower) > 0) {
- widget = GTK_WIDGET (ruler);
- gc = widget->style->fg_gc[GTK_STATE_NORMAL];
- xthickness = widget->style->xthickness;
- ythickness = widget->style->ythickness;
- width = widget->allocation.width;
- height = widget->allocation.height - ythickness * 2;
-
- bs_width = height / 2;
- bs_width |= 1; /* make sure it's odd */
- bs_height = bs_width / 2 + 1;
-
- if ((bs_width > 0) && (bs_height > 0)) {
- /* If a backing store exists, restore the ruler */
- if (ruler->backing_store && ruler->non_gr_exp_gc)
- gdk_draw_drawable (ruler->widget.window,
- ruler->non_gr_exp_gc,
- GDK_DRAWABLE(ruler->backing_store), ruler->xsrc, ruler->ysrc, ruler->xsrc, ruler->ysrc, bs_width, bs_height);
-
- increment = (gfloat) width / (ruler->upper - ruler->lower);
- x = ROUND ((ruler->position - ruler->lower) * increment) + (xthickness - bs_width) / 2 - 1;
- y = (height + bs_height) / 2 + ythickness;
-
- for (i = 0; i < bs_height; i++)
- gdk_draw_line (widget->window, gc, x + i, y + i, x + bs_width - 1 - i, y + i);
-
-
- ruler->xsrc = x;
- ruler->ysrc = y;
- }
- }
-}
+++ /dev/null
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
- * file for a list of people on the GTK+ Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/* modified by andreas meyer <hexx3000@gmx.de> */
-
-#ifndef __GTK_CUSTOM_HRULER_H__
-#define __GTK_CUSTOM_HRULER_H__
-
-
-#include <gdk/gdk.h>
-#include "gtk-custom-ruler.h"
-
-
-G_BEGIN_DECLS
-
-
-#define GTK_CUSTOM_HRULER(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, gtk_custom_hruler_get_type (), GtkCustomHRuler)
-#define GTK_CUSTOM_HRULER_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, gtk_custom_hruler_get_type (), GtkCustomHRulerClass)
-#define GTK_IS_CUSTOM_HRULER(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, gtk_custom_hruler_get_type ())
-
-
-typedef struct _GtkCustomHRuler GtkCustomHRuler;
-typedef struct _GtkCustomHRulerClass GtkCustomHRulerClass;
-
-struct _GtkCustomHRuler
-{
- GtkCustomRuler ruler;
-};
-
-struct _GtkCustomHRulerClass
-{
- GtkCustomRulerClass parent_class;
-};
-
-
-GType gtk_custom_hruler_get_type (void);
-GtkWidget* gtk_custom_hruler_new (void);
-
-
-G_END_DECLS
-
-
-#endif /* __GTK_CUSTOM_HRULER_H__ */
+++ /dev/null
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
- * file for a list of people on the GTK+ Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/* modified by andreas meyer <hexx3000@gmx.de> */
-
-#include <stdio.h>
-#include "gettext.h"
-#define _(Text) dgettext (PACKAGE,Text)
-
-#include "gtk-custom-ruler.h"
-
-enum
-{
- PROP_0,
- PROP_LOWER,
- PROP_UPPER,
- PROP_POSITION,
- PROP_MAX_SIZE,
- PROP_SHOW_POSITION
-};
-
-static void gtk_custom_ruler_class_init (GtkCustomRulerClass * klass);
-static void gtk_custom_ruler_init (GtkCustomRuler * ruler);
-static void gtk_custom_ruler_realize (GtkWidget * widget);
-static void gtk_custom_ruler_unrealize (GtkWidget * widget);
-static void gtk_custom_ruler_size_allocate (GtkWidget * widget, GtkAllocation * allocation);
-static gint gtk_custom_ruler_expose (GtkWidget * widget, GdkEventExpose * event);
-static void gtk_custom_ruler_make_pixmap (GtkCustomRuler * ruler);
-static void gtk_custom_ruler_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec);
-static void gtk_custom_ruler_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec);
-
-
-static gint
-default_metric_get_marks (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
- (void) marks;
- (void) lower;
- (void) upper;
- (void) maxchars;
-
- return 0;
-}
-
-static const GtkCustomMetric default_metric = {
- 1.0,
- default_metric_get_marks
-};
-
-static GtkWidgetClass *parent_class;
-
-GType gtk_custom_ruler_get_type (void)
-{
- static GType ruler_type = 0;
-
- if (!ruler_type)
- {
- static const GTypeInfo ruler_info =
- {
- sizeof (GtkCustomRulerClass),
- (GBaseInitFunc) NULL, /* base_init */
- (GBaseFinalizeFunc) NULL, /* base_finalize */
- (GClassInitFunc) gtk_custom_ruler_class_init,
- (GClassFinalizeFunc) NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (GtkCustomRuler),
- 0, /* n_preallocs */
- (GInstanceInitFunc) gtk_custom_ruler_init,
- NULL /* value_table */
- };
-
- ruler_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkCustomRuler",
- &ruler_info, (GTypeFlags)0);
- }
-
- return ruler_type;
-}
-
-static void
-gtk_custom_ruler_class_init (GtkCustomRulerClass * class)
-{
- GObjectClass *gobject_class;
- GtkWidgetClass *widget_class;
-
- gobject_class = (GObjectClass *) class;
- widget_class = (GtkWidgetClass*) class;
-
- parent_class = g_type_class_peek_parent (class);
-
- gobject_class->set_property = gtk_custom_ruler_set_property;
- gobject_class->get_property = gtk_custom_ruler_get_property;
-
- widget_class->realize = gtk_custom_ruler_realize;
- widget_class->unrealize = gtk_custom_ruler_unrealize;
- widget_class->size_allocate = gtk_custom_ruler_size_allocate;
- widget_class->expose_event = gtk_custom_ruler_expose;
-
- class->draw_ticks = NULL;
- class->draw_pos = NULL;
-
- g_object_class_install_property (gobject_class,
- PROP_LOWER,
- g_param_spec_double ("lower",
- _("Lower"),
- _("Lower limit of ruler"),
- -G_MAXDOUBLE,
- G_MAXDOUBLE,
- 0.0,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class,
- PROP_UPPER,
- g_param_spec_double ("upper",
- _("Upper"),
- _("Upper limit of ruler"),
- -G_MAXDOUBLE,
- G_MAXDOUBLE,
- 0.0,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class,
- PROP_POSITION,
- g_param_spec_double ("position",
- _("Position"),
- _("Position of mark on the ruler"),
- -G_MAXDOUBLE,
- G_MAXDOUBLE,
- 0.0,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class,
- PROP_MAX_SIZE,
- g_param_spec_double ("max_size",
- _("Max Size"),
- _("Maximum size of the ruler"),
- -G_MAXDOUBLE,
- G_MAXDOUBLE,
- 0.0,
- G_PARAM_READWRITE));
-
- g_object_class_install_property (gobject_class,
- PROP_SHOW_POSITION,
- g_param_spec_boolean ("show_position",
- _("Show Position"),
- _("Draw current ruler position"),
- TRUE,
- G_PARAM_READWRITE));
-}
-
-static void
-gtk_custom_ruler_init (GtkCustomRuler * ruler)
-{
- ruler->backing_store = NULL;
- ruler->non_gr_exp_gc = NULL;
- ruler->xsrc = 0;
- ruler->ysrc = 0;
- ruler->slider_size = 0;
- ruler->lower = 0;
- ruler->upper = 0;
- ruler->position = 0;
- ruler->max_size = 0;
- ruler->show_position = FALSE;
-
- gtk_custom_ruler_set_metric (ruler, NULL);
-}
-
-static void
-gtk_custom_ruler_set_property (GObject *object,
- guint prop_id,
- const GValue *value,
- GParamSpec *pspec)
-{
- GtkCustomRuler *ruler = GTK_CUSTOM_RULER (object);
- (void) pspec;
-
- switch (prop_id)
- {
- case PROP_LOWER:
- gtk_custom_ruler_set_range (ruler, g_value_get_double (value), ruler->upper,
- ruler->position, ruler->max_size);
- break;
- case PROP_UPPER:
- gtk_custom_ruler_set_range (ruler, ruler->lower, g_value_get_double (value),
- ruler->position, ruler->max_size);
- break;
- case PROP_POSITION:
- gtk_custom_ruler_set_range (ruler, ruler->lower, ruler->upper,
- g_value_get_double (value), ruler->max_size);
- break;
- case PROP_MAX_SIZE:
- gtk_custom_ruler_set_range (ruler, ruler->lower, ruler->upper,
- ruler->position, g_value_get_double (value));
- break;
- case PROP_SHOW_POSITION:
- gtk_custom_ruler_set_show_position (ruler, g_value_get_boolean (value));
- break;
- }
-}
-
-static void
-gtk_custom_ruler_get_property (GObject *object,
- guint prop_id,
- GValue *value,
- GParamSpec *pspec)
-{
- GtkCustomRuler *ruler = GTK_CUSTOM_RULER (object);
-
- switch (prop_id)
- {
- case PROP_LOWER:
- g_value_set_double (value, ruler->lower);
- break;
- case PROP_UPPER:
- g_value_set_double (value, ruler->upper);
- break;
- case PROP_POSITION:
- g_value_set_double (value, ruler->position);
- break;
- case PROP_MAX_SIZE:
- g_value_set_double (value, ruler->max_size);
- break;
- case PROP_SHOW_POSITION:
- g_value_set_boolean (value, ruler->show_position);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
- }
-}
-
-void
-gtk_custom_ruler_set_metric (GtkCustomRuler * ruler, GtkCustomMetric * metric)
-{
- g_return_if_fail (ruler != NULL);
- g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
- if (metric == NULL)
- ruler->metric = (GtkCustomMetric *) & default_metric;
- else
- ruler->metric = metric;
-
- if (GTK_WIDGET_DRAWABLE (ruler))
- gtk_widget_queue_draw (GTK_WIDGET (ruler));
-}
-
-void
-gtk_custom_ruler_set_range (GtkCustomRuler *ruler,
- gdouble lower,
- gdouble upper,
- gdouble position,
- gdouble max_size)
-{
- g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
- g_object_freeze_notify (G_OBJECT (ruler));
- if (ruler->lower != lower)
- {
- ruler->lower = lower;
- g_object_notify (G_OBJECT (ruler), "lower");
- }
- if (ruler->upper != upper)
- {
- ruler->upper = upper;
- g_object_notify (G_OBJECT (ruler), "upper");
- }
- if (ruler->position != position)
- {
- ruler->position = position;
- g_object_notify (G_OBJECT (ruler), "position");
- }
- if (ruler->max_size != max_size)
- {
- ruler->max_size = max_size;
- g_object_notify (G_OBJECT (ruler), "max-size");
- }
- g_object_thaw_notify (G_OBJECT (ruler));
-
- if (GTK_WIDGET_DRAWABLE (ruler))
- gtk_widget_queue_draw (GTK_WIDGET (ruler));
-}
-
-/**
- * gtk_custom_ruler_get_range:
- * @ruler: a #GtkCustomRuler
- * @lower: location to store lower limit of the ruler, or %NULL
- * @upper: location to store upper limit of the ruler, or %NULL
- * @position: location to store the current position of the mark on the ruler, or %NULL
- * @max_size: location to store the maximum size of the ruler used when calculating
- * the space to leave for the text, or %NULL.
- *
- * Retrieves values indicating the range and current position of a #GtkCustomRuler.
- * See gtk_custom_ruler_set_range().
- **/
-void
-gtk_custom_ruler_get_range (GtkCustomRuler *ruler,
- gdouble *lower,
- gdouble *upper,
- gdouble *position,
- gdouble *max_size)
-{
- g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
- if (lower)
- *lower = ruler->lower;
- if (upper)
- *upper = ruler->upper;
- if (position)
- *position = ruler->position;
- if (max_size)
- *max_size = ruler->max_size;
-}
-
-void
-gtk_custom_ruler_draw_ticks (GtkCustomRuler * ruler)
-{
- g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
- if (GTK_CUSTOM_RULER_GET_CLASS (ruler)->draw_ticks)
- GTK_CUSTOM_RULER_GET_CLASS (ruler)->draw_ticks (ruler);
-
-}
-
-void
-gtk_custom_ruler_draw_pos (GtkCustomRuler * ruler)
-{
- g_return_if_fail (GTK_IS_CUSTOM_RULER (ruler));
-
- if (GTK_CUSTOM_RULER_GET_CLASS (ruler)->draw_pos && ruler->show_position)
- GTK_CUSTOM_RULER_GET_CLASS (ruler)->draw_pos (ruler);
-}
-
-static void
-gtk_custom_ruler_realize (GtkWidget * widget)
-{
- GtkCustomRuler *ruler;
- GdkWindowAttr attributes;
- gint attributes_mask;
-
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_CUSTOM_RULER (widget));
-
- ruler = GTK_CUSTOM_RULER (widget);
- GTK_WIDGET_SET_FLAGS (ruler, GTK_REALIZED);
-
- attributes.window_type = GDK_WINDOW_CHILD;
- attributes.x = widget->allocation.x;
- attributes.y = widget->allocation.y;
- attributes.width = widget->allocation.width;
- attributes.height = widget->allocation.height;
- attributes.wclass = GDK_INPUT_OUTPUT;
- attributes.visual = gtk_widget_get_visual (widget);
- attributes.colormap = gtk_widget_get_colormap (widget);
- attributes.event_mask = gtk_widget_get_events (widget);
- attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
-
- attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-
- widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
- gdk_window_set_user_data (widget->window, ruler);
-
- widget->style = gtk_style_attach (widget->style, widget->window);
- gtk_style_set_background (widget->style, widget->window, GTK_STATE_ACTIVE);
-
- gtk_custom_ruler_make_pixmap (ruler);
-}
-
-static void
-gtk_custom_ruler_unrealize (GtkWidget *widget)
-{
- GtkCustomRuler *ruler = GTK_CUSTOM_RULER (widget);
-
- if (ruler->backing_store)
- g_object_unref (ruler->backing_store);
- if (ruler->non_gr_exp_gc)
- g_object_unref (ruler->non_gr_exp_gc);
-
- ruler->backing_store = NULL;
- ruler->non_gr_exp_gc = NULL;
-
- if (GTK_WIDGET_CLASS (parent_class)->unrealize)
- (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
-}
-
-static void
-gtk_custom_ruler_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation)
-{
- GtkCustomRuler *ruler = GTK_CUSTOM_RULER (widget);
-
- widget->allocation = *allocation;
-
- if (GTK_WIDGET_REALIZED (widget))
- {
- gdk_window_move_resize (widget->window,
- allocation->x, allocation->y,
- allocation->width, allocation->height);
-
- gtk_custom_ruler_make_pixmap (ruler);
- }
-}
-
-static gint
-gtk_custom_ruler_expose (GtkWidget * widget, GdkEventExpose * event)
-{
- GtkCustomRuler *ruler;
-
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_CUSTOM_RULER (widget), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
-
- if (GTK_WIDGET_DRAWABLE (widget)) {
- ruler = GTK_CUSTOM_RULER (widget);
-
- gtk_custom_ruler_draw_ticks (ruler);
-
- gdk_draw_drawable (widget->window,
- ruler->non_gr_exp_gc,
- GDK_DRAWABLE(ruler->backing_store), 0, 0, 0, 0, widget->allocation.width, widget->allocation.height);
-
- gtk_custom_ruler_draw_pos (ruler);
- }
-
- return FALSE;
-}
-
-
-static void
-gtk_custom_ruler_make_pixmap (GtkCustomRuler *ruler)
-{
- GtkWidget *widget;
- gint width;
- gint height;
-
- widget = GTK_WIDGET (ruler);
-
- if (ruler->backing_store)
- {
- gdk_drawable_get_size (ruler->backing_store, &width, &height);
- if ((width == widget->allocation.width) &&
- (height == widget->allocation.height))
- return;
-
- g_object_unref (ruler->backing_store);
- }
-
- ruler->backing_store = gdk_pixmap_new (widget->window,
- widget->allocation.width,
- widget->allocation.height,
- -1);
-
- ruler->xsrc = 0;
- ruler->ysrc = 0;
-
- if (!ruler->non_gr_exp_gc)
- {
- ruler->non_gr_exp_gc = gdk_gc_new (widget->window);
- gdk_gc_set_exposures (ruler->non_gr_exp_gc, FALSE);
- }
-}
-
-void
-gtk_custom_ruler_set_show_position (GtkCustomRuler * ruler, gboolean yn)
-{
- ruler->show_position = yn;
-}
+++ /dev/null
-/* GTK - The GIMP Toolkit
- * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the
- * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
- */
-
-/*
- * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
- * file for a list of people on the GTK+ Team. See the ChangeLog
- * files for a list of changes. These files are distributed with
- * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
- */
-
-/* modified by andreas meyer <hexx3000@gmx.de> */
-
-#ifndef __GTK_CUSTOM_RULER_H__
-#define __GTK_CUSTOM_RULER_H__
-
-#include <gdk/gdk.h>
-#include <gtk/gtkwidget.h>
-
-
-G_BEGIN_DECLS
-
-#define GTK_TYPE_CUSTOM_RULER (gtk_custom_ruler_get_type ())
-#define GTK_CUSTOM_RULER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CUSTOM_RULER, GtkCustomRuler))
-#define GTK_CUSTOM_RULER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CUSTOM_RULER, GtkCustomRulerClass))
-#define GTK_IS_CUSTOM_RULER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CUSTOM_RULER))
-#define GTK_IS_CUSTOM_RULER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CUSTOM_RULER))
-#define GTK_CUSTOM_RULER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CUSTOM_RULER, GtkCustomRulerClass))
-
-typedef struct _GtkCustomRuler GtkCustomRuler;
-typedef struct _GtkCustomRulerClass GtkCustomRulerClass;
-typedef struct _GtkCustomMetric GtkCustomMetric;
-typedef struct _GtkCustomRulerMark GtkCustomRulerMark;
-
-struct _GtkCustomRuler {
- GtkWidget widget;
-
- GdkPixmap *backing_store;
- GdkGC *non_gr_exp_gc;
- GtkCustomMetric *metric;
- gint xsrc, ysrc;
- gint slider_size;
- gboolean show_position;
-
- /* The upper limit of the ruler (in points) */
- gdouble lower;
- /* The lower limit of the ruler */
- gdouble upper;
- /* The position of the mark on the ruler */
- gdouble position;
- /* The maximum size of the ruler */
- gdouble max_size;
-};
-
-struct _GtkCustomRulerClass {
- GtkWidgetClass parent_class;
-
- void (* draw_ticks) (GtkCustomRuler *ruler);
- void (* draw_pos) (GtkCustomRuler *ruler);
-};
-
-typedef enum {
- GtkCustomRulerMarkMajor,
- GtkCustomRulerMarkMinor,
- GtkCustomRulerMarkMicro
-} GtkCustomRulerMarkStyle;
-
-struct _GtkCustomRulerMark {
- gchar *label;
- gdouble position;
- GtkCustomRulerMarkStyle style;
-};
-
-struct _GtkCustomMetric {
- gfloat units_per_pixel;
- gint (* get_marks) (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars);
-};
-
-GType gtk_custom_ruler_get_type (void);
-void gtk_custom_ruler_set_metric (GtkCustomRuler *ruler, GtkCustomMetric *metric);
-void gtk_custom_ruler_set_range (GtkCustomRuler *ruler,
- gdouble lower,
- gdouble upper,
- gdouble position,
- gdouble max_size);
-void gtk_custom_ruler_draw_ticks (GtkCustomRuler *ruler);
-void gtk_custom_ruler_draw_pos (GtkCustomRuler *ruler);
-void gtk_custom_ruler_set_show_position (GtkCustomRuler *rule, gboolean yn);
-
-G_END_DECLS
-
-#endif /* __GTK_CUSTOM_RULER_H__ */
#include "midi_region_view.h"
#include "public_editor.h"
-#include "utils.h"
#include "hit.h"
using namespace ARDOUR;
using namespace ArdourCanvas;
-Hit::Hit (MidiRegionView& region, Group* group, double size, const boost::shared_ptr<NoteType> note, bool with_events)
+Hit::Hit (MidiRegionView& region, Item* parent, double size, const boost::shared_ptr<NoteType> note, bool with_events)
: NoteBase (region, with_events, note)
{
- _polygon = new ArdourCanvas::Polygon (group);
+ _polygon = new ArdourCanvas::Polygon (parent);
CANVAS_DEBUG_NAME (_polygon, "note");
set_item (_polygon);
set_height (size);
typedef Evoral::Note<double> NoteType;
Hit (MidiRegionView& region,
- ArdourCanvas::Group* group,
+ ArdourCanvas::Item* parent,
double size,
const boost::shared_ptr<NoteType> note = boost::shared_ptr<NoteType>(),
bool with_events = true);
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace Gtk;
IOSelector::IOSelector (Gtk::Window* p, ARDOUR::Session* session, boost::shared_ptr<ARDOUR::IO> io)
/* not absolute - look in the usual places */
std::string keybindings_file;
- if ( ! find_file_in_search_path (ardour_config_search_path(), keybindings_path, keybindings_file)) {
+ if ( ! find_file (ardour_config_search_path(), keybindings_path, keybindings_file)) {
if (keybindings_path == default_bindings) {
error << string_compose (_("Default keybindings not found - %1 will be hard to use!"), PROGRAM_NAME) << endmsg;
#include "actions.h"
#include "keyboard.h"
#include "keyeditor.h"
-#include "utils.h"
#include "i18n.h"
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtkmm2ext;
using namespace Gtk;
VSTState* vstfx;
int LXVST_sched_timer_interval = 40; //ms, 25fps
XEvent event;
- struct timeval clock1, clock2;
+ uint64_t clock1, clock2;
- gettimeofday(&clock1, NULL);
+ clock1 = g_get_monotonic_time();
/*The 'Forever' loop - runs the plugin UIs etc - based on the FST gui event loop*/
while (!gui_quit)
/*See if its time for us to do a scheduled event pass on all the plugins*/
- gettimeofday(&clock2, NULL);
- const int elapsed_time = (clock2.tv_sec - clock1.tv_sec) * 1000 + (clock2.tv_usec - clock1.tv_usec) / 1000;
+ clock2 = g_get_monotonic_time();
+ const int64_t elapsed_time_ms = (clock2 - clock1) / 1000;
- if((LXVST_sched_timer_interval != 0) && elapsed_time >= LXVST_sched_timer_interval)
+ if((LXVST_sched_timer_interval != 0) && elapsed_time_ms >= LXVST_sched_timer_interval)
{
- //printf("elapsed %d ms ^= %.2f Hz\n", elapsed_time, 1000.0/(double)elapsed_time); // DEBUG
+ //printf("elapsed %d ms ^= %.2f Hz\n", elapsed_time_ms, 1000.0/(double)elapsed_time_ms); // DEBUG
pthread_mutex_lock (&plugin_mutex);
again:
}
pthread_mutex_unlock (&plugin_mutex);
- gettimeofday(&clock1, NULL);
+ clock1 = g_get_monotonic_time();
}
}
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
set_location (loc);
set_number (num);
+ cd_toggled(); // show/hide cd-track details
}
LocationEditRow::~LocationEditRow()
location->set_cd (cd_check_button.get_active(), this);
- if (location->is_cd_marker() && !(location->is_mark())) {
+ if (location->is_cd_marker()) {
show_cd_track_details ();
#include <gtkmm2ext/utils.h>
#include "version.h"
-#include "utils.h"
#include "ardour_ui.h"
#include "opts.h"
#include "enums.h"
#include "ardour/tempo.h"
#include "canvas/rectangle.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
#include "canvas/line.h"
#include "canvas/polygon.h"
#include "canvas/text.h"
#include "canvas/canvas.h"
+#include "canvas/scroll_group.h"
#include "canvas/debug.h"
#include "ardour_ui.h"
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace Gtkmm2ext;
PBD::Signal1<void,Marker*> Marker::CatchDeletion;
static const double marker_height = 13.0;
-Marker::Marker (PublicEditor& ed, ArdourCanvas::Group& parent, guint32 rgba, const string& annotation,
+Marker::Marker (PublicEditor& ed, ArdourCanvas::Container& parent, guint32 rgba, const string& annotation,
Type type, framepos_t frame, bool handle_events)
: editor (ed)
, _parent (&parent)
- , _time_bars_line (0)
, _track_canvas_line (0)
, _type (type)
, _selected (false)
unit_position = editor.sample_to_pixel (frame);
unit_position -= _shift;
- group = new ArdourCanvas::Group (&parent, ArdourCanvas::Duple (unit_position, 0));
+ group = new ArdourCanvas::Container (&parent, ArdourCanvas::Duple (unit_position, 0));
#ifdef CANVAS_DEBUG
group->name = string_compose ("Marker::group for %1", annotation);
#endif
/* destroying the parent group destroys its contents, namely any polygons etc. that we added */
delete group;
- delete _time_bars_line;
delete _track_canvas_line;
}
-void Marker::reparent(ArdourCanvas::Group & parent)
+void Marker::reparent(ArdourCanvas::Container & parent)
{
group->reparent (&parent);
_parent = &parent;
{
if (_shown && (_selected || _line_shown)) {
- if (_time_bars_line == 0) {
+ if (_track_canvas_line == 0) {
- _time_bars_line = new ArdourCanvas::Line (editor.get_time_bars_group());
- _time_bars_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EditPoint());
- _time_bars_line->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
-
- _track_canvas_line = new ArdourCanvas::Line (editor.get_track_canvas_group());
+ _track_canvas_line = new ArdourCanvas::Line (editor.get_hscroll_group());
_track_canvas_line->set_outline_color (ARDOUR_UI::config()->get_canvasvar_EditPoint());
_track_canvas_line->Event.connect (sigc::bind (sigc::mem_fun (editor, &PublicEditor::canvas_marker_event), group, this));
}
- ArdourCanvas::Duple g = group->item_to_canvas (ArdourCanvas::Duple (0, 0));
- ArdourCanvas::Duple d = _time_bars_line->canvas_to_item (ArdourCanvas::Duple (g.x + _shift, 0));
-
- _time_bars_line->set_x0 (d.x);
- _time_bars_line->set_x1 (d.x);
- _time_bars_line->set_y0 (d.y);
- _time_bars_line->set_y1 (ArdourCanvas::COORD_MAX);
- _time_bars_line->set_outline_color (_selected ? ARDOUR_UI::config()->get_canvasvar_EditPoint() : _color);
- _time_bars_line->raise_to_top ();
- _time_bars_line->show ();
+ ArdourCanvas::Duple g = group->canvas_origin();
+ ArdourCanvas::Duple d = _track_canvas_line->canvas_to_item (ArdourCanvas::Duple (g.x + _shift, 0));
- d = _track_canvas_line->canvas_to_item (ArdourCanvas::Duple (g.x + _shift, 0));
_track_canvas_line->set_x0 (d.x);
_track_canvas_line->set_x1 (d.x);
_track_canvas_line->set_y0 (d.y);
_track_canvas_line->show ();
} else {
- if (_time_bars_line) {
- _time_bars_line->hide ();
+ if (_track_canvas_line) {
_track_canvas_line->hide ();
}
}
mark->set_fill_color (_color);
mark->set_outline_color (_color);
- if (_time_bars_line && !_selected) {
- _time_bars_line->set_outline_color (_color);
+ if (_track_canvas_line && !_selected) {
_track_canvas_line->set_outline_color (_color);
}
/***********************************************************************/
-TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
+TempoMarker::TempoMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
ARDOUR::TempoSection& temp)
: Marker (editor, parent, rgba, text, Tempo, 0, false),
_tempo (temp)
/***********************************************************************/
-MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Group& parent, guint32 rgba, const string& text,
+MeterMarker::MeterMarker (PublicEditor& editor, ArdourCanvas::Container& parent, guint32 rgba, const string& text,
ARDOUR::MeterSection& m)
: Marker (editor, parent, rgba, text, Meter, 0, false),
_meter (m)
};
- Marker (PublicEditor& editor, ArdourCanvas::Group &, guint32 rgba, const std::string& text, Type,
+ Marker (PublicEditor& editor, ArdourCanvas::Container &, guint32 rgba, const std::string& text, Type,
framepos_t frame = 0, bool handle_events = true);
virtual ~Marker ();
framepos_t position() const { return frame_position; }
- ArdourCanvas::Group * get_parent() { return _parent; }
- void reparent (ArdourCanvas::Group & parent);
+ ArdourCanvas::Container * get_parent() { return _parent; }
+ void reparent (ArdourCanvas::Container & parent);
void hide ();
void show ();
Pango::FontDescription name_font;
- ArdourCanvas::Group* _parent;
- ArdourCanvas::Group *group;
+ ArdourCanvas::Container* _parent;
+ ArdourCanvas::Container *group;
ArdourCanvas::Polygon *mark;
ArdourCanvas::Text *_name_item;
ArdourCanvas::Points *points;
- ArdourCanvas::Line* _time_bars_line;
ArdourCanvas::Line* _track_canvas_line;
ArdourCanvas::Rectangle* _name_background;
class TempoMarker : public Marker
{
public:
- TempoMarker (PublicEditor& editor, ArdourCanvas::Group &, guint32 rgba, const std::string& text, ARDOUR::TempoSection&);
+ TempoMarker (PublicEditor& editor, ArdourCanvas::Container &, guint32 rgba, const std::string& text, ARDOUR::TempoSection&);
~TempoMarker ();
ARDOUR::TempoSection& tempo() const { return _tempo; }
class MeterMarker : public Marker
{
public:
- MeterMarker (PublicEditor& editor, ArdourCanvas::Group &, guint32 rgba, const std::string& text, ARDOUR::MeterSection&);
+ MeterMarker (PublicEditor& editor, ArdourCanvas::Container &, guint32 rgba, const std::string& text, ARDOUR::MeterSection&);
~MeterMarker ();
ARDOUR::MeterSection& meter() const { return _meter; }
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
Pango::AttrFontDesc* font_attr;
Pango::FontDescription font;
- font = Pango::FontDescription ("ArdourMono");
+ font = Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_SmallMonospaceFont());
double fixfontsize = 81920.0 / (double) ARDOUR::Config->get_font_scale();
font.set_weight (Pango::WEIGHT_NORMAL);
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
MeterStrip::MeterStrip (Session* sess, boost::shared_ptr<ARDOUR::Route> rt)
: AxisView(sess)
- , RouteUI(sess)
+ , RouteUI(0)
, _route(rt)
, peak_display()
{
mtr_vbox.set_spacing(2);
nfo_vbox.set_spacing(2);
- RouteUI::set_route (rt);
SessionHandlePtr::set_session (sess);
+ RouteUI::init ();
+ RouteUI::set_route (rt);
_has_midi = false;
_tick_bar = 0;
peakbx.pack_start(peak_align, true, true, 3);
peakbx.set_size_request(-1, 14);
- // add track-name label
- name_label.set_text(_route->name());
+ // add track-name & -number label
+ number_label.set_text("-");
+ number_label.set_size_request(18, 18);
+
+ name_changed();
+
name_label.set_corner_radius(2);
name_label.set_name("meterbridge label");
name_label.set_angle(-90.0);
ARDOUR_UI::instance()->set_tip (name_label, _route->name());
ARDOUR_UI::instance()->set_tip (*level_meter, _route->name());
+ number_label.set_corner_radius(2);
+ number_label.set_name("tracknumber label");
+ number_label.set_angle(-90.0);
+ number_label.layout()->set_width(18 * PANGO_SCALE);
+ number_label.set_alignment(.5, .5);
+
namebx.set_size_request(18, 52);
- namebx.pack_start(name_label, true, false, 3);
+ namebx.pack_start(namenumberbx, true, false, 0);
+ namenumberbx.pack_start(name_label, true, false, 0);
+ namenumberbx.pack_start(number_label, false, false, 0);
mon_in_box.pack_start(*monitor_input_button, true, false);
btnbox.pack_start(mon_in_box, false, false, 1);
rec_enable_button->set_corner_radius(2);
rec_enable_button->set_size_request(16, 16);
+ rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
mute_button->set_corner_radius(2);
mute_button->set_size_request(16, 16);
mtr_container.show();
mtr_hsep.show();
nfo_vbox.show();
- monitor_input_button->show();
- monitor_disk_button->show();
+ namenumberbx.show();
+
+ if (boost::dynamic_pointer_cast<Track>(_route)) {
+ monitor_input_button->show();
+ monitor_disk_button->show();
+ } else {
+ monitor_input_button->hide();
+ monitor_disk_button->hide();
+ }
_route->shared_peak_meter()->ConfigurationChanged.connect (
- route_connections, invalidator (*this), boost::bind (&MeterStrip::meter_configuration_changed, this, _1), gui_context()
+ meter_route_connections, invalidator (*this), boost::bind (&MeterStrip::meter_configuration_changed, this, _1), gui_context()
);
ResetAllPeakDisplays.connect (sigc::mem_fun(*this, &MeterStrip::reset_peak_display));
meter_ticks1_area.signal_expose_event().connect (sigc::mem_fun(*this, &MeterStrip::meter_ticks1_expose));
meter_ticks2_area.signal_expose_event().connect (sigc::mem_fun(*this, &MeterStrip::meter_ticks2_expose));
- _route->DropReferences.connect (route_connections, invalidator (*this), boost::bind (&MeterStrip::self_delete, this), gui_context());
- _route->PropertyChanged.connect (route_connections, invalidator (*this), boost::bind (&MeterStrip::strip_property_changed, this, _1), gui_context());
+ _route->DropReferences.connect (meter_route_connections, invalidator (*this), boost::bind (&MeterStrip::self_delete, this), gui_context());
+ _route->PropertyChanged.connect (meter_route_connections, invalidator (*this), boost::bind (&MeterStrip::strip_property_changed, this, _1), gui_context());
peak_display.signal_button_release_event().connect (sigc::mem_fun(*this, &MeterStrip::peak_button_release), false);
name_label.signal_button_release_event().connect (sigc::mem_fun(*this, &MeterStrip::name_label_button_release), false);
{
mute_button->set_text (_("M"));
rec_enable_button->set_text ("");
- rec_enable_button->set_image (::get_icon (X_("record_normal_red")));
if (_route && _route->solo_safe()) {
solo_button->set_visual_state (Gtkmm2ext::VisualState (solo_button->visual_state() | Gtkmm2ext::Insensitive));
if (!what_changed.contains (ARDOUR::Properties::name)) {
return;
}
- ENSURE_GUI_THREAD (*this, &MeterStrip::strip_name_changed, what_changed)
- name_label.set_text(_route->name());
+ ENSURE_GUI_THREAD (*this, &MeterStrip::strip_name_changed, what_changed);
+ name_changed();
ARDOUR_UI::instance()->set_tip (name_label, _route->name());
if (level_meter) {
ARDOUR_UI::instance()->set_tip (*level_meter, _route->name());
nh = 148;
break;
}
- namebx.set_size_request(18, nh);
+ int tnh = 0;
+ if (_session && _session->config.get_track_name_number()) {
+ tnh = 4 + _session->track_number_decimals() * 8;
+ }
+ namebx.set_size_request(18, nh + tnh);
+ namenumberbx.set_size_request(18, nh + tnh);
if (_route) {
- name_label.set_size_request(18, nh-2);
- name_label.layout()->set_width((nh-4) * PANGO_SCALE);
+ name_label.set_size_request(18, nh + (_route->is_master() ? tnh : -1));
+ name_label.layout()->set_width((nh - 4 + (_route->is_master() ? tnh : 0)) * PANGO_SCALE);
}
VBox::on_size_allocate(a);
}
else if (p == "meterbridge-label-height") {
queue_resize();
}
+ else if (p == "track-name-number") {
+ name_changed();
+ queue_resize();
+ }
+}
+
+void
+MeterStrip::name_changed () {
+ if (!_route) {
+ return;
+ }
+ name_label.set_text(_route->name ());
+ if (_session && _session->config.get_track_name_number()) {
+ const int64_t track_number = _route->track_number ();
+ if (track_number == 0) {
+ number_label.set_text("-");
+ number_label.hide();
+ } else {
+ number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
+ number_label.show();
+ }
+ number_label.set_size_request(18, 4 + _session->track_number_decimals() * 8);
+ } else {
+ number_label.hide();
+ }
}
bool
protected:
boost::shared_ptr<ARDOUR::Route> _route;
- PBD::ScopedConnectionList route_connections;
+ PBD::ScopedConnectionList meter_route_connections;
PBD::ScopedConnectionList level_meter_connection;
void self_delete ();
Gtk::HBox meterbox;
Gtk::HBox spacer;
Gtk::HBox namebx;
+ Gtk::VBox namenumberbx;
ArdourButton name_label;
+ ArdourButton number_label;
Gtk::DrawingArea meter_metric_area;
Gtk::DrawingArea meter_ticks1_area;
Gtk::DrawingArea meter_ticks2_area;
LevelMeterHBox *level_meter;
- PBD::ScopedConnection _config_connection;
void strip_property_changed (const PBD::PropertyChange&);
void meter_configuration_changed (ARDOUR::ChanCount);
void meter_type_changed (ARDOUR::MeterType);
void redraw_metrics ();
void update_button_box ();
void update_name_box ();
+ void name_changed ();
bool _suspend_menu_callbacks;
bool level_meter_button_release (GdkEventButton* ev);
#include "ardour/audio_track.h"
#include "ardour/midi_track.h"
+#include "ardour/route_sorters.h"
#include "meterbridge.h"
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Glib;
return _instance;
}
-/* copy from gtk2_ardour/mixer_ui.cc -- TODO consolidate
- * used by Meterbridge::set_session() below
- */
-struct SignalOrderRouteSorter {
- bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
- if (a->is_master() || a->is_monitor()) {
- /* "a" is a special route (master, monitor, etc), and comes
- * last in the mixer ordering
- */
- return false;
- } else if (b->is_master() || b->is_monitor()) {
- /* everything comes before b */
- return true;
- }
- return a->order_key () < b->order_key ();
- }
-};
-
Meterbridge::Meterbridge ()
: Window (Gtk::WINDOW_TOPLEVEL)
, VisibilityTracker (*((Gtk::Window*) this))
_show_master = _session->config.get_show_master_on_meterbridge();
_show_midi = _session->config.get_show_midi_on_meterbridge();
- SignalOrderRouteSorter sorter;
+ ARDOUR::SignalOrderRouteSorter sorter;
boost::shared_ptr<RouteList> routes = _session->get_routes();
RouteList copy(*routes);
else if (p == "meterbridge-label-height") {
scroller.queue_resize();
}
+ else if (p == "show-monitor-on-meterbridge") {
+ scroller.queue_resize();
+ }
+ else if (p == "track-name-number") {
+ scroller.queue_resize();
+ }
}
void
MidiAutomationLine::MidiAutomationLine (
const std::string& name,
TimeAxisView& tav,
- ArdourCanvas::Group& group,
+ ArdourCanvas::Item& parent,
boost::shared_ptr<ARDOUR::AutomationList> list,
boost::shared_ptr<ARDOUR::MidiRegion> region,
Evoral::Parameter parameter,
Evoral::TimeConverter<double, ARDOUR::framepos_t>* converter)
- : AutomationLine (name, tav, group, list, converter)
+ : AutomationLine (name, tav, parent, list, converter)
, _region (region)
, _parameter (parameter)
{
class MidiAutomationLine : public AutomationLine
{
public:
- MidiAutomationLine (const std::string&, TimeAxisView&, ArdourCanvas::Group&,
+ MidiAutomationLine (const std::string&, TimeAxisView&, ArdourCanvas::Item&,
boost::shared_ptr<ARDOUR::AutomationList>,
boost::shared_ptr<ARDOUR::MidiRegion>,
Evoral::Parameter,
#include "evoral/midi_util.h"
#include "canvas/debug.h"
+#include "canvas/text.h"
#include "automation_region_view.h"
#include "automation_time_axis.h"
#include "rgb_macros.h"
#include "selection.h"
#include "streamview.h"
-#include "utils.h"
#include "patch_change_dialog.h"
#include "verbose_cursor.h"
#include "ardour_ui.h"
#define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
-MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
- boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
+MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
+ boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color)
: RegionView (parent, tv, r, spu, basic_color)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
- , _note_group (new ArdourCanvas::Group (group))
+ , _note_group (new ArdourCanvas::Container (group))
, _note_diff_command (0)
, _ghost_note(0)
, _step_edit_cursor (0)
SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
}
-MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
- boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
+MidiRegionView::MidiRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
+ boost::shared_ptr<MidiRegion> r, double spu, uint32_t basic_color,
TimeAxisViewItem::Visibility visibility)
: RegionView (parent, tv, r, spu, basic_color, false, visibility)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
- , _note_group (new ArdourCanvas::Group (parent))
+ , _note_group (new ArdourCanvas::Container (parent))
, _note_diff_command (0)
, _ghost_note(0)
, _step_edit_cursor (0)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
- , _note_group (new ArdourCanvas::Group (get_canvas_group()))
+ , _note_group (new ArdourCanvas::Container (get_canvas_group()))
, _note_diff_command (0)
, _ghost_note(0)
, _step_edit_cursor (0)
, pre_press_cursor (0)
, _note_player (0)
{
- Gdk::Color c;
- int r,g,b,a;
-
- UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
- c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
- init (c, false);
+ init (false);
}
MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
, _current_range_min(0)
, _current_range_max(0)
, _active_notes(0)
- , _note_group (new ArdourCanvas::Group (get_canvas_group()))
+ , _note_group (new ArdourCanvas::Container (get_canvas_group()))
, _note_diff_command (0)
, _ghost_note(0)
, _step_edit_cursor (0)
, pre_press_cursor (0)
, _note_player (0)
{
- Gdk::Color c;
- int r,g,b,a;
-
- UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
- c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
- init (c, true);
+ init (true);
}
void
-MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
+MidiRegionView::init (bool wfd)
{
PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
_model = midi_region()->midi_source(0)->model();
_enable_display = false;
- RegionView::init (basic_color, false);
-
- compute_colors (basic_color);
+ RegionView::init (false);
set_height (trackview.current_height());
MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
{
if (_step_edit_cursor == 0) {
- ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
+ ArdourCanvas::Item* const group = get_canvas_group();
_step_edit_cursor = new ArdourCanvas::Rectangle (group);
_step_edit_cursor->set_y0 (0);
/* Reparent the note group to the region view's parent, so that it doesn't change
when the region view is trimmed.
*/
- _temporary_note_group = new ArdourCanvas::Group (group->parent ());
+ _temporary_note_group = new ArdourCanvas::Container (group->parent ());
_temporary_note_group->move (group->position ());
_note_group->reparent (_temporary_note_group);
}
void
MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
{
- double wx, wy;
-
- trackview.editor().verbose_cursor()->set_text (text);
- trackview.editor().get_pointer_position (wx, wy);
-
- wx += xoffset;
- wy += yoffset;
-
- /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
-
- boost::optional<ArdourCanvas::Rect> bbo = trackview.editor().verbose_cursor()->item().bounding_box();
-
- assert (bbo);
-
- ArdourCanvas::Rect bb = bbo.get();
-
- if ((wy + bb.y1 - bb.y0) > trackview.editor().visible_canvas_height()) {
- wy -= (bb.y1 - bb.y0) + 2 * yoffset;
- }
-
- trackview.editor().verbose_cursor()->set_position (wx, wy);
+ trackview.editor().verbose_cursor()->set (text);
trackview.editor().verbose_cursor()->show ();
+ trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
}
/** @param p A session framepos.
#include <string>
#include <vector>
-
+#include <stdint.h>
#include "pbd/signals.h"
typedef Evoral::Note<Evoral::MusicalTime> NoteType;
typedef Evoral::Sequence<Evoral::MusicalTime>::Notes Notes;
- MidiRegionView (ArdourCanvas::Group *,
+ MidiRegionView (ArdourCanvas::Container *,
RouteTimeAxisView&,
boost::shared_ptr<ARDOUR::MidiRegion>,
double initial_samples_per_pixel,
- Gdk::Color const & basic_color);
+ uint32_t base_color);
MidiRegionView (const MidiRegionView& other);
MidiRegionView (const MidiRegionView& other, boost::shared_ptr<ARDOUR::MidiRegion>);
~MidiRegionView ();
- virtual void init (Gdk::Color const & basic_color, bool wfd);
+ void init (bool wfd);
const boost::shared_ptr<ARDOUR::MidiRegion> midi_region() const;
/** Allows derived types to specify their visibility requirements
* to the TimeAxisViewItem parent class.
*/
- MidiRegionView (ArdourCanvas::Group *,
+ MidiRegionView (ArdourCanvas::Container *,
RouteTimeAxisView&,
boost::shared_ptr<ARDOUR::MidiRegion>,
double samples_per_pixel,
- Gdk::Color& basic_color,
+ uint32_t basic_color,
TimeAxisViewItem::Visibility);
void region_resized (const PBD::PropertyChange&);
PatchChanges _patch_changes;
SysExes _sys_exes;
Note** _active_notes;
- ArdourCanvas::Group* _note_group;
+ ArdourCanvas::Container* _note_group;
ARDOUR::MidiModel::NoteDiffCommand* _note_diff_command;
Note* _ghost_note;
double _last_ghost_x;
/** A group used to temporarily reparent _note_group to during start trims, so
* that the notes don't move with the parent region view.
*/
- ArdourCanvas::Group* _temporary_note_group;
+ ArdourCanvas::Container* _temporary_note_group;
MouseState _mouse_state;
int _pressed_button;
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Editing;
, _updates_suspended (false)
{
/* use a group dedicated to MIDI underlays. Audio underlays are not in this group. */
- midi_underlay_group = new ArdourCanvas::Group (_canvas_group);
+ midi_underlay_group = new ArdourCanvas::Container (_canvas_group);
midi_underlay_group->lower_to_bottom();
/* put the note lines in the timeaxisview's group, so it
can be put below ghost regions from MIDI underlays
*/
- _note_lines = new ArdourCanvas::LineSet (_canvas_group);
-
+ _note_lines = new ArdourCanvas::LineSet (_canvas_group, ArdourCanvas::LineSet::Horizontal);
+
_note_lines->Event.connect(
sigc::bind(sigc::mem_fun(_trackview.editor(),
&PublicEditor::canvas_stream_view_event),
RegionView* region_view = new MidiRegionView (_canvas_group, _trackview, region,
_samples_per_pixel, region_color);
- region_view->init (region_color, false);
+ region_view->init (false);
return region_view;
}
MidiStreamView::update_contents_height ()
{
StreamView::update_contents_height();
- _note_lines->set_height (child_height ());
+
+ _note_lines->set_extent (ArdourCanvas::COORD_MAX);
apply_note_range (lowest_note(), highest_note(), true);
}
};
Gtk::Adjustment note_range_adjustment;
- ArdourCanvas::Group* midi_underlay_group;
+ ArdourCanvas::Container* midi_underlay_group;
void set_note_range(VisibleNoteRange r);
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
std::string binding_file;
- if (find_file_in_search_path (ardour_config_search_path(), "mixer.bindings", binding_file)) {
+ if (find_file (ardour_config_search_path(), "mixer.bindings", binding_file)) {
bindings.load (binding_file);
info << string_compose (_("Loaded mixer bindings from %1"), binding_file) << endmsg;
} else {
#include "gtkmm2ext/utils.h"
#include "ardour/route_group.h"
+
+#include "canvas/utils.h"
+
+#include "ardour_ui.h"
#include "mixer_group_tabs.h"
#include "mixer_strip.h"
#include "mixer_ui.h"
+#include "rgb_macros.h"
+#include "route_group_dialog.h"
#include "utils.h"
+
#include "i18n.h"
-#include "route_group_dialog.h"
using namespace std;
using namespace Gtk;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
MixerGroupTabs::MixerGroupTabs (Mixer_UI* m)
MixerGroupTabs::draw_tab (cairo_t* cr, Tab const & tab) const
{
double const arc_radius = get_height();
-
+ double r, g, b, a;
+
if (tab.group && tab.group->is_active()) {
- cairo_set_source_rgba (cr, tab.color.get_red_p (), tab.color.get_green_p (), tab.color.get_blue_p (), 1);
+ ArdourCanvas::color_to_rgba (tab.color, r, g, b, a);
} else {
- cairo_set_source_rgba (cr, 1, 1, 1, 0.2);
+ ArdourCanvas::color_to_rgba (ARDOUR_UI::config()->get_canvasvar_InactiveGroupTab(), r, g, b, a);
}
+
+ a = 1.0;
+ cairo_set_source_rgba (cr, r, g, b, a);
cairo_arc (cr, tab.from + arc_radius, get_height(), arc_radius, M_PI, 3 * M_PI / 2);
cairo_line_to (cr, tab.to - arc_radius, 0);
cairo_arc (cr, tab.to - arc_radius, get_height(), arc_radius, 3 * M_PI / 2, 2 * M_PI);
cairo_text_extents_t ext;
cairo_text_extents (cr, tab.group->name().c_str(), &ext);
+
+ ArdourCanvas::Color c = ArdourCanvas::contrasting_text_color (ArdourCanvas::rgba_to_color (r, g, b, a));
+ ArdourCanvas::color_to_rgba (c, r, g, b, a);
- cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_set_source_rgb (cr, r, g, b);
cairo_move_to (cr, tab.from + (tab.to - tab.from - f.second) / 2, get_height() - ext.height / 2);
cairo_save (cr);
cairo_show_text (cr, f.first.c_str());
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Gtkmm2ext;
parameter_changed (X_("mixer-strip-visibility"));
Config->ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
+ _session->config.ParameterChanged.connect (_config_connection, MISSING_INVALIDATOR, boost::bind (&MixerStrip::parameter_changed, this, _1), gui_context());
gpm.LevelMeterButtonPress.connect_same_thread (_level_meter_connection, boost::bind (&MixerStrip::level_meter_button_press, this, _1));
}
items.push_back (CheckMenuElem (_("Active")));
Gtk::CheckMenuItem* i = dynamic_cast<Gtk::CheckMenuItem *> (&items.back());
i->set_active (_route->active());
+ i->set_sensitive(! _session->transport_rolling());
i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), !_route->active(), false));
items.push_back (SeparatorElem());
{
switch (_width) {
case Wide:
- name_button.set_text (_route->name());
+ if (_session->config.get_track_name_number()) {
+ name_button.set_markup(track_number_to_string (_route->track_number (), " ", _route->name ()));
+ } else {
+ name_button.set_text (_route->name());
+ }
break;
case Narrow:
- name_button.set_text (PBD::short_version (_route->name(), 5));
+ if (_session->config.get_track_name_number()) {
+ name_button.set_markup(track_number_to_string (_route->track_number (), " ",
+ PBD::short_version (_route->name (), 5)));
+ } else {
+ name_button.set_text (PBD::short_version (_route->name(), 5));
+ }
break;
}
panner_ui().set_panner (_current_delivery->panner_shell(), _current_delivery->panner());
panner_ui().set_available_panners(PannerManager::instance().PannerManager::get_available_panners(in, out));
panner_ui().setup_pan ();
+ panner_ui().set_send_drawing_mode (true);
panner_ui().show_all ();
input_button.set_sensitive (false);
panner_ui().set_panner (_route->main_outs()->panner_shell(), _route->main_outs()->panner());
update_panner_choices();
panner_ui().setup_pan ();
+ panner_ui().set_send_drawing_mode (false);
if (has_audio_outputs ()) {
panners.show_all ();
*/
_visibility.set_state (Config->get_mixer_strip_visibility ());
}
+ else if (p == "track-name-number") {
+ name_changed ();
+ }
}
/** Called to decide whether the solo isolate / solo lock button visibility should
VisibilityGroup _visibility;
boost::optional<bool> override_solo_visibility () const;
- PBD::ScopedConnection _config_connection;
+ PBD::ScopedConnectionList _config_connection;
void add_input_port (ARDOUR::DataType);
void add_output_port (ARDOUR::DataType);
#include "ardour/midi_track.h"
#include "ardour/plugin_manager.h"
#include "ardour/route_group.h"
+#include "ardour/route_sorters.h"
#include "ardour/session.h"
#include "keyboard.h"
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtk;
using namespace Glib;
, _monitor_section (0)
, _strip_width (Config->get_default_narrow_ms() ? Narrow : Wide)
, ignore_reorder (false)
+ , _in_group_rebuild_or_clear (false)
+ , _route_deletion_in_progress (false)
, _following_editor_selection (false)
+ , _maximised (false)
{
/* allow this window to become the key focus window */
set_flags (CAN_FOCUS);
list_hpane.show();
group_display.show();
- _in_group_rebuild_or_clear = false;
- _maximised = false;
-
MixerStrip::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&Mixer_UI::remove_strip, this, _1), gui_context());
MonitorSection::setup_knob_images ();
for (ri = rows.begin(); ri != rows.end(); ++ri) {
if ((*ri)[track_columns.strip] == strip) {
+ PBD::Unwinder<bool> uw (_route_deletion_in_progress, true);
track_model->erase (ri);
break;
}
{
/* this happens as the second step of a DnD within the treeview as well
as when a row/route is actually deleted.
+
+ if it was a deletion then we have to force a redisplay because
+ order keys may not have changed.
*/
+
DEBUG_TRACE (DEBUG::OrderKeys, "mixer UI treeview row deleted\n");
sync_order_keys_from_treeview ();
+
+ if (_route_deletion_in_progress) {
+ redisplay_track_list ();
+ }
}
void
}
-struct SignalOrderRouteSorter {
- bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
- if (a->is_master() || a->is_monitor()) {
- /* "a" is a special route (master, monitor, etc), and comes
- * last in the mixer ordering
- */
- return false;
- } else if (b->is_master() || b->is_monitor()) {
- /* everything comes before b */
- return true;
- }
- return a->order_key () < b->order_key ();
-
- }
-};
-
void
Mixer_UI::initial_track_display ()
{
boost::shared_ptr<RouteList> routes = _session->get_routes();
RouteList copy (*routes);
- SignalOrderRouteSorter sorter;
+ ARDOUR::SignalOrderRouteSorter sorter;
copy.sort (sorter);
it during a session teardown.
*/
bool _in_group_rebuild_or_clear;
+ bool _route_deletion_in_progress;
void update_title ();
MixerStrip* strip_by_x (int x);
@trans|Transport/ToggleAutoPlay|5|toggle auto play
@trans|Transport/ToggleAutoReturn|6|toggle auto return
@trans|Transport/ToggleClick|7|toggle click (metronome)
-@ranges|Editor/set-tempo-from-region|9|set tempo (1 bar) from region(s)
+@ranges|Region/set-tempo-from-region|9|set tempo (1 bar) from region(s)
@ranges|Editor/set-tempo-from-edit-range|0|set tempo (1 bar) from edit range
; mouse stuff
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace Gtk;
using namespace Gtkmm2ext;
using namespace PBD;
using namespace std;
using namespace Gtk;
using namespace Gtkmm2ext;
+using namespace ARDOUR_UI_UTILS;
static const int pos_box_size = 9;
static const int lr_box_size = 15;
if (!have_font) {
Pango::FontDescription font;
Pango::AttrFontDesc* font_attr;
- font = Pango::FontDescription ("ArdourMono");
- font.set_weight (Pango::WEIGHT_BOLD);
- font.set_size(9 * PANGO_SCALE);
+ font = Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_SmallBoldMonospaceFont());
font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
panner_font_attributes.change(*font_attr);
delete font_attr;
t = 0x606060ff;
}
+ if (_send_mode) {
+ b = rgba_from_style("SendStripBase",
+ UINT_RGBA_R(b), UINT_RGBA_G(b), UINT_RGBA_B(b), 255, "fg");
+ }
/* background */
context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
context->rectangle (0, 0, width, height);
#include "mouse_cursors.h"
#include "editor_xpms"
+using namespace ARDOUR_UI_UTILS;
+
MouseCursors::MouseCursors ()
+ : cross_hair (0)
+ , trimmer (0)
+ , right_side_trim (0)
+ , anchored_right_side_trim (0)
+ , left_side_trim (0)
+ , anchored_left_side_trim (0)
+ , right_side_trim_left_only (0)
+ , left_side_trim_right_only (0)
+ , fade_in (0)
+ , fade_out (0)
+ , selector (0)
+ , grabber (0)
+ , grabber_note (0)
+ , grabber_edit_point (0)
+ , zoom_in (0)
+ , zoom_out (0)
+ , time_fx (0)
+ , fader (0)
+ , speaker (0)
+ , midi_pencil (0)
+ , midi_select (0)
+ , midi_resize (0)
+ , midi_erase (0)
+ , up_down (0)
+ , wait (0)
+ , timebar (0)
+ , transparent (0)
+ , resize_left (0)
+ , resize_top_left (0)
+ , resize_top (0)
+ , resize_top_right (0)
+ , resize_right (0)
+ , resize_bottom_right (0)
+ , resize_bottom (0)
+ , resize_bottom_left (0)
+ , move (0)
+ , expand_left_right (0)
+ , expand_up_down (0)
+{
+}
+
+void
+MouseCursors::drop_all ()
+{
+ delete cross_hair; cross_hair = 0;
+ delete trimmer; trimmer = 0;
+ delete right_side_trim; right_side_trim = 0;
+ delete anchored_right_side_trim; anchored_right_side_trim = 0;
+ delete left_side_trim; left_side_trim = 0;
+ delete anchored_left_side_trim; anchored_left_side_trim = 0;
+ delete right_side_trim_left_only; right_side_trim_left_only = 0;
+ delete left_side_trim_right_only; left_side_trim_right_only = 0;
+ delete fade_in; fade_in = 0;
+ delete fade_out; fade_out = 0;
+ delete selector; selector = 0;
+ delete grabber; grabber = 0;
+ delete grabber_note; grabber_note = 0;
+ delete grabber_edit_point; grabber_edit_point = 0;
+ delete zoom_in; zoom_in = 0;
+ delete zoom_out; zoom_out = 0;
+ delete time_fx; time_fx = 0;
+ delete fader; fader = 0;
+ delete speaker; speaker = 0;
+ delete midi_pencil; midi_pencil = 0;
+ delete midi_select; midi_select = 0;
+ delete midi_resize; midi_resize = 0;
+ delete midi_erase; midi_erase = 0;
+ delete up_down; up_down = 0;
+ delete wait; wait = 0;
+ delete timebar; timebar = 0;
+ delete transparent; transparent = 0;
+ delete resize_left; resize_left = 0;
+ delete resize_top_left; resize_top_left = 0;
+ delete resize_top; resize_top = 0;
+ delete resize_top_right; resize_top_right = 0;
+ delete resize_right; resize_right = 0;
+ delete resize_bottom_right; resize_bottom_right = 0;
+ delete resize_bottom; resize_bottom = 0;
+ delete resize_bottom_left; resize_bottom_left = 0;
+ delete move; move = 0;
+ delete expand_left_right; expand_left_right = 0;
+ delete expand_up_down; expand_up_down = 0;
+}
+
+void
+MouseCursors::set_cursor_set (const std::string& name)
{
using namespace Glib;
using namespace Gdk;
+
+ drop_all ();
+ _cursor_set = name;
{
- RefPtr<Pixbuf> p (::get_icon ("zoom_in_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("zoom_in_cursor", _cursor_set));
zoom_in = new Cursor (Display::get_default(), p, 10, 5);
}
{
- RefPtr<Pixbuf> p (::get_icon ("zoom_out_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("zoom_out_cursor", _cursor_set));
zoom_out = new Cursor (Display::get_default(), p, 5, 5);
}
}
{
- RefPtr<Pixbuf> p (::get_icon ("grabber"));
+ RefPtr<Pixbuf> p (::get_icon ("grabber", _cursor_set));
grabber = new Cursor (Display::get_default(), p, 5, 0);
}
{
- RefPtr<Pixbuf> p (::get_icon ("grabber_note"));
+ RefPtr<Pixbuf> p (::get_icon ("grabber_note", _cursor_set));
grabber_note = new Cursor (Display::get_default(), p, 5, 10);
}
{
- RefPtr<Pixbuf> p (::get_icon ("grabber_edit_point"));
+ RefPtr<Pixbuf> p (::get_icon ("grabber_edit_point", _cursor_set));
grabber_edit_point = new Cursor (Display::get_default(), p, 5, 17);
}
trimmer = new Cursor (SB_H_DOUBLE_ARROW);
{
- RefPtr<Pixbuf> p (::get_icon ("trim_left_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("trim_left_cursor", _cursor_set));
left_side_trim = new Cursor (Display::get_default(), p, 5, 11);
}
{
- RefPtr<Pixbuf> p (::get_icon ("trim_right_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("anchored_trim_left_cursor", _cursor_set));
+ anchored_left_side_trim = new Cursor (Display::get_default(), p, 5, 11);
+ }
+
+ {
+ RefPtr<Pixbuf> p (::get_icon ("trim_right_cursor", _cursor_set));
right_side_trim = new Cursor (Display::get_default(), p, 23, 11);
}
{
- RefPtr<Pixbuf> p (::get_icon ("trim_left_cursor_right_only"));
+ RefPtr<Pixbuf> p (::get_icon ("anchored_trim_right_cursor", _cursor_set));
+ anchored_right_side_trim = new Cursor (Display::get_default(), p, 23, 11);
+ }
+
+ {
+ RefPtr<Pixbuf> p (::get_icon ("trim_left_cursor_right_only", _cursor_set));
left_side_trim_right_only = new Cursor (Display::get_default(), p, 5, 11);
}
{
- RefPtr<Pixbuf> p (::get_icon ("trim_right_cursor_left_only"));
+ RefPtr<Pixbuf> p (::get_icon ("trim_right_cursor_left_only", _cursor_set));
right_side_trim_left_only = new Cursor (Display::get_default(), p, 23, 11);
}
{
- RefPtr<Pixbuf> p (::get_icon ("fade_in_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("fade_in_cursor", _cursor_set));
fade_in = new Cursor (Display::get_default(), p, 0, 0);
}
{
- RefPtr<Pixbuf> p (::get_icon ("fade_out_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("fade_out_cursor", _cursor_set));
fade_out = new Cursor (Display::get_default(), p, 29, 0);
}
{
- RefPtr<Pixbuf> p (::get_icon ("resize_left_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("resize_left_cursor", _cursor_set));
resize_left = new Cursor (Display::get_default(), p, 3, 10);
}
{
- RefPtr<Pixbuf> p (::get_icon ("resize_top_left_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("resize_top_left_cursor", _cursor_set));
resize_top_left = new Cursor (Display::get_default(), p, 3, 3);
}
{
- RefPtr<Pixbuf> p (::get_icon ("resize_top_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("resize_top_cursor", _cursor_set));
resize_top = new Cursor (Display::get_default(), p, 10, 3);
}
{
- RefPtr<Pixbuf> p (::get_icon ("resize_top_right_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("resize_top_right_cursor", _cursor_set));
resize_top_right = new Cursor (Display::get_default(), p, 18, 3);
}
{
- RefPtr<Pixbuf> p (::get_icon ("resize_right_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("resize_right_cursor", _cursor_set));
resize_right = new Cursor (Display::get_default(), p, 24, 10);
}
{
- RefPtr<Pixbuf> p (::get_icon ("resize_bottom_right_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("resize_bottom_right_cursor", _cursor_set));
resize_bottom_right = new Cursor (Display::get_default(), p, 18, 18);
}
{
- RefPtr<Pixbuf> p (::get_icon ("resize_bottom_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("resize_bottom_cursor", _cursor_set));
resize_bottom = new Cursor (Display::get_default(), p, 10, 24);
}
{
- RefPtr<Pixbuf> p (::get_icon ("resize_bottom_left_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("resize_bottom_left_cursor", _cursor_set));
resize_bottom_left = new Cursor (Display::get_default(), p, 3, 18);
}
{
- RefPtr<Pixbuf> p (::get_icon ("move_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("move_cursor", _cursor_set));
move = new Cursor (Display::get_default(), p, 11, 11);
}
{
- RefPtr<Pixbuf> p (::get_icon ("expand_left_right_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("expand_left_right_cursor", _cursor_set));
expand_left_right = new Cursor (Display::get_default(), p, 11, 4);
}
{
- RefPtr<Pixbuf> p (::get_icon ("expand_up_down_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("expand_up_down_cursor", _cursor_set));
expand_up_down = new Cursor (Display::get_default(), p, 4, 11);
}
{
- RefPtr<Pixbuf> p (::get_icon ("i_beam_cursor"));
+ RefPtr<Pixbuf> p (::get_icon ("i_beam_cursor", _cursor_set));
selector = new Cursor (Display::get_default(), p, 4, 11);
}
public:
MouseCursors ();
+ void set_cursor_set (const std::string& name);
+ std::string cursor_set() const { return _cursor_set; }
+
Gdk::Cursor* cross_hair;
Gdk::Cursor* trimmer;
Gdk::Cursor* right_side_trim;
+ Gdk::Cursor* anchored_right_side_trim;
Gdk::Cursor* left_side_trim;
+ Gdk::Cursor* anchored_left_side_trim;
Gdk::Cursor* right_side_trim_left_only;
Gdk::Cursor* left_side_trim_right_only;
Gdk::Cursor* fade_in;
Gdk::Cursor* move;
Gdk::Cursor* expand_left_right;
Gdk::Cursor* expand_up_down;
+
+ private:
+ std::string _cursor_set;
+ void drop_all ();
+
};
#endif /* __gtk2_ardour_mouse_cursors__ */
using namespace ArdourCanvas;
Note::Note (
- MidiRegionView& region, Group* group, const boost::shared_ptr<NoteType> note, bool with_events)
+ MidiRegionView& region, Item* parent, const boost::shared_ptr<NoteType> note, bool with_events)
: NoteBase (region, with_events, note)
- , _rectangle (new ArdourCanvas::Rectangle (group))
+ , _rectangle (new ArdourCanvas::Rectangle (parent))
{
CANVAS_DEBUG_NAME (_rectangle, "note");
set_item (_rectangle);
#include "midi_util.h"
namespace ArdourCanvas {
- class Group;
+ class Container;
}
class Note : public NoteBase
typedef Evoral::Note<Evoral::MusicalTime> NoteType;
Note (MidiRegionView& region,
- ArdourCanvas::Group* group,
+ ArdourCanvas::Item* parent,
const boost::shared_ptr<NoteType> note = boost::shared_ptr<NoteType>(),
bool with_events = true);
switch (mode) {
case TrackColor:
{
- Gdk::Color color = _region.midi_stream_view()->get_region_color();
- return UINT_INTERPOLATE (RGBA_TO_UINT(
- SCALE_USHORT_TO_UINT8_T(color.get_red()),
- SCALE_USHORT_TO_UINT8_T(color.get_green()),
- SCALE_USHORT_TO_UINT8_T(color.get_blue()),
- opacity),
- ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(), 0.5);
+ uint32_t color = _region.midi_stream_view()->get_region_color();
+ return UINT_INTERPOLATE (UINT_RGBA_CHANGE_A (color, opacity),
+ ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(),
+ 0.5);
}
case ChannelColors:
- return UINT_INTERPOLATE (UINT_RGBA_CHANGE_A (NoteBase::midi_channel_colors[_note->channel()],
- opacity),
+ return UINT_INTERPOLATE (UINT_RGBA_CHANGE_A (NoteBase::midi_channel_colors[_note->channel()], opacity),
ARDOUR_UI::config()->get_canvasvar_MidiNoteSelected(), 0.5);
default:
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <algorithm>
#include <gtkmm/box.h>
#include <gtkmm/alignment.h>
#include "option_editor.h"
#include "gui_thread.h"
-#include "utils.h"
#include "i18n.h"
using namespace std;
_label = manage (left_aligned_label (n + ":"));
_entry = manage (new Entry);
_entry->signal_activate().connect (sigc::mem_fun (*this, &EntryOption::activated));
+ _entry->signal_focus_out_event().connect (sigc::mem_fun (*this, &EntryOption::focus_out));
+ _entry->signal_insert_text().connect (sigc::mem_fun (*this, &EntryOption::filter_text));
}
void
_entry->set_text (_get ());
}
+void
+EntryOption::set_sensitive (bool s)
+{
+ _entry->set_sensitive (s);
+}
+
+void
+EntryOption::filter_text (const Glib::ustring&, int*)
+{
+ std::string text = _entry->get_text ();
+ for (size_t i = 0; i < _invalid.length(); ++i) {
+ text.erase (std::remove(text.begin(), text.end(), _invalid.at(i)), text.end());
+ }
+ if (text != _entry->get_text ()) {
+ _entry->set_text (text);
+ }
+}
+
void
EntryOption::activated ()
{
_set (_entry->get_text ());
}
+bool
+EntryOption::focus_out (GdkEventFocus*)
+{
+ _set (_entry->get_text ());
+ return true;
+}
+
/** Construct a BoolComboOption.
* @param i id
* @param n User-visible name.
_db_slider = manage (new HSliderController (&_db_adjustment, 115, 18, false));
_label.set_text (n + ":");
+ _label.set_alignment (0, 0.5);
_label.set_name (X_("OptionsLabel"));
_fader_centering_box.pack_start (*_db_slider, true, false);
void
DirectoryOption::add_to_page (OptionEditorPage* p)
{
- add_widgets_to_page (p, manage (new Label (_name)), &_file_chooser);
+ Gtk::Label *label = manage (new Label (_name));
+ label->set_alignment (0, 0.5);
+ label->set_name (X_("OptionsLabel"));
+ add_widgets_to_page (p, label, &_file_chooser);
}
void
Gtk::Label* _label; ///< label for button, so we can use markup
};
+/** Component which allows to add any GTK Widget - intended for single buttons and custom stateless objects */
+class FooOption : public OptionEditorComponent
+{
+public:
+ FooOption (Gtk::Widget *w) : _w (w) {}
+
+ void add_to_page (OptionEditorPage* p) {
+ add_widget_to_page (p, _w);
+ }
+
+ Gtk::Widget& tip_widget() { return *_w; }
+ void set_state_from_config () {}
+ void parameter_changed (std::string const &) {}
+private:
+ Gtk::Widget *_w;
+};
+
/** Component which provides the UI to handle a string option using a GTK Entry */
class EntryOption : public Option
{
EntryOption (std::string const &, std::string const &, sigc::slot<std::string>, sigc::slot<bool, std::string>);
void set_state_from_config ();
void add_to_page (OptionEditorPage*);
+ void set_sensitive (bool);
+ void set_invalid_chars (std::string i) { _invalid = i; }
- Gtk::Widget& tip_widget() { return *_entry; }
+ Gtk::Widget& tip_widget() { return *_entry; }
private:
void activated ();
+ bool focus_out (GdkEventFocus*);
+ void filter_text (const Glib::ustring&, int*);
sigc::slot<std::string> _get; ///< slot to get the configuration variable's value
sigc::slot<bool, std::string> _set; ///< slot to set the configuration variable's value
Gtk::Label* _label; ///< UI label
Gtk::Entry* _entry; ///< UI entry
+ std::string _invalid;
};
}
Gtk::Widget& tip_widget() { return *_hscale; }
+ Gtk::HScale& scale() { return *_hscale; }
private:
sigc::slot<float> _get;
{ "help", 0, 0, 'h' },
{ "no-announcements", 0, 0, 'a' },
{ "bindings", 0, 0, 'b' },
- { "disable-plugins", 1, 0, 'd' },
+ { "disable-plugins", 0, 0, 'd' },
{ "debug", 1, 0, 'D' },
{ "no-splash", 0, 0, 'n' },
{ "menus", 1, 0, 'm' },
using namespace std;
using namespace Gtk;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using Gtkmm2ext::Keyboard;
, height (h)
, last_width (0)
, have_elevation (false)
+ , _send_mode (false)
{
panner_shell->Changed.connect (panshell_connections, invalidator (*this), boost::bind (&Panner2d::handle_state_change, this), gui_context());
return closest;
}
+void
+Panner2d::set_send_drawing_mode (bool onoff)
+{
+ if (_send_mode != onoff) {
+ _send_mode = onoff;
+ queue_draw ();
+ }
+}
+
bool
Panner2d::on_motion_notify_event (GdkEventMotion *ev)
{
/* background */
cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
+
+ float r, g, b;
+ r = g = b = 0.1;
+ if (_send_mode) {
+ rgba_p_from_style("SendStripBase", &r, &g, &b, "fg");
+ }
if (!panner_shell->bypassed()) {
- cairo_set_source_rgba (cr, 0.1, 0.1, 0.1, 1.0);
+ cairo_set_source_rgba (cr, r, g, b, 1.0);
} else {
- cairo_set_source_rgba (cr, 0.1, 0.1, 0.1, 0.2);
+ cairo_set_source_rgba (cr, r, g, b , 0.2);
}
cairo_fill_preserve (cr);
cairo_clip (cr);
}
namespace Pango {
- class Layout;
+ class Container;
}
class Panner2dWindow;
int add_signal (const char* text, const PBD::AngularVector&);
void move_signal (int which, const PBD::AngularVector&);
void reset (uint32_t n_inputs);
+ void set_send_drawing_mode (bool);
boost::shared_ptr<ARDOUR::PannerShell> get_panner_shell() const { return panner_shell; }
double last_width;
bool did_move;
bool have_elevation;
+ bool _send_mode;
Target *find_closest_object (gdouble x, gdouble y, bool& is_signal);
PannerInterface::PannerInterface (boost::shared_ptr<Panner> p)
: _panner (p)
, _tooltip (this)
+ , _send_mode (false)
, _editor (0)
{
set_flags (Gtk::CAN_FOCUS);
_editor->show ();
}
+void
+PannerInterface::set_send_drawing_mode(bool onoff) {
+ if (_send_mode != onoff) {
+ _send_mode = onoff;
+ queue_draw ();
+ }
+}
+
PannerPersistentTooltip::PannerPersistentTooltip (Gtk::Widget* w)
: PersistentTooltip (w)
, _dragging (false)
}
void edit ();
+ void set_send_drawing_mode (bool);
protected:
virtual void set_tooltip () = 0;
boost::shared_ptr<ARDOUR::Panner> _panner;
PannerPersistentTooltip _tooltip;
+ bool _send_mode;
+
private:
virtual PannerEditor* editor () = 0;
PannerEditor* _editor;
#include "ardour_ui.h"
#include "panner_ui.h"
#include "panner2d.h"
-#include "utils.h"
#include "gui_thread.h"
#include "stereo_panner.h"
#include "mono_panner.h"
: _current_nouts (-1)
, _current_nins (-1)
, _current_uri ("")
+ , _send_mode (false)
, pan_automation_style_button ("")
, pan_automation_state_button ("")
, _panner_list()
_stereo_panner = new StereoPanner (_panshell);
_stereo_panner->set_size_request (-1, pan_bar_height);
+ _stereo_panner->set_send_drawing_mode (_send_mode);
pan_vbox.pack_start (*_stereo_panner, false, false);
boost::shared_ptr<AutomationControl> ac;
_mono_panner->signal_button_release_event().connect (sigc::mem_fun(*this, &PannerUI::pan_button_event));
_mono_panner->set_size_request (-1, pan_bar_height);
+ _mono_panner->set_send_drawing_mode (_send_mode);
update_pan_sensitive ();
pan_vbox.pack_start (*_mono_panner, false, false);
big_window->reset (nins);
}
twod_panner->set_size_request (-1, 61);
+ twod_panner->set_send_drawing_mode (_send_mode);
/* and finally, add it to the panner frame */
pan_vbox.show_all ();
}
+void
+PannerUI::set_send_drawing_mode (bool onoff)
+{
+ if (_stereo_panner) {
+ _stereo_panner->set_send_drawing_mode (onoff);
+ } else if (_mono_panner) {
+ _mono_panner->set_send_drawing_mode (onoff);
+ } else if (twod_panner) {
+ twod_panner->set_send_drawing_mode (onoff);
+ }
+ _send_mode = onoff;
+}
+
void
PannerUI::start_touch (boost::weak_ptr<AutomationControl> wac)
{
void set_width (Width);
void setup_pan ();
void set_available_panners(std::map<std::string,std::string>);
+ void set_send_drawing_mode (bool);
void effective_pan_display ();
int _current_nouts;
int _current_nins;
std::string _current_uri;
+ bool _send_mode;
static const int pan_bar_height;
*/
PatchChange::PatchChange(
MidiRegionView& region,
- ArdourCanvas::Group* parent,
+ ArdourCanvas::Container* parent,
const string& text,
double height,
double x,
public:
PatchChange(
MidiRegionView& region,
- ArdourCanvas::Group* parent,
+ ArdourCanvas::Container* parent,
const string& text,
double height,
double x,
#else
while (pos < creator.length() && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
#endif
- creator = creator.substr (0, pos);
+ // If there were too few characters to create a
+ // meaningful name, mark this creator as 'Unknown'
+ if (creator.length()<2 || pos<3)
+ creator = "Unknown";
+ else
+ creator = creator.substr (0, pos);
newrow[plugin_columns.creator] = creator;
/* stupid LADSPA creator strings */
string::size_type pos = 0;
#ifdef PLATFORM_WINDOWS
- while (pos < creator.length() && creator[pos] > -2 && creator[pos] < 256 && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
+ while (pos < creator.length() && creator[pos]>(-2) && creator[pos]<256 && (isprint (creator[pos]))) ++pos;
#else
while (pos < creator.length() && (isalnum (creator[pos]) || isspace (creator[pos]))) ++pos;
#endif
- creator = creator.substr (0, pos);
+
+ // Check to see if we found any invalid characters.
+ if (creator.length() != pos) {
+ // If there were too few characters to create a
+ // meaningful name, mark this creator as 'Unknown'
+ if (pos<3)
+ creator = "Unknown?";
+ else
+ creator = creator.substr (0, pos);
+ }
SubmenuMap::iterator x;
Gtk::Menu* submenu;
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtkmm2ext;
using namespace Gtk;
#include "ardour/session.h"
#include "port_insert_ui.h"
-#include "utils.h"
#include "gui_thread.h"
#include "i18n.h"
using namespace std;
using namespace Gtk;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
/** PortMatrix constructor.
* @param session Our session.
#include "port_matrix_column_labels.h"
#include "port_matrix.h"
#include "port_matrix_body.h"
-#include "utils.h"
#include "i18n.h"
#include "port_matrix.h"
#include "port_matrix_body.h"
#include "i18n.h"
-#include "utils.h"
using namespace std;
#include "return_ui.h"
#include "route_processor_selection.h"
#include "send_ui.h"
-#include "utils.h"
#include "i18n.h"
boost::shared_ptr<Pannable> sendpan(new Pannable (*_session));
XMLNode n (**niter);
- InternalSend* s = new InternalSend (*_session, sendpan, _route->mute_master(),
- boost::shared_ptr<Route>(), Delivery::Aux);
+ InternalSend* s = new InternalSend (*_session, sendpan, _route->mute_master(),
+ _route, boost::shared_ptr<Route>(), Delivery::Aux);
IOProcessor::prepare_for_reset (n, s->name());
PublicEditor::PublicEditor ()
: Window (Gtk::WINDOW_TOPLEVEL)
, VisibilityTracker (*((Gtk::Window*)this))
+ , _suspend_route_redisplay_counter (0)
{
}
class XMLNode;
struct SelectionRect;
+class DisplaySuspender;
+
+namespace ARDOUR_UI_UTILS {
+bool relay_key_press (GdkEventKey* ev, Gtk::Window* win);
+bool forward_key_press (GdkEventKey* ev);
+}
+
using ARDOUR::framepos_t;
using ARDOUR::framecnt_t;
virtual framecnt_t current_page_samples() const = 0;
virtual double visible_canvas_height () const = 0;
virtual void temporal_zoom_step (bool coarser) = 0;
- virtual void ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top = false) {
- _ensure_time_axis_view_is_visible (tav, at_top);
+ /* The virtual version, without a default argument, is protected below.
+ */
+ void ensure_time_axis_view_is_visible (TimeAxisView const & tav, bool at_top = false) {
+ _ensure_time_axis_view_is_visible (tav, at_top);
}
virtual void override_visible_track_count () = 0;
virtual void scroll_tracks_down_line () = 0;
virtual void reset_focus () = 0;
+ virtual bool canvas_scroll_event (GdkEventScroll* event, bool from_canvas) = 0;
virtual bool canvas_control_point_event (GdkEvent* event, ArdourCanvas::Item*, ControlPoint*) = 0;
virtual bool canvas_line_event (GdkEvent* event, ArdourCanvas::Item*, AutomationLine*) = 0;
virtual bool canvas_selection_rect_event (GdkEvent* event, ArdourCanvas::Item*, SelectionRect*) = 0;
virtual bool canvas_start_xfade_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
virtual bool canvas_end_xfade_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
virtual bool canvas_fade_in_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
- virtual bool canvas_fade_in_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
+ virtual bool canvas_fade_in_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*, bool) = 0;
virtual bool canvas_fade_out_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
- virtual bool canvas_fade_out_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*) = 0;
+ virtual bool canvas_fade_out_handle_event (GdkEvent* event, ArdourCanvas::Item*, AudioRegionView*, bool) = 0;
virtual bool canvas_region_view_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
+ virtual bool canvas_wave_view_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
virtual bool canvas_frame_handle_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
virtual bool canvas_region_view_name_highlight_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
virtual bool canvas_region_view_name_event (GdkEvent* event, ArdourCanvas::Item*, RegionView*) = 0;
virtual Gtk::HBox& get_status_bar_packer() = 0;
#endif
- virtual ArdourCanvas::Group* get_trackview_group () const = 0;
- virtual ArdourCanvas::Group* get_time_bars_group () const = 0;
- virtual ArdourCanvas::Group* get_track_canvas_group () const = 0;
+ virtual ArdourCanvas::Container* get_trackview_group () const = 0;
+ virtual ArdourCanvas::ScrollGroup* get_hscroll_group () const = 0;
+ virtual ArdourCanvas::ScrollGroup* get_vscroll_group () const = 0;
+ virtual ArdourCanvas::ScrollGroup* get_hvscroll_group () const = 0;
- virtual ArdourCanvas::GtkCanvasViewport* get_time_bars_canvas() const = 0;
virtual ArdourCanvas::GtkCanvasViewport* get_track_canvas() const = 0;
virtual TimeAxisView* axis_view_from_route (boost::shared_ptr<ARDOUR::Route>) const = 0;
static PublicEditor* _instance;
- friend bool relay_key_press (GdkEventKey*, Gtk::Window*);
- friend bool forward_key_press (GdkEventKey*);
+ friend bool ARDOUR_UI_UTILS::relay_key_press (GdkEventKey*, Gtk::Window*);
+ friend bool ARDOUR_UI_UTILS::forward_key_press (GdkEventKey*);
PBD::Signal0<void> SnapChanged;
PBD::Signal0<void> MouseModeChanged;
protected:
- virtual void _ensure_time_axis_view_is_visible (const TimeAxisView& tav, bool at_top) = 0;
+ /* This _ variant of ensure_time_axis_view_is_visible exists because
+ C++ doesn't really like default values for virtual methods. So the
+ public version is non-virtual, with a default value; the virtual
+ (and protected) method here does not have a default value.
+ */
+ virtual void _ensure_time_axis_view_is_visible (TimeAxisView const & tav, bool at_top) = 0;
+
+ friend class DisplaySuspender;
+ virtual void suspend_route_redisplay () = 0;
+ virtual void resume_route_redisplay () = 0;
+ gint _suspend_route_redisplay_counter;
+};
+
+class DisplaySuspender {
+ public:
+ DisplaySuspender() {
+ if (g_atomic_int_add(&PublicEditor::instance()._suspend_route_redisplay_counter, 1) == 0) {
+ PublicEditor::instance().suspend_route_redisplay ();
+ }
+ }
+ ~DisplaySuspender () {
+ if (g_atomic_int_dec_and_test (&PublicEditor::instance()._suspend_route_redisplay_counter)) {
+ PublicEditor::instance().resume_route_redisplay ();
+ }
+ }
};
#endif // __gtk_ardour_public_editor_h__
, swing_adjustment (100.0, -130.0, 130.0, 1.0, 10.0)
, swing_spinner (swing_adjustment)
, swing_button (_("Swing"))
- , threshold_adjustment (0.0, -1920.0, 1920.0, 1.0, 10.0) // XXX MAGIC TICK NUMBER FIX ME
+ , threshold_adjustment (0.0, -Timecode::BBT_Time::ticks_per_beat, Timecode::BBT_Time::ticks_per_beat, 1.0, 10.0)
, threshold_spinner (threshold_adjustment)
, threshold_label (_("Threshold (ticks)"))
, snap_start_button (_("Snap note start"))
#include "canvas/wave_view.h"
+#include "ardour_ui.h"
#include "ardour_window.h"
#include "ardour_dialog.h"
#include "gui_thread.h"
using namespace Gtkmm2ext;
using namespace PBD;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
class ClickOptions : public OptionEditorBox
{
Gtkmm2ext::UI::instance()->set_tip (tsf->tip_widget(),
string_compose (_("<b>When enabled</b> %1 will stop recording if an over- or underrun is detected by the audio engine"),
PROGRAM_NAME));
+ add_option (_("Transport"), tsf);
tsf = new BoolOption (
"loop-is-mode",
/* EDITOR */
+ add_option (S_("Editor"),
+ new BoolOption (
+ "draggable-playhead",
+ _("Allow dragging of playhead"),
+ sigc::mem_fun (*ARDOUR_UI::config(), &UIConfiguration::get_draggable_playhead),
+ sigc::mem_fun (*ARDOUR_UI::config(), &UIConfiguration::set_draggable_playhead)
+ ));
+
add_option (_("Editor"),
new BoolOption (
"link-region-and-track-selection",
sigc::mem_fun (*_rc_config, &RCConfiguration::set_super_rapid_clock_update)
));
+ /* Lock GUI timeout */
+
+ Gtk::Adjustment *lts = manage (new Gtk::Adjustment(0, 0, 1000, 1, 10));
+ HSliderOption *slts = new HSliderOption("lock-gui-after-seconds",
+ _("Lock timeout (seconds)"),
+ lts,
+ sigc::mem_fun (*ARDOUR_UI::config(), &UIConfiguration::get_lock_gui_after_seconds),
+ sigc::mem_fun (*ARDOUR_UI::config(), &UIConfiguration::set_lock_gui_after_seconds)
+ );
+ slts->scale().set_digits (0);
+ Gtkmm2ext::UI::instance()->set_tip
+ (slts->tip_widget(),
+ _("Lock GUI after this many idle seconds (zero to never lock)"));
+ add_option (S_("Preferences|GUI"), slts);
+
/* The names of these controls must be the same as those given in MixerStrip
for the actual widgets being controlled.
*/
#include "main_clock.h"
#include "gui_thread.h"
#include "region_editor.h"
-#include "utils.h"
#include "i18n.h"
#include "control_point.h"
#include "region_gain_line.h"
#include "audio_region_view.h"
-#include "utils.h"
#include "time_axis_view.h"
#include "editor.h"
using namespace ARDOUR;
using namespace PBD;
-AudioRegionGainLine::AudioRegionGainLine (const string & name, AudioRegionView& r, ArdourCanvas::Group& parent, boost::shared_ptr<AutomationList> l)
+AudioRegionGainLine::AudioRegionGainLine (const string & name, AudioRegionView& r, ArdourCanvas::Container& parent, boost::shared_ptr<AutomationList> l)
: AutomationLine (name, r.get_time_axis_view(), parent, l)
, rv (r)
{
class AudioRegionGainLine : public AutomationLine
{
public:
- AudioRegionGainLine (const std::string & name, AudioRegionView&, ArdourCanvas::Group& parent, boost::shared_ptr<ARDOUR::AutomationList>);
+ AudioRegionGainLine (const std::string & name, AudioRegionView&, ArdourCanvas::Container& parent, boost::shared_ptr<ARDOUR::AutomationList>);
void start_drag_single (ControlPoint*, double, float);
void end_drag (bool with_push, uint32_t final_index);
using namespace std;
using namespace Gtk;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
RegionLayeringOrderEditor::RegionLayeringOrderEditor (PublicEditor& pe)
: ArdourWindow (_("RegionLayeringOrderEditor"))
#include "canvas/pixbuf.h"
#include "canvas/text.h"
#include "canvas/line.h"
+#include "canvas/utils.h"
#include "ardour_ui.h"
#include "global_signals.h"
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Editing;
using namespace Gtk;
PBD::Signal1<void,RegionView*> RegionView::RegionViewGoingAway;
-RegionView::RegionView (ArdourCanvas::Group* parent,
+RegionView::RegionView (ArdourCanvas::Container* parent,
TimeAxisView& tv,
boost::shared_ptr<ARDOUR::Region> r,
double spu,
- Gdk::Color const & basic_color,
+ uint32_t basic_color,
bool automation)
: TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(), false, automation,
(automation ? TimeAxisViewItem::ShowFrame :
GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
}
-RegionView::RegionView (ArdourCanvas::Group* parent,
+RegionView::RegionView (ArdourCanvas::Container* parent,
TimeAxisView& tv,
boost::shared_ptr<ARDOUR::Region> r,
double spu,
- Gdk::Color const & basic_color,
+ uint32_t basic_color,
bool recording,
TimeAxisViewItem::Visibility visibility)
: TimeAxisViewItem (r->name(), *parent, tv, spu, basic_color, r->position(), r->length(), recording, false, visibility)
}
void
-RegionView::init (Gdk::Color const & basic_color, bool wfd)
+RegionView::init (bool wfd)
{
editor = 0;
valid = true;
sync_mark = 0;
sync_line = 0;
- compute_colors (basic_color);
-
if (name_highlight) {
name_highlight->set_data ("regionview", this);
name_highlight->Event.connect (sigc::bind (sigc::mem_fun (PublicEditor::instance(), &PublicEditor::canvas_region_view_name_highlight_event), name_highlight, this));
return true;
}
+uint32_t
+RegionView::fill_opacity () const
+{
+ if (!_region->opaque()) {
+ return 60;
+ }
+
+ uint32_t normal_tavi_opacity = TimeAxisViewItem::fill_opacity ();
+
+ return normal_tavi_opacity;
+}
+
void
RegionView::set_colors ()
{
if (sync_mark) {
/* XXX: make these colours themable */
- sync_mark->set_fill_color (RGBA_TO_UINT (0, 255, 0, 255));
- sync_line->set_outline_color (RGBA_TO_UINT (0, 255, 0, 255));
+ sync_mark->set_fill_color (ArdourCanvas::rgba_to_color (0, 1.0, 0, 1.0));
+ sync_line->set_outline_color (ArdourCanvas::rgba_to_color (0, 1.0, 0, 1.0));
}
}
void
RegionView::set_frame_color ()
{
- if (_region->opaque()) {
- fill_opacity = 130;
- } else {
- fill_opacity = 60;
- }
-
TimeAxisViewItem::set_frame_color ();
}
-void
-RegionView::fake_set_opaque (bool yn)
-{
- if (yn) {
- fill_opacity = 130;
- } else {
- fill_opacity = 60;
- }
-
- set_frame_color ();
-}
-
void
RegionView::show_region_editor ()
{
sync_mark = new ArdourCanvas::Polygon (group);
CANVAS_DEBUG_NAME (sync_mark, string_compose ("sync mark for %1", get_item_name()));
- sync_mark->set_fill_color (RGBA_TO_UINT(0,255,0,255)); // FIXME make a themeable colour
+ sync_mark->set_fill_color (ArdourCanvas::rgba_to_color (0, 1.0, 0, 1.0)); // FIXME make a themeable colour
sync_line = new ArdourCanvas::Line (group);
CANVAS_DEBUG_NAME (sync_line, string_compose ("sync mark for %1", get_item_name()));
- sync_line->set_outline_color (RGBA_TO_UINT(0,255,0,255)); // FIXME make a themeable colour
+ sync_line->set_outline_color (ArdourCanvas::rgba_to_color (0, 1.0, 0, 1.0)); // FIXME make a themeable colour
}
/* this has to handle both a genuine change of position, a change of samples_per_pixel
return;
}
- get_canvas_group()->move (ArdourCanvas::Duple (x_delta, y_delta));
+ /* items will not prevent Item::move() moving
+ * them to a negative x-axis coordinate, which
+ * is legal, but we don't want that here.
+ */
+
+ ArdourCanvas::Item *item = get_canvas_group ();
+
+ if (item->position().x + x_delta < 0) {
+ x_delta = -item->position().x; /* move it to zero */
+ }
+
+ item->move (ArdourCanvas::Duple (x_delta, y_delta));
/* note: ghosts never leave their tracks so y_delta for them is always zero */
}
}
-uint32_t
-RegionView::get_fill_color ()
-{
- return fill_color;
-}
-
void
RegionView::set_height (double h)
{
class RegionView : public TimeAxisViewItem
{
public:
- RegionView (ArdourCanvas::Group* parent,
+ RegionView (ArdourCanvas::Container* parent,
TimeAxisView& time_view,
boost::shared_ptr<ARDOUR::Region> region,
double samples_per_pixel,
- Gdk::Color const & basic_color,
+ uint32_t base_color,
bool automation = false);
RegionView (const RegionView& other);
~RegionView ();
- virtual void init (Gdk::Color const & base_color, bool wait_for_data);
+ virtual void init (bool wait_for_data);
boost::shared_ptr<ARDOUR::Region> region() const { return _region; }
void lower_to_bottom ();
bool set_position(framepos_t pos, void* src, double* delta = 0);
- void fake_set_opaque (bool yn);
virtual void show_region_editor ();
void hide_region_editor ();
void remove_ghost_in (TimeAxisView&);
void remove_ghost (GhostRegion*);
- uint32_t get_fill_color ();
-
virtual void entered (bool) {}
virtual void exited () {}
/** Allows derived types to specify their visibility requirements
* to the TimeAxisViewItem parent class
*/
- RegionView (ArdourCanvas::Group *,
+ RegionView (ArdourCanvas::Container *,
TimeAxisView&,
boost::shared_ptr<ARDOUR::Region>,
double samples_per_pixel,
- Gdk::Color const & basic_color,
+ uint32_t basic_color,
bool recording,
TimeAxisViewItem::Visibility);
virtual void set_frame_color ();
virtual void reset_width_dependent_items (double pixel_width);
+ uint32_t fill_opacity () const;
+
virtual void color_handler () {}
boost::shared_ptr<ARDOUR::Region> _region;
#include "ardour/rc_configuration.h"
#include "ardour/return.h"
-#include "utils.h"
#include "return_ui.h"
#include "io_selector.h"
#include "ardour_ui.h"
#include "rhythm_ferret.h"
#include "audio_region_view.h"
#include "editor.h"
-#include "utils.h"
#include "time_axis_view.h"
#include "i18n.h"
*/
+#include <iostream>
+
+#include "ardour/route_group.h"
+#include "ardour/session.h"
+
#include <gtkmm/table.h>
#include <gtkmm/stock.h>
#include <gtkmm/messagedialog.h>
-#include "ardour/route_group.h"
-#include "ardour/session.h"
+
#include "route_group_dialog.h"
#include "group_tabs.h"
+#include "utils.h"
+
#include "i18n.h"
-#include <iostream>
using namespace Gtk;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace std;
using namespace PBD;
main_vbox->pack_start (*top_vbox, false, false);
- _name.set_text (_group->name ());
_active.set_active (_group->is_active ());
- _color.set_color (GroupTabs::group_color (_group));
+
+ Gdk::Color c;
+ set_color_from_rgba (c, GroupTabs::group_color (_group));
+ _color.set_color (c);
VBox* options_box = manage (new VBox);
options_box->set_spacing (6);
l->set_use_markup ();
options_box->pack_start (*l, false, true);
- _name.signal_activate ().connect (sigc::bind (sigc::mem_fun (*this, &Dialog::response), RESPONSE_OK));
-
_gain.set_active (_group->is_gain());
_relative.set_active (_group->is_relative());
_mute.set_active (_group->is_mute());
_share_color.set_active (_group->is_color());
_share_monitoring.set_active (_group->is_monitoring());
+ if (_group->name ().empty()) {
+ _initial_name = "1";
+ while (!unique_name (_initial_name)) {
+ _initial_name = bump_name_number (_initial_name);
+ }
+ _name.set_text (_initial_name);
+ update();
+ } else {
+ _name.set_text (_initial_name);
+ }
+
+ _name.signal_activate ().connect (sigc::bind (sigc::mem_fun (*this, &Dialog::response), RESPONSE_OK));
_name.signal_changed().connect (sigc::mem_fun (*this, &RouteGroupDialog::update));
_active.signal_toggled().connect (sigc::mem_fun (*this, &RouteGroupDialog::update));
_color.signal_color_set().connect (sigc::mem_fun (*this, &RouteGroupDialog::update));
return Gtk::RESPONSE_CANCEL;
}
- if (unique_name ()) {
+ if (unique_name (_name.get_text())) {
/* not cancelled and the name is ok, so all is well */
return false;
}
_group->set_name (_initial_name);
MessageDialog msg (
- _("A route group of this name already exists. Please use a different name."),
+ _("The group name is not unique. Please use a different name."),
false,
Gtk::MESSAGE_ERROR,
Gtk::BUTTONS_OK,
_group->apply_changes (plist);
- GroupTabs::set_group_color (_group, _color.get_color ());
+ GroupTabs::set_group_color (_group, gdk_color_to_rgba (_color.get_color ()));
}
void
/** @return true if the current group's name is unique accross the session */
bool
-RouteGroupDialog::unique_name () const
+RouteGroupDialog::unique_name (std::string const name) const
{
+ if (name.empty()) return false; // do not allow empty name, empty means unset.
list<RouteGroup*> route_groups = _group->session().route_groups ();
list<RouteGroup*>::iterator i = route_groups.begin ();
- while (i != route_groups.end() && ((*i)->name() != _name.get_text() || *i == _group)) {
+ while (i != route_groups.end() && ((*i)->name() != name || *i == _group)) {
++i;
}
void gain_toggled ();
void update ();
- bool unique_name () const;
+ bool unique_name (std::string const name) const;
};
#include <gtkmm2ext/window_title.h>
#include "ardour/audioengine.h"
+#include "ardour/audio_track.h"
#include "ardour/plugin.h"
#include "ardour/plugin_insert.h"
#include "ardour/plugin_manager.h"
#include "return_ui.h"
#include "route_params_ui.h"
#include "send_ui.h"
-#include "utils.h"
#include "i18n.h"
}
}
+void
+RouteParams_UI::map_frozen()
+{
+ ENSURE_GUI_THREAD (*this, &RouteParams_UI::map_frozen)
+ boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack>(_route);
+ if (at && insert_box) {
+ switch (at->freeze_state()) {
+ case AudioTrack::Frozen:
+ insert_box->set_sensitive (false);
+ //hide_redirect_editors (); // TODO hide editor windows
+ break;
+ default:
+ insert_box->set_sensitive (true);
+ // XXX need some way, maybe, to retoggle redirect editors
+ break;
+ }
+ }
+}
+
void
RouteParams_UI::setup_processor_boxes()
{
insert_box = new ProcessorBox (_session, boost::bind (&RouteParams_UI::plugin_selector, this), _rr_selection, 0);
insert_box->set_route (_route);
+ boost::shared_ptr<AudioTrack> at = boost::dynamic_pointer_cast<AudioTrack>(_route);
+ if (at) {
+ at->FreezeChange.connect (route_connections, invalidator (*this), boost::bind (&RouteParams_UI::map_frozen, this), gui_context());
+ }
redir_hpane.pack1 (*insert_box);
insert_box->ProcessorSelected.connect (sigc::mem_fun(*this, &RouteParams_UI::redirect_selected));
boost::shared_ptr<ARDOUR::Route> _route;
PBD::ScopedConnection _route_processors_connection;
+ PBD::ScopedConnectionList route_connections;
boost::shared_ptr<ARDOUR::Processor> _processor;
PBD::ScopedConnection _processor_going_away_connection;
void route_property_changed (const PBD::PropertyChange&, boost::weak_ptr<ARDOUR::Route> route);
void route_removed (boost::weak_ptr<ARDOUR::Route> route);
+ void map_frozen ();
void route_selected();
#include "i18n.h"
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Gtkmm2ext;
using namespace Gtk;
, gm (sess, true, 125, 18)
, _ignore_set_layer_display (false)
{
+ number_label.set_corner_radius(2);
+ number_label.set_name("tracknumber label");
+ number_label.set_alignment(.5, .5);
+
+ sess->config.ParameterChanged.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::parameter_changed, this, _1), gui_context());
}
void
_route->meter_change.connect (*this, invalidator (*this), bind (&RouteTimeAxisView::meter_changed, this), gui_context());
_route->input()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
_route->output()->changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::io_changed, this, _1, _2), gui_context());
+ _route->track_number_changed.connect (*this, invalidator (*this), boost::bind (&RouteTimeAxisView::label_view, this), gui_context());
controls_table.attach (*mute_button, 6, 7, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 0, 0);
ARDOUR_UI::instance()->set_tip(automation_button, _("Automation"));
}
+ update_track_number_visibility();
label_view ();
if (!ARDOUR::Profile->get_trx()) {
void
RouteTimeAxisView::label_view ()
{
- string x = _route->name();
-
- if (x != name_label.get_text()) {
+ string x = _route->name ();
+ if (x != name_label.get_text ()) {
name_label.set_text (x);
}
+ const int64_t track_number = _route->track_number ();
+ if (track_number == 0) {
+ number_label.set_text ("");
+ } else {
+ number_label.set_text (PBD::to_string (abs(_route->track_number ()), std::dec));
+ }
+}
+
+void
+RouteTimeAxisView::update_track_number_visibility ()
+{
+ bool show_label = _session->config.get_track_name_number();
+
+ if (_route && _route->is_master()) {
+ show_label = false;
+ }
+ //if (show_label == number_label.is_visible()) { return; }
+ if (number_label.get_parent()) {
+ controls_table.remove (number_label);
+ }
+ if (name_hbox.get_parent()) {
+ controls_table.remove (name_hbox);
+ }
+ if (show_label) {
+ controls_table.attach (number_label, 0, 1, 0, 1, Gtk::SHRINK, Gtk::FILL|Gtk::EXPAND, 3, 0);
+ controls_table.attach (name_hbox, 1, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
+ number_label.set_size_request(3 + _session->track_number_decimals() * 8, -1);
+ name_hbox.show ();
+ number_label.show ();
+ } else {
+ controls_table.attach (name_hbox, 0, 5, 0, 1, Gtk::FILL|Gtk::EXPAND, Gtk::FILL|Gtk::EXPAND, 3, 0);
+ name_hbox.show ();
+ number_label.hide ();
+ }
+}
+
+void
+RouteTimeAxisView::parameter_changed (string const & p)
+{
+ if (p == "track-name-number") {
+ update_track_number_visibility();
+ }
}
void
} else if (active > 0 && inactive > 0) {
i->set_inconsistent (true);
}
+ i->set_sensitive(! _session->transport_rolling());
i->signal_activate().connect (sigc::bind (sigc::mem_fun (*this, &RouteUI::set_route_active), click_sets_active, true));
items.push_back (SeparatorElem());
ArdourButton route_group_button;
ArdourButton playlist_button;
ArdourButton automation_button;
+ ArdourButton number_label;
Gtk::Menu subplugin_menu;
Gtk::Menu* automation_action_menu;
void remove_child (boost::shared_ptr<TimeAxisView>);
void update_playlist_tip ();
+ void parameter_changed (std::string const & p);
+ void update_track_number_visibility();
};
#endif /* __ardour_route_time_axis_h__ */
using namespace Gtk;
using namespace Gtkmm2ext;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
uint32_t RouteUI::_max_invert_buttons = 3;
_mute_release->routes = copy;
}
+ DisplaySuspender ds;
_session->set_mute (copy, !_route->muted());
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
rl->push_back (_route);
}
+ DisplaySuspender ds;
_session->set_mute (rl, !_route->muted(), Session::rt_cleanup, true);
}
{
if (!_i_am_the_modifier) {
if (_mute_release){
+ DisplaySuspender ds;
_session->set_mute (_mute_release->routes, _mute_release->active, Session::rt_cleanup, true);
delete _mute_release;
_mute_release = 0;
_solo_release->routes = _session->get_routes ();
}
+ DisplaySuspender ds;
if (Config->get_solo_control_is_listen_control()) {
_session->set_listen (_session->get_routes(), !_route->listening_via_monitor(), Session::rt_cleanup, true);
} else {
if (Config->get_solo_control_is_listen_control()) {
/* ??? we need a just_one_listen() method */
} else {
+ DisplaySuspender ds;
_session->set_just_one_solo (_route, true);
}
rl->push_back (_route);
}
+ DisplaySuspender ds;
if (Config->get_solo_control_is_listen_control()) {
_session->set_listen (rl, !_route->listening_via_monitor(), Session::rt_cleanup, true);
} else {
_solo_release->routes = rl;
}
+ DisplaySuspender ds;
if (Config->get_solo_control_is_listen_control()) {
_session->set_listen (rl, !_route->listening_via_monitor());
} else {
if (_solo_release->exclusive) {
} else {
- if (Config->get_solo_control_is_listen_control()) {
- _session->set_listen (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
- } else {
- _session->set_solo (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
- }
+ DisplaySuspender ds;
+ if (Config->get_solo_control_is_listen_control()) {
+ _session->set_listen (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
+ } else {
+ _session->set_solo (_solo_release->routes, _solo_release->active, Session::rt_cleanup, true);
+ }
}
delete _solo_release;
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
+ DisplaySuspender ds;
_session->set_record_enabled (_session->get_routes(), !rec_enable_button->active_state());
} else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
rl.reset (new RouteList);
rl->push_back (_route);
}
-
+
+ DisplaySuspender ds;
_session->set_record_enabled (rl, !rec_enable_button->active_state(), Session::rt_cleanup, true);
}
boost::shared_ptr<RouteList> rl (new RouteList);
rl->push_back (route());
+ DisplaySuspender ds;
_session->set_record_enabled (rl, !rec_enable_button->active_state());
}
}
rl->push_back (route());
}
+ DisplaySuspender ds;
_session->set_monitoring (rl, mc, Session::rt_cleanup, true);
return true;
bool
RouteUI::solo_isolate_button_release (GdkEventButton* ev)
{
- if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
- return true;
- }
+ if (ev->type == GDK_2BUTTON_PRESS || ev->type == GDK_3BUTTON_PRESS) {
+ return true;
+ }
- bool view = solo_isolated_led->active_state();
- bool model = _route->solo_isolated();
+ bool view = solo_isolated_led->active_state();
+ bool model = _route->solo_isolated();
- /* called BEFORE the view has changed */
+ /* called BEFORE the view has changed */
- if (ev->button == 1) {
- if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
+ if (ev->button == 1) {
+ if (Keyboard::modifier_state_equals (ev->state, Keyboard::ModifierMask (Keyboard::PrimaryModifier|Keyboard::TertiaryModifier))) {
- if (model) {
- /* disable isolate for all routes */
- _session->set_solo_isolated (_session->get_routes(), false, Session::rt_cleanup, true);
- }
+ if (model) {
+ /* disable isolate for all routes */
+ DisplaySuspender ds;
+ _session->set_solo_isolated (_session->get_routes(), false, Session::rt_cleanup, true);
+ }
- } else {
- if (model == view) {
+ } else {
+ if (model == view) {
- /* flip just this route */
+ /* flip just this route */
- boost::shared_ptr<RouteList> rl (new RouteList);
- rl->push_back (_route);
- _session->set_solo_isolated (rl, !view, Session::rt_cleanup, true);
- }
- }
- }
+ boost::shared_ptr<RouteList> rl (new RouteList);
+ rl->push_back (_route);
+ DisplaySuspender ds;
+ _session->set_solo_isolated (rl, !view, Session::rt_cleanup, true);
+ }
+ }
+ }
- return true;
+ return true;
}
bool
RouteGroup* g = _route->route_group ();
if (g && g->is_color()) {
- return GroupTabs::group_color (g);
+ Gdk::Color c;
+ set_color_from_rgba (c, GroupTabs::group_color (g));
+ return c;
}
return _color;
--- /dev/null
+/*
+ Copyright (C) 2014 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 <gtkmm/stock.h>
+
+#include "actions.h"
+#include "ruler_dialog.h"
+
+#include "i18n.h"
+
+RulerDialog::RulerDialog ()
+ : ArdourDialog (_("Rulers"))
+{
+ add_button (Gtk::Stock::OK, Gtk::RESPONSE_ACCEPT);
+
+ get_vbox()->pack_start (samples_button);
+ get_vbox()->pack_start (timecode_button);
+ get_vbox()->pack_start (minsec_button);
+ get_vbox()->pack_start (bbt_button);
+ get_vbox()->pack_start (tempo_button);
+ get_vbox()->pack_start (meter_button);
+ get_vbox()->pack_start (loop_punch_button);
+ get_vbox()->pack_start (range_button);
+ get_vbox()->pack_start (mark_button);
+ get_vbox()->pack_start (cdmark_button);
+ get_vbox()->pack_start (video_button);
+
+ get_vbox()->show_all ();
+
+ connect_action (samples_button, "samples-ruler");
+ connect_action (timecode_button, "timecode-ruler");
+ connect_action (minsec_button, "minsec-ruler");
+ connect_action (bbt_button, "bbt-ruler");
+ connect_action (tempo_button, "tempo-ruler");
+ connect_action (meter_button, "meter-ruler");
+ connect_action (loop_punch_button, "loop-punch-ruler");
+ connect_action (range_button, "range-ruler");
+ connect_action (mark_button, "marker-ruler");
+ connect_action (cdmark_button, "cd-marker-ruler");
+ connect_action (video_button, "video-ruler");
+}
+
+RulerDialog::~RulerDialog ()
+{
+}
+
+void
+RulerDialog::on_response (int)
+{
+ hide ();
+}
+
+void
+RulerDialog::connect_action (Gtk::CheckButton& button, std::string const &action_name_part)
+{
+ std::string action_name = "toggle-";
+ action_name += action_name_part;
+
+ Glib::RefPtr<Gtk::Action> act = ActionManager::get_action ("Rulers", action_name.c_str());
+ if (!act) {
+ return;
+ }
+
+ Glib::RefPtr<Gtk::ToggleAction> tact = Glib::RefPtr<Gtk::ToggleAction>::cast_dynamic (act);
+ if (!tact) {
+ return;
+ }
+
+ tact->connect_proxy (button);
+}
--- /dev/null
+/*
+ Copyright (C) 2014 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 __gtk_ardour_ruler_h__
+#define __gtk_ardour_ruler _h__
+
+#include <string>
+#include <gtkmm/checkbutton.h>
+
+#include "ardour_dialog.h"
+
+
+class Editor;
+
+class RulerDialog : public ArdourDialog
+{
+ public:
+ RulerDialog ();
+ ~RulerDialog ();
+
+ private:
+ Gtk::CheckButton samples_button;
+ Gtk::CheckButton timecode_button;
+ Gtk::CheckButton minsec_button;
+ Gtk::CheckButton bbt_button;
+ Gtk::CheckButton tempo_button;
+ Gtk::CheckButton meter_button;
+ Gtk::CheckButton loop_punch_button;
+ Gtk::CheckButton range_button;
+ Gtk::CheckButton mark_button;
+ Gtk::CheckButton cdmark_button;
+ Gtk::CheckButton video_button;
+
+ void connect_action (Gtk::CheckButton& button, std::string const &action_name_part);
+ void on_response (int);
+};
+
+#endif /* __gtk_ardour_add_route_dialog_h__ */
#include "ardour/send.h"
#include "ardour/rc_configuration.h"
-#include "utils.h"
#include "send_ui.h"
#include "io_selector.h"
#include "ardour_ui.h"
#include "pbd/replace_all.h"
#include "pbd/whitespace.h"
#include "pbd/stacktrace.h"
+#include "pbd/stl_delete.h"
#include "pbd/openuri.h"
#include "ardour/audioengine.h"
using namespace Glib;
using namespace PBD;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
static string poor_mans_glob (string path)
{
/* existing session chosen from file chooser */
return Glib::path_get_dirname (existing_session_chooser.get_current_folder ());
} else {
- std::string legal_session_folder_name = legalize_for_path (new_name_entry.get_text());
+ std::string val = new_name_entry.get_text();
+ strip_whitespace_edges (val);
+ std::string legal_session_folder_name = legalize_for_path (val);
return Glib::build_filename (new_folder_chooser.get_current_folder(), legal_session_folder_name);
}
}
string image_path;
- if (find_file_in_search_path (ardour_data_search_path(), "small-splash.png", image_path)) {
+ if (find_file (ardour_data_search_path(), "small-splash.png", image_path)) {
Gtk::Image* image;
if ((image = manage (new Gtk::Image (image_path))) != 0) {
hbox->pack_start (*image, false, false);
recent_session_display.signal_row_activated().connect (sigc::mem_fun (*this, &SessionDialog::recent_row_activated));
centering_vbox->pack_start (recent_label, false, false, 12);
- centering_vbox->pack_start (recent_scroller, false, true);
+ centering_vbox->pack_start (recent_scroller, true, true);
/* Browse button */
get_state_files_in_directory (*i, state_file_paths);
- vector<string*>* states;
+ vector<string> states;
vector<const gchar*> item;
string dirname = *i;
/* now get available states for this session */
- if ((states = Session::possible_states (dirname)) == 0) {
+ states = Session::possible_states (dirname);
+
+ if (states.empty()) {
/* no state file? */
continue;
}
float sr;
SampleFormat sf;
- std::string s = Glib::build_filename (dirname, state_file_names.front() + statefile_suffix);
+ std::string state_file_basename = state_file_names.front();
+
+ std::string s = Glib::build_filename (dirname, state_file_basename + statefile_suffix);
- row[recent_session_columns.visible_name] = Glib::path_get_basename (dirname);
row[recent_session_columns.fullpath] = dirname; /* just the dir, but this works too */
row[recent_session_columns.tip] = Glib::Markup::escape_text (dirname);
++session_snapshot_count;
if (state_file_names.size() > 1) {
+ // multiple session files in the session directory - show the directory name.
+ // if there's not a session file with the same name as the session directory,
+ // opening the parent item will fail, but expanding it will show the session
+ // files that actually exist, and the right one can then be opened.
+ row[recent_session_columns.visible_name] = Glib::path_get_basename (dirname);
// add the children
-
for (std::vector<std::string>::iterator i2 = state_file_names.begin(); i2 != state_file_names.end(); ++i2) {
Gtk::TreeModel::Row child_row = *(recent_session_model->append (row.children()));
++session_snapshot_count;
}
+ } else {
+ // only a single session file in the directory - show its actual name.
+ row[recent_session_columns.visible_name] = state_file_basename;
}
}
SessionDialog::existing_session_selected ()
{
_existing_session_chooser_used = true;
+ recent_session_display.get_selection()->unselect_all();
/* mark this sensitive in case we come back here after a failed open
* attempt and the user has hacked up the fix. sigh.
*/
struct RecentSessionsSorter {
bool operator() (std::pair<std::string,std::string> a, std::pair<std::string,std::string> b) const {
- return cmp_nocase(a.first, b.first) == -1;
+ return ARDOUR::cmp_nocase(a.first, b.first) == -1;
}
};
/* FADES */
- ComboOption<CrossfadeChoice>* cfc = new ComboOption<CrossfadeChoice> (
- "xfade-choice",
- _("Default crossfade type"),
- sigc::mem_fun (*_session_config, &SessionConfiguration::get_xfade_choice),
- sigc::mem_fun (*_session_config, &SessionConfiguration::set_xfade_choice)
- );
-
- cfc->add (ConstantPowerMinus3dB, _("Constant power (-3dB) crossfade"));
- cfc->add (ConstantPowerMinus6dB, _("Linear (-6dB) crossfade"));
-
- add_option (_("Fades"), cfc);
-
add_option (_("Fades"), new SpinOption<float> (
_("destructive-xfade-seconds"),
_("Destructive crossfade length"),
add_option (_("Media"), hf);
- add_option (_("Media"), new OptionEditorHeading (_("File locations")));
+ add_option (_("Locations"), new OptionEditorHeading (_("File locations")));
SearchPathOption* spo = new SearchPathOption ("audio-search-path", _("Search for audio files in:"),
_session->path(),
sigc::mem_fun (*_session_config, &SessionConfiguration::get_audio_search_path),
sigc::mem_fun (*_session_config, &SessionConfiguration::set_audio_search_path));
- add_option (_("Media"), spo);
+ add_option (_("Locations"), spo);
spo = new SearchPathOption ("midi-search-path", _("Search for MIDI files in:"),
_session->path(),
sigc::mem_fun (*_session_config, &SessionConfiguration::get_midi_search_path),
sigc::mem_fun (*_session_config, &SessionConfiguration::set_midi_search_path));
- add_option (_("Media"), spo);
+ add_option (_("Locations"), spo);
+
+ /* File Naming */
+
+ add_option (_("Filenames"), new OptionEditorHeading (_("File Naming")));
+
+ BoolOption *bo;
+
+ bo = new BoolOption (
+ "track-name-number",
+ _("Prefix Track number"),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::get_track_name_number),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::set_track_name_number)
+ );
+ Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
+ _("Adds the current track number to the beginning of the recorded file name."));
+ add_option (_("Filenames"), bo);
+
+ bo = new BoolOption (
+ "track-name-take",
+ _("Prefix Take Name"),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::get_track_name_take),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::set_track_name_take)
+ );
+ Gtkmm2ext::UI::instance()->set_tip (bo->tip_widget(),
+ _("Adds the Take Name to the beginning of the recorded file name."));
+ add_option (_("Filenames"), bo);
+
+ _take_name = new EntryOption (
+ "take-name",
+ _("Take Name"),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::get_take_name),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::set_take_name)
+ );
+ _take_name->set_invalid_chars(".");
+ _take_name->set_sensitive(_session_config->get_track_name_take());
+
+ add_option (_("Filenames"), _take_name);
/* Monitoring */
sigc::mem_fun (*this, &SessionOptionEditor::get_use_monitor_section),
sigc::mem_fun (*this, &SessionOptionEditor::set_use_monitor_section)
));
-
- /* Misc */
-
- add_option (_("Misc"), new OptionEditorHeading (_("MIDI Options")));
-
- add_option (_("Misc"), new BoolOption (
- "midi-copy-is-fork",
- _("MIDI region copies are independent"),
- sigc::mem_fun (*_session_config, &SessionConfiguration::get_midi_copy_is_fork),
- sigc::mem_fun (*_session_config, &SessionConfiguration::set_midi_copy_is_fork)
- ));
-
- ComboOption<InsertMergePolicy>* li = new ComboOption<InsertMergePolicy> (
- "insert-merge-policy",
- _("Policy for handling overlapping notes\n on the same MIDI channel"),
- sigc::mem_fun (*_session_config, &SessionConfiguration::get_insert_merge_policy),
- sigc::mem_fun (*_session_config, &SessionConfiguration::set_insert_merge_policy)
- );
-
- li->add (InsertMergeReject, _("never allow them"));
- li->add (InsertMergeRelax, _("don't do anything in particular"));
- li->add (InsertMergeReplace, _("replace any overlapped existing note"));
- li->add (InsertMergeTruncateExisting, _("shorten the overlapped existing note"));
- li->add (InsertMergeTruncateAddition, _("shorten the overlapping new note"));
- li->add (InsertMergeExtend, _("replace both overlapping notes with a single note"));
-
- add_option (_("Misc"), li);
-
- add_option (_("Misc"), new OptionEditorHeading (_("Glue to bars and beats")));
-
- add_option (_("Misc"), new BoolOption (
- "glue-new-markers-to-bars-and-beats",
- _("Glue new markers to bars and beats"),
- sigc::mem_fun (*_session_config, &SessionConfiguration::get_glue_new_markers_to_bars_and_beats),
- sigc::mem_fun (*_session_config, &SessionConfiguration::set_glue_new_markers_to_bars_and_beats)
- ));
-
- add_option (_("Misc"), new BoolOption (
- "glue-new-regions-to-bars-and-beats",
- _("Glue new regions to bars and beats"),
- sigc::mem_fun (*_session_config, &SessionConfiguration::get_glue_new_regions_to_bars_and_beats),
- sigc::mem_fun (*_session_config, &SessionConfiguration::set_glue_new_regions_to_bars_and_beats)
- ));
-
+ /* Meterbridge */
add_option (_("Meterbridge"), new OptionEditorHeading (_("Route Display")));
add_option (_("Meterbridge"), new BoolOption (
sigc::mem_fun (*_session_config, &SessionConfiguration::set_show_name_on_meterbridge)
));
+ /* Misc */
+
+ add_option (_("Misc"), new OptionEditorHeading (_("MIDI Options")));
+
+ add_option (_("Misc"), new BoolOption (
+ "midi-copy-is-fork",
+ _("MIDI region copies are independent"),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::get_midi_copy_is_fork),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::set_midi_copy_is_fork)
+ ));
+
+ ComboOption<InsertMergePolicy>* li = new ComboOption<InsertMergePolicy> (
+ "insert-merge-policy",
+ _("Policy for handling overlapping notes\n on the same MIDI channel"),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::get_insert_merge_policy),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::set_insert_merge_policy)
+ );
+
+ li->add (InsertMergeReject, _("never allow them"));
+ li->add (InsertMergeRelax, _("don't do anything in particular"));
+ li->add (InsertMergeReplace, _("replace any overlapped existing note"));
+ li->add (InsertMergeTruncateExisting, _("shorten the overlapped existing note"));
+ li->add (InsertMergeTruncateAddition, _("shorten the overlapping new note"));
+ li->add (InsertMergeExtend, _("replace both overlapping notes with a single note"));
+
+ add_option (_("Misc"), li);
+
+ add_option (_("Misc"), new OptionEditorHeading (_("Glue to bars and beats")));
+
+ add_option (_("Misc"), new BoolOption (
+ "glue-new-markers-to-bars-and-beats",
+ _("Glue new markers to bars and beats"),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::get_glue_new_markers_to_bars_and_beats),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::set_glue_new_markers_to_bars_and_beats)
+ ));
+
+ add_option (_("Misc"), new BoolOption (
+ "glue-new-regions-to-bars-and-beats",
+ _("Glue new regions to bars and beats"),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::get_glue_new_regions_to_bars_and_beats),
+ sigc::mem_fun (*_session_config, &SessionConfiguration::set_glue_new_regions_to_bars_and_beats)
+ ));
+
+ add_option (_("Misc"), new OptionEditorHeading (_("Defaults")));
+
+ Gtk::Button* btn = Gtk::manage (new Gtk::Button (_("Use these settings as defaults")));
+ btn->signal_clicked().connect (sigc::mem_fun (*this, &SessionOptionEditor::save_defaults));
+ add_option (_("Misc"), new FooOption (btn));
+
}
void
_vpu->set_sensitive(true);
}
}
- if (p == "timecode-format") {
+ else if (p == "timecode-format") {
/* update offset clocks */
parameter_changed("timecode-generator-offset");
parameter_changed("slave-timecode-offset");
}
+ else if (p == "track-name-take") {
+ _take_name->set_sensitive(_session_config->get_track_name_take());
+ }
}
/* the presence of absence of a monitor section is not really a regular session
{
return _session->monitor_out() != 0;
}
+
+void
+SessionOptionEditor::save_defaults ()
+{
+ _session->save_default_options();
+}
bool get_use_monitor_section ();
ComboOption<float>* _vpu;
+ EntryOption* _take_name;
+
+ void save_defaults ();
};
#endif /* __gtk_ardour_session_option_editor_h__ */
-/* sfdb_freesound_mootcher.cpp **********************************************************************\r
-\r
- Adapted for Ardour by Ben Loftis, March 2008\r
- Updated to new Freesound API by Colin Fletcher, November 2011\r
-\r
- Mootcher 23-8-2005\r
-\r
- Mootcher Online Access to thefreesoundproject website\r
- http://freesound.iua.upf.edu/\r
-\r
- GPL 2005 Jorn Lemon\r
- mail for questions/remarks: mootcher@twistedlemon.nl\r
- or go to the freesound website forum\r
-\r
- -----------------------------------------------------------------\r
-\r
- Includes:\r
- curl.h (version 7.14.0)\r
- Librarys:\r
- libcurl.lib\r
-\r
- -----------------------------------------------------------------\r
- Licence GPL:\r
-\r
- This program is free software; you can redistribute it and/or\r
- modify it under the terms of the GNU General Public License\r
- as published by the Free Software Foundation; either version 2\r
- of the License, or (at your option) any later version.\r
-\r
- This program is distributed in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
- GNU General Public License for more details.\r
-\r
- You should have received a copy of the GNU General Public License\r
- along with this program; if not, write to the Free Software\r
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
-\r
-\r
-*************************************************************************************/\r
-#include "sfdb_freesound_mootcher.h"\r
-\r
-#include "pbd/xml++.h"\r
-#include "pbd/error.h"\r
-\r
-#include <sys/stat.h>\r
-#include <sys/types.h>\r
-#include <iostream>\r
-\r
-#include <glib.h>\r
-#include <glib/gstdio.h>\r
-\r
-#include "i18n.h"\r
-\r
-#include "ardour/audio_library.h"\r
-#include "ardour/rc_configuration.h"\r
-#include "pbd/pthread_utils.h"\r
-#include "gui_thread.h"\r
-\r
-using namespace PBD;\r
-\r
-static const std::string base_url = "http://www.freesound.org/api";\r
-static const std::string api_key = "9d77cb8d841b4bcfa960e1aae62224eb"; // ardour3\r
-\r
-//------------------------------------------------------------------------\r
-Mootcher::Mootcher()\r
- : curl(curl_easy_init())\r
-{\r
- cancel_download_btn.set_label (_("Cancel"));\r
- progress_hbox.pack_start (progress_bar, true, true);\r
- progress_hbox.pack_end (cancel_download_btn, false, false);\r
- progress_bar.show();\r
- cancel_download_btn.show();\r
- cancel_download_btn.signal_clicked().connect(sigc::mem_fun (*this, &Mootcher::cancelDownload));\r
-};\r
-//------------------------------------------------------------------------\r
-Mootcher:: ~Mootcher()\r
-{\r
- curl_easy_cleanup(curl);\r
-}\r
-\r
-//------------------------------------------------------------------------\r
-\r
-void Mootcher::ensureWorkingDir ()\r
-{\r
- std::string p = ARDOUR::Config->get_freesound_download_dir();\r
-\r
- if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {\r
- if (g_mkdir_with_parents (p.c_str(), 0775) != 0) {\r
- PBD::error << "Unable to create Mootcher working dir" << endmsg;\r
- }\r
- }\r
- basePath = p;\r
-#ifdef PLATFORM_WINDOWS\r
- std::string replace = "/";\r
- size_t pos = basePath.find("\\");\r
- while( pos != std::string::npos ){\r
- basePath.replace(pos, 1, replace);\r
- pos = basePath.find("\\");\r
- }\r
-#endif\r
-}\r
- \r
-\r
-//------------------------------------------------------------------------\r
-size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)\r
-{\r
- register int realsize = (int)(size * nmemb);\r
- struct MemoryStruct *mem = (struct MemoryStruct *)data;\r
-\r
- mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);\r
-\r
- if (mem->memory) {\r
- memcpy(&(mem->memory[mem->size]), ptr, realsize);\r
- mem->size += realsize;\r
- mem->memory[mem->size] = 0;\r
- }\r
- return realsize;\r
-}\r
-\r
-\r
-//------------------------------------------------------------------------\r
-\r
-std::string Mootcher::sortMethodString(enum sortMethod sort)\r
-{\r
-// given a sort type, returns the string value to be passed to the API to\r
-// sort the results in the requested way.\r
-\r
- switch (sort) {\r
- case sort_duration_desc: return "duration_desc"; \r
- case sort_duration_asc: return "duration_asc";\r
- case sort_created_desc: return "created_desc";\r
- case sort_created_asc: return "created_asc";\r
- case sort_downloads_desc: return "downloads_desc";\r
- case sort_downloads_asc: return "downloads_asc";\r
- case sort_rating_desc: return "rating_desc";\r
- case sort_rating_asc: return "rating_asc";\r
- default: return ""; \r
- }\r
-}\r
-\r
-//------------------------------------------------------------------------\r
-void Mootcher::setcUrlOptions()\r
-{\r
- // basic init for curl\r
- curl_global_init(CURL_GLOBAL_ALL);\r
- // some servers don't like requests that are made without a user-agent field, so we provide one\r
- curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");\r
- // setup curl error buffer\r
- curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);\r
- // Allow redirection\r
- curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);\r
- \r
- // Allow connections to time out (without using signals)\r
- curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);\r
- curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);\r
-\r
-\r
-}\r
-\r
-std::string Mootcher::doRequest(std::string uri, std::string params)\r
-{\r
- std::string result;\r
- struct MemoryStruct xml_page;\r
- xml_page.memory = NULL;\r
- xml_page.size = 0;\r
-\r
- setcUrlOptions();\r
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);\r
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &xml_page);\r
-\r
- // curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);\r
- // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());\r
- // curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);\r
-\r
- // the url to get\r
- std::string url = base_url + uri + "?";\r
- if (params != "") {\r
- url += params + "&api_key=" + api_key + "&format=xml";\r
- } else {\r
- url += "api_key=" + api_key + "&format=xml";\r
- }\r
- \r
- curl_easy_setopt(curl, CURLOPT_URL, url.c_str() );\r
- \r
- // perform online request\r
- CURLcode res = curl_easy_perform(curl);\r
- if( res != 0 ) {\r
- error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;\r
- return "";\r
- }\r
-\r
- // free the memory\r
- if (xml_page.memory) {\r
- result = xml_page.memory;\r
- }\r
- \r
- free (xml_page.memory);\r
- xml_page.memory = NULL;\r
- xml_page.size = 0;\r
-\r
- return result;\r
-}\r
-\r
-\r
-std::string Mootcher::searchSimilar(std::string id)\r
-{\r
- std::string params = "";\r
-\r
- params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve";\r
- params += "&num_results=100";\r
-\r
- return doRequest("/sounds/" + id + "/similar", params);\r
-}\r
-\r
-//------------------------------------------------------------------------\r
-\r
-std::string Mootcher::searchText(std::string query, int page, std::string filter, enum sortMethod sort)\r
-{\r
- std::string params = "";\r
- char buf[24];\r
-\r
- if (page > 1) {\r
- snprintf(buf, 23, "p=%d&", page);\r
- params += buf;\r
- }\r
-\r
- char *eq = curl_easy_escape(curl, query.c_str(), query.length());\r
- params += "q=\"" + std::string(eq) + "\"";\r
- free(eq);\r
-\r
- if (filter != "") {\r
- char *ef = curl_easy_escape(curl, filter.c_str(), filter.length());\r
- params += "&f=" + std::string(ef);\r
- free(ef);\r
- }\r
- \r
- if (sort)\r
- params += "&s=" + sortMethodString(sort);\r
-\r
- params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve";\r
- params += "&sounds_per_page=100";\r
-\r
- return doRequest("/sounds/search", params);\r
-}\r
-\r
-//------------------------------------------------------------------------\r
-\r
-std::string Mootcher::getSoundResourceFile(std::string ID)\r
-{\r
-\r
- std::string originalSoundURI;\r
- std::string audioFileName;\r
- std::string xml;\r
-\r
-\r
- // download the xmlfile into xml_page\r
- xml = doRequest("/sounds/" + ID, "");\r
-\r
- XMLTree doc;\r
- doc.read_buffer( xml.c_str() );\r
- XMLNode *freesound = doc.root();\r
-\r
- // if the page is not a valid xml document with a 'freesound' root\r
- if (freesound == NULL) {\r
- error << _("getSoundResourceFile: There is no valid root in the xml file") << endmsg;\r
- return "";\r
- }\r
-\r
- if (strcmp(doc.root()->name().c_str(), "response") != 0) {\r
- error << string_compose (_("getSoundResourceFile: root = %1, != response"), doc.root()->name()) << endmsg;\r
- return "";\r
- }\r
-\r
- XMLNode *name = freesound->child("original_filename");\r
-\r
- // get the file name and size from xml file\r
- if (name) {\r
-\r
- audioFileName = Glib::build_filename (basePath, ID + "-" + name->child("text")->content());\r
-\r
- //store all the tags in the database\r
- XMLNode *tags = freesound->child("tags");\r
- if (tags) {\r
- XMLNodeList children = tags->children();\r
- XMLNodeConstIterator niter;\r
- std::vector<std::string> strings;\r
- for (niter = children.begin(); niter != children.end(); ++niter) {\r
- XMLNode *node = *niter;\r
- if( strcmp( node->name().c_str(), "resource") == 0 ) {\r
- XMLNode *text = node->child("text");\r
- if (text) {\r
- // std::cerr << "tag: " << text->content() << std::endl;\r
- strings.push_back(text->content());\r
- }\r
- }\r
- }\r
- ARDOUR::Library->set_tags (std::string("//")+audioFileName, strings);\r
- ARDOUR::Library->save_changes ();\r
- }\r
- }\r
-\r
- return audioFileName;\r
-}\r
-\r
-int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)\r
-{\r
- return (int)fwrite(buffer, size, nmemb, (FILE*) file);\r
-};\r
-\r
-//------------------------------------------------------------------------\r
-\r
-void *\r
-Mootcher::threadFunc() {\r
-CURLcode res;\r
-\r
- res = curl_easy_perform (curl);\r
- fclose (theFile);\r
- curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar\r
- \r
- if (res != CURLE_OK) {\r
- /* it's not an error if the user pressed the stop button */\r
- if (res != CURLE_ABORTED_BY_CALLBACK) {\r
- error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;\r
- }\r
- remove ( (audioFileName+".part").c_str() ); \r
- } else {\r
- rename ( (audioFileName+".part").c_str(), audioFileName.c_str() );\r
- // now download the tags &c.\r
- getSoundResourceFile(ID);\r
- }\r
-\r
- return (void *) res;\r
-}\r
- \r
-void\r
-Mootcher::doneWithMootcher()\r
-{\r
-\r
- // update the sound info pane if the selection in the list box is still us \r
- sfb->refresh_display(ID, audioFileName);\r
-\r
- delete this; // this should be OK to do as long as Progress and Finished signals are always received in the order in which they are emitted\r
-}\r
-\r
-static void *\r
-freesound_download_thread_func(void *arg) \r
-{ \r
- Mootcher *thisMootcher = (Mootcher *) arg;\r
- void *res;\r
-\r
- // std::cerr << "freesound_download_thread_func(" << arg << ")" << std::endl;\r
- res = thisMootcher->threadFunc();\r
-\r
- thisMootcher->Finished(); /* EMIT SIGNAL */\r
- return res;\r
-}\r
-\r
-\r
-//------------------------------------------------------------------------\r
-\r
-bool Mootcher::checkAudioFile(std::string originalFileName, std::string theID)\r
-{\r
- ensureWorkingDir();\r
- ID = theID;\r
- audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);\r
-\r
- // check to see if audio file already exists\r
- FILE *testFile = g_fopen(audioFileName.c_str(), "r");\r
- if (testFile) { \r
- fseek (testFile , 0 , SEEK_END);\r
- if (ftell (testFile) > 256) {\r
- fclose (testFile);\r
- return true;\r
- }\r
- \r
- // else file was small, probably an error, delete it \r
- fclose(testFile);\r
- remove( audioFileName.c_str() ); \r
- }\r
- return false;\r
-}\r
-\r
-\r
-bool Mootcher::fetchAudioFile(std::string originalFileName, std::string theID, std::string audioURL, SoundFileBrowser *caller)\r
-{\r
- ensureWorkingDir();\r
- ID = theID;\r
- audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);\r
-\r
- if (!curl) {\r
- return false;\r
- }\r
- // now download the actual file\r
- theFile = g_fopen( (audioFileName + ".part").c_str(), "wb" );\r
-\r
- if (!theFile) {\r
- return false;\r
- }\r
- \r
- // create the download url\r
- audioURL += "?api_key=" + api_key;\r
-\r
- setcUrlOptions();\r
- curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );\r
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);\r
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);\r
-\r
- std::string prog;\r
- prog = string_compose (_("%1"), originalFileName);\r
- progress_bar.set_text(prog);\r
-\r
- Gtk::VBox *freesound_vbox = dynamic_cast<Gtk::VBox *> (caller->notebook.get_nth_page(2));\r
- freesound_vbox->pack_start(progress_hbox, Gtk::PACK_SHRINK);\r
- progress_hbox.show();\r
- cancel_download = false;\r
- sfb = caller;\r
-\r
- curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar\r
- curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);\r
- curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, this);\r
-\r
- Progress.connect(*this, invalidator (*this), boost::bind(&Mootcher::updateProgress, this, _1, _2), gui_context());\r
- Finished.connect(*this, invalidator (*this), boost::bind(&Mootcher::doneWithMootcher, this), gui_context());\r
- pthread_t freesound_download_thread;\r
- pthread_create_and_store("freesound_import", &freesound_download_thread, freesound_download_thread_func, this);\r
-\r
- return true;\r
-}\r
-\r
-//---------\r
-\r
-void \r
-Mootcher::updateProgress(double dlnow, double dltotal) \r
-{\r
- if (dltotal > 0) {\r
- double fraction = dlnow / dltotal;\r
- // std::cerr << "progress idle: " << progress->bar->get_text() << ". " << progress->dlnow << " / " << progress->dltotal << " = " << fraction << std::endl;\r
- if (fraction > 1.0) {\r
- fraction = 1.0;\r
- } else if (fraction < 0.0) {\r
- fraction = 0.0;\r
- }\r
- progress_bar.set_fraction(fraction);\r
- }\r
-}\r
-\r
-int \r
-Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/)\r
-{\r
- // It may seem curious to pass a pointer to an instance of an object to a static\r
- // member function, but we can't use a normal member function as a curl progress callback,\r
- // and we want access to some private members of Mootcher.\r
-\r
- Mootcher *thisMootcher = (Mootcher *) caller;\r
- \r
- if (thisMootcher->cancel_download) {\r
- return -1;\r
- }\r
-\r
- thisMootcher->Progress(dlnow, dltotal); /* EMIT SIGNAL */\r
- return 0;\r
-}\r
-\r
+/* sfdb_freesound_mootcher.cpp **********************************************************************
+
+ Adapted for Ardour by Ben Loftis, March 2008
+ Updated to new Freesound API by Colin Fletcher, November 2011
+
+ Mootcher 23-8-2005
+
+ Mootcher Online Access to thefreesoundproject website
+ http://freesound.iua.upf.edu/
+
+ GPL 2005 Jorn Lemon
+ mail for questions/remarks: mootcher@twistedlemon.nl
+ or go to the freesound website forum
+
+ -----------------------------------------------------------------
+
+ Includes:
+ curl.h (version 7.14.0)
+ Librarys:
+ libcurl.lib
+
+ -----------------------------------------------------------------
+ Licence GPL:
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+*************************************************************************************/
+#include "sfdb_freesound_mootcher.h"
+
+#include "pbd/xml++.h"
+#include "pbd/error.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+#include "ardour/audio_library.h"
+#include "ardour/rc_configuration.h"
+#include "pbd/pthread_utils.h"
+#include "gui_thread.h"
+
+using namespace PBD;
+
+static const std::string base_url = "http://www.freesound.org/api";
+static const std::string api_key = "9d77cb8d841b4bcfa960e1aae62224eb"; // ardour3
+
+//------------------------------------------------------------------------
+Mootcher::Mootcher()
+ : curl(curl_easy_init())
+{
+ cancel_download_btn.set_label (_("Cancel"));
+ progress_hbox.pack_start (progress_bar, true, true);
+ progress_hbox.pack_end (cancel_download_btn, false, false);
+ progress_bar.show();
+ cancel_download_btn.show();
+ cancel_download_btn.signal_clicked().connect(sigc::mem_fun (*this, &Mootcher::cancelDownload));
+};
+//------------------------------------------------------------------------
+Mootcher:: ~Mootcher()
+{
+ curl_easy_cleanup(curl);
+}
+
+//------------------------------------------------------------------------
+
+void Mootcher::ensureWorkingDir ()
+{
+ std::string p = ARDOUR::Config->get_freesound_download_dir();
+
+ if (!Glib::file_test (p, Glib::FILE_TEST_IS_DIR)) {
+ if (g_mkdir_with_parents (p.c_str(), 0775) != 0) {
+ PBD::error << "Unable to create Mootcher working dir" << endmsg;
+ }
+ }
+ basePath = p;
+#ifdef PLATFORM_WINDOWS
+ std::string replace = "/";
+ size_t pos = basePath.find("\\");
+ while( pos != std::string::npos ){
+ basePath.replace(pos, 1, replace);
+ pos = basePath.find("\\");
+ }
+#endif
+}
+
+
+//------------------------------------------------------------------------
+size_t Mootcher::WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ register int realsize = (int)(size * nmemb);
+ struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+ mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+
+ if (mem->memory) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+ return realsize;
+}
+
+
+//------------------------------------------------------------------------
+
+std::string Mootcher::sortMethodString(enum sortMethod sort)
+{
+// given a sort type, returns the string value to be passed to the API to
+// sort the results in the requested way.
+
+ switch (sort) {
+ case sort_duration_desc: return "duration_desc";
+ case sort_duration_asc: return "duration_asc";
+ case sort_created_desc: return "created_desc";
+ case sort_created_asc: return "created_asc";
+ case sort_downloads_desc: return "downloads_desc";
+ case sort_downloads_asc: return "downloads_asc";
+ case sort_rating_desc: return "rating_desc";
+ case sort_rating_asc: return "rating_asc";
+ default: return "";
+ }
+}
+
+//------------------------------------------------------------------------
+void Mootcher::setcUrlOptions()
+{
+ // basic init for curl
+ curl_global_init(CURL_GLOBAL_ALL);
+ // some servers don't like requests that are made without a user-agent field, so we provide one
+ curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+ // setup curl error buffer
+ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
+ // Allow redirection
+ curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
+
+ // Allow connections to time out (without using signals)
+ curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30);
+
+
+}
+
+std::string Mootcher::doRequest(std::string uri, std::string params)
+{
+ std::string result;
+ struct MemoryStruct xml_page;
+ xml_page.memory = NULL;
+ xml_page.size = 0;
+
+ setcUrlOptions();
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+ // curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
+ // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postMessage.c_str());
+ // curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, -1);
+
+ // the url to get
+ std::string url = base_url + uri + "?";
+ if (params != "") {
+ url += params + "&api_key=" + api_key + "&format=xml";
+ } else {
+ url += "api_key=" + api_key + "&format=xml";
+ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, url.c_str() );
+
+ // perform online request
+ CURLcode res = curl_easy_perform(curl);
+ if( res != 0 ) {
+ error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;
+ return "";
+ }
+
+ // free the memory
+ if (xml_page.memory) {
+ result = xml_page.memory;
+ }
+
+ free (xml_page.memory);
+ xml_page.memory = NULL;
+ xml_page.size = 0;
+
+ return result;
+}
+
+
+std::string Mootcher::searchSimilar(std::string id)
+{
+ std::string params = "";
+
+ params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve";
+ params += "&num_results=100";
+
+ return doRequest("/sounds/" + id + "/similar", params);
+}
+
+//------------------------------------------------------------------------
+
+std::string Mootcher::searchText(std::string query, int page, std::string filter, enum sortMethod sort)
+{
+ std::string params = "";
+ char buf[24];
+
+ if (page > 1) {
+ snprintf(buf, 23, "p=%d&", page);
+ params += buf;
+ }
+
+ char *eq = curl_easy_escape(curl, query.c_str(), query.length());
+ params += "q=\"" + std::string(eq) + "\"";
+ free(eq);
+
+ if (filter != "") {
+ char *ef = curl_easy_escape(curl, filter.c_str(), filter.length());
+ params += "&f=" + std::string(ef);
+ free(ef);
+ }
+
+ if (sort)
+ params += "&s=" + sortMethodString(sort);
+
+ params += "&fields=id,original_filename,duration,filesize,samplerate,license,serve";
+ params += "&sounds_per_page=100";
+
+ return doRequest("/sounds/search", params);
+}
+
+//------------------------------------------------------------------------
+
+std::string Mootcher::getSoundResourceFile(std::string ID)
+{
+
+ std::string originalSoundURI;
+ std::string audioFileName;
+ std::string xml;
+
+
+ // download the xmlfile into xml_page
+ xml = doRequest("/sounds/" + ID, "");
+
+ XMLTree doc;
+ doc.read_buffer( xml.c_str() );
+ XMLNode *freesound = doc.root();
+
+ // if the page is not a valid xml document with a 'freesound' root
+ if (freesound == NULL) {
+ error << _("getSoundResourceFile: There is no valid root in the xml file") << endmsg;
+ return "";
+ }
+
+ if (strcmp(doc.root()->name().c_str(), "response") != 0) {
+ error << string_compose (_("getSoundResourceFile: root = %1, != response"), doc.root()->name()) << endmsg;
+ return "";
+ }
+
+ XMLNode *name = freesound->child("original_filename");
+
+ // get the file name and size from xml file
+ if (name) {
+
+ audioFileName = Glib::build_filename (basePath, ID + "-" + name->child("text")->content());
+
+ //store all the tags in the database
+ XMLNode *tags = freesound->child("tags");
+ if (tags) {
+ XMLNodeList children = tags->children();
+ XMLNodeConstIterator niter;
+ std::vector<std::string> strings;
+ for (niter = children.begin(); niter != children.end(); ++niter) {
+ XMLNode *node = *niter;
+ if( strcmp( node->name().c_str(), "resource") == 0 ) {
+ XMLNode *text = node->child("text");
+ if (text) {
+ // std::cerr << "tag: " << text->content() << std::endl;
+ strings.push_back(text->content());
+ }
+ }
+ }
+ ARDOUR::Library->set_tags (std::string("//")+audioFileName, strings);
+ ARDOUR::Library->save_changes ();
+ }
+ }
+
+ return audioFileName;
+}
+
+int audioFileWrite(void *buffer, size_t size, size_t nmemb, void *file)
+{
+ return (int)fwrite(buffer, size, nmemb, (FILE*) file);
+};
+
+//------------------------------------------------------------------------
+
+void *
+Mootcher::threadFunc() {
+CURLcode res;
+
+ res = curl_easy_perform (curl);
+ fclose (theFile);
+ curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
+
+ if (res != CURLE_OK) {
+ /* it's not an error if the user pressed the stop button */
+ if (res != CURLE_ABORTED_BY_CALLBACK) {
+ error << string_compose (_("curl error %1 (%2)"), res, curl_easy_strerror(res)) << endmsg;
+ }
+ remove ( (audioFileName+".part").c_str() );
+ } else {
+ rename ( (audioFileName+".part").c_str(), audioFileName.c_str() );
+ // now download the tags &c.
+ getSoundResourceFile(ID);
+ }
+
+ return (void *) res;
+}
+
+void
+Mootcher::doneWithMootcher()
+{
+
+ // update the sound info pane if the selection in the list box is still us
+ sfb->refresh_display(ID, audioFileName);
+
+ delete this; // this should be OK to do as long as Progress and Finished signals are always received in the order in which they are emitted
+}
+
+static void *
+freesound_download_thread_func(void *arg)
+{
+ Mootcher *thisMootcher = (Mootcher *) arg;
+ void *res;
+
+ // std::cerr << "freesound_download_thread_func(" << arg << ")" << std::endl;
+ res = thisMootcher->threadFunc();
+
+ thisMootcher->Finished(); /* EMIT SIGNAL */
+ return res;
+}
+
+
+//------------------------------------------------------------------------
+
+bool Mootcher::checkAudioFile(std::string originalFileName, std::string theID)
+{
+ ensureWorkingDir();
+ ID = theID;
+ audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);
+
+ // check to see if audio file already exists
+ FILE *testFile = g_fopen(audioFileName.c_str(), "r");
+ if (testFile) {
+ fseek (testFile , 0 , SEEK_END);
+ if (ftell (testFile) > 256) {
+ fclose (testFile);
+ return true;
+ }
+
+ // else file was small, probably an error, delete it
+ fclose(testFile);
+ remove( audioFileName.c_str() );
+ }
+ return false;
+}
+
+
+bool Mootcher::fetchAudioFile(std::string originalFileName, std::string theID, std::string audioURL, SoundFileBrowser *caller)
+{
+ ensureWorkingDir();
+ ID = theID;
+ audioFileName = Glib::build_filename (basePath, ID + "-" + originalFileName);
+
+ if (!curl) {
+ return false;
+ }
+ // now download the actual file
+ theFile = g_fopen( (audioFileName + ".part").c_str(), "wb" );
+
+ if (!theFile) {
+ return false;
+ }
+
+ // create the download url
+ audioURL += "?api_key=" + api_key;
+
+ setcUrlOptions();
+ curl_easy_setopt(curl, CURLOPT_URL, audioURL.c_str() );
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, audioFileWrite);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, theFile);
+
+ std::string prog;
+ prog = string_compose (_("%1"), originalFileName);
+ progress_bar.set_text(prog);
+
+ Gtk::VBox *freesound_vbox = dynamic_cast<Gtk::VBox *> (caller->notebook.get_nth_page(2));
+ freesound_vbox->pack_start(progress_hbox, Gtk::PACK_SHRINK);
+ progress_hbox.show();
+ cancel_download = false;
+ sfb = caller;
+
+ curl_easy_setopt (curl, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
+ curl_easy_setopt (curl, CURLOPT_PROGRESSFUNCTION, progress_callback);
+ curl_easy_setopt (curl, CURLOPT_PROGRESSDATA, this);
+
+ Progress.connect(*this, invalidator (*this), boost::bind(&Mootcher::updateProgress, this, _1, _2), gui_context());
+ Finished.connect(*this, invalidator (*this), boost::bind(&Mootcher::doneWithMootcher, this), gui_context());
+ pthread_t freesound_download_thread;
+ pthread_create_and_store("freesound_import", &freesound_download_thread, freesound_download_thread_func, this);
+
+ return true;
+}
+
+//---------
+
+void
+Mootcher::updateProgress(double dlnow, double dltotal)
+{
+ if (dltotal > 0) {
+ double fraction = dlnow / dltotal;
+ // std::cerr << "progress idle: " << progress->bar->get_text() << ". " << progress->dlnow << " / " << progress->dltotal << " = " << fraction << std::endl;
+ if (fraction > 1.0) {
+ fraction = 1.0;
+ } else if (fraction < 0.0) {
+ fraction = 0.0;
+ }
+ progress_bar.set_fraction(fraction);
+ }
+}
+
+int
+Mootcher::progress_callback(void *caller, double dltotal, double dlnow, double /*ultotal*/, double /*ulnow*/)
+{
+ // It may seem curious to pass a pointer to an instance of an object to a static
+ // member function, but we can't use a normal member function as a curl progress callback,
+ // and we want access to some private members of Mootcher.
+
+ Mootcher *thisMootcher = (Mootcher *) caller;
+
+ if (thisMootcher->cancel_download) {
+ return -1;
+ }
+
+ thisMootcher->Progress(dlnow, dltotal); /* EMIT SIGNAL */
+ return 0;
+}
+
#include "prompter.h"
#include "sfdb_ui.h"
#include "editing.h"
-#include "utils.h"
#include "gain_meter.h"
#include "main_clock.h"
#include "public_editor.h"
string error_msg;
- if (SMFSource::safe_midi_file_extension (path)) {
+ if (SMFSource::valid_midi_file (path)) {
boost::shared_ptr<SMFSource> ms =
boost::dynamic_pointer_cast<SMFSource> (
boost::shared_ptr<Region> r;
- if (SMFSource::safe_midi_file_extension (path)) {
+ if (SMFSource::valid_midi_file (path)) {
boost::shared_ptr<SMFSource> ms =
boost::dynamic_pointer_cast<SMFSource> (
/* See if we are thinking about importing any MIDI files */
vector<string>::iterator i = paths.begin ();
- while (i != paths.end() && SMFSource::safe_midi_file_extension (*i) == false) {
+ while (i != paths.end() && SMFSource::valid_midi_file (*i) == false) {
++i;
}
bool const have_a_midi_file = (i != paths.end ());
src_needed = true;
}
- } else if (SMFSource::safe_midi_file_extension (*i)) {
+ } else if (SMFSource::valid_midi_file (*i)) {
Evoral::SMF reader;
reader.open(*i);
--- /dev/null
+/* soundcloud_export_selector.cpp ***************************************************
+
+ Adapted for Ardour by Ben Loftis, March 2012
+
+ Licence GPL:
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+*************************************************************************************/
+#include "ardour/debug.h"
+#include "ardour/soundcloud_upload.h"
+#include "soundcloud_export_selector.h"
+
+#include <pbd/error.h>
+#include "pbd/openuri.h"
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+#include "ardour/session_metadata.h"
+#include "utils.h"
+
+SoundcloudExportSelector::SoundcloudExportSelector () :
+ sc_table (4, 3),
+ soundcloud_username_label (_("User Email"), 1.0, 0.5),
+ soundcloud_password_label (_("Password"), 1.0, 0.5),
+ soundcloud_public_checkbox (_("Make files public")),
+ soundcloud_open_checkbox (_("Open uploaded files in browser")),
+ soundcloud_download_checkbox (_("Make files downloadable")),
+ progress_bar()
+{
+
+
+ soundcloud_public_checkbox.set_name ("ExportCheckbox");
+ soundcloud_download_checkbox.set_name ("ExportCheckbox");
+ soundcloud_username_label.set_name ("ExportFormatLabel");
+ soundcloud_username_entry.set_name ("ExportFormatDisplay");
+ soundcloud_password_label.set_name ("ExportFormatLabel");
+ soundcloud_password_entry.set_name ("ExportFormatDisplay");
+
+ soundcloud_username_entry.set_text (ARDOUR::SessionMetadata::Metadata()->user_email());
+ soundcloud_password_entry.set_visibility (false);
+
+ Gtk::Frame *sc_frame = manage (new Gtk::Frame);
+ sc_frame->set_border_width (4);
+ sc_frame->set_shadow_type (Gtk::SHADOW_ETCHED_OUT);
+ sc_frame->set_name ("soundcloud_export_box");
+ pack_start (*sc_frame, false, false);
+
+ sc_table.set_border_width (4);
+ sc_table.set_col_spacings (5);
+ sc_table.set_row_spacings (5);
+ sc_frame->add (sc_table);
+
+ sc_table.attach ( *(Gtk::manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon (X_("soundcloud"))))) , 0, 1, 0, 2);
+
+ sc_table.attach (soundcloud_username_label, 0, 1, 1, 2);
+ sc_table.attach (soundcloud_username_entry, 1, 3, 1, 2);
+ sc_table.attach (soundcloud_password_label, 0, 1, 2, 3);
+ sc_table.attach (soundcloud_password_entry, 1, 3, 2, 3);
+ sc_table.attach (soundcloud_public_checkbox, 2, 3, 3, 4);
+ sc_table.attach (soundcloud_open_checkbox, 2, 3, 4, 5);
+ sc_table.attach (soundcloud_download_checkbox, 2, 3, 5, 6);
+
+ pack_end (progress_bar, false, false);
+ sc_frame->show_all ();
+}
+
+
+int
+SoundcloudExportSelector::do_progress_callback (double ultotal, double ulnow, const std::string &filename)
+{
+ DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("SoundcloudExportSelector::do_progress_callback(%1, %2, %3)", ultotal, ulnow, filename));
+ if (soundcloud_cancel) {
+ progress_bar.set_fraction (0);
+ // cancel_button.set_label ("");
+ return -1;
+ }
+
+ double fraction = 0.0;
+ if (ultotal != 0) {
+ fraction = ulnow / ultotal;
+ }
+
+ progress_bar.set_fraction ( fraction );
+
+ std::string prog;
+ prog = string_compose (_("%1: %2 of %3 bytes uploaded"), filename, ulnow, ultotal);
+ progress_bar.set_text (prog);
+
+
+ return 0;
+}
+
--- /dev/null
+/*soundcloud_export_selector.h***********************************************
+
+ Adapted for Ardour by Ben Loftis, March 2012
+
+*****************************************************************************/
+#ifndef __soundcloud_export_selector_h__
+#define __soundcloud_export_selector_h__
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <vector>
+#include <gtkmm.h>
+#include <gtkmm/progressbar.h>
+
+class SoundcloudExportSelector : public Gtk::VBox, public ARDOUR::SessionHandlePtr
+{
+ public:
+ SoundcloudExportSelector ();
+ int do_progress_callback (double ultotal, double ulnow, const std::string &filename);
+ std::string username () { return soundcloud_username_entry.get_text (); }
+ std::string password () { return soundcloud_password_entry.get_text (); }
+ bool make_public () { return soundcloud_public_checkbox.get_active (); }
+ bool open_page () { return soundcloud_open_checkbox.get_active (); }
+ bool downloadable () { return soundcloud_download_checkbox.get_active (); }
+ void cancel () { soundcloud_cancel = true; }
+
+ private:
+ Gtk::Table sc_table;
+ Gtk::Label soundcloud_username_label;
+ Gtk::Entry soundcloud_username_entry;
+ Gtk::Label soundcloud_password_label;
+ Gtk::Entry soundcloud_password_entry;
+ Gtk::CheckButton soundcloud_public_checkbox;
+ Gtk::CheckButton soundcloud_open_checkbox;
+ Gtk::CheckButton soundcloud_download_checkbox;
+ bool soundcloud_cancel;
+ Gtk::ProgressBar progress_bar;
+
+};
+
+#endif // __soundcloud_export_selector_h__
std::string splash_file;
- if (!find_file_in_search_path (ardour_data_search_path(), "splash.png", splash_file)) {
+ if (!find_file (ardour_data_search_path(), "splash.png", splash_file)) {
cerr << "Cannot find splash screen image file\n";
throw failed_constructor();
}
using namespace Glib;
using namespace PBD;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
ArdourStartup* ArdourStartup::the_startup = 0;
using namespace Gtkmm2ext;
using namespace PBD;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
static void
_note_off_event_handler (GtkWidget* /*widget*/, int note, gpointer arg)
std::string binding_file;
- if (find_file_in_search_path (ardour_config_search_path(), "step_editing.bindings", binding_file)) {
+ if (find_file (ardour_config_search_path(), "step_editing.bindings", binding_file)) {
bindings.load (binding_file);
}
}
using namespace std;
using namespace Gtk;
using namespace Gtkmm2ext;
+using namespace ARDOUR_UI_UTILS;
static const int pos_box_size = 8;
static const int lr_box_size = 15;
if (!have_font) {
Pango::FontDescription font;
Pango::AttrFontDesc* font_attr;
- font = Pango::FontDescription ("ArdourMono");
- font.set_weight (Pango::WEIGHT_BOLD);
- font.set_size(9 * PANGO_SCALE);
+ font = Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_SmallBoldMonospaceFont());
font_attr = new Pango::AttrFontDesc (Pango::Attribute::create_attr_font_desc (font));
panner_font_attributes.change(*font_attr);
delete font_attr;
r = 0x606060ff;
}
+ if (_send_mode) {
+ b = rgba_from_style("SendStripBase",
+ UINT_RGBA_R(b), UINT_RGBA_G(b), UINT_RGBA_B(b), 255,
+ "fg");
+ }
/* background */
context->set_source_rgba (UINT_RGBA_R_FLT(b), UINT_RGBA_G_FLT(b), UINT_RGBA_B_FLT(b), UINT_RGBA_A_FLT(b));
using namespace std;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Editing;
-StreamView::StreamView (RouteTimeAxisView& tv, ArdourCanvas::Group* canvas_group)
+StreamView::StreamView (RouteTimeAxisView& tv, ArdourCanvas::Container* canvas_group)
: _trackview (tv)
- , _canvas_group (canvas_group ? canvas_group : new ArdourCanvas::Group (_trackview.canvas_display()))
+ , _canvas_group (canvas_group ? canvas_group : new ArdourCanvas::Container (_trackview.canvas_display()))
, _samples_per_pixel (_trackview.editor().get_current_zoom ())
, rec_updating(false)
, rec_active(false)
}
void
-StreamView::apply_color (Gdk::Color color, ColorTarget target)
+StreamView::apply_color (Gdk::Color const& c, ColorTarget target)
+{
+ return apply_color (gdk_color_to_rgba (c), target);
+}
+
+void
+StreamView::apply_color (uint32_t color, ColorTarget target)
{
list<RegionView *>::iterator i;
break;
case StreamBaseColor:
- stream_base_color = RGBA_TO_UINT (color.get_red_p(), color.get_green_p(), color.get_blue_p(), 255);
+ stream_base_color = color;
canvas_rect->set_fill_color (stream_base_color);
break;
}
namespace ArdourCanvas {
class Rectangle;
- class Group;
+ class Container;
}
struct RecBoxInfo {
void set_layer_display (LayerDisplay);
LayerDisplay layer_display () const { return _layer_display; }
- ArdourCanvas::Group* canvas_item() { return _canvas_group; }
+ ArdourCanvas::Container* canvas_item() { return _canvas_group; }
enum ColorTarget {
RegionColor,
StreamBaseColor
};
- Gdk::Color get_region_color () const { return region_color; }
- void apply_color (Gdk::Color, ColorTarget t);
+ uint32_t get_region_color () const { return region_color; }
+ void apply_color (uint32_t, ColorTarget t);
+ void apply_color (Gdk::Color const &, ColorTarget t);
uint32_t num_selected_regionviews () const;
sigc::signal<void> ContentsHeightChanged;
protected:
- StreamView (RouteTimeAxisView&, ArdourCanvas::Group* canvas_group = 0);
+ StreamView (RouteTimeAxisView&, ArdourCanvas::Container* canvas_group = 0);
void transport_changed();
void transport_looped();
virtual void color_handler () = 0;
RouteTimeAxisView& _trackview;
- ArdourCanvas::Group* _canvas_group;
+ ArdourCanvas::Container* _canvas_group;
ArdourCanvas::Rectangle* canvas_rect; /* frame around the whole thing */
typedef std::list<RegionView* > RegionViewList;
bool rec_updating;
bool rec_active;
- Gdk::Color region_color; ///< Contained region color
- uint32_t stream_base_color; ///< Background color
+ uint32_t region_color; ///< Contained region color
+ uint32_t stream_base_color; ///< Background color
PBD::ScopedConnectionList playlist_connections;
PBD::ScopedConnection playlist_switched_connection;
SysEx::SysEx (
MidiRegionView& region,
- ArdourCanvas::Group* parent,
+ ArdourCanvas::Container* parent,
string& text,
double height,
double x,
public:
SysEx (
MidiRegionView& region,
- ArdourCanvas::Group* parent,
+ ArdourCanvas::Container* parent,
std::string& text,
double height,
double x,
TimeAxisViewItem::HideFrameRight |
TimeAxisViewItem::FullWidthNameHighlight);
-TapeAudioRegionView::TapeAudioRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
+TapeAudioRegionView::TapeAudioRegionView (ArdourCanvas::Container *parent, RouteTimeAxisView &tv,
boost::shared_ptr<AudioRegion> r,
double spu,
- Gdk::Color const & basic_color)
+ uint32_t basic_color)
: AudioRegionView (parent, tv, r, spu, basic_color, false,
TimeAxisViewItem::Visibility ((r->position() != 0) ? default_tape_visibility :
}
void
-TapeAudioRegionView::init (Gdk::Color const & basic_color, bool /*wfw*/)
+TapeAudioRegionView::init (bool /*wfw*/)
{
/* never wait for data: always just create the waves, connect once and then
we'll update whenever we need to.
*/
- AudioRegionView::init(basic_color, false);
+ AudioRegionView::init (false);
/* every time the wave data changes and peaks are ready, redraw */
// CAIROCANVAS
// waves[n]->rebuild ();
}
-
-void
-TapeAudioRegionView::set_frame_color ()
-{
- fill_opacity = 255;
- AudioRegionView::set_frame_color ();
-}
class TapeAudioRegionView : public AudioRegionView
{
public:
- TapeAudioRegionView (ArdourCanvas::Group *,
+ TapeAudioRegionView (ArdourCanvas::Container *,
RouteTimeAxisView&,
boost::shared_ptr<ARDOUR::AudioRegion>,
double initial_samples_per_pixel,
- Gdk::Color const & base_color);
+ uint32_t base_color);
~TapeAudioRegionView ();
protected:
- void init (Gdk::Color const & base_color, bool wait_for_waves);
+ void init (bool wait_for_waves);
- void set_frame_color ();
void update (uint32_t n);
static const TimeAxisViewItem::Visibility default_tape_visibility;
#include "ardour/rc_configuration.h"
#include "tempo_dialog.h"
-#include "utils.h"
#include "i18n.h"
#include "pbd/compose.h"
-#include "canvas/line.h"
#include "canvas/canvas.h"
#include "canvas/debug.h"
using namespace std;
-TempoLines::TempoLines (ArdourCanvas::Canvas& canvas, ArdourCanvas::Group* group, double h)
- : _canvas (canvas)
- , _group (group)
- , _height (h)
+TempoLines::TempoLines (ArdourCanvas::Container* group, double)
+ : lines (group, ArdourCanvas::LineSet::Vertical)
{
+ lines.set_extent (ArdourCanvas::COORD_MAX);
}
void
TempoLines::tempo_map_changed()
{
- /* remove all lines from the group, put them in the cache (to avoid
- * unnecessary object destruction+construction later), and clear _lines
- */
-
- _group->clear ();
- _cache.insert (_cache.end(), _lines.begin(), _lines.end());
- _lines.clear ();
+ lines.clear ();
}
void
TempoLines::show ()
{
- _group->show ();
+ lines.show ();
}
void
TempoLines::hide ()
{
- _group->hide ();
+ lines.hide ();
}
void
const ARDOUR::TempoMap::BBTPointList::const_iterator& end)
{
ARDOUR::TempoMap::BBTPointList::const_iterator i;
- ArdourCanvas::Rect const visible = _canvas.visible_area ();
double beat_density;
uint32_t beats = 0;
bars = (*i).bar - (*begin).bar;
beats = distance (begin, end) - bars;
- beat_density = (beats * 10.0f) / visible.width ();
+ beat_density = (beats * 10.0f) / lines.canvas()->width();
if (beat_density > 4.0f) {
/* if the lines are too close together, they become useless */
- tempo_map_changed();
+ lines.clear ();
return;
}
- tempo_map_changed ();
+ lines.clear ();
for (i = begin; i != end; ++i) {
if ((*i).is_bar()) {
color = ARDOUR_UI::config()->get_canvasvar_MeasureLineBar();
} else {
- if (beat_density > 2.0) {
+ if (beat_density > 0.3) {
continue; /* only draw beat lines if the gaps between beats are large. */
}
color = ARDOUR_UI::config()->get_canvasvar_MeasureLineBeat();
ArdourCanvas::Coord xpos = PublicEditor::instance().sample_to_pixel_unrounded ((*i).frame);
- ArdourCanvas::Line* line;
-
- if (!_cache.empty()) {
- line = _cache.back ();
- _cache.pop_back ();
- line->reparent (_group);
- } else {
- line = new ArdourCanvas::Line (_group);
- CANVAS_DEBUG_NAME (line, string_compose ("tempo measure line @ %1", (*i).frame));
- line->set_ignore_events (true);
- }
-
- line->set_x0 (xpos);
- line->set_x1 (xpos);
- line->set_y0 (0.0);
- line->set_y1 (_height);
- line->set_outline_color (color);
- line->show ();
+ lines.add (xpos, 1.0, color);
}
}
#ifndef __ardour_tempo_lines_h__
#define __ardour_tempo_lines_h__
-#include <list>
#include "ardour/tempo.h"
+#include "canvas/line_set.h"
+
class TempoLines {
public:
- TempoLines(ArdourCanvas::Canvas& canvas, ArdourCanvas::Group* group, double screen_height);
+ TempoLines (ArdourCanvas::Container* group, double screen_height);
void tempo_map_changed();
void hide();
private:
- typedef std::list<ArdourCanvas::Line*> Lines;
- Lines _lines;
- Lines _cache;
-
- ArdourCanvas::Canvas& _canvas;
- ArdourCanvas::Group* _group;
+ ArdourCanvas::LineSet lines;
double _height;
};
#include "gtkmm2ext/gtk_ui.h"
#include "gtkmm2ext/cell_renderer_color_selector.h"
+#include "gtkmm2ext/utils.h"
#include "pbd/file_utils.h"
#include "pbd/compose.h"
#include "rgb_macros.h"
#include "ardour_ui.h"
#include "global_signals.h"
+#include "utils.h"
#include "i18n.h"
using namespace Gtk;
using namespace PBD;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
-sigc::signal<void> ColorsChanged;
-sigc::signal<void,uint32_t> ColorChanged;
+namespace ARDOUR_UI_UTILS {
+ sigc::signal<void> ColorsChanged;
+ sigc::signal<void,uint32_t> ColorChanged;
+}
ThemeManager::ThemeManager()
: ArdourWindow (_("Theme Manager"))
, timeline_item_gradient_depth (0, 1.0, 0.05)
, timeline_item_gradient_depth_label (_("Timeline item gradient depth"))
, all_dialogs (_("All floating windows are dialogs"))
+ , icon_set_label (_("Icon Set"))
{
set_title (_("Theme Manager"));
vbox->pack_start (region_color_button, PACK_SHRINK);
vbox->pack_start (show_clipping_button, PACK_SHRINK);
- Gtk::HBox* hbox = Gtk::manage (new Gtk::HBox());
+ Gtk::HBox* hbox;
+
+ vector<string> icon_sets = ::get_icon_sets ();
+
+ if (icon_sets.size() > 1) {
+ Gtkmm2ext::set_popdown_strings (icon_set_dropdown, icon_sets);
+ icon_set_dropdown.set_active_text (ARDOUR_UI::config()->get_icon_set());
+
+ hbox = Gtk::manage (new Gtk::HBox());
+ hbox->set_spacing (6);
+ hbox->pack_start (icon_set_label, false, false);
+ hbox->pack_start (icon_set_dropdown, true, true);
+ vbox->pack_start (*hbox, PACK_SHRINK);
+ }
+
+
+ hbox = Gtk::manage (new Gtk::HBox());
hbox->set_spacing (6);
hbox->pack_start (waveform_gradient_depth, true, true);
hbox->pack_start (waveform_gradient_depth_label, false, false);
hbox->set_spacing (6);
hbox->pack_start (timeline_item_gradient_depth, true, true);
hbox->pack_start (timeline_item_gradient_depth_label, false, false);
-
vbox->pack_start (*hbox, PACK_SHRINK);
+
vbox->pack_start (scroller);
vbox->show_all ();
waveform_gradient_depth.signal_value_changed().connect (sigc::mem_fun (*this, &ThemeManager::on_waveform_gradient_depth_change));
timeline_item_gradient_depth.signal_value_changed().connect (sigc::mem_fun (*this, &ThemeManager::on_timeline_item_gradient_depth_change));
all_dialogs.signal_toggled().connect (sigc::mem_fun (*this, &ThemeManager::on_all_dialogs_toggled));
+ icon_set_dropdown.signal_changed().connect (sigc::mem_fun (*this, &ThemeManager::on_icon_set_changed));
Gtkmm2ext::UI::instance()->set_tip (all_dialogs,
string_compose (_("Mark all floating windows to be type \"Dialog\" rather than using \"Utility\" for some.\n"
int cellx;
int celly;
- UIConfigVariable<uint32_t> *ccvar;
+ ColorVariable<uint32_t> *ccvar;
if (!color_display.get_path_at_pos ((int)ev->x, (int)ev->y, path, column, cellx, celly)) {
return false;
case 1: /* color */
if ((iter = color_list->get_iter (path))) {
- UIConfigVariable<uint32_t>* var = (*iter)[columns.pVar];
+ ColorVariable<uint32_t>* var = (*iter)[columns.pVar];
if (!var) {
/* parent row, do nothing */
return false;
{
std::string rc_file_path;
- if (!find_file_in_search_path (ardour_config_search_path(), filename, rc_file_path)) {
+ if (!find_file (ardour_config_search_path(), filename, rc_file_path)) {
warning << string_compose (_("Unable to find UI style file %1 in search path %2. %3 will look strange"),
filename, ardour_config_search_path().to_string(), PROGRAM_NAME)
<< endmsg;
ARDOUR_UI::config()->set_dirty ();
}
+void
+ThemeManager::on_icon_set_changed ()
+{
+ string new_set = icon_set_dropdown.get_active_text();
+ ARDOUR_UI::config()->set_icon_set (new_set);
+}
+
void
ThemeManager::on_dark_theme_button_toggled()
{
color_list->clear();
- for (std::map<std::string,UIConfigVariable<uint32_t> *>::iterator i = ARDOUR_UI::config()->canvas_colors.begin(); i != ARDOUR_UI::config()->canvas_colors.end(); i++) {
+ for (std::map<std::string,ColorVariable<uint32_t> *>::iterator i = ARDOUR_UI::config()->canvas_colors.begin(); i != ARDOUR_UI::config()->canvas_colors.end(); i++) {
- UIConfigVariable<uint32_t>* var = i->second;
+ ColorVariable<uint32_t>* var = i->second;
TreeModel::Children rows = color_list->children();
TreeModel::Row row;
void on_waveform_gradient_depth_change ();
void on_timeline_item_gradient_depth_change ();
void on_all_dialogs_toggled ();
+ void on_icon_set_changed ();
private:
struct ColorDisplayModelColumns : public Gtk::TreeModel::ColumnRecord {
Gtk::TreeModelColumn<std::string> name;
Gtk::TreeModelColumn<Gdk::Color> gdkcolor;
- Gtk::TreeModelColumn<UIConfigVariable<uint32_t> *> pVar;
+ Gtk::TreeModelColumn<ColorVariable<uint32_t> *> pVar;
Gtk::TreeModelColumn<uint32_t> rgba;
};
Gtk::Label timeline_item_gradient_depth_label;
Gtk::CheckButton all_dialogs;
Gtk::CheckButton gradient_waveforms;
+ Gtk::Label icon_set_label;
+ Gtk::ComboBoxText icon_set_dropdown;
bool button_press_event (GdkEventButton*);
};
using namespace Gtk;
using namespace Gdk;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace PBD;
using namespace Editing;
using namespace ArdourCanvas;
compute_heights ();
}
- _canvas_display = new Group (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
+ _canvas_display = new ArdourCanvas::Container (ed.get_trackview_group (), ArdourCanvas::Duple (0.0, 0.0));
CANVAS_DEBUG_NAME (_canvas_display, "main for TAV");
_canvas_display->hide(); // reveal as needed
- selection_group = new Group (_canvas_display);
+ selection_group = new ArdourCanvas::Container (_canvas_display);
CANVAS_DEBUG_NAME (selection_group, "selection for TAV");
selection_group->set_data (X_("timeselection"), (void *) 1);
selection_group->hide();
-
- _ghost_group = new Group (_canvas_display);
+
+ _ghost_group = new ArdourCanvas::Container (_canvas_display);
CANVAS_DEBUG_NAME (_ghost_group, "ghost for TAV");
_ghost_group->lower_to_bottom();
_ghost_group->show();
}
e.stepping_axis_view()->step_height (false);
return true;
- } else if (Keyboard::no_modifiers_active (ev->state)) {
- _editor.scroll_up_one_track();
- return true;
- }
+ }
break;
case GDK_SCROLL_DOWN:
}
e.stepping_axis_view()->step_height (true);
return true;
- } else if (Keyboard::no_modifiers_active (ev->state)) {
- _editor.scroll_down_one_track();
- return true;
- }
+ }
break;
default:
break;
}
- return false;
+ /* Just forward to the normal canvas scroll method. The coordinate
+ systems are different but since the canvas is always larger than the
+ track headers, and aligned with the trackview area, this will work.
+
+ In the not too distant future this layout is going away anyway and
+ headers will be on the canvas.
+ */
+ return _editor.canvas_scroll_event (ev, false);
}
bool
x1 = _editor.sample_to_pixel (start);
x2 = _editor.sample_to_pixel (start + cnt - 1);
- y2 = current_height();
+ y2 = current_height() - 1;
- rect->rect->set (ArdourCanvas::Rect (x1, 1, x2, y2));
+ rect->rect->set (ArdourCanvas::Rect (x1, 0, x2, y2));
// trim boxes are at the top for selections
rect->rect->set_fill_color (ARDOUR_UI::config()->get_canvasvar_SelectionRect());
rect->start_trim = new ArdourCanvas::Rectangle (selection_group);
- CANVAS_DEBUG_NAME (rect->rect, "selection rect start trim");
+ CANVAS_DEBUG_NAME (rect->start_trim, "selection rect start trim");
rect->start_trim->set_outline (false);
rect->start_trim->set_fill (false);
rect->end_trim = new ArdourCanvas::Rectangle (selection_group);
- CANVAS_DEBUG_NAME (rect->rect, "selection rect end trim");
+ CANVAS_DEBUG_NAME (rect->end_trim, "selection rect end trim");
rect->end_trim->set_outline (false);
rect->end_trim->set_fill (false);
}
/** @return Pair: TimeAxisView, layer index.
- * TimeAxisView is non-0 if this object covers y, or one of its children does.
+ * TimeAxisView is non-0 if this object covers @param y, or one of its children
+ * does. @param y is an offset from the top of the trackview area.
+ *
* If the covering object is a child axis, then the child is returned.
* TimeAxisView is 0 otherwise.
+ *
* Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
* and is in stacked or expanded * region display mode, otherwise 0.
*/
namespace ArdourCanvas {
class Canvas;
- class Group;
+ class Container;
class Item;
}
virtual void enter_internal_edit_mode () {}
virtual void leave_internal_edit_mode () {}
- ArdourCanvas::Group* canvas_display () { return _canvas_display; }
- ArdourCanvas::Group* ghost_group () { return _ghost_group; }
+ ArdourCanvas::Container* canvas_display () { return _canvas_display; }
+ ArdourCanvas::Container* ghost_group () { return _ghost_group; }
/** @return effective height (taking children into account) in canvas units, or
0 if this TimeAxisView has not yet been shown */
std::string controls_base_selected_name;
Gtk::Menu* display_menu; /* The standard LHS Track control popup-menus */
TimeAxisView* parent;
- ArdourCanvas::Group* selection_group;
- ArdourCanvas::Group* _ghost_group;
+ ArdourCanvas::Container* selection_group;
+ ArdourCanvas::Container* _ghost_group;
std::list<GhostRegion*> ghosts;
std::list<SelectionRect*> free_selection_rects;
std::list<SelectionRect*> used_selection_rects;
bool _hidden;
bool in_destructor;
Gtk::Menu* _size_menu;
- ArdourCanvas::Group* _canvas_display;
+ ArdourCanvas::Container* _canvas_display;
double _y_position;
PublicEditor& _editor;
#include "gtkmm2ext/utils.h"
#include "gtkmm2ext/gui_thread.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
#include "canvas/rectangle.h"
#include "canvas/debug.h"
-#include "canvas/drag_handle.h"
#include "canvas/text.h"
#include "canvas/utils.h"
using namespace Glib;
using namespace PBD;
using namespace ARDOUR;
+using namespace ARDOUR_UI_UTILS;
using namespace Gtkmm2ext;
Pango::FontDescription TimeAxisViewItem::NAME_FONT;
void
TimeAxisViewItem::set_constant_heights ()
{
- NAME_FONT = get_font_for_style (X_("TimeAxisViewItemName"));
+ NAME_FONT = Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_SmallFont());
Gtk::Window win;
Gtk::Label foo;
* @param automation true if this is an automation region view
*/
TimeAxisViewItem::TimeAxisViewItem(
- const string & it_name, ArdourCanvas::Group& parent, TimeAxisView& tv, double spu, Gdk::Color const & base_color,
+ const string & it_name, ArdourCanvas::Item& parent, TimeAxisView& tv, double spu, uint32_t base_color,
framepos_t start, framecnt_t duration, bool recording, bool automation, Visibility vis
)
: trackview (tv)
, _dragging (other._dragging)
, _width (0.0)
{
-
- Gdk::Color c;
- int r,g,b,a;
-
- UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
- c.set_rgb_p (r/255.0, g/255.0, b/255.0);
-
/* share the other's parent, but still create a new group */
- ArdourCanvas::Group* parent = other.group->parent();
+ ArdourCanvas::Item* parent = other.group->parent();
_selected = other._selected;
- init (parent, other.samples_per_pixel, c, other.frame_position,
+ init (parent, other.samples_per_pixel, other.fill_color, other.frame_position,
other.item_duration, other.visibility, other.wide_enough_for_name, other.high_enough_for_name);
}
void
-TimeAxisViewItem::init (ArdourCanvas::Group* parent, double fpp, Gdk::Color const & base_color,
+TimeAxisViewItem::init (ArdourCanvas::Item* parent, double fpp, uint32_t base_color,
framepos_t start, framepos_t duration, Visibility vis,
bool wide, bool high)
{
- group = new ArdourCanvas::Group (parent);
+ group = new ArdourCanvas::Container (parent);
CANVAS_DEBUG_NAME (group, string_compose ("TAVI group for %1", get_item_name()));
group->Event.connect (sigc::mem_fun (*this, &TimeAxisViewItem::canvas_group_event));
+ fill_color = base_color;
samples_per_pixel = fpp;
frame_position = start;
item_duration = duration;
name_connected = false;
- fill_opacity = 60;
position_locked = false;
max_item_duration = ARDOUR::max_framepos;
min_item_duration = 0;
name_text->set_position (ArdourCanvas::Duple (NAME_X_OFFSET, NAME_Y_OFFSET));
}
name_text->set_font_description (NAME_FONT);
+ name_text->set_ignore_events (true);
} else {
name_text = 0;
}
double top = TimeAxisViewItem::GRAB_HANDLE_TOP;
double width = TimeAxisViewItem::GRAB_HANDLE_WIDTH;
- frame_handle_start = new ArdourCanvas::DragHandle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()), true);
+ frame_handle_start = new ArdourCanvas::Rectangle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()));
CANVAS_DEBUG_NAME (frame_handle_start, "TAVI frame handle start");
frame_handle_start->set_outline (false);
frame_handle_start->set_fill (false);
frame_handle_start->Event.connect (sigc::bind (sigc::mem_fun (*this, &TimeAxisViewItem::frame_handle_crossing), frame_handle_start));
- frame_handle_end = new ArdourCanvas::DragHandle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()), false);
+ frame_handle_end = new ArdourCanvas::Rectangle (group, ArdourCanvas::Rect (0.0, top, width, trackview.current_height()));
CANVAS_DEBUG_NAME (frame_handle_end, "TAVI frame handle end");
frame_handle_end->set_outline (false);
frame_handle_end->set_fill (false);
if (_selected != yn) {
Selectable::set_selected (yn);
set_frame_color ();
+ set_name_text_color ();
}
}
}
void
-TimeAxisViewItem::set_color (Gdk::Color const & base_color)
+TimeAxisViewItem::set_color (uint32_t base_color)
{
- compute_colors (base_color);
+ fill_color = base_color;
set_colors ();
}
return frame;
}
-ArdourCanvas::Group*
+ArdourCanvas::Item*
TimeAxisViewItem::get_canvas_group()
{
return group;
return name_highlight;
}
-/**
- * Calculate some contrasting color for displaying various parts of this item, based upon the base color.
- *
- * @param color the base color of the item
- */
-void
-TimeAxisViewItem::compute_colors (Gdk::Color const & base_color)
-{
- unsigned char radius;
- char minor_shift;
-
- unsigned char r,g,b;
-
- /* FILL: this is simple */
- r = base_color.get_red()/256;
- g = base_color.get_green()/256;
- b = base_color.get_blue()/256;
- fill_color = RGBA_TO_UINT(r,g,b,160);
-
- /* for minor colors:
- if the overall saturation is strong, make the minor colors light.
- if its weak, make them dark.
-
- we do this by moving an equal distance to the other side of the
- central circle in the color wheel from where we started.
- */
-
- radius = (unsigned char) rint (floor (sqrt (static_cast<double>(r*r + g*g + b+b))/3.0f));
- minor_shift = 125 - radius;
-
- /* LABEL: rotate around color wheel by 120 degrees anti-clockwise */
-
- r = base_color.get_red()/256;
- g = base_color.get_green()/256;
- b = base_color.get_blue()/256;
-
- if (r > b)
- {
- if (r > g)
- {
- /* red sector => green */
- swap (r,g);
- }
- else
- {
- /* green sector => blue */
- swap (g,b);
- }
- }
- else
- {
- if (b > g)
- {
- /* blue sector => red */
- swap (b,r);
- }
- else
- {
- /* green sector => blue */
- swap (g,b);
- }
- }
-
- r += minor_shift;
- b += minor_shift;
- g += minor_shift;
-
- label_color = RGBA_TO_UINT(r,g,b,255);
- r = (base_color.get_red()/256) + 127;
- g = (base_color.get_green()/256) + 127;
- b = (base_color.get_blue()/256) + 127;
-
- label_color = RGBA_TO_UINT(r,g,b,255);
-
- /* XXX can we do better than this ? */
- /* We're trying;) */
- /* NUKECOLORS */
-
- //frame_color_r = 192;
- //frame_color_g = 192;
- //frame_color_b = 194;
-
- //selected_frame_color_r = 182;
- //selected_frame_color_g = 145;
- //selected_frame_color_b = 168;
-
- //handle_color_r = 25;
- //handle_color_g = 0;
- //handle_color_b = 255;
- //lock_handle_color_r = 235;
- //lock_handle_color_g = 16;
- //lock_handle_color_b = 16;
-}
-
/**
* Convenience method to set the various canvas item colors
*/
void
TimeAxisViewItem::set_colors()
{
- set_frame_color();
+ set_frame_color ();
if (name_highlight) {
name_highlight->set_fill_color (fill_color);
}
- if (name_text) {
- double r, g, b, a;
-
- const double black_r = 0.0;
- const double black_g = 0.0;
- const double black_b = 0.0;
-
- const double white_r = 1.0;
- const double white_g = 1.0;
- const double white_b = 1.0;
-
- ArdourCanvas::color_to_rgba (fill_color, r, g, b, a);
-
- /* Use W3C contrast guideline calculation */
-
- double white_contrast = (max (r, white_r) - min (r, white_r)) +
- (max (g, white_g) - min (g, white_g)) +
- (max (b, white_b) - min (b, white_b));
-
- double black_contrast = (max (r, black_r) - min (r, black_r)) +
- (max (g, black_g) - min (g, black_g)) +
- (max (b, black_b) - min (b, black_b));
-
- if (white_contrast > black_contrast) {
- /* use white */
- name_text->set_color (ArdourCanvas::rgba_to_color (1.0, 1.0, 1.0, 1.0));
- } else {
- /* use black */
- name_text->set_color (ArdourCanvas::rgba_to_color (0.0, 0.0, 0.0, 1.0));
- }
-
-#if 0
- double h, s, v;
+ set_name_text_color ();
+ set_trim_handle_colors();
+}
- ArdourCanvas::color_to_hsv (fill_color, h, s, v);
+void
+TimeAxisViewItem::set_name_text_color ()
+{
+ if (!name_text) {
+ return;
+ }
+
- if (v == 0.0) {
- /* fill is black, set text to white */
- name_text->set_color (ArdourCanvas::rgba_to_color (1.0, 1.0, 1.0, 1.0));
- } else if (v == 1.0) {
- /* fill is white, set text to black */
- name_text->set_color (ArdourCanvas::rgba_to_color (0.0, 0.0, 0.0, 1.0));
- } else {
+ uint32_t f;
+
+ if (Config->get_show_name_highlight()) {
+ /* name text will always be on top of name highlight, which
+ will always use our fill color.
+ */
+ f = fill_color;
+ } else {
+ /* name text will be on top of the item, whose color
+ may vary depending on various conditions.
+ */
+ f = get_fill_color ();
+ }
- h = fabs (fmod ((h - 180), 360.0)); /* complementary color */
- s = 1.0; /* fully saturate */
- v = 0.9; /* increase lightness/brightness/value */
+ name_text->set_color (ArdourCanvas::contrasting_text_color (f));
+}
- name_text->set_color (ArdourCanvas::hsv_to_color (h, s, v, 1.0));
- }
-#endif
+uint32_t
+TimeAxisViewItem::fill_opacity () const
+{
+ if (!rect_visible) {
+ /* if the frame/rect is marked as invisible, then the
+ * fill should be transparent. simplest: set
+
+ * alpha/opacity to zero.
+ */
+ return 0;
+ }
+ if (_dragging) {
+ return 130;
}
-
- set_trim_handle_colors();
+
+ uint32_t col = ARDOUR_UI::config()->get_canvasvar_FrameBase();
+ return UINT_RGBA_A (col);
}
uint32_t
TimeAxisViewItem::get_fill_color () const
{
- uint32_t f = 0;
+ uint32_t f;
+ uint32_t o;
+
+ o = fill_opacity ();
if (_selected) {
f = ARDOUR_UI::config()->get_canvasvar_SelectedFrameBase();
+ if (o == 0) {
+ /* some condition of this item has set fill opacity to
+ * zero, but it has been selected, so use a mid-way
+ * alpha value to make it reasonably visible.
+ */
+ o = 130;
+ }
+
} else {
if (_recregion) {
f = ARDOUR_UI::config()->get_canvasvar_RecordingRect();
} else {
-
- if (high_enough_for_name && !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
+ if ((!Config->get_show_name_highlight() || high_enough_for_name) && !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
f = ARDOUR_UI::config()->get_canvasvar_FrameBase();
} else {
f = fill_color;
}
}
- return f;
+ return UINT_RGBA_CHANGE_A (f, o);
}
/**
void
TimeAxisViewItem::set_frame_color()
{
- uint32_t f = 0;
-
if (!frame) {
return;
}
- f = get_fill_color ();
-
- if (fill_opacity) {
- f = UINT_RGBA_CHANGE_A (f, fill_opacity);
- }
-
- if (!rect_visible) {
- f = UINT_RGBA_CHANGE_A (f, 0);
- }
-
- frame->set_fill_color (f);
+ frame->set_fill_color (get_fill_color());
set_frame_gradient ();
if (!_recregion) {
+ uint32_t f;
+
if (_selected) {
f = ARDOUR_UI::config()->get_canvasvar_SelectedTimeAxisFrame();
} else {
}
if (!rect_visible) {
+ /* make the frame outline be visible but rather transparent */
f = UINT_RGBA_CHANGE_A (f, 64);
}
_width = pixel_width;
manage_name_highlight ();
+ manage_name_text ();
if (pixel_width < 2.0) {
set_frame_gradient ();
}
}
+
+void
+TimeAxisViewItem::drag_start ()
+{
+ _dragging = true;
+ set_frame_color ();
+}
+
+void
+TimeAxisViewItem::drag_end ()
+{
+ _dragging = false;
+ set_frame_color ();
+}
class Pixbuf;
class Rectangle;
class Item;
- class Group;
- class Text;
+ class Container;
+ class Text;
}
using ARDOUR::framepos_t;
TimeAxisView& get_time_axis_view () const;
void set_name_text(const std::string&);
virtual void set_height(double h);
+ virtual double height() const { return _height; }
void set_y (double);
- void set_color (Gdk::Color const &);
+ void set_color (uint32_t);
+ void set_name_text_color ();
uint32_t get_fill_color () const;
ArdourCanvas::Item* get_canvas_frame();
- ArdourCanvas::Group* get_canvas_group();
+ ArdourCanvas::Item* get_canvas_group();
ArdourCanvas::Item* get_name_highlight();
virtual void set_samples_per_pixel (double);
double get_samples_per_pixel () const;
- virtual void drag_start() { _dragging = true; }
- virtual void drag_end() { _dragging = false; }
+ virtual void drag_start();
+ virtual void drag_end();
bool dragging() const { return _dragging; }
virtual void raise () { return; }
};
protected:
- TimeAxisViewItem(const std::string &, ArdourCanvas::Group&, TimeAxisView&, double, Gdk::Color const &,
- framepos_t, framecnt_t, bool recording = false, bool automation = false, Visibility v = Visibility (0));
+ TimeAxisViewItem (const std::string &, ArdourCanvas::Item&, TimeAxisView&, double, uint32_t fill_color,
+ framepos_t, framecnt_t, bool recording = false, bool automation = false, Visibility v = Visibility (0));
TimeAxisViewItem (const TimeAxisViewItem&);
- void init (ArdourCanvas::Group*, double, Gdk::Color const &, framepos_t, framepos_t, Visibility, bool, bool);
+ void init (ArdourCanvas::Item*, double, uint32_t, framepos_t, framepos_t, Visibility, bool, bool);
virtual bool canvas_group_event (GdkEvent*);
- virtual void compute_colors (Gdk::Color const &);
virtual void set_colors();
virtual void set_frame_color();
virtual void set_frame_gradient ();
/** true if a small vestigial rect should be shown when the item gets very narrow */
bool show_vestigial;
- uint32_t fill_opacity;
uint32_t fill_color;
- uint32_t frame_color_r;
- uint32_t frame_color_g;
- uint32_t frame_color_b;
- uint32_t selected_frame_color_r;
- uint32_t selected_frame_color_g;
- uint32_t selected_frame_color_b;
- uint32_t label_color;
-
- uint32_t handle_color_r;
- uint32_t handle_color_g;
- uint32_t handle_color_b;
- uint32_t lock_handle_color_r;
- uint32_t lock_handle_color_g;
- uint32_t lock_handle_color_b;
+
+ virtual uint32_t fill_opacity() const;
+
uint32_t last_item_width;
int name_text_width;
bool wide_enough_for_name;
bool high_enough_for_name;
bool rect_visible;
- ArdourCanvas::Group* group;
+ ArdourCanvas::Container* group;
ArdourCanvas::Rectangle* vestigial_frame;
ArdourCanvas::Rectangle* frame;
ArdourCanvas::Text* name_text;
template <typename Function>
void foreach_route_ui (Function f) {
- for (iterator i = begin(); i != end(); ++i) {
+ for (iterator i = begin(); i != end(); ) {
+ iterator tmp = i;
+ ++tmp;
+
RouteUI* t = dynamic_cast<RouteUI*> (*i);
if (t) {
f (t);
}
+ i = tmp;
}
}
template <typename Function>
void foreach_route_time_axis (Function f) {
- for (iterator i = begin(); i != end(); ++i) {
+ for (iterator i = begin(); i != end(); ) {
+ iterator tmp = i;
+ ++tmp;
RouteTimeAxisView* t = dynamic_cast<RouteTimeAxisView*> (*i);
if (t) {
f (t);
}
+ i = tmp;
}
}
template <typename Function>
void foreach_audio_time_axis (Function f) {
- for (iterator i = begin(); i != end(); ++i) {
+ for (iterator i = begin(); i != end(); ) {
+ iterator tmp = i;
+ ++tmp;
AudioTimeAxisView* t = dynamic_cast<AudioTimeAxisView*> (*i);
if (t) {
f (t);
}
+ i = tmp;
}
}
template <typename Function>
void foreach_midi_time_axis (Function f) {
- for (iterator i = begin(); i != end(); ++i) {
+ for (iterator i = begin(); i != end(); ) {
+ iterator tmp = i;
+ ++tmp;
MidiTimeAxisView* t = dynamic_cast<MidiTimeAxisView*> (*i);
if (t) {
f (t);
}
+ i = tmp;
}
}
#endif
std::string ff_file_path;
- if (find_file_in_search_path (Searchpath(Glib::getenv("PATH")), X_("ffmpeg_harvid"), ff_file_path)) { ffmpeg_exe = ff_file_path; }
+ if (find_file (Searchpath(Glib::getenv("PATH")), X_("ffmpeg_harvid"), ff_file_path)) { ffmpeg_exe = ff_file_path; }
else if (Glib::file_test(X_("C:\\Program Files\\harvid\\ffmpeg.exe"), Glib::FILE_TEST_EXISTS)) {
ffmpeg_exe = X_("C:\\Program Files\\ffmpeg\\ffmpeg.exe");
}
ffmpeg_exe = X_("C:\\Program Files\\ffmpeg\\ffmpeg.exe");
}
- if (find_file_in_search_path (Searchpath(Glib::getenv("PATH")), X_("ffprobe_harvid"), ff_file_path)) { ffprobe_exe = ff_file_path; }
+ if (find_file (Searchpath(Glib::getenv("PATH")), X_("ffprobe_harvid"), ff_file_path)) { ffprobe_exe = ff_file_path; }
else if (Glib::file_test(X_("C:\\Program Files\\harvid\\ffprobe.exe"), Glib::FILE_TEST_EXISTS)) {
ffprobe_exe = X_("C:\\Program Files\\ffmpeg\\ffprobe.exe");
}
if (ffmpeg_exe.empty() || ffprobe_exe.empty()) {
warning << string_compose(
_(
- "No ffprobe or ffmpeg executables could be found on this system.\n"
- "Video import and export is not possible until you install those tools.\n"
- "%1 requires ffmpeg and ffprobe from ffmpeg.org - version 1.1 or newer.\n"
- "\n"
- "The tools are included with the %1 releases from ardour.org "
- "and also available with the video-server at http://x42.github.com/harvid/\n"
- "\n"
- "Important: the files need to be installed in $PATH and named ffmpeg_harvid and ffprobe_harvid.\n"
- "If you already have a suitable ffmpeg installation on your system, we recommend creating "
- "symbolic links from ffmpeg to ffmpeg_harvid and from ffprobe to ffprobe_harvid.\n"
- ), PROGRAM_NAME) << endmsg;
+ "No ffprobe or ffmpeg executables could be found on this system.\n"
+ "Video import and export is not possible until you install those tools.\n"
+ "%1 requires ffmpeg and ffprobe from ffmpeg.org - version 1.1 or newer.\n"
+ "\n"
+ "The tools are included with the %1 releases from ardour.org "
+ "and also available with the video-server at http://x42.github.com/harvid/\n"
+ "\n"
+ "Important: the files need to be installed in $PATH and named ffmpeg_harvid and ffprobe_harvid.\n"
+ "If you already have a suitable ffmpeg installation on your system, we recommend creating "
+ "symbolic links from ffmpeg to ffmpeg_harvid and from ffprobe to ffprobe_harvid.\n"
+ "\n"
+ "see also http://manual.ardour.org/video-timeline/setup/"
+ ), PROGRAM_NAME) << endmsg;
return;
}
ffexecok = true;
#include "ardour_ui.h"
#include "gui_thread.h"
-#include "utils.h"
#include "opts.h"
#include "transcode_video_dialog.h"
#include "utils_videotl.h"
#undef CANVAS_VARIABLE
#define UI_CONFIG_VARIABLE(Type,var,name,val) var (name,val),
#define CANVAS_VARIABLE(var,name) var (name),
+#define CANVAS_STRING_VARIABLE(var,name) var (name),
+#define CANVAS_FONT_VARIABLE(var,name) var (name),
#include "ui_config_vars.h"
#include "canvas_vars.h"
#undef UI_CONFIG_VARIABLE
#undef CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
+#undef CANVAS_FONT_VARIABLE
_dirty (false)
{
load_state();
rcfile = "ardour3_ui_default.conf";
}
- if (find_file_in_search_path (ardour_config_search_path(), rcfile, default_ui_rc_file) ) {
+ if (find_file (ardour_config_search_path(), rcfile, default_ui_rc_file) ) {
XMLTree tree;
found = 1;
std::string default_ui_rc_file;
- if ( find_file_in_search_path (ardour_config_search_path(), "ardour3_ui_default.conf", default_ui_rc_file)) {
+ if ( find_file (ardour_config_search_path(), "ardour3_ui_default.conf", default_ui_rc_file)) {
XMLTree tree;
found = true;
std::string user_ui_rc_file;
- if (find_file_in_search_path (ardour_config_search_path(), "ardour3_ui.conf", user_ui_rc_file)) {
+ if (find_file (ardour_config_search_path(), "ardour3_ui.conf", user_ui_rc_file)) {
XMLTree tree;
found = true;
#undef CANVAS_VARIABLE
#define UI_CONFIG_VARIABLE(Type,var,Name,value) if (node->name() == "UI") { var.add_to_node (*node); }
#define CANVAS_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
+#define CANVAS_STRING_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
+#define CANVAS_FONT_VARIABLE(var,Name) if (node->name() == "Canvas") { var.add_to_node (*node); }
#include "ui_config_vars.h"
#include "canvas_vars.h"
#undef UI_CONFIG_VARIABLE
#undef CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
+#undef CANVAS_FONT_VARIABLE
return *node;
}
if (var.set_from_node (node)) { \
ParameterChanged (name); \
}
+#define CANVAS_STRING_VARIABLE(var,name) \
+ if (var.set_from_node (node)) { \
+ ParameterChanged (name); \
+ }
+#define CANVAS_FONT_VARIABLE(var,name) \
+ if (var.set_from_node (node)) { \
+ ParameterChanged (name); \
+ }
#include "ui_config_vars.h"
#include "canvas_vars.h"
#undef UI_CONFIG_VARIABLE
#undef CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
+#undef CANVAS_FONT_VARIABLE
}
void
UIConfiguration::pack_canvasvars ()
{
#undef CANVAS_VARIABLE
-#define CANVAS_VARIABLE(var,name) canvas_colors.insert (std::pair<std::string,UIConfigVariable<uint32_t>* >(name,&var));
+#define CANVAS_VARIABLE(var,name) canvas_colors.insert (std::pair<std::string,ColorVariable<uint32_t>* >(name,&var));
+#define CANVAS_STRING_VARIABLE(var,name)
+#define CANVAS_FONT_VARIABLE(var,name)
#include "canvas_vars.h"
#undef CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
+#undef CANVAS_FONT_VARIABLE
}
uint32_t
UIConfiguration::color_by_name (const std::string& name)
{
- map<std::string,UIConfigVariable<uint32_t>* >::iterator i = canvas_colors.find (name);
+ map<std::string,ColorVariable<uint32_t>* >::iterator i = canvas_colors.find (name);
if (i != canvas_colors.end()) {
return i->second->get();
#include "pbd/xml++.h"
#include "ardour/configuration_variable.h"
+#include "utils.h"
+
+/* This is very similar to ARDOUR::ConfigVariable but expects numeric values to
+ * be in hexadecimal. This is because it is intended for use with color
+ * specifications which are easier to scan for issues in "rrggbbaa" format than
+ * as decimals.
+ */
template<class T>
-class UIConfigVariable : public ARDOUR::ConfigVariableBase
+class ColorVariable : public ARDOUR::ConfigVariableBase
{
public:
- UIConfigVariable (std::string str) : ARDOUR::ConfigVariableBase (str) {}
- UIConfigVariable (std::string str, T val) : ARDOUR::ConfigVariableBase (str), value (val) {}
+ ColorVariable (std::string str) : ARDOUR::ConfigVariableBase (str) {}
+ ColorVariable (std::string str, T val) : ARDOUR::ConfigVariableBase (str), value (val) {}
bool set (T val) {
if (val == value) {
UIConfiguration();
~UIConfiguration();
- std::map<std::string,UIConfigVariable<uint32_t> *> canvas_colors;
+ std::map<std::string,ColorVariable<uint32_t> *> canvas_colors;
bool dirty () const;
void set_dirty ();
#include "ui_config_vars.h"
#undef UI_CONFIG_VARIABLE
#undef CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
#define CANVAS_VARIABLE(var,name) \
uint32_t get_##var () const { return var.get(); } \
bool set_##var (uint32_t val) { bool ret = var.set (val); if (ret) { ParameterChanged (name); } return ret; }
+#define CANVAS_STRING_VARIABLE(var,name) \
+ std::string get_##var () const { return var.get(); } \
+ bool set_##var (const std::string& val) { bool ret = var.set (val); if (ret) { ParameterChanged (name); } return ret; }
+#define CANVAS_FONT_VARIABLE(var,name) \
+ Pango::FontDescription get_##var () const { return ARDOUR_UI_UTILS::sanitized_font (var.get()); } \
+ bool set_##var (const std::string& val) { bool ret = var.set (val); if (ret) { ParameterChanged (name); } return ret; }
#include "canvas_vars.h"
#undef CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
+#undef CANVAS_FONT_VARIABLE
private:
/* declare variables */
#undef UI_CONFIG_VARIABLE
-#define UI_CONFIG_VARIABLE(Type,var,name,value) UIConfigVariable<Type> var;
+#define UI_CONFIG_VARIABLE(Type,var,name,value) ARDOUR::ConfigVariable<Type> var;
#include "ui_config_vars.h"
#undef UI_CONFIG_VARIABLE
#undef CANVAS_VARIABLE
-#define CANVAS_VARIABLE(var,name) UIConfigVariable<uint32_t> var;
+#define CANVAS_VARIABLE(var,name) ColorVariable<uint32_t> var;
+#define CANVAS_STRING_VARIABLE(var,name) ARDOUR::ConfigVariable<std::string> var;
+#define CANVAS_FONT_VARIABLE(var,name) ARDOUR::ConfigVariable<std::string> var;
#include "canvas_vars.h"
#undef CANVAS_VARIABLE
+#undef CANVAS_STRING_VARIABLE
+#undef CANVAS_FONT_VARIABLE
XMLNode& state ();
bool _dirty;
*/
+UI_CONFIG_VARIABLE(std::string, icon_set, "icon-set", "default")
UI_CONFIG_VARIABLE(std::string, ui_rc_file, "ui-rc-file", "ardour3_ui_dark.rc")
UI_CONFIG_VARIABLE(bool, flat_buttons, "flat-buttons", false)
-UI_CONFIG_VARIABLE(float, waveform_gradient_depth, "waveform-gradient-depth", 0.6)
-UI_CONFIG_VARIABLE(float, timeline_item_gradient_depth, "timeline-item-gradient-depth", 1.3)
+UI_CONFIG_VARIABLE(float, waveform_gradient_depth, "waveform-gradient-depth", 0)
+UI_CONFIG_VARIABLE(float, timeline_item_gradient_depth, "timeline-item-gradient-depth", 0.5)
UI_CONFIG_VARIABLE(bool, all_floating_windows_are_dialogs, "all-floating-windows-are-dialogs", false)
UI_CONFIG_VARIABLE (bool, color_regions_using_track_color, "color-regions-using-track-color", false)
UI_CONFIG_VARIABLE (bool, show_waveform_clipping, "show-waveform-clipping", true)
+UI_CONFIG_VARIABLE (uint32_t, lock_gui_after_seconds, "lock-gui-after-seconds", 0)
+UI_CONFIG_VARIABLE (bool, draggable_playhead, "draggable-playhead", true)
#include <gtkmm2ext/utils.h>
#include "ardour/rc_configuration.h"
#include "ardour/filesystem_paths.h"
+
#include "canvas/item.h"
+#include "canvas/utils.h"
#include "ardour_ui.h"
#include "debug.h"
using namespace PBD;
using Gtkmm2ext::Keyboard;
-sigc::signal<void> DPIReset;
+namespace ARDOUR_UI_UTILS {
+ sigc::signal<void> DPIReset;
+}
#ifdef PLATFORM_WINDOWS
#define random() rand()
* @param s true to make sensitive, false to make insensitive
*/
void
-add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
+ARDOUR_UI_UTILS::add_item_with_sensitivity (Menu_Helpers::MenuList& m, Menu_Helpers::MenuElem e, bool s)
{
m.push_back (e);
if (!s) {
gint
-just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
+ARDOUR_UI_UTILS::just_hide_it (GdkEventAny */*ev*/, Gtk::Window *win)
{
win->hide ();
return 0;
*/
unsigned char*
-xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
+ARDOUR_UI_UTILS::xpm2rgb (const char** xpm, uint32_t& w, uint32_t& h)
{
static long vals[256], val;
uint32_t t, x, y, colors, cpp;
}
unsigned char*
-xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
+ARDOUR_UI_UTILS::xpm2rgba (const char** xpm, uint32_t& w, uint32_t& h)
{
static long vals[256], val;
uint32_t t, x, y, colors, cpp;
return (savergb);
}
+/** Returns a Pango::FontDescription given a string describing the font.
+ *
+ * If the returned FontDescription does not specify a family, then
+ * the family is set to "Sans". This mirrors GTK's behaviour in
+ * gtkstyle.c.
+ *
+ * Some environments will force Pango to specify the family
+ * even if it was not specified in the string describing the font.
+ * Such environments should be left unaffected by this function,
+ * since the font family will be left alone.
+ *
+ * There may be other similar font specification enforcement
+ * that we might add here later.
+ */
+Pango::FontDescription
+ARDOUR_UI_UTILS::sanitized_font (std::string const& name)
+{
+ Pango::FontDescription fd (name);
+
+ if (fd.get_family().empty()) {
+ fd.set_family ("Sans");
+ }
+
+ return fd;
+}
+
Pango::FontDescription
-get_font_for_style (string widgetname)
+ARDOUR_UI_UTILS::get_font_for_style (string widgetname)
{
Gtk::Window window (WINDOW_TOPLEVEL);
Gtk::Label foobar;
}
uint32_t
-rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
+ARDOUR_UI_UTILS::rgba_from_style (string style, uint32_t r, uint32_t g, uint32_t b, uint32_t a, string attr, int state, bool rgba)
{
/* In GTK+2, styles aren't set up correctly if the widget is not
attached to a toplevel window that has a screen pointer.
if (state == Gtk::STATE_NORMAL && rgba) {
return (uint32_t) RGBA_TO_UINT(r,g,b,a);
} else {
- return (uint32_t) RGB_TO_UINT(r,g,b);
+ return (uint32_t) RGBA_TO_UINT(r,g,b,255);
}
}
bool
-rgba_p_from_style (string style, float *r, float *g, float *b, string attr, int state)
+ARDOUR_UI_UTILS::rgba_p_from_style (string style, float *r, float *g, float *b, string attr, int state)
{
static Gtk::Window* window = 0;
assert (r && g && b);
}
void
-set_color (Gdk::Color& c, int rgb)
+ARDOUR_UI_UTILS::set_color_from_rgb (Gdk::Color& c, uint32_t rgb)
+{
+ /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by
+ multiplying by 256.
+ */
+ c.set_rgb ((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
+}
+
+void
+ARDOUR_UI_UTILS::set_color_from_rgba (Gdk::Color& c, uint32_t rgba)
+{
+ /* Gdk::Color color ranges are 16 bit, so scale from 8 bit by
+ multiplying by 256.
+ */
+ c.set_rgb ((rgba >> 24)*256, ((rgba & 0xff0000) >> 16)*256, ((rgba & 0xff00) >> 8)*256);
+}
+
+uint32_t
+ARDOUR_UI_UTILS::gdk_color_to_rgba (Gdk::Color const& c)
{
- c.set_rgb((rgb >> 16)*256, ((rgb & 0xff00) >> 8)*256, (rgb & 0xff)*256);
+ /* since alpha value is not available from a Gdk::Color, it is
+ hardcoded as 0xff (aka 255 or 1.0)
+ */
+
+ const uint32_t r = c.get_red_p () * 255.0;
+ const uint32_t g = c.get_green_p () * 255.0;
+ const uint32_t b = c.get_blue_p () * 255.0;
+ const uint32_t a = 0xff;
+
+ return RGBA_TO_UINT (r,g,b,a);
}
+
bool
-relay_key_press (GdkEventKey* ev, Gtk::Window* win)
+ARDOUR_UI_UTILS::relay_key_press (GdkEventKey* ev, Gtk::Window* win)
{
PublicEditor& ed (PublicEditor::instance());
}
bool
-forward_key_press (GdkEventKey* ev)
+ARDOUR_UI_UTILS::forward_key_press (GdkEventKey* ev)
{
- return PublicEditor::instance().on_key_press_event(ev);
+ return PublicEditor::instance().on_key_press_event(ev);
}
bool
-emulate_key_event (Gtk::Widget* w, unsigned int keyval)
+ARDOUR_UI_UTILS::emulate_key_event (Gtk::Widget* w, unsigned int keyval)
{
GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET(w->gobj()));
GdkKeymap *keymap = gdk_keymap_get_for_display (display);
}
bool
-key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
+ARDOUR_UI_UTILS::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
{
GtkWindow* win = window.gobj();
GtkWidget* focus = gtk_window_get_focus (win);
}
Glib::RefPtr<Gdk::Pixbuf>
-get_xpm (std::string name)
+ARDOUR_UI_UTILS::get_xpm (std::string name)
{
if (!xpm_map[name]) {
std::string data_file_path;
- if(!find_file_in_search_path (spath, name, data_file_path)) {
+ if(!find_file (spath, name, data_file_path)) {
fatal << string_compose (_("cannot find XPM file for %1"), name) << endmsg;
}
return xpm_map[name];
}
+vector<string>
+ARDOUR_UI_UTILS::get_icon_sets ()
+{
+ Searchpath spath(ARDOUR::ardour_data_search_path());
+ spath.add_subdirectory_to_paths ("icons");
+ vector<string> r;
+
+ r.push_back (_("default"));
+
+ for (vector<string>::iterator s = spath.begin(); s != spath.end(); ++s) {
+
+ vector<string> entries;
+
+ get_paths (entries, *s, false, false);
+
+ for (vector<string>::iterator e = entries.begin(); e != entries.end(); ++e) {
+ if (Glib::file_test (*e, Glib::FILE_TEST_IS_DIR)) {
+ r.push_back (Glib::filename_to_utf8 (Glib::path_get_basename(*e)));
+ }
+ }
+ }
+
+ return r;
+}
+
std::string
-get_icon_path (const char* cname)
+ARDOUR_UI_UTILS::get_icon_path (const char* cname, string icon_set)
{
+ std::string data_file_path;
string name = cname;
name += X_(".png");
Searchpath spath(ARDOUR::ardour_data_search_path());
- spath.add_subdirectory_to_paths("icons");
-
- std::string data_file_path;
+ if (!icon_set.empty() && icon_set != _("default")) {
- if (!find_file_in_search_path (spath, name, data_file_path)) {
- fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg;
+ /* add "icons/icon_set" but .. not allowed to add both of these at once */
+ spath.add_subdirectory_to_paths ("icons");
+ spath.add_subdirectory_to_paths (icon_set);
+
+ find_file (spath, name, data_file_path);
+ }
+
+ if (data_file_path.empty()) {
+
+ if (!icon_set.empty() && icon_set != _("default")) {
+ warning << string_compose (_("icon \"%1\" not found for icon set \"%2\", fallback to default"), cname, icon_set) << endmsg;
+ }
+
+ Searchpath def (ARDOUR::ardour_data_search_path());
+ def.add_subdirectory_to_paths ("icons");
+
+ if (!find_file (def, name, data_file_path)) {
+ fatal << string_compose (_("cannot find icon image for %1 using %2"), name, spath.to_string()) << endmsg;
+ /*NOTREACHED*/
+ }
}
return data_file_path;
}
+Glib::RefPtr<Gdk::Pixbuf>
+ARDOUR_UI_UTILS::get_icon (const char* cname, string icon_set)
+{
+ Glib::RefPtr<Gdk::Pixbuf> img;
+ try {
+ img = Gdk::Pixbuf::create_from_file (get_icon_path (cname, icon_set));
+ } catch (const Gdk::PixbufError &e) {
+ cerr << "Caught PixbufError: " << e.what() << endl;
+ } catch (...) {
+ error << string_compose (_("Caught exception while loading icon named %1"), cname) << endmsg;
+ }
+
+ return img;
+}
+
+namespace ARDOUR_UI_UTILS {
Glib::RefPtr<Gdk::Pixbuf>
get_icon (const char* cname)
{
return img;
}
+}
string
-longest (vector<string>& strings)
+ARDOUR_UI_UTILS::longest (vector<string>& strings)
{
if (strings.empty()) {
return string ("");
}
bool
-key_is_legal_for_numeric_entry (guint keyval)
+ARDOUR_UI_UTILS::key_is_legal_for_numeric_entry (guint keyval)
{
/* we assume that this does not change over the life of the process
*/
return false;
}
+
void
-set_pango_fontsize ()
+ARDOUR_UI_UTILS::set_pango_fontsize ()
{
long val = ARDOUR::Config->get_font_scale();
}
void
-reset_dpi ()
+ARDOUR_UI_UTILS::reset_dpi ()
{
long val = ARDOUR::Config->get_font_scale();
set_pango_fontsize ();
}
void
-resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
+ARDOUR_UI_UTILS::resize_window_to_proportion_of_monitor (Gtk::Window* window, int max_width, int max_height)
{
Glib::RefPtr<Gdk::Screen> screen = window->get_screen ();
Gdk::Rectangle monitor_rect;
/** Replace _ with __ in a string; for use with menu item text to make underscores displayed correctly */
string
-escape_underscores (string const & s)
+ARDOUR_UI_UTILS::escape_underscores (string const & s)
{
string o;
string::size_type const N = s.length ();
/** Replace < and > with < and > respectively to make < > display correctly in markup strings */
string
-escape_angled_brackets (string const & s)
+ARDOUR_UI_UTILS::escape_angled_brackets (string const & s)
{
string o = s;
boost::replace_all (o, "<", "<");
}
Gdk::Color
-unique_random_color (list<Gdk::Color>& used_colors)
+ARDOUR_UI_UTILS::unique_random_color (list<Gdk::Color>& used_colors)
{
Gdk::Color newcolor;
}
string
-rate_as_string (float r)
+ARDOUR_UI_UTILS::rate_as_string (float r)
{
char buf[32];
if (fmod (r, 1000.0f)) {
}
return buf;
}
+
+
+string
+ARDOUR_UI_UTILS::track_number_to_string (
+ int64_t tracknumber,
+ std::string sep,
+ std::string postfix
+ )
+{
+ string rv;
+ if (tracknumber > 0) {
+ rv = "<span weight=\"bold\" font_family=\"ArdourMono, Mono\">";
+ rv += PBD::to_string (tracknumber, std::dec);
+ rv += "</span>";
+ rv += sep;
+ }
+ else if (tracknumber < 0) {
+ rv = "<span weight=\"bold\" font_family=\"ArdourMono, Mono\">";
+ rv += PBD::to_string (-tracknumber, std::dec);
+ rv += "</span>";
+ rv += sep;
+ }
+ rv += Glib::Markup::escape_text(postfix);
+ return rv;
+}
class Item;
}
+namespace ARDOUR_UI_UTILS {
+
extern sigc::signal<void> DPIReset;
gint just_hide_it (GdkEventAny*, Gtk::Window*);
ArdourCanvas::Points* get_canvas_points (std::string who, uint32_t npoints);
+Pango::FontDescription sanitized_font (std::string const&);
Pango::FontDescription get_font_for_style (std::string widgetname);
uint32_t rgba_from_style (std::string, uint32_t, uint32_t, uint32_t, uint32_t, std::string = "fg", int = Gtk::STATE_NORMAL, bool = true);
void decorate (Gtk::Window& w, Gdk::WMDecoration d);
-void set_color (Gdk::Color&, int);
+void set_color_from_rgb (Gdk::Color&, uint32_t);
+void set_color_from_rgba (Gdk::Color&, uint32_t);
+uint32_t gdk_color_to_rgba (Gdk::Color const&);
+uint32_t contrasting_text_color (uint32_t c);
bool relay_key_press (GdkEventKey* ev, Gtk::Window* win);
bool forward_key_press (GdkEventKey* ev);
bool emulate_key_event (Gtk::Widget*, unsigned int);
Glib::RefPtr<Gdk::Pixbuf> get_xpm (std::string);
-std::string get_icon_path (const char*);
-Glib::RefPtr<Gdk::Pixbuf> get_icon (const char*);
+std::vector<std::string> get_icon_sets ();
+std::string get_icon_path (const char*, std::string icon_set = std::string());
+Glib::RefPtr<Gdk::Pixbuf> get_icon (const char*, std::string icon_set = std::string());
static std::map<std::string, Glib::RefPtr<Gdk::Pixbuf> > xpm_map;
const char* const *get_xpm_data (std::string path);
std::string longest (std::vector<std::string>&);
std::string rate_as_string (float r);
+std::string track_number_to_string (int64_t tracknumber, std::string sep = "", std::string postfix = "");
+
+} // namespace
#endif /* __ardour_gtk_utils_h__ */
#include "ardour/profile.h"
#include "canvas/debug.h"
+#include "canvas/scroll_group.h"
+#include "canvas/tracking_text.h"
#include "ardour_ui.h"
#include "audio_clock.h"
#include "editor.h"
#include "editor_drag.h"
+#include "global_signals.h"
#include "main_clock.h"
-#include "utils.h"
#include "verbose_cursor.h"
#include "i18n.h"
VerboseCursor::VerboseCursor (Editor* editor)
: _editor (editor)
- , _visible (false)
- , _xoffset (0)
- , _yoffset (0)
{
- _canvas_item = new ArdourCanvas::Text (_editor->_track_canvas->root());
+ _canvas_item = new ArdourCanvas::TrackingText (_editor->get_noscroll_group());
CANVAS_DEBUG_NAME (_canvas_item, "verbose canvas cursor");
- _canvas_item->set_ignore_events (true);
- _canvas_item->set_font_description (get_font_for_style (N_("VerboseCanvasCursor")));
+ _canvas_item->set_font_description (Pango::FontDescription (ARDOUR_UI::config()->get_canvasvar_LargerBoldFont()));
+ color_handler ();
+
+ ARDOUR_UI_UTILS::ColorsChanged.connect (sigc::mem_fun (*this, &VerboseCursor::color_handler));
}
-ArdourCanvas::Item *
-VerboseCursor::canvas_item () const
+void
+VerboseCursor::color_handler ()
{
- return _canvas_item;
+ _canvas_item->set_color (ARDOUR_UI::config()->get_canvasvar_VerboseCanvasCursor());
}
-void
-VerboseCursor::set (string const & text, double x, double y)
+ArdourCanvas::Item *
+VerboseCursor::canvas_item () const
{
- set_text (text);
- set_position (x, y);
+ return _canvas_item;
}
+/** Set the contents of the cursor.
+ */
void
-VerboseCursor::set_text (string const & text)
+VerboseCursor::set (string const & text)
{
_canvas_item->set (text);
}
-/** @param xoffset x offset to be applied on top of any set_position() call
- * before the next show ().
- * @param yoffset y offset as above.
- */
void
-VerboseCursor::show (double xoffset, double yoffset)
+VerboseCursor::show ()
{
- _xoffset = xoffset;
- _yoffset = yoffset;
-
- if (_visible) {
- return;
- }
-
- _canvas_item->raise_to_top ();
- _canvas_item->show ();
- _visible = true;
+ _canvas_item->show_and_track (true, true);
+ _canvas_item->parent()->raise_to_top ();
}
void
VerboseCursor::hide ()
{
_canvas_item->hide ();
- _visible = false;
+ _canvas_item->parent()->lower_to_bottom ();
+ /* reset back to a sensible default for the next time we display the VC */
+ _canvas_item->set_offset (ArdourCanvas::Duple (10, 10));
}
-double
-VerboseCursor::clamp_x (double x)
-{
- _editor->clamp_verbose_cursor_x (x);
- return x;
-}
-
-double
-VerboseCursor::clamp_y (double y)
+void
+VerboseCursor::set_offset (ArdourCanvas::Duple const & d)
{
- _editor->clamp_verbose_cursor_y (y);
- return y;
+ _canvas_item->set_offset (d);
}
void
-VerboseCursor::set_time (framepos_t frame, double x, double y)
+VerboseCursor::set_time (framepos_t frame)
{
char buf[128];
Timecode::Time timecode;
return;
}
- AudioClock::Mode m;
+ /* Take clock mode from the primary clock */
- if (Profile->get_sae() || Profile->get_small_screen()) {
- m = ARDOUR_UI::instance()->primary_clock->mode();
- } else {
- m = ARDOUR_UI::instance()->secondary_clock->mode();
- }
+ AudioClock::Mode m = ARDOUR_UI::instance()->primary_clock->mode();
switch (m) {
case AudioClock::BBT:
break;
}
- set (buf, x, y);
+ _canvas_item->set (buf);
}
void
-VerboseCursor::set_duration (framepos_t start, framepos_t end, double x, double y)
+VerboseCursor::set_duration (framepos_t start, framepos_t end)
{
char buf[128];
Timecode::Time timecode;
AudioClock::Mode m;
- if (Profile->get_sae() || Profile->get_small_screen()) {
+ if (Profile->get_sae() || Profile->get_small_screen() || Profile->get_trx()) {
m = ARDOUR_UI::instance()->primary_clock->mode ();
} else {
m = ARDOUR_UI::instance()->secondary_clock->mode ();
break;
}
- set (buf, x, y);
-}
-
-void
-VerboseCursor::set_color (uint32_t color)
-{
- _canvas_item->set_color (color);
-}
-
-/** Set the position of the verbose cursor. Any x/y offsets
- * passed to the last call to show() will be applied to the
- * coordinates passed in here.
- */
-void
-VerboseCursor::set_position (double x, double y)
-{
- _canvas_item->set_x_position (clamp_x (x + _xoffset));
- _canvas_item->set_y_position (clamp_y (y + _yoffset));
+ _canvas_item->set (buf);
}
bool
VerboseCursor::visible () const
{
- return _visible;
+ return _canvas_item->visible();
}
*/
#include "ardour/types.h"
-#include "canvas/text.h"
#include "canvas/canvas.h"
class Editor;
+namespace ArdourCanvas {
+ class TrackingText;
+}
+
class VerboseCursor
{
public:
ArdourCanvas::Item* canvas_item () const;
bool visible () const;
- void set_color (uint32_t);
-
- void set (std::string const &, double, double);
- void set_text (std::string const &);
- void set_position (double, double);
- void set_time (framepos_t, double, double);
- void set_duration (framepos_t, framepos_t, double, double);
+ void set (std::string const &);
+ void set_time (framepos_t);
+ void set_duration (framepos_t, framepos_t);
+ void set_offset (ArdourCanvas::Duple const&);
- void show (double xoffset = 0, double yoffset = 0);
+ void show ();
void hide ();
-
- ArdourCanvas::Item& item() { return *_canvas_item; }
-
+
private:
- double clamp_x (double);
- double clamp_y (double);
-
- Editor* _editor;
- ArdourCanvas::Text* _canvas_item;
- bool _visible;
- double _xoffset;
- double _yoffset;
+ Editor* _editor;
+ ArdourCanvas::TrackingText* _canvas_item;
+
+ void color_handler ();
};
#include "ardour_ui.h"
#include "video_image_frame.h"
#include "public_editor.h"
-#include "utils.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
#include "utils_videotl.h"
#include <gtkmm2ext/utils.h>
free (d);
}
-VideoImageFrame::VideoImageFrame (PublicEditor& ed, ArdourCanvas::Group& parent, int w, int h, std::string vsurl, std::string vfn)
+VideoImageFrame::VideoImageFrame (PublicEditor& ed, ArdourCanvas::Container& parent, int w, int h, std::string vsurl, std::string vfn)
: editor (ed)
, _parent(&parent)
, clip_width(w)
#include "ardour/ardour.h"
#include "pbd/signals.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
#include "canvas/pixbuf.h"
#include "canvas/image.h"
class VideoImageFrame : public sigc::trackable
{
public:
- VideoImageFrame (PublicEditor&, ArdourCanvas::Group&, int, int, std::string, std::string);
+ VideoImageFrame (PublicEditor&, ArdourCanvas::Container&, int, int, std::string, std::string);
virtual ~VideoImageFrame ();
void set_position (framepos_t);
protected:
PublicEditor& editor;
- ArdourCanvas::Group *_parent;
+ ArdourCanvas::Container *_parent;
ArdourCanvas::Image *image;
boost::shared_ptr<ArdourCanvas::Image::Data> img;
using namespace std;
using namespace PBD;
+using namespace ARDOUR_UI_UTILS;
VideoMonitor::VideoMonitor (PublicEditor *ed, std::string xjadeo_bin_path)
: editor (ed)
{
manually_seeked_frame = 0;
fps =0.0; // = _session->timecode_frames_per_second();
- sync_by_manual_seek = false;
+ sync_by_manual_seek = true;
_restore_settings_mask = 0;
clock_connection = sigc::connection();
state_connection = sigc::connection();
starting = 0;
osdmode = 10; // 1: frameno, 2: timecode, 8: box
- process = new ARDOUR::SystemExec(xjadeo_bin_path, X_("-R"));
+ process = new ARDOUR::SystemExec(xjadeo_bin_path, X_("-R -J"));
process->ReadStdout.connect_same_thread (*this, boost::bind (&VideoMonitor::parse_output, this, _1 ,_2));
process->Terminated.connect (*this, invalidator (*this), boost::bind (&VideoMonitor::terminated, this), gui_context());
XJKeyEvent.connect (*this, invalidator (*this), boost::bind (&VideoMonitor::forward_keyevent, this, _1), gui_context());
#include "utils_videotl.h"
#include "i18n.h"
+#ifdef PLATFORM_WINDOWS
+#include <windows.h>
+#endif
+
using namespace Gtk;
using namespace std;
using namespace PBD;
#endif
std::string icsd_file_path;
- if (find_file_in_search_path (PBD::Searchpath(Glib::getenv("PATH")), X_("harvid"), icsd_file_path)) {
+ if (find_file (PBD::Searchpath(Glib::getenv("PATH")), X_("harvid"), icsd_file_path)) {
path_entry.set_text(icsd_file_path);
}
#ifdef PLATFORM_WINDOWS
else {
PBD::warning <<
string_compose(
- _("The external video server 'harvid' can not be found. The tool is included with the %1 releases from ardour.org, "
- "alternatively you can download it from http://x42.github.com/harvid/ or acquire it from your distribution."), PROGRAM_NAME)
+ _("The external video server 'harvid' can not be found.\n"
+ "The tool is included with the %1 releases from ardour.org, "
+ "alternatively you can download it from http://x42.github.com/harvid/ "
+ "or acquire it from your distribution.\n"
+ "\n"
+ "see also http://manual.ardour.org/video-timeline/setup/"
+ ), PROGRAM_NAME)
<< endmsg;
}
#include "ardour_ui.h"
#include "public_editor.h"
#include "gui_thread.h"
-#include "utils.h"
#include "utils_videotl.h"
#include "rgb_macros.h"
#include "video_timeline.h"
using namespace Timecode;
using namespace VideoUtils;
-VideoTimeLine::VideoTimeLine (PublicEditor *ed, ArdourCanvas::Group *vbg, int initial_height)
+VideoTimeLine::VideoTimeLine (PublicEditor *ed, ArdourCanvas::Container *vbg, int initial_height)
: editor (ed)
, videotl_group(vbg)
, bar_height(initial_height)
}
/* video-monitor for this timeline */
+void
+VideoTimeLine::xjadeo_readversion (std::string d, size_t /* s */) {
+ xjadeo_version += d;
+}
+
void
VideoTimeLine::find_xjadeo () {
std::string xjadeo_file_path;
#endif
if (getenv("XJREMOTE")) {
_xjadeo_bin = getenv("XJREMOTE");
- } else if (find_file_in_search_path (Searchpath(Glib::getenv("PATH")), X_("xjremote"), xjadeo_file_path)) {
+ } else if (find_file (Searchpath(Glib::getenv("PATH")), X_("xjremote"), xjadeo_file_path)) {
_xjadeo_bin = xjadeo_file_path;
}
#ifdef PLATFORM_WINDOWS
_xjadeo_bin = X_("");
warning << _("Video-monitor 'xjadeo' was not found. Please install http://xjadeo.sf.net/ "
"(a custom path to xjadeo can be specified by setting the XJREMOTE environment variable. "
- "It should point to an application compatible with xjadeo's remote-control interface 'xjremote').")
+ "It should point to an application compatible with xjadeo's remote-control interface 'xjremote').\n"
+ "\n"
+ "see also http://manual.ardour.org/video-timeline/setup/")
<< endmsg;
}
+ if (found_xjadeo ()) {
+ ARDOUR::SystemExec version_check(_xjadeo_bin, X_("--version"));
+ xjadeo_version = "";
+ version_check.ReadStdout.connect_same_thread (*this, boost::bind (&VideoTimeLine::xjadeo_readversion, this, _1 ,_2));
+ version_check.Terminated.connect_same_thread (*this, boost::bind (&VideoTimeLine::xjadeo_readversion, this, "\n" ,1));
+ if (version_check.start(2)) {
+ warning << _(
+ "Video-monitor 'xjadeo' cannot be launched."
+ ) << endmsg;
+ _xjadeo_bin = X_("");
+ return;
+ }
+
+ version_check.wait ();
+ int timeout = 300;
+ while (xjadeo_version.empty() && --timeout) {
+ Glib::usleep(10000);
+ }
+
+ bool v_ok = false;
+ size_t vo = xjadeo_version.find(" version ");
+ if (vo != string::npos) {
+ int v_major, v_minor, v_micro;
+ if(sscanf(xjadeo_version.substr(vo + 9, string::npos).c_str(),"%d.%d.%d",
+ &v_major, &v_minor, &v_micro) == 3)
+ {
+ if (v_major >= 1) v_ok = true;
+ else if (v_major == 0 && v_minor >= 8) v_ok = true;
+ else if (v_major == 0 && v_minor >= 7 && v_micro >= 7) v_ok = true;
+ }
+ }
+ if (!v_ok) {
+ _xjadeo_bin = X_("");
+ warning << _(
+ "Video-monitor 'xjadeo' is too old. "
+ "Please install xjadeo version 0.7.7 or later. http://xjadeo.sf.net/"
+ ) << endmsg;
+ }
+ }
}
void
#include "video_image_frame.h"
#include "video_monitor.h"
#include "pbd/signals.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
namespace ARDOUR {
class Session;
class VideoTimeLine : public sigc::trackable, public ARDOUR::SessionHandlePtr, public PBD::ScopedConnectionList, public PBD::StatefulDestructible
{
public:
- VideoTimeLine (PublicEditor*, ArdourCanvas::Group*, int);
+ VideoTimeLine (PublicEditor*, ArdourCanvas::Container*, int);
virtual ~VideoTimeLine ();
void set_session (ARDOUR::Session *s);
protected:
PublicEditor *editor;
- ArdourCanvas::Group *videotl_group;
+ ArdourCanvas::Container *videotl_group;
int bar_height;
std::string _xjadeo_bin;
std::string video_server_url;
std::string server_docroot;
+ void xjadeo_readversion (std::string d, size_t s);
+ std::string xjadeo_version;
+
typedef std::list<VideoImageFrame*> VideoFrames;
VideoFrames video_frames;
VideoImageFrame *get_video_frame (framepos_t vfn, int cut=0, int rightend = -1);
if (_linear) {
v = _controllable->lower() + ((_controllable->upper() - _controllable->lower()) * display_value);
} else {
- v = slider_position_to_gain_with_max (display_value, ARDOUR::Config->get_max_gain());
+ v = ARDOUR::slider_position_to_gain_with_max (display_value, ARDOUR::Config->get_max_gain());
}
return v;
if (_linear) {
v = (control_value - _controllable->lower ()) / (_controllable->upper() - _controllable->lower());
} else {
- v = gain_to_slider_position_with_max (control_value, _controllable->upper());
+ v = ARDOUR::gain_to_slider_position_with_max (control_value, _controllable->upper());
}
return v;
*/
#if 0
/* convert to linear/fractional slider position domain */
- v = gain_to_slider_position_with_max (_controllable->get_value (), _controllable->upper());
+ v = ARDOUR::gain_to_slider_position_with_max (_controllable->get_value (), _controllable->upper());
/* increment in this domain */
v += control_delta;
/* clamp to appropriate range for linear/fractional slider domain */
v = std::max (0.0, std::min (1.0, v));
/* convert back to gain coefficient domain */
- v = slider_position_to_gain_with_max (v, _controllable->upper());
+ v = ARDOUR::slider_position_to_gain_with_max (v, _controllable->upper());
/* clamp in controller domain */
v = std::max (_controllable->lower(), std::min (_controllable->upper(), v));
/* convert to dB domain */
'ghostregion.cc',
'global_port_matrix.cc',
'group_tabs.cc',
- 'gtk-custom-hruler.c',
- 'gtk-custom-ruler.c',
'gtk_pianokeyboard.c',
'gui_object.cc',
'insert_time_dialog.cc',
'route_processor_selection.cc',
'route_time_axis.cc',
'route_ui.cc',
+ 'ruler_dialog.cc',
'search_path_option.cc',
'selection.cc',
'send_ui.cc',
'session_option_editor.cc',
'sfdb_ui.cc',
'shuttle_control.cc',
+ 'soundcloud_export_selector.cc',
'splash.cc',
'speaker_dialog.cc',
'startup.cc',
'', '')
autowaf.configure(conf)
- if Options.options.dist_target == 'auto':
- if re.search ("linux", sys.platform) != None:
- autowaf.check_pkg(conf, 'alsa', uselib_store='ALSA')
-
# TODO: Insert a sanity check for on OS X to ensure CoreAudio is present
autowaf.check_pkg(conf, 'fftw3f', uselib_store='FFTW3F',
'INSTALL_PREFIX' : bld.env['PREFIX'],
'LIBDIR' : os.path.normpath(bld.env['DLLDIR']),
'DATADIR' : os.path.normpath(bld.env['DATADIR']),
- 'SYSCONFDIR' : os.path.normpath(bld.env['CONFDIR']),
+ 'CONFDIR' : os.path.normpath(bld.env['CONFDIR']),
'LIBS' : 'build/libs',
'VERSION' : bld.env['VERSION'],
'EXECUTABLE' : 'build/gtk2_ardour/ardour-' + bld.env['VERSION']
dark_rc_subst_dict = {}
light_rc_subst_dict = {}
+ ui_conf_dict = {}
font_sizes = {}
base_font = ""
dark_rc_subst_dict[key] = fontstyle
light_rc_subst_dict[key] = fontstyle
-
+ ui_conf_dict[key] = points
+
# @FONT_SIZE_XXXX@
for sizename,points in iter(font_sizes.items()):
key = "_".join (['FONT_SIZE',sizename])
dark_rc_subst_dict[key] = points
light_rc_subst_dict[key] = points
+ ui_conf_dict[key] = points
# various font names, eg @BOLD_MONOSPACE@
for font_sym,text in iter(font_names.items()):
key = font_sym
dark_rc_subst_dict[key] = text
light_rc_subst_dict[key] = text
+ ui_conf_dict[key] = text
# RC files
dark_rc_subst_dict['COLOR_SCHEME'] = build_color_scheme(
obj.target = 'ardour3_ui_light.rc'
obj.install_path = bld.env['CONFDIR']
+ obj = bld(features = 'subst')
+ obj.source = [ 'ardour3_ui_default.conf.in' ]
+ obj.target = 'ardour3_ui_default.conf'
+ obj.install_path = None
+ set_subst_dict(obj, ui_conf_dict)
+
# Menus
menus_argv = []
if bld.is_defined('GTKOSX'):
using namespace PBD;
#ifdef PLATFORM_WINDOWS
+#include <windows.h>
#define sleep(X) Sleep((X) * 1000)
#endif
'INSTALL_PREFIX' : bld.env['PREFIX'],
'LIBDIR' : os.path.normpath(bld.env['LIBDIR']),
'DATADIR' : os.path.normpath(bld.env['DATADIR']),
- 'SYSCONFDIR' : os.path.normpath(bld.env['SYSCONFDIR']),
+ 'CONFDIR' : os.path.normpath(bld.env['CONFDIR']),
'LIBS' : 'build/libs',
'VERSION' : bld.env['VERSION'],
'EXECUTABLE' : 'build/headless/hardour-' + bld.env['VERSION']
RelativePath="..\default_click.cc"
>
</File>
+ <File
+ RelativePath="..\delayline.cc"
+ >
+ </File>
<File
RelativePath="..\delivery.cc"
>
RelativePath="..\midi_ui.cc"
>
</File>
+ <File
+ RelativePath="..\mididm.cc"
+ >
+ </File>
<File
RelativePath="..\midiport_manager.cc"
>
RelativePath="..\sndfilesource.cc"
>
</File>
+ <File
+ RelativePath="..\soundcloud_upload.cc"
+ >
+ </File>
<File
RelativePath="..\source.cc"
>
RelativePath="..\ardour\debug.h"
>
</File>
+ <File
+ RelativePath="..\ardour\delayline.h"
+ >
+ </File>
<File
RelativePath="..\ardour\delivery.h"
>
RelativePath="..\ardour\midi_ui.h"
>
</File>
+ <File
+ RelativePath="..\ardour\mididm.h"
+ >
+ </File>
<File
RelativePath="..\ardour\midiport_manager.h"
>
RelativePath="..\ardour\route_group_specialized.h"
>
</File>
+ <File
+ RelativePath="..\ardour\route_sorters.h"
+ >
+ </File>
<File
RelativePath="..\ardour\runtime_functions.h"
>
RelativePath="..\ardour\sndfilesource.h"
>
</File>
+ <File
+ RelativePath="..\ardour\soundcloud_upload.h"
+ >
+ </File>
<File
RelativePath="..\ardour\soundseq.h"
>
{
Glib::Threads::Mutex::Lock am (control_lock(), Glib::Threads::TRY_LOCK);
- if (am.locked() && _session.transport_rolling() && _gain_control->automation_playback()) {
+ if (am.locked()
+ && (_session.transport_rolling() || _session.bounce_processing())
+ && _gain_control->automation_playback())
+ {
assert (_gain_automation_buffer);
_apply_gain_automation = _gain_control->list()->curve().rt_safe_get_vector (
start_frame, end_frame, _gain_automation_buffer, nframes);
* external D-A/D-A converters. Units are samples.
*/
virtual int set_systemic_output_latency (uint32_t) = 0;
+ /** Set the (additional) input latency for a specific midi device,
+ * or if the identifier is empty, apply to all midi devices.
+ */
+ virtual int set_systemic_midi_input_latency (std::string const, uint32_t) = 0;
+ /** Set the (additional) output latency for a specific midi device,
+ * or if the identifier is empty, apply to all midi devices.
+ */
+ virtual int set_systemic_midi_output_latency (std::string const, uint32_t) = 0;
/* Retrieving parameters */
virtual uint32_t output_channels () const = 0;
virtual uint32_t systemic_input_latency () const = 0;
virtual uint32_t systemic_output_latency () const = 0;
+ virtual uint32_t systemic_midi_input_latency (std::string const) const = 0;
+ virtual uint32_t systemic_midi_output_latency (std::string const) const = 0;
/** override this if this implementation returns true from
* requires_driver_selection()
virtual int set_midi_option (const std::string& option) = 0;
virtual std::string midi_option () const = 0;
-
+
+ /** Detailed MIDI device list - if available */
+ virtual std::vector<DeviceStatus> enumerate_midi_devices () const = 0;
+
+ /** mark a midi-devices as enabled */
+ virtual int set_midi_device_enabled (std::string const, bool) = 0;
+
+ /** query if a midi-device is enabled */
+ virtual bool midi_device_enabled (std::string const) const = 0;
+
+ /** if backend supports systemic_midi_[in|ou]tput_latency() */
+ virtual bool can_set_systemic_midi_latencies () const = 0;
+
/* State Control */
/** Start using the device named in the most recent call
int remove_channel (uint32_t how_many);
bool set_name (std::string const &);
+ bool set_write_source_name (const std::string& str);
/* stateful */
boost::shared_ptr<Region> bounce_range (framepos_t start, framepos_t end, InterThreadInfo&,
boost::shared_ptr<Processor> endpoint, bool include_endpoint);
int export_stuff (BufferSet& bufs, framepos_t start_frame, framecnt_t nframes,
- boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export);
+ boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze);
int set_state (const XMLNode&, int version);
class InternalPort;
class MidiPort;
+class MIDIDM;
class Port;
class Session;
class ProcessThread;
/* latency measurement */
- MTDM* mtdm();
+ MTDM* mtdm() { return _mtdm; }
+ MIDIDM* mididm() { return _mididm; }
+
int prepare_for_latency_measurement ();
- int start_latency_detection ();
+ int start_latency_detection (bool);
void stop_latency_detection ();
void set_latency_input_port (const std::string&);
void set_latency_output_port (const std::string&);
uint32_t latency_signal_delay () const { return _latency_signal_latency; }
+ enum LatencyMeasurement {
+ MeasureNone,
+ MeasureAudio,
+ MeasureMIDI
+ };
+
+ LatencyMeasurement measuring_latency () const { return _measuring_latency; }
+
private:
AudioEngine ();
Glib::Threads::Thread* m_meter_thread;
ProcessThread* _main_thread;
MTDM* _mtdm;
- bool _measuring_latency;
+ MIDIDM* _mididm;
+ LatencyMeasurement _measuring_latency;
PortEngine::PortHandle _latency_input_port;
PortEngine::PortHandle _latency_output_port;
framecnt_t _latency_flush_frames;
/** Constructor to be called for existing in-session files */
AudioFileSource (Session&, const XMLNode&, bool must_exist = true);
+ /** Constructor to be called for crash recovery. Final argument is not
+ * used but exists to differentiate from the external-to-session
+ * constructor above.
+ */
+ AudioFileSource (Session&, const std::string& path, Source::Flag flags, bool);
+
int init (const std::string& idstr, bool must_exist);
virtual void set_header_timeline_position () = 0;
boost::shared_ptr<Region> bounce_range (framepos_t, framepos_t, InterThreadInfo&, boost::shared_ptr<Processor>, bool)
{ return boost::shared_ptr<Region> (); }
- int export_stuff (BufferSet&, framepos_t, framecnt_t, boost::shared_ptr<Processor>, bool, bool)
+ int export_stuff (BufferSet&, framepos_t, framecnt_t, boost::shared_ptr<Processor>, bool, bool, bool)
{ return -1; }
boost::shared_ptr<Diskstream> diskstream_factory (XMLNode const &)
static ThreadBuffers* get_thread_buffers ();
static void put_thread_buffers (ThreadBuffers*);
- static void ensure_buffers (ChanCount howmany = ChanCount::ZERO);
+ static void ensure_buffers (ChanCount howmany = ChanCount::ZERO, size_t custom = 0);
private:
static Glib::Threads::Mutex rb_mutex;
LIBARDOUR_API extern uint64_t SnapBBT;
LIBARDOUR_API extern uint64_t Configuration;
LIBARDOUR_API extern uint64_t Latency;
+ LIBARDOUR_API extern uint64_t LatencyCompensation;
LIBARDOUR_API extern uint64_t Peaks;
LIBARDOUR_API extern uint64_t Processors;
LIBARDOUR_API extern uint64_t ProcessThreads;
LIBARDOUR_API extern uint64_t WiimoteControl;
LIBARDOUR_API extern uint64_t Ports;
LIBARDOUR_API extern uint64_t AudioEngine;
+ LIBARDOUR_API extern uint64_t Soundcloud;
}
}
--- /dev/null
+/*
+ Copyright (C) 2006, 2013 Paul Davis
+ Copyright (C) 2013, 2014 Robin Gareus <robin@gareus.org>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the 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_delayline_h__
+#define __ardour_delayline_h__
+
+#include "ardour/types.h"
+#include "ardour/processor.h"
+
+namespace ARDOUR {
+
+class BufferSet;
+class ChanCount;
+class Session;
+
+/** Meters peaks on the input and stores them for access.
+ */
+class LIBARDOUR_API DelayLine : public Processor {
+public:
+
+ DelayLine (Session& s, const std::string& name);
+ ~DelayLine ();
+
+ bool display_to_user() const { return false; }
+
+ void run (BufferSet&, framepos_t, framepos_t, pframes_t, bool);
+ void set_delay(framecnt_t signal_delay);
+ framecnt_t get_delay() { return _pending_delay; }
+
+ bool configure_io (ChanCount in, ChanCount out);
+ bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
+
+ void flush();
+ void realtime_handle_transport_stopped () { flush(); }
+ void realtime_locate () { flush(); }
+ void monitoring_changed() { flush(); }
+
+ XMLNode& state (bool full);
+
+private:
+ friend class IO;
+ framecnt_t _delay, _pending_delay;
+ framecnt_t _bsiz, _pending_bsiz;
+ frameoffset_t _roff, _woff;
+ boost::shared_ptr<Sample> _buf;
+ boost::shared_ptr<Sample> _pending_buf;
+ boost::shared_ptr<MidiBuffer> _midi_buf;
+ bool _pending_flush;
+};
+
+} // namespace ARDOUR
+
+#endif // __ardour_meter_h__
virtual ~Diskstream();
virtual bool set_name (const std::string& str);
+ virtual bool set_write_source_name (const std::string& str);
+
+ std::string write_source_name () const {
+ if (_write_source_name.empty()) {
+ return name();
+ } else {
+ return _write_source_name;
+ }
+ }
virtual std::string steal_write_source_name () { return std::string(); }
bool in_set_state;
+ std::string _write_source_name;
+
Glib::Threads::Mutex state_lock;
PBD::ScopedConnectionList playlist_connections;
void select_with_cue (bool);
void select_with_toc (bool);
+ void select_upload (bool);
+ void set_command (std::string);
void select_src_quality (ExportFormatBase::SRCQuality value);
void select_trim_beginning (bool value);
void select_silence_beginning (AnyTime const & time);
void set_tag (bool tag_it) { _tag = tag_it; }
void set_with_cue (bool yn) { _with_cue = yn; }
void set_with_toc (bool yn) { _with_toc = yn; }
+ void set_soundcloud_upload (bool yn) { _soundcloud_upload = yn; }
+ void set_command (std::string command) { _command = command; }
void set_silence_beginning (AnyTime const & value) { _silence_beginning = value; }
void set_silence_end (AnyTime const & value) { _silence_end = value; }
float normalize_target () const { return _normalize_target; }
bool with_toc() const { return _with_toc; }
bool with_cue() const { return _with_cue; }
+ bool soundcloud_upload() const { return _soundcloud_upload; }
+ std::string command() const { return _command; }
bool tag () const { return _tag && supports_tagging; }
float _normalize_target;
bool _with_toc;
bool _with_cue;
+ bool _soundcloud_upload;
+ std::string _command;
/* serialization helpers */
#include "ardour/session.h"
#include "ardour/libardour_visibility.h"
#include "ardour/types.h"
+#include "pbd/signals.h"
namespace AudioGrapher {
class BroadcastInfo;
Session & session;
};
-class LIBARDOUR_API ExportHandler : public ExportElementFactory
+class LIBARDOUR_API ExportHandler : public ExportElementFactory, public sigc::trackable
{
public:
struct FileSpec {
friend boost::shared_ptr<ExportHandler> Session::get_export_handler();
ExportHandler (Session & session);
+ void command_output(std::string output, size_t size);
+
public:
~ExportHandler ();
std::string get_cd_marker_filename(std::string filename, CDMarkerFormat format);
+ /** signal emitted when soundcloud export reports progress updates during upload.
+ * The parameters are total and current bytes downloaded, and the current filename
+ */
+ PBD::Signal3<void, double, double, std::string> SoundcloudProgress;
+
+ /* upload credentials & preferences */
+ std::string soundcloud_username;
+ std::string soundcloud_password;
+ bool soundcloud_make_public;
+ bool soundcloud_open_page;
+ bool soundcloud_downloadable;
+
private:
void handle_duplicate_format_extensions();
void existence_check ();
virtual void prevent_deletion ();
+ /** Rename the file on disk referenced by this source to \param newname
+ */
+ int rename (const std::string& name);
+
protected:
FileSource (Session& session, DataType type,
const std::string& path,
class LIBARDOUR_API InternalSend : public Send
{
public:
- InternalSend (Session&, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster>, boost::shared_ptr<Route> send_to, Delivery::Role role = Delivery::Aux, bool ignore_bitslot = false);
+ InternalSend (Session&, boost::shared_ptr<Pannable>, boost::shared_ptr<MuteMaster>, boost::shared_ptr<Route> send_from, boost::shared_ptr<Route> send_to, Delivery::Role role = Delivery::Aux, bool ignore_bitslot = false);
virtual ~InternalSend ();
std::string display_name() const;
bool configure_io (ChanCount in, ChanCount out);
int set_block_size (pframes_t);
+ boost::shared_ptr<Route> source_route() const { return _send_from; }
boost::shared_ptr<Route> target_route() const { return _send_to; }
const PBD::ID& target_id() const { return _send_to_id; }
private:
BufferSet mixbufs;
+ boost::shared_ptr<Route> _send_from;
boost::shared_ptr<Route> _send_to;
PBD::ID _send_to_id;
PBD::ScopedConnection connect_c;
bool push_back(const Evoral::MIDIEvent<TimeType>& event);
bool push_back(TimeType time, size_t size, const uint8_t* data);
+
uint8_t* reserve(TimeType time, size_t size);
void resize(size_t);
size_t size() const { return _size; }
bool empty() const { return _size == 0; }
+ bool insert_event(const Evoral::MIDIEvent<TimeType>& event);
bool merge_in_place(const MidiBuffer &other);
template<typename BufferType, typename EventType>
- class iterator_base {
+ class iterator_base
+ {
public:
- iterator_base<BufferType, EventType>(BufferType& b, framecnt_t o)
- : buffer(&b), offset(o) {}
- iterator_base<BufferType, EventType>(const iterator_base<BufferType,EventType>& o)
- : buffer (o.buffer), offset(o.offset) {}
-
- inline iterator_base<BufferType,EventType> operator= (const iterator_base<BufferType,EventType>& o) {
+ iterator_base<BufferType, EventType>(BufferType& b, framecnt_t o)
+ : buffer(&b), offset(o) {}
+
+ iterator_base<BufferType, EventType>(const iterator_base<BufferType,EventType>& o)
+ : buffer (o.buffer), offset(o.offset) {}
+
+ inline iterator_base<BufferType,EventType> operator= (const iterator_base<BufferType,EventType>& o) {
if (&o != this) {
buffer = o.buffer;
offset = o.offset;
*((TimeType*)(buffer->_data + offset)),
event_size, ev_start);
}
+
inline EventType operator*() {
uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType);
int event_size = Evoral::midi_event_size(ev_start);
event_size, ev_start);
}
+ inline TimeType * timeptr() {
+ return ((TimeType*)(buffer->_data + offset));
+ }
+
inline iterator_base<BufferType, EventType>& operator++() {
uint8_t* ev_start = buffer->_data + offset + sizeof(TimeType);
int event_size = Evoral::midi_event_size(ev_start);
offset += sizeof(TimeType) + event_size;
return *this;
}
+
inline bool operator!=(const iterator_base<BufferType, EventType>& other) const {
return (buffer != other.buffer) || (offset != other.offset);
}
+
inline bool operator==(const iterator_base<BufferType, EventType>& other) const {
return (buffer == other.buffer) && (offset == other.offset);
}
+
BufferType* buffer;
size_t offset;
};
const_iterator begin() const { return const_iterator(*this, 0); }
const_iterator end() const { return const_iterator(*this, _size); }
- iterator erase(const iterator& i) {
+ iterator erase(const iterator& i) {
assert (i.buffer == this);
uint8_t* ev_start = _data + i.offset + sizeof (TimeType);
int event_size = Evoral::midi_event_size (ev_start);
size_t total_data_deleted = sizeof(TimeType) + event_size;
- if (i.offset + total_data_deleted >= _size) {
+ if (i.offset + total_data_deleted > _size) {
_size = 0;
return end();
}
* its MIDI status byte.
*/
static bool second_simultaneous_midi_byte_is_first (uint8_t, uint8_t);
-
+
private:
friend class iterator_base< MidiBuffer, Evoral::MIDIEvent<TimeType> >;
friend class iterator_base< const MidiBuffer, const Evoral::MIDIEvent<TimeType> >;
pframes_t _size;
};
-
} // namespace ARDOUR
#endif // __ardour_midi_buffer_h__
int use_copy_playlist ();
bool set_name (std::string const &);
+ bool set_write_source_name (const std::string& str);
/* stateful */
XMLNode& get_state(void);
gint _frames_read_from_ringbuffer;
volatile gint _frames_pending_write;
volatile gint _num_captured_loops;
+ framepos_t _accumulated_capture_offset;
/** A buffer that we use to put newly-arrived MIDI data in for
the GUI to read (so that it can update itself).
framecnt_t end_frame,
boost::shared_ptr<Processor> endpoint,
bool include_endpoint,
- bool for_export);
+ bool for_export,
+ bool for_freeze);
int set_state (const XMLNode&, int version);
--- /dev/null
+/*
+ * Copyright (C) 2013-2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the 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 __libardour_mididm_h__
+#define __libardour_mididm_h__
+
+#include "ardour/types.h"
+#include "ardour/libardour_visibility.h"
+
+namespace ARDOUR {
+
+class PortEngine;
+
+class LIBARDOUR_API MIDIDM
+{
+public:
+
+ MIDIDM (framecnt_t sample_rate);
+
+ int process (pframes_t nframes, PortEngine &pe, void *midi_in, void *midi_out);
+
+ framecnt_t latency (void) { return _cnt_total > 10 ? _avg_delay : 0; }
+ framecnt_t processed (void) { return _cnt_total; }
+ double deviation (void) { return _cnt_total > 1 ? sqrt(_var_s / ((double)(_cnt_total - 1))) : 0; }
+ bool ok (void) { return _cnt_total > 200; }
+ bool have_signal (void) { return (_monotonic_cnt - _last_signal_tme) < (uint64_t) _sample_rate ; }
+
+private:
+ int64_t parse_mclk (uint8_t* buf, pframes_t timestamp) const;
+ int64_t parse_mtc (uint8_t* buf, pframes_t timestamp) const;
+
+ framecnt_t _sample_rate;
+
+ uint64_t _monotonic_cnt;
+ uint64_t _last_signal_tme;
+
+ uint64_t _cnt_total;
+ uint64_t _dly_total;
+ uint32_t _min_delay;
+ uint32_t _max_delay;
+ double _avg_delay;
+ double _var_m;
+ double _var_s;
+
+};
+
+}
+
+#endif /* __libardour_mididm_h__ */
/** details of the match currently being used */
Match _match;
- void automation_run (BufferSet& bufs, pframes_t nframes);
+ void automation_run (BufferSet& bufs, framepos_t start, pframes_t nframes);
void connect_and_run (BufferSet& bufs, pframes_t nframes, framecnt_t offset, bool with_auto, framepos_t now = 0);
void create_automatable_parameters ();
}
};
+/* sort by RegionSortByLayerAndPosition()
+ * is equivalent to
+ * stable_sort by RegionSortByPosition();
+ * stable_sort by RegionSortByLayer();
+ */
+struct LIBARDOUR_API RegionSortByLayerAndPosition {
+ bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
+ return
+ (a->layer() < b->layer() && a->position() < b->position())
+ || (a->layer() == b->layer() && a->position() < b->position());
+ }
+};
+
} // namespace
#endif /* __libardour_region_sorters_h__ */
namespace ARDOUR {
class Amp;
+class DelayLine;
class Delivery;
class IOProcessor;
class Panner;
PeakMeter& peak_meter() { return *_meter.get(); }
const PeakMeter& peak_meter() const { return *_meter.get(); }
boost::shared_ptr<PeakMeter> shared_peak_meter() const { return _meter; }
+ boost::shared_ptr<DelayLine> delay_line() const { return _delayline; }
void flush_processors ();
PBD::Signal1<void,void*> mute_changed;
PBD::Signal0<void> mute_points_changed;
+ /** track numbers - assigned by session
+ * nubers > 0 indicate tracks (audio+midi)
+ * nubers < 0 indicate busses
+ * zero is reserved for unnumbered special busses.
+ * */
+ PBD::Signal0<void> track_number_changed;
+ int64_t track_number() const { return _track_number; }
+
+ void set_track_number(int64_t tn) {
+ if (tn == _track_number) { return; }
+ _track_number = tn;
+ track_number_changed();
+ PropertyChanged (ARDOUR::Properties::name);
+ }
+
/** the processors have changed; the parameter indicates what changed */
PBD::Signal1<void,RouteProcessorChange> processors_changed;
PBD::Signal1<void,void*> record_enable_changed;
pframes_t nframes, int declick,
bool gain_automation_ok);
+ virtual void bounce_process (BufferSet& bufs,
+ framepos_t start_frame, framecnt_t nframes,
+ boost::shared_ptr<Processor> endpoint, bool include_endpoint,
+ bool for_export, bool for_freeze);
+
+ framecnt_t bounce_get_latency (boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze) const;
+ ChanCount bounce_get_output_streams (ChanCount &cc, boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze) const;
+
boost::shared_ptr<IO> _input;
boost::shared_ptr<IO> _output;
bool _active;
framecnt_t _signal_latency;
+ framecnt_t _signal_latency_at_amp_position;
framecnt_t _initial_delay;
framecnt_t _roll_delay;
boost::shared_ptr<Amp> _amp;
boost::shared_ptr<PeakMeter> _meter;
+ boost::shared_ptr<DelayLine> _delayline;
boost::shared_ptr<Processor> the_instrument_unlocked() const;
bool _has_order_key;
uint32_t _remote_control_id;
+ int64_t _track_number;
+
void input_change_handler (IOChange, void *src);
void output_change_handler (IOChange, void *src);
--- /dev/null
+/*
+ Copyright (C) 2000-2014 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 __libardour_route_sorters_h__
+#define __libardour_route_sorters_h__
+
+#include "ardour/route.h"
+
+namespace ARDOUR {
+
+struct SignalOrderRouteSorter {
+ bool operator() (boost::shared_ptr<Route> a, boost::shared_ptr<Route> b) {
+ if (a->is_master() || a->is_monitor()) {
+ /* "a" is a special route (master, monitor, etc), and comes
+ * last in the mixer ordering
+ */
+ return false;
+ } else if (b->is_master() || b->is_monitor()) {
+ /* everything comes before b */
+ return true;
+ }
+ return a->order_key () < b->order_key ();
+ }
+};
+
+} // namespace
+
+#endif /* __libardour_route_sorters_h__ */
#include "ardour/ardour.h"
#include "ardour/delivery.h"
+#include "ardour/delayline.h"
namespace ARDOUR {
bool can_support_io_configuration (const ChanCount& in, ChanCount& out);
bool configure_io (ChanCount in, ChanCount out);
+ /* latency compensation */
+ void set_delay_in (framecnt_t);
+ void set_delay_out (framecnt_t);
+ framecnt_t get_delay_in () const { return _delay_in; }
+ framecnt_t get_delay_out () const { return _delay_out; }
+
void activate ();
void deactivate ();
bool _metering;
boost::shared_ptr<Amp> _amp;
boost::shared_ptr<PeakMeter> _meter;
+ boost::shared_ptr<DelayLine> _delayline;
private:
/* disallow copy construction */
int set_state_2X (XMLNode const &, int);
uint32_t _bitslot;
+
+ framecnt_t _delay_in;
+ framecnt_t _delay_out;
};
} // namespace ARDOUR
std::string peak_path (std::string) const;
std::string peak_path_from_audio_path (std::string) const;
- std::string new_audio_source_name (const std::string&, uint32_t nchans, uint32_t chan, bool destructive);
- std::string new_midi_source_name (const std::string&);
- std::string new_source_path_from_name (DataType type, const std::string&);
+ std::string new_audio_source_path (const std::string&, uint32_t nchans, uint32_t chan, bool destructive, bool take_required);
+ std::string new_midi_source_path (const std::string&);
RouteList new_route_from_template (uint32_t how_many, const std::string& template_path, const std::string& name);
+ std::vector<std::string> get_paths_for_new_sources (bool allow_replacing, const std::string& import_file_path, uint32_t channels);
void process (pframes_t nframes);
bool route_name_unique (std::string) const;
bool route_name_internal (std::string) const;
+ uint32_t track_number_decimals () const {
+ return _track_number_decimals;
+ }
+
bool get_record_enabled() const {
return (record_status () >= Enabled);
}
int rename (const std::string&);
bool get_nsm_state () const { return _under_nsm_control; }
void set_nsm_state (bool state) { _under_nsm_control = state; }
+ bool save_default_options ();
PBD::Signal1<void,std::string> StateSaved;
PBD::Signal0<void> StateReady;
PBD::Signal0<void> SaveSession;
- std::vector<std::string*>* possible_states() const;
- static std::vector<std::string*>* possible_states (std::string path);
+ std::vector<std::string> possible_states() const;
+ static std::vector<std::string> possible_states (std::string path);
XMLNode& get_state();
int set_state(const XMLNode& node, int version); // not idempotent
StateOfTheState state_of_the_state() const { return _state_of_the_state; }
+ class StateProtector {
+ public:
+ StateProtector (Session* s) : _session (s) {
+ g_atomic_int_inc (&s->_suspend_save);
+ }
+ ~StateProtector () {
+ if (g_atomic_int_dec_and_test (&_session->_suspend_save)) {
+ while (_session->_save_queued) {
+ _session->_save_queued = false;
+ _session->save_state ("");
+ }
+ }
+ }
+ private:
+ Session * _session;
+ };
+
void add_route_group (RouteGroup *);
void remove_route_group (RouteGroup&);
void reorder_route_groups (std::list<RouteGroup*>);
boost::shared_ptr<Region> find_whole_file_parent (boost::shared_ptr<Region const>) const;
- std::string path_from_region_name (DataType type, std::string name, std::string identifier);
-
boost::shared_ptr<Region> XMLRegionFactory (const XMLNode&, bool full);
boost::shared_ptr<AudioRegion> XMLAudioRegionFactory (const XMLNode&, bool full);
boost::shared_ptr<MidiRegion> XMLMidiRegionFactory (const XMLNode&, bool full);
boost::shared_ptr<MidiSource> create_midi_source_by_stealing_name (boost::shared_ptr<Track>);
boost::shared_ptr<Source> source_by_id (const PBD::ID&);
- boost::shared_ptr<AudioFileSource> source_by_path_and_channel (const std::string&, uint16_t) const;
- boost::shared_ptr<MidiSource> source_by_path (const std::string&) const;
+ boost::shared_ptr<AudioFileSource> audio_source_by_path_and_channel (const std::string&, uint16_t) const;
+ boost::shared_ptr<MidiSource> midi_source_by_path (const std::string&) const;
uint32_t count_sources_by_origin (const std::string&);
void add_playlist (boost::shared_ptr<Playlist>, bool unused = false);
boost::shared_ptr<Region> write_one_track (AudioTrack&, framepos_t start, framepos_t end,
bool overwrite, std::vector<boost::shared_ptr<Source> >&, InterThreadInfo& wot,
- boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export);
+ boost::shared_ptr<Processor> endpoint,
+ bool include_endpoint, bool for_export, bool for_freeze);
int freeze_all (InterThreadInfo&);
/* session-wide solo/mute/rec-enable */
return _exporting;
}
+ bool bounce_processing() const {
+ return _bounce_processing_active;
+ }
+
/* this is a private enum, but setup_enum_writer() needs it,
and i can't find a way to give that function
friend access. sigh.
mutable gint processing_prohibited;
process_function_type process_function;
process_function_type last_process_function;
+ bool _bounce_processing_active;
bool waiting_for_sync_offset;
framecnt_t _base_frame_rate;
framecnt_t _current_frame_rate; //this includes video pullup offset
void process_without_events (pframes_t);
void process_with_events (pframes_t);
void process_audition (pframes_t);
- int process_export (pframes_t);
+ int process_export (pframes_t);
int process_export_fw (pframes_t);
void block_processing() { g_atomic_int_set (&processing_prohibited, 1); }
void unblock_processing() { g_atomic_int_set (&processing_prohibited, 0); }
bool processing_blocked() const { return g_atomic_int_get (&processing_prohibited); }
+ static const framecnt_t bounce_chunk_size;
+
/* slave tracking */
static const int delta_accumulator_size = 25;
bool state_was_pending;
StateOfTheState _state_of_the_state;
+ friend class StateProtector;
+ gint _suspend_save; /* atomic */
+ volatile bool _save_queued;
+
void auto_save();
int load_options (const XMLNode&);
int load_state (std::string snapshot_name);
ChanCount input_start = ChanCount (), ChanCount output_start = ChanCount ());
void midi_output_change_handler (IOChange change, void* /*src*/, boost::weak_ptr<Route> midi_track);
+ /* track numbering */
+
+ void reassign_track_numbers ();
+ uint32_t _track_number_decimals;
+
/* mixer stuff */
bool solo_update_disabled;
bool no_questions_about_missing_files;
- std::string get_best_session_directory_for_new_source ();
+ std::string get_best_session_directory_for_new_audio ();
mutable gint _playback_load;
mutable gint _capture_load;
static int get_session_info_from_path (XMLTree& state_tree, const std::string& xmlpath);
};
+
} // namespace ARDOUR
#endif /* __ardour_session_h__ */
XMLNode& get_variables ();
void set_variables (XMLNode const &);
+ bool load_state ();
+ bool save_state ();
+
/* define accessor methods */
#undef CONFIG_VARIABLE
the value of the variable.
*****************************************************/
-CONFIG_VARIABLE (CrossfadeChoice, xfade_choice, "xfade-choice", ConstantPowerMinus3dB)
CONFIG_VARIABLE (uint32_t, destructive_xfade_msecs, "destructive-xfade-msecs", 2)
CONFIG_VARIABLE (bool, use_region_fades, "use-region-fades", true)
CONFIG_VARIABLE (bool, show_region_fades, "show-region-fades", true)
CONFIG_VARIABLE_SPECIAL(std::string, raid_path, "raid-path", "", PBD::path_expand)
CONFIG_VARIABLE_SPECIAL(std::string, audio_search_path, "audio-search-path", "", PBD::search_path_expand)
CONFIG_VARIABLE_SPECIAL(std::string, midi_search_path, "midi-search-path", "", PBD::search_path_expand)
+CONFIG_VARIABLE (bool, track_name_number, "track-name-number", false)
+CONFIG_VARIABLE (bool, track_name_take, "track-name-take", false)
+CONFIG_VARIABLE (std::string, take_name, "take-name", "Take1")
CONFIG_VARIABLE (bool, jack_time_master, "jack-time-master", true)
CONFIG_VARIABLE (bool, use_video_sync, "use-video-sync", false)
CONFIG_VARIABLE (float, video_pullup, "video-pullup", 0.0f)
/** Standard Midi File (Type 0) Source */
class LIBARDOUR_API SMFSource : public MidiSource, public FileSource, public Evoral::SMF {
public:
+ /** Constructor for new internal-to-session files */
+ SMFSource (Session& session, const std::string& path, Source::Flag flags);
+
/** Constructor for existing external-to-session files */
- SMFSource (Session& session, const std::string& path,
- Source::Flag flags = Source::Flag(0));
+ SMFSource (Session& session, const std::string& path);
/** Constructor for existing in-session files */
SMFSource (Session& session, const XMLNode&, bool must_exist = false);
virtual ~SMFSource ();
- /** Rename the file on disk referenced by this source to \param newname
- *
- * This method exists only for MIDI file sources, not for audio, which
- * can never be renamed. It exists for MIDI so that we can get
- * consistent and sane region/source numbering when regions are added
- * manually (which never happens with audio).
- */
- int rename (const std::string& name);
-
bool safe_file_extension (const std::string& path) const {
return safe_midi_file_extension(path);
}
void ensure_disk_file ();
static bool safe_midi_file_extension (const std::string& path);
+ static bool valid_midi_file (const std::string& path);
void prevent_deletion ();
SampleFormat samp_format, HeaderFormat hdr_format, framecnt_t rate,
Flag flags = SndFileSource::default_writable_flags);
- /** Constructor to be called for existing in-session files */
+ /* Constructor to be called for recovering files being used for
+ * capture. They are in-session, they already exist, they should not
+ * be writable. They are an odd hybrid (from a constructor point of
+ * view) of the previous two constructors.
+ */
+ SndFileSource (Session&, const std::string& path, int chn);
+
+ /** Constructor to be called for existing in-session files during
+ * session loading
+ */
SndFileSource (Session&, const XMLNode&);
~SndFileSource ();
--- /dev/null
+/* soundcloud_upload.h ******************************************************
+
+ Adapted for Ardour by Ben Loftis, March 2012
+
+*****************************************************************************/
+
+#ifndef __ardour_soundcloud_upload_h__
+#define __ardour_soundcloud_upload_h__
+
+#include <string>
+#include <fstream>
+#include <iostream>
+#include <stdio.h>
+#include <cstring>
+#include <string>
+#include <sstream>
+#include <vector>
+
+#include "curl/curl.h"
+#include "ardour/session_handle.h"
+#include "ardour/export_handler.h"
+#include "pbd/signals.h"
+
+//--- struct to store XML file
+struct MemoryStruct {
+ char *memory;
+ size_t size;
+};
+
+
+class SoundcloudUploader
+{
+public:
+ SoundcloudUploader();
+ ~SoundcloudUploader();
+
+ std::string Get_Auth_Token(std::string username, std::string password);
+ std::string Upload (std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller);
+ static int progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow);
+
+
+private:
+
+ void setcUrlOptions();
+
+ CURL *curl_handle;
+ CURLM *multi_handle;
+ char errorBuffer[CURL_ERROR_SIZE]; // storage for cUrl error message
+
+ std::string title;
+ ARDOUR::ExportHandler *caller;
+
+};
+
+#endif /* __ardour_soundcloud_upload_h__ */
bool destructive, framecnt_t rate, bool announce = true, bool async = false);
+ static boost::shared_ptr<Source> createForRecovery
+ (DataType type, Session&, const std::string& path, int chn);
+
static boost::shared_ptr<Source> createFromPlaylist
(DataType type, Session& s, boost::shared_ptr<Playlist> p, const PBD::ID& orig, const std::string& name,
uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks);
public:
SystemExec (std::string c, std::string a = "");
SystemExec (std::string c, char ** a);
+ SystemExec (std::string c, const std::map<char, std::string> subs);
~SystemExec ();
int start (int stderr_mode = 1) {
ThreadBuffers ();
~ThreadBuffers ();
- void ensure_buffers (ChanCount howmany = ChanCount::ZERO);
+ void ensure_buffers (ChanCount howmany = ChanCount::ZERO, size_t custom = 0);
BufferSet* silent_buffers;
BufferSet* scratch_buffers;
int init ();
bool set_name (const std::string& str);
+ void resync_track_name ();
TrackMode mode () const { return _mode; }
virtual int set_mode (TrackMode /*m*/) { return false; }
virtual boost::shared_ptr<Region> bounce_range (framepos_t start, framepos_t end, InterThreadInfo&,
boost::shared_ptr<Processor> endpoint, bool include_endpoint) = 0;
virtual int export_stuff (BufferSet& bufs, framepos_t start_frame, framecnt_t nframes,
- boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export) = 0;
+ boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze) = 0;
XMLNode& get_state();
XMLNode& get_template();
void diskstream_record_enable_changed ();
void diskstream_speed_changed ();
void diskstream_alignment_style_changed ();
+ void parameter_changed (std::string const & p);
+
+ std::string _diskstream_name;
};
}; /* namespace ARDOUR*/
MixerOrdered
};
- enum CrossfadeModel {
- FullCrossfade,
- ShortCrossfade
- };
-
- enum CrossfadeChoice {
- RegionFades,
- ConstantPowerMinus3dB,
- ConstantPowerMinus6dB,
- };
-
enum ListenPosition {
AfterFaderListen,
PreFaderListen
std::istream& operator>>(std::istream& o, ARDOUR::RemoteModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::ListenPosition& sf);
std::istream& operator>>(std::istream& o, ARDOUR::InsertMergePolicy& sf);
-std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeModel& sf);
-std::istream& operator>>(std::istream& o, ARDOUR::CrossfadeChoice& sf);
std::istream& operator>>(std::istream& o, ARDOUR::SyncSource& sf);
std::istream& operator>>(std::istream& o, ARDOUR::ShuttleBehaviour& sf);
std::istream& operator>>(std::istream& o, ARDOUR::ShuttleUnits& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::RemoteModel& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::ListenPosition& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::InsertMergePolicy& sf);
-std::ostream& operator<<(std::ostream& o, const ARDOUR::CrossfadeModel& sf);
-std::ostream& operator<<(std::ostream& o, const ARDOUR::CrossfadeChoice& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::SyncSource& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::ShuttleBehaviour& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::ShuttleUnits& sf);
class XMLNode;
+namespace ARDOUR {
+
LIBARDOUR_API std::string legalize_for_path (const std::string& str);
LIBARDOUR_API std::string legalize_for_universal_path (const std::string& str);
LIBARDOUR_API std::string legalize_for_uri (const std::string& str);
}
LIBARDOUR_API std::string bump_name_once(const std::string& s, char delimiter);
+LIBARDOUR_API std::string bump_name_number(const std::string& s);
LIBARDOUR_API int cmp_nocase (const std::string& s, const std::string& s2);
LIBARDOUR_API int cmp_nocase_utf8 (const std::string& s1, const std::string& s2);
LIBARDOUR_API std::string CFStringRefToStdString(CFStringRef stringRef);
#endif // __APPLE__
+} //namespave
+
#endif /* __ardour_utils_h__ */
try {
if ((chan->write_source = _session.create_audio_source_for_session (
- n_channels().n_audio(), name(), n, destructive())) == 0) {
+ n_channels().n_audio(), write_source_name(), n, destructive())) == 0) {
throw failed_constructor();
}
}
continue;
}
+ /* XXX as of June 2014, we always record to mono
+ files. Since this Source is being created as part of
+ crash recovery, we know that we need the first
+ channel (the final argument to the SourceFactory
+ call below). If we ever support non-mono files for
+ capture, this will need rethinking.
+ */
+
try {
- fs = boost::dynamic_pointer_cast<AudioFileSource> (
- SourceFactory::createWritable (
- DataType::AUDIO, _session,
- prop->value(), false, _session.frame_rate()));
+ fs = boost::dynamic_pointer_cast<AudioFileSource> (SourceFactory::createForRecovery (DataType::AUDIO, _session, prop->value(), 0));
}
catch (failed_constructor& err) {
return -1;
}
- boost::shared_ptr<AudioRegion> region;
-
try {
- PropertyList plist;
+ boost::shared_ptr<AudioRegion> wf_region;
+ boost::shared_ptr<AudioRegion> region;
+
+ /* First create the whole file region */
+ PropertyList plist;
+
plist.add (Properties::start, 0);
plist.add (Properties::length, first_fs->length (first_fs->timeline_position()));
plist.add (Properties::name, region_name_from_path (first_fs->name(), true));
- region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
+ wf_region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
+
+ wf_region->set_automatic (true);
+ wf_region->set_whole_file (true);
+ wf_region->special_set_position (position);
- region->set_automatic (true);
- region->set_whole_file (true);
- region->special_set_position (0);
+ /* Now create a region that isn't the whole file for adding to
+ * the playlist */
+
+ region = boost::dynamic_pointer_cast<AudioRegion> (RegionFactory::create (pending_sources, plist));
+
+ _playlist->add_region (region, position);
}
catch (failed_constructor& err) {
return -1;
}
- _playlist->add_region (region, position);
return 0;
}
bool
AudioDiskstream::set_name (string const & name)
{
+ if (_name == name) {
+ return true;
+ }
Diskstream::set_name (name);
/* get a new write source so that its name reflects the new diskstream name */
return true;
}
+
+bool
+AudioDiskstream::set_write_source_name (const std::string& str) {
+ if (_write_source_name == str) {
+ return true;
+ }
+
+ Diskstream::set_write_source_name (str);
+
+ if (_write_source_name == name()) {
+ return true;
+ }
+ boost::shared_ptr<ChannelList> c = channels.reader();
+ ChannelList::iterator i;
+ int n = 0;
+
+ for (n = 0, i = c->begin(); i != c->end(); ++i, ++n) {
+ use_new_write_source (n);
+ }
+ return true;
+}
process_output_buffers (bufs, start_frame, end_frame, nframes, declick, (!diskstream->record_enabled() && _session.transport_rolling()));
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ boost::shared_ptr<Delivery> d = boost::dynamic_pointer_cast<Delivery> (*i);
+ if (d) {
+ d->flush_buffers (nframes);
+ }
+ }
+
need_butler = diskstream->commit (playback_distance);
return 0;
int
AudioTrack::export_stuff (BufferSet& buffers, framepos_t start, framecnt_t nframes,
- boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export)
+ boost::shared_ptr<Processor> endpoint, bool include_endpoint, bool for_export, bool for_freeze)
{
boost::scoped_array<gain_t> gain_buffer (new gain_t[nframes]);
boost::scoped_array<Sample> mix_buffer (new Sample[nframes]);
}
}
- // If no processing is required, there's no need to go any further.
-
- if (!endpoint && !include_endpoint) {
- return 0;
- }
-
- for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
-
- if (!include_endpoint && (*i) == endpoint) {
- break;
- }
-
- /* if we're not exporting, stop processing if we come across a routing processor.
- */
-
- if (!for_export && (*i)->does_routing()) {
- break;
- }
-
- /* even for export, don't run any processor that does routing.
-
- oh, and don't bother with the peak meter either.
- */
-
- if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
- (*i)->run (buffers, start, start+nframes, nframes, true);
- }
-
- if ((*i) == endpoint) {
- break;
- }
- }
+ bounce_process (buffers, start, nframes, endpoint, include_endpoint, for_export, for_freeze);
return 0;
}
boost::shared_ptr<Processor> endpoint, bool include_endpoint)
{
vector<boost::shared_ptr<Source> > srcs;
- return _session.write_one_track (*this, start, end, false, srcs, itt, endpoint, include_endpoint, false);
+ return _session.write_one_track (*this, start, end, false, srcs, itt, endpoint, include_endpoint, false, false);
}
void
boost::shared_ptr<Region> res;
- if ((res = _session.write_one_track (*this, _session.current_start_frame(), _session.current_end_frame(), true, srcs, itt,
- main_outs(), false, false)) == 0) {
+ if ((res = _session.write_one_track (*this, _session.current_start_frame(), _session.current_end_frame(),
+ true, srcs, itt, main_outs(), false, false, true)) == 0) {
return;
}
for (ProcessorList::iterator r = _processors.begin(); r != _processors.end(); ++r) {
- if (!(*r)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*r)) {
+ if ((*r)->does_routing() && (*r)->active()) {
+ break;
+ }
+ if (!boost::dynamic_pointer_cast<PeakMeter>(*r)) {
FreezeRecordProcessorInfo* frii = new FreezeRecordProcessorInfo ((*r)->get_state(), (*r));
_freeze_record.processor_info.push_back (frii);
- /* now deactivate the processor */
-
- (*r)->deactivate ();
+ /* now deactivate the processor, */
+ if (!boost::dynamic_pointer_cast<Amp>(*r)) {
+ (*r)->deactivate ();
+ }
}
_session.set_dirty ();
#include "pbd/xml++.h"
#include "pbd/convert.h"
#include "pbd/whitespace.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
#include "pbd/locale_guard.h"
#include <glibmm/threads.h>
void
AUPlugin::find_presets ()
{
- vector<string*>* preset_files;
- PathScanner scanner;
+ vector<string> preset_files;
user_preset_map.clear ();
- preset_files = scanner (preset_search_path, au_preset_filter, this, true, true, -1, true);
+ find_files_matching_filter (preset_files, preset_search_path, au_preset_filter, this, true, true, true);
- if (!preset_files) {
+ if (preset_files.empty()) {
return;
}
- for (vector<string*>::iterator x = preset_files->begin(); x != preset_files->end(); ++x) {
+ for (vector<string>::iterator x = preset_files.begin(); x != preset_files.end(); ++x) {
- string path = *(*x);
+ string path = *x;
string preset_name;
/* make an initial guess at the preset name using the path */
user_preset_map[preset_name] = path;
}
- delete *x;
}
- delete preset_files;
-
/* now fill the vector<string> with the names we have */
for (UserPresetMap::iterator i = user_preset_map.begin(); i != user_preset_map.end(); ++i) {
#include "ardour/meter.h"
#include "ardour/midi_port.h"
#include "ardour/midiport_manager.h"
+#include "ardour/mididm.h"
#include "ardour/mtdm.h"
#include "ardour/port.h"
#include "ardour/process_thread.h"
, m_meter_thread (0)
, _main_thread (0)
, _mtdm (0)
- , _measuring_latency (false)
+ , _mididm (0)
+ , _measuring_latency (MeasureNone)
, _latency_input_port (0)
, _latency_output_port (0)
, _latency_flush_frames (0)
bool return_after_remove_check = false;
- if (_measuring_latency && _mtdm) {
+ if (_measuring_latency == MeasureAudio && _mtdm) {
/* run a normal cycle from the perspective of the PortManager
so that we get silence on all registered ports.
PortManager::cycle_end (nframes);
return_after_remove_check = true;
+ } else if (_measuring_latency == MeasureMIDI && _mididm) {
+ /* run a normal cycle from the perspective of the PortManager
+ so that we get silence on all registered ports.
+
+ we overwrite the silence on the two ports used for latency
+ measurement.
+ */
+
+ PortManager::cycle_start (nframes);
+ PortManager::silence (nframes);
+
+ if (_latency_input_port && _latency_output_port) {
+ PortEngine& pe (port_engine());
+
+ _mididm->process (nframes, pe,
+ pe.get_buffer (_latency_input_port, nframes),
+ pe.get_buffer (_latency_output_port, nframes));
+ }
+
+ PortManager::cycle_end (nframes);
+ return_after_remove_check = true;
+
} else if (_latency_flush_frames) {
/* wait for the appropriate duration for the MTDM signal to
#else
Glib::PatternSpec dll_extension_pattern("*backend.dll");
#endif
-
- find_matching_files_in_search_path (backend_search_path (),
- so_extension_pattern, backend_modules);
- find_matching_files_in_search_path (backend_search_path (),
- dylib_extension_pattern, backend_modules);
+ find_files_matching_pattern (backend_modules, backend_search_path (),
+ so_extension_pattern);
- find_matching_files_in_search_path (backend_search_path (),
- dll_extension_pattern, backend_modules);
+ find_files_matching_pattern (backend_modules, backend_search_path (),
+ dylib_extension_pattern);
+
+ find_files_matching_pattern (backend_modules, backend_search_path (),
+ dll_extension_pattern);
DEBUG_TRACE (DEBUG::AudioEngine, string_compose ("looking for backends in %1\n", backend_search_path().to_string()));
{
if (_backend) {
_backend->stop ();
- _backend->drop_device();
+ _backend->drop_device();
_backend.reset ();
+ _running = false;
}
}
_running = false;
_processed_frames = 0;
- _measuring_latency = false;
+ _measuring_latency = MeasureNone;
_latency_output_port = 0;
_latency_input_port = 0;
_started_for_latency = false;
return true;
}
-MTDM*
-AudioEngine::mtdm()
-{
- return _mtdm;
-}
-
int
AudioEngine::prepare_for_latency_measurement ()
{
}
int
-AudioEngine::start_latency_detection ()
+AudioEngine::start_latency_detection (bool for_midi)
{
if (!running()) {
if (prepare_for_latency_measurement ()) {
delete _mtdm;
_mtdm = 0;
+ delete _mididm;
+ _mididm = 0;
+
/* find the ports we will connect to */
PortEngine::PortHandle out = pe.get_port_by_name (_latency_output_name);
}
/* create the ports we will use to read/write data */
-
- if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) {
- stop (true);
- return -1;
- }
- if (pe.connect (_latency_output_port, _latency_output_name)) {
- pe.unregister_port (_latency_output_port);
- stop (true);
- return -1;
- }
+ if (for_midi) {
+ if ((_latency_output_port = pe.register_port ("latency_out", DataType::MIDI, IsOutput)) == 0) {
+ stop (true);
+ return -1;
+ }
+ if (pe.connect (_latency_output_port, _latency_output_name)) {
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+
+ const string portname ("latency_in");
+ if ((_latency_input_port = pe.register_port (portname, DataType::MIDI, IsInput)) == 0) {
+ pe.unregister_port (_latency_input_port);
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+ if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
+ pe.unregister_port (_latency_input_port);
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+
+ _mididm = new MIDIDM (sample_rate());
+
+ } else {
+
+ if ((_latency_output_port = pe.register_port ("latency_out", DataType::AUDIO, IsOutput)) == 0) {
+ stop (true);
+ return -1;
+ }
+ if (pe.connect (_latency_output_port, _latency_output_name)) {
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+
+ const string portname ("latency_in");
+ if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) {
+ pe.unregister_port (_latency_input_port);
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+ if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
+ pe.unregister_port (_latency_input_port);
+ pe.unregister_port (_latency_output_port);
+ stop (true);
+ return -1;
+ }
+
+ _mtdm = new MTDM (sample_rate());
- const string portname ("latency_in");
- if ((_latency_input_port = pe.register_port (portname, DataType::AUDIO, IsInput)) == 0) {
- pe.unregister_port (_latency_output_port);
- stop (true);
- return -1;
- }
- if (pe.connect (_latency_input_name, make_port_name_non_relative (portname))) {
- pe.unregister_port (_latency_output_port);
- stop (true);
- return -1;
}
LatencyRange lr;
_latency_signal_latency += lr.max;
/* all created and connected, lets go */
-
- _mtdm = new MTDM (sample_rate());
- _measuring_latency = true;
- _latency_flush_frames = samples_per_cycle();
+ _latency_flush_frames = samples_per_cycle();
+ _measuring_latency = for_midi ? MeasureMIDI : MeasureAudio;
return 0;
}
void
AudioEngine::stop_latency_detection ()
{
- _measuring_latency = false;
+ _measuring_latency = MeasureNone;
if (_latency_output_port) {
port_engine().unregister_port (_latency_output_port);
#include "pbd/stl_delete.h"
#include "pbd/strsplit.h"
#include "pbd/shortpath.h"
+#include "pbd/stacktrace.h"
#include "pbd/enumwriter.h"
#include <sndfile.h>
}
}
+/** Constructor used for existing internal-to-session files during crash
+ * recovery. File must exist
+ */
+AudioFileSource::AudioFileSource (Session& s, const string& path, Source::Flag flags, bool /* ignored-exists-for-prototype differentiation */)
+ : Source (s, DataType::AUDIO, path, flags)
+ , AudioSource (s, path)
+ , FileSource (s, DataType::AUDIO, path, string(), flags)
+{
+ /* note that origin remains empty */
+
+ if (init (_path, true)) {
+ throw failed_constructor ();
+ }
+}
+
+
/** Constructor used for existing internal-to-session files via XML. File must exist. */
AudioFileSource::AudioFileSource (Session& s, const XMLNode& node, bool must_exist)
: Source (s, node)
/* see if some part of this read is within the fade out */
/* ................. >| REGION
- _length
-
- { } FADE
- fade_out_length
- ^
- _length - fade_out_length
- |--------------|
- ^internal_offset
- ^internal_offset + to_read
-
- we need the intersection of [internal_offset,internal_offset+to_read] with
- [_length - fade_out_length, _length]
-
- */
-
+ * _length
+ *
+ * { } FADE
+ * fade_out_length
+ * ^
+ * _length - fade_out_length
+ *
+ * |--------------|
+ * ^internal_offset
+ * ^internal_offset + to_read
+ *
+ * we need the intersection of [internal_offset,internal_offset+to_read] with
+ * [_length - fade_out_length, _length]
+ *
+ */
fade_interval_start = max (internal_offset, _length - framecnt_t (_fade_out->back()->when));
framecnt_t fade_interval_end = min(internal_offset + to_read, _length.val());
boost::shared_ptr<Evoral::ControlList> c2 (new Evoral::ControlList (FadeInAutomation));
boost::shared_ptr<Evoral::ControlList> c3 (new Evoral::ControlList (FadeInAutomation));
- const int num_steps = min ((framecnt_t) 16, len);
-
_fade_in->freeze ();
_fade_in->clear ();
_inverse_fade_in->clear ();
+ const int num_steps = 32;
+
switch (shape) {
case FadeLinear:
- _fade_in->fast_simple_add (0.0, 0.0);
+ _fade_in->fast_simple_add (0.0, VERY_SMALL_SIGNAL);
_fade_in->fast_simple_add (len, 1.0);
reverse_curve (_inverse_fade_in.val(), _fade_in.val());
break;
break;
case FadeSlow:
- generate_db_fade (c1, len, num_steps/2, -1); // start off with a slow fade
- generate_db_fade (c2, len, num_steps/2, -80); // end with a fast fade
+ generate_db_fade (c1, len, num_steps, -1); // start off with a slow fade
+ generate_db_fade (c2, len, num_steps, -80); // end with a fast fade
merge_curves (_fade_in.val(), c1, c2);
reverse_curve (c3, _fade_in.val());
_fade_in->copy_events (*c3);
break;
case FadeConstantPower:
- for (int i = 0; i < num_steps; ++i) {
- float dist = (float) i / (num_steps+1.0);
- _fade_in->fast_simple_add (len*dist, sin (dist*M_PI/2));
+ _fade_in->fast_simple_add (0.0, VERY_SMALL_SIGNAL);
+ for (int i = 1; i < num_steps; ++i) {
+ const float dist = i / (num_steps + 1.f);
+ _fade_in->fast_simple_add (len * dist, sin (dist * M_PI / 2.0));
}
_fade_in->fast_simple_add (len, 1.0);
reverse_curve (_inverse_fade_in.val(), _fade_in.val());
case FadeSymmetric:
//start with a nearly linear cuve
_fade_in->fast_simple_add (0, 1);
- _fade_in->fast_simple_add (0.5*len, 0.6);
+ _fade_in->fast_simple_add (0.5 * len, 0.6);
//now generate a fade-out curve by successively applying a gain drop
- const float breakpoint = 0.7; //linear for first 70%
- for (int i = 2; i < num_steps; i++) {
- float coeff = (1.0-breakpoint);
- for (int j = 0; j < i; j++) {
- coeff *= 0.5; //6dB drop per step
- }
- _fade_in->fast_simple_add (len* (breakpoint+((1.0-breakpoint)*(double)i/(double)num_steps)), coeff);
+ const double breakpoint = 0.7; //linear for first 70%
+ for (int i = 2; i < 9; ++i) {
+ const float coeff = (1.f - breakpoint) * powf (0.5, i);
+ _fade_in->fast_simple_add (len * (breakpoint + ((1.0 - breakpoint) * (double)i / 9.0)), coeff);
}
_fade_in->fast_simple_add (len, VERY_SMALL_SIGNAL);
reverse_curve (c3, _fade_in.val());
break;
}
+ _fade_in->set_interpolation(Evoral::ControlList::Curved);
+ _inverse_fade_in->set_interpolation(Evoral::ControlList::Curved);
+
_default_fade_in = false;
_fade_in->thaw ();
send_change (PropertyChange (Properties::fade_in));
_fade_out->clear ();
_inverse_fade_out->clear ();
+ const int num_steps = 32;
+
switch (shape) {
case FadeLinear:
_fade_out->fast_simple_add (0.0, 1.0);
break;
case FadeFast:
- generate_db_fade (_fade_out.val(), len, 10, -60);
+ generate_db_fade (_fade_out.val(), len, num_steps, -60);
generate_inverse_power_curve (_inverse_fade_out.val(), _fade_out.val());
break;
case FadeSlow:
- generate_db_fade (c1, len, 10, -1); //start off with a slow fade
- generate_db_fade (c2, len, 10, -80); //end with a fast fade
+ generate_db_fade (c1, len, num_steps, -1); //start off with a slow fade
+ generate_db_fade (c2, len, num_steps, -80); //end with a fast fade
merge_curves (_fade_out.val(), c1, c2);
generate_inverse_power_curve (_inverse_fade_out.val(), _fade_out.val());
break;
//constant-power fades use a sin/cos relationship
//the cutoff is abrupt but it has the benefit of being symmetrical
_fade_out->fast_simple_add (0.0, 1.0);
- for (int i = 1; i < 9; i++ ) {
- float dist = (float)i/10.0;
- _fade_out->fast_simple_add ((len * dist), cos(dist*M_PI/2));
+ for (int i = 1; i < num_steps; ++i) {
+ const float dist = i / (num_steps + 1.f);
+ _fade_out->fast_simple_add (len * dist, cos (dist * M_PI / 2.0));
}
_fade_out->fast_simple_add (len, VERY_SMALL_SIGNAL);
reverse_curve (_inverse_fade_out.val(), _fade_out.val());
case FadeSymmetric:
//start with a nearly linear cuve
_fade_out->fast_simple_add (0, 1);
- _fade_out->fast_simple_add (0.5*len, 0.6);
-
+ _fade_out->fast_simple_add (0.5 * len, 0.6);
//now generate a fade-out curve by successively applying a gain drop
- const float breakpoint = 0.7; //linear for first 70%
- const int num_steps = 9;
- for (int i = 2; i < num_steps; i++) {
- float coeff = (1.0-breakpoint);
- for (int j = 0; j < i; j++) {
- coeff *= 0.5; //6dB drop per step
- }
- _fade_out->fast_simple_add (len* (breakpoint+((1.0-breakpoint)*(double)i/(double)num_steps)), coeff);
+ const double breakpoint = 0.7; //linear for first 70%
+ for (int i = 2; i < 9; ++i) {
+ const float coeff = (1.f - breakpoint) * powf (0.5, i);
+ _fade_out->fast_simple_add (len * (breakpoint + ((1.0 - breakpoint) * (double)i / 9.0)), coeff);
}
_fade_out->fast_simple_add (len, VERY_SMALL_SIGNAL);
reverse_curve (_inverse_fade_out.val(), _fade_out.val());
break;
}
+ _fade_out->set_interpolation(Evoral::ControlList::Curved);
+ _inverse_fade_out->set_interpolation(Evoral::ControlList::Curved);
+
_default_fade_out = false;
_fade_out->thaw ();
send_change (PropertyChange (Properties::fade_out));
fast_simple_add (x, y);
}
- thin ();
-
if (!ok) {
clear ();
error << _("automation list: cannot load coordinates from XML, all points ignored") << endmsg;
fast_simple_add (x, y);
}
- thin ();
-
- thaw ();
+ thaw ();
return 0;
}
}
void
-BufferManager::ensure_buffers (ChanCount howmany)
+BufferManager::ensure_buffers (ChanCount howmany, size_t custom)
{
/* this is protected by the audioengine's process lock: we do not */
for (ThreadBufferList::iterator i = thread_buffers_list->begin(); i != thread_buffers_list->end(); ++i) {
- (*i)->ensure_buffers (howmany);
+ (*i)->ensure_buffers (howmany, custom);
}
}
Glib::PatternSpec so_extension_pattern("*.so");
Glib::PatternSpec dylib_extension_pattern("*.dylib");
- find_matching_files_in_search_path (control_protocol_search_path (),
- dll_extension_pattern, cp_modules);
+ find_files_matching_pattern (cp_modules, control_protocol_search_path (),
+ dll_extension_pattern);
- find_matching_files_in_search_path (control_protocol_search_path (),
- so_extension_pattern, cp_modules);
+ find_files_matching_pattern (cp_modules, control_protocol_search_path (),
+ so_extension_pattern);
- find_matching_files_in_search_path (control_protocol_search_path (),
- dylib_extension_pattern, cp_modules);
+ find_files_matching_pattern (cp_modules, control_protocol_search_path (),
+ dylib_extension_pattern);
DEBUG_TRACE (DEBUG::ControlProtocols,
string_compose (_("looking for control protocols in %1\n"), control_protocol_search_path().to_string()));
uint64_t PBD::DEBUG::SnapBBT = PBD::new_debug_bit ("snapbbt");
uint64_t PBD::DEBUG::Configuration = PBD::new_debug_bit ("configuration");
uint64_t PBD::DEBUG::Latency = PBD::new_debug_bit ("latency");
+uint64_t PBD::DEBUG::LatencyCompensation = PBD::new_debug_bit ("latencycompensation");
uint64_t PBD::DEBUG::Peaks = PBD::new_debug_bit ("peaks");
uint64_t PBD::DEBUG::Processors = PBD::new_debug_bit ("processors");
uint64_t PBD::DEBUG::ProcessThreads = PBD::new_debug_bit ("processthreads");
uint64_t PBD::DEBUG::WiimoteControl = PBD::new_debug_bit ("wiimotecontrol");
uint64_t PBD::DEBUG::Ports = PBD::new_debug_bit ("Ports");
uint64_t PBD::DEBUG::AudioEngine = PBD::new_debug_bit ("AudioEngine");
+uint64_t PBD::DEBUG::Soundcloud = PBD::new_debug_bit ("Soundcloud");
--- /dev/null
+/*
+ Copyright (C) 2006, 2013 Paul Davis
+ Copyright (C) 2013, 2014 Robin Gareus <robin@gareus.org>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by the Free
+ Software Foundation; either version 2 of the License, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ for more details.
+
+ You should have received a copy of the 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 <assert.h>
+#include <cmath>
+
+#include "pbd/compose.h"
+
+#include "ardour/debug.h"
+#include "ardour/audio_buffer.h"
+#include "ardour/midi_buffer.h"
+#include "ardour/buffer_set.h"
+#include "ardour/delayline.h"
+
+using namespace std;
+using namespace PBD;
+using namespace ARDOUR;
+
+DelayLine::DelayLine (Session& s, const std::string& name)
+ : Processor (s, string_compose ("latency-compensation-%1", name))
+ , _delay(0)
+ , _pending_delay(0)
+ , _bsiz(0)
+ , _pending_bsiz(0)
+ , _roff(0)
+ , _woff(0)
+ , _pending_flush(false)
+{
+}
+
+DelayLine::~DelayLine ()
+{
+}
+
+#define FADE_LEN (16)
+void
+DelayLine::run (BufferSet& bufs, framepos_t /* start_frame */, framepos_t /* end_frame */, pframes_t nsamples, bool)
+{
+ const uint32_t chn = _configured_output.n_audio();
+ pframes_t p0 = 0;
+ uint32_t c;
+
+ const frameoffset_t pending_delay = _pending_delay;
+ const frameoffset_t delay_diff = _delay - pending_delay;
+ const bool pending_flush = _pending_flush;
+ _pending_flush = false;
+
+ /* run() and set_delay() may be called in parallel by
+ * different threads.
+ * if a larger buffer is needed, it is allocated in
+ * set_delay(), here it is just swap'ed in place
+ */
+ if (_pending_bsiz) {
+ assert(_pending_bsiz >= _bsiz);
+
+ const size_t boff = _pending_bsiz - _bsiz;
+ if (_bsiz > 0) {
+ /* write offset is retained. copy existing data to new buffer */
+ frameoffset_t wl = _bsiz - _woff;
+ memcpy(_pending_buf.get(), _buf.get(), sizeof(Sample) * _woff * chn);
+ memcpy(_pending_buf.get() + (_pending_bsiz - wl) * chn, _buf.get() + _woff * chn, sizeof(Sample) * wl * chn);
+
+ /* new buffer is all zero by default, fade into the existing data copied above */
+ frameoffset_t wo = _pending_bsiz - wl;
+ for (pframes_t pos = 0; pos < FADE_LEN; ++pos) {
+ const gain_t gain = (gain_t)pos / (gain_t)FADE_LEN;
+ for (c = 0; c < _configured_input.n_audio(); ++c) {
+ _pending_buf.get()[ wo * chn + c ] *= gain;
+ wo = (wo + 1) % (_pending_bsiz + 1);
+ }
+ }
+
+ /* read-pointer will be moved and may up anywhere..
+ * copy current data for smooth fade-out below
+ */
+ frameoffset_t roold = _roff;
+ frameoffset_t ro = _roff;
+ if (ro > _woff) {
+ ro += boff;
+ }
+ ro += delay_diff;
+ if (ro < 0) {
+ ro -= (_pending_bsiz +1) * floor(ro / (float)(_pending_bsiz +1));
+ }
+ ro = ro % (_pending_bsiz + 1);
+ for (pframes_t pos = 0; pos < FADE_LEN; ++pos) {
+ for (c = 0; c < _configured_input.n_audio(); ++c) {
+ _pending_buf.get()[ ro * chn + c ] = _buf.get()[ roold * chn + c ];
+ ro = (ro + 1) % (_pending_bsiz + 1);
+ roold = (roold + 1) % (_bsiz + 1);
+ }
+ }
+ }
+
+ if (_roff > _woff) {
+ _roff += boff;
+ }
+
+ _buf = _pending_buf;
+ _bsiz = _pending_bsiz;
+ _pending_bsiz = 0;
+ _pending_buf.reset();
+ }
+
+ /* there may be no buffer when delay == 0.
+ * we also need to check audio-channels in case all audio-channels
+ * were removed in which case no new buffer was allocated. */
+ Sample *buf = _buf.get();
+ if (buf && _configured_output.n_audio() > 0) {
+
+ assert (_bsiz >= pending_delay);
+ const framecnt_t rbs = _bsiz + 1;
+
+ if (pending_delay != _delay || pending_flush) {
+ const pframes_t fade_len = (nsamples >= FADE_LEN) ? FADE_LEN : nsamples / 2;
+
+ DEBUG_TRACE (DEBUG::LatencyCompensation,
+ string_compose ("Old %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n",
+ name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff));
+
+ // fade out at old position
+ c = 0;
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++c) {
+ Sample * const data = i->data();
+ for (pframes_t pos = 0; pos < fade_len; ++pos) {
+ const gain_t gain = (gain_t)(fade_len - pos) / (gain_t)fade_len;
+ buf[ _woff * chn + c ] = data[ pos ];
+ data[ pos ] = buf[ _roff * chn + c ] * gain;
+ _roff = (_roff + 1) % rbs;
+ _woff = (_woff + 1) % rbs;
+ }
+ }
+
+ if (pending_flush) {
+ DEBUG_TRACE (DEBUG::LatencyCompensation,
+ string_compose ("Flush buffer: %1\n", name()));
+ memset(buf, 0, _configured_output.n_audio() * rbs * sizeof (Sample));
+ }
+
+ // adjust read pointer
+ _roff += _delay - pending_delay;
+
+ if (_roff < 0) {
+ _roff -= rbs * floor(_roff / (float)rbs);
+ }
+ _roff = _roff % rbs;
+
+ // fade in at new position
+ c = 0;
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++c) {
+ Sample * const data = i->data();
+ for (pframes_t pos = fade_len; pos < 2 * fade_len; ++pos) {
+ const gain_t gain = (gain_t)(pos - fade_len) / (gain_t)fade_len;
+ buf[ _woff * chn + c ] = data[ pos ];
+ data[ pos ] = buf[ _roff * chn + c ] * gain;
+ _roff = (_roff + 1) % rbs;
+ _woff = (_woff + 1) % rbs;
+ }
+ }
+ p0 = 2 * fade_len;
+
+ _delay = pending_delay;
+
+ DEBUG_TRACE (DEBUG::LatencyCompensation,
+ string_compose ("New %1 delay: %2 bufsiz: %3 offset-diff: %4 write-offset: %5 read-offset: %6\n",
+ name(), _delay, _bsiz, ((_woff - _roff + rbs) % rbs), _woff, _roff));
+ }
+
+ assert(_delay == ((_woff - _roff + rbs) % rbs));
+
+ c = 0;
+ for (BufferSet::audio_iterator i = bufs.audio_begin(); i != bufs.audio_end(); ++i, ++c) {
+ Sample * const data = i->data();
+ for (pframes_t pos = p0; pos < nsamples; ++pos) {
+ buf[ _woff * chn + c ] = data[ pos ];
+ data[ pos ] = buf[ _roff * chn + c ];
+ _roff = (_roff + 1) % rbs;
+ _woff = (_woff + 1) % rbs;
+ }
+ }
+ }
+
+ if (_midi_buf.get()) {
+ _delay = pending_delay;
+
+ for (BufferSet::midi_iterator i = bufs.midi_begin(); i != bufs.midi_end(); ++i) {
+ if (i != bufs.midi_begin()) { break; } // XXX only one buffer for now
+
+ MidiBuffer* dly = _midi_buf.get();
+ MidiBuffer& mb (*i);
+ if (pending_flush) {
+ dly->silence(nsamples);
+ }
+
+ // If the delay time changes, iterate over all events in the dly-buffer
+ // and adjust the time in-place. <= 0 becomes 0.
+ //
+ // iterate over all events in dly-buffer and subtract one cycle
+ // (nsamples) from the timestamp, bringing them closer to de-queue.
+ for (MidiBuffer::iterator m = dly->begin(); m != dly->end(); ++m) {
+ MidiBuffer::TimeType *t = m.timeptr();
+ if (*t > nsamples + delay_diff) {
+ *t -= nsamples + delay_diff;
+ } else {
+ *t = 0;
+ }
+ }
+
+ if (_delay != 0) {
+ // delay events in current-buffer, in place.
+ for (MidiBuffer::iterator m = mb.begin(); m != mb.end(); ++m) {
+ MidiBuffer::TimeType *t = m.timeptr();
+ *t += _delay;
+ }
+ }
+
+ // move events from dly-buffer into current-buffer until nsamples
+ // and remove them from the dly-buffer
+ for (MidiBuffer::iterator m = dly->begin(); m != dly->end();) {
+ const Evoral::MIDIEvent<MidiBuffer::TimeType> ev (*m, false);
+ if (ev.time() >= nsamples) {
+ break;
+ }
+ mb.insert_event(ev);
+ m = dly->erase(m);
+ }
+
+ /* For now, this is only relevant if there is there's a positive delay.
+ * In the future this could also be used to delay 'too early' events
+ * (ie '_global_port_buffer_offset + _port_buffer_offset' - midi_port.cc)
+ */
+ if (_delay != 0) {
+ // move events after nsamples from current-buffer into dly-buffer
+ // and trim current-buffer after nsamples
+ for (MidiBuffer::iterator m = mb.begin(); m != mb.end();) {
+ const Evoral::MIDIEvent<MidiBuffer::TimeType> ev (*m, false);
+ if (ev.time() < nsamples) {
+ ++m;
+ continue;
+ }
+ dly->insert_event(ev);
+ m = mb.erase(m);
+ }
+ }
+ }
+ }
+
+ _delay = pending_delay;
+}
+
+void
+DelayLine::set_delay(framecnt_t signal_delay)
+{
+ if (signal_delay < 0) {
+ signal_delay = 0;
+ cerr << "WARNING: latency compensation is not possible.\n";
+ }
+
+ const framecnt_t rbs = signal_delay + 1;
+
+ DEBUG_TRACE (DEBUG::LatencyCompensation,
+ string_compose ("%1 set_delay to %2 samples for %3 channels\n",
+ name(), signal_delay, _configured_output.n_audio()));
+
+ if (signal_delay <= _bsiz) {
+ _pending_delay = signal_delay;
+ return;
+ }
+
+ if (_pending_bsiz) {
+ if (_pending_bsiz < signal_delay) {
+ cerr << "LatComp: buffer resize in progress. "<< name() << "pending: "<< _pending_bsiz <<" want: " << signal_delay <<"\n"; // XXX
+ } else {
+ _pending_delay = signal_delay;
+ }
+ return;
+ }
+
+ if (_configured_output.n_audio() > 0 ) {
+ _pending_buf.reset(new Sample[_configured_output.n_audio() * rbs]);
+ memset(_pending_buf.get(), 0, _configured_output.n_audio() * rbs * sizeof (Sample));
+ _pending_bsiz = signal_delay;
+ } else {
+ _pending_buf.reset();
+ _pending_bsiz = 0;
+ }
+
+ _pending_delay = signal_delay;
+
+ DEBUG_TRACE (DEBUG::LatencyCompensation,
+ string_compose ("allocated buffer for %1 of size %2\n",
+ name(), signal_delay));
+}
+
+bool
+DelayLine::can_support_io_configuration (const ChanCount& in, ChanCount& out)
+{
+ out = in;
+ return true;
+}
+
+bool
+DelayLine::configure_io (ChanCount in, ChanCount out)
+{
+ if (out != in) { // always 1:1
+ return false;
+ }
+
+ // TODO realloc buffers if channel count changes..
+ // TODO support multiple midi buffers
+
+ DEBUG_TRACE (DEBUG::LatencyCompensation,
+ string_compose ("configure IO: %1 Ain: %2 Aout: %3 Min: %4 Mout: %5\n",
+ name(), in.n_audio(), out.n_audio(), in.n_midi(), out.n_midi()));
+
+ if (in.n_midi() > 0 && !_midi_buf) {
+ _midi_buf.reset(new MidiBuffer(16384));
+ }
+
+ return Processor::configure_io (in, out);
+}
+
+void
+DelayLine::flush()
+{
+ _pending_flush = true;
+}
+
+XMLNode&
+DelayLine::state (bool full_state)
+{
+ XMLNode& node (Processor::state (full_state));
+ node.add_property("type", "delay");
+ return node;
+}
processing pathway that wants to use this->output_buffers() for some reason.
*/
+ // TODO delayline -- latency-compensation
output_buffers().get_backend_port_addresses (ports, nframes);
// this Delivery processor is not a derived type, and thus we assume
{
bool ret = IOProcessor::set_name (name);
- if (ret) {
+ if (ret && _panshell) {
ret = _panshell->set_name (name);
}
if ((a != _alignment_choice) || force) {
_alignment_choice = a;
- switch (_alignment_choice) {
- case Automatic:
- set_align_style_from_io ();
- break;
- case UseExistingMaterial:
- set_align_style (ExistingMaterial);
- break;
- case UseCaptureTime:
- set_align_style (CaptureTime);
- break;
- }
+ switch (_alignment_choice) {
+ case Automatic:
+ set_align_style_from_io ();
+ break;
+ case UseExistingMaterial:
+ set_align_style (ExistingMaterial);
+ break;
+ case UseCaptureTime:
+ set_align_style (CaptureTime);
+ break;
+ }
}
}
playlist()->set_name (str);
SessionObject::set_name(str);
}
- return true;
+ return true;
+}
+
+bool
+Diskstream::set_write_source_name (const std::string& str) {
+ _write_source_name = str;
+ return true;
}
XMLNode&
const int transport_rolling = 0x4;
const int track_rec_enabled = 0x2;
const int global_rec_enabled = 0x1;
- const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled);
+ const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled);
/* merge together the 3 factors that affect record status, and compute
- what has changed.
- */
+ * what has changed.
+ */
rolling = _session.transport_speed() != 0.0f;
possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record;
return;
}
- framecnt_t existing_material_offset = _session.worst_playback_latency();
+ framecnt_t existing_material_offset = _session.worst_playback_latency();
- if (possibly_recording == fully_rec_enabled) {
+ if (possibly_recording == fully_rec_enabled) {
- if (last_possibly_recording == fully_rec_enabled) {
- return;
- }
+ if (last_possibly_recording == fully_rec_enabled) {
+ return;
+ }
capture_start_frame = _session.transport_frame();
first_recordable_frame = capture_start_frame + _capture_offset;
first_recordable_frame));
}
- prepare_record_status (capture_start_frame);
+ prepare_record_status (capture_start_frame);
- } else {
+ } else {
- if (last_possibly_recording == fully_rec_enabled) {
+ if (last_possibly_recording == fully_rec_enabled) {
- /* we were recording last time */
+ /* we were recording last time */
- if (change & transport_rolling) {
+ if (change & transport_rolling) {
- /* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We
- had to set it there because we likely rolled past the stopping point to declick out,
- and then backed up.
- */
+ /* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We
+ * had to set it there because we likely rolled past the stopping point to declick out,
+ * and then backed up.
+ */
- } else {
- /* punch out */
+ } else {
+ /* punch out */
- last_recordable_frame = _session.transport_frame() + _capture_offset;
+ last_recordable_frame = _session.transport_frame() + _capture_offset;
- if (_alignment_style == ExistingMaterial) {
- last_recordable_frame += existing_material_offset;
- }
- }
- }
- }
+ if (_alignment_style == ExistingMaterial) {
+ last_recordable_frame += existing_material_offset;
+ }
+ }
+ }
+ }
last_possibly_recording = possibly_recording;
}
{
g_atomic_int_set (&_record_enabled, 0);
}
-
AFLPosition _AFLPosition;
RemoteModel _RemoteModel;
DenormalModel _DenormalModel;
- CrossfadeModel _CrossfadeModel;
- CrossfadeChoice _CrossfadeChoice;
InsertMergePolicy _InsertMergePolicy;
ListenPosition _ListenPosition;
SampleFormat _SampleFormat;
*/
enum_writer.add_to_hack_table ("EditorOrdered", "MixerOrdered");
- REGISTER_ENUM (FullCrossfade);
- REGISTER_ENUM (ShortCrossfade);
- REGISTER (_CrossfadeModel);
-
- REGISTER_ENUM (RegionFades);
- REGISTER_ENUM (ConstantPowerMinus3dB);
- REGISTER_ENUM (ConstantPowerMinus6dB);
- REGISTER (_CrossfadeChoice);
-
REGISTER_ENUM (InsertMergeReject);
REGISTER_ENUM (InsertMergeRelax);
REGISTER_ENUM (InsertMergeReplace);
return o << s;
}
-std::istream& operator>>(std::istream& o, CrossfadeModel& var)
-{
- std::string s;
- o >> s;
- var = (CrossfadeModel) string_2_enum (s, var);
- return o;
-}
-
-std::ostream& operator<<(std::ostream& o, const CrossfadeModel& var)
-{
- std::string s = enum_2_string (var);
- return o << s;
-}
-
-std::istream& operator>>(std::istream& o, CrossfadeChoice& var)
-{
- std::string s;
- o >> s;
- var = (CrossfadeChoice) string_2_enum (s, var);
- return o;
-}
-
-std::ostream& operator<<(std::ostream& o, const CrossfadeChoice& var)
-{
- std::string s = enum_2_string (var);
- return o << s;
-}
-
std::istream& operator>>(std::istream& o, SyncSource& var)
{
std::string s;
assert (mixdown_buffer && gain_buffer);
for (size_t channel = 0; channel < n_channels; ++channel) {
memset (mixdown_buffer.get(), 0, sizeof (Sample) * frames);
+ buffers.get_audio (channel).silence(frames);
region.read_at (buffers.get_audio (channel).data(), mixdown_buffer.get(), gain_buffer.get(), position, frames, channel);
}
break;
case Processed:
- track.export_stuff (buffers, position, frames, track.main_outs(), true, true);
+ track.export_stuff (buffers, position, frames, track.main_outs(), true, true, false);
break;
default:
throw ExportFailed ("Unhandled type in ExportChannelFactory::update_buffers");
check_for_description_change ();
}
+
+void
+ExportFormatManager::set_command (std::string command)
+{
+ current_selection->set_command (command);
+ check_for_description_change ();
+}
+
void
ExportFormatManager::select_trim_beginning (bool value)
{
, _normalize_target (1.0)
, _with_toc (false)
, _with_cue (false)
+ , _soundcloud_upload (false)
+ , _command ("")
{
format_ids.insert (F_None);
endiannesses.insert (E_FileDefault);
root->add_property ("id", _id.to_s());
root->add_property ("with-cue", _with_cue ? "true" : "false");
root->add_property ("with-toc", _with_toc ? "true" : "false");
+ root->add_property ("command", _command);
node = root->add_child ("Encoding");
node->add_property ("id", enum_2_string (format_id()));
_with_toc = false;
}
+
+ if ((prop = root.property ("command"))) {
+ _command = prop->value();
+ } else {
+ _command = "";
+ }
+
/* Encoding and SRC */
if ((child = root.child ("Encoding"))) {
components.push_back ("CUE");
}
+ if (!_command.empty()) {
+ components.push_back ("+");
+ }
+
string desc;
if (include_name) {
desc = _name + ": ";
#include "pbd/convert.h"
#include "ardour/audiofile_tagger.h"
+#include "ardour/debug.h"
#include "ardour/export_graph_builder.h"
#include "ardour/export_timespan.h"
#include "ardour/export_channel_configuration.h"
#include "ardour/export_status.h"
#include "ardour/export_format_specification.h"
#include "ardour/export_filename.h"
+#include "ardour/soundcloud_upload.h"
+#include "ardour/system_exec.h"
+#include "pbd/openuri.h"
+#include "pbd/basename.h"
#include "ardour/session_metadata.h"
#include "i18n.h"
return 0;
}
+void
+ExportHandler::command_output(std::string output, size_t size)
+{
+ std::cerr << "command: " << size << ", " << output << std::endl;
+ info << output << endmsg;
+}
+
void
ExportHandler::finish_timespan ()
{
AudiofileTagger::tag_file(filename, *SessionMetadata::Metadata());
}
+ if (!fmt->command().empty()) {
+
+#if 0 // would be nicer with C++11 initialiser...
+ std::map<char, std::string> subs {
+ { 'f', filename },
+ { 'd', Glib::path_get_dirname(filename) + G_DIR_SEPARATOR },
+ { 'b', PBD::basename_nosuffix(filename) },
+ ...
+ };
+#endif
+
+ PBD::ScopedConnection command_connection;
+ std::map<char, std::string> subs;
+ subs.insert (std::pair<char, std::string> ('f', filename));
+ subs.insert (std::pair<char, std::string> ('d', Glib::path_get_dirname (filename) + G_DIR_SEPARATOR));
+ subs.insert (std::pair<char, std::string> ('b', PBD::basename_nosuffix (filename)));
+ subs.insert (std::pair<char, std::string> ('s', session.path ()));
+ subs.insert (std::pair<char, std::string> ('n', session.name ()));
+
+ ARDOUR::SystemExec *se = new ARDOUR::SystemExec(fmt->command(), subs);
+ se->ReadStdout.connect_same_thread(command_connection, boost::bind(&ExportHandler::command_output, this, _1, _2));
+ if (se->start (2) == 0) {
+ // successfully started
+ while (se->is_running ()) {
+ // wait for system exec to terminate
+ Glib::usleep (1000);
+ }
+ }
+ delete (se);
+ }
+
+ if (fmt->soundcloud_upload()) {
+ SoundcloudUploader *soundcloud_uploader = new SoundcloudUploader;
+ std::string token = soundcloud_uploader->Get_Auth_Token(soundcloud_username, soundcloud_password);
+ DEBUG_TRACE (DEBUG::Soundcloud, string_compose(
+ "uploading %1 - username=%2, password=%3, token=%4",
+ filename, soundcloud_username, soundcloud_password, token) );
+ std::string path = soundcloud_uploader->Upload (
+ filename,
+ PBD::basename_nosuffix(filename), // title
+ token,
+ soundcloud_make_public,
+ soundcloud_downloadable,
+ this);
+
+ if (path.length() != 0) {
+ info << string_compose ( _("File %1 uploaded to %2"), filename, path) << endmsg;
+ if (soundcloud_open_page) {
+ DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("opening %1", path) );
+ open_uri(path.c_str()); // open the soundcloud website to the new file
+ }
+ } else {
+ error << _("upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
+ }
+ delete soundcloud_uploader;
+ }
config_map.erase (config_map.begin());
}
start_timespan ();
}
-/*** CD Marker sutff ***/
+/*** CD Marker stuff ***/
struct LocationSortByStart {
bool operator() (Location *a, Location *b) {
{
vector<std::string> found;
- Glib::PatternSpec pattern_spec (pattern);
- find_matching_files_in_search_path (search_path, pattern_spec, found);
+ find_files_matching_pattern (found, search_path, pattern);
return found;
}
FileSource::FileSource (Session& session, DataType type, const string& path, const string& origin, Source::Flag flag)
: Source(session, type, path, flag)
, _path (path)
- , _file_is_new (!origin.empty()) // origin empty => new file VS. origin !empty => new file
+ , _file_is_new (!origin.empty()) // if origin is left unspecified (empty string) then file must exist
, _channel (0)
, _origin (origin)
, _open (false)
if (move_dependents_to_trash() != 0) {
/* try to back out */
- rename (newpath.c_str(), _path.c_str());
+ ::rename (newpath.c_str(), _path.c_str());
return -1;
}
return true;
}
+int
+FileSource::rename (const string& newpath)
+{
+ Glib::Threads::Mutex::Lock lm (_lock);
+ string oldpath = _path;
+
+ // Test whether newpath exists, if yes notify the user but continue.
+ if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
+ error << string_compose (_("Programming error! %1 tried to rename a file over another file! It's safe to continue working, but please report this to the developers."), PROGRAM_NAME) << endmsg;
+ return -1;
+ }
+
+ if (Glib::file_test (oldpath.c_str(), Glib::FILE_TEST_EXISTS)) {
+ /* rename only needed if file exists on disk */
+ if (::rename (oldpath.c_str(), newpath.c_str()) != 0) {
+ error << string_compose (_("cannot rename file %1 to %2 (%3)"), oldpath, newpath, strerror(errno)) << endmsg;
+ return -1;
+ }
+ }
+
+ _name = Glib::path_get_basename (newpath);
+ _path = newpath;
+
+ return 0;
+}
}
}
- string path = session.path_from_region_name (region->data_type(),
- PBD::basename_nosuffix (names[i]), string (""));
+ string path = session.new_audio_source_path (name, region->n_channels(), i, false, false);
- if (path.length() == 0) {
+ if (path.empty()) {
error << string_compose (_("filter: error creating name for new file based on %1"), region->name())
<< endmsg;
return -1;
#ifdef ENABLE_NLS
(void) bindtextdomain(PACKAGE, localedir);
+ (void) bind_textdomain_codeset (PACKAGE, "UTF-8");
#endif
SessionEvent::init_event_pool ();
#ifdef LXVST_SUPPORT
vstfx_exit();
#endif
+ delete &PluginManager::instance();
+ delete Config;
PBD::cleanup ();
return;
Searchpath spath = ardour_config_search_path();
if (getenv ("ARDOUR_SAE")) {
- Glib::PatternSpec pattern("*SAE-*.bindings");
- find_matching_files_in_search_path (spath, pattern, found);
+ find_files_matching_pattern (found, spath, "*SAE-*.bindings");
} else {
- Glib::PatternSpec pattern("*.bindings");
- find_matching_files_in_search_path (spath, pattern, found);
+ find_files_matching_pattern (found, spath, "*.bindings");
}
if (found.empty()) {
}
}
-static std::string
-get_non_existent_filename (HeaderFormat hf, DataType type, const bool allow_replacing, const std::string& destdir, const std::string& basename, uint32_t channel, uint32_t channels)
-{
- char buf[PATH_MAX+1];
- bool goodfile = false;
- string base = basename;
- string ext = native_header_format_extension (hf, type);
- uint32_t cnt = 1;
-
- do {
-
- if (type == DataType::AUDIO && channels == 2) {
- if (channel == 0) {
- if (cnt == 1) {
- snprintf (buf, sizeof(buf), "%s-L%s", base.c_str(), ext.c_str());
- } else {
- snprintf (buf, sizeof(buf), "%s-%d-L%s", base.c_str(), cnt, ext.c_str());
- }
- } else {
- if (cnt == 1) {
- snprintf (buf, sizeof(buf), "%s-R%s", base.c_str(), ext.c_str());
- } else {
- snprintf (buf, sizeof(buf), "%s-%d-R%s", base.c_str(), cnt, ext.c_str());
- }
- }
- } else if (channels > 1) {
- if (cnt == 1) {
- snprintf (buf, sizeof(buf), "%s-c%d%s", base.c_str(), channel, ext.c_str());
- } else {
- snprintf (buf, sizeof(buf), "%s-%d-c%d%s", base.c_str(), cnt, channel, ext.c_str());
- }
- } else {
- if (cnt == 1) {
- snprintf (buf, sizeof(buf), "%s%s", base.c_str(), ext.c_str());
- } else {
- snprintf (buf, sizeof(buf), "%s-%d%s", base.c_str(), cnt, ext.c_str());
- }
- }
-
- string tempname = destdir + "/" + buf;
-
- if (!allow_replacing && Glib::file_test (tempname, Glib::FILE_TEST_EXISTS)) {
-
- cnt++;
-
- } else {
-
- goodfile = true;
- }
-
- } while (!goodfile);
-
- return buf;
-}
-
-static vector<string>
-get_paths_for_new_sources (HeaderFormat hf, const bool allow_replacing, const string& import_file_path, const string& session_dir, uint32_t channels)
+vector<string>
+Session::get_paths_for_new_sources (bool /*allow_replacing*/, const string& import_file_path, uint32_t channels)
{
vector<string> new_paths;
const string basename = basename_nosuffix (import_file_path);
- SessionDirectory sdir(session_dir);
-
for (uint32_t n = 0; n < channels; ++n) {
const DataType type = SMFSource::safe_midi_file_extension (import_file_path) ? DataType::MIDI : DataType::AUDIO;
+ string filepath;
- std::string filepath = (type == DataType::MIDI)
- ? sdir.midi_path() : sdir.sound_path();
+ switch (type) {
+ case DataType::MIDI:
+ filepath = new_midi_source_path (basename);
+ break;
+ case DataType::AUDIO:
+ filepath = new_audio_source_path (basename, channels, n, false, false);
+ break;
+ }
+
+ if (filepath.empty()) {
+ error << string_compose (_("Cannot find new filename for imported file %1"), import_file_path) << endmsg;
+ return vector<string>();
+ }
- filepath = Glib::build_filename (filepath,
- get_non_existent_filename (hf, type, allow_replacing, filepath, basename, n, channels));
new_paths.push_back (filepath);
}
for (vector<string>::const_iterator i = new_paths.begin();
i != new_paths.end(); ++i)
{
- boost::shared_ptr<Source> source = session->source_by_path_and_channel(*i, 0);
+ boost::shared_ptr<Source> source = session->audio_source_by_path_and_channel(*i, 0);
if (source == 0) {
error << string_compose(_("Could not find a source for %1 even though we are updating this file!"), (*i)) << endl;
progress_base = 0.5;
}
- uint32_t read_count = 0;
+ framecnt_t read_count = 0;
while (!status.cancel) {
return;
}
}
+
+ if (channels == 0) {
+ error << _("Import: file contains no channels.") << endmsg;
+ continue;
+ }
- vector<string> new_paths = get_paths_for_new_sources (config.get_native_file_header_format(),
- status.replace_existing_source, *p,
- get_best_session_directory_for_new_source (),
- channels);
+ vector<string> new_paths = get_paths_for_new_sources (status.replace_existing_source, *p, channels);
Sources newfiles;
framepos_t natural_position = source ? source->natural_position() : 0;
#include "ardour/internal_return.h"
#include "ardour/internal_send.h"
+#include "ardour/route.h"
using namespace std;
using namespace ARDOUR;
if (lm.locked ()) {
for (list<InternalSend*>::iterator i = _sends.begin(); i != _sends.end(); ++i) {
- if ((*i)->active ()) {
+ if ((*i)->active () && (!(*i)->source_route() || (*i)->source_route()->active())) {
bufs.merge_from ((*i)->get_buffers(), nframes);
}
}
PBD::Signal1<void, pframes_t> InternalSend::CycleStart;
-InternalSend::InternalSend (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMaster> mm, boost::shared_ptr<Route> sendto, Delivery::Role role, bool ignore_bitslot)
+InternalSend::InternalSend (Session& s,
+ boost::shared_ptr<Pannable> p,
+ boost::shared_ptr<MuteMaster> mm,
+ boost::shared_ptr<Route> sendfrom,
+ boost::shared_ptr<Route> sendto,
+ Delivery::Role role,
+ bool ignore_bitslot)
: Send (s, p, mm, role, ignore_bitslot)
+ , _send_from (sendfrom)
{
- if (sendto) {
- if (use_target (sendto)) {
- throw failed_constructor();
- }
- }
+ if (sendto) {
+ if (use_target (sendto)) {
+ throw failed_constructor();
+ }
+ }
init_gain ();
_amp->setup_gain_automation (start_frame, end_frame, nframes);
_amp->run (mixbufs, start_frame, end_frame, nframes, true);
+ _delayline->run (mixbufs, start_frame, end_frame, nframes, true);
+
/* consider metering */
if (_metering) {
len2 = strlen(path);
/*Try all the possibilities in the path - deliminated by : */
-
- lxvst_path = strtok (envdup, ":");
+ char *saveptr;
+ lxvst_path = strtok_r (envdup, ":", &saveptr);
while (lxvst_path != 0)
{
/*Try again*/
- lxvst_path = strtok (0, ":");
+ lxvst_path = strtok_r (0, ":", &saveptr);
}
/*Free the path*/
} else if ((monotonic_cnt - last_timestamp) > 2 * frames_per_ltc_frame) {
snprintf(delta, sizeof(delta), "%s", _("flywheel"));
} else {
- snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%" PRIi64 "</span>sm",
+ snprintf(delta, sizeof(delta), "\u0394<span foreground=\"green\" face=\"monospace\" >%s%s%lld</span>sm",
LEADINGZERO(llabs(current_delta)), PLUSMINUS(-current_delta), llabs(current_delta));
}
return std::string(delta);
#include <boost/utility.hpp>
-#include "pbd/clear_dir.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
+#include "pbd/stl_delete.h"
#include "pbd/compose.h"
#include "pbd/error.h"
#include "pbd/xml++.h"
{
if (!_bundle_checked) {
cout << "Scanning folders for bundled LV2s: " << ARDOUR::lv2_bundled_search_path().to_string() << endl;
- PathScanner scanner;
- vector<string *> *plugin_objects = scanner (ARDOUR::lv2_bundled_search_path().to_string(), lv2_filter, 0, true, true);
- if (plugin_objects) {
- for ( vector<string *>::iterator x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
+
+ vector<string> plugin_objects;
+ find_paths_matching_filter (plugin_objects, ARDOUR::lv2_bundled_search_path(), lv2_filter, 0, true, true, true);
+ for ( vector<string>::iterator x = plugin_objects.begin(); x != plugin_objects.end (); ++x) {
#ifdef PLATFORM_WINDOWS
- string uri = "file:///" + **x + "/";
+ string uri = "file:///" + *x + "/";
#else
- string uri = "file://" + **x + "/";
+ string uri = "file://" + *x + "/";
#endif
- LilvNode *node = lilv_new_uri(world, uri.c_str());
- lilv_world_load_bundle(world, node);
- lilv_node_free(node);
- }
+ LilvNode *node = lilv_new_uri(world, uri.c_str());
+ lilv_world_load_bundle(world, node);
+ lilv_node_free(node);
}
- delete (plugin_objects);
_bundle_checked = true;
}
return true;
}
+bool
+MidiBuffer::insert_event(const Evoral::MIDIEvent<TimeType>& ev)
+{
+ if (size() == 0) {
+ return push_back(ev);
+ }
+
+ const size_t stamp_size = sizeof(TimeType);
+ const size_t bytes_to_merge = stamp_size + ev.size();
+
+ if (_size + bytes_to_merge >= _capacity) {
+ cerr << "MidiBuffer::push_back failed (buffer is full)" << endl;
+ PBD::stacktrace (cerr, 20);
+ return false;
+ }
+
+ TimeType t = ev.time();
+
+ ssize_t insert_offset = -1;
+ for (MidiBuffer::iterator m = begin(); m != end(); ++m) {
+ if ((*m).time() < t) {
+ continue;
+ }
+ if ((*m).time() == t) {
+ const uint8_t our_midi_status_byte = *(_data + m.offset + sizeof (TimeType));
+ if (second_simultaneous_midi_byte_is_first (ev.type(), our_midi_status_byte)) {
+ continue;
+ }
+ }
+ insert_offset = m.offset;
+ break;
+ }
+ if (insert_offset == -1) {
+ return push_back(ev);
+ }
+
+ // don't use memmove - it may use malloc(!)
+ // memmove (_data + insert_offset + bytes_to_merge, _data + insert_offset, _size - insert_offset);
+ for (ssize_t a = _size + bytes_to_merge - 1, b = _size - 1; b >= insert_offset; --b, --a) {
+ _data[a] = _data[b];
+ }
+
+ uint8_t* const write_loc = _data + insert_offset;
+ *((TimeType*)write_loc) = t;
+ memcpy(write_loc + stamp_size, ev.buffer(), ev.size());
+
+ _size += bytes_to_merge;
+
+ return true;
+}
+
/** Reserve space for a new event in the buffer.
*
* This call is for copying MIDI directly into the buffer, the data location
, _frames_read_from_ringbuffer(0)
, _frames_pending_write(0)
, _num_captured_loops(0)
+ , _accumulated_capture_offset(0)
, _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
{
in_set_state = true;
, _frames_read_from_ringbuffer(0)
, _frames_pending_write(0)
, _num_captured_loops(0)
+ , _accumulated_capture_offset(0)
, _gui_feed_buffer(AudioEngine::instance()->raw_buffer_size (DataType::MIDI))
{
in_set_state = true;
MidiDiskstream::~MidiDiskstream ()
{
Glib::Threads::Mutex::Lock lm (state_lock);
+ delete _playback_buf;
+ delete _capture_buf;
}
Evoral::OverlapType ot = Evoral::coverage (first_recordable_frame, last_recordable_frame, transport_frame, transport_frame + nframes);
calculate_record_range(ot, transport_frame, nframes, rec_nframes, rec_offset);
+ /* For audio: not writing frames to the capture ringbuffer offsets
+ * the recording. For midi: we need to keep track of the record range
+ * and subtract the accumulated difference from the event time.
+ */
+ if (rec_nframes) {
+ _accumulated_capture_offset += rec_offset;
+ } else {
+ _accumulated_capture_offset += nframes;
+ }
if (rec_nframes && !was_recording) {
if (loop_loc) {
for (MidiBuffer::iterator i = buf.begin(); i != buf.end(); ++i) {
Evoral::MIDIEvent<MidiBuffer::TimeType> ev(*i, false);
+ if (ev.time() + rec_offset > rec_nframes) {
+ break;
+ }
#ifndef NDEBUG
if (DEBUG::MidiIO & PBD::debug_bits) {
const uint8_t* __data = ev.buffer();
any desirable behaviour. We don't want to send event with
transport time here since that way the source can not
reconstruct their actual time; future clever MIDI looping should
- probabl be implemented in the source instead of here.
+ probably be implemented in the source instead of here.
*/
const framecnt_t loop_offset = _num_captured_loops * loop_length;
-
+ const framepos_t event_time = transport_frame + loop_offset - _accumulated_capture_offset + ev.time();
+ if (event_time < 0 || event_time < first_recordable_frame) {
+ continue;
+ }
switch (mode) {
case AllChannels:
- _capture_buf->write(transport_frame + loop_offset + ev.time(),
+ _capture_buf->write(event_time,
ev.type(), ev.size(), ev.buffer());
break;
case FilterChannels:
if (ev.is_channel_event()) {
if ((1<<ev.channel()) & mask) {
- _capture_buf->write(transport_frame + loop_offset + ev.time(),
+ _capture_buf->write(event_time,
ev.type(), ev.size(), ev.buffer());
}
} else {
- _capture_buf->write(transport_frame + loop_offset + ev.time(),
+ _capture_buf->write(event_time,
ev.type(), ev.size(), ev.buffer());
}
break;
if (ev.is_channel_event()) {
ev.set_channel (PBD::ffs(mask) - 1);
}
- _capture_buf->write(transport_frame + loop_offset + ev.time(),
+ _capture_buf->write(event_time,
ev.type(), ev.size(), ev.buffer());
break;
}
if (was_recording) {
finish_capture ();
}
+ _accumulated_capture_offset = 0;
}
RegionFactory::region_name (region_name, _write_source->name(), false);
+ DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 capture start @ %2 length %3 add new region %4\n",
+ _name, (*ci)->start, (*ci)->frames, region_name));
+
+
// cerr << _name << ": based on ci of " << (*ci)->start << " for " << (*ci)->frames << " add a region\n";
try {
return 1;
}
+ _accumulated_capture_offset = 0;
_write_source.reset();
try {
_write_source = boost::dynamic_pointer_cast<SMFSource>(
- _session.create_midi_source_for_session (name ()));
+ _session.create_midi_source_for_session (write_source_name ()));
if (!_write_source) {
throw failed_constructor();
*/
try {
- string new_name = _session.new_midi_source_name (name());
+ string new_path = _session.new_midi_source_path (name());
- if (_write_source->rename (new_name)) {
+ if (_write_source->rename (new_path)) {
return string();
}
} catch (...) {
bool
MidiDiskstream::set_name (string const & name)
{
+ if (_name == name) {
+ return true;
+ }
Diskstream::set_name (name);
/* get a new write source so that its name reflects the new diskstream name */
return true;
}
+bool
+MidiDiskstream::set_write_source_name (const std::string& str) {
+ if (_write_source_name == str) {
+ return true;
+ }
+ Diskstream::set_write_source_name (str);
+ if (_write_source_name == name()) {
+ return true;
+ }
+ use_new_write_source (0);
+ return true;
+}
+
boost::shared_ptr<MidiBuffer>
MidiDiskstream::get_gui_feed_buffer () const
{
assert (Glib::file_test (path_to_patches, Glib::FILE_TEST_IS_DIR));
- Glib::PatternSpec pattern(string("*.midnam"));
vector<std::string> result;
- find_matching_files_in_directory (path_to_patches, pattern, result);
+ find_files_matching_pattern (result, path_to_patches, "*.midnam");
info << "Loading " << result.size() << " MIDI patches from " << path_to_patches << endmsg;
_all_models.clear();
Searchpath search_path = midi_patch_search_path ();
- Glib::PatternSpec pattern (string("*.midnam"));
vector<std::string> result;
- find_matching_files_in_search_path (search_path, pattern, result);
+ find_files_matching_pattern (result, search_path, "*.midnam");
info << "Loading " << result.size() << " MIDI patches from " << search_path.to_string() << endmsg;
int
MidiTrack::export_stuff (BufferSet& /*bufs*/, framepos_t /*start_frame*/, framecnt_t /*nframes*/,
- boost::shared_ptr<Processor> /*endpoint*/, bool /*include_endpoint*/, bool /*forexport*/)
+ boost::shared_ptr<Processor> /*endpoint*/, bool /*include_endpoint*/, bool /*for_export*/, bool /*for_freeze*/)
{
return -1;
}
--- /dev/null
+/*
+ * Copyright (C) 2013-2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "ardour/mididm.h"
+#include "ardour/port_engine.h"
+
+using namespace ARDOUR;
+
+MIDIDM::MIDIDM (framecnt_t sample_rate)
+ : _sample_rate (sample_rate)
+ , _monotonic_cnt (sample_rate)
+ , _last_signal_tme (0)
+ , _cnt_total (0)
+ , _dly_total (0)
+ , _min_delay (INT32_MAX)
+ , _max_delay (0)
+ , _avg_delay (0)
+ , _var_m (0)
+ , _var_s (0)
+{
+
+}
+
+int64_t
+MIDIDM::parse_mclk (uint8_t* buf, pframes_t timestamp) const
+{
+ /* calculate time difference */
+#define MODCLK (16384) // 1<<(2*7)
+ const int64_t tc = (_monotonic_cnt + timestamp) & 0x3fff; // MODCLK - 1;
+ const int64_t ti = ((buf[2] & 0x7f) << 7) | (buf[1] & 0x7f);
+ const int64_t tdiff = (MODCLK + tc - ti) % MODCLK;
+#ifdef DEBUG_MIDIDM
+ printf("MCLK DELAY: #%5"PRId64" dt:%6"PRId64" [spl] (%6"PRId64" - %8"PRId64") @(%8"PRId64" + %d)\n",
+ _cnt_total, tdiff, tc, ti, _monotonic_cnt, timestamp);
+#endif
+ return tdiff;
+}
+
+int64_t
+MIDIDM::parse_mtc (uint8_t* buf, pframes_t timestamp) const
+{
+#define MODTC (2097152) // 1<<(3*7)
+ const int64_t tc = (_monotonic_cnt + timestamp) & 0x001FFFFF;
+ const int64_t ti = (buf[5] & 0x7f)
+ | ((buf[6] & 0x7f) << 7)
+ | ((buf[7] & 0x7f) << 14)
+ | ((buf[8] & 0x7f) << 21);
+ const int64_t tdiff = (MODTC + tc - ti) % MODTC;
+#ifdef DEBUG_MIDIDM
+ printf("MTC DELAY: #%5"PRId64" dt:%6"PRId64" [spl] (%6"PRId64" - %8"PRId64") @(%8"PRId64" + %d)\n",
+ _cnt_total, tdiff, tc, ti, _monotonic_cnt, timestamp);
+#endif
+ return tdiff;
+}
+
+int MIDIDM::process (pframes_t nframes, PortEngine &pe, void *midi_in, void *midi_out)
+{
+ /* send midi event */
+ pe.midi_clear(midi_out);
+#ifndef USE_MTC // use 3-byte song position
+ uint8_t obuf[3];
+ obuf[0] = 0xf2;
+ obuf[1] = (_monotonic_cnt) & 0x7f;
+ obuf[2] = (_monotonic_cnt >> 7) & 0x7f;
+ pe.midi_event_put (midi_out, 0, obuf, 3);
+#else // sysex MTC frame
+ uint8_t obuf[10];
+ obuf[0] = 0xf0;
+ obuf[1] = 0x7f;
+ obuf[2] = 0x7f;
+ obuf[3] = 0x01;
+ obuf[4] = 0x01;
+ obuf[9] = 0xf7;
+ obuf[5] = (_monotonic_cnt ) & 0x7f;
+ obuf[6] = (_monotonic_cnt >> 7) & 0x7f;
+ obuf[7] = (_monotonic_cnt >> 14) & 0x7f;
+ obuf[8] = (_monotonic_cnt >> 21) & 0x7f;
+ pe.midi_event_put (midi_out, 0, obuf, 10);
+#endif
+
+ /* process incoming */
+ const pframes_t nevents = pe.get_midi_event_count (midi_in);
+#ifdef DEBUG_MIDIDM
+ printf("MIDI SEND: @%8"PRId64", recv: %d systime:%"PRId64"\n", _monotonic_cnt, nevents, g_get_monotonic_time());
+#endif
+ for (pframes_t n = 0; n < nevents; ++n) {
+ pframes_t timestamp;
+ size_t size;
+ uint8_t* buf;
+ int64_t tdiff;
+ pe.midi_event_get (timestamp, size, &buf, midi_in, n);
+
+ if (size == 3 && buf[0] == 0xf2 )
+ {
+ tdiff = parse_mclk(buf, timestamp);
+ } else if (size == 10 && buf[0] == 0xf0)
+ {
+ tdiff = parse_mtc(buf, timestamp);
+ }
+ else
+ {
+ continue;
+ }
+
+ _last_signal_tme = _monotonic_cnt;
+
+ /* running variance */
+ if (_cnt_total == 0) {
+ _var_m = tdiff;
+ } else {
+ const double var_m1 = _var_m;
+ _var_m = _var_m + ((double)tdiff - _var_m) / (double)(_cnt_total + 1);
+ _var_s = _var_s + ((double)tdiff - _var_m) * ((double)tdiff - var_m1);
+ }
+ /* average and mix/max */
+ ++_cnt_total;
+ _dly_total += tdiff;
+ _avg_delay = _dly_total / _cnt_total;
+ if (tdiff < _min_delay) _min_delay = tdiff;
+ if (tdiff > _max_delay) _max_delay = tdiff;
+ }
+
+ _monotonic_cnt += nframes;
+ return 0;
+}
#include "pbd/error.h"
#include "pbd/compose.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
#include "pbd/stl_delete.h"
#include "ardour/debug.h"
void
PannerManager::discover_panners ()
{
- PathScanner scanner;
- std::vector<std::string *> *panner_modules;
- std::string search_path = panner_search_path().to_string();
+ std::vector<std::string> panner_modules;
- DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for panners in %1\n"), search_path));
+ DEBUG_TRACE (DEBUG::Panning, string_compose (_("looking for panners in %1\n"), panner_search_path().to_string()));
- panner_modules = scanner (search_path, panner_filter, 0, false, true, 1, true);
+ find_files_matching_filter (panner_modules, panner_search_path(), panner_filter, 0, false, true, true);
- for (vector<std::string *>::iterator i = panner_modules->begin(); i != panner_modules->end(); ++i) {
- panner_discover (**i);
+ for (vector<std::string>::iterator i = panner_modules.begin(); i != panner_modules.end(); ++i) {
+ panner_discover (*i);
}
-
- vector_delete (panner_modules);
}
int
if (i == panner_info.end()) {
panner_info.push_back (pinfo);
DEBUG_TRACE (DEBUG::Panning, string_compose(_("Panner discovered: \"%1\" in %2\n"), pinfo->descriptor.name, path));
+ } else {
+ delete pinfo;
}
}
_current_panner_uri = (*p)->descriptor.panner_uri;
_panner_gui_uri = (*p)->descriptor.gui_uri;
+ if (_is_send) {
+ if (!_panlinked) {
+ _pannable_internal->set_panner(_panner);
+ } else {
+ _force_reselect = true;
+ }
+ } else {
+ _pannable_route->set_panner(_panner);
+ }
+
if (_panner->set_state (**niter, version) == 0) {
return -1;
}
pl->in_partition = true;
- for (RegionList::const_iterator i = r.begin(); i != r.end(); ++i) {
+ /* sort by position then layer.
+ * route_time_axis passes 'selected_regions' - which is not sorted.
+ * here we need the top-most first, then every layer's region sorted by position.
+ */
+ RegionList sorted(r);
+ sorted.sort(RegionSortByLayerAndPosition());
+
+ for (RegionList::const_iterator i = sorted.begin(); i != sorted.end(); ++i) {
/* copy the region */
for (vector<boost::shared_ptr<Region> >::iterator i = originals.begin(); i != originals.end(); ++i) {
add_region ((*i), (*i)->position());
+ set_layer((*i), (*i)->layer());
}
in_partition = false;
}
void
-PluginInsert::run (BufferSet& bufs, framepos_t /*start_frame*/, framepos_t /*end_frame*/, pframes_t nframes, bool)
+PluginInsert::run (BufferSet& bufs, framepos_t start_frame, framepos_t /*end_frame*/, pframes_t nframes, bool)
{
if (_pending_active) {
/* run as normal if we are active or moving from inactive to active */
- if (_session.transport_rolling()) {
- automation_run (bufs, nframes);
+ if (_session.transport_rolling() || _session.bounce_processing()) {
+ automation_run (bufs, start_frame, nframes);
} else {
connect_and_run (bufs, nframes, 0, false);
}
}
void
-PluginInsert::automation_run (BufferSet& bufs, pframes_t nframes)
+PluginInsert::automation_run (BufferSet& bufs, framepos_t start, pframes_t nframes)
{
Evoral::ControlEvent next_event (0, 0.0f);
- framepos_t now = _session.transport_frame ();
+ framepos_t now = start;
framepos_t end = now + nframes;
framecnt_t offset = 0;
_logarithmic = desc.logarithmic;
_sr_dependent = desc.sr_dependent;
_toggled = desc.toggled;
+
+ if (desc.toggled) {
+ set_flags(Controllable::Toggle);
+ }
}
/** @param val `user' value */
#include <glibmm/miscutils.h>
#include <glibmm/pattern.h>
-#include "pbd/pathscanner.h"
#include "pbd/whitespace.h"
#include "pbd/file_utils.h"
string lrdf_path;
string scan_p = Glib::build_filename(ARDOUR::ardour_dll_directory(), "fst");
- if (!PBD::find_file_in_search_path ( PBD::Searchpath(scan_p), "ardour-vst-scanner", scanner_bin_path)) {
+ if (!PBD::find_file ( PBD::Searchpath(scan_p), "ardour-vst-scanner", scanner_bin_path)) {
PBD::warning << "VST scanner app (ardour-vst-scanner) not found in path " << scan_p << endmsg;
}
// see also libs/ardour/vst_info_file.cc - vstfx_infofile_path()
#ifdef WINDOWS_VST_SUPPORT
{
- PathScanner scanner;
- vector<string *> *fsi_files;
-
- fsi_files = scanner (Config->get_plugin_path_vst(), "\\.fsi$", true, true, -1, false);
- if (fsi_files) {
- for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
- ::g_unlink((*i)->c_str());
- }
+ vector<string> fsi_files;
+ find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\.fsi$");
+ for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+ ::g_unlink(i->c_str());
}
- vector_delete(fsi_files);
}
#endif
#ifdef LXVST_SUPPORT
{
- PathScanner scanner;
- vector<string *> *fsi_files;
- fsi_files = scanner (Config->get_plugin_path_lxvst(), "\\.fsi$", true, true, -1, false);
- if (fsi_files) {
- for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
- ::g_unlink((*i)->c_str());
- }
+ vector<string> fsi_files;
+ find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\.fsi$");
+ for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+ ::g_unlink(i->c_str());
}
- vector_delete(fsi_files);
}
#endif
#if (defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT)
{
string personal = get_personal_vst_info_cache_dir();
- PathScanner scanner;
- vector<string *> *fsi_files;
- fsi_files = scanner (personal, "\\.fsi$", true, true, -1, false);
- if (fsi_files) {
- for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
- ::g_unlink((*i)->c_str());
- }
+ vector<string> fsi_files;
+ find_files_matching_regex (fsi_files, personal, "\\.fsi$");
+ for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+ ::g_unlink(i->c_str());
}
- vector_delete(fsi_files);
}
#endif
}
{
#ifdef WINDOWS_VST_SUPPORT
{
- PathScanner scanner;
- vector<string *> *fsi_files;
-
- fsi_files = scanner (Config->get_plugin_path_vst(), "\\.fsb$", true, true, -1, false);
- if (fsi_files) {
- for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
- ::g_unlink((*i)->c_str());
- }
+ vector<string> fsi_files;
+ find_files_matching_regex (fsi_files, Config->get_plugin_path_vst(), "\\.fsb$");
+ for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+ ::g_unlink(i->c_str());
}
- vector_delete(fsi_files);
}
#endif
#ifdef LXVST_SUPPORT
{
- PathScanner scanner;
- vector<string *> *fsi_files;
- fsi_files = scanner (Config->get_plugin_path_lxvst(), "\\.fsb$", true, true, -1, false);
- if (fsi_files) {
- for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
- ::g_unlink((*i)->c_str());
- }
+ vector<string> fsi_files;
+ find_files_matching_regex (fsi_files, Config->get_plugin_path_lxvst(), "\\.fsb$");
+ for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+ ::g_unlink(i->c_str());
}
- vector_delete(fsi_files);
}
#endif
{
string personal = get_personal_vst_blacklist_dir();
- PathScanner scanner;
- vector<string *> *fsi_files;
- fsi_files = scanner (personal, "\\.fsb$", true, true, -1, false);
- if (fsi_files) {
- for (vector<string *>::iterator i = fsi_files->begin(); i != fsi_files->end (); ++i) {
- ::g_unlink((*i)->c_str());
- }
+ vector<string> fsi_files;
+ find_files_matching_regex (fsi_files, personal, "\\.fsb$");
+ for (vector<string>::iterator i = fsi_files.begin(); i != fsi_files.end (); ++i) {
+ ::g_unlink(i->c_str());
}
- vector_delete(fsi_files);
}
#endif
}
DEBUG_TRACE (DEBUG::PluginManager, string_compose ("LADSPA: search along: [%1]\n", ladspa_search_path().to_string()));
- Glib::PatternSpec so_extension_pattern("*.so");
- Glib::PatternSpec dylib_extension_pattern("*.dylib");
- Glib::PatternSpec dll_extension_pattern("*.dll");
-
- find_matching_files_in_search_path (ladspa_search_path (),
- so_extension_pattern, ladspa_modules);
-
- find_matching_files_in_search_path (ladspa_search_path (),
- dylib_extension_pattern, ladspa_modules);
-
- find_matching_files_in_search_path (ladspa_search_path (),
- dll_extension_pattern, ladspa_modules);
+ find_files_matching_pattern (ladspa_modules, ladspa_search_path (), "*.so");
+ find_files_matching_pattern (ladspa_modules, ladspa_search_path (), "*.dylib");
+ find_files_matching_pattern (ladspa_modules, ladspa_search_path (), "*.dll");
for (vector<std::string>::iterator i = ladspa_modules.begin(); i != ladspa_modules.end(); ++i) {
ARDOUR::PluginScanMessage(_("LADSPA"), *i, false);
PluginManager::add_presets(string domain)
{
#ifdef HAVE_LRDF
- PathScanner scanner;
- vector<string *> *presets;
- vector<string *>::iterator x;
+ vector<string> presets;
+ vector<string>::iterator x;
char* envvar;
if ((envvar = getenv ("HOME")) == 0) {
}
string path = string_compose("%1/.%2/rdf", envvar, domain);
- presets = scanner (path, rdf_filter, 0, false, true);
+ find_files_matching_filter (presets, path, rdf_filter, 0, false, true);
- if (presets) {
- for (x = presets->begin(); x != presets->end (); ++x) {
- string file = "file:" + **x;
- if (lrdf_read_file(file.c_str())) {
- warning << string_compose(_("Could not parse rdf file: %1"), *x) << endmsg;
- }
+ for (x = presets.begin(); x != presets.end (); ++x) {
+ string file = "file:" + *x;
+ if (lrdf_read_file(file.c_str())) {
+ warning << string_compose(_("Could not parse rdf file: %1"), *x) << endmsg;
}
-
- vector_delete (presets);
}
+
#endif
}
PluginManager::add_lrdf_data (const string &path)
{
#ifdef HAVE_LRDF
- PathScanner scanner;
- vector<string *>* rdf_files;
- vector<string *>::iterator x;
+ vector<string> rdf_files;
+ vector<string>::iterator x;
- rdf_files = scanner (path, rdf_filter, 0, false, true);
+ find_files_matching_filter (rdf_files, path, rdf_filter, 0, false, true);
- if (rdf_files) {
- for (x = rdf_files->begin(); x != rdf_files->end (); ++x) {
- const string uri(string("file://") + **x);
+ for (x = rdf_files.begin(); x != rdf_files.end (); ++x) {
+ const string uri(string("file://") + *x);
- if (lrdf_read_file(uri.c_str())) {
- warning << "Could not parse rdf file: " << uri << endmsg;
- }
+ if (lrdf_read_file(uri.c_str())) {
+ warning << "Could not parse rdf file: " << uri << endmsg;
}
-
- vector_delete (rdf_files);
}
#endif
}
int
PluginManager::windows_vst_discover_from_path (string path, bool cache_only)
{
- PathScanner scanner;
- vector<string *> *plugin_objects;
- vector<string *>::iterator x;
+ vector<string> plugin_objects;
+ vector<string>::iterator x;
int ret = 0;
DEBUG_TRACE (DEBUG::PluginManager, string_compose ("detecting Windows VST plugins along %1\n", path));
- plugin_objects = scanner (Config->get_plugin_path_vst(), windows_vst_filter, 0, false, true);
-
- if (plugin_objects) {
- for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
- ARDOUR::PluginScanMessage(_("VST"), **x, !cache_only && !cancelled());
- windows_vst_discover (**x, cache_only || cancelled());
- }
+ find_files_matching_filter (plugin_objects, Config->get_plugin_path_vst(), windows_vst_filter, 0, false, true);
- vector_delete (plugin_objects);
+ for (x = plugin_objects.begin(); x != plugin_objects.end (); ++x) {
+ ARDOUR::PluginScanMessage(_("VST"), *x, !cache_only && !cancelled());
+ windows_vst_discover (*x, cache_only || cancelled());
}
return ret;
int
PluginManager::lxvst_discover_from_path (string path, bool cache_only)
{
- PathScanner scanner;
- vector<string *> *plugin_objects;
- vector<string *>::iterator x;
+ vector<string> plugin_objects;
+ vector<string>::iterator x;
int ret = 0;
#ifndef NDEBUG
DEBUG_TRACE (DEBUG::PluginManager, string_compose ("Discovering linuxVST plugins along %1\n", path));
- plugin_objects = scanner (Config->get_plugin_path_lxvst(), lxvst_filter, 0, false, true);
-
- if (plugin_objects) {
- for (x = plugin_objects->begin(); x != plugin_objects->end (); ++x) {
- ARDOUR::PluginScanMessage(_("LXVST"), **x, !cache_only && !cancelled());
- lxvst_discover (**x, cache_only || cancelled());
- }
+ find_files_matching_filter (plugin_objects, Config->get_plugin_path_lxvst(), lxvst_filter, 0, false, true);
- vector_delete (plugin_objects);
+ for (x = plugin_objects.begin(); x != plugin_objects.end (); ++x) {
+ ARDOUR::PluginScanMessage(_("LXVST"), *x, !cache_only && !cancelled());
+ lxvst_discover (*x, cache_only || cancelled());
}
return ret;
/* load system configuration first */
- if (find_file_in_search_path (ardour_config_search_path(), "ardour_system.rc", rcfile)) {
+ if (find_file (ardour_config_search_path(), "ardour_system.rc", rcfile)) {
/* stupid XML Parser hates empty files */
/* now load configuration file for user */
- if (find_file_in_search_path (ardour_config_search_path(), "ardour.rc", rcfile)) {
+ if (find_file (ardour_config_search_path(), "ardour.rc", rcfile)) {
/* stupid XML parser hates empty files */
#include "ardour/internal_return.h"
#include "ardour/internal_send.h"
#include "ardour/meter.h"
+#include "ardour/delayline.h"
#include "ardour/midi_buffer.h"
#include "ardour/midi_port.h"
#include "ardour/monitor_processor.h"
, GraphNode (sess._process_graph)
, _active (true)
, _signal_latency (0)
+ , _signal_latency_at_amp_position (0)
, _initial_delay (0)
, _roll_delay (0)
, _flags (flg)
, _order_key (0)
, _has_order_key (false)
, _remote_control_id (0)
+ , _track_number (0)
, _in_configure_processors (false)
, _initial_io_setup (false)
, _custom_meter_position_noted (false)
_output->changed.connect_same_thread (*this, boost::bind (&Route::output_change_handler, this, _1, _2));
_output->PortCountChanging.connect_same_thread (*this, boost::bind (&Route::output_port_count_changing, this, _1));
+ if (!is_master() && !is_monitor() && !is_auditioner()) {
+ _delayline.reset (new DelayLine (_session, _name));
+ add_processor (_delayline, PreFader);
+ }
+
/* add amp processor */
_amp.reset (new Amp (_session));
/* figure out if we're going to use gain automation */
if (gain_automation_ok) {
_amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
- _amp->setup_gain_automation (start_frame, end_frame, nframes);
+ _amp->setup_gain_automation (
+ start_frame + _signal_latency_at_amp_position,
+ end_frame + _signal_latency_at_amp_position,
+ nframes);
} else {
_amp->apply_gain_automation (false);
}
/* set this to be true if the meter will already have been ::run() earlier */
bool const meter_already_run = metering_state() == MeteringInput;
+ framecnt_t latency = 0;
+
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if (meter_already_run && boost::dynamic_pointer_cast<PeakMeter> (*i)) {
do we catch route != active somewhere higher?
*/
- (*i)->run (bufs, start_frame, end_frame, nframes, *i != _processors.back());
+ if (boost::dynamic_pointer_cast<Send>(*i) != 0) {
+ boost::dynamic_pointer_cast<Send>(*i)->set_delay_in(_signal_latency - latency);
+ }
+
+ (*i)->run (bufs, start_frame - latency, end_frame - latency, nframes, *i != _processors.back());
bufs.set_count ((*i)->output_streams());
+
+ if ((*i)->active ()) {
+ latency += (*i)->signal_latency ();
+ }
}
}
+void
+Route::bounce_process (BufferSet& buffers, framepos_t start, framecnt_t nframes,
+ boost::shared_ptr<Processor> endpoint,
+ bool include_endpoint, bool for_export, bool for_freeze)
+{
+ /* If no processing is required, there's no need to go any further. */
+ if (!endpoint && !include_endpoint) {
+ return;
+ }
+
+ framecnt_t latency = bounce_get_latency(_amp, false, for_export, for_freeze);
+ _amp->set_gain_automation_buffer (_session.gain_automation_buffer ());
+ _amp->setup_gain_automation (start - latency, start - latency + nframes, nframes);
+
+ latency = 0;
+ for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
+
+ if (!include_endpoint && (*i) == endpoint) {
+ break;
+ }
+
+ /* if we're not exporting, stop processing if we come across a routing processor. */
+ if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+ break;
+ }
+ if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+ break;
+ }
+
+ /* don't run any processors that does routing.
+ * oh, and don't bother with the peak meter either.
+ */
+ if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+ (*i)->run (buffers, start - latency, start - latency + nframes, nframes, true);
+ buffers.set_count ((*i)->output_streams());
+ latency += (*i)->signal_latency ();
+ }
+
+ if ((*i) == endpoint) {
+ break;
+ }
+ }
+}
+
+framecnt_t
+Route::bounce_get_latency (boost::shared_ptr<Processor> endpoint,
+ bool include_endpoint, bool for_export, bool for_freeze) const
+{
+ framecnt_t latency = 0;
+ if (!endpoint && !include_endpoint) {
+ return latency;
+ }
+
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (!include_endpoint && (*i) == endpoint) {
+ break;
+ }
+ if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+ break;
+ }
+ if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+ break;
+ }
+ if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+ latency += (*i)->signal_latency ();
+ }
+ if ((*i) == endpoint) {
+ break;
+ }
+ }
+ return latency;
+}
+
+ChanCount
+Route::bounce_get_output_streams (ChanCount &cc, boost::shared_ptr<Processor> endpoint,
+ bool include_endpoint, bool for_export, bool for_freeze) const
+{
+ if (!endpoint && !include_endpoint) {
+ return cc;
+ }
+
+ for (ProcessorList::const_iterator i = _processors.begin(); i != _processors.end(); ++i) {
+ if (!include_endpoint && (*i) == endpoint) {
+ break;
+ }
+ if (!for_export && boost::dynamic_pointer_cast<PortInsert>(*i)) {
+ break;
+ }
+ if (!for_export && for_freeze && (*i)->does_routing() && (*i)->active()) {
+ break;
+ }
+ if (!(*i)->does_routing() && !boost::dynamic_pointer_cast<PeakMeter>(*i)) {
+ cc = (*i)->output_streams();
+ }
+ if ((*i) == endpoint) {
+ break;
+ }
+ }
+ return cc;
+}
+
ChanCount
Route::n_process_buffers ()
{
seen_amp = true;
}
- if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs) {
+ if ((*i) == _amp || (*i) == _meter || (*i) == _main_outs || (*i) == _delayline) {
/* you can't remove these */
/* these can never be removed */
- if (processor == _amp || processor == _meter || processor == _main_outs) {
+ if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) {
return 0;
}
/* these can never be removed */
- if (processor == _amp || processor == _meter || processor == _main_outs) {
+ if (processor == _amp || processor == _meter || processor == _main_outs || processor == _delayline) {
++i;
continue;
}
} else if (prop->value() == "meter") {
_meter->set_state (**niter, Stateful::current_state_version);
new_order.push_back (_meter);
+ } else if (prop->value() == "delay") {
+ _delayline->set_state (**niter, Stateful::current_state_version);
+ new_order.push_back (_delayline);
} else if (prop->value() == "main-outs") {
_main_outs->set_state (**niter, Stateful::current_state_version);
} else if (prop->value() == "intreturn") {
if (prop->value() == "intsend") {
- processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::shared_ptr<Route>(), Delivery::Aux, true));
+ processor.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), boost::shared_ptr<Route>(), Delivery::Aux, true));
} else if (prop->value() == "ladspa" || prop->value() == "Ladspa" ||
prop->value() == "lv2" ||
/* make sure we have one */
if (!_monitor_send) {
- _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, _session.monitor_out(), Delivery::Listen));
+ _monitor_send.reset (new InternalSend (_session, _pannable, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), _session.monitor_out(), Delivery::Listen));
_monitor_send->set_display_to_user (false);
}
{
Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
boost::shared_ptr<Pannable> sendpan (new Pannable (_session));
- listener.reset (new InternalSend (_session, sendpan, _mute_master, route, Delivery::Aux));
+ listener.reset (new InternalSend (_session, sendpan, _mute_master, boost::dynamic_pointer_cast<ARDOUR::Route>(shared_from_this()), route, Delivery::Aux));
}
add_processor (listener, before);
Route::update_signal_latency ()
{
framecnt_t l = _output->user_latency();
+ framecnt_t lamp = 0;
+ bool before_amp = true;
for (ProcessorList::iterator i = _processors.begin(); i != _processors.end(); ++i) {
if ((*i)->active ()) {
l += (*i)->signal_latency ();
}
+ if ((*i) == _amp) {
+ before_amp = false;
+ }
+ if (before_amp) {
+ lamp = l;
+ }
}
DEBUG_TRACE (DEBUG::Latency, string_compose ("%1: internal signal latency = %2\n", _name, l));
+ _signal_latency_at_amp_position = lamp;
if (_signal_latency != l) {
_signal_latency = l;
signal_latency_changed (); /* EMIT SIGNAL */
bool
Route::set_name (const string& str)
{
- bool ret;
- string ioproc_name;
- string name;
+ if (str == name()) {
+ return true;
+ }
- name = Route::ensure_track_or_route_name (str, _session);
+ string name = Route::ensure_track_or_route_name (str, _session);
SessionObject::set_name (name);
- ret = (_input->set_name(name) && _output->set_name(name));
+ bool ret = (_input->set_name(name) && _output->set_name(name));
if (ret) {
/* rename the main outs. Leave other IO processors
void
Route::set_active (bool yn, void* src)
{
+ if (_session.transport_rolling()) {
+ return;
+ }
+
if (_route_group && src != _route_group && _route_group->is_active() && _route_group->is_route_active()) {
_route_group->foreach_route (boost::bind (&Route::set_active, _1, yn, _route_group));
return;
}
}
+ if (!is_master() && !is_monitor() && !is_auditioner()) {
+ new_processors.push_front (_delayline);
+ }
+
/* MONITOR CONTROL */
if (_monitor_control && is_monitor ()) {
_pannable->transport_located (pos);
}
+ if (_delayline.get()) {
+ _delayline.get()->flush();
+ }
+
{
//Glib::Threads::Mutex::Lock lx (AudioEngine::instance()->process_lock ());
Glib::Threads::RWLock::ReaderLock lm (_processor_lock);
#include "pbd/boost_debug.h"
#include "ardour/amp.h"
-#include "ardour/send.h"
-#include "ardour/session.h"
#include "ardour/buffer_set.h"
-#include "ardour/meter.h"
+#include "ardour/debug.h"
#include "ardour/io.h"
+#include "ardour/meter.h"
#include "ardour/panner_shell.h"
+#include "ardour/send.h"
+#include "ardour/session.h"
#include "i18n.h"
Send::Send (Session& s, boost::shared_ptr<Pannable> p, boost::shared_ptr<MuteMaster> mm, Role r, bool ignore_bitslot)
: Delivery (s, p, mm, name_and_id_new_send (s, r, _bitslot, ignore_bitslot), r)
, _metering (false)
+ , _delay_in (0)
+ , _delay_out (0)
{
if (_role == Listen) {
/* we don't need to do this but it keeps things looking clean
_amp.reset (new Amp (_session));
_meter.reset (new PeakMeter (_session, name()));
+ _delayline.reset (new DelayLine (_session, name()));
+
add_control (_amp->gain_control ());
if (panner_shell()) {
Processor::deactivate ();
}
+void
+Send::set_delay_in(framecnt_t delay)
+{
+ if (!_delayline) return;
+ if (_delay_in == delay) {
+ return;
+ }
+ _delay_in = delay;
+
+ DEBUG_TRACE (DEBUG::LatencyCompensation,
+ string_compose ("Send::set_delay_in(%1) + %2 = %3\n",
+ delay, _delay_out, _delay_out + _delay_in));
+ _delayline.get()->set_delay(_delay_out + _delay_in);
+}
+
+void
+Send::set_delay_out(framecnt_t delay)
+{
+ if (!_delayline) return;
+ if (_delay_out == delay) {
+ return;
+ }
+ _delay_out = delay;
+ DEBUG_TRACE (DEBUG::LatencyCompensation,
+ string_compose ("Send::set_delay_out(%1) + %2 = %3\n",
+ delay, _delay_in, _delay_out + _delay_in));
+ _delayline.get()->set_delay(_delay_out + _delay_in);
+}
+
void
Send::run (BufferSet& bufs, framepos_t start_frame, framepos_t end_frame, pframes_t nframes, bool)
{
_amp->setup_gain_automation (start_frame, end_frame, nframes);
_amp->run (sendbufs, start_frame, end_frame, nframes, true);
+ _delayline->run (sendbufs, start_frame, end_frame, nframes, true);
+
/* deliver to outputs */
Delivery::run (sendbufs, start_frame, end_frame, nframes, true);
return false;
}
+ if (_delayline && !_delayline->configure_io(in, out)) {
+ cerr << "send delayline config failed\n";
+ return false;
+ }
+
reset_panner ();
return true;
#include <boost/algorithm/string/erase.hpp>
+#include "pbd/convert.h"
#include "pbd/error.h"
#include "pbd/boost_debug.h"
-#include "pbd/pathscanner.h"
#include "pbd/stl_delete.h"
#include "pbd/basename.h"
#include "pbd/stacktrace.h"
#include "ardour/region_factory.h"
#include "ardour/route_graph.h"
#include "ardour/route_group.h"
+#include "ardour/route_sorters.h"
#include "ardour/send.h"
#include "ardour/session.h"
#include "ardour/session_directory.h"
PBD::Signal0<void> Session::SuccessfulGraphSort;
PBD::Signal2<void,std::string,std::string> Session::VersionMismatch;
+const framecnt_t Session::bounce_chunk_size = 65536;
static void clean_up_session_event (SessionEvent* ev) { delete ev; }
const SessionEvent::RTeventCallback Session::rt_cleanup (clean_up_session_event);
: playlists (new SessionPlaylists)
, _engine (eng)
, process_function (&Session::process_with_events)
+ , _bounce_processing_active (false)
, waiting_for_sync_offset (false)
, _base_frame_rate (0)
, _current_frame_rate (0)
, state_tree (0)
, state_was_pending (false)
, _state_of_the_state (StateOfTheState(CannotSave|InitialConnecting|Loading))
+ , _suspend_save (0)
+ , _save_queued (false)
, _last_roll_location (0)
, _last_roll_or_reversal_location (0)
, _last_record_location (0)
, routes (new RouteList)
, _adding_routes_in_progress (false)
, destructive_index (0)
+ , _track_number_decimals(1)
, solo_update_disabled (false)
, default_fade_steepness (0)
, default_fade_msecs (0)
throw failed_constructor ();
}
+ /* load default session properties - if any */
+ config.load_state();
+
} else {
if (load_state (_current_snapshot_name)) {
clear_clicks ();
+ /* need to remove auditioner before monitoring section
+ * otherwise it is re-connected */
+ auditioner.reset ();
+
+ /* drop references to routes held by the monitoring section
+ * specifically _monitor_out aux/listen references */
+ remove_monitor_section();
+
/* clear out any pending dead wood from RCU managed objects */
routes.flush ();
/* reset these three references to special routes before we do the usual route delete thing */
- auditioner.reset ();
_master_out.reset ();
_monitor_out.reset ();
failed:
if (!new_routes.empty()) {
+ StateProtector sp (this);
add_routes (new_routes, true, true, true);
if (instrument) {
failed:
if (!new_routes.empty()) {
+ StateProtector sp (this);
add_routes (new_routes, true, true, true);
}
failure:
if (!ret.empty()) {
+ StateProtector sp (this);
add_routes (ret, false, true, true); // autoconnect outputs only
}
out:
if (!ret.empty()) {
+ StateProtector sp (this);
add_routes (ret, true, true, true);
IO::enable_connecting ();
}
save_state (_current_snapshot_name);
}
+ reassign_track_numbers();
+
RouteAdded (new_routes); /* EMIT SIGNAL */
}
}
}
+ /* if the monitoring section had a pointer to this route, remove it */
+ if (_monitor_out && !route->is_master() && !route->is_monitor()) {
+ Glib::Threads::Mutex::Lock lm (AudioEngine::instance()->process_lock ());
+ PBD::Unwinder<bool> uw (ignore_route_processor_changes, true);
+ route->remove_aux_or_listen (_monitor_out);
+ }
+
boost::shared_ptr<MidiTrack> mt = boost::dynamic_pointer_cast<MidiTrack> (route);
if (mt && mt->step_editing()) {
if (_step_editors > 0) {
if (save_state (_current_snapshot_name)) {
save_history (_current_snapshot_name);
}
+ reassign_track_numbers();
}
void
return boost::shared_ptr<Route> ((Route*) 0);
}
+
+void
+Session::reassign_track_numbers ()
+{
+ int64_t tn = 0;
+ int64_t bn = 0;
+ RouteList r (*(routes.reader ()));
+ SignalOrderRouteSorter sorter;
+ r.sort (sorter);
+
+ StateProtector sp (this);
+
+ for (RouteList::iterator i = r.begin(); i != r.end(); ++i) {
+ if (boost::dynamic_pointer_cast<Track> (*i)) {
+ (*i)->set_track_number(++tn);
+ }
+ else if (!(*i)->is_master() && !(*i)->is_monitor() && !(*i)->is_auditioner()) {
+ (*i)->set_track_number(--bn);
+ }
+ }
+ const uint32_t decimals = ceilf (log10f (tn + 1));
+ const bool decimals_changed = _track_number_decimals != decimals;
+ _track_number_decimals = decimals;
+
+ if (decimals_changed && config.get_track_name_number ()) {
+ for (RouteList::iterator i = r.begin(); i != r.end(); ++i) {
+ boost::shared_ptr<Track> t = boost::dynamic_pointer_cast<Track> (*i);
+ if (t) {
+ t->resync_track_name();
+ }
+ }
+ // trigger GUI re-layout
+ config.ParameterChanged("track-name-number");
+ }
+}
+
void
Session::playlist_region_added (boost::weak_ptr<Region> w)
{
}
boost::shared_ptr<AudioFileSource>
-Session::source_by_path_and_channel (const string& path, uint16_t chn) const
+Session::audio_source_by_path_and_channel (const string& path, uint16_t chn) const
{
/* Restricted to audio files because only audio sources have channel
as a property.
}
boost::shared_ptr<MidiSource>
-Session::source_by_path (const std::string& path) const
+Session::midi_source_by_path (const std::string& path) const
{
/* Restricted to MIDI files because audio sources require a channel
for unique identification, in addition to a path.
return cnt;
}
-/** Return the full path (in some session directory) for a new within-session source.
- * \a name must be a session-unique name that does not contain slashes
- * (e.g. as returned by new_*_source_name)
- */
-string
-Session::new_source_path_from_name (DataType type, const string& name)
-{
- assert(name.find("/") == string::npos);
-
- SessionDirectory sdir(get_best_session_directory_for_new_source());
-
- std::string p;
- if (type == DataType::AUDIO) {
- p = sdir.sound_path();
- } else if (type == DataType::MIDI) {
- p = sdir.midi_path();
- } else {
- error << "Unknown source type, unable to create file path" << endmsg;
- return "";
- }
-
- return Glib::build_filename (p, name);
-}
-
string
Session::peak_path (string base) const
{
/** Return a unique name based on \a base for a new internal audio source */
string
-Session::new_audio_source_name (const string& base, uint32_t nchan, uint32_t chan, bool destructive)
+Session::new_audio_source_path (const string& base, uint32_t nchan, uint32_t chan, bool destructive, bool take_required)
{
uint32_t cnt;
- char buf[PATH_MAX+1];
- const uint32_t limit = 10000;
+ string possible_name;
+ const uint32_t limit = 9999; // arbitrary limit on number of files with the same basic name
string legalized;
string ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
+ bool some_related_source_name_exists = false;
- buf[0] = '\0';
+ possible_name[0] = '\0';
legalized = legalize_for_path (base);
// Find a "version" of the base name that doesn't exist in any of the possible directories.
+
for (cnt = (destructive ? ++destructive_index : 1); cnt <= limit; ++cnt) {
vector<space_and_path>::iterator i;
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
- if (destructive) {
-
- if (nchan < 2) {
- snprintf (buf, sizeof(buf), "T%04d-%s%s",
- cnt, legalized.c_str(), ext.c_str());
- } else if (nchan == 2) {
- if (chan == 0) {
- snprintf (buf, sizeof(buf), "T%04d-%s%%L%s",
- cnt, legalized.c_str(), ext.c_str());
- } else {
- snprintf (buf, sizeof(buf), "T%04d-%s%%R%s",
- cnt, legalized.c_str(), ext.c_str());
- }
- } else if (nchan < 26) {
- snprintf (buf, sizeof(buf), "T%04d-%s%%%c%s",
- cnt, legalized.c_str(), 'a' + chan, ext.c_str());
- } else {
- snprintf (buf, sizeof(buf), "T%04d-%s%s",
- cnt, legalized.c_str(), ext.c_str());
- }
+ ostringstream sstr;
+ if (destructive) {
+ sstr << 'T';
+ sstr << setfill ('0') << setw (4) << cnt;
+ sstr << legalized;
} else {
-
- if (nchan < 2) {
- snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
- } else if (nchan == 2) {
- if (chan == 0) {
- snprintf (buf, sizeof(buf), "%s-%u%%L%s", legalized.c_str(), cnt, ext.c_str());
- } else {
- snprintf (buf, sizeof(buf), "%s-%u%%R%s", legalized.c_str(), cnt, ext.c_str());
- }
- } else if (nchan < 26) {
- snprintf (buf, sizeof(buf), "%s-%u%%%c%s", legalized.c_str(), cnt, 'a' + chan, ext.c_str());
- } else {
- snprintf (buf, sizeof(buf), "%s-%u%s", legalized.c_str(), cnt, ext.c_str());
+ sstr << legalized;
+
+ if (take_required || some_related_source_name_exists) {
+ sstr << '-';
+ sstr << cnt;
}
}
+
+ if (nchan == 2) {
+ if (chan == 0) {
+ sstr << "%L";
+ } else {
+ sstr << "%R";
+ }
+ } else if (nchan > 2 && nchan < 26) {
+ sstr << '%';
+ sstr << 'a' + chan;
+ }
- SessionDirectory sdir((*i).path);
+ sstr << ext;
- string spath = sdir.sound_path();
+ possible_name = sstr.str();
+ SessionDirectory sdir((*i).path);
+ const string spath = sdir.sound_path();
/* note that we search *without* the extension so that
we don't end up both "Audio 1-1.wav" and "Audio 1-1.caf"
a file format change.
*/
- if (matching_unsuffixed_filename_exists_in (spath, buf)) {
+ if (matching_unsuffixed_filename_exists_in (spath, possible_name)) {
existing++;
break;
}
* notions of their removability.
*/
- string possible_path = Glib::build_filename (spath, buf);
+ string possible_path = Glib::build_filename (spath, possible_name);
- if (source_by_path (possible_path)) {
+ if (audio_source_by_path_and_channel (possible_path, chan)) {
existing++;
break;
}
break;
}
+ some_related_source_name_exists = true;
+
if (cnt > limit) {
error << string_compose(
_("There are already %1 recordings for %2, which I consider too many."),
}
}
- return Glib::path_get_basename (buf);
-}
+ /* We've established that the new name does not exist in any session
+ * directory, so now find out which one we should use for this new
+ * audio source.
+ */
-/** Create a new within-session audio source */
-boost::shared_ptr<AudioFileSource>
-Session::create_audio_source_for_session (size_t n_chans, string const & n, uint32_t chan, bool destructive)
-{
- const string name = new_audio_source_name (n, n_chans, chan, destructive);
- const string path = new_source_path_from_name(DataType::AUDIO, name);
+ SessionDirectory sdir (get_best_session_directory_for_new_audio());
- return boost::dynamic_pointer_cast<AudioFileSource> (
- SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
+ std::string s = Glib::build_filename (sdir.sound_path(), possible_name);
+
+ return s;
}
/** Return a unique name based on \a owner_name for a new internal MIDI source */
string
-Session::new_midi_source_name (const string& owner_name)
+Session::new_midi_source_path (const string& base)
{
uint32_t cnt;
char buf[PATH_MAX+1];
const uint32_t limit = 10000;
string legalized;
+ string possible_path;
string possible_name;
buf[0] = '\0';
- legalized = legalize_for_path (owner_name);
+ legalized = legalize_for_path (base);
// Find a "version" of the file name that doesn't exist in any of the possible directories.
vector<space_and_path>::iterator i;
uint32_t existing = 0;
-
+
for (i = session_dirs.begin(); i != session_dirs.end(); ++i) {
SessionDirectory sdir((*i).path);
snprintf (buf, sizeof(buf), "%s-%u.mid", legalized.c_str(), cnt);
possible_name = buf;
- std::string possible_path = Glib::build_filename (sdir.midi_path(), possible_name);
+ possible_path = Glib::build_filename (sdir.midi_path(), possible_name);
if (Glib::file_test (possible_path, Glib::FILE_TEST_EXISTS)) {
existing++;
}
- if (source_by_path (possible_path)) {
+ if (midi_source_by_path (possible_path)) {
existing++;
}
}
if (cnt > limit) {
error << string_compose(
_("There are already %1 recordings for %2, which I consider too many."),
- limit, owner_name) << endmsg;
+ limit, base) << endmsg;
destroy ();
- throw failed_constructor();
+ return 0;
}
}
- return possible_name;
+ /* No need to "find best location" for software/app-based RAID, because
+ MIDI is so small that we always put it in the same place.
+ */
+
+ return possible_path;
}
+/** Create a new within-session audio source */
+boost::shared_ptr<AudioFileSource>
+Session::create_audio_source_for_session (size_t n_chans, string const & base, uint32_t chan, bool destructive)
+{
+ const string path = new_audio_source_path (base, n_chans, chan, destructive, true);
+
+ if (!path.empty()) {
+ return boost::dynamic_pointer_cast<AudioFileSource> (
+ SourceFactory::createWritable (DataType::AUDIO, *this, path, destructive, frame_rate()));
+ } else {
+ throw failed_constructor ();
+ }
+}
+
/** Create a new within-session MIDI source */
boost::shared_ptr<MidiSource>
Session::create_midi_source_for_session (string const & basic_name)
{
- std::string name;
-
- if (name.empty()) {
- name = new_midi_source_name (basic_name);
+ const string path = new_midi_source_path (basic_name);
+
+ if (!path.empty()) {
+ return boost::dynamic_pointer_cast<SMFSource> (
+ SourceFactory::createWritable (
+ DataType::MIDI, *this, path, false, frame_rate()));
+ } else {
+ throw failed_constructor ();
}
-
- const string path = new_source_path_from_name (DataType::MIDI, name);
-
- return boost::dynamic_pointer_cast<SMFSource> (
- SourceFactory::createWritable (
- DataType::MIDI, *this, path, false, frame_rate()));
}
/** Create a new within-session MIDI source */
return boost::shared_ptr<MidiSource>();
}
- const string path = new_source_path_from_name (DataType::MIDI, name);
+ const string path = new_midi_source_path (name);
return boost::dynamic_pointer_cast<SMFSource> (
SourceFactory::createWritable (
void
Session::cancel_audition ()
{
+ if (!auditioner) {
+ return;
+ }
if (auditioner->auditioning()) {
auditioner->cancel_audition ();
AuditionActive (false); /* EMIT SIGNAL */
void
Session::ensure_buffers (ChanCount howmany)
{
- BufferManager::ensure_buffers (howmany);
+ BufferManager::ensure_buffers (howmany, bounce_processing() ? bounce_chunk_size : 0);
}
void
bool /*overwrite*/, vector<boost::shared_ptr<Source> >& srcs,
InterThreadInfo& itt,
boost::shared_ptr<Processor> endpoint, bool include_endpoint,
- bool for_export)
+ bool for_export, bool for_freeze)
{
boost::shared_ptr<Region> result;
boost::shared_ptr<Playlist> playlist;
boost::shared_ptr<AudioFileSource> fsource;
- uint32_t x;
- char buf[PATH_MAX+1];
ChanCount diskstream_channels (track.n_channels());
framepos_t position;
framecnt_t this_chunk;
framepos_t to_do;
+ framepos_t latency_skip;
BufferSet buffers;
- SessionDirectory sdir(get_best_session_directory_for_new_source ());
- const string sound_dir = sdir.sound_path();
framepos_t len = end - start;
bool need_block_size_reset = false;
- string ext;
ChanCount const max_proc = track.max_processor_streams ();
+ string legal_playlist_name;
+ string possible_path;
if (end <= start) {
error << string_compose (_("Cannot write a range where end <= start (e.g. %1 <= %2)"),
return result;
}
- const framecnt_t chunk_size = (256 * 1024)/4;
+ diskstream_channels = track.bounce_get_output_streams (diskstream_channels, endpoint,
+ include_endpoint, for_export, for_freeze);
+
+ if (diskstream_channels.n_audio() < 1) {
+ error << _("Cannot write a range with no audio.") << endmsg;
+ return result;
+ }
// block all process callback handling
block_processing ();
+ {
+ // synchronize with AudioEngine::process_callback()
+ // make sure processing is not currently running
+ // and processing_blocked() is honored before
+ // acquiring thread buffers
+ Glib::Threads::Mutex::Lock lm (_engine.process_lock());
+ }
+
+ _bounce_processing_active = true;
+
/* call tree *MUST* hold route_lock */
if ((playlist = track.playlist()) == 0) {
goto out;
}
- ext = native_header_format_extension (config.get_native_file_header_format(), DataType::AUDIO);
+ legal_playlist_name = legalize_for_path (playlist->name());
for (uint32_t chan_n = 0; chan_n < diskstream_channels.n_audio(); ++chan_n) {
- for (x = 0; x < 99999; ++x) {
- snprintf (buf, sizeof(buf), "%s/%s-%d-bounce-%" PRIu32 "%s", sound_dir.c_str(), playlist->name().c_str(), chan_n, x+1, ext.c_str());
- if (!Glib::file_test (buf, Glib::FILE_TEST_EXISTS)) {
- break;
- }
- }
-
- if (x == 99999) {
- error << string_compose (_("too many bounced versions of playlist \"%1\""), playlist->name()) << endmsg;
+ string base_name = string_compose ("%1-%2-bounce", playlist->name(), chan_n);
+ string path = new_audio_source_path (legal_playlist_name, diskstream_channels.n_audio(), chan_n, false, true);
+
+ if (path.empty()) {
goto out;
}
try {
fsource = boost::dynamic_pointer_cast<AudioFileSource> (
- SourceFactory::createWritable (DataType::AUDIO, *this, buf, false, frame_rate()));
+ SourceFactory::createWritable (DataType::AUDIO, *this, path, false, frame_rate()));
}
catch (failed_constructor& err) {
- error << string_compose (_("cannot create new audio file \"%1\" for %2"), buf, track.name()) << endmsg;
+ error << string_compose (_("cannot create new audio file \"%1\" for %2"), path, track.name()) << endmsg;
goto out;
}
*/
need_block_size_reset = true;
- track.set_block_size (chunk_size);
+ track.set_block_size (bounce_chunk_size);
+ _engine.main_thread()->get_buffers ();
position = start;
to_do = len;
+ latency_skip = track.bounce_get_latency (endpoint, include_endpoint, for_export, for_freeze);
/* create a set of reasonably-sized buffers */
- buffers.ensure_buffers (DataType::AUDIO, max_proc.n_audio(), chunk_size);
+ for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
+ buffers.ensure_buffers(*t, max_proc.get(*t), bounce_chunk_size);
+ }
buffers.set_count (max_proc);
for (vector<boost::shared_ptr<Source> >::iterator src = srcs.begin(); src != srcs.end(); ++src) {
while (to_do && !itt.cancel) {
- this_chunk = min (to_do, chunk_size);
+ this_chunk = min (to_do, bounce_chunk_size);
- if (track.export_stuff (buffers, start, this_chunk, endpoint, include_endpoint, for_export)) {
+ if (track.export_stuff (buffers, start, this_chunk, endpoint, include_endpoint, for_export, for_freeze)) {
goto out;
}
+ start += this_chunk;
+ to_do -= this_chunk;
+ itt.progress = (float) (1.0 - ((double) to_do / len));
+
+ if (latency_skip >= bounce_chunk_size) {
+ latency_skip -= bounce_chunk_size;
+ continue;
+ }
+
+ const framecnt_t current_chunk = this_chunk - latency_skip;
+
uint32_t n = 0;
for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) {
boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
if (afs) {
- if (afs->write (buffers.get_audio(n).data(), this_chunk) != this_chunk) {
+ if (afs->write (buffers.get_audio(n).data(latency_skip), current_chunk) != current_chunk) {
goto out;
}
}
}
+ latency_skip = 0;
+ }
- start += this_chunk;
- to_do -= this_chunk;
+ /* post-roll, pick up delayed processor output */
+ latency_skip = track.bounce_get_latency (endpoint, include_endpoint, for_export, for_freeze);
- itt.progress = (float) (1.0 - ((double) to_do / len));
+ while (latency_skip && !itt.cancel) {
+ this_chunk = min (latency_skip, bounce_chunk_size);
+ latency_skip -= this_chunk;
+
+ buffers.silence (this_chunk, 0);
+ track.bounce_process (buffers, start, this_chunk, endpoint, include_endpoint, for_export, for_freeze);
+
+ uint32_t n = 0;
+ for (vector<boost::shared_ptr<Source> >::iterator src=srcs.begin(); src != srcs.end(); ++src, ++n) {
+ boost::shared_ptr<AudioFileSource> afs = boost::dynamic_pointer_cast<AudioFileSource>(*src);
+ if (afs) {
+ if (afs->write (buffers.get_audio(n).data(), this_chunk) != this_chunk) {
+ goto out;
+ }
+ }
+ }
}
if (!itt.cancel) {
}
}
+ _bounce_processing_active = false;
if (need_block_size_reset) {
+ _engine.main_thread()->drop_buffers ();
track.set_block_size (get_block_size());
}
DEBUG_TRACE (DEBUG::OrderKeys, "Sync Order Keys.\n");
+ reassign_track_numbers();
+
Route::SyncOrderKeys (); /* EMIT SIGNAL */
DEBUG_TRACE (DEBUG::OrderKeys, "\tsync done\n");
*/
+#include <glib.h>
+#include <glib/gstdio.h> /* for g_stat() */
+#include <glibmm/miscutils.h> /* for build_filename() */
+
+#include "pbd/file_utils.h"
#include "pbd/pathexpand.h"
#include "ardour/types.h"
+#include "ardour/filesystem_paths.h"
#include "ardour/session_configuration.h"
#include "i18n.h"
#undef CONFIG_VARIABLE
#undef CONFIG_VARIABLE_SPECIAL
}
+
+
+bool
+SessionConfiguration::load_state ()
+{
+ std::string rcfile;
+ GStatBuf statbuf;
+ if (find_file (ardour_config_search_path(), "session.rc", rcfile)) {
+ if (g_stat (rcfile.c_str(), &statbuf)) {
+ return false;
+ }
+ if (statbuf.st_size == 0) {
+ return false;
+ }
+ XMLTree tree;
+ if (!tree.read (rcfile.c_str())) {
+ error << string_compose(_("%1: cannot part default session options \"%2\""), PROGRAM_NAME, rcfile) << endmsg;
+ return false;
+ }
+
+ XMLNode& root (*tree.root());
+ if (root.name() != X_("SessionDefaults")) {
+ warning << _("Invalid session default XML Root.") << endmsg;
+ return false;
+ }
+
+ XMLNode* node;
+ if (((node = find_named_node (root, X_("Config"))) != 0)) {
+ LocaleGuard lg (X_("POSIX"));
+ set_variables(*node);
+ info << _("Loaded custom session defaults.") << endmsg;
+ } else {
+ warning << _("Found no session defaults in XML file.") << endmsg;
+ return false;
+ }
+
+ /* CUSTOM OVERRIDES */
+ set_audio_search_path("");
+ set_midi_search_path("");
+ set_raid_path("");
+ }
+ return true;
+}
+
+bool
+SessionConfiguration::save_state ()
+{
+ const std::string rcfile = Glib::build_filename (user_config_directory(), "session.rc");
+ if (rcfile.empty()) {
+ return false;
+ }
+
+ XMLTree tree;
+ XMLNode* root = new XMLNode(X_("SessionDefaults"));
+ root->add_child_nocopy (get_variables ());
+ tree.set_root (root);
+
+ if (!tree.write (rcfile.c_str())) {
+ error << _("Could not save session options") << endmsg;
+ return false;
+ }
+
+ return true;
+}
msg[4] = 0x1;
msg[9] = 0xf7;
- msg[5] = mtc_timecode_bits | timecode.hours;
+ msg[5] = mtc_timecode_bits | (timecode.hours % 24);
msg[6] = timecode.minutes;
msg[7] = timecode.seconds;
msg[8] = timecode.frames;
#include "pbd/error.h"
#include "pbd/file_utils.h"
#include "pbd/pathexpand.h"
-#include "pbd/pathscanner.h"
#include "pbd/pthread_utils.h"
#include "pbd/stacktrace.h"
#include "pbd/convert.h"
-#include "pbd/clear_dir.h"
#include "pbd/localtime_r.h"
#include "ardour/amp.h"
return 1;
}
+ if (g_atomic_int_get(&_suspend_save)) {
+ _save_queued = true;
+ return 1;
+ }
+ _save_queued = false;
+
if (!_engine.connected ()) {
error << string_compose (_("the %1 audio engine is not connected and state saving would lose all I/O connections. Session not saved"),
PROGRAM_NAME)
return 0;
}
+bool
+Session::save_default_options ()
+{
+ return config.save_state();
+}
+
XMLNode&
Session::get_state()
{
ret = track;
} else {
- boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
+ enum Route::Flag flags = Route::Flag(0);
+ const XMLProperty* prop = node.property("flags");
+ if (prop) {
+ flags = Route::Flag (string_2_enum (prop->value(), flags));
+ }
+
+ boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
if (r->init () == 0 && r->set_state (node, version) == 0) {
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
ret = track;
} else {
- boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML")));
+ enum Route::Flag flags = Route::Flag(0);
+ const XMLProperty* prop = node.property("flags");
+ if (prop) {
+ flags = Route::Flag (string_2_enum (prop->value(), flags));
+ }
+
+ boost::shared_ptr<Route> r (new Route (*this, X_("toBeResetFroXML"), flags));
if (r->init () == 0 && r->set_state (node, version) == 0) {
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
return *node;
}
-string
-Session::path_from_region_name (DataType type, string name, string identifier)
-{
- char buf[PATH_MAX+1];
- uint32_t n;
- SessionDirectory sdir(get_best_session_directory_for_new_source());
- std::string source_dir = ((type == DataType::AUDIO)
- ? sdir.sound_path() : sdir.midi_path());
-
- string ext = native_header_format_extension (config.get_native_file_header_format(), type);
-
- for (n = 0; n < 999999; ++n) {
- if (identifier.length()) {
- snprintf (buf, sizeof(buf), "%s%s%" PRIu32 "%s", name.c_str(),
- identifier.c_str(), n, ext.c_str());
- } else {
- snprintf (buf, sizeof(buf), "%s-%" PRIu32 "%s", name.c_str(),
- n, ext.c_str());
- }
-
- std::string source_path = Glib::build_filename (source_dir, buf);
-
- if (!Glib::file_test (source_path, Glib::FILE_TEST_EXISTS)) {
- return source_path;
- }
- }
-
- error << string_compose (_("cannot create new file from region name \"%1\" with ident = \"%2\": too many existing files with similar names"),
- name, identifier)
- << endmsg;
-
- return "";
-}
-
-
int
Session::load_sources (const XMLNode& node)
{
}
string
-Session::get_best_session_directory_for_new_source ()
+Session::get_best_session_directory_for_new_audio ()
{
vector<space_and_path>::iterator i;
string result = _session_dir->root_path();
str.find (statefile_suffix) == (str.length() - strlen (statefile_suffix)));
}
-struct string_cmp {
- bool operator()(const string* a, const string* b) {
- return *a < *b;
- }
-};
-
-static string*
-remove_end(string* state)
+static string
+remove_end(string state)
{
- string statename(*state);
+ string statename(state);
string::size_type start,end;
if ((start = statename.find_last_of (G_DIR_SEPARATOR)) != string::npos) {
end = statename.length();
}
- return new string(statename.substr (0, end));
+ return string(statename.substr (0, end));
}
-vector<string *> *
+vector<string>
Session::possible_states (string path)
{
- PathScanner scanner;
- vector<string*>* states = scanner (path, state_file_filter, 0, false, false);
+ vector<string> states;
+ find_files_matching_filter (states, path, state_file_filter, 0, false, false);
- transform(states->begin(), states->end(), states->begin(), remove_end);
+ transform(states.begin(), states.end(), states.begin(), remove_end);
- string_cmp cmp;
- sort (states->begin(), states->end(), cmp);
+ sort (states.begin(), states.end());
return states;
}
-vector<string *> *
+vector<string>
Session::possible_states () const
{
return possible_states(_path);
int
Session::find_all_sources_across_snapshots (set<string>& result, bool exclude_this_snapshot)
{
- PathScanner scanner;
- vector<string*>* state_files;
+ vector<string> state_files;
string ripped;
string this_snapshot_path;
ripped = ripped.substr (0, ripped.length() - 1);
}
- state_files = scanner (ripped, accept_all_state_files, (void *) 0, true, true);
+ find_files_matching_filter (state_files, ripped, accept_all_state_files, (void *) 0, true, true);
- if (state_files == 0) {
+ if (state_files.empty()) {
/* impossible! */
return 0;
}
this_snapshot_path += legalize_for_path (_current_snapshot_name);
this_snapshot_path += statefile_suffix;
- for (vector<string*>::iterator i = state_files->begin(); i != state_files->end(); ++i) {
+ for (vector<string>::iterator i = state_files.begin(); i != state_files.end(); ++i) {
- if (exclude_this_snapshot && **i == this_snapshot_path) {
+ if (exclude_this_snapshot && *i == this_snapshot_path) {
continue;
}
- if (find_all_sources (**i, result) < 0) {
+ if (find_all_sources (*i, result) < 0) {
return -1;
}
}
// FIXME: needs adaptation to midi
vector<boost::shared_ptr<Source> > dead_sources;
- PathScanner scanner;
string audio_path;
string midi_path;
- vector<space_and_path>::iterator i;
- vector<space_and_path>::iterator nexti;
- vector<string*>* candidates;
- vector<string*>* candidates2;
+ vector<string> candidates;
vector<string> unused;
set<string> all_sources;
bool used;
int ret = -1;
string tmppath1;
string tmppath2;
+ Searchpath asp;
+ Searchpath msp;
_state_of_the_state = (StateOfTheState) (_state_of_the_state | InCleanup);
/* build a list of all the possible audio directories for the session */
- for (i = session_dirs.begin(); i != session_dirs.end(); ) {
-
- nexti = i;
- ++nexti;
-
+ for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
SessionDirectory sdir ((*i).path);
- audio_path += sdir.sound_path();
-
- if (nexti != session_dirs.end()) {
- audio_path += G_SEARCHPATH_SEPARATOR;
- }
-
- i = nexti;
+ asp += sdir.sound_path();
}
+ audio_path += asp.to_string();
/* build a list of all the possible midi directories for the session */
- for (i = session_dirs.begin(); i != session_dirs.end(); ) {
-
- nexti = i;
- ++nexti;
-
+ for (vector<space_and_path>::const_iterator i = session_dirs.begin(); i != session_dirs.end(); ++i) {
SessionDirectory sdir ((*i).path);
- midi_path += sdir.midi_path();
-
- if (nexti != session_dirs.end()) {
- midi_path += G_SEARCHPATH_SEPARATOR;
- }
-
- i = nexti;
+ msp += sdir.midi_path();
}
+ midi_path += msp.to_string();
- candidates = scanner (audio_path, accept_all_audio_files, (void *) 0, true, true);
- candidates2 = scanner (midi_path, accept_all_midi_files, (void *) 0, true, true);
-
- /* merge them */
-
- if (candidates) {
- if (candidates2) {
- for (vector<string*>::iterator i = candidates2->begin(); i != candidates2->end(); ++i) {
- candidates->push_back (*i);
- }
- delete candidates2;
- }
- } else {
- candidates = candidates2; // might still be null
- }
+ find_files_matching_filter (candidates, audio_path, accept_all_audio_files, (void *) 0, true, true);
+ find_files_matching_filter (candidates, midi_path, accept_all_midi_files, (void *) 0, true, true);
/* find all sources, but don't use this snapshot because the
state file on disk still references sources we may have already
i = tmp;
}
- if (candidates) {
- for (vector<string*>::iterator x = candidates->begin(); x != candidates->end(); ++x) {
+ for (vector<string>::iterator x = candidates.begin(); x != candidates.end(); ++x) {
- used = false;
- spath = **x;
+ used = false;
+ spath = *x;
- for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
-
- tmppath1 = canonical_path (spath);
- tmppath2 = canonical_path ((*i));
-
- if (tmppath1 == tmppath2) {
- used = true;
- break;
- }
- }
+ for (set<string>::iterator i = all_sources.begin(); i != all_sources.end(); ++i) {
- if (!used) {
- unused.push_back (spath);
- }
+ tmppath1 = canonical_path (spath);
+ tmppath2 = canonical_path ((*i));
- delete *x;
- }
+ if (tmppath1 == tmppath2) {
+ used = true;
+ break;
+ }
+ }
- delete candidates;
- }
+ if (!used) {
+ unused.push_back (spath);
+ }
+ }
/* now try to move all unused files into the "dead" directory(ies) */
get_state_files_in_directory (const std::string & directory_path,
vector<std::string> & result)
{
- Glib::PatternSpec state_file_pattern('*' + string(statefile_suffix));
-
- find_matching_files_in_directory (directory_path, state_file_pattern,
- result);
+ find_files_matching_pattern (result, directory_path,
+ '*' + string(statefile_suffix));
}
vector<string>
if (did_record) {
commit_reversible_command ();
+ /* increase take name */
+ if (config.get_track_name_take () && !config.get_take_name ().empty()) {
+ string newname = config.get_take_name();
+ config.set_take_name(bump_name_number (newname));
+ }
}
if (_engine.running()) {
#include <errno.h>
#include <regex.h>
-#include "pbd/pathscanner.h"
#include "pbd/stl_delete.h"
#include "pbd/strsplit.h"
#include <glibmm/fileutils.h>
#include "evoral/Control.hpp"
+#include "evoral/SMF.hpp"
#include "ardour/event_type_map.h"
#include "ardour/midi_model.h"
using namespace ARDOUR;
using namespace Glib;
using namespace PBD;
+using namespace Evoral;
/** Constructor used for new internal-to-session files. File cannot exist. */
SMFSource::SMFSource (Session& s, const string& path, Source::Flag flags)
_open = true;
}
+/** Constructor used for external-to-session files. File must exist. */
+SMFSource::SMFSource (Session& s, const string& path)
+ : Source(s, DataType::MIDI, path, Source::Flag (0))
+ , MidiSource(s, path, Source::Flag (0))
+ , FileSource(s, DataType::MIDI, path, string(), Source::Flag (0))
+ , Evoral::SMF()
+ , _last_ev_time_beats(0.0)
+ , _last_ev_time_frames(0)
+ , _smf_last_read_end (0)
+ , _smf_last_read_time (0)
+{
+ /* note that origin remains empty */
+
+ if (init (_path, false)) {
+ throw failed_constructor ();
+ }
+
+ assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
+ existence_check ();
+
+ /* file is not opened until write */
+
+ if (_flags & Writable) {
+ return;
+ }
+
+ if (open (_path)) {
+ throw failed_constructor ();
+ }
+
+ _open = true;
+}
+
/** Constructor used for existing internal-to-session files. */
SMFSource::SMFSource (Session& s, const XMLNode& node, bool must_exist)
: Source(s, node)
mark_nonremovable ();
}
+bool
+SMFSource::valid_midi_file (const string& file)
+{
+ if (safe_midi_file_extension (file) ) {
+ return (SMF::test (file) );
+ }
+ return false;
+}
+
bool
SMFSource::safe_midi_file_extension (const string& file)
{
_flags = Flag (_flags & ~(Removable|RemovableIfEmpty|RemoveAtDestroy));
}
-int
-SMFSource::rename (const string& newname)
-{
- Glib::Threads::Mutex::Lock lm (_lock);
- string oldpath = _path;
- string newpath = _session.new_source_path_from_name (DataType::MIDI, newname);
-
- if (newpath.empty()) {
- error << string_compose (_("programming error: %1"), "cannot generate a changed file path") << endmsg;
- return -1;
- }
-
- // Test whether newpath exists, if yes notify the user but continue.
- if (Glib::file_test (newpath, Glib::FILE_TEST_EXISTS)) {
- error << string_compose (_("Programming error! %1 tried to rename a file over another file! It's safe to continue working, but please report this to the developers."), PROGRAM_NAME) << endmsg;
- return -1;
- }
-
- if (Glib::file_test (oldpath.c_str(), Glib::FILE_TEST_EXISTS)) {
- /* rename only needed if file exists on disk */
- if (::rename (oldpath.c_str(), newpath.c_str()) != 0) {
- error << string_compose (_("cannot rename file %1 to %2 (%3)"), oldpath, newpath, strerror(errno)) << endmsg;
- return -1;
- }
- }
-
- _name = Glib::path_get_basename (newpath);
- _path = newpath;
-
- return 0;
-}
}
}
+/** Constructor to be called for recovering files being used for
+ * capture. They are in-session, they already exist, they should not
+ * be writable. They are an odd hybrid (from a constructor point of
+ * view) of the previous two constructors.
+ */
+SndFileSource::SndFileSource (Session& s, const string& path, int chn)
+ : Source (s, DataType::AUDIO, path, Flag (0))
+ /* the final boolean argument is not used, its value is irrelevant. see audiofilesource.h for explanation */
+ , AudioFileSource (s, path, Flag (0))
+ , _descriptor (0)
+ , _broadcast_info (0)
+ , _capture_start (false)
+ , _capture_end (false)
+ , file_pos (0)
+ , xfade_buf (0)
+{
+ _channel = chn;
+
+ init_sndfile ();
+
+ assert (Glib::file_test (_path, Glib::FILE_TEST_EXISTS));
+ existence_check ();
+
+ if (open()) {
+ throw failed_constructor ();
+ }
+}
+
void
SndFileSource::init_sndfile ()
{
delete _broadcast_info;
_broadcast_info = 0;
_flags = Flag (_flags & ~Broadcast);
+ }
+
+ /* Set the broadcast flag if the BWF info is already there. We need
+ * this when recovering or using existing files.
+ */
+
+ if (bwf_info_exists) {
+ _flags = Flag (_flags | Broadcast);
}
if (writable()) {
--- /dev/null
+/* soundcloud_export.cpp **********************************************************************
+
+ Adapted for Ardour by Ben Loftis, March 2012
+
+ Licence GPL:
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+
+*************************************************************************************/
+#include "ardour/debug.h"
+#include "ardour/soundcloud_upload.h"
+
+#include "pbd/xml++.h"
+#include <pbd/error.h>
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <iostream>
+#include <glib/gstdio.h>
+
+#include "i18n.h"
+
+using namespace PBD;
+
+size_t
+WriteMemoryCallback(void *ptr, size_t size, size_t nmemb, void *data)
+{
+ register int realsize = (int)(size * nmemb);
+ struct MemoryStruct *mem = (struct MemoryStruct *)data;
+
+ mem->memory = (char *)realloc(mem->memory, mem->size + realsize + 1);
+
+ if (mem->memory) {
+ memcpy(&(mem->memory[mem->size]), ptr, realsize);
+ mem->size += realsize;
+ mem->memory[mem->size] = 0;
+ }
+ return realsize;
+}
+
+SoundcloudUploader::SoundcloudUploader()
+{
+ curl_handle = curl_easy_init();
+ multi_handle = curl_multi_init();
+}
+
+std::string
+SoundcloudUploader::Get_Auth_Token( std::string username, std::string password )
+{
+ struct MemoryStruct xml_page;
+ xml_page.memory = NULL;
+ xml_page.size = 0;
+
+ setcUrlOptions();
+
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+ struct curl_httppost *formpost=NULL;
+ struct curl_httppost *lastptr=NULL;
+
+ /* Fill in the filename field */
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "client_id",
+ CURLFORM_COPYCONTENTS, "6dd9cf0ad281aa57e07745082cec580b",
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "client_secret",
+ CURLFORM_COPYCONTENTS, "53f5b0113fb338800f8a7a9904fc3569",
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "grant_type",
+ CURLFORM_COPYCONTENTS, "password",
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "username",
+ CURLFORM_COPYCONTENTS, username.c_str(),
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "password",
+ CURLFORM_COPYCONTENTS, password.c_str(),
+ CURLFORM_END);
+
+ struct curl_slist *headerlist=NULL;
+ headerlist = curl_slist_append(headerlist, "Expect:");
+ headerlist = curl_slist_append(headerlist, "Accept: application/xml");
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+
+ /* what URL that receives this POST */
+ std::string url = "https://api.soundcloud.com/oauth2/token";
+ curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+ // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+ // perform online request
+ CURLcode res = curl_easy_perform(curl_handle);
+ if (res != 0) {
+ DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("curl error %1 (%2)", res, curl_easy_strerror(res) ) );
+ return "";
+ }
+
+ if (xml_page.memory){
+ // cheesy way to parse the json return value. find access_token, then advance 3 quotes
+
+ if ( strstr ( xml_page.memory , "access_token" ) == NULL) {
+ error << _("Upload to Soundcloud failed. Perhaps your email or password are incorrect?\n") << endmsg;
+ return "";
+ }
+
+ std::string token = strtok( xml_page.memory, "access_token" );
+ token = strtok( NULL, "\"" );
+ token = strtok( NULL, "\"" );
+ token = strtok( NULL, "\"" );
+
+ free( xml_page.memory );
+ return token;
+ }
+
+ return "";
+}
+
+int
+SoundcloudUploader::progress_callback(void *caller, double dltotal, double dlnow, double ultotal, double ulnow)
+{
+ SoundcloudUploader *scu = (SoundcloudUploader *) caller;
+ DEBUG_TRACE (DEBUG::Soundcloud, string_compose ("%1: uploaded %2 of %3", scu->title, ulnow, ultotal) );
+ scu->caller->SoundcloudProgress(ultotal, ulnow, scu->title); /* EMIT SIGNAL */
+ return 0;
+}
+
+
+std::string
+SoundcloudUploader::Upload(std::string file_path, std::string title, std::string token, bool ispublic, bool downloadable, ARDOUR::ExportHandler *caller)
+{
+ int still_running;
+
+ struct MemoryStruct xml_page;
+ xml_page.memory = NULL;
+ xml_page.size = 0;
+
+ setcUrlOptions();
+
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *) &xml_page);
+
+ struct curl_httppost *formpost=NULL;
+ struct curl_httppost *lastptr=NULL;
+
+ /* Fill in the file upload field. This makes libcurl load data from
+ the given file name when curl_easy_perform() is called. */
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "track[asset_data]",
+ CURLFORM_FILE, file_path.c_str(),
+ CURLFORM_END);
+
+ /* Fill in the filename field */
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "oauth_token",
+ CURLFORM_COPYCONTENTS, token.c_str(),
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "track[title]",
+ CURLFORM_COPYCONTENTS, title.c_str(),
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "track[sharing]",
+ CURLFORM_COPYCONTENTS, ispublic ? "public" : "private",
+ CURLFORM_END);
+
+ curl_formadd(&formpost,
+ &lastptr,
+ CURLFORM_COPYNAME, "track[downloadable]",
+ CURLFORM_COPYCONTENTS, downloadable ? "true" : "false",
+ CURLFORM_END);
+
+
+
+ /* initalize custom header list (stating that Expect: 100-continue is not
+ wanted */
+ struct curl_slist *headerlist=NULL;
+ static const char buf[] = "Expect:";
+ headerlist = curl_slist_append(headerlist, buf);
+
+
+ if (curl_handle && multi_handle) {
+
+ /* what URL that receives this POST */
+ std::string url = "https://api.soundcloud.com/tracks";
+ curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+ // curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
+
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, headerlist);
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPPOST, formpost);
+
+ this->title = title; // save title to show in progress bar
+ this->caller = caller;
+
+ curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 0); // turn on the progress bar
+ curl_easy_setopt (curl_handle, CURLOPT_PROGRESSFUNCTION, &SoundcloudUploader::progress_callback);
+ curl_easy_setopt (curl_handle, CURLOPT_PROGRESSDATA, this);
+
+ curl_multi_add_handle(multi_handle, curl_handle);
+
+ curl_multi_perform(multi_handle, &still_running);
+
+
+ while(still_running) {
+ struct timeval timeout;
+ int rc; /* select() return code */
+
+ fd_set fdread;
+ fd_set fdwrite;
+ fd_set fdexcep;
+ int maxfd = -1;
+
+ long curl_timeo = -1;
+
+ FD_ZERO(&fdread);
+ FD_ZERO(&fdwrite);
+ FD_ZERO(&fdexcep);
+
+ /* set a suitable timeout to play around with */
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+
+ curl_multi_timeout(multi_handle, &curl_timeo);
+ if(curl_timeo >= 0) {
+ timeout.tv_sec = curl_timeo / 1000;
+ if(timeout.tv_sec > 1)
+ timeout.tv_sec = 1;
+ else
+ timeout.tv_usec = (curl_timeo % 1000) * 1000;
+ }
+
+ /* get file descriptors from the transfers */
+ curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
+
+ /* In a real-world program you OF COURSE check the return code of the
+ function calls. On success, the value of maxfd is guaranteed to be
+ greater or equal than -1. We call select(maxfd + 1, ...), specially in
+ case of (maxfd == -1), we call select(0, ...), which is basically equal
+ to sleep. */
+
+ rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
+
+ switch(rc) {
+ case -1:
+ /* select error */
+ break;
+ case 0:
+ default:
+ /* timeout or readable/writable sockets */
+ curl_multi_perform(multi_handle, &still_running);
+ break;
+ }
+ }
+
+ /* then cleanup the formpost chain */
+ curl_formfree(formpost);
+
+ /* free slist */
+ curl_slist_free_all (headerlist);
+ }
+
+ curl_easy_setopt (curl_handle, CURLOPT_NOPROGRESS, 1); // turn off the progress bar
+
+ if(xml_page.memory){
+
+ DEBUG_TRACE (DEBUG::Soundcloud, xml_page.memory);
+
+ XMLTree doc;
+ doc.read_buffer( xml_page.memory );
+ XMLNode *root = doc.root();
+
+ if (!root) {
+ DEBUG_TRACE (DEBUG::Soundcloud, "no root XML node!");
+ return "";
+ }
+
+ XMLNode *url_node = root->child("permalink-url");
+ if (!url_node) {
+ DEBUG_TRACE (DEBUG::Soundcloud, "no child node \"permalink-url\" found!");
+ return "";
+ }
+
+ XMLNode *text_node = url_node->child("text");
+ if (!text_node) {
+ DEBUG_TRACE (DEBUG::Soundcloud, "no text node found!");
+ return "";
+ }
+
+ free( xml_page.memory );
+ return text_node->content();
+ }
+
+ return "";
+};
+
+
+SoundcloudUploader:: ~SoundcloudUploader()
+{
+ curl_easy_cleanup(curl_handle);
+ curl_multi_cleanup(multi_handle);
+}
+
+
+void
+SoundcloudUploader::setcUrlOptions()
+{
+ // basic init for curl
+ curl_global_init(CURL_GLOBAL_ALL);
+ // some servers don't like requests that are made without a user-agent field, so we provide one
+ curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
+ // setup curl error buffer
+ curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errorBuffer);
+ // Allow redirection
+ curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1);
+
+ // Allow connections to time out (without using signals)
+ curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1);
+ curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 30);
+
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0);
+ curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
+}
+
} else if (type == DataType::MIDI) {
- boost::shared_ptr<SMFSource> src (new SMFSource (s, path, SMFSource::Flag(0)));
+ boost::shared_ptr<SMFSource> src (new SMFSource (s, path));
src->load_model (true, true);
#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
// boost_debug_shared_ptr_mark_interesting (src, "Source");
return boost::shared_ptr<Source> ();
}
+boost::shared_ptr<Source>
+SourceFactory::createForRecovery (DataType type, Session& s, const std::string& path, int chn)
+{
+ /* this might throw failed_constructor(), which is OK */
+
+ if (type == DataType::AUDIO) {
+ Source* src = new SndFileSource (s, path, chn);
+
+#ifdef BOOST_SP_ENABLE_DEBUG_HOOKS
+ // boost_debug_shared_ptr_mark_interesting (src, "Source");
+#endif
+ boost::shared_ptr<Source> ret (src);
+
+ if (setup_peakfile (ret, false)) {
+ return boost::shared_ptr<Source>();
+ }
+
+ // no analysis data - this is still basically a new file (we
+ // crashed while recording.
+
+ // always announce these files
+
+ SourceCreated (ret);
+
+ return ret;
+
+ } else if (type == DataType::MIDI) {
+ error << _("Recovery attempted on a MIDI file - not implemented") << endmsg;
+ }
+
+ return boost::shared_ptr<Source> ();
+}
+
boost::shared_ptr<Source>
SourceFactory::createFromPlaylist (DataType type, Session& s, boost::shared_ptr<Playlist> p, const PBD::ID& orig, const std::string& name,
uint32_t chn, frameoffset_t start, framecnt_t len, bool copy, bool defer_peaks)
*/
#include <glibmm/miscutils.h>
-#include "pbd/pathscanner.h"
#include "pbd/file_utils.h"
#include "pbd/error.h"
return NULL;
#else
std::string vfork_exec_wrapper;
- if (!PBD::find_file_in_search_path (
+ if (!PBD::find_file (
PBD::Searchpath(Glib::build_filename(ARDOUR::ardour_dll_directory(), "vfork")),
"ardour-exec-wrapper", vfork_exec_wrapper)) {
PBD::warning << "vfork exec wrapper not found..'" << endmsg;
#endif
}
+SystemExec::SystemExec (std::string c, const std::map<char, std::string> subs)
+ : PBD::SystemExec(c, subs)
+{
+#ifndef PLATFORM_WINDOWS
+ if (!_vfork_exec_wrapper) {
+ _vfork_exec_wrapper = vfork_exec_wrapper_path();
+ }
+#endif
+}
+
SystemExec::~SystemExec() { }
#include <glibmm.h>
#include "pbd/basename.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
+#include "pbd/stl_delete.h"
#include "pbd/xml++.h"
#include "ardour/template_utils.h"
void
find_session_templates (vector<TemplateInfo>& template_names)
{
- vector<string *> *templates;
- PathScanner scanner;
- Searchpath spath (template_search_path());
+ vector<string> templates;
- templates = scanner (spath.to_string(), template_filter, 0, true, true);
+ find_files_matching_filter (templates, template_search_path(), template_filter, 0, true, true);
- if (!templates) {
- cerr << "Found nothing along " << spath.to_string() << endl;
+ if (templates.empty()) {
+ cerr << "Found nothing along " << template_search_path().to_string() << endl;
return;
}
- cerr << "Found " << templates->size() << " along " << spath.to_string() << endl;
+ cerr << "Found " << templates.size() << " along " << template_search_path().to_string() << endl;
- for (vector<string*>::iterator i = templates->begin(); i != templates->end(); ++i) {
- string file = session_template_dir_to_file (**i);
+ for (vector<string>::iterator i = templates.begin(); i != templates.end(); ++i) {
+ string file = session_template_dir_to_file (*i);
XMLTree tree;
TemplateInfo rti;
- rti.name = basename_nosuffix (**i);
- rti.path = **i;
+ rti.name = basename_nosuffix (*i);
+ rti.path = *i;
template_names.push_back (rti);
}
-
- delete templates;
}
void
find_route_templates (vector<TemplateInfo>& template_names)
{
- vector<string *> *templates;
- PathScanner scanner;
- Searchpath spath (route_template_search_path());
+ vector<string> templates;
- templates = scanner (spath.to_string(), route_template_filter, 0, false, true);
+ find_files_matching_filter (templates, route_template_search_path(), route_template_filter, 0, false, true);
- if (!templates) {
+ if (templates.empty()) {
return;
}
- for (vector<string*>::iterator i = templates->begin(); i != templates->end(); ++i) {
- string fullpath = *(*i);
+ for (vector<string>::iterator i = templates.begin(); i != templates.end(); ++i) {
+ string fullpath = *i;
XMLTree tree;
template_names.push_back (rti);
}
-
- delete templates;
}
}
TempoSection* ts;
MeterSection* ms;
double beat_frames;
+ double current_frame_exact;
framepos_t bar_start_frame;
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Extend map to %1 from %2 = %3\n", end, current, current_frame));
}
beat_frames = meter->frames_per_grid (*tempo,_frame_rate);
+ current_frame_exact = current_frame;
while (current_frame < end) {
current.beats++;
- current_frame += beat_frames;
+ current_frame_exact += beat_frames;
+ current_frame = llrint(current_frame_exact);
if (current.beats > meter->divisions_per_bar()) {
current.bars++;
tempo->start(), current_frame, tempo->bar_offset()));
/* back up to previous beat */
- current_frame -= beat_frames;
+ current_frame_exact -= beat_frames;
+ current_frame = llrint(current_frame_exact);
/* set tempo section location
* based on offset from last
double offset_within_old_beat = (tempo->frame() - current_frame) / beat_frames;
- current_frame += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+ current_frame_exact += (offset_within_old_beat * beat_frames) + ((1.0 - offset_within_old_beat) * next_beat_frames);
+ current_frame = llrint(current_frame_exact);
/* next metric doesn't have to
* match this precisely to
if (current.beats == 1) {
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Bar at %1|1 @ %2\n", current.bars, current_frame));
- _map.push_back (BBTPoint (*meter, *tempo,(framepos_t) llrint(current_frame), current.bars, 1));
+ _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, 1));
bar_start_frame = current_frame;
} else {
DEBUG_TRACE (DEBUG::TempoMath, string_compose ("Add Beat at %1|%2 @ %3\n", current.bars, current.beats, current_frame));
- _map.push_back (BBTPoint (*meter, *tempo, (framepos_t) llrint(current_frame), current.bars, current.beats));
+ _map.push_back (BBTPoint (*meter, *tempo, current_frame, current.bars, current.beats));
}
if (next_metric == metrics.end()) {
std::string test_file_path;
const string test_filename = "test.wav";
- CPPUNIT_ASSERT (find_file_in_search_path (test_search_path (), test_filename, test_file_path));
+ CPPUNIT_ASSERT (find_file (test_search_path (), test_filename, test_file_path));
boost::shared_ptr<SndFileImportableSource> s (new SndFileImportableSource (test_file_path));
ResampledImportableSource r (s, 48000, SrcBest);
}
void
-ThreadBuffers::ensure_buffers (ChanCount howmany)
+ThreadBuffers::ensure_buffers (ChanCount howmany, size_t custom)
{
// std::cerr << "ThreadBuffers " << this << " resize buffers with count = " << howmany << std::endl;
for (DataType::iterator t = DataType::begin(); t != DataType::end(); ++t) {
size_t count = std::max (scratch_buffers->available().get(*t), howmany.get(*t));
- size_t size = _engine->raw_buffer_size (*t) / sizeof (Sample);
+ size_t size;
+ if (custom > 0) {
+ size = custom;
+ } else {
+ size = (*t == DataType::MIDI)
+ ? _engine->raw_buffer_size (*t)
+ : _engine->raw_buffer_size (*t) / sizeof (Sample);
+ }
scratch_buffers->ensure_buffers (*t, count, size);
mix_buffers->ensure_buffers (*t, count, size);
route_buffers->ensure_buffers (*t, count, size);
}
- size_t audio_buffer_size = _engine->raw_buffer_size (DataType::AUDIO) / sizeof (Sample);
+ size_t audio_buffer_size = custom > 0 ? custom : _engine->raw_buffer_size (DataType::AUDIO) / sizeof (Sample);
delete [] gain_automation_buffer;
gain_automation_buffer = new gain_t[audio_buffer_size];
/* don't add rec_enable_control to controls because we don't want it to
* appear as an automatable parameter
*/
+ track_number_changed.connect_same_thread (*this, boost::bind (&Track::resync_track_name, this));
+ _session.config.ParameterChanged.connect_same_thread (*this, boost::bind (&Track::parameter_changed, this, _1));
- return 0;
+ return 0;
}
void
_rec_enable_control->Changed ();
}
+void
+Track::parameter_changed (string const & p)
+{
+ if (p == "track-name-number") {
+ resync_track_name ();
+ }
+ else if (p == "track-name-take") {
+ resync_track_name ();
+ }
+ else if (p == "take-name") {
+ if (_session.config.get_track_name_take()) {
+ resync_track_name ();
+ }
+ }
+}
+
+void
+Track::resync_track_name ()
+{
+ set_name(name());
+}
+
bool
Track::set_name (const string& str)
{
return false;
}
+ string diskstream_name = "";
+ if (_session.config.get_track_name_take () && !_session.config.get_take_name ().empty()) {
+ // Note: any text is fine, legalize_for_path() fixes this later
+ diskstream_name += _session.config.get_take_name ();
+ diskstream_name += "_";
+ }
+ const int64_t tracknumber = track_number();
+ if (tracknumber > 0 && _session.config.get_track_name_number()) {
+ char num[64], fmt[10];
+ snprintf(fmt, sizeof(fmt), "%%0%d" PRId64, _session.track_number_decimals());
+ snprintf(num, sizeof(num), fmt, tracknumber);
+ diskstream_name += num;
+ diskstream_name += "_";
+ }
+ diskstream_name += str;
+
+ if (diskstream_name == _diskstream_name) {
+ return true;
+ }
+ _diskstream_name = diskstream_name;
+
+ _diskstream->set_write_source_name (diskstream_name);
+
boost::shared_ptr<Track> me = boost::dynamic_pointer_cast<Track> (shared_from_this ());
if (_diskstream->playlist()->all_regions_empty () && _session.playlists->playlists_for_track (me).size() == 1) {
/* Only rename the diskstream (and therefore the playlist) if
* the goal there is to be legal across filesystems.
*/
string
-legalize_for_path (const string& str)
+ARDOUR::legalize_for_path (const string& str)
{
return replace_chars (str, "/\\");
}
* ANY filesystem.
*/
string
-legalize_for_universal_path (const string& str)
+ARDOUR::legalize_for_universal_path (const string& str)
{
return replace_chars (str, "<>:\"/\\|?*");
}
* correct.
*/
string
-legalize_for_uri (const string& str)
+ARDOUR::legalize_for_uri (const string& str)
{
return replace_chars (str, "<>:\"/\\|?* #");
}
*/
string
-legalize_for_path_2X (const string& str)
+ARDOUR::legalize_for_path_2X (const string& str)
{
string::size_type pos;
string legal_chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_+=: ";
}
string
-bump_name_once (const std::string& name, char delimiter)
+ARDOUR::bump_name_once (const std::string& name, char delimiter)
{
string::size_type delim;
string newname;
}
+string
+ARDOUR::bump_name_number (const std::string& name)
+{
+ size_t pos = name.length();
+ size_t num = 0;
+ bool have_number = false;
+ while (pos > 0 && isdigit(name.at(--pos))) {
+ have_number = true;
+ num = pos;
+ }
+
+ string newname;
+ if (have_number) {
+ int32_t seq = strtol (name.c_str() + num, (char **)NULL, 10);
+ char buf[32];
+ snprintf (buf, sizeof(buf), "%d", seq + 1);
+ newname = name.substr (0, num);
+ newname += buf;
+ } else {
+ newname = name;
+ newname += "1";
+ }
+
+ return newname;
+}
+
XMLNode *
-find_named_node (const XMLNode& node, string name)
+ARDOUR::find_named_node (const XMLNode& node, string name)
{
XMLNodeList nlist;
XMLNodeConstIterator niter;
}
int
-cmp_nocase (const string& s, const string& s2)
+ARDOUR::cmp_nocase (const string& s, const string& s2)
{
string::const_iterator p = s.begin();
string::const_iterator p2 = s2.begin();
return (s2.size() == s.size()) ? 0 : (s.size() < s2.size()) ? -1 : 1;
}
-int cmp_nocase_utf8 (const string& s1, const string& s2)
+int
+ARDOUR::cmp_nocase_utf8 (const string& s1, const string& s2)
{
const char *cstr1 = s1.c_str();
const char *cstr2 = s2.c_str();
}
int
-touch_file (string path)
+ARDOUR::touch_file (string path)
{
int fd = open (path.c_str(), O_RDWR|O_CREAT, 0660);
if (fd >= 0) {
}
string
-region_name_from_path (string path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one)
+ARDOUR::region_name_from_path (string path, bool strip_channels, bool add_channel_suffix, uint32_t total, uint32_t this_one)
{
path = PBD::basename_nosuffix (path);
}
bool
-path_is_paired (string path, string& pair_base)
+ARDOUR::path_is_paired (string path, string& pair_base)
{
string::size_type pos;
#if __APPLE__
string
-CFStringRefToStdString(CFStringRef stringRef)
+ARDOUR::CFStringRefToStdString(CFStringRef stringRef)
{
CFIndex size =
CFStringGetMaximumSizeForEncoding(CFStringGetLength(stringRef) ,
#endif // __APPLE__
void
-compute_equal_power_fades (framecnt_t nframes, float* in, float* out)
+ARDOUR::compute_equal_power_fades (framecnt_t nframes, float* in, float* out)
{
double step;
}
EditMode
-string_to_edit_mode (string str)
+ARDOUR::string_to_edit_mode (string str)
{
if (str == _("Splice")) {
return Splice;
}
const char*
-edit_mode_to_string (EditMode mode)
+ARDOUR::edit_mode_to_string (EditMode mode)
{
switch (mode) {
case Slide:
}
SyncSource
-string_to_sync_source (string str)
+ARDOUR::string_to_sync_source (string str)
{
if (str == _("MIDI Timecode") || str == _("MTC")) {
return MTC;
/** @param sh Return a short version of the string */
const char*
-sync_source_to_string (SyncSource src, bool sh)
+ARDOUR::sync_source_to_string (SyncSource src, bool sh)
{
switch (src) {
case Engine:
}
float
-meter_falloff_to_float (MeterFalloff falloff)
+ARDOUR::meter_falloff_to_float (MeterFalloff falloff)
{
switch (falloff) {
case MeterFalloffOff:
}
MeterFalloff
-meter_falloff_from_float (float val)
+ARDOUR::meter_falloff_from_float (float val)
{
if (val == METER_FALLOFF_OFF) {
return MeterFalloffOff;
}
const char*
-native_header_format_extension (HeaderFormat hf, const DataType& type)
+ARDOUR::native_header_format_extension (HeaderFormat hf, const DataType& type)
{
if (type == DataType::MIDI) {
return ".mid";
}
bool
-matching_unsuffixed_filename_exists_in (const string& dir, const string& path)
+ARDOUR::matching_unsuffixed_filename_exists_in (const string& dir, const string& path)
{
string bws = basename_nosuffix (path);
struct dirent* dentry;
}
uint32_t
-how_many_dsp_threads ()
+ARDOUR::how_many_dsp_threads ()
{
/* CALLER MUST HOLD PROCESS LOCK */
return num_threads;
}
-double gain_to_slider_position_with_max (double g, double max_gain)
+double
+ARDOUR::gain_to_slider_position_with_max (double g, double max_gain)
{
return gain_to_slider_position (g * 2.0/max_gain);
}
-double slider_position_to_gain_with_max (double g, double max_gain)
+double
+ARDOUR::slider_position_to_gain_with_max (double g, double max_gain)
{
return slider_position_to_gain (g * max_gain/2.0);
}
#include <glib/gstdio.h>
#include <glibmm.h>
+#include "pbd/error.h"
-#ifdef VST_SCANNER_APP
-#define errormsg cerr
-#define warningmsg cerr
-#define endmsg endl
-#else
+#ifndef VST_SCANNER_APP
#include "ardour/plugin_manager.h" // scanner_bin_path
#include "ardour/rc_configuration.h"
#include "ardour/system_exec.h"
-#include "pbd/error.h"
-#define errormsg PBD::error
-#define warningmsg PBD::warning
#endif
#include "ardour/filesystem_paths.h"
} else if (infos->size() == 1) {
vstfx_write_info_block(fp, infos->front());
} else {
- errormsg << "Zero plugins in VST." << endmsg; // XXX here? rather make this impossible before if it ain't already.
+ PBD::error << "Zero plugins in VST." << endmsg; // XXX here? rather make this impossible before if it ain't already.
}
}
::g_unlink(vstfx_blacklist_path (dllpath, 1).c_str());
}
-#ifndef VST_SCANNER_APP
/** remove info file from cache */
static void
vstfx_remove_infofile (const char *dllpath)
::g_unlink(vstfx_infofile_path (dllpath, 0).c_str());
::g_unlink(vstfx_infofile_path (dllpath, 1).c_str());
}
-#endif
/** helper function, check if cache is newer than plugin
* @return path to cache file */
rv = vstfx_load_info_file(infofile, infos);
fclose (infofile);
if (!rv) {
- warningmsg << "Cannot get VST information form " << dllpath << ": info file load failed." << endmsg;
+ PBD::warning << "Cannot get VST information form " << dllpath << ": info file load failed." << endmsg;
}
}
return rv;
fail to implement getVendorString, and so won't stuff the
string with any name*/
- char creator[65] = "Unknown\0";
+ char creator[65] = "Unknown";
+ char name[65] = "";
AEffect* plugin = vstfx->plugin;
- info->name = strdup (vstfx->handle->name);
+
+ plugin->dispatcher (plugin, effGetEffectName, 0, 0, name, 0);
+
+ if (strlen(name) == 0) {
+ plugin->dispatcher (plugin, effGetProductString, 0, 0, name, 0);
+ }
+
+ if (strlen(name) == 0) {
+ info->name = strdup (vstfx->handle->name);
+ } else {
+ info->name = strdup (name);
+ }
/*If the plugin doesn't bother to implement GetVendorString we will
- have pre-stuffed the string with 'Unkown' */
+ have pre-stuffed the string with 'Unknown' */
plugin->dispatcher (plugin, effGetVendorString, 0, 0, creator, 0);
string path = vstfx->handle->path;
do {
- char name[65] = "Unknown\0";
+ char name[65] = "Unknown";
id = plugin->dispatcher (plugin, effShellGetNextPlugin, 0, 0, name, 0);
ids.push_back(std::make_pair(id, name));
} while ( id != 0 );
VSTHandle* h;
VSTState* vstfx;
if (!(h = vstfx_load(dllpath))) {
- warningmsg << "Cannot get LinuxVST information from " << dllpath << ": load failed." << endmsg;
+ PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": load failed." << endmsg;
return false;
}
if (!(vstfx = vstfx_instantiate(h, simple_master_callback, 0))) {
vstfx_unload(h);
- warningmsg << "Cannot get LinuxVST information from " << dllpath << ": instantiation failed." << endmsg;
+ PBD::warning << "Cannot get LinuxVST information from " << dllpath << ": instantiation failed." << endmsg;
return false;
}
VSTHandle* h;
VSTState* vstfx;
if(!(h = fst_load(dllpath))) {
- warningmsg << "Cannot get Windows VST information from " << dllpath << ": load failed." << endmsg;
+ PBD::warning << "Cannot get Windows VST information from " << dllpath << ": load failed." << endmsg;
return false;
}
if(!(vstfx = fst_instantiate(h, simple_master_callback, 0))) {
fst_unload(&h);
vstfx_current_loading_id = 0;
- warningmsg << "Cannot get Windows VST information from " << dllpath << ": instantiation failed." << endmsg;
+ PBD::warning << "Cannot get Windows VST information from " << dllpath << ": instantiation failed." << endmsg;
return false;
}
vstfx_current_loading_id = 0;
static void parse_scanner_output (std::string msg, size_t /*len*/)
{
if (!_errorlog_fd && !_errorlog_dll) {
- errormsg << "VST scanner: " << msg;
+ PBD::error << "VST scanner: " << msg;
return;
}
if (!_errorlog_fd) {
if (!(_errorlog_fd = fopen(vstfx_errorfile_path(_errorlog_dll, 0).c_str(), "w"))) {
if (!(_errorlog_fd = fopen(vstfx_errorfile_path(_errorlog_dll, 1).c_str(), "w"))) {
- errormsg << "Cannot create plugin error-log for plugin " << _errorlog_dll;
+ PBD::error << "Cannot create plugin error-log for plugin " << _errorlog_dll;
free(_errorlog_dll);
_errorlog_dll = NULL;
}
if (_errorlog_fd) {
fprintf (_errorlog_fd, "%s\n", msg.c_str());
} else {
- errormsg << "VST scanner: " << msg;
+ PBD::error << "VST scanner: " << msg;
}
}
PBD::ScopedConnectionList cons;
scanner.ReadStdout.connect_same_thread (cons, boost::bind (&parse_scanner_output, _1 ,_2));
if (scanner.start (2 /* send stderr&stdout via signal */)) {
- errormsg << "Cannot launch VST scanner app '" << scanner_bin_path << "': "<< strerror(errno) << endmsg;
+ PBD::error << "Cannot launch VST scanner app '" << scanner_bin_path << "': "<< strerror(errno) << endmsg;
close_error_log();
return infos;
} else {
/* crate cache/whitelist */
infofile = vstfx_infofile_for_write (dllpath);
if (!infofile) {
- warningmsg << "Cannot cache VST information for " << dllpath << ": cannot create new FST info file." << endmsg;
+ PBD::warning << "Cannot cache VST information for " << dllpath << ": cannot create new FST info file." << endmsg;
return infos;
} else {
vstfx_write_info_file (infofile, infos);
/* if the directory doesn't exist, try to create it */
if (!Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) {
if (g_mkdir (dir.c_str (), 0700)) {
- errormsg << "Cannot create VST blacklist folder '" << dir << "'" << endmsg;
+ PBD::error << "Cannot create VST blacklist folder '" << dir << "'" << endmsg;
//exit(1);
}
}
/* if the directory doesn't exist, try to create it */
if (!Glib::file_test (dir, Glib::FILE_TEST_IS_DIR)) {
if (g_mkdir (dir.c_str (), 0700)) {
- errormsg << "Cannot create VST info folder '" << dir << "'" << endmsg;
+ PBD::error << "Cannot create VST info folder '" << dir << "'" << endmsg;
//exit(1);
}
}
#include "pbd/floating.h"
#include "pbd/locale_guard.h"
-#include "pbd/pathscanner.h"
#include "ardour/vst_plugin.h"
#include "ardour/vestige/aeffectx.h"
{
VstParameterProperties prop;
+ memset (&prop, 0, sizeof (VstParameterProperties));
desc.min_unbound = false;
desc.max_unbound = false;
prop.flags = 0;
if (_plugin->dispatcher (_plugin, effGetParameterProperties, which, 0, &prop, 0)) {
/* i have yet to find or hear of a VST plugin that uses this */
+ /* RG: faust2vsti does use this :) */
if (prop.flags & kVstParameterUsesIntegerMinMax) {
desc.lower = prop.minInteger;
desc.largestep = desc.step * 10.0f;
}
+ if (strlen(prop.label) == 0) {
+ _plugin->dispatcher (_plugin, effGetParamName, which, 0, prop.label, 0);
+ }
+
desc.toggled = prop.flags & kVstParameterIsSwitch;
desc.logarithmic = false;
desc.sr_dependent = false;
'data_type.cc',
'default_click.cc',
'debug.cc',
+ 'delayline.cc',
'delivery.cc',
'directory_names.cc',
'diskstream.cc',
'mix.cc',
'monitor_processor.cc',
'mtc_slave.cc',
+ 'mididm.cc',
'mtdm.cc',
'mute_master.cc',
'onset_detector.cc',
'sndfile_helpers.cc',
'sndfileimportable.cc',
'sndfilesource.cc',
+ 'soundcloud_upload.cc',
'source.cc',
'source_factory.cc',
'speakers.cc',
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013 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 __libardouralsautil_devicelist_h__
+#define __libardouralsautil_devicelist_h__
+
+#include <string>
+#include <map>
+namespace ARDOUR {
+
+ void get_alsa_audio_device_names (std::map<std::string, std::string>& devices);
+ void get_alsa_rawmidi_device_names (std::map<std::string, std::string>& devices);
+ void get_alsa_sequencer_names (std::map<std::string, std::string>& devices);
+ int card_to_num(const char* device_name);
+
+}
+#endif
--- /dev/null
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
+
+#ifndef fooreservehfoo
+#define fooreservehfoo
+
+/***
+ Copyright 2009 Lennart Poettering
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#include <dbus/dbus.h>
+#include <inttypes.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct rd_device rd_device;
+
+/* Prototype for a function that is called whenever someone else wants
+ * your application to release the device it has locked. A return
+ * value <= 0 denies the request, a positive return value agrees to
+ * it. Before returning your application should close the device in
+ * question completely to make sure the new application may access
+ * it. */
+typedef int (*rd_request_cb_t)(
+ rd_device *d,
+ int forced); /* Non-zero if an application forcibly took the lock away without asking. If this is the case then the return value of this call is ignored. */
+
+/* Try to lock the device. Returns 0 on success, a negative errno
+ * style return value on error. The DBus error might be set as well if
+ * the error was caused D-Bus. */
+int rd_acquire(
+ rd_device **d, /* On success a pointer to the newly allocated rd_device object will be filled in here */
+ DBusConnection *connection, /* Session bus (when D-Bus learns about user busses we should switch to user busses) */
+ const char *device_name, /* The device to lock, e.g. "Audio0" */
+ const char *application_name, /* A human readable name of the application, e.g. "PulseAudio Sound Server" */
+ int32_t priority, /* The priority for this application. If unsure use 0 */
+ rd_request_cb_t request_cb, /* Will be called whenever someone requests that this device shall be released. May be NULL if priority is INT32_MAX */
+ DBusError *error); /* If we fail due to a D-Bus related issue the error will be filled in here. May be NULL. */
+
+/* Unlock (if needed) and destroy an rd_device object again */
+void rd_release(rd_device *d);
+
+/* Set the application device name for an rd_device object. Returns 0
+ * on success, a negative errno style return value on error. */
+int rd_set_application_device_name(rd_device *d, const char *name);
+
+/* Attach a userdata pointer to an rd_device */
+void rd_set_userdata(rd_device *d, void *userdata);
+
+/* Query the userdata pointer from an rd_device. Returns NULL if no
+ * userdata was set. */
+void* rd_get_userdata(rd_device *d);
+
+/* Helper function to get the unique connection name owning a given
+ * name. Returns 0 on success, a negative errno style return value on
+ * error. */
+int rd_dbus_get_name_owner(
+ DBusConnection *connection,
+ const char *name,
+ char **name_owner,
+ DBusError *error);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013 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 <alsa/asoundlib.h>
+#include "pbd/convert.h"
+#include "ardouralsautil/devicelist.h"
+
+using namespace std;
+
+void
+ARDOUR::get_alsa_audio_device_names (std::map<std::string, std::string>& devices)
+{
+ 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;
+
+ while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
+
+ devname = "hw:";
+ devname += PBD::to_string (cardnum, std::dec);
+
+ if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
+
+ if (snd_ctl_card_info (handle, info) < 0) {
+ continue;
+ }
+
+ string card_name = snd_ctl_card_info_get_name (info);
+
+ /* change devname to use ID, not number */
+
+ devname = "hw:";
+ devname += snd_ctl_card_info_get_id (info);
+
+ while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
+
+ /* only detect duplex devices here. more
+ * complex arrangements are beyond our scope
+ */
+
+ 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) {
+ continue;
+ }
+
+ 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) {
+ continue;
+ }
+ devname += ',';
+ devname += PBD::to_string (device, std::dec);
+ devices.insert (std::make_pair (card_name, devname));
+ }
+
+ snd_ctl_close(handle);
+ }
+ }
+}
+
+void
+ARDOUR::get_alsa_rawmidi_device_names (std::map<std::string, std::string>& devices)
+{
+ int cardnum = -1;
+ snd_ctl_card_info_t *cinfo;
+ snd_ctl_card_info_alloca (&cinfo);
+ while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
+ snd_ctl_t *handle;
+ std::string devname = "hw:";
+ devname += PBD::to_string (cardnum, std::dec);
+ if (snd_ctl_open (&handle, devname.c_str (), 0) >= 0 && snd_ctl_card_info (handle, cinfo) >= 0) {
+ int device = -1;
+ while (snd_ctl_rawmidi_next_device (handle, &device) >= 0 && device >= 0) {
+ snd_rawmidi_info_t *info;
+ snd_rawmidi_info_alloca (&info);
+ snd_rawmidi_info_set_device (info, device);
+
+ int subs_in, subs_out;
+
+ snd_rawmidi_info_set_stream (info, SND_RAWMIDI_STREAM_INPUT);
+ if (snd_ctl_rawmidi_info (handle, info) >= 0) {
+ subs_in = snd_rawmidi_info_get_subdevices_count (info);
+ } else {
+ subs_in = 0;
+ }
+
+ snd_rawmidi_info_set_stream (info, SND_RAWMIDI_STREAM_OUTPUT);
+ if (snd_ctl_rawmidi_info (handle, info) >= 0) {
+ subs_out = snd_rawmidi_info_get_subdevices_count (info);
+ } else {
+ subs_out = 0;
+ }
+
+ const int subs = subs_in > subs_out ? subs_in : subs_out;
+ if (!subs) {
+ continue;
+ }
+
+ for (int sub = 0; sub < subs; ++sub) {
+ snd_rawmidi_info_set_stream (info, sub < subs_in ?
+ SND_RAWMIDI_STREAM_INPUT :
+ SND_RAWMIDI_STREAM_OUTPUT);
+
+ snd_rawmidi_info_set_subdevice (info, sub);
+ if (snd_ctl_rawmidi_info (handle, info) < 0) {
+ continue;
+ }
+
+ const char *sub_name = snd_rawmidi_info_get_subdevice_name (info);
+ if (sub == 0 && sub_name[0] == '\0') {
+ devname = "hw:";
+ devname += snd_ctl_card_info_get_id (cinfo);
+ devname += ",";
+ devname += PBD::to_string (device, std::dec);
+
+ std::string card_name;
+ card_name = snd_rawmidi_info_get_name (info);
+ card_name += " (";
+ if (sub < subs_in) card_name += "I";
+ if (sub < subs_out) card_name += "O";
+ card_name += ")";
+
+ devices.insert (std::make_pair (card_name, devname));
+ break;
+ } else {
+ devname = "hw:";
+ devname += snd_ctl_card_info_get_id (cinfo);
+ devname += ",";
+ devname += PBD::to_string (device, std::dec);
+ devname += ",";
+ devname += PBD::to_string (sub, std::dec);
+
+ std::string card_name = sub_name;
+ card_name += " (";
+ if (sub < subs_in) card_name += "I";
+ if (sub < subs_out) card_name += "O";
+ card_name += ")";
+ devices.insert (std::make_pair (card_name, devname));
+ }
+ }
+ }
+ snd_ctl_close (handle);
+ }
+ }
+}
+
+void
+ARDOUR::get_alsa_sequencer_names (std::map<std::string, std::string>& devices)
+{
+ snd_seq_t *seq= NULL;
+ snd_seq_client_info_t *cinfo;
+ snd_seq_port_info_t *pinfo;
+
+ snd_seq_client_info_alloca (&cinfo);
+ snd_seq_port_info_alloca (&pinfo);
+
+ if (snd_seq_open (&seq, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0) {
+ return;
+ }
+
+ snd_seq_client_info_set_client(cinfo, -1);
+ while (snd_seq_query_next_client (seq, cinfo) >= 0) {
+ int client = snd_seq_client_info_get_client (cinfo);
+ if (client == SND_SEQ_CLIENT_SYSTEM) {
+ continue;
+ }
+ if (!strcmp (snd_seq_client_info_get_name(cinfo), "Midi Through")) {
+ continue;
+ }
+ snd_seq_port_info_set_client (pinfo, client);
+ snd_seq_port_info_set_port (pinfo, -1);
+
+ while (snd_seq_query_next_port (seq, pinfo) >= 0) {
+ int caps = snd_seq_port_info_get_capability(pinfo);
+ if (0 == (caps & (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_WRITE))) {
+ continue;
+ }
+ if (caps & SND_SEQ_PORT_CAP_NO_EXPORT) {
+ continue;
+ }
+ std::string card_name;
+ card_name = snd_seq_port_info_get_name (pinfo);
+
+ card_name += " (";
+ if (caps & SND_SEQ_PORT_CAP_READ) card_name += "I";
+ if (caps & SND_SEQ_PORT_CAP_WRITE) card_name += "O";
+ card_name += ")";
+
+ std::string devname;
+ devname = PBD::to_string(snd_seq_port_info_get_client (pinfo), std::dec);
+ devname += ":";
+ devname += PBD::to_string(snd_seq_port_info_get_port (pinfo), std::dec);
+ devices.insert (std::make_pair (card_name, devname));
+ }
+ }
+ snd_seq_close (seq);
+}
+
+int
+ARDOUR::card_to_num(const char* device_name)
+{
+ char* ctl_name;
+ const char * comma;
+ snd_ctl_t* ctl_handle;
+ int i = -1;
+
+ if (strncasecmp(device_name, "plughw:", 7) == 0) {
+ device_name += 4;
+ }
+ if (!(comma = strchr(device_name, ','))) {
+ ctl_name = strdup(device_name);
+ } else {
+ ctl_name = strndup(device_name, comma - device_name);
+ }
+
+ if (snd_ctl_open (&ctl_handle, ctl_name, 0) >= 0) {
+ snd_ctl_card_info_t *card_info;
+ snd_ctl_card_info_alloca (&card_info);
+ if (snd_ctl_card_info(ctl_handle, card_info) >= 0) {
+ i = snd_ctl_card_info_get_card(card_info);
+ }
+ snd_ctl_close(ctl_handle);
+ }
+ free(ctl_name);
+ return i;
+}
--- /dev/null
+/* alsa/ardour dbus device request tool
+ *
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+// NB generate man-page with
+// help2man -N -n "alsa/ardour dbus device request tool" -o ardour-request-device.1 ./build/libs/ardouralsautil/ardour-request-device
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <signal.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "ardouralsautil/reserve.h"
+
+#ifndef ARD_PROG_NAME
+#define ARD_PROG_NAME "alsa_request_device"
+#endif
+#ifndef ARD_APPL_NAME
+#define ARD_APPL_NAME "ALSA User"
+#endif
+#ifndef VERSION
+#define VERSION "v0.3"
+#endif
+
+static int run = 1;
+static int release_wait_for_signal = 0;
+static pid_t parent_pid = 0;
+
+static void wearedone(int sig) {
+ (void) sig; // skip 'unused variable' compiler warning;
+ fprintf(stderr, "caught signal - shutting down.\n");
+ run=0;
+}
+
+static int stdin_available(void) {
+ errno = 0;
+ if (fcntl(STDIN_FILENO, F_GETFD) == 1) return 0;
+ return errno != EBADF;
+}
+
+static void print_version(int status) {
+ printf (ARD_PROG_NAME " " VERSION "\n\n");
+ printf (
+ "Copyright (C) 2014 Robin Gareus <robin@gareus.org>\n"
+ "This is free software; see the source for copying conditions. There is NO\n"
+ "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\n"
+ );
+ exit (status);
+}
+
+static void usage(int status) {
+ printf (ARD_PROG_NAME " - DBus Audio Reservation Utility.\n");
+ printf ("Usage: " ARD_PROG_NAME " [ OPTIONS ] <Audio-Device-ID>\n");
+ printf ("Options:\n\
+ -h, --help display this help and exit\n\
+ -p, --priority <int> reservation priority (default: int32_max)\n\
+ -P, --pid <int> process-id to watch (default 0: none)\n\
+ -n, --name <string> application name to use for registration\n\
+ -V, --version print version information and exit\n\
+ -w, --releasewait wait for signal on yield-release\n\
+");
+
+ printf ("\n\
+This tool issues a dbus request to reserve an ALSA Audio-device.\n\
+If successful other users of the device (e.g. pulseaudio) will\n\
+release the device.\n\
+\n\
+" ARD_PROG_NAME " by default announces itself as \"" ARD_APPL_NAME "\"\n\
+and uses the maximum possible priority for requesting the device.\n\
+These settings can be overriden using the -n and -p options respectively.\n\
+\n\
+If a PID is given the tool will watch the process and if that is not running\n\
+release the device and exit. Otherwise " ARD_PROG_NAME " runs until\n\
+either stdin is closed, a SIGINT or SIGTERM is received or some other\n\
+application requests the device with a higher priority.\n\
+\n\
+Without the -w option, " ARD_PROG_NAME " yields the device after 500ms to\n\
+any higher-priority request. With the -w option this tool waits until it\n\
+for SIGINT or SIGTERM - but at most 4 sec to acknowledge before releasing.\n\
+\n\
+The audio-device-id is a string e.g. 'Audio1'\n\
+\n\
+Examples:\n\
+" ARD_PROG_NAME " Audio0\n\
+\n");
+
+ printf ("Report bugs to Robin Gareus <robin@gareus.org>\n");
+ exit (status);
+}
+
+static struct option const long_options[] =
+{
+ {"help", no_argument, 0, 'h'},
+ {"name", required_argument, 0, 'n'},
+ {"pid", required_argument, 0, 'P'},
+ {"priority", required_argument, 0, 'p'},
+ {"version", no_argument, 0, 'V'},
+ {"releasewait", no_argument, 0, 'w'},
+ {NULL, 0, NULL, 0}
+};
+
+static int request_cb(rd_device *d, int forced) {
+ (void) d; // skip 'unused variable' compiler warning;
+ (void) forced; // skip 'unused variable' compiler warning;
+ fprintf(stdout, "Received higher priority request - releasing device.\n");
+ fflush(stdout);
+ if(!release_wait_for_signal) {
+ usleep (500000);
+ run = 0;
+ } else if (run) {
+ int timeout = 4000;
+ fprintf(stdout, "Waiting for acknowledge signal to release.\n");
+ while (release_wait_for_signal && run && --timeout) {
+ if (!stdin_available()) {
+ break;
+ }
+ if (parent_pid > 0 && kill (parent_pid, 0)) {
+ break;
+ }
+ usleep (1000);
+ }
+ run = 0;
+ }
+ return 1; // OK
+}
+
+int main(int argc, char **argv) {
+ DBusConnection* dbus_connection = NULL;
+ rd_device * reserved_device = NULL;
+ DBusError error;
+ int ret, c;
+
+ int32_t priority = INT32_MAX;
+ char *name = strdup(ARD_APPL_NAME);
+
+ while ((c = getopt_long (argc, argv,
+ "h" /* help */
+ "n:" /* name */
+ "P:" /* pid */
+ "p:" /* priority */
+ "V" /* version */
+ "w", /* release wait for signal */
+ long_options, (int *) 0)) != EOF)
+ {
+ switch (c) {
+ case 'h':
+ free(name);
+ usage(EXIT_SUCCESS);
+ break;
+ case 'n':
+ free(name);
+ name = strdup(optarg);
+ break;
+ case 'p':
+ priority = atoi (optarg);
+ if (priority < 0) priority = 0;
+ break;
+ case 'P':
+ parent_pid = atoi (optarg);
+ break;
+ case 'V':
+ free(name);
+ print_version(EXIT_SUCCESS);
+ break;
+ case 'w':
+ release_wait_for_signal = 1;
+ break;
+ default:
+ free(name);
+ usage(EXIT_FAILURE);
+ break;
+ }
+ }
+
+ if (optind + 1 != argc) {
+ free(name);
+ usage(EXIT_FAILURE);
+ }
+ const char *device_name = argv[optind];
+
+ if (parent_pid > 0 && kill (parent_pid, 0)) {
+ fprintf(stderr, "Given PID to watch is not running.\n");
+ free(name);
+ return EXIT_FAILURE;
+ }
+
+ dbus_error_init(&error);
+
+ if (!(dbus_connection = dbus_bus_get (DBUS_BUS_SESSION, &error))) {
+ fprintf(stderr, "Failed to connect to session bus for device reservation: %s\n", error.message ? error.message : "unknown error.");
+ dbus_error_free(&error);
+ free(name);
+ return EXIT_FAILURE;
+ }
+
+ if ((ret = rd_acquire (
+ &reserved_device,
+ dbus_connection,
+ device_name,
+ name,
+ priority,
+ request_cb,
+ &error)) < 0)
+ {
+ fprintf(stderr, "Failed to acquire device: '%s'\n%s\n", device_name, (error.message ? error.message : strerror(-ret)));
+ dbus_error_free(&error);
+ dbus_connection_unref(dbus_connection);
+ free(name);
+ return EXIT_FAILURE;
+ }
+
+ fprintf(stdout, "Acquired audio-card '%s'\n", device_name);
+ fprintf(stdout, "Press Ctrl+C or close stdin to release the device.\n");
+ fflush(stdout);
+
+ signal(SIGTERM, wearedone);
+ signal(SIGINT, wearedone);
+
+ while (run && dbus_connection_read_write_dispatch (dbus_connection, 200)) {
+ if (!stdin_available()) {
+ fprintf(stderr, "stdin closed - releasing device.\n");
+ break;
+ }
+ if (parent_pid > 0 && kill (parent_pid, 0)) {
+ fprintf(stderr, "watched PID no longer exists - releasing device.\n");
+ break;
+ }
+ }
+
+ rd_release (reserved_device);
+ fprintf(stdout, "Released audio-card '%s'\n", device_name);
+
+ dbus_connection_unref(dbus_connection);
+ dbus_error_free(&error);
+ free(name);
+ return EXIT_SUCCESS;
+}
--- /dev/null
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: t -*-*/
+
+/***
+ Copyright 2009 Lennart Poettering
+
+ Permission is hereby granted, free of charge, to any person
+ obtaining a copy of this software and associated documentation files
+ (the "Software"), to deal in the Software without restriction,
+ including without limitation the rights to use, copy, modify, merge,
+ publish, distribute, sublicense, and/or sell copies of the Software,
+ and to permit persons to whom the Software is furnished to do so,
+ subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+***/
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "ardouralsautil/reserve.h"
+
+#ifndef DBUS_TIMEOUT_USE_DEFAULT
+#define DBUS_TIMEOUT_USE_DEFAULT (-1)
+#endif
+
+struct rd_device {
+ int ref;
+
+ char *device_name;
+ char *application_name;
+ char *application_device_name;
+ char *service_name;
+ char *object_path;
+ int32_t priority;
+
+ DBusConnection *connection;
+
+ unsigned owning:1;
+ unsigned registered:1;
+ unsigned filtering:1;
+ unsigned gave_up:1;
+
+ rd_request_cb_t request_cb;
+ void *userdata;
+};
+
+#define SERVICE_PREFIX "org.freedesktop.ReserveDevice1."
+#define OBJECT_PREFIX "/org/freedesktop/ReserveDevice1/"
+
+static const char introspection[] =
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE
+ "<node>"
+ " <!-- If you are looking for documentation make sure to check out\n"
+ " http://git.0pointer.de/?p=reserve.git;a=blob;f=reserve.txt -->\n"
+ " <interface name=\"org.freedesktop.ReserveDevice1\">"
+ " <method name=\"RequestRelease\">"
+ " <arg name=\"priority\" type=\"i\" direction=\"in\"/>"
+ " <arg name=\"result\" type=\"b\" direction=\"out\"/>"
+ " </method>"
+ " <property name=\"Priority\" type=\"i\" access=\"read\"/>"
+ " <property name=\"ApplicationName\" type=\"s\" access=\"read\"/>"
+ " <property name=\"ApplicationDeviceName\" type=\"s\" access=\"read\"/>"
+ " </interface>"
+ " <interface name=\"org.freedesktop.DBus.Properties\">"
+ " <method name=\"Get\">"
+ " <arg name=\"interface\" direction=\"in\" type=\"s\"/>"
+ " <arg name=\"property\" direction=\"in\" type=\"s\"/>"
+ " <arg name=\"value\" direction=\"out\" type=\"v\"/>"
+ " </method>"
+ " </interface>"
+ " <interface name=\"org.freedesktop.DBus.Introspectable\">"
+ " <method name=\"Introspect\">"
+ " <arg name=\"data\" type=\"s\" direction=\"out\"/>"
+ " </method>"
+ " </interface>"
+ "</node>";
+
+static dbus_bool_t add_variant(
+ DBusMessage *m,
+ int type,
+ const void *data) {
+
+ DBusMessageIter iter, sub;
+ char t[2];
+
+ t[0] = (char) type;
+ t[1] = 0;
+
+ dbus_message_iter_init_append(m, &iter);
+
+ if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, t, &sub))
+ return FALSE;
+
+ if (!dbus_message_iter_append_basic(&sub, type, data))
+ return FALSE;
+
+ if (!dbus_message_iter_close_container(&iter, &sub))
+ return FALSE;
+
+ return TRUE;
+}
+
+static DBusHandlerResult object_handler(
+ DBusConnection *c,
+ DBusMessage *m,
+ void *userdata) {
+
+ rd_device *d;
+ DBusError error;
+ DBusMessage *reply = NULL;
+
+ dbus_error_init(&error);
+
+ d = userdata;
+ assert(d->ref >= 1);
+
+ if (dbus_message_is_method_call(
+ m,
+ "org.freedesktop.ReserveDevice1",
+ "RequestRelease")) {
+
+ int32_t priority;
+ dbus_bool_t ret;
+
+ if (!dbus_message_get_args(
+ m,
+ &error,
+ DBUS_TYPE_INT32, &priority,
+ DBUS_TYPE_INVALID))
+ goto invalid;
+
+ ret = FALSE;
+
+ if (priority > d->priority && d->request_cb) {
+ d->ref++;
+
+ if (d->request_cb(d, 0) > 0) {
+ ret = TRUE;
+ d->gave_up = 1;
+ }
+
+ rd_release(d);
+ }
+
+ if (!(reply = dbus_message_new_method_return(m)))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_BOOLEAN, &ret,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (!dbus_connection_send(c, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+ } else if (dbus_message_is_method_call(
+ m,
+ "org.freedesktop.DBus.Properties",
+ "Get")) {
+
+ const char *interface, *property;
+
+ if (!dbus_message_get_args(
+ m,
+ &error,
+ DBUS_TYPE_STRING, &interface,
+ DBUS_TYPE_STRING, &property,
+ DBUS_TYPE_INVALID))
+ goto invalid;
+
+ if (strcmp(interface, "org.freedesktop.ReserveDevice1") == 0) {
+ const char *empty = "";
+
+ if (strcmp(property, "ApplicationName") == 0 && d->application_name) {
+ if (!(reply = dbus_message_new_method_return(m)))
+ goto oom;
+
+ if (!add_variant(
+ reply,
+ DBUS_TYPE_STRING,
+ d->application_name ? (const char * const *) &d->application_name : &empty))
+ goto oom;
+
+ } else if (strcmp(property, "ApplicationDeviceName") == 0) {
+ if (!(reply = dbus_message_new_method_return(m)))
+ goto oom;
+
+ if (!add_variant(
+ reply,
+ DBUS_TYPE_STRING,
+ d->application_device_name ? (const char * const *) &d->application_device_name : &empty))
+ goto oom;
+
+ } else if (strcmp(property, "Priority") == 0) {
+ if (!(reply = dbus_message_new_method_return(m)))
+ goto oom;
+
+ if (!add_variant(
+ reply,
+ DBUS_TYPE_INT32,
+ &d->priority))
+ goto oom;
+ } else {
+ if (!(reply = dbus_message_new_error_printf(
+ m,
+ DBUS_ERROR_UNKNOWN_METHOD,
+ "Unknown property %s",
+ property)))
+ goto oom;
+ }
+
+ if (!dbus_connection_send(c, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ } else if (dbus_message_is_method_call(
+ m,
+ "org.freedesktop.DBus.Introspectable",
+ "Introspect")) {
+ const char *i = introspection;
+
+ if (!(reply = dbus_message_new_method_return(m)))
+ goto oom;
+
+ if (!dbus_message_append_args(
+ reply,
+ DBUS_TYPE_STRING,
+ &i,
+ DBUS_TYPE_INVALID))
+ goto oom;
+
+ if (!dbus_connection_send(c, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+invalid:
+ if (reply)
+ dbus_message_unref(reply);
+
+ if (!(reply = dbus_message_new_error(
+ m,
+ DBUS_ERROR_INVALID_ARGS,
+ "Invalid arguments")))
+ goto oom;
+
+ if (!dbus_connection_send(c, reply, NULL))
+ goto oom;
+
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+
+oom:
+ if (reply)
+ dbus_message_unref(reply);
+
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+}
+
+static DBusHandlerResult filter_handler(
+ DBusConnection *c,
+ DBusMessage *m,
+ void *userdata) {
+
+ rd_device *d;
+ DBusError error;
+ char *name_owner = NULL;
+
+ dbus_error_init(&error);
+
+ d = userdata;
+ assert(d->ref >= 1);
+
+ if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameLost")) {
+ const char *name;
+
+ if (!dbus_message_get_args(
+ m,
+ &error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ goto invalid;
+
+ if (strcmp(name, d->service_name) == 0 && d->owning) {
+ /* Verify the actual owner of the name to avoid leaked NameLost
+ * signals from previous reservations. The D-Bus daemon will send
+ * all messages asynchronously in the correct order, but we could
+ * potentially process them too late due to the pseudo-blocking
+ * call mechanism used during both acquisition and release. This
+ * can happen if we release the device and immediately after
+ * reacquire it before NameLost is processed. */
+ if (!d->gave_up) {
+ const char *un;
+
+ if ((un = dbus_bus_get_unique_name(c)) && rd_dbus_get_name_owner(c, d->service_name, &name_owner, &error) == 0)
+ if (name_owner && strcmp(name_owner, un) == 0)
+ goto invalid; /* Name still owned by us */
+ }
+
+ d->owning = 0;
+
+ if (!d->gave_up) {
+ d->ref++;
+
+ if (d->request_cb)
+ d->request_cb(d, 1);
+ d->gave_up = 1;
+
+ rd_release(d);
+ }
+
+ }
+ }
+
+invalid:
+ free(name_owner);
+ dbus_error_free(&error);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static const struct DBusObjectPathVTable vtable ={
+ .message_function = object_handler
+};
+
+int rd_acquire(
+ rd_device **_d,
+ DBusConnection *connection,
+ const char *device_name,
+ const char *application_name,
+ int32_t priority,
+ rd_request_cb_t request_cb,
+ DBusError *error) {
+
+ rd_device *d = NULL;
+ int r, k;
+ DBusError _error;
+ DBusMessage *m = NULL, *reply = NULL;
+ dbus_bool_t good;
+
+ if (!error)
+ error = &_error;
+
+ dbus_error_init(error);
+
+ if (!_d)
+ return -EINVAL;
+
+ if (!connection)
+ return -EINVAL;
+
+ if (!device_name)
+ return -EINVAL;
+
+ if (!request_cb && priority != INT32_MAX)
+ return -EINVAL;
+
+ if (!(d = calloc(sizeof(rd_device), 1)))
+ return -ENOMEM;
+
+ d->ref = 1;
+
+ if (!(d->device_name = strdup(device_name))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(d->application_name = strdup(application_name))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ d->priority = priority;
+ d->connection = dbus_connection_ref(connection);
+ d->request_cb = request_cb;
+
+ if (!(d->service_name = malloc(sizeof(SERVICE_PREFIX) + strlen(device_name)))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ sprintf(d->service_name, SERVICE_PREFIX "%s", d->device_name);
+
+ if (!(d->object_path = malloc(sizeof(OBJECT_PREFIX) + strlen(device_name)))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+ sprintf(d->object_path, OBJECT_PREFIX "%s", d->device_name);
+
+ if ((k = dbus_bus_request_name(
+ d->connection,
+ d->service_name,
+ DBUS_NAME_FLAG_DO_NOT_QUEUE|
+ (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0),
+ error)) < 0) {
+ r = -EIO;
+ goto fail;
+ }
+
+ if (k == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ goto success;
+
+ if (k != DBUS_REQUEST_NAME_REPLY_EXISTS) {
+ r = -EIO;
+ goto fail;
+ }
+
+ if (priority <= INT32_MIN) {
+ r = -EBUSY;
+ goto fail;
+ }
+
+ if (!(m = dbus_message_new_method_call(
+ d->service_name,
+ d->object_path,
+ "org.freedesktop.ReserveDevice1",
+ "RequestRelease"))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!dbus_message_append_args(
+ m,
+ DBUS_TYPE_INT32, &d->priority,
+ DBUS_TYPE_INVALID)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!(reply = dbus_connection_send_with_reply_and_block(
+ d->connection,
+ m,
+ 5000, /* 5s */
+ error))) {
+
+ if (dbus_error_has_name(error, DBUS_ERROR_TIMED_OUT) ||
+ dbus_error_has_name(error, DBUS_ERROR_UNKNOWN_METHOD) ||
+ dbus_error_has_name(error, DBUS_ERROR_NO_REPLY)) {
+ /* This must be treated as denied. */
+ r = -EBUSY;
+ goto fail;
+ }
+
+ r = -EIO;
+ goto fail;
+ }
+
+ if (!dbus_message_get_args(
+ reply,
+ error,
+ DBUS_TYPE_BOOLEAN, &good,
+ DBUS_TYPE_INVALID)) {
+ r = -EIO;
+ goto fail;
+ }
+
+ if (!good) {
+ r = -EBUSY;
+ goto fail;
+ }
+
+ if ((k = dbus_bus_request_name(
+ d->connection,
+ d->service_name,
+ DBUS_NAME_FLAG_DO_NOT_QUEUE|
+ (priority < INT32_MAX ? DBUS_NAME_FLAG_ALLOW_REPLACEMENT : 0)|
+ DBUS_NAME_FLAG_REPLACE_EXISTING,
+ error)) < 0) {
+ r = -EIO;
+ goto fail;
+ }
+
+ if (k != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
+ r = -EIO;
+ goto fail;
+ }
+
+success:
+ d->owning = 1;
+
+ if (!(dbus_connection_register_object_path(
+ d->connection,
+ d->object_path,
+ &vtable,
+ d))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ d->registered = 1;
+
+ if (!dbus_connection_add_filter(
+ d->connection,
+ filter_handler,
+ d,
+ NULL)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ d->filtering = 1;
+
+ *_d = d;
+ return 0;
+
+fail:
+ if (m)
+ dbus_message_unref(m);
+
+ if (reply)
+ dbus_message_unref(reply);
+
+ if (&_error == error)
+ dbus_error_free(&_error);
+
+ if (d)
+ rd_release(d);
+
+ return r;
+}
+
+void rd_release(
+ rd_device *d) {
+
+ if (!d)
+ return;
+
+ assert(d->ref > 0);
+
+ if (--d->ref > 0)
+ return;
+
+
+ if (d->filtering)
+ dbus_connection_remove_filter(
+ d->connection,
+ filter_handler,
+ d);
+
+ if (d->registered)
+ dbus_connection_unregister_object_path(
+ d->connection,
+ d->object_path);
+
+ if (d->owning)
+ dbus_bus_release_name(
+ d->connection,
+ d->service_name,
+ NULL);
+
+ free(d->device_name);
+ free(d->application_name);
+ free(d->application_device_name);
+ free(d->service_name);
+ free(d->object_path);
+
+ if (d->connection)
+ dbus_connection_unref(d->connection);
+
+ free(d);
+}
+
+int rd_set_application_device_name(rd_device *d, const char *n) {
+ char *t;
+
+ if (!d)
+ return -EINVAL;
+
+ assert(d->ref > 0);
+
+ if (!(t = strdup(n)))
+ return -ENOMEM;
+
+ free(d->application_device_name);
+ d->application_device_name = t;
+ return 0;
+}
+
+void rd_set_userdata(rd_device *d, void *userdata) {
+
+ if (!d)
+ return;
+
+ assert(d->ref > 0);
+ d->userdata = userdata;
+}
+
+void* rd_get_userdata(rd_device *d) {
+
+ if (!d)
+ return NULL;
+
+ assert(d->ref > 0);
+
+ return d->userdata;
+}
+
+int rd_dbus_get_name_owner(
+ DBusConnection *connection,
+ const char *name,
+ char **name_owner,
+ DBusError *error) {
+
+ DBusMessage *msg, *reply;
+ int r;
+
+ *name_owner = NULL;
+
+ if (!(msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetNameOwner"))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ reply = dbus_connection_send_with_reply_and_block(connection, msg, DBUS_TIMEOUT_USE_DEFAULT, error);
+ dbus_message_unref(msg);
+ msg = NULL;
+
+ if (reply) {
+ if (!dbus_message_get_args(reply, error, DBUS_TYPE_STRING, name_owner, DBUS_TYPE_INVALID)) {
+ dbus_message_unref(reply);
+ r = -EIO;
+ goto fail;
+ }
+
+ *name_owner = strdup(*name_owner);
+ dbus_message_unref(reply);
+
+ if (!*name_owner) {
+ r = -ENOMEM;
+ goto fail;
+ }
+
+ } else if (dbus_error_has_name(error, "org.freedesktop.DBus.Error.NameHasNoOwner"))
+ dbus_error_free(error);
+ else {
+ r = -EIO;
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ if (msg)
+ dbus_message_unref(msg);
+
+ return r;
+}
--- /dev/null
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+from waflib import Options
+import os
+import sys
+import re
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+path_prefix = 'libs/ardouralsautil/'
+
+def options(opt):
+ autowaf.set_options(opt)
+
+
+def configure(conf):
+ autowaf.configure(conf)
+ if re.search ("linux", sys.platform) != None and Options.options.dist_target != 'mingw':
+ autowaf.check_pkg(conf, 'alsa', uselib_store='ALSA')
+ autowaf.check_pkg(conf, 'dbus-1', uselib_store='DBUS', mandatory = False)
+
+def build(bld):
+ if re.search ("linux", sys.platform) != None:
+ if bld.is_defined('HAVE_ALSA'):
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = [
+ 'devicelist.cc'
+ ]
+ obj.export_includes = ['.']
+ obj.includes = ['.']
+ obj.name = 'ardouralsautil'
+ obj.target = 'ardouralsautil'
+ obj.use = 'libpbd'
+ obj.uselib = [ 'ALSA' ]
+ obj.vnum = '0.0.1'
+ obj.install_path = os.path.join(bld.env['LIBDIR'], 'ardouralsautil')
+
+ if bld.env['BUILD_ALSABACKEND'] and bld.is_defined('HAVE_ALSA') and bld.is_defined('HAVE_DBUS'):
+ obj = bld(features = 'c cprogram')
+ obj.source = [
+ 'reserve.c',
+ 'request_device.c'
+ ]
+ obj.includes = ['.']
+ obj.target = 'ardour-request-device'
+ obj.uselib = [ 'DBUS' ]
+ obj.install_path = os.path.join(bld.env['LIBDIR'])
+ obj.defines = [
+ '_POSIX_SOURCE',
+ '_XOPEN_SOURCE=500',
+ 'ARD_PROG_NAME="ardour-request-device"',
+ 'ARD_APPL_NAME="Ardour ALSA Backend"',
+ ]
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="dummy_audiobackend"
+ ProjectGUID="{A72277FF-4C0C-4851-9751-2F524507A052}"
+ RootNamespace="dummy_audiobackend"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug 32|Win32"
+ OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;..\dummy;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;"..\..\midi++2";"$(GenericIncludeFolder)\ardourext";"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";"$(GenericIncludeFolder)\boost";..\.."
+ PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_DUMMY_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;_DEBUG;DEBUG="Debug";ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_dummy\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
+ MinimalRebuild="true"
+ RuntimeLibrary="3"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ CompileAs="2"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib boost-regex32D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)ardour32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib"
+ OutputFile="$(OutDir)\$(ProjectName)D.dll"
+ AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+ IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ UseFAT32Workaround="true"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release 32|Win32"
+ OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories="..;..\dummy;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;"..\..\midi++2";"$(GenericIncludeFolder)\ardourext";"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";"$(GenericIncludeFolder)\boost";..\.."
+ PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_DUMMY_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_dummy\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
+ StringPooling="false"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="1"
+ WarningLevel="3"
+ CompileAs="2"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib boost-regex32.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)ardour32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib"
+ OutputFile="$(OutDir)\$(ProjectName).dll"
+ AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+ IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+ GenerateDebugInformation="false"
+ SubSystem="2"
+ OptimizeReferences="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ UseFAT32Workaround="true"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release 32 with Debugging Capability|Win32"
+ OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+ CharacterSet="2"
+ WholeProgramOptimization="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;..\dummy;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;"..\..\midi++2";"$(GenericIncludeFolder)\ardourext";"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";"$(GenericIncludeFolder)\boost";..\.."
+ PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_DUMMY_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_dummy\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
+ StringPooling="false"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="1"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ CompileAs="2"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib boost-regex32RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)ardour32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib"
+ OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
+ AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+ IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ UseFAT32Workaround="true"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\dummy\dummy_audiobackend.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\dummy\dummy_audiobackend.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="jack_audiobackend"
+ ProjectGUID="{4A58CE49-541E-43D9-92CD-7E85EA7C96AF}"
+ RootNamespace="jack_audiobackend"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug 32|Win32"
+ OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;"$(GenericIncludeFolder)\ardourext";..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;"..\..\vamp-sdk";"..\..\midi++2";..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";..\..\..\..\support\MB3JackPortaudio\include"
+ PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_DEBUG;DEBUG="Debug";ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_jack\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_PORTAUDIO=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
+ MinimalRebuild="true"
+ RuntimeLibrary="3"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ CompileAs="2"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="libjackD.lib $(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib jack_portaudio_x86D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+ OutputFile="$(OutDir)\$(ProjectName)D.dll"
+ AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+ IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ UseFAT32Workaround="true"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release 32|Win32"
+ OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;"$(GenericIncludeFolder)\ardourext";..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;"..\..\vamp-sdk";"..\..\midi++2";..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";..\..\..\..\support\MB3JackPortaudio\include"
+ PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_jack\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_PORTAUDIO=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
+ StringPooling="false"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="1"
+ WarningLevel="3"
+ CompileAs="2"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="libjack.lib $(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib jack_portaudio_x86.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+ OutputFile="$(OutDir)\$(ProjectName).dll"
+ AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+ IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+ SubSystem="2"
+ OptimizeReferences="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ UseFAT32Workaround="true"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release 32 with Debugging Capability|Win32"
+ OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+ CharacterSet="2"
+ WholeProgramOptimization="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;"$(GenericIncludeFolder)\ardourext";..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;"..\..\vamp-sdk";"..\..\midi++2";..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";..\..\..\..\support\MB3JackPortaudio\include"
+ PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_jack\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_PORTAUDIO=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
+ StringPooling="false"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="1"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ CompileAs="2"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="libjack.lib $(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib jack_portaudio_x86RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+ OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
+ AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+ IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ UseFAT32Workaround="true"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\jack\jack_api.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\jack\jack_audiobackend.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\jack\jack_connection.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\jack\jack_portengine.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\jack\jack_session.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\jack\jack_utils.cc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\jack\jack_audiobackend.h"
+ >
+ </File>
+ <File
+ RelativePath="..\jack\jack_connection.h"
+ >
+ </File>
+ <File
+ RelativePath="..\jack\jack_utils.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
+++ /dev/null
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="8.00"
- Name="jack_backend"
- ProjectGUID="{4A58CE49-541E-43D9-92CD-7E85EA7C96AF}"
- RootNamespace="jack_backend"
- >
- <Platforms>
- <Platform
- Name="Win32"
- />
- </Platforms>
- <ToolFiles>
- </ToolFiles>
- <Configurations>
- <Configuration
- Name="Debug 32|Win32"
- OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
- IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
- ConfigurationType="2"
- InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
- Optimization="0"
- AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;"$(GenericIncludeFolder)\ardourext";..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;"..\..\vamp-sdk";"..\..\midi++2";..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor""
- PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_DEBUG;DEBUG="Debug";ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_jack\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
- MinimalRebuild="true"
- RuntimeLibrary="3"
- WarningLevel="3"
- DebugInformationFormat="3"
- CompileAs="2"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="libjackD.lib $(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib lilv-0D.lib serd-0D.lib sord-0D.lib sratom-0D.lib suil-0D.lib suil_win_in_gtk2.lib"
- OutputFile="$(OutDir)\$(ProjectName)D.dll"
- AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
- IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
- GenerateDebugInformation="true"
- SubSystem="2"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- UseFAT32Workaround="true"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
- />
- </Configuration>
- <Configuration
- Name="Release 32|Win32"
- OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
- IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
- ConfigurationType="2"
- InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
- CharacterSet="2"
- WholeProgramOptimization="1"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
- Optimization="2"
- InlineFunctionExpansion="1"
- AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;"$(GenericIncludeFolder)\ardourext";..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;"..\..\vamp-sdk";"..\..\midi++2";..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor""
- PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_jack\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
- StringPooling="false"
- RuntimeLibrary="2"
- EnableEnhancedInstructionSet="1"
- WarningLevel="3"
- CompileAs="2"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="libjack.lib $(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib lilv-0.lib serd-0.lib sord-0.lib sratom-0.lib suil-0.lib suil_win_in_gtk2.lib"
- OutputFile="$(OutDir)\$(ProjectName).dll"
- AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
- IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
- SubSystem="2"
- OptimizeReferences="2"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- UseFAT32Workaround="true"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
- />
- </Configuration>
- <Configuration
- Name="Release 32 with Debugging Capability|Win32"
- OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
- IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
- ConfigurationType="2"
- InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
- CharacterSet="2"
- WholeProgramOptimization="0"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
- Optimization="0"
- AdditionalIncludeDirectories="..;..\..\ardour;..\..\pbd;..\..\fst;"$(GenericIncludeFolder)\ardourext";..\..\surfaces\control_protocol;..\..\evoral;..\..\libltc;..\..\timecode;..\..\rubberband;"..\..\vamp-sdk";"..\..\midi++2";..\..\taglib;..\..\taglib\taglib;..\..\taglib\taglib\toolkit;..\..\audiographer;"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor""
- PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_JACK_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_jack\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
- StringPooling="false"
- RuntimeLibrary="2"
- EnableEnhancedInstructionSet="1"
- WarningLevel="3"
- DebugInformationFormat="3"
- CompileAs="2"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="libjack.lib $(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib"
- OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
- AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
- IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
- GenerateDebugInformation="true"
- SubSystem="2"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- UseFAT32Workaround="true"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
- />
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Source Files"
- Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
- UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
- >
- <File
- RelativePath="..\jack\jack_api.cc"
- >
- </File>
- <File
- RelativePath="..\jack\jack_audiobackend.cc"
- >
- </File>
- <File
- RelativePath="..\jack\jack_connection.cc"
- >
- </File>
- <File
- RelativePath="..\jack\jack_portengine.cc"
- >
- </File>
- <File
- RelativePath="..\jack\jack_session.cc"
- >
- </File>
- <File
- RelativePath="..\jack\jack_utils.cc"
- >
- </File>
- </Filter>
- <Filter
- Name="Header Files"
- Filter="h;hpp;hxx;hm;inl;inc;xsd"
- UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
- >
- <File
- RelativePath="..\jack\jack_audiobackend.h"
- >
- </File>
- <File
- RelativePath="..\jack\jack_connection.h"
- >
- </File>
- <File
- RelativePath="..\jack\jack_utils.h"
- >
- </File>
- </Filter>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
--- /dev/null
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="waves_audiobackend"
+ ProjectGUID="{D7B1537C-C244-4D86-BBBF-74A1801AB984}"
+ RootNamespace="waves_audiobackend"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug 32|Win32"
+ OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;"..\..\midi++2";"$(GenericIncludeFolder)\ardourext";"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";..\..\..\..\support\MB3WavesPortaudio\include;..\..\..\..\support\MB3WavesPortaudio\src\hostapi\asio\ASIOSDK\common;..\.."
+ PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;_DEBUG;DEBUG="Debug";ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_waves\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
+ MinimalRebuild="true"
+ RuntimeLibrary="3"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ CompileAs="2"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib waves_portaudio_x86D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)ardour32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+ OutputFile="$(OutDir)\$(ProjectName)D.dll"
+ AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+ IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ UseFAT32Workaround="true"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release 32|Win32"
+ OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+ Optimization="2"
+ InlineFunctionExpansion="1"
+ AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;"..\..\midi++2";"$(GenericIncludeFolder)\ardourext";"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";..\..\..\..\support\MB3WavesPortaudio\include;..\..\..\..\support\MB3WavesPortaudio\src\hostapi\asio\ASIOSDK\common;..\.."
+ PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_waves\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
+ StringPooling="false"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="1"
+ WarningLevel="3"
+ CompileAs="2"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib waves_portaudio_x86.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)ardour32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+ OutputFile="$(OutDir)\$(ProjectName).dll"
+ AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+ IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+ SubSystem="2"
+ OptimizeReferences="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ UseFAT32Workaround="true"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release 32 with Debugging Capability|Win32"
+ OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
+ IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
+ ConfigurationType="2"
+ InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
+ CharacterSet="2"
+ WholeProgramOptimization="0"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
+ Optimization="0"
+ AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;"..\..\midi++2";"$(GenericIncludeFolder)\ardourext";"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";..\..\..\..\support\MB3WavesPortaudio\include;..\..\..\..\support\MB3WavesPortaudio\src\hostapi\asio\ASIOSDK\common;..\.."
+ PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_waves\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
+ StringPooling="false"
+ RuntimeLibrary="2"
+ EnableEnhancedInstructionSet="1"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ CompileAs="2"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib waves_portaudio_x86RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)ardour32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib setupapi.lib"
+ OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
+ AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
+ IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ UseFAT32Workaround="true"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath="..\wavesaudio\waves_audiobackend.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_audiobackend.latency.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_audiobackend.midi.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_audiobackend.port_engine.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_audioport.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_dataport.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midi_buffer.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midi_device.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midi_device_manager.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midi_event.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midiport.cc"
+ >
+ </File>
+ <Filter
+ Name="wavesapi"
+ >
+ <File
+ RelativePath="..\wavesaudio\wavesapi\miscutils\UMicroseconds.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRAudioDeviceManager.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRNativeAudio.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRPortAudioDeviceManager.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\refmanager\WCRefManager.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\threads\WCThreadSafe.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="portmidi"
+ >
+ <File
+ RelativePath="..\wavesaudio\portmidi\src\pm_common\pmutil.c"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwin.c"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwinmm.c"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\portmidi\src\pm_common\portmidi.c"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\portmidi\src\porttime\ptwinmm.c"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath="..\wavesaudio\portmidi\src\pm_common\pminternal.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\portmidi\pmutil.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwinmm.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\portmidi\portmidi.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\portmidi\porttime.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_audiobackend.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_audioport.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_dataport.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midi_buffer.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midi_device.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midi_device_manager.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midi_event.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\waves_midiport.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\wavespublicapi\wstdint.h"
+ >
+ </File>
+ <Filter
+ Name="wavesapi"
+ >
+ <File
+ RelativePath="..\wavesaudio\wavesapi\akupara\threading\atomic_ops.hpp"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\akupara\threading\atomic_ops_gcc_x86.hpp"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\akupara\basics.hpp"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\akupara\compiletime_functions.hpp"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\miscutils\MinMaxUtilities.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\miscutils\pthread_utils.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\miscutils\safe_delete.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\miscutils\UMicroseconds.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\miscutils\WCFixedString.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\BasicTypes\WCFourCC.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRAudioDeviceManager.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRNativeAudio.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRPortAudioDeviceManager.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\refmanager\WCRefManager.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\threads\WCThreadSafe.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\BasicTypes\WTByteOrder.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\wavespublicapi\WTErr.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUComPtr.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUDefines.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\miscutils\WUErrors.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUMathConsts.h"
+ >
+ </File>
+ <File
+ RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUTypes.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
+++ /dev/null
-<?xml version="1.0" encoding="Windows-1252"?>
-<VisualStudioProject
- ProjectType="Visual C++"
- Version="8.00"
- Name="waves_backend"
- ProjectGUID="{D7B1537C-C244-4D86-BBBF-74A1801AB984}"
- RootNamespace="waves_backend"
- >
- <Platforms>
- <Platform
- Name="Win32"
- />
- </Platforms>
- <ToolFiles>
- </ToolFiles>
- <Configurations>
- <Configuration
- Name="Debug 32|Win32"
- OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
- IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
- ConfigurationType="2"
- InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
- CharacterSet="2"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
- Optimization="0"
- AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;"..\..\midi++2";"$(GenericIncludeFolder)\ardourext";"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";..\..\..\..\support\PortAudio\include;..\..\..\..\support\PortAudio\src\hostapi\asio\ASIOSDK\common;..\.."
- PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;_DEBUG;DEBUG="Debug";ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_waves\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
- MinimalRebuild="true"
- RuntimeLibrary="3"
- WarningLevel="3"
- DebugInformationFormat="3"
- CompileAs="2"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0D.lib $(DllPrefix)giomm32-2.4-0D.lib portaudio_x86D.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0D.lib $(DllPrefix)gobject32-2.0-0D.lib $(DllPrefix)gmodule32-2.0-0D.lib $(DllPrefix)glib32-2.0-0D.lib $(DllPrefix)gio32-2.0-0D.lib $(DllPrefix)sigc++32-2.0D.lib $(DllPrefix)timecode32D.lib $(DllPrefix)ardour32D.lib $(DllPrefix)pbd32D.lib intlD.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib lilv-0D.lib serd-0D.lib sord-0D.lib sratom-0D.lib suil-0D.lib suil_win_in_gtk2.lib"
- OutputFile="$(OutDir)\$(ProjectName)D.dll"
- AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
- IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;msvcrt.lib;dsound.lib"
- GenerateDebugInformation="true"
- SubSystem="2"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- UseFAT32Workaround="true"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Debug32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
- />
- </Configuration>
- <Configuration
- Name="Release 32|Win32"
- OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
- IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
- ConfigurationType="2"
- InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
- CharacterSet="2"
- WholeProgramOptimization="1"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
- Optimization="2"
- InlineFunctionExpansion="1"
- AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;"..\..\midi++2";"$(GenericIncludeFolder)\ardourext";"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";..\..\..\..\support\PortAudio\include;..\..\..\..\support\PortAudio\src\hostapi\asio\ASIOSDK\common;..\.."
- PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;NDEBUG;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_waves\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
- StringPooling="false"
- RuntimeLibrary="2"
- EnableEnhancedInstructionSet="1"
- WarningLevel="3"
- CompileAs="2"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0.lib $(DllPrefix)giomm32-2.4-0.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0.lib $(DllPrefix)gobject32-2.0-0.lib $(DllPrefix)gmodule32-2.0-0.lib $(DllPrefix)glib32-2.0-0.lib $(DllPrefix)gio32-2.0-0.lib $(DllPrefix)sigc++32-2.0.lib $(DllPrefix)timecode32.lib $(DllPrefix)pbd32.lib intl.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib lilv-0.lib serd-0.lib sord-0.lib sratom-0.lib suil-0.lib suil_win_in_gtk2.lib"
- OutputFile="$(OutDir)\$(ProjectName).dll"
- AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
- IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
- SubSystem="2"
- OptimizeReferences="2"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- UseFAT32Workaround="true"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TargetFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
- />
- </Configuration>
- <Configuration
- Name="Release 32 with Debugging Capability|Win32"
- OutputDirectory="$(ProjectDir)\$(ConfigurationName)\bin"
- IntermediateDirectory="$(ProjectDir)\$(ConfigurationName)\obj\$(ProjectName)"
- ConfigurationType="2"
- InheritedPropertySheets="..\..\..\MSVCMixbus3\MSVCMixbus3.vsprops"
- CharacterSet="2"
- WholeProgramOptimization="0"
- >
- <Tool
- Name="VCPreBuildEventTool"
- />
- <Tool
- Name="VCCustomBuildTool"
- />
- <Tool
- Name="VCXMLDataGeneratorTool"
- />
- <Tool
- Name="VCWebServiceProxyGeneratorTool"
- />
- <Tool
- Name="VCMIDLTool"
- />
- <Tool
- Name="VCCLCompilerTool"
- AdditionalOptions="/FI$(TargetSxsFolder)\targetsxs.h"
- Optimization="0"
- AdditionalIncludeDirectories="..;..\wavesaudio;..\wavesaudio\wavesapi;..\wavesaudio\wavesapi\wavespublicapi;..\wavesaudio\wavesapi\devicemanager;..\wavesaudio\wavesapi\refmanager;..\wavesaudio\wavesapi\threads;..\wavesaudio\wavesapi\miscutils;..\wavesaudio\portmidi\src\pm_common;..\wavesaudio\portmidi;..\..\ardour;..\..\pbd;..\..\timecode;..\..\evoral;"..\..\midi++2";"$(GenericIncludeFolder)\ardourext";"$(GenericLibraryFolder)\glib-2.0\include";"$(GenericIncludeFolder)\libsndfile";"$(GenericIncludeFolder)\gtk-2.0";"$(GenericIncludeFolder)\cairo";"$(GenericIncludeFolder)\freetype2";"$(GenericIncludeFolder)\pango-1.0";"$(GenericIncludeFolder)\gtk-2.0\gdk";"$(GenericIncludeFolder)\atk-2.0";"$(GenericIncludeFolder)\lrdf";"$(GenericIncludeFolder)\raptor";..\..\..\..\support\PortAudio\include;..\..\..\..\support\PortAudio\src\hostapi\asio\ASIOSDK\common;..\.."
- PreprocessorDefinitions="PLATFORM_WINDOWS;COMPILER_MSVC;_SECURE_SCL=0;BUILDING_WAVES_BACKEND;ARDOURBACKEND_DLL_EXPORTS;RUBBERBAND_IS_IN_WIN_STATIC_LIB;NOMINMAX;NO_POSIX_MEMALIGN;INCLUDE_ARDOUR_MISCELLANEOUS=1;BOOST_REGEX_DYN_LINK;BOOST_REGEX_NO_LIB;BOOST_CHRONO_NO_LIB;BOOST_SYSTEM_NO_LIB;BOOST_THREAD_NO_LIB;BOOST_DATE_TIME_NO_LIB;GNU_WIN32;WIN32;_WIN32;_WINDOWS;ARCH_X86;USE_XMMINTRIN;BUILD_SSE_OPTIMIZATIONS;ENABLE_NLS;PROGRAM_NAME="\"Mixbus\"";PACKAGE="\"libardour_waves\"";_REENTRANT;_USE_MATH_DEFINES;_LARGEFILE_SOURCE;_LARGEFILE64_SOURCE;LIBC_DISABLE_DEPRECATED;BOOST_SYSTEM_NO_DEPRECATED;__STDC_LIMIT_MACROS;__STDC_FORMAT_MACROS;INTERNAL_SHARED_LIBS=1;JACK_SESSION=1;HAVE_GLIB=1;HAVE_GTHREAD=1;HAVE_SNDFILE=1;HAVE_GIOMM=1;HAVE_CURL=1;HAVE_LO=1;HAVE_MODE_T=1;PHONE_HOME=1;FREESOUND=1;WINDOWS_KEY=\"Mod4><Super\";IS_OSX=0;HAVE_XML=1;HAVE_UUID=1;HAVE_LIBS_PBD=1;HAVE_JACK=1;HAVE_LIBS_MIDIPP2=1;HAVE_LIBS_EVORAL=1;HAVE_FFTW3=1;HAVE_FFTW3F=1;HAVE_AUBIO=1;HAVE_LIBS_VAMP_SDK=1;HAVE_LIBS_VAMP_PLUGINS=1;HAVE_LIBS_TAGLIB=1;HAVE_LIBS_LIBLTC=1;HAVE_LIBS_RUBBERBAND=1;HAVE_CONTROL_PROTOCOL=1;HAVE_FRONTIER=1;HAVE_GENERIC_MIDI=1;HAVE_MACKIE=1;HAVE_OSC=1;HAVE_TRANZPORT=1;HAVE_WIIMOTE=1;HAVE_LIBS_SURFACES=1;HAVE_2IN2OUT=1;HAVE_1IN2OUT=1;HAVE_VBAP=1;HAVE_LIBS_PANNERS=1;HAVE_LIBS_TIMECODE=1;HAVE_LRDF=1;HAVE_SAMPLERATE=1;HAVE_SERD=1;HAVE_SORD=1;HAVE_SRATOM=1;HAVE_LILV=1;HAVE_NEW_LILV=1;HAVE_OGG=1;HAVE_FLAC=1;HAVE_RUBBERBAND=1;USE_RUBBERBAND=1;HAVE_JACK_SESSION=1;HAVE_UNISTD=1;HAVE_JACK_ON_INFO_SHUTDOWN=1;HAVE_JACK_VIDEO_SUPPORT=1;HAVE_BOOST_SCOPED_PTR_HPP=1;HAVE_BOOST_PTR_CONTAINER_PTR_LIST_HPP=1;HAVE_LIBS_ARDOUR=1;HAVE_GTKMM=1;HAVE_GTK=1;HAVE_LIBS_GTKMM2EXT=1;HAVE_LIBS_CLEARLOOKS_NEWER=1;HAVE_BOOST_FORMAT_HPP=1;HAVE_LIBS_AUDIOGRAPHER=1;HAVE_GNOMECANVAS=0;HAVE_GNOMECANVASMM=0;HAVE_X11=0;HAVE_FONTCONFIG=1;HAVE_BOOST_SHARED_PTR_HPP=1;HAVE_BOOST_WEAK_PTR_HPP=1;HAVE_GTK2_ARDOUR=1;HAVE_EXPORT=1;HAVE_MIDI_MAPS=1;HAVE_MCP=1;HAVE_PATCHFILES=1;HAVE_TOOLS_SANITY_CHECK=1;SMF_VERSION=\"1.2\";CURRENT_SESSION_FILE_VERSION=3001"
- StringPooling="false"
- RuntimeLibrary="2"
- EnableEnhancedInstructionSet="1"
- WarningLevel="3"
- DebugInformationFormat="3"
- CompileAs="2"
- />
- <Tool
- Name="VCManagedResourceCompilerTool"
- />
- <Tool
- Name="VCResourceCompilerTool"
- />
- <Tool
- Name="VCPreLinkEventTool"
- />
- <Tool
- Name="VCLinkerTool"
- AdditionalDependencies="$(DllPrefix)glibmm32-2.4-0RDC.lib $(DllPrefix)giomm32-2.4-0RDC.lib pthreadVCE2.lib $(DllPrefix)gthread32-2.0-0RDC.lib $(DllPrefix)gobject32-2.0-0RDC.lib $(DllPrefix)gmodule32-2.0-0RDC.lib $(DllPrefix)glib32-2.0-0RDC.lib $(DllPrefix)gio32-2.0-0RDC.lib $(DllPrefix)sigc++32-2.0RDC.lib $(DllPrefix)timecode32RDC.lib $(DllPrefix)pbd32RDC.lib intlRDC.lib ws2_32.lib psapi.lib wininet.lib kernel32.lib shell32.lib winmm.lib"
- OutputFile="$(OutDir)\$(ProjectName)RDC.dll"
- AdditionalLibraryDirectories="F:\pthread-win32\Pre-built.2\lib"
- IgnoreDefaultLibraryNames="libboost_regex-vc80-mt-gd-1_40.lib;dsound.lib"
- GenerateDebugInformation="true"
- SubSystem="2"
- />
- <Tool
- Name="VCALinkTool"
- />
- <Tool
- Name="VCManifestTool"
- UseFAT32Workaround="true"
- />
- <Tool
- Name="VCXDCMakeTool"
- />
- <Tool
- Name="VCBscMakeTool"
- />
- <Tool
- Name="VCFxCopTool"
- />
- <Tool
- Name="VCAppVerifierTool"
- />
- <Tool
- Name="VCWebDeploymentTool"
- />
- <Tool
- Name="VCPostBuildEventTool"
- CommandLine="copy /Y "$(OutDir)\$(TargetName).dll" "$(Release32TestSuiteFolder)\..\lib\ardour3\backends\$(TargetName).dll"
copy /Y "$(OutDir)\$(TargetName).lib" "$(GenericWin32LibraryFolder)\$(TargetName).lib"
"
- />
- </Configuration>
- </Configurations>
- <References>
- </References>
- <Files>
- <Filter
- Name="Source Files"
- Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
- UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
- >
- <File
- RelativePath="..\wavesaudio\waves_audiobackend.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_audiobackend.latency.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_audiobackend.midi.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_audiobackend.port_engine.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_audioport.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_dataport.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midi_buffer.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midi_device.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midi_device_manager.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midi_event.cc"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midiport.cc"
- >
- </File>
- <Filter
- Name="wavesapi"
- >
- <File
- RelativePath="..\wavesaudio\wavesapi\miscutils\UMicroseconds.cpp"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRAudioDeviceManager.cpp"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRNativeAudio.cpp"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRPortAudioDeviceManager.cpp"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\refmanager\WCRefManager.cpp"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\threads\WCThreadSafe.cpp"
- >
- </File>
- </Filter>
- <Filter
- Name="portmidi"
- >
- <File
- RelativePath="..\wavesaudio\portmidi\src\pm_common\pmutil.c"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwin.c"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwinmm.c"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\portmidi\src\pm_common\portmidi.c"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\portmidi\src\porttime\ptwinmm.c"
- >
- </File>
- </Filter>
- </Filter>
- <Filter
- Name="Header Files"
- Filter="h;hpp;hxx;hm;inl;inc;xsd"
- UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
- >
- <File
- RelativePath="..\wavesaudio\portmidi\src\pm_common\pminternal.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\portmidi\pmutil.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\portmidi\src\pm_win\pmwinmm.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\portmidi\portmidi.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\portmidi\porttime.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_audiobackend.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_audioport.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_dataport.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midi_buffer.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midi_device.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midi_device_manager.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midi_event.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\waves_midiport.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\wavespublicapi\wstdint.h"
- >
- </File>
- <Filter
- Name="wavesapi"
- >
- <File
- RelativePath="..\wavesaudio\wavesapi\akupara\threading\atomic_ops.hpp"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\akupara\threading\atomic_ops_gcc_x86.hpp"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\akupara\basics.hpp"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\akupara\compiletime_functions.hpp"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\miscutils\MinMaxUtilities.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\miscutils\pthread_utils.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\miscutils\safe_delete.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\miscutils\UMicroseconds.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\miscutils\WCFixedString.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\BasicTypes\WCFourCC.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRAudioDeviceManager.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRNativeAudio.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\devicemanager\WCMRPortAudioDeviceManager.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\refmanager\WCRefManager.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\threads\WCThreadSafe.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\BasicTypes\WTByteOrder.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\wavespublicapi\WTErr.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUComPtr.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUDefines.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\miscutils\WUErrors.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUMathConsts.h"
- >
- </File>
- <File
- RelativePath="..\wavesaudio\wavesapi\BasicTypes\WUTypes.h"
- >
- </File>
- </Filter>
- </Filter>
- </Files>
- <Globals>
- </Globals>
-</VisualStudioProject>
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013 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 <regex.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+
+#include <glibmm.h>
+
+#include "alsa_audiobackend.h"
+#include "rt_thread.h"
+
+#include "pbd/compose.h"
+#include "pbd/error.h"
+#include "pbd/file_utils.h"
+#include "ardour/filesystem_paths.h"
+#include "ardour/port_manager.h"
+#include "ardouralsautil/devicelist.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+static std::string s_instance_name;
+size_t AlsaAudioBackend::_max_buffer_size = 8192;
+std::vector<std::string> AlsaAudioBackend::_midi_options;
+std::vector<AudioBackend::DeviceStatus> AlsaAudioBackend::_audio_device_status;
+std::vector<AudioBackend::DeviceStatus> AlsaAudioBackend::_midi_device_status;
+
+AlsaAudioBackend::AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info)
+ : AudioBackend (e, info)
+ , _pcmi (0)
+ , _run (false)
+ , _active (false)
+ , _freewheeling (false)
+ , _measure_latency (false)
+ , _audio_device("")
+ , _midi_driver_option(_("None"))
+ , _device_reservation(0)
+ , _samplerate (48000)
+ , _samples_per_period (1024)
+ , _periods_per_cycle (2)
+ , _n_inputs (0)
+ , _n_outputs (0)
+ , _systemic_audio_input_latency (0)
+ , _systemic_audio_output_latency (0)
+ , _dsp_load (0)
+ , _processed_samples (0)
+ , _port_change_flag (false)
+{
+ _instance_name = s_instance_name;
+ pthread_mutex_init (&_port_callback_mutex, 0);
+}
+
+AlsaAudioBackend::~AlsaAudioBackend ()
+{
+ pthread_mutex_destroy (&_port_callback_mutex);
+}
+
+/* AUDIOBACKEND API */
+
+std::string
+AlsaAudioBackend::name () const
+{
+ return X_("ALSA");
+}
+
+bool
+AlsaAudioBackend::is_realtime () const
+{
+ return true;
+}
+
+std::vector<AudioBackend::DeviceStatus>
+AlsaAudioBackend::enumerate_devices () const
+{
+ _audio_device_status.clear();
+ std::map<std::string, std::string> devices;
+ get_alsa_audio_device_names(devices);
+ for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+ _audio_device_status.push_back (DeviceStatus (i->first, true));
+ }
+ return _audio_device_status;
+}
+
+void
+AlsaAudioBackend::reservation_stdout (std::string d, size_t /* s */)
+{
+ if (d.substr(0, 19) == "Acquired audio-card") {
+ _reservation_succeeded = true;
+ }
+}
+
+void
+AlsaAudioBackend::release_device()
+{
+ _reservation_connection.drop_connections();
+ ARDOUR::SystemExec * tmp = _device_reservation;
+ _device_reservation = 0;
+ delete tmp;
+}
+
+bool
+AlsaAudioBackend::acquire_device(const char* device_name)
+{
+ /* This is quick hack, ideally we'll link against libdbus and implement a dbus-listener
+ * that owns the device. here we try to get away by just requesting it and then block it...
+ * (pulseaudio periodically checks anyway)
+ *
+ * dbus-send --session --print-reply --type=method_call --dest=org.freedesktop.ReserveDevice1.Audio2 /org/freedesktop/ReserveDevice1/Audio2 org.freedesktop.ReserveDevice1.RequestRelease int32:4
+ * -> should not return 'boolean false'
+ */
+ int device_number = card_to_num(device_name);
+ if (device_number < 0) return false;
+
+ assert(_device_reservation == 0);
+ _reservation_succeeded = false;
+
+ std::string request_device_exe;
+ if (!PBD::find_file (
+ PBD::Searchpath(Glib::build_filename(ARDOUR::ardour_dll_directory(), "ardouralsautil")
+ + G_SEARCHPATH_SEPARATOR_S + ARDOUR::ardour_dll_directory()),
+ "ardour-request-device", request_device_exe))
+ {
+ PBD::warning << "ardour-request-device binary was not found..'" << endmsg;
+ return false;
+ }
+ else
+ {
+ char **argp;
+ char tmp[128];
+ argp=(char**) calloc(5,sizeof(char*));
+ argp[0] = strdup(request_device_exe.c_str());
+ argp[1] = strdup("-P");
+ snprintf(tmp, sizeof(tmp), "%d", getpid());
+ argp[2] = strdup(tmp);
+ snprintf(tmp, sizeof(tmp), "Audio%d", device_number);
+ argp[3] = strdup(tmp);
+ argp[4] = 0;
+
+ _device_reservation = new ARDOUR::SystemExec(request_device_exe, argp);
+ _device_reservation->ReadStdout.connect_same_thread (_reservation_connection, boost::bind (&AlsaAudioBackend::reservation_stdout, this, _1 ,_2));
+ _device_reservation->Terminated.connect_same_thread (_reservation_connection, boost::bind (&AlsaAudioBackend::release_device, this));
+ if (_device_reservation->start(0)) {
+ PBD::warning << _("AlsaAudioBackend: Device Request failed.") << endmsg;
+ release_device();
+ return false;
+ }
+ }
+ // wait to check if reservation suceeded.
+ int timeout = 500; // 5 sec
+ while (_device_reservation && !_reservation_succeeded && --timeout > 0) {
+ Glib::usleep(10000);
+ }
+ if (timeout == 0 || !_reservation_succeeded) {
+ PBD::warning << _("AlsaAudioBackend: Device Reservation failed.") << endmsg;
+ release_device();
+ return false;
+ }
+ return true;
+}
+
+std::vector<float>
+AlsaAudioBackend::available_sample_rates (const std::string&) const
+{
+ std::vector<float> sr;
+ sr.push_back (8000.0);
+ sr.push_back (22050.0);
+ sr.push_back (24000.0);
+ sr.push_back (44100.0);
+ sr.push_back (48000.0);
+ sr.push_back (88200.0);
+ sr.push_back (96000.0);
+ sr.push_back (176400.0);
+ sr.push_back (192000.0);
+ return sr;
+}
+
+std::vector<uint32_t>
+AlsaAudioBackend::available_buffer_sizes (const std::string&) const
+{
+ std::vector<uint32_t> bs;
+ bs.push_back (32);
+ bs.push_back (64);
+ bs.push_back (128);
+ bs.push_back (256);
+ bs.push_back (512);
+ bs.push_back (1024);
+ bs.push_back (2048);
+ bs.push_back (4096);
+ bs.push_back (8192);
+ return bs;
+}
+
+uint32_t
+AlsaAudioBackend::available_input_channel_count (const std::string&) const
+{
+ return 128; // TODO query current device
+}
+
+uint32_t
+AlsaAudioBackend::available_output_channel_count (const std::string&) const
+{
+ return 128; // TODO query current device
+}
+
+bool
+AlsaAudioBackend::can_change_sample_rate_when_running () const
+{
+ return false;
+}
+
+bool
+AlsaAudioBackend::can_change_buffer_size_when_running () const
+{
+ return false;
+}
+
+int
+AlsaAudioBackend::set_device_name (const std::string& d)
+{
+ _audio_device = d;
+ return 0;
+}
+
+int
+AlsaAudioBackend::set_sample_rate (float sr)
+{
+ if (sr <= 0) { return -1; }
+ _samplerate = sr;
+ engine.sample_rate_change (sr);
+ return 0;
+}
+
+int
+AlsaAudioBackend::set_buffer_size (uint32_t bs)
+{
+ if (bs <= 0 || bs >= _max_buffer_size) {
+ return -1;
+ }
+ _samples_per_period = bs;
+ engine.buffer_size_change (bs);
+ return 0;
+}
+
+int
+AlsaAudioBackend::set_interleaved (bool yn)
+{
+ if (!yn) { return 0; }
+ return -1;
+}
+
+int
+AlsaAudioBackend::set_input_channels (uint32_t cc)
+{
+ _n_inputs = cc;
+ return 0;
+}
+
+int
+AlsaAudioBackend::set_output_channels (uint32_t cc)
+{
+ _n_outputs = cc;
+ return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_input_latency (uint32_t sl)
+{
+ _systemic_audio_input_latency = sl;
+ return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_output_latency (uint32_t sl)
+{
+ _systemic_audio_output_latency = sl;
+ return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_midi_input_latency (std::string const device, uint32_t sl)
+{
+ struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+ if (!nfo) return -1;
+ nfo->systemic_input_latency = sl;
+ return 0;
+}
+
+int
+AlsaAudioBackend::set_systemic_midi_output_latency (std::string const device, uint32_t sl)
+{
+ struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+ if (!nfo) return -1;
+ nfo->systemic_output_latency = sl;
+ return 0;
+}
+
+/* Retrieving parameters */
+std::string
+AlsaAudioBackend::device_name () const
+{
+ return _audio_device;
+}
+
+float
+AlsaAudioBackend::sample_rate () const
+{
+ return _samplerate;
+}
+
+uint32_t
+AlsaAudioBackend::buffer_size () const
+{
+ return _samples_per_period;
+}
+
+bool
+AlsaAudioBackend::interleaved () const
+{
+ return false;
+}
+
+uint32_t
+AlsaAudioBackend::input_channels () const
+{
+ return _n_inputs;
+}
+
+uint32_t
+AlsaAudioBackend::output_channels () const
+{
+ return _n_outputs;
+}
+
+uint32_t
+AlsaAudioBackend::systemic_input_latency () const
+{
+ return _systemic_audio_input_latency;
+}
+
+uint32_t
+AlsaAudioBackend::systemic_output_latency () const
+{
+ return _systemic_audio_output_latency;
+}
+
+uint32_t
+AlsaAudioBackend::systemic_midi_input_latency (std::string const device) const
+{
+ struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+ if (!nfo) return 0;
+ return nfo->systemic_input_latency;
+}
+
+uint32_t
+AlsaAudioBackend::systemic_midi_output_latency (std::string const device) const
+{
+ struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+ if (!nfo) return 0;
+ return nfo->systemic_output_latency;
+}
+
+/* MIDI */
+struct AlsaAudioBackend::AlsaMidiDeviceInfo *
+AlsaAudioBackend::midi_device_info(std::string const name) const {
+ for (std::map<std::string, struct AlsaMidiDeviceInfo*>::const_iterator i = _midi_devices.begin (); i != _midi_devices.end(); ++i) {
+ if (i->first == name) {
+ return (i->second);
+ }
+ }
+
+ assert(_midi_driver_option != _("None"));
+
+ std::map<std::string, std::string> devices;
+ if (_midi_driver_option == _("ALSA raw devices")) {
+ get_alsa_rawmidi_device_names(devices);
+ } else {
+ get_alsa_sequencer_names (devices);
+ }
+
+ for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+ if (i->first == name) {
+ _midi_devices[name] = new AlsaMidiDeviceInfo();
+ return _midi_devices[name];
+ }
+ }
+ return 0;
+}
+
+std::vector<std::string>
+AlsaAudioBackend::enumerate_midi_options () const
+{
+ if (_midi_options.empty()) {
+ _midi_options.push_back (_("None"));
+ _midi_options.push_back (_("ALSA raw devices"));
+ _midi_options.push_back (_("ALSA sequencer"));
+ }
+ return _midi_options;
+}
+
+std::vector<AudioBackend::DeviceStatus>
+AlsaAudioBackend::enumerate_midi_devices () const
+{
+ _midi_device_status.clear();
+ std::map<std::string, std::string> devices;
+
+ if (_midi_driver_option == _("ALSA raw devices")) {
+ get_alsa_rawmidi_device_names (devices);
+ }
+ else if (_midi_driver_option == _("ALSA sequencer")) {
+ get_alsa_sequencer_names (devices);
+ }
+
+ for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+ _midi_device_status.push_back (DeviceStatus (i->first, true));
+ }
+ return _midi_device_status;
+}
+
+int
+AlsaAudioBackend::set_midi_option (const std::string& opt)
+{
+ if (opt != _("None") && opt != _("ALSA raw devices") && opt != _("ALSA sequencer")) {
+ return -1;
+ }
+ _midi_driver_option = opt;
+ return 0;
+}
+
+std::string
+AlsaAudioBackend::midi_option () const
+{
+ return _midi_driver_option;
+}
+
+int
+AlsaAudioBackend::set_midi_device_enabled (std::string const device, bool enable)
+{
+ struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+ if (!nfo) return -1;
+ nfo->enabled = enable;
+ return 0;
+}
+
+bool
+AlsaAudioBackend::midi_device_enabled (std::string const device) const
+{
+ struct AlsaMidiDeviceInfo * nfo = midi_device_info(device);
+ if (!nfo) return false;
+ return nfo->enabled;
+}
+
+/* State Control */
+
+static void * pthread_process (void *arg)
+{
+ AlsaAudioBackend *d = static_cast<AlsaAudioBackend *>(arg);
+ d->main_process_thread ();
+ pthread_exit (0);
+ return 0;
+}
+
+int
+AlsaAudioBackend::_start (bool for_latency_measurement)
+{
+ if (!_active && _run) {
+ // recover from 'halted', reap threads
+ stop();
+ }
+
+ if (_active || _run) {
+ PBD::error << _("AlsaAudioBackend: already active.") << endmsg;
+ return -1;
+ }
+
+ if (_ports.size()) {
+ PBD::warning << _("AlsaAudioBackend: recovering from unclean shutdown, port registry is not empty.") << endmsg;
+ _system_inputs.clear();
+ _system_outputs.clear();
+ _system_midi_in.clear();
+ _system_midi_out.clear();
+ _ports.clear();
+ }
+
+ release_device();
+
+ assert(_rmidi_in.size() == 0);
+ assert(_rmidi_out.size() == 0);
+ assert(_pcmi == 0);
+
+ std::string alsa_device;
+ std::map<std::string, std::string> devices;
+ get_alsa_audio_device_names(devices);
+ for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+ if (i->first == _audio_device) {
+ alsa_device = i->second;
+ break;
+ }
+ }
+
+ acquire_device(alsa_device.c_str());
+ _pcmi = new Alsa_pcmi (alsa_device.c_str(), alsa_device.c_str(), 0, _samplerate, _samples_per_period, _periods_per_cycle, 0);
+ switch (_pcmi->state ()) {
+ case 0: /* OK */ break;
+ case -1: PBD::error << _("AlsaAudioBackend: failed to open device.") << endmsg; break;
+ case -2: PBD::error << _("AlsaAudioBackend: failed to allocate parameters.") << endmsg; break;
+ case -3: PBD::error << _("AlsaAudioBackend: cannot set requested sample rate.") << endmsg; break;
+ case -4: PBD::error << _("AlsaAudioBackend: cannot set requested period size.") << endmsg; break;
+ case -5: PBD::error << _("AlsaAudioBackend: cannot set requested number of periods.") << endmsg; break;
+ case -6: PBD::error << _("AlsaAudioBackend: unsupported sample format.") << endmsg; break;
+ default: PBD::error << _("AlsaAudioBackend: initialization failed.") << endmsg; break;
+ }
+ if (_pcmi->state ()) {
+ delete _pcmi; _pcmi = 0;
+ release_device();
+ return -1;
+ }
+
+#ifndef NDEBUG
+ _pcmi->printinfo ();
+#endif
+
+ if (_n_outputs != _pcmi->nplay ()) {
+ if (_n_outputs == 0) {
+ _n_outputs = _pcmi->nplay ();
+ } else {
+ _n_outputs = std::min (_n_outputs, _pcmi->nplay ());
+ }
+ PBD::warning << _("AlsaAudioBackend: adjusted output channel count to match device.") << endmsg;
+ }
+
+ if (_n_inputs != _pcmi->ncapt ()) {
+ if (_n_inputs == 0) {
+ _n_inputs = _pcmi->ncapt ();
+ } else {
+ _n_inputs = std::min (_n_inputs, _pcmi->ncapt ());
+ }
+ PBD::warning << _("AlsaAudioBackend: adjusted input channel count to match device.") << endmsg;
+ }
+
+ if (_pcmi->fsize() != _samples_per_period) {
+ _samples_per_period = _pcmi->fsize();
+ PBD::warning << _("AlsaAudioBackend: samples per period does not match.") << endmsg;
+ }
+
+ if (_pcmi->fsamp() != _samplerate) {
+ _samplerate = _pcmi->fsamp();
+ engine.sample_rate_change (_samplerate);
+ PBD::warning << _("AlsaAudioBackend: sample rate does not match.") << endmsg;
+ }
+
+ _measure_latency = for_latency_measurement;
+
+ register_system_midi_ports();
+
+ if (register_system_audio_ports()) {
+ PBD::error << _("AlsaAudioBackend: failed to register system ports.") << endmsg;
+ delete _pcmi; _pcmi = 0;
+ release_device();
+ return -1;
+ }
+
+ engine.sample_rate_change (_samplerate);
+ engine.buffer_size_change (_samples_per_period);
+
+ if (engine.reestablish_ports ()) {
+ PBD::error << _("AlsaAudioBackend: Could not re-establish ports.") << endmsg;
+ delete _pcmi; _pcmi = 0;
+ release_device();
+ return -1;
+ }
+
+ engine.reconnect_ports ();
+ _run = true;
+ _port_change_flag = false;
+
+ if (_realtime_pthread_create (SCHED_FIFO, -20, 100000,
+ &_main_thread, pthread_process, this))
+ {
+ if (pthread_create (&_main_thread, NULL, pthread_process, this))
+ {
+ PBD::error << _("AlsaAudioBackend: failed to create process thread.") << endmsg;
+ delete _pcmi; _pcmi = 0;
+ release_device();
+ _run = false;
+ return -1;
+ } else {
+ PBD::warning << _("AlsaAudioBackend: cannot acquire realtime permissions.") << endmsg;
+ }
+ }
+
+ int timeout = 5000;
+ while (!_active && --timeout > 0) { Glib::usleep (1000); }
+
+ if (timeout == 0 || !_active) {
+ PBD::error << _("AlsaAudioBackend: failed to start process thread.") << endmsg;
+ delete _pcmi; _pcmi = 0;
+ release_device();
+ _run = false;
+ return -1;
+ }
+
+ return 0;
+}
+
+int
+AlsaAudioBackend::stop ()
+{
+ void *status;
+ if (!_run) {
+ return 0;
+ }
+
+ _run = false;
+ if (pthread_join (_main_thread, &status)) {
+ PBD::error << _("AlsaAudioBackend: failed to terminate.") << endmsg;
+ return -1;
+ }
+
+ while (!_rmidi_out.empty ()) {
+ AlsaMidiIO *m = _rmidi_out.back ();
+ m->stop();
+ _rmidi_out.pop_back ();
+ delete m;
+ }
+ while (!_rmidi_in.empty ()) {
+ AlsaMidiIO *m = _rmidi_in.back ();
+ m->stop();
+ _rmidi_in.pop_back ();
+ delete m;
+ }
+
+ unregister_system_ports();
+ delete _pcmi; _pcmi = 0;
+ release_device();
+
+ return (_active == false) ? 0 : -1;
+}
+
+int
+AlsaAudioBackend::freewheel (bool onoff)
+{
+ if (onoff == _freewheeling) {
+ return 0;
+ }
+ _freewheeling = onoff;
+ engine.freewheel_callback (onoff);
+ return 0;
+}
+
+float
+AlsaAudioBackend::dsp_load () const
+{
+ return 100.f * _dsp_load;
+}
+
+size_t
+AlsaAudioBackend::raw_buffer_size (DataType t)
+{
+ switch (t) {
+ case DataType::AUDIO:
+ return _samples_per_period * sizeof(Sample);
+ case DataType::MIDI:
+ return _max_buffer_size; // XXX not really limited
+ }
+ return 0;
+}
+
+/* Process time */
+pframes_t
+AlsaAudioBackend::sample_time ()
+{
+ return _processed_samples;
+}
+
+pframes_t
+AlsaAudioBackend::sample_time_at_cycle_start ()
+{
+ return _processed_samples;
+}
+
+pframes_t
+AlsaAudioBackend::samples_since_cycle_start ()
+{
+ return 0;
+}
+
+
+void *
+AlsaAudioBackend::alsa_process_thread (void *arg)
+{
+ ThreadData* td = reinterpret_cast<ThreadData*> (arg);
+ boost::function<void ()> f = td->f;
+ delete td;
+ f ();
+ return 0;
+}
+
+int
+AlsaAudioBackend::create_process_thread (boost::function<void()> func)
+{
+ pthread_t thread_id;
+ pthread_attr_t attr;
+ size_t stacksize = 100000;
+
+ ThreadData* td = new ThreadData (this, func, stacksize);
+
+ if (_realtime_pthread_create (SCHED_FIFO, -21, stacksize,
+ &thread_id, alsa_process_thread, td)) {
+ pthread_attr_init (&attr);
+ pthread_attr_setstacksize (&attr, stacksize);
+ if (pthread_create (&thread_id, &attr, alsa_process_thread, td)) {
+ PBD::error << _("AudioEngine: cannot create process thread.") << endmsg;
+ pthread_attr_destroy (&attr);
+ return -1;
+ }
+ pthread_attr_destroy (&attr);
+ }
+
+ _threads.push_back (thread_id);
+ return 0;
+}
+
+int
+AlsaAudioBackend::join_process_threads ()
+{
+ int rv = 0;
+
+ for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
+ {
+ void *status;
+ if (pthread_join (*i, &status)) {
+ PBD::error << _("AudioEngine: cannot terminate process thread.") << endmsg;
+ rv -= 1;
+ }
+ }
+ _threads.clear ();
+ return rv;
+}
+
+bool
+AlsaAudioBackend::in_process_thread ()
+{
+ for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
+ {
+ if (pthread_equal (*i, pthread_self ()) != 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+uint32_t
+AlsaAudioBackend::process_thread_count ()
+{
+ return _threads.size ();
+}
+
+void
+AlsaAudioBackend::update_latencies ()
+{
+ // trigger latency callback in RT thread (locked graph)
+ port_connect_add_remove_callback();
+}
+
+/* PORTENGINE API */
+
+void*
+AlsaAudioBackend::private_handle () const
+{
+ return NULL;
+}
+
+const std::string&
+AlsaAudioBackend::my_name () const
+{
+ return _instance_name;
+}
+
+bool
+AlsaAudioBackend::available () const
+{
+ return _run && _active;
+}
+
+uint32_t
+AlsaAudioBackend::port_name_size () const
+{
+ return 256;
+}
+
+int
+AlsaAudioBackend::set_port_name (PortEngine::PortHandle port, const std::string& name)
+{
+ if (!valid_port (port)) {
+ PBD::error << _("AlsaBackend::set_port_name: Invalid Port(s)") << endmsg;
+ return -1;
+ }
+ return static_cast<AlsaPort*>(port)->set_name (_instance_name + ":" + name);
+}
+
+std::string
+AlsaAudioBackend::get_port_name (PortEngine::PortHandle port) const
+{
+ if (!valid_port (port)) {
+ PBD::error << _("AlsaBackend::get_port_name: Invalid Port(s)") << endmsg;
+ return std::string ();
+ }
+ return static_cast<AlsaPort*>(port)->name ();
+}
+
+PortEngine::PortHandle
+AlsaAudioBackend::get_port_by_name (const std::string& name) const
+{
+ PortHandle port = (PortHandle) find_port (name);
+ return port;
+}
+
+int
+AlsaAudioBackend::get_ports (
+ const std::string& port_name_pattern,
+ DataType type, PortFlags flags,
+ std::vector<std::string>& port_names) const
+{
+ int rv = 0;
+ regex_t port_regex;
+ bool use_regexp = false;
+ if (port_name_pattern.size () > 0) {
+ if (!regcomp (&port_regex, port_name_pattern.c_str (), REG_EXTENDED|REG_NOSUB)) {
+ use_regexp = true;
+ }
+ }
+ for (size_t i = 0; i < _ports.size (); ++i) {
+ AlsaPort* port = _ports[i];
+ if ((port->type () == type) && (port->flags () & flags)) {
+ if (!use_regexp || !regexec (&port_regex, port->name ().c_str (), 0, NULL, 0)) {
+ port_names.push_back (port->name ());
+ ++rv;
+ }
+ }
+ }
+ if (use_regexp) {
+ regfree (&port_regex);
+ }
+ return rv;
+}
+
+DataType
+AlsaAudioBackend::port_data_type (PortEngine::PortHandle port) const
+{
+ if (!valid_port (port)) {
+ return DataType::NIL;
+ }
+ return static_cast<AlsaPort*>(port)->type ();
+}
+
+PortEngine::PortHandle
+AlsaAudioBackend::register_port (
+ const std::string& name,
+ ARDOUR::DataType type,
+ ARDOUR::PortFlags flags)
+{
+ if (name.size () == 0) { return 0; }
+ if (flags & IsPhysical) { return 0; }
+ return add_port (_instance_name + ":" + name, type, flags);
+}
+
+PortEngine::PortHandle
+AlsaAudioBackend::add_port (
+ const std::string& name,
+ ARDOUR::DataType type,
+ ARDOUR::PortFlags flags)
+{
+ assert(name.size ());
+ if (find_port (name)) {
+ PBD::error << _("AlsaBackend::register_port: Port already exists:")
+ << " (" << name << ")" << endmsg;
+ return 0;
+ }
+ AlsaPort* port = NULL;
+ switch (type) {
+ case DataType::AUDIO:
+ port = new AlsaAudioPort (*this, name, flags);
+ break;
+ case DataType::MIDI:
+ port = new AlsaMidiPort (*this, name, flags);
+ break;
+ default:
+ PBD::error << _("AlsaBackend::register_port: Invalid Data Type.") << endmsg;
+ return 0;
+ }
+
+ _ports.push_back (port);
+
+ return port;
+}
+
+void
+AlsaAudioBackend::unregister_port (PortEngine::PortHandle port_handle)
+{
+ if (!valid_port (port_handle)) {
+ PBD::error << _("AlsaBackend::unregister_port: Invalid Port.") << endmsg;
+ }
+ AlsaPort* port = static_cast<AlsaPort*>(port_handle);
+ std::vector<AlsaPort*>::iterator i = std::find (_ports.begin (), _ports.end (), static_cast<AlsaPort*>(port_handle));
+ if (i == _ports.end ()) {
+ PBD::error << _("AlsaBackend::unregister_port: Failed to find port") << endmsg;
+ return;
+ }
+ disconnect_all(port_handle);
+ _ports.erase (i);
+ delete port;
+}
+
+int
+AlsaAudioBackend::register_system_audio_ports()
+{
+ LatencyRange lr;
+
+ const int a_ins = _n_inputs > 0 ? _n_inputs : 2;
+ const int a_out = _n_outputs > 0 ? _n_outputs : 2;
+
+ /* audio ports */
+ lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : _systemic_audio_input_latency);
+ for (int i = 1; i <= a_ins; ++i) {
+ char tmp[64];
+ snprintf(tmp, sizeof(tmp), "system:capture_%d", i);
+ PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
+ if (!p) return -1;
+ set_latency_range (p, false, lr);
+ _system_inputs.push_back(static_cast<AlsaPort*>(p));
+ }
+
+ lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : _systemic_audio_output_latency);
+ for (int i = 1; i <= a_out; ++i) {
+ char tmp[64];
+ snprintf(tmp, sizeof(tmp), "system:playback_%d", i);
+ PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
+ if (!p) return -1;
+ set_latency_range (p, true, lr);
+ _system_outputs.push_back(static_cast<AlsaPort*>(p));
+ }
+ return 0;
+}
+
+int
+AlsaAudioBackend::register_system_midi_ports()
+{
+ std::map<std::string, std::string> devices;
+ int midi_ins = 0;
+ int midi_outs = 0;
+
+ if (_midi_driver_option == _("None")) {
+ return 0;
+ } else if (_midi_driver_option == _("ALSA raw devices")) {
+ get_alsa_rawmidi_device_names(devices);
+ } else {
+ get_alsa_sequencer_names (devices);
+ }
+
+ for (std::map<std::string, std::string>::const_iterator i = devices.begin (); i != devices.end(); ++i) {
+ struct AlsaMidiDeviceInfo * nfo = midi_device_info(i->first);
+ if (!nfo) continue;
+ if (!nfo->enabled) continue;
+
+ AlsaMidiOut *mout;
+ if (_midi_driver_option == _("ALSA raw devices")) {
+ mout = new AlsaRawMidiOut (i->second.c_str());
+ } else {
+ mout = new AlsaSeqMidiOut (i->second.c_str());
+ }
+
+ if (mout->state ()) {
+ PBD::warning << string_compose (
+ _("AlsaMidiOut: failed to open midi device '%1'."), i->second)
+ << endmsg;
+ delete mout;
+ } else {
+ mout->setup_timing(_samples_per_period, _samplerate);
+ mout->sync_time (g_get_monotonic_time());
+ if (mout->start ()) {
+ PBD::warning << string_compose (
+ _("AlsaMidiOut: failed to start midi device '%1'."), i->second)
+ << endmsg;
+ delete mout;
+ } else {
+ char tmp[64];
+ snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", ++midi_ins);
+ PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
+ if (!p) {
+ mout->stop();
+ delete mout;
+ }
+ LatencyRange lr;
+ lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : nfo->systemic_output_latency);
+ set_latency_range (p, false, lr);
+ static_cast<AlsaMidiPort*>(p)->set_n_periods(2);
+ _system_midi_out.push_back(static_cast<AlsaPort*>(p));
+ _rmidi_out.push_back (mout);
+ }
+ }
+
+ AlsaMidiIn *midin;
+ if (_midi_driver_option == _("ALSA raw devices")) {
+ midin = new AlsaRawMidiIn (i->second.c_str());
+ } else {
+ midin = new AlsaSeqMidiIn (i->second.c_str());
+ }
+
+ if (midin->state ()) {
+ PBD::warning << string_compose (
+ _("AlsaMidiIn: failed to open midi device '%1'."), i->second)
+ << endmsg;
+ delete midin;
+ } else {
+ midin->setup_timing(_samples_per_period, _samplerate);
+ midin->sync_time (g_get_monotonic_time());
+ if (midin->start ()) {
+ PBD::warning << string_compose (
+ _("AlsaMidiIn: failed to start midi device '%1'."), i->second)
+ << endmsg;
+ delete midin;
+ } else {
+ char tmp[64];
+ snprintf(tmp, sizeof(tmp), "system:midi_capture_%d", ++midi_outs);
+ PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsOutput | IsPhysical | IsTerminal));
+ if (!p) {
+ midin->stop();
+ delete midin;
+ continue;
+ }
+ LatencyRange lr;
+ lr.min = lr.max = _samples_per_period + (_measure_latency ? 0 : nfo->systemic_input_latency);
+ set_latency_range (p, false, lr);
+ _system_midi_in.push_back(static_cast<AlsaPort*>(p));
+ _rmidi_in.push_back (midin);
+ }
+ }
+ }
+ return 0;
+}
+
+void
+AlsaAudioBackend::unregister_system_ports()
+{
+ size_t i = 0;
+ _system_inputs.clear();
+ _system_outputs.clear();
+ _system_midi_in.clear();
+ _system_midi_out.clear();
+ while (i < _ports.size ()) {
+ AlsaPort* port = _ports[i];
+ if (port->is_physical () && port->is_terminal ()) {
+ port->disconnect_all ();
+ _ports.erase (_ports.begin() + i);
+ } else {
+ ++i;
+ }
+ }
+}
+
+int
+AlsaAudioBackend::connect (const std::string& src, const std::string& dst)
+{
+ AlsaPort* src_port = find_port (src);
+ AlsaPort* dst_port = find_port (dst);
+
+ if (!src_port) {
+ PBD::error << _("AlsaBackend::connect: Invalid Source port:")
+ << " (" << src <<")" << endmsg;
+ return -1;
+ }
+ if (!dst_port) {
+ PBD::error << _("AlsaBackend::connect: Invalid Destination port:")
+ << " (" << dst <<")" << endmsg;
+ return -1;
+ }
+ return src_port->connect (dst_port);
+}
+
+int
+AlsaAudioBackend::disconnect (const std::string& src, const std::string& dst)
+{
+ AlsaPort* src_port = find_port (src);
+ AlsaPort* dst_port = find_port (dst);
+
+ if (!src_port || !dst_port) {
+ PBD::error << _("AlsaBackend::disconnect: Invalid Port(s)") << endmsg;
+ return -1;
+ }
+ return src_port->disconnect (dst_port);
+}
+
+int
+AlsaAudioBackend::connect (PortEngine::PortHandle src, const std::string& dst)
+{
+ AlsaPort* dst_port = find_port (dst);
+ if (!valid_port (src)) {
+ PBD::error << _("AlsaBackend::connect: Invalid Source Port Handle") << endmsg;
+ return -1;
+ }
+ if (!dst_port) {
+ PBD::error << _("AlsaBackend::connect: Invalid Destination Port")
+ << " (" << dst << ")" << endmsg;
+ return -1;
+ }
+ return static_cast<AlsaPort*>(src)->connect (dst_port);
+}
+
+int
+AlsaAudioBackend::disconnect (PortEngine::PortHandle src, const std::string& dst)
+{
+ AlsaPort* dst_port = find_port (dst);
+ if (!valid_port (src) || !dst_port) {
+ PBD::error << _("AlsaBackend::disconnect: Invalid Port(s)") << endmsg;
+ return -1;
+ }
+ return static_cast<AlsaPort*>(src)->disconnect (dst_port);
+}
+
+int
+AlsaAudioBackend::disconnect_all (PortEngine::PortHandle port)
+{
+ if (!valid_port (port)) {
+ PBD::error << _("AlsaBackend::disconnect_all: Invalid Port") << endmsg;
+ return -1;
+ }
+ static_cast<AlsaPort*>(port)->disconnect_all ();
+ return 0;
+}
+
+bool
+AlsaAudioBackend::connected (PortEngine::PortHandle port, bool /* process_callback_safe*/)
+{
+ if (!valid_port (port)) {
+ PBD::error << _("AlsaBackend::disconnect_all: Invalid Port") << endmsg;
+ return false;
+ }
+ return static_cast<AlsaPort*>(port)->is_connected ();
+}
+
+bool
+AlsaAudioBackend::connected_to (PortEngine::PortHandle src, const std::string& dst, bool /*process_callback_safe*/)
+{
+ AlsaPort* dst_port = find_port (dst);
+ if (!valid_port (src) || !dst_port) {
+ PBD::error << _("AlsaBackend::connected_to: Invalid Port") << endmsg;
+ return false;
+ }
+ return static_cast<AlsaPort*>(src)->is_connected (dst_port);
+}
+
+bool
+AlsaAudioBackend::physically_connected (PortEngine::PortHandle port, bool /*process_callback_safe*/)
+{
+ if (!valid_port (port)) {
+ PBD::error << _("AlsaBackend::physically_connected: Invalid Port") << endmsg;
+ return false;
+ }
+ return static_cast<AlsaPort*>(port)->is_physically_connected ();
+}
+
+int
+AlsaAudioBackend::get_connections (PortEngine::PortHandle port, std::vector<std::string>& names, bool /*process_callback_safe*/)
+{
+ if (!valid_port (port)) {
+ PBD::error << _("AlsaBackend::get_connections: Invalid Port") << endmsg;
+ return -1;
+ }
+
+ assert (0 == names.size ());
+
+ const std::vector<AlsaPort*>& connected_ports = static_cast<AlsaPort*>(port)->get_connections ();
+
+ for (std::vector<AlsaPort*>::const_iterator i = connected_ports.begin (); i != connected_ports.end (); ++i) {
+ names.push_back ((*i)->name ());
+ }
+
+ return (int)names.size ();
+}
+
+/* MIDI */
+int
+AlsaAudioBackend::midi_event_get (
+ pframes_t& timestamp,
+ size_t& size, uint8_t** buf, void* port_buffer,
+ uint32_t event_index)
+{
+ assert (buf && port_buffer);
+ AlsaMidiBuffer& source = * static_cast<AlsaMidiBuffer*>(port_buffer);
+ if (event_index >= source.size ()) {
+ return -1;
+ }
+ AlsaMidiEvent * const event = source[event_index].get ();
+
+ timestamp = event->timestamp ();
+ size = event->size ();
+ *buf = event->data ();
+ return 0;
+}
+
+int
+AlsaAudioBackend::midi_event_put (
+ void* port_buffer,
+ pframes_t timestamp,
+ const uint8_t* buffer, size_t size)
+{
+ assert (buffer && port_buffer);
+ AlsaMidiBuffer& dst = * static_cast<AlsaMidiBuffer*>(port_buffer);
+ if (dst.size () && (pframes_t)dst.back ()->timestamp () > timestamp) {
+ fprintf (stderr, "AlsaMidiBuffer: it's too late for this event. %d > %d\n",
+ (pframes_t)dst.back ()->timestamp (), timestamp);
+ return -1;
+ }
+ dst.push_back (boost::shared_ptr<AlsaMidiEvent>(new AlsaMidiEvent (timestamp, buffer, size)));
+ return 0;
+}
+
+uint32_t
+AlsaAudioBackend::get_midi_event_count (void* port_buffer)
+{
+ assert (port_buffer);
+ return static_cast<AlsaMidiBuffer*>(port_buffer)->size ();
+}
+
+void
+AlsaAudioBackend::midi_clear (void* port_buffer)
+{
+ assert (port_buffer);
+ AlsaMidiBuffer * buf = static_cast<AlsaMidiBuffer*>(port_buffer);
+ assert (buf);
+ buf->clear ();
+}
+
+/* Monitoring */
+
+bool
+AlsaAudioBackend::can_monitor_input () const
+{
+ return false;
+}
+
+int
+AlsaAudioBackend::request_input_monitoring (PortEngine::PortHandle, bool)
+{
+ return -1;
+}
+
+int
+AlsaAudioBackend::ensure_input_monitoring (PortEngine::PortHandle, bool)
+{
+ return -1;
+}
+
+bool
+AlsaAudioBackend::monitoring_input (PortEngine::PortHandle)
+{
+ return false;
+}
+
+/* Latency management */
+
+void
+AlsaAudioBackend::set_latency_range (PortEngine::PortHandle port, bool for_playback, LatencyRange latency_range)
+{
+ if (!valid_port (port)) {
+ PBD::error << _("AlsaPort::set_latency_range (): invalid port.") << endmsg;
+ }
+ static_cast<AlsaPort*>(port)->set_latency_range (latency_range, for_playback);
+}
+
+LatencyRange
+AlsaAudioBackend::get_latency_range (PortEngine::PortHandle port, bool for_playback)
+{
+ if (!valid_port (port)) {
+ PBD::error << _("AlsaPort::get_latency_range (): invalid port.") << endmsg;
+ LatencyRange r;
+ r.min = 0;
+ r.max = 0;
+ return r;
+ }
+ return static_cast<AlsaPort*>(port)->latency_range (for_playback);
+}
+
+/* Discovering physical ports */
+
+bool
+AlsaAudioBackend::port_is_physical (PortEngine::PortHandle port) const
+{
+ if (!valid_port (port)) {
+ PBD::error << _("AlsaPort::port_is_physical (): invalid port.") << endmsg;
+ return false;
+ }
+ return static_cast<AlsaPort*>(port)->is_physical ();
+}
+
+void
+AlsaAudioBackend::get_physical_outputs (DataType type, std::vector<std::string>& port_names)
+{
+ for (size_t i = 0; i < _ports.size (); ++i) {
+ AlsaPort* port = _ports[i];
+ if ((port->type () == type) && port->is_input () && port->is_physical ()) {
+ port_names.push_back (port->name ());
+ }
+ }
+}
+
+void
+AlsaAudioBackend::get_physical_inputs (DataType type, std::vector<std::string>& port_names)
+{
+ for (size_t i = 0; i < _ports.size (); ++i) {
+ AlsaPort* port = _ports[i];
+ if ((port->type () == type) && port->is_output () && port->is_physical ()) {
+ port_names.push_back (port->name ());
+ }
+ }
+}
+
+ChanCount
+AlsaAudioBackend::n_physical_outputs () const
+{
+ int n_midi = 0;
+ int n_audio = 0;
+ for (size_t i = 0; i < _ports.size (); ++i) {
+ AlsaPort* port = _ports[i];
+ if (port->is_output () && port->is_physical ()) {
+ switch (port->type ()) {
+ case DataType::AUDIO: ++n_audio; break;
+ case DataType::MIDI: ++n_midi; break;
+ default: break;
+ }
+ }
+ }
+ ChanCount cc;
+ cc.set (DataType::AUDIO, n_audio);
+ cc.set (DataType::MIDI, n_midi);
+ return cc;
+}
+
+ChanCount
+AlsaAudioBackend::n_physical_inputs () const
+{
+ int n_midi = 0;
+ int n_audio = 0;
+ for (size_t i = 0; i < _ports.size (); ++i) {
+ AlsaPort* port = _ports[i];
+ if (port->is_input () && port->is_physical ()) {
+ switch (port->type ()) {
+ case DataType::AUDIO: ++n_audio; break;
+ case DataType::MIDI: ++n_midi; break;
+ default: break;
+ }
+ }
+ }
+ ChanCount cc;
+ cc.set (DataType::AUDIO, n_audio);
+ cc.set (DataType::MIDI, n_midi);
+ return cc;
+}
+
+/* Getting access to the data buffer for a port */
+
+void*
+AlsaAudioBackend::get_buffer (PortEngine::PortHandle port, pframes_t nframes)
+{
+ assert (port);
+ assert (valid_port (port));
+ return static_cast<AlsaPort*>(port)->get_buffer (nframes);
+}
+
+/* Engine Process */
+void *
+AlsaAudioBackend::main_process_thread ()
+{
+ AudioEngine::thread_init_callback (this);
+ _active = true;
+ _processed_samples = 0;
+
+ uint64_t clock1, clock2;
+ clock1 = g_get_monotonic_time();
+ _pcmi->pcm_start ();
+ int no_proc_errors = 0;
+ const int bailout = 2 * _samplerate / _samples_per_period;
+ const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate;
+
+ manager.registration_callback();
+ manager.graph_order_callback();
+
+ while (_run) {
+ long nr;
+ bool xrun = false;
+ if (!_freewheeling) {
+ nr = _pcmi->pcm_wait ();
+
+ if (_pcmi->state () > 0) {
+ ++no_proc_errors;
+ xrun = true;
+ }
+ if (_pcmi->state () < 0 || no_proc_errors > bailout) {
+ PBD::error << _("AlsaAudioBackend: I/O error. Audio Process Terminated.") << endmsg;
+ break;
+ }
+ while (nr >= (long)_samples_per_period) {
+ uint32_t i = 0;
+ clock1 = g_get_monotonic_time();
+ no_proc_errors = 0;
+
+ _pcmi->capt_init (_samples_per_period);
+ for (std::vector<AlsaPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it, ++i) {
+ _pcmi->capt_chan (i, (float*)((*it)->get_buffer(_samples_per_period)), _samples_per_period);
+ }
+ _pcmi->capt_done (_samples_per_period);
+
+ /* de-queue midi*/
+ i = 0;
+ for (std::vector<AlsaPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it, ++i) {
+ assert (_rmidi_in.size() > i);
+ AlsaMidiIn *rm = _rmidi_in.at(i);
+ void *bptr = (*it)->get_buffer(0);
+ pframes_t time;
+ uint8_t data[64]; // match MaxAlsaEventSize in alsa_rawmidi.cc
+ size_t size = sizeof(data);
+ midi_clear(bptr);
+ while (rm->recv_event (time, data, size)) {
+ midi_event_put(bptr, time, data, size);
+ size = sizeof(data);
+ }
+ rm->sync_time (clock1);
+ }
+
+ for (std::vector<AlsaPort*>::const_iterator it = _system_outputs.begin (); it != _system_outputs.end (); ++it) {
+ memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample));
+ }
+
+ if (engine.process_callback (_samples_per_period)) {
+ _pcmi->pcm_stop ();
+ _active = false;
+ return 0;
+ }
+
+ i = 0;
+ for (std::vector<AlsaPort*>::iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) {
+ static_cast<AlsaMidiPort*>(*it)->next_period();
+ }
+
+ /* queue midi */
+ i = 0;
+ for (std::vector<AlsaPort*>::const_iterator it = _system_midi_out.begin (); it != _system_midi_out.end (); ++it, ++i) {
+ assert (_rmidi_out.size() > i);
+ const AlsaMidiBuffer src = static_cast<const AlsaMidiPort*>(*it)->const_buffer();
+ AlsaMidiOut *rm = _rmidi_out.at(i);
+ rm->sync_time (clock1);
+ for (AlsaMidiBuffer::const_iterator mit = src.begin (); mit != src.end (); ++mit) {
+ rm->send_event ((*mit)->timestamp(), (*mit)->data(), (*mit)->size());
+ }
+ }
+
+ /* write back audio */
+ i = 0;
+ _pcmi->play_init (_samples_per_period);
+ for (std::vector<AlsaPort*>::const_iterator it = _system_outputs.begin (); it != _system_outputs.end (); ++it, ++i) {
+ _pcmi->play_chan (i, (const float*)(*it)->get_buffer (_samples_per_period), _samples_per_period);
+ }
+ for (; i < _pcmi->nplay (); ++i) {
+ _pcmi->clear_chan (i, _samples_per_period);
+ }
+ _pcmi->play_done (_samples_per_period);
+ nr -= _samples_per_period;
+ _processed_samples += _samples_per_period;
+
+ /* calculate DSP load */
+ clock2 = g_get_monotonic_time();
+ const int64_t elapsed_time = clock2 - clock1;
+ _dsp_load = elapsed_time / (float) nomial_time;
+ }
+
+ if (xrun && (_pcmi->capt_xrun() > 0 || _pcmi->play_xrun() > 0)) {
+ engine.Xrun ();
+#if 0
+ fprintf(stderr, "ALSA x-run read: %.1f ms, write: %.1f ms\n",
+ _pcmi->capt_xrun() * 1000.0, _pcmi->play_xrun() * 1000.0);
+#endif
+ }
+ } else {
+ // Freewheelin'
+ for (std::vector<AlsaPort*>::const_iterator it = _system_inputs.begin (); it != _system_inputs.end (); ++it) {
+ memset ((*it)->get_buffer (_samples_per_period), 0, _samples_per_period * sizeof (Sample));
+ }
+ for (std::vector<AlsaPort*>::const_iterator it = _system_midi_in.begin (); it != _system_midi_in.end (); ++it) {
+ static_cast<AlsaMidiBuffer*>((*it)->get_buffer(0))->clear ();
+ }
+
+ if (engine.process_callback (_samples_per_period)) {
+ _pcmi->pcm_stop ();
+ return 0;
+ }
+ _dsp_load = 1.0;
+ Glib::usleep (100); // don't hog cpu
+ }
+
+ bool connections_changed = false;
+ bool ports_changed = false;
+ if (!pthread_mutex_trylock (&_port_callback_mutex)) {
+ if (_port_change_flag) {
+ ports_changed = true;
+ _port_change_flag = false;
+ }
+ if (!_port_connection_queue.empty ()) {
+ connections_changed = true;
+ }
+ while (!_port_connection_queue.empty ()) {
+ PortConnectData *c = _port_connection_queue.back ();
+ manager.connect_callback (c->a, c->b, c->c);
+ _port_connection_queue.pop_back ();
+ delete c;
+ }
+ pthread_mutex_unlock (&_port_callback_mutex);
+ }
+ if (ports_changed) {
+ manager.registration_callback();
+ }
+ if (connections_changed) {
+ manager.graph_order_callback();
+ }
+ if (connections_changed || ports_changed) {
+ engine.latency_callback(false);
+ engine.latency_callback(true);
+ }
+
+ }
+ _pcmi->pcm_stop ();
+ _active = false;
+ if (_run) {
+ engine.halted_callback("ALSA I/O error.");
+ }
+ return 0;
+}
+
+
+/******************************************************************************/
+
+static boost::shared_ptr<AlsaAudioBackend> _instance;
+
+static boost::shared_ptr<AudioBackend> backend_factory (AudioEngine& e);
+static int instantiate (const std::string& arg1, const std::string& /* arg2 */);
+static int deinstantiate ();
+static bool already_configured ();
+
+static ARDOUR::AudioBackendInfo _descriptor = {
+ "ALSA",
+ instantiate,
+ deinstantiate,
+ backend_factory,
+ already_configured,
+};
+
+static boost::shared_ptr<AudioBackend>
+backend_factory (AudioEngine& e)
+{
+ if (!_instance) {
+ _instance.reset (new AlsaAudioBackend (e, _descriptor));
+ }
+ return _instance;
+}
+
+static int
+instantiate (const std::string& arg1, const std::string& /* arg2 */)
+{
+ s_instance_name = arg1;
+ return 0;
+}
+
+static int
+deinstantiate ()
+{
+ _instance.reset ();
+ return 0;
+}
+
+static bool
+already_configured ()
+{
+ return false;
+}
+
+extern "C" ARDOURBACKEND_API ARDOUR::AudioBackendInfo* descriptor ()
+{
+ return &_descriptor;
+}
+
+
+/******************************************************************************/
+AlsaPort::AlsaPort (AlsaAudioBackend &b, const std::string& name, PortFlags flags)
+ : _alsa_backend (b)
+ , _name (name)
+ , _flags (flags)
+{
+ _capture_latency_range.min = 0;
+ _capture_latency_range.max = 0;
+ _playback_latency_range.min = 0;
+ _playback_latency_range.max = 0;
+}
+
+AlsaPort::~AlsaPort () {
+ disconnect_all ();
+}
+
+
+int AlsaPort::connect (AlsaPort *port)
+{
+ if (!port) {
+ PBD::error << _("AlsaPort::connect (): invalid (null) port") << endmsg;
+ return -1;
+ }
+
+ if (type () != port->type ()) {
+ PBD::error << _("AlsaPort::connect (): wrong port-type") << endmsg;
+ return -1;
+ }
+
+ if (is_output () && port->is_output ()) {
+ PBD::error << _("AlsaPort::connect (): cannot inter-connect output ports.") << endmsg;
+ return -1;
+ }
+
+ if (is_input () && port->is_input ()) {
+ PBD::error << _("AlsaPort::connect (): cannot inter-connect input ports.") << endmsg;
+ return -1;
+ }
+
+ if (this == port) {
+ PBD::error << _("AlsaPort::connect (): cannot self-connect ports.") << endmsg;
+ return -1;
+ }
+
+ if (is_connected (port)) {
+#if 0 // don't bother to warn about this for now. just ignore it
+ PBD::error << _("AlsaPort::connect (): ports are already connected:")
+ << " (" << name () << ") -> (" << port->name () << ")"
+ << endmsg;
+#endif
+ return -1;
+ }
+
+ _connect (port, true);
+ return 0;
+}
+
+
+void AlsaPort::_connect (AlsaPort *port, bool callback)
+{
+ _connections.push_back (port);
+ if (callback) {
+ port->_connect (this, false);
+ _alsa_backend.port_connect_callback (name(), port->name(), true);
+ }
+}
+
+int AlsaPort::disconnect (AlsaPort *port)
+{
+ if (!port) {
+ PBD::error << _("AlsaPort::disconnect (): invalid (null) port") << endmsg;
+ return -1;
+ }
+
+ if (!is_connected (port)) {
+ PBD::error << _("AlsaPort::disconnect (): ports are not connected:")
+ << " (" << name () << ") -> (" << port->name () << ")"
+ << endmsg;
+ return -1;
+ }
+ _disconnect (port, true);
+ return 0;
+}
+
+void AlsaPort::_disconnect (AlsaPort *port, bool callback)
+{
+ std::vector<AlsaPort*>::iterator it = std::find (_connections.begin (), _connections.end (), port);
+
+ assert (it != _connections.end ());
+
+ _connections.erase (it);
+
+ if (callback) {
+ port->_disconnect (this, false);
+ _alsa_backend.port_connect_callback (name(), port->name(), false);
+ }
+}
+
+
+void AlsaPort::disconnect_all ()
+{
+ while (!_connections.empty ()) {
+ _connections.back ()->_disconnect (this, false);
+ _alsa_backend.port_connect_callback (name(), _connections.back ()->name(), false);
+ _connections.pop_back ();
+ }
+}
+
+bool
+AlsaPort::is_connected (const AlsaPort *port) const
+{
+ return std::find (_connections.begin (), _connections.end (), port) != _connections.end ();
+}
+
+bool AlsaPort::is_physically_connected () const
+{
+ for (std::vector<AlsaPort*>::const_iterator it = _connections.begin (); it != _connections.end (); ++it) {
+ if ((*it)->is_physical ()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/******************************************************************************/
+
+AlsaAudioPort::AlsaAudioPort (AlsaAudioBackend &b, const std::string& name, PortFlags flags)
+ : AlsaPort (b, name, flags)
+{
+ memset (_buffer, 0, sizeof (_buffer));
+ mlock(_buffer, sizeof (_buffer));
+}
+
+AlsaAudioPort::~AlsaAudioPort () { }
+
+void* AlsaAudioPort::get_buffer (pframes_t n_samples)
+{
+ if (is_input ()) {
+ std::vector<AlsaPort*>::const_iterator it = get_connections ().begin ();
+ if (it == get_connections ().end ()) {
+ memset (_buffer, 0, n_samples * sizeof (Sample));
+ } else {
+ AlsaAudioPort const * source = static_cast<const AlsaAudioPort*>(*it);
+ assert (source && source->is_output ());
+ memcpy (_buffer, source->const_buffer (), n_samples * sizeof (Sample));
+ while (++it != get_connections ().end ()) {
+ source = static_cast<const AlsaAudioPort*>(*it);
+ assert (source && source->is_output ());
+ Sample* dst = buffer ();
+ const Sample* src = source->const_buffer ();
+ for (uint32_t s = 0; s < n_samples; ++s, ++dst, ++src) {
+ *dst += *src;
+ }
+ }
+ }
+ }
+ return _buffer;
+}
+
+
+AlsaMidiPort::AlsaMidiPort (AlsaAudioBackend &b, const std::string& name, PortFlags flags)
+ : AlsaPort (b, name, flags)
+ , _n_periods (1)
+ , _bufperiod (0)
+{
+ _buffer[0].clear ();
+ _buffer[1].clear ();
+}
+
+AlsaMidiPort::~AlsaMidiPort () { }
+
+struct MidiEventSorter {
+ bool operator() (const boost::shared_ptr<AlsaMidiEvent>& a, const boost::shared_ptr<AlsaMidiEvent>& b) {
+ return *a < *b;
+ }
+};
+
+void* AlsaMidiPort::get_buffer (pframes_t /* nframes */)
+{
+ if (is_input ()) {
+ (_buffer[_bufperiod]).clear ();
+ for (std::vector<AlsaPort*>::const_iterator i = get_connections ().begin ();
+ i != get_connections ().end ();
+ ++i) {
+ const AlsaMidiBuffer src = static_cast<const AlsaMidiPort*>(*i)->const_buffer ();
+ for (AlsaMidiBuffer::const_iterator it = src.begin (); it != src.end (); ++it) {
+ (_buffer[_bufperiod]).push_back (boost::shared_ptr<AlsaMidiEvent>(new AlsaMidiEvent (**it)));
+ }
+ }
+ std::sort ((_buffer[_bufperiod]).begin (), (_buffer[_bufperiod]).end (), MidiEventSorter());
+ }
+ return &(_buffer[_bufperiod]);
+}
+
+AlsaMidiEvent::AlsaMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size)
+ : _size (size)
+ , _timestamp (timestamp)
+ , _data (0)
+{
+ if (size > 0) {
+ _data = (uint8_t*) malloc (size);
+ memcpy (_data, data, size);
+ }
+}
+
+AlsaMidiEvent::AlsaMidiEvent (const AlsaMidiEvent& other)
+ : _size (other.size ())
+ , _timestamp (other.timestamp ())
+ , _data (0)
+{
+ if (other.size () && other.const_data ()) {
+ _data = (uint8_t*) malloc (other.size ());
+ memcpy (_data, other.const_data (), other.size ());
+ }
+};
+
+AlsaMidiEvent::~AlsaMidiEvent () {
+ free (_data);
+};
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2013 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 __libbackend_alsa_audiobackend_h__
+#define __libbackend_alsa_audiobackend_h__
+
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+
+#include <stdint.h>
+#include <pthread.h>
+
+#include <boost/shared_ptr.hpp>
+
+#include "ardour/audio_backend.h"
+#include "ardour/system_exec.h"
+#include "ardour/types.h"
+
+#include "zita-alsa-pcmi.h"
+#include "alsa_rawmidi.h"
+#include "alsa_sequencer.h"
+
+namespace ARDOUR {
+
+class AlsaAudioBackend;
+
+class AlsaMidiEvent {
+ public:
+ AlsaMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size);
+ AlsaMidiEvent (const AlsaMidiEvent& other);
+ ~AlsaMidiEvent ();
+ size_t size () const { return _size; };
+ pframes_t timestamp () const { return _timestamp; };
+ const unsigned char* const_data () const { return _data; };
+ unsigned char* data () { return _data; };
+ bool operator< (const AlsaMidiEvent &other) const { return timestamp () < other.timestamp (); };
+ private:
+ size_t _size;
+ pframes_t _timestamp;
+ uint8_t *_data;
+};
+
+typedef std::vector<boost::shared_ptr<AlsaMidiEvent> > AlsaMidiBuffer;
+
+class AlsaPort {
+ protected:
+ AlsaPort (AlsaAudioBackend &b, const std::string&, PortFlags);
+ public:
+ virtual ~AlsaPort ();
+
+ const std::string& name () const { return _name; }
+ PortFlags flags () const { return _flags; }
+
+ int set_name (const std::string &name) { _name = name; return 0; }
+
+ virtual DataType type () const = 0;
+
+ bool is_input () const { return flags () & IsInput; }
+ bool is_output () const { return flags () & IsOutput; }
+ bool is_physical () const { return flags () & IsPhysical; }
+ bool is_terminal () const { return flags () & IsTerminal; }
+ bool is_connected () const { return _connections.size () != 0; }
+ bool is_connected (const AlsaPort *port) const;
+ bool is_physically_connected () const;
+
+ const std::vector<AlsaPort *>& get_connections () const { return _connections; }
+
+ int connect (AlsaPort *port);
+ int disconnect (AlsaPort *port);
+ void disconnect_all ();
+
+ virtual void* get_buffer (pframes_t nframes) = 0;
+
+ const LatencyRange& latency_range (bool for_playback) const
+ {
+ return for_playback ? _playback_latency_range : _capture_latency_range;
+ }
+
+ void set_latency_range (const LatencyRange &latency_range, bool for_playback)
+ {
+ if (for_playback)
+ {
+ _playback_latency_range = latency_range;
+ }
+ else
+ {
+ _capture_latency_range = latency_range;
+ }
+ }
+
+ private:
+ AlsaAudioBackend &_alsa_backend;
+ std::string _name;
+ const PortFlags _flags;
+ LatencyRange _capture_latency_range;
+ LatencyRange _playback_latency_range;
+ std::vector<AlsaPort*> _connections;
+
+ void _connect (AlsaPort* , bool);
+ void _disconnect (AlsaPort* , bool);
+
+}; // class AlsaPort
+
+class AlsaAudioPort : public AlsaPort {
+ public:
+ AlsaAudioPort (AlsaAudioBackend &b, const std::string&, PortFlags);
+ ~AlsaAudioPort ();
+
+ DataType type () const { return DataType::AUDIO; };
+
+ Sample* buffer () { return _buffer; }
+ const Sample* const_buffer () const { return _buffer; }
+ void* get_buffer (pframes_t nframes);
+
+ private:
+ Sample _buffer[8192];
+}; // class AlsaAudioPort
+
+class AlsaMidiPort : public AlsaPort {
+ public:
+ AlsaMidiPort (AlsaAudioBackend &b, const std::string&, PortFlags);
+ ~AlsaMidiPort ();
+
+ DataType type () const { return DataType::MIDI; };
+
+ void* get_buffer (pframes_t nframes);
+ const AlsaMidiBuffer const_buffer () const { return _buffer[_bufperiod]; }
+
+ void next_period() { if (_n_periods > 1) { get_buffer(0); _bufperiod = (_bufperiod + 1) % _n_periods; } }
+ void set_n_periods(int n) { if (n > 0 && n < 3) { _n_periods = n; } }
+
+ private:
+ AlsaMidiBuffer _buffer[2];
+ int _n_periods;
+ int _bufperiod;
+}; // class AlsaMidiPort
+
+class AlsaAudioBackend : public AudioBackend {
+ friend class AlsaPort;
+ public:
+ AlsaAudioBackend (AudioEngine& e, AudioBackendInfo& info);
+ ~AlsaAudioBackend ();
+
+ /* AUDIOBACKEND API */
+
+ std::string name () const;
+ bool is_realtime () const;
+
+ std::vector<DeviceStatus> enumerate_devices () const;
+ std::vector<float> available_sample_rates (const std::string& device) const;
+ std::vector<uint32_t> available_buffer_sizes (const std::string& device) const;
+ uint32_t available_input_channel_count (const std::string& device) const;
+ uint32_t available_output_channel_count (const std::string& device) const;
+
+ bool can_change_sample_rate_when_running () const;
+ bool can_change_buffer_size_when_running () const;
+
+ int set_device_name (const std::string&);
+ int set_sample_rate (float);
+ int set_buffer_size (uint32_t);
+ int set_interleaved (bool yn);
+ int set_input_channels (uint32_t);
+ int set_output_channels (uint32_t);
+ int set_systemic_input_latency (uint32_t);
+ int set_systemic_output_latency (uint32_t);
+ int set_systemic_midi_input_latency (std::string const, uint32_t);
+ int set_systemic_midi_output_latency (std::string const, uint32_t);
+
+ /* Retrieving parameters */
+ std::string device_name () const;
+ float sample_rate () const;
+ uint32_t buffer_size () const;
+ bool interleaved () const;
+ uint32_t input_channels () const;
+ uint32_t output_channels () const;
+ uint32_t systemic_input_latency () const;
+ uint32_t systemic_output_latency () const;
+ uint32_t systemic_midi_input_latency (std::string const) const;
+ uint32_t systemic_midi_output_latency (std::string const) const;
+
+ bool can_set_systemic_midi_latencies () const { return true; }
+
+ /* External control app */
+ std::string control_app_name () const { return std::string (); }
+ void launch_control_app () {}
+
+ /* MIDI */
+ std::vector<std::string> enumerate_midi_options () const;
+ int set_midi_option (const std::string&);
+ std::string midi_option () const;
+
+ std::vector<DeviceStatus> enumerate_midi_devices () const;
+ int set_midi_device_enabled (std::string const, bool);
+ bool midi_device_enabled (std::string const) const;
+
+ /* State Control */
+ protected:
+ int _start (bool for_latency_measurement);
+ public:
+ int stop ();
+ int freewheel (bool);
+ float dsp_load () const;
+ size_t raw_buffer_size (DataType t);
+
+ /* Process time */
+ pframes_t sample_time ();
+ pframes_t sample_time_at_cycle_start ();
+ pframes_t samples_since_cycle_start ();
+
+ int create_process_thread (boost::function<void()> func);
+ int join_process_threads ();
+ bool in_process_thread ();
+ uint32_t process_thread_count ();
+
+ void update_latencies ();
+
+ /* PORTENGINE API */
+
+ void* private_handle () const;
+ const std::string& my_name () const;
+ bool available () const;
+ uint32_t port_name_size () const;
+
+ int set_port_name (PortHandle, const std::string&);
+ std::string get_port_name (PortHandle) const;
+ PortHandle get_port_by_name (const std::string&) const;
+
+ int get_ports (const std::string& port_name_pattern, DataType type, PortFlags flags, std::vector<std::string>&) const;
+
+ DataType port_data_type (PortHandle) const;
+
+ PortHandle register_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags);
+ void unregister_port (PortHandle);
+
+ int connect (const std::string& src, const std::string& dst);
+ int disconnect (const std::string& src, const std::string& dst);
+ int connect (PortHandle, const std::string&);
+ int disconnect (PortHandle, const std::string&);
+ int disconnect_all (PortHandle);
+
+ bool connected (PortHandle, bool process_callback_safe);
+ bool connected_to (PortHandle, const std::string&, bool process_callback_safe);
+ bool physically_connected (PortHandle, bool process_callback_safe);
+ int get_connections (PortHandle, std::vector<std::string>&, bool process_callback_safe);
+
+ /* MIDI */
+ int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index);
+ int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
+ uint32_t get_midi_event_count (void* port_buffer);
+ void midi_clear (void* port_buffer);
+
+ /* Monitoring */
+
+ bool can_monitor_input () const;
+ int request_input_monitoring (PortHandle, bool);
+ int ensure_input_monitoring (PortHandle, bool);
+ bool monitoring_input (PortHandle);
+
+ /* Latency management */
+
+ void set_latency_range (PortHandle, bool for_playback, LatencyRange);
+ LatencyRange get_latency_range (PortHandle, bool for_playback);
+
+ /* Discovering physical ports */
+
+ bool port_is_physical (PortHandle) const;
+ void get_physical_outputs (DataType type, std::vector<std::string>&);
+ void get_physical_inputs (DataType type, std::vector<std::string>&);
+ ChanCount n_physical_outputs () const;
+ ChanCount n_physical_inputs () const;
+
+ /* Getting access to the data buffer for a port */
+
+ void* get_buffer (PortHandle, pframes_t);
+
+ void* main_process_thread ();
+
+ private:
+ std::string _instance_name;
+ Alsa_pcmi *_pcmi;
+
+ bool _run; /* keep going or stop, ardour thread */
+ bool _active; /* is running, process thread */
+ bool _freewheeling;
+ bool _measure_latency;
+
+ static std::vector<std::string> _midi_options;
+ static std::vector<AudioBackend::DeviceStatus> _audio_device_status;
+ static std::vector<AudioBackend::DeviceStatus> _midi_device_status;
+
+ std::string _audio_device;
+ std::string _midi_driver_option;
+
+ /* audio device reservation */
+ ARDOUR::SystemExec *_device_reservation;
+ PBD::ScopedConnectionList _reservation_connection;
+ void reservation_stdout (std::string, size_t);
+ bool acquire_device(const char* device_name);
+ void release_device();
+ bool _reservation_succeeded;
+
+ /* audio settings */
+ float _samplerate;
+ size_t _samples_per_period;
+ size_t _periods_per_cycle;
+ static size_t _max_buffer_size;
+
+ uint32_t _n_inputs;
+ uint32_t _n_outputs;
+
+ uint32_t _systemic_audio_input_latency;
+ uint32_t _systemic_audio_output_latency;
+
+ /* midi settings */
+ struct AlsaMidiDeviceInfo {
+ bool enabled;
+ uint32_t systemic_input_latency;
+ uint32_t systemic_output_latency;
+ AlsaMidiDeviceInfo()
+ : enabled (true)
+ , systemic_input_latency (0)
+ , systemic_output_latency (0)
+ {}
+ };
+
+ mutable std::map<std::string, struct AlsaMidiDeviceInfo *> _midi_devices;
+ struct AlsaMidiDeviceInfo * midi_device_info(std::string const) const;
+
+ /* processing */
+ float _dsp_load;
+ uint64_t _processed_samples;
+ pthread_t _main_thread;
+
+ /* process threads */
+ static void* alsa_process_thread (void *);
+ std::vector<pthread_t> _threads;
+
+ struct ThreadData {
+ AlsaAudioBackend* engine;
+ boost::function<void ()> f;
+ size_t stacksize;
+
+ ThreadData (AlsaAudioBackend* e, boost::function<void ()> fp, size_t stacksz)
+ : engine (e) , f (fp) , stacksize (stacksz) {}
+ };
+
+ /* port engine */
+ PortHandle add_port (const std::string& shortname, ARDOUR::DataType, ARDOUR::PortFlags);
+ int register_system_audio_ports ();
+ int register_system_midi_ports ();
+ void unregister_system_ports ();
+
+ std::vector<AlsaPort *> _ports;
+ std::vector<AlsaPort *> _system_inputs;
+ std::vector<AlsaPort *> _system_outputs;
+ std::vector<AlsaPort *> _system_midi_in;
+ std::vector<AlsaPort *> _system_midi_out;
+
+ std::vector<AlsaMidiOut *> _rmidi_out;
+ std::vector<AlsaMidiIn *> _rmidi_in;
+
+ struct PortConnectData {
+ std::string a;
+ std::string b;
+ bool c;
+
+ PortConnectData (const std::string& a, const std::string& b, bool c)
+ : a (a) , b (b) , c (c) {}
+ };
+
+ std::vector<PortConnectData *> _port_connection_queue;
+ pthread_mutex_t _port_callback_mutex;
+ bool _port_change_flag;
+
+ void port_connect_callback (const std::string& a, const std::string& b, bool conn) {
+ pthread_mutex_lock (&_port_callback_mutex);
+ _port_connection_queue.push_back(new PortConnectData(a, b, conn));
+ pthread_mutex_unlock (&_port_callback_mutex);
+ }
+
+ void port_connect_add_remove_callback () {
+ pthread_mutex_lock (&_port_callback_mutex);
+ _port_change_flag = true;
+ pthread_mutex_unlock (&_port_callback_mutex);
+ }
+
+ bool valid_port (PortHandle port) const {
+ return std::find (_ports.begin (), _ports.end (), (AlsaPort*)port) != _ports.end ();
+ }
+
+ AlsaPort * find_port (const std::string& port_name) const {
+ for (std::vector<AlsaPort*>::const_iterator it = _ports.begin (); it != _ports.end (); ++it) {
+ if ((*it)->name () == port_name) {
+ return *it;
+ }
+ }
+ return NULL;
+ }
+
+}; // class AlsaAudioBackend
+
+} // namespace
+
+#endif /* __libbackend_alsa_audiobackend_h__ */
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <unistd.h>
+
+#include <glibmm.h>
+
+#include "alsa_midi.h"
+#include "rt_thread.h"
+
+#include "pbd/error.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+#ifndef NDEBUG
+#define _DEBUGPRINT(STR) fprintf(stderr, STR);
+#else
+#define _DEBUGPRINT(STR) ;
+#endif
+
+AlsaMidiIO::AlsaMidiIO ()
+ : _state (-1)
+ , _running (false)
+ , _pfds (0)
+ , _sample_length_us (1e6 / 48000.0)
+ , _period_length_us (1.024e6 / 48000.0)
+ , _samples_per_period (1024)
+ , _rb (0)
+{
+ pthread_mutex_init (&_notify_mutex, 0);
+ pthread_cond_init (&_notify_ready, 0);
+
+ // MIDI (hw port) 31.25 kbaud
+ // worst case here is 8192 SPP and 8KSPS for which we'd need
+ // 4000 bytes sans MidiEventHeader.
+ // since we're not always in sync, let's use 4096.
+ _rb = new RingBuffer<uint8_t>(4096 + 4096 * sizeof(MidiEventHeader));
+}
+
+AlsaMidiIO::~AlsaMidiIO ()
+{
+ delete _rb;
+ pthread_mutex_destroy (&_notify_mutex);
+ pthread_cond_destroy (&_notify_ready);
+ free (_pfds);
+}
+
+static void * pthread_process (void *arg)
+{
+ AlsaMidiIO *d = static_cast<AlsaMidiIO *>(arg);
+ d->main_process_thread ();
+ pthread_exit (0);
+ return 0;
+}
+
+int
+AlsaMidiIO::start ()
+{
+ if (_realtime_pthread_create (SCHED_FIFO, -21, 100000,
+ &_main_thread, pthread_process, this))
+ {
+ if (pthread_create (&_main_thread, NULL, pthread_process, this)) {
+ PBD::error << _("AlsaMidiIO: Failed to create process thread.") << endmsg;
+ return -1;
+ } else {
+ PBD::warning << _("AlsaMidiIO: Cannot acquire realtime permissions.") << endmsg;
+ }
+ }
+ int timeout = 5000;
+ while (!_running && --timeout > 0) { Glib::usleep (1000); }
+ if (timeout == 0 || !_running) {
+ return -1;
+ }
+ return 0;
+}
+
+int
+AlsaMidiIO::stop ()
+{
+ void *status;
+ if (!_running) {
+ return 0;
+ }
+
+ _running = false;
+
+ pthread_mutex_lock (&_notify_mutex);
+ pthread_cond_signal (&_notify_ready);
+ pthread_mutex_unlock (&_notify_mutex);
+
+ if (pthread_join (_main_thread, &status)) {
+ PBD::error << _("AlsaMidiIO: Failed to terminate.") << endmsg;
+ return -1;
+ }
+ return 0;
+}
+
+void
+AlsaMidiIO::setup_timing (const size_t samples_per_period, const float samplerate)
+{
+ _period_length_us = (double) samples_per_period * 1e6 / samplerate;
+ _sample_length_us = 1e6 / samplerate;
+ _samples_per_period = samples_per_period;
+}
+
+void
+AlsaMidiIO::sync_time (const uint64_t tme)
+{
+ // TODO consider a PLL, if this turns out to be the bottleneck for jitter
+ // also think about using
+ // snd_pcm_status_get_tstamp() and snd_rawmidi_status_get_tstamp()
+ // instead of monotonic clock.
+#ifdef DEBUG_TIMING
+ double tdiff = (_clock_monotonic + _period_length_us - tme) / 1000.0;
+ if (abs(tdiff) >= .05) {
+ printf("AlsaMidiIO MJ: %.1f ms\n", tdiff);
+ }
+#endif
+ _clock_monotonic = tme;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaMidiOut::AlsaMidiOut ()
+ : AlsaMidiIO ()
+{
+}
+
+int
+AlsaMidiOut::send_event (const pframes_t time, const uint8_t *data, const size_t size)
+{
+ const uint32_t buf_size = sizeof (MidiEventHeader) + size;
+ if (_rb->write_space() < buf_size) {
+ _DEBUGPRINT("AlsaMidiOut: ring buffer overflow\n");
+ return -1;
+ }
+ struct MidiEventHeader h (_clock_monotonic + time * _sample_length_us, size);
+ _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
+ _rb->write (data, size);
+
+ if (pthread_mutex_trylock (&_notify_mutex) == 0) {
+ pthread_cond_signal (&_notify_ready);
+ pthread_mutex_unlock (&_notify_mutex);
+ }
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaMidiIn::AlsaMidiIn ()
+ : AlsaMidiIO ()
+{
+}
+
+size_t
+AlsaMidiIn::recv_event (pframes_t &time, uint8_t *data, size_t &size)
+{
+ const uint32_t read_space = _rb->read_space();
+ struct MidiEventHeader h(0,0);
+
+ if (read_space <= sizeof(MidiEventHeader)) {
+ return 0;
+ }
+
+ RingBuffer<uint8_t>::rw_vector vector;
+ _rb->get_read_vector(&vector);
+ if (vector.len[0] >= sizeof(MidiEventHeader)) {
+ memcpy((uint8_t*)&h, vector.buf[0], sizeof(MidiEventHeader));
+ } else {
+ if (vector.len[0] > 0) {
+ memcpy ((uint8_t*)&h, vector.buf[0], vector.len[0]);
+ }
+ memcpy (((uint8_t*)&h) + vector.len[0], vector.buf[1], sizeof(MidiEventHeader) - vector.len[0]);
+ }
+
+ if (h.time >= _clock_monotonic + _period_length_us ) {
+#ifdef DEBUG_TIMING
+ printf("AlsaMidiIn DEBUG: POSTPONE EVENT TO NEXT CYCLE: %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
+#endif
+ return 0;
+ }
+ _rb->increment_read_idx (sizeof(MidiEventHeader));
+
+ assert (h.size > 0);
+ if (h.size > size) {
+ _DEBUGPRINT("AlsaMidiIn::recv_event MIDI event too large!\n");
+ _rb->increment_read_idx (h.size);
+ return 0;
+ }
+ if (_rb->read (&data[0], h.size) != h.size) {
+ _DEBUGPRINT("AlsaMidiIn::recv_event Garbled MIDI EVENT DATA!!\n");
+ return 0;
+ }
+ if (h.time < _clock_monotonic) {
+#ifdef DEBUG_TIMING
+ printf("AlsaMidiIn DEBUG: MIDI TIME < 0 %.1f spl\n", ((_clock_monotonic - h.time) / -_sample_length_us));
+#endif
+ time = 0;
+ } else if (h.time >= _clock_monotonic + _period_length_us ) {
+#ifdef DEBUG_TIMING
+ printf("AlsaMidiIn DEBUG: MIDI TIME > PERIOD %.1f spl\n", ((h.time - _clock_monotonic) / _sample_length_us));
+#endif
+ time = _samples_per_period - 1;
+ } else {
+ time = floor ((h.time - _clock_monotonic) / _sample_length_us);
+ }
+ assert(time < _samples_per_period);
+ size = h.size;
+ return h.size;
+}
+
+int
+AlsaMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
+ const uint32_t buf_size = sizeof(MidiEventHeader) + size;
+
+ if (size == 0) {
+ return -1;
+ }
+ if (_rb->write_space() < buf_size) {
+ _DEBUGPRINT("AlsaMidiIn: ring buffer overflow\n");
+ return -1;
+ }
+ struct MidiEventHeader h (time, size);
+ _rb->write ((uint8_t*) &h, sizeof(MidiEventHeader));
+ _rb->write (data, size);
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the 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 __libbackend_alsa_midi_h__
+#define __libbackend_alsa_midi_h__
+
+#include <stdint.h>
+#include <poll.h>
+#include <pthread.h>
+
+#include "pbd/ringbuffer.h"
+#include "ardour/types.h"
+
+namespace ARDOUR {
+
+class AlsaMidiIO {
+public:
+ AlsaMidiIO ();
+ virtual ~AlsaMidiIO ();
+
+ int state (void) const { return _state; }
+ int start ();
+ int stop ();
+
+ void setup_timing (const size_t samples_per_period, const float samplerate);
+ void sync_time(uint64_t);
+
+ virtual void* main_process_thread () = 0;
+
+protected:
+ pthread_t _main_thread;
+ pthread_mutex_t _notify_mutex;
+ pthread_cond_t _notify_ready;
+
+ int _state;
+ bool _running;
+
+ int _npfds;
+ struct pollfd *_pfds;
+
+ double _sample_length_us;
+ double _period_length_us;
+ size_t _samples_per_period;
+ uint64_t _clock_monotonic;
+
+ struct MidiEventHeader {
+ uint64_t time;
+ size_t size;
+ MidiEventHeader(const uint64_t t, const size_t s)
+ : time(t)
+ , size(s) {}
+ };
+
+ RingBuffer<uint8_t>* _rb;
+
+protected:
+ virtual void init (const char *device_name, const bool input) = 0;
+
+};
+
+class AlsaMidiOut : virtual public AlsaMidiIO
+{
+public:
+ AlsaMidiOut ();
+
+ int send_event (const pframes_t, const uint8_t *, const size_t);
+};
+
+class AlsaMidiIn : virtual public AlsaMidiIO
+{
+public:
+ AlsaMidiIn ();
+
+ size_t recv_event (pframes_t &, uint8_t *, size_t &);
+
+protected:
+ int queue_event (const uint64_t, const uint8_t *, const size_t);
+};
+
+} // namespace
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2010 Devin Anderson
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <unistd.h>
+#include <glibmm.h>
+
+#include "select_sleep.h"
+#include "alsa_rawmidi.h"
+
+#include "pbd/error.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+/* max bytes per individual midi-event
+ * events larger than this are ignored */
+#define MaxAlsaRawEventSize (64)
+
+#ifndef NDEBUG
+#define _DEBUGPRINT(STR) fprintf(stderr, STR);
+#else
+#define _DEBUGPRINT(STR) ;
+#endif
+
+AlsaRawMidiIO::AlsaRawMidiIO (const char *device, const bool input)
+ : AlsaMidiIO()
+ , _device (0)
+{
+ init (device, input);
+}
+
+AlsaRawMidiIO::~AlsaRawMidiIO ()
+{
+ if (_device) {
+ snd_rawmidi_drain (_device);
+ snd_rawmidi_close (_device);
+ _device = 0;
+ }
+}
+
+void
+AlsaRawMidiIO::init (const char *device_name, const bool input)
+{
+ if (snd_rawmidi_open (
+ input ? &_device : NULL,
+ input ? NULL : &_device,
+ device_name, SND_RAWMIDI_NONBLOCK) < 0) {
+ return;
+ }
+
+ _npfds = snd_rawmidi_poll_descriptors_count (_device);
+ if (_npfds < 1) {
+ _DEBUGPRINT("AlsaRawMidiIO: no poll descriptor(s).\n");
+ snd_rawmidi_close (_device);
+ _device = 0;
+ return;
+ }
+ _pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
+ snd_rawmidi_poll_descriptors (_device, _pfds, _npfds);
+
+#if 0
+ _state = 0;
+#else
+ snd_rawmidi_params_t *params;
+ if (snd_rawmidi_params_malloc (¶ms)) {
+ goto initerr;
+ }
+ if (snd_rawmidi_params_current (_device, params)) {
+ goto initerr;
+ }
+ if (snd_rawmidi_params_set_avail_min (_device, params, 1)) {
+ goto initerr;
+ }
+ if ( snd_rawmidi_params_set_buffer_size (_device, params, 64)) {
+ goto initerr;
+ }
+ if (snd_rawmidi_params_set_no_active_sensing (_device, params, 1)) {
+ goto initerr;
+ }
+
+ _state = 0;
+ return;
+
+initerr:
+ _DEBUGPRINT("AlsaRawMidiIO: parameter setup error\n");
+ snd_rawmidi_close (_device);
+ _device = 0;
+#endif
+ return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaRawMidiOut::AlsaRawMidiOut (const char *device)
+ : AlsaRawMidiIO (device, false)
+ , AlsaMidiOut ()
+{
+}
+
+void *
+AlsaRawMidiOut::main_process_thread ()
+{
+ _running = true;
+ pthread_mutex_lock (&_notify_mutex);
+ bool need_drain = false;
+ while (_running) {
+ bool have_data = false;
+ struct MidiEventHeader h(0,0);
+ uint8_t data[MaxAlsaRawEventSize];
+
+ const uint32_t read_space = _rb->read_space();
+
+ if (read_space > sizeof(MidiEventHeader)) {
+ if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
+ _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT HEADER!!\n");
+ break;
+ }
+ assert (read_space >= h.size);
+ if (h.size > MaxAlsaRawEventSize) {
+ _rb->increment_read_idx (h.size);
+ _DEBUGPRINT("AlsaRawMidiOut: MIDI event too large!\n");
+ continue;
+ }
+ if (_rb->read (&data[0], h.size) != h.size) {
+ _DEBUGPRINT("AlsaRawMidiOut: Garbled MIDI EVENT DATA!!\n");
+ break;
+ }
+ have_data = true;
+ }
+
+ if (!have_data) {
+ if (need_drain) {
+ snd_rawmidi_drain (_device);
+ need_drain = false;
+ }
+ pthread_cond_wait (&_notify_ready, &_notify_mutex);
+ continue;
+ }
+
+ uint64_t now = g_get_monotonic_time();
+ while (h.time > now + 500) {
+ if (need_drain) {
+ snd_rawmidi_drain (_device);
+ need_drain = false;
+ } else {
+ select_sleep(h.time - now);
+ }
+ now = g_get_monotonic_time();
+ }
+
+retry:
+ int perr = poll (_pfds, _npfds, 10 /* ms */);
+ if (perr < 0) {
+ PBD::error << _("AlsaRawMidiOut: Error polling device. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+ if (perr == 0) {
+ _DEBUGPRINT("AlsaRawMidiOut: poll() timed out.\n");
+ goto retry;
+ }
+
+ unsigned short revents = 0;
+ if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
+ PBD::error << _("AlsaRawMidiOut: Failed to poll device. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+
+ if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ PBD::error << _("AlsaRawMidiOut: poll error. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+
+ if (!(revents & POLLOUT)) {
+ _DEBUGPRINT("AlsaRawMidiOut: POLLOUT not ready.\n");
+ select_sleep (1000);
+ goto retry;
+ }
+
+ ssize_t err = snd_rawmidi_write (_device, data, h.size);
+
+ if ((err == -EAGAIN)) {
+ snd_rawmidi_drain (_device);
+ goto retry;
+ }
+ if (err == -EWOULDBLOCK) {
+ select_sleep (1000);
+ goto retry;
+ }
+ if (err < 0) {
+ PBD::error << _("AlsaRawMidiOut: write failed. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+ if ((size_t) err < h.size) {
+ _DEBUGPRINT("AlsaRawMidiOut: short write\n");
+ memmove(&data[0], &data[err], err);
+ h.size -= err;
+ goto retry;
+ }
+ need_drain = true;
+ }
+
+ pthread_mutex_unlock (&_notify_mutex);
+ _DEBUGPRINT("AlsaRawMidiOut: MIDI OUT THREAD STOPPED\n");
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaRawMidiIn::AlsaRawMidiIn (const char *device)
+ : AlsaRawMidiIO (device, true)
+ , AlsaMidiIn ()
+ , _event(0,0)
+ , _first_time(true)
+ , _unbuffered_bytes(0)
+ , _total_bytes(0)
+ , _expected_bytes(0)
+ , _status_byte(0)
+{
+}
+
+void *
+AlsaRawMidiIn::main_process_thread ()
+{
+ _running = true;
+ while (_running) {
+ unsigned short revents = 0;
+
+ int perr = poll (_pfds, _npfds, 100 /* ms */);
+ if (perr < 0) {
+ PBD::error << _("AlsaRawMidiIn: Error polling device. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+ if (perr == 0) {
+ continue;
+ }
+
+ if (snd_rawmidi_poll_descriptors_revents (_device, _pfds, _npfds, &revents)) {
+ PBD::error << _("AlsaRawMidiIn: Failed to poll device. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+
+ if (revents & (POLLERR | POLLHUP | POLLNVAL)) {
+ PBD::error << _("AlsaRawMidiIn: poll error. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+
+ if (!(revents & POLLIN)) {
+ _DEBUGPRINT("AlsaRawMidiOut: POLLIN not ready.\n");
+ select_sleep (1000);
+ continue;
+ }
+
+ uint8_t data[MaxAlsaRawEventSize];
+ uint64_t time = g_get_monotonic_time();
+ ssize_t err = snd_rawmidi_read (_device, data, sizeof(data));
+
+ if ((err == -EAGAIN) || (err == -EWOULDBLOCK)) {
+ continue;
+ }
+ if (err < 0) {
+ PBD::error << _("AlsaRawMidiIn: read error. Terminating Midi") << endmsg;
+ break;
+ }
+ if (err == 0) {
+ _DEBUGPRINT("AlsaRawMidiIn: zero read\n");
+ continue;
+ }
+
+#if 0
+ queue_event (time, data, err);
+#else
+ parse_events (time, data, err);
+#endif
+ }
+
+ _DEBUGPRINT("AlsaRawMidiIn: MIDI IN THREAD STOPPED\n");
+ return 0;
+}
+
+int
+AlsaRawMidiIn::queue_event (const uint64_t time, const uint8_t *data, const size_t size) {
+ _event._pending = false;
+ return AlsaMidiIn::queue_event(time, data, size);
+}
+
+void
+AlsaRawMidiIn::parse_events (const uint64_t time, const uint8_t *data, const size_t size) {
+ if (_event._pending) {
+ _DEBUGPRINT("AlsaRawMidiIn: queue pending event\n");
+ if (queue_event (_event._time, _parser_buffer, _event._size)) {
+ return;
+ }
+ }
+ for (size_t i = 0; i < size; ++i) {
+ if (_first_time && !(data[i] & 0x80)) {
+ continue;
+ }
+ _first_time = false; /// TODO optimize e.g. use fn pointer to different parse_events()
+ if (process_byte(time, data[i])) {
+ if (queue_event (_event._time, _parser_buffer, _event._size)) {
+ return;
+ }
+ }
+ }
+}
+
+// based on JackMidiRawInputWriteQueue by Devin Anderson //
+bool
+AlsaRawMidiIn::process_byte(const uint64_t time, const uint8_t byte)
+{
+ if (byte >= 0xf8) {
+ // Realtime
+ if (byte == 0xfd) {
+ return false;
+ }
+ _parser_buffer[0] = byte;
+ prepare_byte_event(time, byte);
+ return true;
+ }
+ if (byte == 0xf7) {
+ // Sysex end
+ if (_status_byte == 0xf0) {
+ record_byte(byte);
+ return prepare_buffered_event(time);
+ }
+ _total_bytes = 0;
+ _unbuffered_bytes = 0;
+ _expected_bytes = 0;
+ _status_byte = 0;
+ return false;
+ }
+ if (byte >= 0x80) {
+ // Non-realtime status byte
+ if (_total_bytes) {
+ _DEBUGPRINT("AlsaRawMidiIn: discarded bogus midi message\n");
+#if 0
+ for (size_t i=0; i < _total_bytes; ++i) {
+ printf("%02x ", _parser_buffer[i]);
+ }
+ printf("\n");
+#endif
+ _total_bytes = 0;
+ _unbuffered_bytes = 0;
+ }
+ _status_byte = byte;
+ switch (byte & 0xf0) {
+ case 0x80:
+ case 0x90:
+ case 0xa0:
+ case 0xb0:
+ case 0xe0:
+ // Note On, Note Off, Aftertouch, Control Change, Pitch Wheel
+ _expected_bytes = 3;
+ break;
+ case 0xc0:
+ case 0xd0:
+ // Program Change, Channel Pressure
+ _expected_bytes = 2;
+ break;
+ case 0xf0:
+ switch (byte) {
+ case 0xf0:
+ // Sysex
+ _expected_bytes = 0;
+ break;
+ case 0xf1:
+ case 0xf3:
+ // MTC Quarter Frame, Song Select
+ _expected_bytes = 2;
+ break;
+ case 0xf2:
+ // Song Position
+ _expected_bytes = 3;
+ break;
+ case 0xf4:
+ case 0xf5:
+ // Undefined
+ _expected_bytes = 0;
+ _status_byte = 0;
+ return false;
+ case 0xf6:
+ // Tune Request
+ prepare_byte_event(time, byte);
+ _expected_bytes = 0;
+ _status_byte = 0;
+ return true;
+ }
+ }
+ record_byte(byte);
+ return false;
+ }
+ // Data byte
+ if (! _status_byte) {
+ // Data bytes without a status will be discarded.
+ _total_bytes++;
+ _unbuffered_bytes++;
+ return false;
+ }
+ if (! _total_bytes) {
+ _DEBUGPRINT("AlsaRawMidiIn: apply running status\n");
+ record_byte(_status_byte);
+ }
+ record_byte(byte);
+ return (_total_bytes == _expected_bytes) ? prepare_buffered_event(time) : false;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the 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 __libbackend_alsa_rawmidi_h__
+#define __libbackend_alsa_rawmidi_h__
+
+#include <stdint.h>
+#include <poll.h>
+#include <pthread.h>
+
+#include <alsa/asoundlib.h>
+
+#include "pbd/ringbuffer.h"
+#include "ardour/types.h"
+#include "alsa_midi.h"
+
+namespace ARDOUR {
+
+class AlsaRawMidiIO : virtual public AlsaMidiIO {
+public:
+ AlsaRawMidiIO (const char *device, const bool input);
+ virtual ~AlsaRawMidiIO ();
+
+protected:
+ snd_rawmidi_t *_device;
+
+private:
+ void init (const char *device_name, const bool input);
+};
+
+class AlsaRawMidiOut : public AlsaRawMidiIO, public AlsaMidiOut
+{
+public:
+ AlsaRawMidiOut (const char *device);
+ void* main_process_thread ();
+};
+
+class AlsaRawMidiIn : public AlsaRawMidiIO, public AlsaMidiIn
+{
+public:
+ AlsaRawMidiIn (const char *device);
+
+ void* main_process_thread ();
+
+protected:
+ int queue_event (const uint64_t, const uint8_t *, const size_t);
+private:
+ void parse_events (const uint64_t, const uint8_t *, const size_t);
+ bool process_byte (const uint64_t, const uint8_t);
+
+ void record_byte(uint8_t byte) {
+ if (_total_bytes < sizeof(_parser_buffer)) {
+ _parser_buffer[_total_bytes] = byte;
+ } else {
+ ++_unbuffered_bytes;
+ }
+ ++_total_bytes;
+ }
+
+ void prepare_byte_event(const uint64_t time, const uint8_t byte) {
+ _parser_buffer[0] = byte;
+ _event.prepare(time, 1);
+ }
+
+ bool prepare_buffered_event(const uint64_t time) {
+ const bool result = _unbuffered_bytes == 0;
+ if (result) {
+ _event.prepare(time, _total_bytes);
+ }
+ _total_bytes = 0;
+ _unbuffered_bytes = 0;
+ if (_status_byte >= 0xf0) {
+ _expected_bytes = 0;
+ _status_byte = 0;
+ }
+ return result;
+ }
+
+ struct ParserEvent {
+ uint64_t _time;
+ size_t _size;
+ bool _pending;
+ ParserEvent (const uint64_t time, const size_t size)
+ : _time(time)
+ , _size(size)
+ , _pending(false) {}
+
+ void prepare(const uint64_t time, const size_t size) {
+ _time = time;
+ _size = size;
+ _pending = true;
+ }
+ } _event;
+
+ bool _first_time;
+ size_t _unbuffered_bytes;
+ size_t _total_bytes;
+ size_t _expected_bytes;
+ uint8_t _status_byte;
+ uint8_t _parser_buffer[1024];
+};
+
+} // namespace
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <unistd.h>
+#include <glibmm.h>
+
+#include "select_sleep.h"
+#include "alsa_sequencer.h"
+
+#include "pbd/error.h"
+#include "i18n.h"
+
+using namespace ARDOUR;
+
+/* max bytes per individual midi-event
+ * events larger than this are ignored */
+#define MaxAlsaSeqEventSize (64)
+
+#ifndef NDEBUG
+#define _DEBUGPRINT(STR) fprintf(stderr, STR);
+#else
+#define _DEBUGPRINT(STR) ;
+#endif
+
+AlsaSeqMidiIO::AlsaSeqMidiIO (const char *device, const bool input)
+ : AlsaMidiIO()
+ , _seq (0)
+{
+ init (device, input);
+}
+
+AlsaSeqMidiIO::~AlsaSeqMidiIO ()
+{
+ if (_seq) {
+ snd_seq_close (_seq);
+ _seq = 0;
+ }
+}
+
+void
+AlsaSeqMidiIO::init (const char *device_name, const bool input)
+{
+ if (snd_seq_open (&_seq, "hw",
+ input ? SND_SEQ_OPEN_INPUT : SND_SEQ_OPEN_OUTPUT, 0) < 0)
+ {
+ _seq = 0;
+ return;
+ }
+
+ if (snd_seq_set_client_name (_seq, "Ardour")) {
+ _DEBUGPRINT("AlsaSeqMidiIO: cannot set client name.\n");
+ goto initerr;
+ }
+
+ _port = snd_seq_create_simple_port (_seq, "port", SND_SEQ_PORT_CAP_NO_EXPORT |
+ (input ? SND_SEQ_PORT_CAP_WRITE : SND_SEQ_PORT_CAP_READ),
+ SND_SEQ_PORT_TYPE_APPLICATION);
+
+ if (_port < 0) {
+ _DEBUGPRINT("AlsaSeqMidiIO: cannot create port.\n");
+ goto initerr;
+ }
+
+ _npfds = snd_seq_poll_descriptors_count (_seq, input ? POLLIN : POLLOUT);
+ if (_npfds < 1) {
+ _DEBUGPRINT("AlsaSeqMidiIO: no poll descriptor(s).\n");
+ goto initerr;
+ }
+ _pfds = (struct pollfd*) malloc (_npfds * sizeof(struct pollfd));
+ snd_seq_poll_descriptors (_seq, _pfds, _npfds, input ? POLLIN : POLLOUT);
+
+
+ snd_seq_addr_t port;
+ if (snd_seq_parse_address (_seq, &port, device_name) < 0) {
+ _DEBUGPRINT("AlsaSeqMidiIO: cannot resolve hardware port.\n");
+ goto initerr;
+ }
+
+ if (input) {
+ if (snd_seq_connect_from (_seq, _port, port.client, port.port) < 0) {
+ _DEBUGPRINT("AlsaSeqMidiIO: cannot connect input port.\n");
+ goto initerr;
+ }
+ } else {
+ if (snd_seq_connect_to (_seq, _port, port.client, port.port) < 0) {
+ _DEBUGPRINT("AlsaSeqMidiIO: cannot connect output port.\n");
+ goto initerr;
+ }
+ }
+
+ snd_seq_nonblock(_seq, 1);
+
+ _state = 0;
+ return;
+
+initerr:
+ PBD::error << _("AlsaSeqMidiIO: Device initialization failed.") << endmsg;
+ snd_seq_close (_seq);
+ _seq = 0;
+ return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaSeqMidiOut::AlsaSeqMidiOut (const char *device)
+ : AlsaSeqMidiIO (device, false)
+ , AlsaMidiOut ()
+{
+}
+
+void *
+AlsaSeqMidiOut::main_process_thread ()
+{
+ _running = true;
+ bool need_drain = false;
+ snd_midi_event_t *alsa_codec = NULL;
+ snd_midi_event_new (MaxAlsaSeqEventSize, &alsa_codec);
+ pthread_mutex_lock (&_notify_mutex);
+ while (_running) {
+ bool have_data = false;
+ struct MidiEventHeader h(0,0);
+ uint8_t data[MaxAlsaSeqEventSize];
+
+ const uint32_t read_space = _rb->read_space();
+
+ if (read_space > sizeof(MidiEventHeader)) {
+ if (_rb->read ((uint8_t*)&h, sizeof(MidiEventHeader)) != sizeof(MidiEventHeader)) {
+ _DEBUGPRINT("AlsaSeqMidiOut: Garbled MIDI EVENT HEADER!!\n");
+ break;
+ }
+ assert (read_space >= h.size);
+ if (h.size > MaxAlsaSeqEventSize) {
+ _rb->increment_read_idx (h.size);
+ _DEBUGPRINT("AlsaSeqMidiOut: MIDI event too large!\n");
+ continue;
+ }
+ if (_rb->read (&data[0], h.size) != h.size) {
+ _DEBUGPRINT("AlsaSeqMidiOut: Garbled MIDI EVENT DATA!!\n");
+ break;
+ }
+ have_data = true;
+ }
+
+ if (!have_data) {
+ if (need_drain) {
+ snd_seq_drain_output (_seq);
+ need_drain = false;
+ }
+ pthread_cond_wait (&_notify_ready, &_notify_mutex);
+ continue;
+ }
+
+ snd_seq_event_t alsa_event;
+ snd_seq_ev_clear (&alsa_event);
+ snd_midi_event_reset_encode (alsa_codec);
+ if (!snd_midi_event_encode (alsa_codec, data, h.size, &alsa_event)) {
+ PBD::error << _("AlsaSeqMidiOut: Invalid Midi Event.") << endmsg;
+ continue;
+ }
+
+ snd_seq_ev_set_source (&alsa_event, _port);
+ snd_seq_ev_set_subs (&alsa_event);
+ snd_seq_ev_set_direct (&alsa_event);
+
+ uint64_t now = g_get_monotonic_time();
+ while (h.time > now + 500) {
+ if (need_drain) {
+ snd_seq_drain_output (_seq);
+ need_drain = false;
+ } else {
+ select_sleep(h.time - now);
+ }
+ now = g_get_monotonic_time();
+ }
+
+retry:
+ int perr = poll (_pfds, _npfds, 10 /* ms */);
+ if (perr < 0) {
+ PBD::error << _("AlsaSeqMidiOut: Error polling device. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+ if (perr == 0) {
+ _DEBUGPRINT("AlsaSeqMidiOut: poll() timed out.\n");
+ goto retry;
+ }
+
+ ssize_t err = snd_seq_event_output(_seq, &alsa_event);
+
+ if ((err == -EAGAIN)) {
+ snd_seq_drain_output (_seq);
+ goto retry;
+ }
+ if (err == -EWOULDBLOCK) {
+ select_sleep (1000);
+ goto retry;
+ }
+ if (err < 0) {
+ PBD::error << _("AlsaSeqMidiOut: write failed. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+ need_drain = true;
+ }
+
+ pthread_mutex_unlock (&_notify_mutex);
+
+ if (alsa_codec) {
+ snd_midi_event_free(alsa_codec);
+ }
+ _DEBUGPRINT("AlsaSeqMidiOut: MIDI OUT THREAD STOPPED\n");
+ return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+AlsaSeqMidiIn::AlsaSeqMidiIn (const char *device)
+ : AlsaSeqMidiIO (device, true)
+ , AlsaMidiIn ()
+{
+}
+
+void *
+AlsaSeqMidiIn::main_process_thread ()
+{
+ _running = true;
+ bool do_poll = true;
+ snd_midi_event_t *alsa_codec = NULL;
+ snd_midi_event_new (MaxAlsaSeqEventSize, &alsa_codec);
+
+ while (_running) {
+
+ if (do_poll) {
+ snd_seq_poll_descriptors (_seq, _pfds, _npfds, POLLIN);
+ int perr = poll (_pfds, _npfds, 100 /* ms */);
+
+ if (perr < 0) {
+ PBD::error << _("AlsaSeqMidiIn: Error polling device. Terminating Midi Thread.") << endmsg;
+ break;
+ }
+ if (perr == 0) {
+ continue;
+ }
+ }
+
+ snd_seq_event_t *event;
+ uint64_t time = g_get_monotonic_time();
+ ssize_t err = snd_seq_event_input (_seq, &event);
+
+ if ((err == -EAGAIN) || (err == -EWOULDBLOCK)) {
+ do_poll = true;
+ continue;
+ }
+ if (err == -ENOSPC) {
+ PBD::error << _("AlsaSeqMidiIn: FIFO overrun.") << endmsg;
+ do_poll = true;
+ continue;
+ }
+ if (err < 0) {
+ PBD::error << _("AlsaSeqMidiIn: read error. Terminating Midi") << endmsg;
+ break;
+ }
+
+ uint8_t data[MaxAlsaSeqEventSize];
+ snd_midi_event_reset_decode (alsa_codec);
+ ssize_t size = snd_midi_event_decode (alsa_codec, data, sizeof(data), event);
+
+ if (size > 0) {
+ queue_event (time, data, size);
+ }
+ do_poll = (0 == err);
+ }
+
+ if (alsa_codec) {
+ snd_midi_event_free(alsa_codec);
+ }
+ _DEBUGPRINT("AlsaSeqMidiIn: MIDI IN THREAD STOPPED\n");
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the 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 __libbackend_alsa_sequencer_h__
+#define __libbackend_alsa_sequencer_h__
+
+#include <stdint.h>
+#include <poll.h>
+#include <pthread.h>
+
+#include <alsa/asoundlib.h>
+
+#include "pbd/ringbuffer.h"
+#include "ardour/types.h"
+#include "alsa_midi.h"
+
+namespace ARDOUR {
+
+class AlsaSeqMidiIO : virtual public AlsaMidiIO {
+public:
+ AlsaSeqMidiIO (const char *port_name, const bool input);
+ virtual ~AlsaSeqMidiIO ();
+
+protected:
+ snd_seq_t *_seq;
+ int _port;
+
+private:
+ void init (const char *device_name, const bool input);
+};
+
+class AlsaSeqMidiOut : public AlsaSeqMidiIO, public AlsaMidiOut
+{
+public:
+ AlsaSeqMidiOut (const char *port_name);
+ void* main_process_thread ();
+};
+
+class AlsaSeqMidiIn : public AlsaSeqMidiIO, public AlsaMidiIn
+{
+public:
+ AlsaSeqMidiIn (const char *port_name);
+
+ void* main_process_thread ();
+};
+
+} // namespace
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the 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 __libbackend_alsa_rthread_h__
+#define __libbackend_alsa_rthread_h__
+
+#include <pthread.h>
+#include <sched.h>
+
+static int
+_realtime_pthread_create (
+ const int policy, int priority, const size_t stacksize,
+ pthread_t *thread,
+ void *(*start_routine) (void *),
+ void *arg)
+{
+ int rv;
+
+ pthread_attr_t attr;
+ struct sched_param parm;
+
+ const int p_min = sched_get_priority_min (policy);
+ const int p_max = sched_get_priority_max (policy);
+ priority += p_max;
+ if (priority > p_max) priority = p_max;
+ if (priority < p_min) priority = p_min;
+ parm.sched_priority = priority;
+
+ pthread_attr_init (&attr);
+ pthread_attr_setschedpolicy (&attr, policy);
+ pthread_attr_setschedparam (&attr, &parm);
+ pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
+ pthread_attr_setinheritsched (&attr, PTHREAD_EXPLICIT_SCHED);
+ pthread_attr_setstacksize (&attr, stacksize);
+ rv = pthread_create (thread, &attr, start_routine, arg);
+ pthread_attr_destroy (&attr);
+ return rv;
+}
+
+#endif
--- /dev/null
+/*
+ * Copyright (C) 2004,2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <sys/select.h>
+
+/* select() sleeps _at most_ a given time.
+ * (compared to usleep() or nanosleep() which sleep at least a given time)
+ */
+static void select_sleep (uint64_t usec) {
+ if (usec <= 10) return;
+ fd_set fd;
+ int max_fd=0;
+ struct timeval tv;
+ tv.tv_sec = usec / 1000000;
+ tv.tv_usec = usec % 1000000;
+ FD_ZERO (&fd);
+ select (max_fd, &fd, NULL, NULL, &tv);
+ // on Linux, tv reflects the actual time slept.
+}
--- /dev/null
+#!/usr/bin/env python
+from waflib.extras import autowaf as autowaf
+import os
+import sys
+import re
+
+# Library version (UNIX style major, minor, micro)
+# major increment <=> incompatible changes
+# minor increment <=> compatible changes (additions)
+# micro increment <=> no interface changes
+ALSABACKEND_VERSION = '0.0.1'
+I18N_PACKAGE = 'alsa-backend'
+
+# Mandatory variables
+top = '.'
+out = 'build'
+
+def options(opt):
+ autowaf.set_options(opt)
+
+def configure(conf):
+ autowaf.configure(conf)
+
+def build(bld):
+ obj = bld(features = 'cxx cxxshlib')
+ obj.source = [
+ 'alsa_audiobackend.cc',
+ 'alsa_midi.cc',
+ 'alsa_rawmidi.cc',
+ 'alsa_sequencer.cc',
+ 'zita-alsa-pcmi.cc',
+ ]
+ obj.includes = ['.']
+ obj.name = 'alsa_audiobackend'
+ obj.target = 'alsa_audiobackend'
+ obj.use = 'libardour libpbd ardouralsautil'
+ obj.uselib = 'ALSA'
+ obj.vnum = ALSABACKEND_VERSION
+ obj.install_path = os.path.join(bld.env['LIBDIR'], 'backends')
+ obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
+ 'ARDOURBACKEND_DLL_EXPORTS'
+ ]
--- /dev/null
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#include <endian.h>
+#include <sys/time.h>
+#include "zita-alsa-pcmi.h"
+
+
+// Public members ----------------------------------------------------------------------
+
+
+int zita_alsa_pcmi_major_version (void)
+{
+ return ZITA_ALSA_PCMI_MAJOR_VERSION;
+}
+
+
+int zita_alsa_pcmi_minor_version (void)
+{
+ return ZITA_ALSA_PCMI_MINOR_VERSION;
+}
+
+
+Alsa_pcmi::Alsa_pcmi (
+ const char *play_name,
+ const char *capt_name,
+ const char *ctrl_name,
+ unsigned int fsamp,
+ unsigned int fsize,
+ unsigned int nfrag,
+ unsigned int debug)
+ : _fsamp (fsamp)
+ , _fsize (fsize)
+ , _nfrag (nfrag)
+ , _debug (debug)
+ , _state (-1)
+ , _play_handle (0)
+ , _capt_handle (0)
+ , _ctrl_handle (0)
+ , _play_hwpar (0)
+ , _play_swpar (0)
+ , _capt_hwpar (0)
+ , _capt_swpar (0)
+ , _play_nchan (0)
+ , _capt_nchan (0)
+ , _play_xrun (0)
+ , _capt_xrun (0)
+ , _synced (false)
+ , _play_npfd (0)
+ , _capt_npfd (0)
+{
+ const char *p;
+
+ p = getenv ("ZITA_ALSA_PCMI_DEBUG");
+ if (p && *p) _debug = atoi (p);
+ initialise (play_name, capt_name, ctrl_name);
+}
+
+
+Alsa_pcmi::~Alsa_pcmi (void)
+{
+ if (_play_handle) snd_pcm_close (_play_handle);
+ if (_capt_handle) snd_pcm_close (_capt_handle);
+ if (_ctrl_handle) snd_ctl_close (_ctrl_handle);
+
+ snd_pcm_sw_params_free (_capt_swpar);
+ snd_pcm_hw_params_free (_capt_hwpar);
+ snd_pcm_sw_params_free (_play_swpar);
+ snd_pcm_hw_params_free (_play_hwpar);
+}
+
+
+int Alsa_pcmi::pcm_start (void)
+{
+ unsigned int i, j, n;
+ int err;
+
+ if (_play_handle)
+ {
+ n = snd_pcm_avail_update (_play_handle);
+ if (n != _fsize * _nfrag)
+ {
+ if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: full buffer not available at start.\n");
+ return -1;
+ }
+ for (i = 0; i < _nfrag; i++)
+ {
+ play_init (_fsize);
+ for (j = 0; j < _play_nchan; j++) clear_chan (j, _fsize);
+ play_done (_fsize);
+ }
+ if ((err = snd_pcm_start (_play_handle)) < 0)
+ {
+ if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_start(play): %s.\n", snd_strerror (err));
+ return -1;
+ }
+ }
+ if (_capt_handle && !_synced && ((err = snd_pcm_start (_capt_handle)) < 0))
+ {
+ if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_start(capt): %s.\n", snd_strerror (err));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int Alsa_pcmi::pcm_stop (void)
+{
+ int err;
+
+ if (_play_handle && ((err = snd_pcm_drop (_play_handle)) < 0))
+ {
+ if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_drop(play): %s.\n", snd_strerror (err));
+ return -1;
+ }
+ if (_capt_handle && !_synced && ((err = snd_pcm_drop (_capt_handle)) < 0))
+ {
+ if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_drop(capt): %s.\n", snd_strerror (err));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+snd_pcm_sframes_t Alsa_pcmi::pcm_wait (void)
+{
+ bool need_capt;
+ bool need_play;
+ snd_pcm_sframes_t capt_av;
+ snd_pcm_sframes_t play_av;
+ unsigned short rev;
+ int i, r, n1, n2;
+
+ _state = 0;
+ need_capt = _capt_handle ? true : false;
+ need_play = _play_handle ? true : false;
+
+ while (need_play || need_capt)
+ {
+ n1 = 0;
+ if (need_play)
+ {
+ snd_pcm_poll_descriptors (_play_handle, _poll_fd, _play_npfd);
+ n1 += _play_npfd;
+ }
+ n2 = n1;
+ if (need_capt)
+ {
+ snd_pcm_poll_descriptors (_capt_handle, _poll_fd + n1, _capt_npfd);
+ n2 += _capt_npfd;
+ }
+ for (i = 0; i < n2; i++) _poll_fd [i].events |= POLLERR;
+
+ r = poll (_poll_fd, n2, 1000);
+ if (r < 0)
+ {
+ if (errno == EINTR) return 0;
+ if (_debug & DEBUG_WAIT) fprintf (stderr, "Alsa_pcmi: poll(): %s\n.", strerror (errno));
+ _state = -1;
+ return 0;
+ }
+ if (r == 0)
+ {
+ if (_debug & DEBUG_WAIT) fprintf (stderr, "Alsa_pcmi: poll timed out.\n");
+ _state = -1;
+ return 0;
+ }
+
+ if (need_play)
+ {
+ snd_pcm_poll_descriptors_revents (_play_handle, _poll_fd, n1, &rev);
+ if (rev & POLLERR)
+ {
+ if (_debug & DEBUG_WAIT) fprintf (stderr, "Alsa_pcmi: error on playback pollfd.\n");
+ _state = 1;
+ recover ();
+ return 0;
+ }
+ if (rev & POLLOUT) need_play = false;
+ }
+ if (need_capt)
+ {
+ snd_pcm_poll_descriptors_revents (_capt_handle, _poll_fd + n1, n2 - n1, &rev);
+ if (rev & POLLERR)
+ {
+ if (_debug & DEBUG_WAIT) fprintf (stderr, "Alsa_pcmi: error on capture pollfd.\n");
+ _state = 1;
+ recover ();
+ return 0;
+ }
+ if (rev & POLLIN) need_capt = false;
+ }
+ }
+
+ play_av = 999999999;
+ if (_play_handle && (play_av = snd_pcm_avail_update (_play_handle)) < 0)
+ {
+ _state = -1;
+ recover ();
+ return 0;
+ }
+ capt_av = 999999999;
+ if (_capt_handle && (capt_av = snd_pcm_avail_update (_capt_handle)) < 0)
+ {
+ _state = -1;
+ recover ();
+ return 0;
+ }
+
+ return (capt_av < play_av) ? capt_av : play_av;
+}
+
+
+int Alsa_pcmi::pcm_idle (int len)
+{
+ unsigned int i;
+ snd_pcm_uframes_t n, k;
+
+ if (_capt_handle)
+ {
+ n = len;
+ while (n)
+ {
+ k = capt_init (n);
+ capt_done (k);
+ n -= k;
+ }
+ }
+ if (_play_handle)
+ {
+ n = len;
+ while (n)
+ {
+ k = play_init (n);
+ for (i = 0; i < _play_nchan; i++) clear_chan (i, k);
+ play_done (k);
+ n -= k;
+ }
+ }
+ return 0;
+}
+
+
+int Alsa_pcmi::play_init (snd_pcm_uframes_t len)
+{
+ unsigned int i;
+ const snd_pcm_channel_area_t *a;
+ int err;
+
+ if ((err = snd_pcm_mmap_begin (_play_handle, &a, &_play_offs, &len)) < 0)
+ {
+ if (_debug & DEBUG_DATA) fprintf (stderr, "Alsa_pcmi: snd_pcm_mmap_begin(play): %s.\n", snd_strerror (err));
+ return -1;
+ }
+ _play_step = (a->step) >> 3;
+ for (i = 0; i < _play_nchan; i++, a++)
+ {
+ _play_ptr [i] = (char *) a->addr + ((a->first + a->step * _play_offs) >> 3);
+ }
+
+ return len;
+}
+
+
+int Alsa_pcmi::capt_init (snd_pcm_uframes_t len)
+{
+ unsigned int i;
+ const snd_pcm_channel_area_t *a;
+ int err;
+
+ if ((err = snd_pcm_mmap_begin (_capt_handle, &a, &_capt_offs, &len)) < 0)
+ {
+ if (_debug & DEBUG_DATA) fprintf (stderr, "Alsa_pcmi: snd_pcm_mmap_begin(capt): %s.\n", snd_strerror (err));
+ return -1;
+ }
+ _capt_step = (a->step) >> 3;
+ for (i = 0; i < _capt_nchan; i++, a++)
+ {
+ _capt_ptr [i] = (char *) a->addr + ((a->first + a->step * _capt_offs) >> 3);
+ }
+
+ return len;
+}
+
+
+void Alsa_pcmi::clear_chan (int chan, int len)
+{
+ _play_ptr [chan] = (this->*Alsa_pcmi::_clear_func)(_play_ptr [chan], len);
+}
+
+
+void Alsa_pcmi::play_chan (int chan, const float *src, int len, int step)
+{
+ _play_ptr [chan] = (this->*Alsa_pcmi::_play_func)(src, _play_ptr [chan], len, step);
+}
+
+
+void Alsa_pcmi::capt_chan (int chan, float *dst, int len, int step)
+{
+ _capt_ptr [chan] = (this->*Alsa_pcmi::_capt_func)(_capt_ptr [chan], dst, len, step);
+}
+
+
+int Alsa_pcmi::play_done (int len)
+{
+ return snd_pcm_mmap_commit (_play_handle, _play_offs, len);
+}
+
+
+int Alsa_pcmi::capt_done (int len)
+{
+ return snd_pcm_mmap_commit (_capt_handle, _capt_offs, len);
+}
+
+
+void Alsa_pcmi::printinfo (void)
+{
+ fprintf (stdout, "playback :");
+ if (_play_handle)
+ {
+ fprintf (stdout, "\n nchan : %d\n", _play_nchan);
+ fprintf (stdout, " fsamp : %d\n", _fsamp);
+ fprintf (stdout, " fsize : %ld\n", _fsize);
+ fprintf (stdout, " nfrag : %d\n", _nfrag);
+ fprintf (stdout, " format : %s\n", snd_pcm_format_name (_play_format));
+ }
+ else fprintf (stdout, " not enabled\n");
+ fprintf (stdout, "capture :");
+ if (_capt_handle)
+ {
+ fprintf (stdout, "\n nchan : %d\n", _capt_nchan);
+ fprintf (stdout, " fsamp : %d\n", _fsamp);
+ fprintf (stdout, " fsize : %ld\n", _fsize);
+ fprintf (stdout, " nfrag : %d\n", _nfrag);
+ fprintf (stdout, " format : %s\n", snd_pcm_format_name (_capt_format));
+ if (_play_handle) fprintf (stdout, "%s\n", _synced ? "synced" : "not synced");
+ }
+ else fprintf (stdout, " not enabled\n");
+}
+
+
+// Private members ---------------------------------------------------------------------
+
+
+void Alsa_pcmi::initialise (const char *play_name, const char *capt_name, const char *ctrl_name)
+{
+ unsigned int fsamp;
+ snd_pcm_uframes_t fsize;
+ unsigned int nfrag;
+ int err;
+ int dir;
+ snd_ctl_card_info_t *card;
+
+ if (play_name)
+ {
+ if (snd_pcm_open (&_play_handle, play_name, SND_PCM_STREAM_PLAYBACK, 0) < 0)
+ {
+ _play_handle = 0;
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: Cannot open PCM device %s for playback.\n",
+ play_name);
+ }
+ }
+
+ if (capt_name)
+ {
+ if (snd_pcm_open (&_capt_handle, capt_name, SND_PCM_STREAM_CAPTURE, 0) < 0)
+ {
+ _capt_handle = 0;
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: Cannot open PCM device %s for capture.\n",
+ capt_name);
+ }
+ }
+
+ if (! _play_handle || ! _capt_handle) return;
+
+ if (ctrl_name)
+ {
+ snd_ctl_card_info_alloca (&card);
+
+ if ((err = snd_ctl_open (&_ctrl_handle, ctrl_name, 0)) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alse_driver: ctl_open(): %s\n",
+ snd_strerror (err));
+ return;
+ }
+ if ((err = snd_ctl_card_info (_ctrl_handle, card)) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: ctl_card_info(): %s\n",
+ snd_strerror (err));
+ return;
+ }
+ }
+
+ _state = -2;
+ if (_play_handle)
+ {
+ if (snd_pcm_hw_params_malloc (&_play_hwpar) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't allocate playback hw params\n");
+ return;
+ }
+ if (snd_pcm_sw_params_malloc (&_play_swpar) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't allocate playback sw params\n");
+ return;
+ }
+ if (set_hwpar (_play_handle, _play_hwpar, "playback", &_play_nchan) < 0) return;
+ if (set_swpar (_play_handle, _play_swpar, "playback") < 0) return;
+ }
+
+ if (_capt_handle)
+ {
+ if (snd_pcm_hw_params_malloc (&_capt_hwpar) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't allocate capture hw params\n");
+ return;
+ }
+ if (snd_pcm_sw_params_malloc (&_capt_swpar) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't allocate capture sw params\n");
+ return;
+ }
+ if (set_hwpar (_capt_handle, _capt_hwpar, "capture", &_capt_nchan) < 0) return;
+ if (set_swpar (_capt_handle, _capt_swpar, "capture") < 0) return;
+ }
+
+ if (_play_handle)
+ {
+ if (snd_pcm_hw_params_get_rate (_play_hwpar, &fsamp, &dir) || (fsamp != _fsamp) || dir)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested sample rate for playback.\n");
+ _state = -3;
+ return;
+ }
+ if (snd_pcm_hw_params_get_period_size (_play_hwpar, &fsize, &dir) || (fsize != _fsize) || dir)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested period size for playback.\n");
+ _state = -4;
+ return;
+ }
+ if (snd_pcm_hw_params_get_periods (_play_hwpar, &nfrag, &dir) || (nfrag != _nfrag) || dir)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested number of periods for playback.\n");
+ _state = -5;
+ return;
+ }
+
+ snd_pcm_hw_params_get_format (_play_hwpar, &_play_format);
+ snd_pcm_hw_params_get_access (_play_hwpar, &_play_access);
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ switch (_play_format)
+ {
+ case SND_PCM_FORMAT_FLOAT_LE:
+ _clear_func = &Alsa_pcmi::clear_32;
+ _play_func = &Alsa_pcmi::play_float;
+ break;
+
+ case SND_PCM_FORMAT_S32_LE:
+ _clear_func = &Alsa_pcmi::clear_32;
+ _play_func = &Alsa_pcmi::play_32;
+ break;
+
+ case SND_PCM_FORMAT_S32_BE:
+ _clear_func = &Alsa_pcmi::clear_32;
+ _play_func = &Alsa_pcmi::play_32swap;
+ break;
+
+ case SND_PCM_FORMAT_S24_3LE:
+ _clear_func = &Alsa_pcmi::clear_24;
+ _play_func = &Alsa_pcmi::play_24;
+ break;
+
+ case SND_PCM_FORMAT_S24_3BE:
+ _clear_func = &Alsa_pcmi::clear_24;
+ _play_func = &Alsa_pcmi::play_24swap;
+ break;
+
+ case SND_PCM_FORMAT_S16_LE:
+ _clear_func = &Alsa_pcmi::clear_16;
+ _play_func = &Alsa_pcmi::play_16;
+ break;
+
+ case SND_PCM_FORMAT_S16_BE:
+ _clear_func = &Alsa_pcmi::clear_16;
+ _play_func = &Alsa_pcmi::play_16swap;
+ break;
+
+ default:
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't handle playback sample format.\n");
+ _state = -6;
+ return;
+ }
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ switch (_play_format)
+ {
+ case SND_PCM_FORMAT_S32_LE:
+ _clear_func = &Alsa_pcmi::clear_32;
+ _play_func = &Alsa_pcmi::play_32swap;
+ break;
+
+ case SND_PCM_FORMAT_S32_BE:
+ _clear_func = &Alsa_pcmi::clear_32;
+ _play_func = &Alsa_pcmi::play_32;
+ break;
+
+ case SND_PCM_FORMAT_S24_3LE:
+ _clear_func = &Alsa_pcmi::clear_24;
+ _play_func = &Alsa_pcmi::play_24swap;
+ break;
+
+ case SND_PCM_FORMAT_S24_3BE:
+ _clear_func = &Alsa_pcmi::clear_24;
+ _play_func = &Alsa_pcmi::play_24;
+ break;
+
+ case SND_PCM_FORMAT_S16_LE:
+ _clear_func = &Alsa_pcmi::clear_16;
+ _play_func = &Alsa_pcmi::play_16swap;
+ break;
+
+ case SND_PCM_FORMAT_S16_BE:
+ _clear_func = &Alsa_pcmi::clear_16;
+ _play_func = &Alsa_pcmi::play_16;
+ break;
+
+ default:
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't handle playback sample format.\n");
+ _state = -6;
+ return;
+ }
+#else
+#error "System byte order is undefined or not supported"
+#endif
+
+ _play_npfd = snd_pcm_poll_descriptors_count (_play_handle);
+ }
+
+ if (_capt_handle)
+ {
+ if (snd_pcm_hw_params_get_rate (_capt_hwpar, &fsamp, &dir) || (fsamp != _fsamp) || dir)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested sample rate for capture.\n");
+ _state = -3;
+ return;
+ }
+ if (snd_pcm_hw_params_get_period_size (_capt_hwpar, &fsize, &dir) || (fsize != _fsize) || dir)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested period size for capture.\n");
+ _state = -4;
+ return;
+ }
+ if (snd_pcm_hw_params_get_periods (_capt_hwpar, &nfrag, &dir) || (nfrag != _nfrag) || dir)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't get requested number of periods for capture.\n");
+ _state = -5;
+ return;
+ }
+
+ if (_play_handle) _synced = ! snd_pcm_link (_play_handle, _capt_handle);
+
+ snd_pcm_hw_params_get_format (_capt_hwpar, &_capt_format);
+ snd_pcm_hw_params_get_access (_capt_hwpar, &_capt_access);
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+ switch (_capt_format)
+ {
+ case SND_PCM_FORMAT_FLOAT_LE:
+ _capt_func = &Alsa_pcmi::capt_float;
+ break;
+
+ case SND_PCM_FORMAT_S32_LE:
+ _capt_func = &Alsa_pcmi::capt_32;
+ break;
+
+ case SND_PCM_FORMAT_S32_BE:
+ _capt_func = &Alsa_pcmi::capt_32swap;
+ break;
+
+ case SND_PCM_FORMAT_S24_3LE:
+ _capt_func = &Alsa_pcmi::capt_24;
+ break;
+
+ case SND_PCM_FORMAT_S24_3BE:
+ _capt_func = &Alsa_pcmi::capt_24swap;
+ break;
+
+ case SND_PCM_FORMAT_S16_LE:
+ _capt_func = &Alsa_pcmi::capt_16;
+ break;
+
+ case SND_PCM_FORMAT_S16_BE:
+ _capt_func = &Alsa_pcmi::capt_16swap;
+ break;
+
+ default:
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't handle capture sample format.\n");
+ _state = -6;
+ return;
+ }
+#elif __BYTE_ORDER == __BIG_ENDIAN
+ switch (_capt_format)
+ {
+ case SND_PCM_FORMAT_S32_LE:
+ _capt_func = &Alsa_pcmi::capt_32swap;
+ break;
+
+ case SND_PCM_FORMAT_S32_BE:
+ _capt_func = &Alsa_pcmi::capt_32;
+ break;
+
+ case SND_PCM_FORMAT_S24_3LE:
+ _capt_func = &Alsa_pcmi::capt_24swap;
+ break;
+
+ case SND_PCM_FORMAT_S24_3BE:
+ _capt_func = &Alsa_pcmi::capt_24;
+ break;
+
+ case SND_PCM_FORMAT_S16_LE:
+ _capt_func = &Alsa_pcmi::capt_16swap;
+ break;
+
+ case SND_PCM_FORMAT_S16_BE:
+ _capt_func = &Alsa_pcmi::capt_16;
+ break;
+
+ default:
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't handle capture sample format.\n");
+ _state = -6;
+ return;
+ }
+#else
+#error "System byte order is undefined or not supported"
+#endif
+
+ _capt_npfd = snd_pcm_poll_descriptors_count (_capt_handle);
+ }
+
+ if (_play_npfd + _capt_npfd > MAXPFD)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: interface requires more than %d pollfd\n", MAXPFD);
+ return;
+ }
+
+ _state = 0;
+}
+
+
+int Alsa_pcmi::set_hwpar (snd_pcm_t *handle, snd_pcm_hw_params_t *hwpar, const char *sname, unsigned int *nchan)
+{
+ bool err;
+
+ if (snd_pcm_hw_params_any (handle, hwpar) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: no %s hw configurations available.\n",
+ sname);
+ return -1;
+ }
+ if (snd_pcm_hw_params_set_periods_integer (handle, hwpar) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s period size to integral value.\n",
+ sname);
+ return -1;
+ }
+ if ( (snd_pcm_hw_params_set_access (handle, hwpar, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) < 0)
+ && (snd_pcm_hw_params_set_access (handle, hwpar, SND_PCM_ACCESS_MMAP_INTERLEAVED) < 0)
+ && (snd_pcm_hw_params_set_access (handle, hwpar, SND_PCM_ACCESS_MMAP_COMPLEX) < 0))
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: the %s interface doesn't support mmap-based access.\n",
+ sname);
+ return -1;
+ }
+ if (_debug & FORCE_16B)
+ {
+ err = (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S16_LE) < 0)
+ && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S16_BE) < 0);
+ }
+ else
+ {
+ err = (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_FLOAT_LE) < 0)
+ && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S32_LE) < 0)
+ && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S32_BE) < 0)
+ && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S24_3LE) < 0)
+ && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S24_3BE) < 0)
+ && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S16_LE) < 0)
+ && (snd_pcm_hw_params_set_format (handle, hwpar, SND_PCM_FORMAT_S16_BE) < 0);
+ }
+ if (err)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: no supported sample format on %s interface.\n.",
+ sname);
+ return -1;
+ }
+ if (snd_pcm_hw_params_set_rate (handle, hwpar, _fsamp, 0) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s sample rate to %u.\n",
+ sname, _fsamp);
+ return -1;
+ }
+ snd_pcm_hw_params_get_channels_max (hwpar, nchan);
+ if (*nchan > 1024)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: detected more than 1024 %s channnels, reset to 2.\n",
+ sname);
+ *nchan = 2;
+ }
+ if (_debug & FORCE_2CH)
+ {
+ *nchan = 2;
+ }
+ if (*nchan > MAXCHAN)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: number of %s channels reduced to %d.\n",
+ sname, MAXCHAN);
+ *nchan = MAXCHAN;
+ }
+
+ if (snd_pcm_hw_params_set_channels (handle, hwpar, *nchan) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s channel count to %u.\n",
+ sname, *nchan);
+ return -1;
+ }
+ if (snd_pcm_hw_params_set_period_size (handle, hwpar, _fsize, 0) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s period size to %lu.\n",
+ sname, _fsize);
+ return -1;
+ }
+ if (snd_pcm_hw_params_set_periods (handle, hwpar, _nfrag, 0) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s periods to %u.\n",
+ sname, _nfrag);
+ return -1;
+ }
+ if (snd_pcm_hw_params_set_buffer_size (handle, hwpar, _fsize * _nfrag) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s buffer length to %lu.\n",
+ sname, _fsize * _nfrag);
+ return -1;
+ }
+ if (snd_pcm_hw_params (handle, hwpar) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s hardware parameters.\n",
+ sname);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int Alsa_pcmi::set_swpar (snd_pcm_t *handle, snd_pcm_sw_params_t *swpar, const char *sname)
+{
+ int err;
+
+ snd_pcm_sw_params_current (handle, swpar);
+
+ if ((err = snd_pcm_sw_params_set_tstamp_mode (handle, swpar, SND_PCM_TSTAMP_MMAP)) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s timestamp mode to %u.\n",
+ sname, SND_PCM_TSTAMP_MMAP);
+ return -1;
+ }
+ if ((err = snd_pcm_sw_params_set_avail_min (handle, swpar, _fsize)) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s avail_min to %lu.\n",
+ sname, _fsize);
+ return -1;
+ }
+ if ((err = snd_pcm_sw_params (handle, swpar)) < 0)
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: can't set %s software parameters.\n",
+ sname);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int Alsa_pcmi::recover (void)
+{
+ int err;
+ snd_pcm_status_t *stat;
+
+ snd_pcm_status_alloca (&stat);
+
+ if (_play_handle)
+ {
+ if ((err = snd_pcm_status (_play_handle, stat)) < 0)
+ {
+ if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_status(play): %s\n",
+ snd_strerror (err));
+ }
+ _play_xrun = xruncheck (stat);
+ }
+ if (_capt_handle)
+ {
+ if ((err = snd_pcm_status (_capt_handle, stat)) < 0)
+ {
+ if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_status(capt): %s\n",
+ snd_strerror (err));
+ }
+ _capt_xrun = xruncheck (stat);
+ }
+
+ if (pcm_stop ()) return -1;
+ if (_play_handle && ((err = snd_pcm_prepare (_play_handle)) < 0))
+ {
+ if (_debug & DEBUG_STAT) fprintf (stderr, "Alsa_pcmi: pcm_prepare(play): %s\n",
+ snd_strerror (err));
+ return -1;
+ }
+ if (_capt_handle && !_synced && ((err = snd_pcm_prepare (_capt_handle)) < 0))
+ {
+ if (_debug & DEBUG_INIT) fprintf (stderr, "Alsa_pcmi: pcm_prepare(capt): %s\n",
+ snd_strerror (err));
+ return -1;
+ }
+ if (pcm_start ()) return -1;
+
+ return 0;
+}
+
+
+float Alsa_pcmi::xruncheck (snd_pcm_status_t *stat)
+{
+ struct timeval tupd, trig;
+ int ds, du;
+
+ if (snd_pcm_status_get_state (stat) == SND_PCM_STATE_XRUN)
+ {
+ snd_pcm_status_get_tstamp (stat, &tupd);
+ snd_pcm_status_get_trigger_tstamp (stat, &trig);
+ ds = tupd.tv_sec - trig.tv_sec;
+ du = tupd.tv_usec - trig.tv_usec;
+ if (du < 0)
+ {
+ du += 1000000;
+ ds -= 1;
+ }
+ return ds + 1e-6f * du;
+ }
+ return 0.0f;
+}
+
+
+char *Alsa_pcmi::clear_16 (char *dst, int nfrm)
+{
+ while (nfrm--)
+ {
+ *((short int *) dst) = 0;
+ dst += _play_step;
+ }
+ return dst;
+}
+
+char *Alsa_pcmi::clear_24 (char *dst, int nfrm)
+{
+ while (nfrm--)
+ {
+ dst [0] = 0;
+ dst [1] = 0;
+ dst [2] = 0;
+ dst += _play_step;
+ }
+ return dst;
+}
+
+char *Alsa_pcmi::clear_32 (char *dst, int nfrm)
+{
+ while (nfrm--)
+ {
+ *((int *) dst) = 0;
+ dst += _play_step;
+ }
+ return dst;
+}
+
+
+char *Alsa_pcmi::play_16 (const float *src, char *dst, int nfrm, int step)
+{
+ float s;
+ short int d;
+
+ while (nfrm--)
+ {
+ s = *src;
+ if (s > 1) d = 0x7fff;
+ else if (s < -1) d = 0x8001;
+ else d = (short int)((float) 0x7fff * s);
+ *((short int *) dst) = d;
+ dst += _play_step;
+ src += step;
+ }
+ return dst;
+}
+
+char *Alsa_pcmi::play_16swap (const float *src, char *dst, int nfrm, int step)
+{
+ float s;
+ short int d;
+
+ while (nfrm--)
+ {
+ s = *src;
+ if (s > 1) d = 0x7fff;
+ else if (s < -1) d = 0x8001;
+ else d = (short int)((float) 0x7fff * s);
+ dst [0] = d >> 8;
+ dst [1] = d;
+ dst += _play_step;
+ src += step;
+ }
+ return dst;
+}
+
+char *Alsa_pcmi::play_24 (const float *src, char *dst, int nfrm, int step)
+{
+ float s;
+ int d;
+
+ while (nfrm--)
+ {
+ s = *src;
+ if (s > 1) d = 0x007fffff;
+ else if (s < -1) d = 0x00800001;
+ else d = (int)((float) 0x007fffff * s);
+ dst [0] = d;
+ dst [1] = d >> 8;
+ dst [2] = d >> 16;
+ dst += _play_step;
+ src += step;
+ }
+ return dst;
+}
+
+char *Alsa_pcmi::play_24swap (const float *src, char *dst, int nfrm, int step)
+{
+ float s;
+ int d;
+
+ while (nfrm--)
+ {
+ s = *src;
+ if (s > 1) d = 0x007fffff;
+ else if (s < -1) d = 0x00800001;
+ else d = (int)((float) 0x007fffff * s);
+ dst [0] = d >> 16;
+ dst [1] = d >> 8;
+ dst [2] = d;
+ dst += _play_step;
+ src += step;
+ }
+ return dst;
+}
+
+char *Alsa_pcmi::play_32 (const float *src, char *dst, int nfrm, int step)
+{
+ float s;
+ int d;
+
+ while (nfrm--)
+ {
+ s = *src;
+ if (s > 1) d = 0x007fffff;
+ else if (s < -1) d = 0x00800001;
+ else d = (int)((float) 0x007fffff * s);
+ *((int *) dst) = d << 8;
+ dst += _play_step;
+ src += step;
+ }
+ return dst;
+}
+
+char *Alsa_pcmi::play_32swap (const float *src, char *dst, int nfrm, int step)
+{
+ float s;
+ int d;
+
+ while (nfrm--)
+ {
+ s = *src;
+ if (s > 1) d = 0x007fffff;
+ else if (s < -1) d = 0x00800001;
+ else d = (int)((float) 0x007fffff * s);
+ dst [0] = d >> 16;
+ dst [1] = d >> 8;
+ dst [2] = d;
+ dst [3] = 0;
+ dst += _play_step;
+ src += step;
+ }
+ return dst;
+}
+
+char *Alsa_pcmi::play_float (const float *src, char *dst, int nfrm, int step)
+{
+ while (nfrm--)
+ {
+ *((float *) dst) = *src;
+ dst += _play_step;
+ src += step;
+ }
+ return dst;
+}
+
+
+const char *Alsa_pcmi::capt_16 (const char *src, float *dst, int nfrm, int step)
+{
+ while (nfrm--)
+ {
+ const short int s = *((short int const *) src);
+ const float d = (float) s / (float) 0x7fff;
+ *dst = d;
+ dst += step;
+ src += _capt_step;
+ }
+ return src;
+}
+
+const char *Alsa_pcmi::capt_16swap (const char *src, float *dst, int nfrm, int step)
+{
+ float d;
+ short int s;
+
+ while (nfrm--)
+ {
+ s = (src [0] & 0xFF) << 8;
+ s += (src [1] & 0xFF);
+ d = (float) s / (float) 0x7fff;
+ *dst = d;
+ dst += step;
+ src += _capt_step;
+ }
+ return src;
+}
+
+const char *Alsa_pcmi::capt_24 (const char *src, float *dst, int nfrm, int step)
+{
+ float d;
+ int s;
+
+ while (nfrm--)
+ {
+ s = (src [0] & 0xFF);
+ s += (src [1] & 0xFF) << 8;
+ s += (src [2] & 0xFF) << 16;
+ if (s & 0x00800000) s-= 0x01000000;
+ d = (float) s / (float) 0x007fffff;
+ *dst = d;
+ dst += step;
+ src += _capt_step;
+ }
+ return src;
+}
+
+const char *Alsa_pcmi::capt_24swap (const char *src, float *dst, int nfrm, int step)
+{
+ float d;
+ int s;
+
+ while (nfrm--)
+ {
+ s = (src [0] & 0xFF) << 16;
+ s += (src [1] & 0xFF) << 8;
+ s += (src [2] & 0xFF);
+ if (s & 0x00800000) s-= 0x01000000;
+ d = (float) s / (float) 0x007fffff;
+ *dst = d;
+ dst += step;
+ src += _capt_step;
+ }
+ return src;
+}
+
+const char *Alsa_pcmi::capt_32 (const char *src, float *dst, int nfrm, int step)
+{
+ while (nfrm--)
+ {
+ const int s = *((int const *) src);
+ const float d = (float) s / (float) 0x7fffff00;
+ *dst = d;
+ dst += step;
+ src += _capt_step;
+ }
+ return src;
+}
+
+const char *Alsa_pcmi::capt_32swap (const char *src, float *dst, int nfrm, int step)
+{
+ float d;
+ int s;
+
+ while (nfrm--)
+ {
+ s = (src [0] & 0xFF) << 24;
+ s += (src [1] & 0xFF) << 16;
+ s += (src [2] & 0xFF) << 8;
+ d = (float) s / (float) 0x7fffff00;
+ *dst = d;
+ dst += step;
+ src += _capt_step;
+ }
+ return src;
+}
+
+const char *Alsa_pcmi::capt_float (const char *src, float *dst, int nfrm, int step)
+{
+ while (nfrm--)
+ {
+ *dst = *((float const *) src);
+ dst += step;
+ src += _capt_step;
+ }
+ return src;
+}
--- /dev/null
+// ----------------------------------------------------------------------------
+//
+// Copyright (C) 2006-2012 Fons Adriaensen <fons@linuxaudio.org>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+// ----------------------------------------------------------------------------
+
+
+#ifndef _ZITA_ALSA_PCMI_H_
+#define _ZITA_ALSA_PCMI_H_
+
+
+#define ALSA_PCM_NEW_HW_PARAMS_API
+#define ALSA_PCM_NEW_SW_PARAMS_API
+#include <alsa/asoundlib.h>
+
+
+#define ZITA_ALSA_PCMI_MAJOR_VERSION 0
+#define ZITA_ALSA_PCMI_MINOR_VERSION 2
+
+#include <stdint.h>
+
+extern int zita_alsa_pcmi_major_version (void);
+extern int zita_alsa_pcmi_minor_version (void);
+
+
+class Alsa_pcmi
+{
+public:
+
+ Alsa_pcmi (
+ const char *play_name,
+ const char *capt_name,
+ const char *ctrl_name,
+ unsigned int rate,
+ unsigned int frsize,
+ unsigned int nfrags,
+ unsigned int debug = 0);
+
+ ~Alsa_pcmi (void);
+
+ enum
+ {
+ DEBUG_INIT = 1,
+ DEBUG_STAT = 2,
+ DEBUG_WAIT = 4,
+ DEBUG_DATA = 8,
+ DEBUG_ALL = 15,
+ FORCE_16B = 256,
+ FORCE_2CH = 512
+ };
+
+ void printinfo (void);
+
+ int pcm_start (void);
+ int pcm_stop (void);
+ snd_pcm_sframes_t pcm_wait (void);
+ int pcm_idle (int len);
+
+ int play_init (snd_pcm_uframes_t len);
+ void clear_chan (int chan, int len);
+ void play_chan (int chan, const float *src, int len, int step = 1);
+ int play_done (int len);
+
+ int capt_init (snd_pcm_uframes_t len);
+ void capt_chan (int chan, float *dst, int len, int step = 1);
+ int capt_done (int len);
+
+ int play_avail (void)
+ {
+ return snd_pcm_avail (_play_handle);
+ }
+
+ int capt_avail (void)
+ {
+ return snd_pcm_avail (_capt_handle);
+ }
+
+ int play_delay (void)
+ {
+ long k;
+ snd_pcm_delay (_play_handle, &k);
+ return k;
+ }
+
+ int capt_delay (void)
+ {
+ long k;
+ snd_pcm_delay (_capt_handle, &k);
+ return k;
+ }
+
+ float play_xrun (void) const { return _play_xrun; }
+ float capt_xrun (void) const { return _capt_xrun; }
+
+ int state (void) const { return _state; }
+ size_t fsize (void) const { return _fsize; }
+ uint32_t fsamp (void) const { return _fsamp; }
+ uint32_t nfrag (void) const { return _nfrag; }
+ uint32_t nplay (void) const { return _play_nchan; }
+ uint32_t ncapt (void) const { return _capt_nchan; }
+ snd_pcm_t *play_handle (void) const { return _play_handle; }
+ snd_pcm_t *capt_handle (void) const { return _capt_handle; }
+
+
+private:
+
+ typedef char *(Alsa_pcmi::*clear_function)(char *, int);
+ typedef char *(Alsa_pcmi::*play_function)(const float *, char *, int, int);
+ typedef const char *(Alsa_pcmi::*capt_function) (const char *, float *, int, int);
+
+ enum { MAXPFD = 16, MAXCHAN = 64 };
+
+ void initialise (const char *play_name, const char *capt_name, const char *ctrl_name);
+ int set_hwpar (snd_pcm_t *handle, snd_pcm_hw_params_t *hwpar, const char *sname, unsigned int *nchan);
+ int set_swpar (snd_pcm_t *handle, snd_pcm_sw_params_t *swpar, const char *sname);
+ int recover (void);
+ float xruncheck (snd_pcm_status_t *stat);
+
+ char *clear_32 (char *dst, int nfrm);
+ char *clear_24 (char *dst, int nfrm);
+ char *clear_16 (char *dst, int nfrm);
+
+ char *play_float (const float *src, char *dst, int nfrm, int step);
+ char *play_32 (const float *src, char *dst, int nfrm, int step);
+ char *play_24 (const float *src, char *dst, int nfrm, int step);
+ char *play_16 (const float *src, char *dst, int nfrm, int step);
+ char *play_32swap (const float *src, char *dst, int nfrm, int step);
+ char *play_24swap (const float *src, char *dst, int nfrm, int step);
+ char *play_16swap (const float *src, char *dst, int nfrm, int step);
+
+ const char *capt_float (const char *src, float *dst, int nfrm, int step);
+ const char *capt_32 (const char *src, float *dst, int nfrm, int step);
+ const char *capt_24 (const char *src, float *dst, int nfrm, int step);
+ const char *capt_16 (const char *src, float *dst, int nfrm, int step);
+ const char *capt_32swap (const char *src, float *dst, int nfrm, int step);
+ const char *capt_24swap (const char *src, float *dst, int nfrm, int step);
+ const char *capt_16swap (const char *src, float *dst, int nfrm, int step);
+
+ unsigned int _fsamp;
+ snd_pcm_uframes_t _fsize;
+ unsigned int _nfrag;
+ unsigned int _debug;
+ int _state;
+ snd_pcm_t *_play_handle;
+ snd_pcm_t *_capt_handle;
+ snd_ctl_t *_ctrl_handle;
+ snd_pcm_hw_params_t *_play_hwpar;
+ snd_pcm_sw_params_t *_play_swpar;
+ snd_pcm_hw_params_t *_capt_hwpar;
+ snd_pcm_sw_params_t *_capt_swpar;
+ snd_pcm_format_t _play_format;
+ snd_pcm_format_t _capt_format;
+ snd_pcm_access_t _play_access;
+ snd_pcm_access_t _capt_access;
+ unsigned int _play_nchan;
+ unsigned int _capt_nchan;
+ float _play_xrun;
+ float _capt_xrun;
+ bool _synced;
+ int _play_npfd;
+ int _capt_npfd;
+ struct pollfd _poll_fd [MAXPFD];
+ snd_pcm_uframes_t _capt_offs;
+ snd_pcm_uframes_t _play_offs;
+ int _play_step;
+ int _capt_step;
+ char *_play_ptr [MAXCHAN];
+ const char *_capt_ptr [MAXCHAN];
+ clear_function _clear_func;
+ play_function _play_func;
+ capt_function _capt_func;
+ void *_dummy [16];
+};
+
+#endif
#include <sys/time.h>
#include <regex.h>
+#include <glibmm.h>
+
#include "dummy_audiobackend.h"
+
#include "pbd/error.h"
+#include "ardour/port_manager.h"
#include "i18n.h"
using namespace ARDOUR;
static std::string s_instance_name;
size_t DummyAudioBackend::_max_buffer_size = 8192;
+std::vector<std::string> DummyAudioBackend::_midi_options;
+std::vector<AudioBackend::DeviceStatus> DummyAudioBackend::_device_status;
DummyAudioBackend::DummyAudioBackend (AudioEngine& e, AudioBackendInfo& info)
: AudioBackend (e, info)
, _dsp_load (0)
, _n_inputs (0)
, _n_outputs (0)
+ , _n_midi_inputs (0)
+ , _n_midi_outputs (0)
, _systemic_input_latency (0)
, _systemic_output_latency (0)
, _processed_samples (0)
+ , _port_change_flag (false)
{
_instance_name = s_instance_name;
+ pthread_mutex_init (&_port_callback_mutex, 0);
}
DummyAudioBackend::~DummyAudioBackend ()
{
+ pthread_mutex_destroy (&_port_callback_mutex);
}
/* AUDIOBACKEND API */
std::vector<AudioBackend::DeviceStatus>
DummyAudioBackend::enumerate_devices () const
{
- std::vector<AudioBackend::DeviceStatus> s;
- s.push_back (DeviceStatus (_("Dummy"), true));
- return s;
+ if (_device_status.empty()) {
+ _device_status.push_back (DeviceStatus (_("Dummy"), true));
+ }
+ return _device_status;
}
std::vector<float>
return _systemic_output_latency;
}
+
/* MIDI */
std::vector<std::string>
DummyAudioBackend::enumerate_midi_options () const
{
- std::vector<std::string> m;
- m.push_back (_("None"));
- return m;
+ if (_midi_options.empty()) {
+ _midi_options.push_back (_("1 in, 1 out"));
+ _midi_options.push_back (_("2 in, 2 out"));
+ _midi_options.push_back (_("8 in, 8 out"));
+ }
+ return _midi_options;
}
int
-DummyAudioBackend::set_midi_option (const std::string&)
+DummyAudioBackend::set_midi_option (const std::string& opt)
{
- return -1;
+ if (opt == _("1 in, 1 out")) {
+ _n_midi_inputs = _n_midi_outputs = 1;
+ }
+ else if (opt == _("2 in, 2 out")) {
+ _n_midi_inputs = _n_midi_outputs = 2;
+ }
+ else if (opt == _("8 in, 8 out")) {
+ _n_midi_inputs = _n_midi_outputs = 8;
+ }
+ else {
+ _n_midi_inputs = _n_midi_outputs = 0;
+ }
+ return 0;
}
std::string
DummyAudioBackend::midi_option () const
{
- return "";
+ return ""; // TODO
}
/* State Control */
return -1;
}
+ engine.sample_rate_change (_samplerate);
+ engine.buffer_size_change (_samples_per_period);
+
if (engine.reestablish_ports ()) {
PBD::error << _("DummyAudioBackend: Could not re-establish ports.") << endmsg;
stop ();
}
engine.reconnect_ports ();
+ _port_change_flag = false;
if (pthread_create (&_main_thread, NULL, pthread_process, this)) {
PBD::error << _("DummyAudioBackend: cannot start.") << endmsg;
}
int timeout = 5000;
- while (!_running && --timeout > 0) { usleep (1000); }
+ while (!_running && --timeout > 0) { Glib::usleep (1000); }
if (timeout == 0 || !_running) {
PBD::error << _("DummyAudioBackend: failed to start process thread.") << endmsg;
{
void *status;
if (!_running) {
- return -1;
+ return 0;
}
_running = false;
{
switch (t) {
case DataType::AUDIO:
- return _max_buffer_size * sizeof(Sample);
+ return _samples_per_period * sizeof(Sample);
case DataType::MIDI:
return _max_buffer_size; // XXX not really limited
}
if (pthread_create (&thread_id, &attr, dummy_process_thread, td)) {
PBD::error << _("AudioEngine: cannot create process thread.") << endmsg;
+ pthread_attr_destroy (&attr);
return -1;
}
+ pthread_attr_destroy (&attr);
_threads.push_back (thread_id);
return 0;
{
for (std::vector<pthread_t>::const_iterator i = _threads.begin (); i != _threads.end (); ++i)
{
-#ifdef COMPILER_MINGW
- if (*i == GetCurrentThread ()) {
- return true;
- }
-#else // pthreads
if (pthread_equal (*i, pthread_self ()) != 0) {
return true;
}
-#endif
}
return false;
}
void
DummyAudioBackend::update_latencies ()
{
+ // trigger latency callback in RT thread (locked graph)
+ port_connect_add_remove_callback();
}
/* PORTENGINE API */
DummyPort* port = NULL;
switch (type) {
case DataType::AUDIO:
- port = new DummyAudioPort (name, flags);
+ port = new DummyAudioPort (*this, name, flags);
break;
case DataType::MIDI:
- port = new DummyMidiPort (name, flags);
+ port = new DummyMidiPort (*this, name, flags);
break;
default:
PBD::error << _("DummyBackend::register_port: Invalid Data Type.") << endmsg;
const int a_ins = _n_inputs > 0 ? _n_inputs : 8;
const int a_out = _n_outputs > 0 ? _n_outputs : 8;
- const int m_ins = 2; // TODO
- const int m_out = 2;
+ const int m_ins = _n_midi_inputs > 0 ? _n_midi_inputs : 2;
+ const int m_out = _n_midi_outputs > 0 ? _n_midi_outputs : 2;
/* audio ports */
lr.min = lr.max = _samples_per_period + _systemic_input_latency;
snprintf(tmp, sizeof(tmp), "system:playback_%d", i);
PortHandle p = add_port(std::string(tmp), DataType::AUDIO, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
if (!p) return -1;
- set_latency_range (p, false, lr);
+ set_latency_range (p, true, lr);
}
/* midi ports */
snprintf(tmp, sizeof(tmp), "system:midi_playback_%d", i);
PortHandle p = add_port(std::string(tmp), DataType::MIDI, static_cast<PortFlags>(IsInput | IsPhysical | IsTerminal));
if (!p) return -1;
- set_latency_range (p, false, lr);
+ set_latency_range (p, true, lr);
}
-
return 0;
}
uint32_t
DummyAudioBackend::get_midi_event_count (void* port_buffer)
{
- assert (port_buffer && _running);
+ assert (port_buffer);
return static_cast<DummyMidiBuffer*>(port_buffer)->size ();
}
void
DummyAudioBackend::midi_clear (void* port_buffer)
{
- assert (port_buffer && _running);
+ assert (port_buffer);
DummyMidiBuffer * buf = static_cast<DummyMidiBuffer*>(port_buffer);
assert (buf);
buf->clear ();
{
for (size_t i = 0; i < _ports.size (); ++i) {
DummyPort* port = _ports[i];
- if ((port->type () == type) && port->is_output () && port->is_physical ()) {
+ if ((port->type () == type) && port->is_input () && port->is_physical ()) {
port_names.push_back (port->name ());
}
}
{
for (size_t i = 0; i < _ports.size (); ++i) {
DummyPort* port = _ports[i];
- if ((port->type () == type) && port->is_input () && port->is_physical ()) {
+ if ((port->type () == type) && port->is_output () && port->is_physical ()) {
port_names.push_back (port->name ());
}
}
void*
DummyAudioBackend::get_buffer (PortEngine::PortHandle port, pframes_t nframes)
{
- assert (port && _running);
+ assert (port);
assert (valid_port (port));
return static_cast<DummyPort*>(port)->get_buffer (nframes);
}
_running = true;
_processed_samples = 0;
- struct timeval clock1, clock2;
- ::gettimeofday (&clock1, NULL);
+ manager.registration_callback();
+ manager.graph_order_callback();
+
+ uint64_t clock1, clock2;
+ clock1 = g_get_monotonic_time();
while (_running) {
if (engine.process_callback (_samples_per_period)) {
return 0;
}
_processed_samples += _samples_per_period;
if (!_freewheeling) {
- ::gettimeofday (&clock2, NULL);
- const int elapsed_time = (clock2.tv_sec - clock1.tv_sec) * 1000000 + (clock2.tv_usec - clock1.tv_usec);
- const int nomial_time = 1000000 * _samples_per_period / _samplerate;
+ clock2 = g_get_monotonic_time();
+ const int64_t elapsed_time = clock2 - clock1;
+ const int64_t nomial_time = 1e6 * _samples_per_period / _samplerate;
_dsp_load = elapsed_time / (float) nomial_time;
if (elapsed_time < nomial_time) {
- ::usleep (nomial_time - elapsed_time);
+ Glib::usleep (nomial_time - elapsed_time);
} else {
- ::usleep (100); // don't hog cpu
+ Glib::usleep (100); // don't hog cpu
}
} else {
_dsp_load = 1.0;
- ::usleep (100); // don't hog cpu
+ Glib::usleep (100); // don't hog cpu
+ }
+ clock1 = g_get_monotonic_time();
+
+ bool connections_changed = false;
+ bool ports_changed = false;
+ if (!pthread_mutex_trylock (&_port_callback_mutex)) {
+ if (_port_change_flag) {
+ ports_changed = true;
+ _port_change_flag = false;
+ }
+ if (!_port_connection_queue.empty ()) {
+ connections_changed = true;
+ }
+ while (!_port_connection_queue.empty ()) {
+ PortConnectData *c = _port_connection_queue.back ();
+ manager.connect_callback (c->a, c->b, c->c);
+ _port_connection_queue.pop_back ();
+ delete c;
+ }
+ pthread_mutex_unlock (&_port_callback_mutex);
+ }
+ if (ports_changed) {
+ manager.registration_callback();
+ }
+ if (connections_changed) {
+ manager.graph_order_callback();
+ }
+ if (connections_changed || ports_changed) {
+ engine.latency_callback(false);
+ engine.latency_callback(true);
}
- ::gettimeofday (&clock1, NULL);
+
}
_running = false;
return 0;
/******************************************************************************/
-DummyPort::DummyPort (const std::string& name, PortFlags flags)
- : _name (name)
+DummyPort::DummyPort (DummyAudioBackend &b, const std::string& name, PortFlags flags)
+ : _dummy_backend (b)
+ , _name (name)
, _flags (flags)
{
_capture_latency_range.min = 0;
_capture_latency_range.max = 0;
_playback_latency_range.min = 0;
_playback_latency_range.max = 0;
+ _dummy_backend.port_connect_add_remove_callback();
}
DummyPort::~DummyPort () {
disconnect_all ();
+ _dummy_backend.port_connect_add_remove_callback();
}
_connections.push_back (port);
if (callback) {
port->_connect (this, false);
+ _dummy_backend.port_connect_callback (name(), port->name(), true);
}
}
if (callback) {
port->_disconnect (this, false);
+ _dummy_backend.port_connect_callback (name(), port->name(), false);
}
}
{
while (!_connections.empty ()) {
_connections.back ()->_disconnect (this, false);
+ _dummy_backend.port_connect_callback (name(), _connections.back ()->name(), false);
_connections.pop_back ();
}
}
/******************************************************************************/
-DummyAudioPort::DummyAudioPort (const std::string& name, PortFlags flags)
- : DummyPort (name, flags)
+DummyAudioPort::DummyAudioPort (DummyAudioBackend &b, const std::string& name, PortFlags flags)
+ : DummyPort (b, name, flags)
{
memset (_buffer, 0, sizeof (_buffer));
}
}
-DummyMidiPort::DummyMidiPort (const std::string& name, PortFlags flags)
- : DummyPort (name, flags)
+DummyMidiPort::DummyMidiPort (DummyAudioBackend &b, const std::string& name, PortFlags flags)
+ : DummyPort (b, name, flags)
{
_buffer.clear ();
}
DummyMidiPort::~DummyMidiPort () { }
+struct MidiEventSorter {
+ bool operator() (const boost::shared_ptr<DummyMidiEvent>& a, const boost::shared_ptr<DummyMidiEvent>& b) {
+ return *a < *b;
+ }
+};
+
void* DummyMidiPort::get_buffer (pframes_t /* nframes */)
{
if (is_input ()) {
_buffer.push_back (boost::shared_ptr<DummyMidiEvent>(new DummyMidiEvent (**it)));
}
}
- std::sort (_buffer.begin (), _buffer.end ());
+ std::sort (_buffer.begin (), _buffer.end (), MidiEventSorter());
} else if (is_output () && is_physical () && is_terminal()) {
_buffer.clear ();
}
namespace ARDOUR {
+class DummyAudioBackend;
+
class DummyMidiEvent {
public:
DummyMidiEvent (const pframes_t timestamp, const uint8_t* data, size_t size);
class DummyPort {
protected:
- DummyPort (const std::string&, PortFlags);
+ DummyPort (DummyAudioBackend &b, const std::string&, PortFlags);
public:
virtual ~DummyPort ();
}
private:
+ DummyAudioBackend &_dummy_backend;
std::string _name;
const PortFlags _flags;
LatencyRange _capture_latency_range;
class DummyAudioPort : public DummyPort {
public:
- DummyAudioPort (const std::string&, PortFlags);
+ DummyAudioPort (DummyAudioBackend &b, const std::string&, PortFlags);
~DummyAudioPort ();
DataType type () const { return DataType::AUDIO; };
class DummyMidiPort : public DummyPort {
public:
- DummyMidiPort (const std::string&, PortFlags);
+ DummyMidiPort (DummyAudioBackend &b, const std::string&, PortFlags);
~DummyMidiPort ();
DataType type () const { return DataType::MIDI; };
}; // class DummyMidiPort
class DummyAudioBackend : public AudioBackend {
+ friend class DummyPort;
public:
DummyAudioBackend (AudioEngine& e, AudioBackendInfo& info);
~DummyAudioBackend ();
int set_output_channels (uint32_t);
int set_systemic_input_latency (uint32_t);
int set_systemic_output_latency (uint32_t);
+ int set_systemic_midi_input_latency (std::string const, uint32_t) { return 0; }
+ int set_systemic_midi_output_latency (std::string const, uint32_t) { return 0; }
/* Retrieving parameters */
std::string device_name () const;
uint32_t output_channels () const;
uint32_t systemic_input_latency () const;
uint32_t systemic_output_latency () const;
+ uint32_t systemic_midi_input_latency (std::string const) const { return 0; }
+ uint32_t systemic_midi_output_latency (std::string const) const { return 0; }
/* External control app */
std::string control_app_name () const { return std::string (); }
int set_midi_option (const std::string&);
std::string midi_option () const;
+ std::vector<DeviceStatus> enumerate_midi_devices () const {
+ return std::vector<AudioBackend::DeviceStatus> ();
+ }
+ int set_midi_device_enabled (std::string const, bool) {
+ return 0;
+ }
+ bool midi_device_enabled (std::string const) const {
+ return true;
+ }
+ bool can_set_systemic_midi_latencies () const {
+ return false;
+ }
+
/* State Control */
protected:
int _start (bool for_latency_measurement);
private:
std::string _instance_name;
+ static std::vector<std::string> _midi_options;
+ static std::vector<AudioBackend::DeviceStatus> _device_status;
+
bool _running;
bool _freewheeling;
uint32_t _n_inputs;
uint32_t _n_outputs;
+ uint32_t _n_midi_inputs;
+ uint32_t _n_midi_outputs;
+
uint32_t _systemic_input_latency;
uint32_t _systemic_output_latency;
std::vector<DummyPort *> _ports;
+
+ struct PortConnectData {
+ std::string a;
+ std::string b;
+ bool c;
+
+ PortConnectData (const std::string& a, const std::string& b, bool c)
+ : a (a) , b (b) , c (c) {}
+ };
+
+ std::vector<PortConnectData *> _port_connection_queue;
+ pthread_mutex_t _port_callback_mutex;
+ bool _port_change_flag;
+
+ void port_connect_callback (const std::string& a, const std::string& b, bool conn) {
+ pthread_mutex_lock (&_port_callback_mutex);
+ _port_connection_queue.push_back(new PortConnectData(a, b, conn));
+ pthread_mutex_unlock (&_port_callback_mutex);
+ }
+
+ void port_connect_add_remove_callback () {
+ pthread_mutex_lock (&_port_callback_mutex);
+ _port_change_flag = true;
+ pthread_mutex_unlock (&_port_callback_mutex);
+ }
+
bool valid_port (PortHandle port) const {
return std::find (_ports.begin (), _ports.end (), (DummyPort*)port) != _ports.end ();
}
+
DummyPort * find_port (const std::string& port_name) const {
for (std::vector<DummyPort*>::const_iterator it = _ports.begin (); it != _ports.end (); ++it) {
if ((*it)->name () == port_name) {
obj.name = 'dummy_audiobackend'
obj.target = 'dummy_audiobackend'
obj.use = 'libardour libpbd'
- obj.vnum = DUMMYBACKEND_VERSION
+ if (bld.env['build_target'] != 'mingw'):
+ obj.vnum = DUMMYBACKEND_VERSION
obj.install_path = os.path.join(bld.env['LIBDIR'], 'backends')
obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
'ARDOURBACKEND_DLL_EXPORTS'
int
JACKAudioBackend::stop ()
{
+ _running = false; // no 'engine halted message'.
GET_PRIVATE_JACK_POINTER_RET (_priv_jack, -1);
_jack_connection->close ();
int set_output_channels (uint32_t);
int set_systemic_input_latency (uint32_t);
int set_systemic_output_latency (uint32_t);
+ int set_systemic_midi_input_latency (std::string const, uint32_t) { return 0; }
+ int set_systemic_midi_output_latency (std::string const, uint32_t) { return 0; }
std::string device_name () const;
float sample_rate () const;
uint32_t output_channels () const;
uint32_t systemic_input_latency () const;
uint32_t systemic_output_latency () const;
+ uint32_t systemic_midi_input_latency (std::string const) const { return 0; }
+ uint32_t systemic_midi_output_latency (std::string const) const { return 0; }
std::string driver_name() const;
std::string control_app_name () const;
int set_midi_option (const std::string&);
std::string midi_option () const;
+ std::vector<DeviceStatus> enumerate_midi_devices () const {
+ return std::vector<AudioBackend::DeviceStatus> ();
+ }
+ int set_midi_device_enabled (std::string const, bool) {
+ return 0;
+ }
+ bool midi_device_enabled (std::string const) const {
+ return true;
+ }
+ bool can_set_systemic_midi_latencies () const {
+ return false;
+ }
+
int midi_event_get (pframes_t& timestamp, size_t& size, uint8_t** buf, void* port_buffer, uint32_t event_index);
int midi_event_put (void* port_buffer, pframes_t timestamp, const uint8_t* buffer, size_t size);
uint32_t get_midi_event_count (void* port_buffer);
*/
#ifdef HAVE_ALSA
-#include <alsa/asoundlib.h>
+#include "ardouralsautil/devicelist.h"
#endif
#ifdef __APPLE__
#include <mach-o/dyld.h>
#endif
+#ifdef PLATFORM_WINDOWS
+#include <shobjidl.h> // Needed for
+#include <shlguid.h> // 'IShellLink'
+#endif
+
#ifdef HAVE_PORTAUDIO
#include <portaudio.h>
#endif
void
ARDOUR::get_jack_audio_driver_names (vector<string>& audio_driver_names)
{
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
audio_driver_names.push_back (portaudio_driver_name);
#elif __APPLE__
audio_driver_names.push_back (coreaudio_driver_name);
ARDOUR::get_jack_alsa_device_names (device_map_t& devices)
{
#ifdef HAVE_ALSA
- 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;
-
- while (snd_card_next (&cardnum) >= 0 && cardnum >= 0) {
-
- devname = "hw:";
- devname += PBD::to_string (cardnum, std::dec);
-
- if (snd_ctl_open (&handle, devname.c_str(), 0) >= 0 && snd_ctl_card_info (handle, info) >= 0) {
-
- if (snd_ctl_card_info (handle, info) < 0) {
- continue;
- }
-
- string card_name = snd_ctl_card_info_get_name (info);
-
- /* change devname to use ID, not number */
-
- devname = "hw:";
- devname += snd_ctl_card_info_get_id (info);
-
- while (snd_ctl_pcm_next_device (handle, &device) >= 0 && device >= 0) {
-
- /* only detect duplex devices here. more
- * complex arrangements are beyond our scope
- */
-
- 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) {
-
- 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) {
- devname += ',';
- devname += PBD::to_string (device, std::dec);
- devices.insert (std::make_pair (card_name, devname));
- }
- }
- }
-
- snd_ctl_close(handle);
- }
- }
+ get_alsa_audio_device_names(devices);
#else
/* silence a compiler unused variable warning */
(void) devices;
bool
ARDOUR::get_jack_server_application_names (std::vector<std::string>& server_names)
{
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
server_names.push_back ("jackd.exe");
#else
server_names.push_back ("jackd");
Searchpath sp(string(g_getenv("PATH")));
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
+// N.B. The #define (immediately below) can be safely removed once we know that this code builds okay with mingw
+#ifdef COMPILER_MSVC
+ IShellLinkA *pISL = NULL;
+ IPersistFile *ppf = NULL;
+
+ // Mixbus creates a Windows shortcut giving the location of its
+ // own (bundled) version of Jack. Let's see if that shortcut exists
+ if (SUCCEEDED (CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void**)&pISL)))
+ {
+ if (SUCCEEDED (pISL->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf)))
+ {
+ char target_path[MAX_PATH];
+ char shortcut_pathA[MAX_PATH];
+ WCHAR shortcut_pathW[MAX_PATH];
+
+ // Our Windows installer should have created a shortcut to the Jack
+ // server so let's start by finding out what drive it got installed on
+ if (char *env_path = getenv ("windir"))
+ {
+ strcpy (shortcut_pathA, env_path);
+ shortcut_pathA[2] = '\0'; // Gives us just the drive letter and colon
+ }
+ else // Assume 'C:'
+ strcpy (shortcut_pathA, "C:");
+
+ strcat (shortcut_pathA, "\\Program Files (x86)\\Jack\\Start Jack.lnk");
+
+ MultiByteToWideChar (CP_ACP, MB_PRECOMPOSED, shortcut_pathA, -1, shortcut_pathW, MAX_PATH);
+
+ // If it did, load the shortcut into our persistent file
+ if (SUCCEEDED (ppf->Load(shortcut_pathW, 0)))
+ {
+ // Read the target information from the shortcut object
+ if (S_OK == (pISL->GetPath (target_path, MAX_PATH, NULL, SLGP_UNCPRIORITY)))
+ {
+ char *p = strrchr (target_path, '\\');
+
+ if (p)
+ {
+ *p = NULL;
+ sp.push_back (target_path);
+ }
+ }
+ }
+ }
+ }
+
+ if (ppf)
+ ppf->Release();
+
+ if (pISL)
+ pISL->Release();
+#endif
+
gchar *install_dir = g_win32_get_package_installation_directory_of_module (NULL);
if (install_dir) {
sp.push_back (install_dir);
vector<std::string>& server_paths)
{
for (vector<string>::const_iterator i = server_names.begin(); i != server_names.end(); ++i) {
- Glib::PatternSpec ps (*i);
- find_matching_files_in_directories (server_dir_paths, ps, server_paths);
+ find_files_matching_pattern (server_paths, server_dir_paths, *i);
}
return !server_paths.empty();
}
args.push_back (options.server_path);
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
// must use sync mode on windows
args.push_back ("-S");
args.push_back ("-v");
}
-#ifndef WIN32
if (options.temporary) {
args.push_back ("-T");
}
-#endif
if (options.driver == alsa_driver_name) {
if (options.midi_driver == alsa_seq_midi_driver_name) {
obj.uselib = [ 'JACK', 'PORTAUDIO' ]
else:
obj.uselib = [ 'JACK' ]
- obj.use = 'libardour libpbd'
- obj.vnum = JACKBACKEND_VERSION
+ obj.vnum = JACKBACKEND_VERSION
+ obj.use = 'libardour libpbd ardouralsautil'
obj.install_path = os.path.join(bld.env['LIBDIR'], 'backends')
obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
'ARDOURBACKEND_DLL_EXPORTS'
virtual int set_systemic_output_latency (uint32_t);
+ int set_systemic_midi_input_latency (std::string const, uint32_t) { return 0; }
+
+ int set_systemic_midi_output_latency (std::string const, uint32_t) { return 0; }
+
virtual std::string device_name () const;
virtual float sample_rate () const;
virtual uint32_t systemic_output_latency () const;
+ uint32_t systemic_midi_input_latency (std::string const) const { return 0; }
+
+ uint32_t systemic_midi_output_latency (std::string const) const { return 0; }
+
virtual std::string control_app_name () const;
virtual void launch_control_app ();
virtual std::string midi_option () const;
+ std::vector<DeviceStatus> enumerate_midi_devices () const {
+ return std::vector<AudioBackend::DeviceStatus> ();
+ }
+ int set_midi_device_enabled (std::string const, bool) {
+ return 0;
+ }
+ bool midi_device_enabled (std::string const) const {
+ return true;
+ }
+ bool can_set_systemic_midi_latencies () const {
+ return false;
+ }
+
virtual int _start (bool for_latency_measurement);
virtual int stop ();
pDevInfo->m_AvailableSampleRates = availableSampleRates;
//Get max input channels
- uint32 maxInputChannels;
+ uint32_t maxInputChannels;
wErr = getDeviceMaxInputChannels(pDevInfo->m_DeviceId, maxInputChannels);
if (wErr != eNoErr)
pDevInfo->m_MaxInputChannels = maxInputChannels;
//Get max output channels
- uint32 maxOutputChannels;
+ uint32_t maxOutputChannels;
wErr = getDeviceMaxOutputChannels(pDevInfo->m_DeviceId, maxOutputChannels);
if (wErr != eNoErr)
namespace wvNS {
UMicroseconds& UMicroseconds::ReadTime()
{
+ // Note: g_get_monotonic_time() may be a viable alternative
+ // (it is on Linux and OSX); if not, this code should really go into libpbd
#ifdef PLATFORM_WINDOWS
LARGE_INTEGER Frequency, Count ;
QueryPerformanceFrequency(&Frequency) ;
QueryPerformanceCounter(&Count);
theTime = uint64_t((Count.QuadPart * 1000000.0 / Frequency.QuadPart));
-#endif
-#if defined(__linux__) || defined(__APPLE__)
-// Mac code replaced by posix calls, to reduce Carbon dependency.
- timeval buf;
+#elif defined __MACH__ // OSX, BSD..
+
+ clock_serv_t cclock;
+ mach_timespec_t mts;
+ host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
+ clock_get_time(cclock, &mts);
+ mach_port_deallocate(mach_task_self(), cclock);
+ theTime = (uint64_t)mts.tv_sec * 1e6 + (uint64_t)mts.tv_nsec / 1000;
+
+#else // Linux, POSIX
- gettimeofday(&buf,NULL);
+ struct timespec *ts
+ clock_gettime(CLOCK_MONOTONIC, ts);
+ theTime = (uint64_t)ts.tv_sec * 1e6 + (uint64_t)buf.tv_nsec / 1000;
- // micro sec
- theTime = uint64_t(buf.tv_sec) * 1000*1000 + buf.tv_usec;
#endif
return *this;
autowaf.check_pkg(conf, 'portaudio-2.0', uselib_store='PORTAUDIO',
atleast_version='19')
autowaf.configure(conf)
-
+
def build(bld):
-
+
if bld.env['build_target'] == 'mingw':
- obj = bld(features = 'cxx cxxshlib')
+ obj = bld(features = 'cxx cxxshlib')
else:
- obj = bld(features = 'c cxx cxxshlib')
+ obj = bld(features = 'c cxx cxxshlib')
+
+ if sys.platform == 'darwin':
+ if bld.env['build_target'] not in [ 'lion' ]:
+ obj.framework = 'CoreMidi'
+ else:
+ obj.framework = 'CoreMIDI'
- if bld.env['build_target'] == 'mountain_lion':
- obj.framework = 'CoreMidi'
-
obj.source = [
'waves_audiobackend.cc',
'waves_audiobackend.latency.cc',
'wavesapi/devicemanager/WCMRAudioDeviceManager.cpp',
'wavesapi/devicemanager/WCMRNativeAudio.cpp',
'wavesapi/threads/WCThreadSafe.cpp',
- 'portmidi/src/pm_common/pmutil.c',
- 'portmidi/src/pm_common/portmidi.c'
+ 'portmidi/src/pm_common/pmutil.c',
+ 'portmidi/src/pm_common/portmidi.c'
]
-
+
if bld.env['build_target'] == 'mingw':
- platform_dependent = [
- 'wavesapi/miscutils/UMicroseconds.cpp',
- 'wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp',
- 'portmidi/src/pm_win/pmwin.c',
- 'portmidi/src/pm_win/pmwinmm.c',
- 'portmidi/src/porttime/ptwinmm.c'
- ]
+ platform_dependent = [
+ 'wavesapi/miscutils/UMicroseconds.cpp',
+ 'wavesapi/devicemanager/WCMRPortAudioDeviceManager.cpp',
+ 'portmidi/src/pm_win/pmwin.c',
+ 'portmidi/src/pm_win/pmwinmm.c',
+ 'portmidi/src/porttime/ptwinmm.c'
+ ]
else:
- platform_dependent = [
- 'wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp',
- 'portmidi/src/pm_mac/pmmac.c',
- 'portmidi/src/pm_mac/pmmacosxcm.c',
- 'portmidi/src/pm_mac/finddefault.c',
- 'portmidi/src/pm_mac/readbinaryplist.c',
- 'portmidi/src/porttime/ptmacosx_mach.c'
- ]
-
+ platform_dependent = [
+ 'wavesapi/devicemanager/WCMRCoreAudioDeviceManager.cpp',
+ 'portmidi/src/pm_mac/pmmac.c',
+ 'portmidi/src/pm_mac/pmmacosxcm.c',
+ 'portmidi/src/pm_mac/finddefault.c',
+ 'portmidi/src/pm_mac/readbinaryplist.c',
+ 'portmidi/src/porttime/ptmacosx_mach.c'
+ ]
+
obj.source.extend(platform_dependent)
-
+
obj.includes = ['.',
- 'wavesapi',
- 'wavesapi/refmanager',
- 'wavesapi/wavespublicapi',
- 'wavesapi/devicemanager',
- 'wavesapi/miscutils',
- 'wavesapi/threads',
- 'portmidi',
- 'portmidi/src/pm_common'
- ]
-
+ 'wavesapi',
+ 'wavesapi/refmanager',
+ 'wavesapi/wavespublicapi',
+ 'wavesapi/devicemanager',
+ 'wavesapi/miscutils',
+ 'wavesapi/threads',
+ 'portmidi',
+ 'portmidi/src/pm_common'
+ ]
+
obj.cxxflags = [ '-fPIC' ]
obj.cflags = [ '-fPIC', '-fms-extensions' ]
obj.name = 'waves_audiobackend'
obj.target = 'waves_audiobackend'
obj.use = 'libardour libpbd'
if bld.env['build_target'] == 'mingw':
- obj.uselib = ['PORTAUDIO']
+ obj.uselib = ['PORTAUDIO']
obj.vnum = WAVESAUDIOBACKEND_VERSION
obj.install_path = os.path.join(bld.env['LIBDIR'], 'backends')
-
+
if bld.env['build_target']== 'mingw':
- obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
- 'ARDOURBACKEND_DLL_EXPORTS'
- ]
+ obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
+ 'ARDOURBACKEND_DLL_EXPORTS'
+ ]
else:
- obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
- 'ARDOURBACKEND_DLL_EXPORTS'
- ]
+ obj.defines = ['PACKAGE="' + I18N_PACKAGE + '"',
+ 'ARDOURBACKEND_DLL_EXPORTS'
+ ]
#!/usr/bin/env python
from waflib.extras import autowaf as autowaf
+from waflib import Options
import os
import sys
+import re
# Mandatory variables
top = '.'
out = 'build'
-backends = [ 'jack', 'dummy' ]
-
-if sys.platform == 'darwin':
- backends += ['wavesaudio' ]
-
def options(opt):
autowaf.set_options(opt)
autowaf.set_recursive()
autowaf.configure(conf)
+ backends = [ 'jack' ]
+
+ if sys.platform == 'darwin' or sys.platform == 'mingw' or sys.platform == 'msvc':
+ backends += [ 'wavesaudio' ]
+
+ if Options.options.build_dummy:
+ backends += [ 'dummy' ]
+
+ if Options.options.build_alsabackend:
+ if re.search ("linux", sys.platform) != None:
+ backends += [ 'alsa' ]
+
for i in backends:
sub_config_and_use(conf, i)
def build(bld):
+ backends = [ 'jack' ]
+
+ if sys.platform == 'darwin' or sys.platform == 'mingw' or sys.platform == 'msvc':
+ backends += [ 'wavesaudio' ]
+
+ if bld.env['BUILD_DUMMYBACKEND']:
+ backends += [ 'dummy' ]
+
+ if re.search ("linux", sys.platform) != None:
+ if bld.env['BUILD_ALSABACKEND'] and bld.is_defined('HAVE_ALSA'):
+ backends += [ 'alsa' ]
+
for i in backends:
bld.recurse(i)
>
</File>
<File
- RelativePath="..\curve.cc"
+ RelativePath="..\container.cc"
>
</File>
<File
- RelativePath="..\debug.cc"
+ RelativePath="..\curve.cc"
>
</File>
<File
- RelativePath="..\drag_handle.cc"
+ RelativePath="..\debug.cc"
>
</File>
<File
RelativePath="..\flag.cc"
>
</File>
- <File
- RelativePath="..\group.cc"
- >
- </File>
<File
RelativePath="..\image.cc"
>
RelativePath="..\root_group.cc"
>
</File>
+ <File
+ RelativePath="..\ruler.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\scroll_group.cc"
+ >
+ </File>
<File
RelativePath="..\stateful_image.cc"
>
RelativePath="..\text.cc"
>
</File>
+ <File
+ RelativePath="..\tracking_text.cc"
+ >
+ </File>
<File
RelativePath="..\types.cc"
>
RelativePath="..\wave_view.cc"
>
</File>
+ <File
+ RelativePath="..\widget.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\xfade_curve.cc"
+ >
+ </File>
</Filter>
<Filter
Name="Header Files"
>
</File>
<File
- RelativePath="..\canvas\curve.h"
+ RelativePath="..\canvas\container.h"
>
</File>
<File
- RelativePath="..\canvas\debug.h"
+ RelativePath="..\canvas\curve.h"
>
</File>
<File
- RelativePath="..\canvas\drag_handle.h"
+ RelativePath="..\canvas\debug.h"
>
</File>
<File
>
</File>
<File
- RelativePath="..\canvas\group.h"
+ RelativePath="..\i18n.h"
>
</File>
<File
- RelativePath="..\i18n.h"
+ RelativePath="..\canvas\image.h"
>
</File>
<File
- RelativePath="..\canvas\image.h"
+ RelativePath="..\canvas\interpolated_curve.h"
>
</File>
<File
RelativePath="..\canvas\root_group.h"
>
</File>
+ <File
+ RelativePath="..\canvas\ruler.h"
+ >
+ </File>
+ <File
+ RelativePath="..\canvas\scroll_group.h"
+ >
+ </File>
<File
RelativePath="..\canvas\stateful_image.h"
>
RelativePath="..\canvas\text.h"
>
</File>
+ <File
+ RelativePath="..\canvas\tracking_text.h"
+ >
+ </File>
<File
RelativePath="..\canvas\types.h"
>
RelativePath="..\canvas\wave_view.h"
>
</File>
+ <File
+ RelativePath="..\canvas\widget.h"
+ >
+ </File>
+ <File
+ RelativePath="..\canvas\xfade_curve.h"
+ >
+ </File>
</Filter>
</Files>
<Globals>
using namespace std;
using namespace ArdourCanvas;
-Arc::Arc (Group* parent)
- : Item (parent)
- , Outline (parent)
- , Fill (parent)
+Arc::Arc (Canvas* c)
+ : Item (c)
, _radius (0.0)
, _arc_degrees (0.0)
, _start_degrees (0.0)
{
+}
+Arc::Arc (Item* parent)
+ : Item (parent)
+ , _radius (0.0)
+ , _arc_degrees (0.0)
+ , _start_degrees (0.0)
+{
}
void
bool
Arc::covers (Duple const & point) const
{
- Duple p = canvas_to_item (point);
+ Duple p = window_to_item (point);
double angle_degs = atan (p.y/p.x) * 2.0 * M_PI;
double radius = sqrt (p.x * p.x + p.y * p.y);
* @brief Implementation of the Arrow canvas object.
*/
+#include "pbd/compose.h"
+
#include "canvas/arrow.h"
+#include "canvas/debug.h"
#include "canvas/polygon.h"
#include "canvas/line.h"
/** Construct an Arrow.
* @param parent Parent canvas group.
*/
-Arrow::Arrow (Group* parent)
- : Group (parent)
+Arrow::Arrow (Canvas* c)
+ : Container (c)
+{
+ setup ();
+}
+
+Arrow::Arrow (Item* parent)
+ : Container (parent)
{
- assert (parent);
+ setup ();
+}
+void
+Arrow::setup ()
+{
/* set up default arrow heads at each end */
for (int i = 0; i < 2; ++i) {
_heads[i].polygon = new Polygon (this);
- _heads[i].show = true;
_heads[i].outward = true;
_heads[i].width = 4;
_heads[i].height = 4;
setup_polygon (i);
+ CANVAS_DEBUG_NAME (_heads[i].polygon, string_compose ("arrow head %1", i));
}
_line = new Line (this);
+ CANVAS_DEBUG_NAME (_line, "arrow line");
}
+
/** Set whether to show an arrow head at one end or other
* of the line.
* @param which 0 or 1 to specify the arrow head to set up.
begin_change ();
- _heads[which].show = show;
+ if (!show) {
+ delete _heads[which].polygon;
+ _heads[which].polygon = 0;
+ } else {
+ setup_polygon (which);
+ }
- setup_polygon (which);
_bounding_box_dirty = true;
end_change ();
}
Arrow::set_outline_width (Distance width)
{
_line->set_outline_width (width);
- _heads[0].polygon->set_outline_width (width);
- _heads[1].polygon->set_outline_width (width);
+ if (_heads[0].polygon) {
+ _heads[0].polygon->set_outline_width (width);
+ }
+ if (_heads[1].polygon) {
+ _heads[1].polygon->set_outline_width (width);
+ }
}
/** Set the x position of our line.
_line->set_x0 (x);
_line->set_x1 (x);
for (int i = 0; i < 2; ++i) {
- _heads[i].polygon->set_x_position (x - _heads[i].width / 2);
+ if (_heads[i].polygon) {
+ _heads[i].polygon->set_x_position (x - _heads[i].width / 2);
+ }
}
}
Arrow::set_y0 (Coord y0)
{
_line->set_y0 (y0);
- _heads[0].polygon->set_y_position (y0);
+ if (_heads[0].polygon) {
+ _heads[0].polygon->set_y_position (y0);
+ }
}
/** Set the y position of end 1 of our line.
Arrow::set_y1 (Coord y1)
{
_line->set_y1 (y1);
- _heads[1].polygon->set_y_position (y1 - _heads[1].height);
+ if (_heads[1].polygon) {
+ _heads[1].polygon->set_y_position (y1 - _heads[1].height);
+ }
}
/** @return x position of our line in pixels (in our coordinate system) */
{
_line->set_outline_color (color);
for (int i = 0; i < 2; ++i) {
- _heads[i].polygon->set_outline_color (color);
- _heads[i].polygon->set_fill_color (color);
+ if (_heads[i].polygon) {
+ _heads[i].polygon->set_outline_color (color);
+ _heads[i].polygon->set_fill_color (color);
+ }
}
}
#include "canvas/canvas.h"
#include "canvas/debug.h"
#include "canvas/line.h"
+#include "canvas/scroll_group.h"
using namespace std;
using namespace ArdourCanvas;
/** Construct a new Canvas */
Canvas::Canvas ()
: _root (this)
- , _scroll_offset_x (0)
- , _scroll_offset_y (0)
{
set_epoch ();
}
void
Canvas::scroll_to (Coord x, Coord y)
{
- _scroll_offset_x = x;
- _scroll_offset_y = y;
+ /* We do things this way because we do not want to recurse through
+ the canvas for every scroll. In the presence of large MIDI
+ tracks this means traversing item lists that include
+ thousands of items (notes).
+
+ This design limits us to moving only those items (groups, typically)
+ that should move in certain ways as we scroll. In other terms, it
+ becomes O(1) rather than O(N).
+ */
+
+ for (list<ScrollGroup*>::iterator i = scrollers.begin(); i != scrollers.end(); ++i) {
+ (*i)->scroll_to (Duple (x, y));
+ }
pick_current_item (0); // no current mouse position
}
+void
+Canvas::add_scroller (ScrollGroup& i)
+{
+ scrollers.push_back (&i);
+}
+
void
Canvas::zoomed ()
{
}
/** Render an area of the canvas.
- * @param area Area in canvas coordinates.
+ * @param area Area in window coordinates.
* @param context Cairo context to render to.
*/
void
_root.render (*draw, context);
- // This outlines the rect being rendered, after it has been drawn.
- // context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
- // context->set_source_rgba (1.0, 0, 0, 1.0);
- // context->stroke ();
-
+#ifdef CANVAS_DEBUG
+ if (getenv ("CANVAS_HARLEQUIN_DEBUGGING")) {
+ // This transparently colors the rect being rendered, after it has been drawn.
+ double r = (random() % 65536) /65536.0;
+ double g = (random() % 65536) /65536.0;
+ double b = (random() % 65536) /65536.0;
+ context->rectangle (draw->x0, draw->y0, draw->x1 - draw->x0, draw->y1 - draw->y0);
+ context->set_source_rgba (r, g, b, 0.25);
+ context->fill ();
+ }
+#endif
}
}
{
boost::optional<Rect> bbox = item->bounding_box ();
if (bbox) {
- queue_draw_item_area (item, bbox.get ());
+ if (item->item_to_window (*bbox).intersection (visible_area ())) {
+ queue_draw_item_area (item, bbox.get ());
+ }
}
}
{
boost::optional<Rect> bbox = item->bounding_box ();
if (bbox) {
- queue_draw_item_area (item, bbox.get ());
+ if (item->item_to_window (*bbox).intersection (visible_area ())) {
+ queue_draw_item_area (item, bbox.get ());
+ }
}
}
void
Canvas::item_changed (Item* item, boost::optional<Rect> pre_change_bounding_box)
{
+
+ Rect window_bbox = visible_area ();
+
if (pre_change_bounding_box) {
- /* request a redraw of the item's old bounding box */
- queue_draw_item_area (item, pre_change_bounding_box.get ());
+
+ if (item->item_to_window (*pre_change_bounding_box).intersection (window_bbox)) {
+ /* request a redraw of the item's old bounding box */
+ queue_draw_item_area (item, pre_change_bounding_box.get ());
+ }
}
boost::optional<Rect> post_change_bounding_box = item->bounding_box ();
if (post_change_bounding_box) {
- /* request a redraw of the item's new bounding box */
- queue_draw_item_area (item, post_change_bounding_box.get ());
+
+ if (item->item_to_window (*post_change_bounding_box).intersection (window_bbox)) {
+ /* request a redraw of the item's new bounding box */
+ queue_draw_item_area (item, post_change_bounding_box.get ());
+ }
}
}
Duple
Canvas::window_to_canvas (Duple const & d) const
{
- return d.translate (Duple (_scroll_offset_x, _scroll_offset_y));
-}
+ /* Find the scroll group that covers d (a window coordinate). Scroll groups are only allowed
+ * as children of the root group, so we just scan its first level
+ * children and see what we can find.
+ */
-Duple
-Canvas::canvas_to_window (Duple const & d) const
-{
- Duple wd = d.translate (Duple (-_scroll_offset_x, -_scroll_offset_y));
+ std::list<Item*> const& root_children (_root.items());
+ ScrollGroup* sg = 0;
- /* Note that this intentionally always returns integer coordinates */
+ /* if the coordinates are negative, clamp to zero and find the item
+ * that covers that "edge" position.
+ */
- wd.x = round (wd.x);
- wd.y = round (wd.y);
+ Duple in_window (d);
- return wd;
-}
+ if (in_window.x < 0) {
+ in_window.x = 0;
+ }
+ if (in_window.y < 0) {
+ in_window.y = 0;
+ }
-Rect
-Canvas::window_to_canvas (Rect const & r) const
-{
- return r.translate (Duple (_scroll_offset_x, _scroll_offset_y));
+ for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
+ if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_window (in_window)) {
+ break;
+ }
+ }
+
+ if (sg) {
+ return d.translate (sg->scroll_offset());
+ }
+
+ return d;
}
-Rect
-Canvas::canvas_to_window (Rect const & r) const
+Duple
+Canvas::canvas_to_window (Duple const & d, bool rounded) const
{
- Rect wr = r.translate (Duple (-_scroll_offset_x, -_scroll_offset_y));
+ /* Find the scroll group that covers d (a canvas coordinate). Scroll groups are only allowed
+ * as children of the root group, so we just scan its first level
+ * children and see what we can find.
+ */
+
+ std::list<Item*> const& root_children (_root.items());
+ ScrollGroup* sg = 0;
+ Duple wd;
- /* Note that this intentionally always returns integer coordinates */
+ for (std::list<Item*>::const_iterator i = root_children.begin(); i != root_children.end(); ++i) {
+ if (((sg = dynamic_cast<ScrollGroup*>(*i)) != 0) && sg->covers_canvas (d)) {
+ break;
+ }
+ }
+
- wr.x0 = round (wr.x0);
- wr.x1 = round (wr.x1);
- wr.y0 = round (wr.y0);
- wr.y1 = round (wr.y1);
+ if (sg) {
+ wd = d.translate (-sg->scroll_offset());
+ } else {
+ wd = d;
+ }
- return wr;
-}
+ /* Note that this intentionally almost always returns integer coordinates */
+
+ if (rounded) {
+ wd.x = round (wd.x);
+ wd.y = round (wd.y);
+ }
+
+ return wd;
+}
/** Called when an item has moved.
* @param item Item that has moved.
void
Canvas::queue_draw_item_area (Item* item, Rect area)
{
- ArdourCanvas::Rect canvas_area = item->item_to_canvas (area);
- // cerr << "CANVAS " << this << " for " << item << ' ' << item->whatami() << ' ' << item->name << " invalidate " << area << " TRANSLATE AS " << canvas_area << " window = " << canvas_to_window (canvas_area) << std::endl;
- request_redraw (canvas_area);
+ request_redraw (item->item_to_window (area));
}
/** Construct a GtkCanvas */
{
/* these are the events we want to know about */
add_events (Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK | Gdk::POINTER_MOTION_MASK |
- Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
+ Gdk::SCROLL_MASK | Gdk::ENTER_NOTIFY_MASK | Gdk::LEAVE_NOTIFY_MASK);
}
void
return;
}
- pick_current_item (window_to_canvas (Duple (x, y)), state);
+ pick_current_item (Duple (x, y), state);
}
-
+
+/** Given @param point (a position in window coordinates)
+ * and mouse state @param state, check to see if _current_item
+ * (which will be used to deliver events) should change.
+ */
void
GtkCanvas::pick_current_item (Duple const & point, int state)
{
return;
}
- /* find the items at the given position */
+ /* find the items at the given window position */
vector<Item const *> items;
_root.add_items_at_point (point, items);
if (DEBUG_ENABLED(PBD::DEBUG::CanvasEnterLeave)) {
for (vector<Item const*>::const_iterator it = items.begin(); it != items.end(); ++it) {
#ifdef CANVAS_DEBUG
- std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << std::endl;
+ std::cerr << "\tItem " << (*it)->whatami() << '/' << (*it)->name << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
#else
- std::cerr << "\tItem " << (*it)->whatami() << std::endl;
+ std::cerr << "\tItem " << (*it)->whatami() << '/' << " ignore events ? " << (*it)->ignore_events() << " vis ? " << (*it)->visible() << std::endl;
#endif
}
}
for (i = items.begin(); i != items.end(); ++i) {
- Item const * new_item = *i;
+ Item const * possible_item = *i;
- /* We ignore invisible items, groups and items that ignore events */
+ /* We ignore invisible items, containers and items that ignore events */
- if (!new_item->visible() || new_item->ignore_events() || dynamic_cast<Group const *>(new_item) != 0) {
+ if (!possible_item->visible() || possible_item->ignore_events() || dynamic_cast<ArdourCanvas::Container const *>(possible_item) != 0) {
continue;
}
-
- within_items.push_front (new_item);
+ within_items.push_front (possible_item);
}
+ DEBUG_TRACE (PBD::DEBUG::CanvasEnterLeave, string_compose ("after filtering insensitive + containers, we have %1 items\n", within_items.size()));
+
if (within_items.empty()) {
/* no items at point, just send leave event below */
}
}
+/** Deliver a series of enter & leave events based on the pointer position being at window
+ * coordinate @param point, and pointer @param state (modifier keys, etc)
+ */
void
GtkCanvas::deliver_enter_leave (Duple const & point, int state)
{
enter_event.mode = GDK_CROSSING_NORMAL;
enter_event.focus = FALSE;
enter_event.state = state;
- enter_event.x = point.x;
- enter_event.y = point.y;
+
+ /* Events delivered to canvas items are expected to be in canvas
+ * coordinates but @param point is in window coordinates.
+ */
+
+ Duple c = window_to_canvas (point);
+ enter_event.x = c.x;
+ enter_event.y = c.y;
GdkEventCrossing leave_event = enter_event;
leave_event.type = GDK_LEAVE_NOTIFY;
* heirarchy between current and new_current.
*/
-
for (i = _current_item->parent(); i && i != _new_current_item; i = i->parent()) {
items_to_leave_virtual.push_back (i);
}
enter_detail = GDK_NOTIFY_INFERIOR;
leave_detail = GDK_NOTIFY_ANCESTOR;
-
} else if (_new_current_item->is_descendant_of (*_current_item)) {
/* move from ancestor to descendant (X: "_new_current_item is
* an inferior ("child") of _current_item")
_focused_item = 0;
}
+ ScrollGroup* sg = dynamic_cast<ScrollGroup*>(item);
+ if (sg) {
+ scrollers.remove (sg);
+ }
+
if (_current_item == item) {
/* no need to send a leave event to this item, since it is going away
*/
return w->create_cairo_context ();
}
+/** Handler for GDK scroll events.
+ * @param ev Event.
+ * @return true if the event was handled.
+ */
+bool
+GtkCanvas::on_scroll_event (GdkEventScroll* ev)
+{
+ /* translate event coordinates from window to canvas */
+
+ GdkEvent copy = *((GdkEvent*)ev);
+ Duple winpos = Duple (ev->x, ev->y);
+ Duple where = window_to_canvas (winpos);
+
+ pick_current_item (winpos, ev->state);
+
+ copy.button.x = where.x;
+ copy.button.y = where.y;
+
+ /* Coordinates in the event will be canvas coordinates, correctly adjusted
+ for scroll if this GtkCanvas is in a GtkCanvasViewport.
+ */
+
+ DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas scroll @ %1, %2 => %3\n", ev->x, ev->y, where));
+ return deliver_event (reinterpret_cast<GdkEvent*>(©));
+}
+
/** Handler for GDK button press events.
* @param ev Event.
* @return true if the event was handled.
/* translate event coordinates from window to canvas */
GdkEvent copy = *((GdkEvent*)ev);
- Duple where = window_to_canvas (Duple (ev->x, ev->y));
+ Duple winpos = Duple (ev->x, ev->y);
+ Duple where = window_to_canvas (winpos);
+
+ pick_current_item (winpos, ev->state);
copy.button.x = where.x;
copy.button.y = where.y;
for scroll if this GtkCanvas is in a GtkCanvasViewport.
*/
- pick_current_item (where, ev->state);
DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button press @ %1, %2 => %3\n", ev->x, ev->y, where));
return deliver_event (reinterpret_cast<GdkEvent*>(©));
}
/* translate event coordinates from window to canvas */
GdkEvent copy = *((GdkEvent*)ev);
- Duple where = window_to_canvas (Duple (ev->x, ev->y));
+ Duple winpos = Duple (ev->x, ev->y);
+ Duple where = window_to_canvas (winpos);
- pick_current_item (where, ev->state);
+ pick_current_item (winpos, ev->state);
copy.button.x = where.x;
copy.button.y = where.y;
for scroll if this GtkCanvas is in a GtkCanvasViewport.
*/
- pick_current_item (where, ev->state);
DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas button release @ %1, %2 => %3\n", ev->x, ev->y, where));
return deliver_event (reinterpret_cast<GdkEvent*>(©));
}
+bool
+GtkCanvas::get_mouse_position (Duple& winpos) const
+{
+ int x;
+ int y;
+ Gdk::ModifierType mask;
+ Glib::RefPtr<Gdk::Window> self = Glib::RefPtr<Gdk::Window>::cast_const (get_window ());
+
+ if (!self) {
+ std::cerr << " no self window\n";
+ winpos = Duple (0, 0);
+ return false;
+ }
+
+ Glib::RefPtr<Gdk::Window> win = self->get_pointer (x, y, mask);
+
+ winpos.x = x;
+ winpos.y = y;
+
+ return true;
+}
+
/** Handler for GDK motion events.
* @param ev Event.
* @return true if the event was handled.
/* Coordinates in "copy" will be canvas coordinates,
*/
- // DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas motion @ %1, %2\n", ev->x, ev->y));
+ DEBUG_TRACE (PBD::DEBUG::CanvasEvents, string_compose ("canvas motion @ %1, %2 canvas @ %3, %4\n", ev->x, ev->y, copy.motion.x, copy.motion.y));
+
+ MouseMotion (point); /* EMIT SIGNAL */
- pick_current_item (where, ev->state);
+ pick_current_item (point, ev->state);
/* Now deliver the motion event. It may seem a little inefficient
to recompute the items under the event, but the enter notify/leave
bool
GtkCanvas::on_enter_notify_event (GdkEventCrossing* ev)
{
- Duple where = window_to_canvas (Duple (ev->x, ev->y));
- pick_current_item (where, ev->state);
+ pick_current_item (Duple (ev->x, ev->y), ev->state);
return true;
}
GtkCanvas::on_leave_notify_event (GdkEventCrossing* ev)
{
_new_current_item = 0;
- Duple where = window_to_canvas (Duple (ev->x, ev->y));
- deliver_enter_leave (where, ev->state);
+ deliver_enter_leave (Duple (ev->x, ev->y), ev->state);
return true;
}
/** Called to request a redraw of our canvas.
- * @param area Area to redraw, in canvas coordinates.
+ * @param area Area to redraw, in window coordinates.
*/
void
GtkCanvas::request_redraw (Rect const & request)
{
- boost::optional<Rect> req = request.intersection (visible_area());
+ Rect real_area;
- if (req) {
- Rect r = req.get();
- Rect area = canvas_to_window (r);
- queue_draw_area (area.x0, area.y0, area.width(), area.height());
- }
+ Coord const w = width ();
+ Coord const h = height ();
+
+ /* clamp area requested to actual visible window */
+
+ real_area.x0 = max (0.0, min (w, request.x0));
+ real_area.x1 = max (0.0, min (w, request.x1));
+ real_area.y0 = max (0.0, min (h, request.y0));
+ real_area.y1 = max (0.0, min (h, request.y1));
+
+ queue_draw_area (real_area.x0, real_area.y0, real_area.width(), real_area.height());
}
/** Called to request that we try to get a particular size for ourselves.
}
}
-/** @return The visible area of the canvas, in canvas coordinates */
+/** @return The visible area of the canvas, in window coordinates */
Rect
GtkCanvas::visible_area () const
{
- Distance const xo = _scroll_offset_x;
- Distance const yo = _scroll_offset_y;
- return Rect (xo, yo, xo + get_allocation().get_width (), yo + get_allocation().get_height ());
+ return Rect (0, 0, get_allocation().get_width (), get_allocation().get_height ());
+}
+
+Coord
+GtkCanvas::width() const
+{
+ return get_allocation().get_width();
+}
+
+Coord
+GtkCanvas::height() const
+{
+ return get_allocation().get_height();
}
/** Create a GtkCanvaSViewport.
namespace ArdourCanvas {
-class LIBCANVAS_API Arc : virtual public Item, public Outline, public Fill
+class Canvas;
+
+class LIBCANVAS_API Arc : public Item
{
public:
- Arc (Group *);
+ Arc (Canvas*);
+ Arc (Item*);
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
void compute_bounding_box () const;
#define __CANVAS_ARROW_H__
#include "canvas/visibility.h"
-
-#include "canvas/group.h"
+#include "canvas/container.h"
namespace ArdourCanvas {
+class Canvas;
class Line;
class Polygon;
* to draw lines at any angle.
*/
-class LIBCANVAS_API Arrow : public Group
+class LIBCANVAS_API Arrow : public Container
{
public:
- Arrow (Group *);
+ Arrow (Canvas*);
+ Arrow (Item*);
void set_show_head (int, bool);
void set_head_outward (int, bool);
private:
void setup_polygon (int);
+ void setup ();
/** Representation of a single arrow head */
struct Head {
Polygon* polygon; ///< the polygon which represents its shape
- bool show; ///< true if this head should be visible
bool outward; ///< true if this head points out from the line
Distance height; ///< the height of the head
Distance width; ///< the maximum width of the head
namespace ArdourCanvas
{
+struct Rect;
-class Rect;
-class Group;
+class Item;
+class ScrollGroup;
/** The base class for our different types of canvas.
*
Canvas ();
virtual ~Canvas () {}
- /** called to request a redraw of an area of the canvas */
+ /** called to request a redraw of an area of the canvas in WINDOW coordinates */
virtual void request_redraw (Rect const &) = 0;
/** called to ask the canvas to request a particular size from its host */
virtual void request_size (Duple) = 0;
void render (Rect const &, Cairo::RefPtr<Cairo::Context> const &) const;
/** @return root group */
- Group* root () {
+ Item* root () {
return &_root;
}
virtual Cairo::RefPtr<Cairo::Context> context () = 0;
- Rect canvas_to_window (Rect const&) const;
- Rect window_to_canvas (Rect const&) const;
- Duple canvas_to_window (Duple const&) const;
+ Duple canvas_to_window (Duple const&, bool rounded = true) const;
Duple window_to_canvas (Duple const&) const;
void canvas_to_window (Coord cx, Coord cy, Coord& wx, Coord& wy) {
}
void scroll_to (Coord x, Coord y);
- virtual Rect visible_area () const = 0;
+ void add_scroller (ScrollGroup& i);
+
+ virtual Rect visible_area () const = 0;
+ virtual Coord width () const = 0;
+ virtual Coord height () const = 0;
+
+ /** Store the coordinates of the mouse pointer in window coordinates in
+ @param winpos. Return true if the position was within the window,
+ false otherwise.
+ */
+ virtual bool get_mouse_position (Duple& winpos) const = 0;
+
+ /** Signal to be used by items that need to track the mouse position
+ within the window.
+ */
+ sigc::signal<void,Duple const&> MouseMotion;
+
+ /** Ensures that the position given by @param winpos (in window
+ coordinates) is within the current window area, possibly reduced by
+ @param border.
+ */
+ Duple clamp_to_window (Duple const& winpos, Duple border = Duple());
void zoomed();
protected:
void queue_draw_item_area (Item *, Rect);
- /** our root group */
- RootGroup _root;
-
- Coord _scroll_offset_x;
- Coord _scroll_offset_y;
+ /** our root item */
+ Root _root;
virtual void pick_current_item (int state) = 0;
virtual void pick_current_item (Duple const &, int state) = 0;
+
+ std::list<ScrollGroup*> scrollers;
};
/** A canvas which renders onto a GTK EventBox */
Cairo::RefPtr<Cairo::Context> context ();
Rect visible_area () const;
+ Coord width() const;
+ Coord height() const;
+
+ bool get_mouse_position (Duple& winpos) const;
protected:
+ bool on_scroll_event (GdkEventScroll *);
bool on_expose_event (GdkEventExpose *);
bool on_button_press_event (GdkEventButton *);
bool on_button_release_event (GdkEventButton* event);
class LIBCANVAS_API Circle : public Arc
{
-public:
- Circle (Group *);
+ public:
+ Circle (Canvas*);
+ Circle (Item*);
};
}
--- /dev/null
+/*
+ Copyright (C) 2011-2014 Paul Davis
+ Original Author: Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the 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 __CANVAS_CONTAINER_H__
+#define __CANVAS_CONTAINER_H__
+
+#include "canvas/item.h"
+
+namespace ArdourCanvas
+{
+
+/** a Container is an item which has no content of its own
+ * but renders its children in some geometrical arrangement.
+ *
+ * Imagined examples of containers:
+ *
+ * Container: renders each child at the child's self-determined position
+ * Box: renders each child along an axis (vertical or horizontal)
+ * Table/Grid: renders each child within a two-dimensional grid
+ *
+ * Other?
+ */
+class LIBCANVAS_API Container : public Item
+{
+public:
+ Container (Canvas *);
+ Container (Item *);
+ Container (Item *, Duple const & position);
+
+ /** The compute_bounding_box() method is likely to be identical
+ * in all containers (the union of the children's bounding boxes).
+ * It can be overriden as necessary.
+ */
+ void compute_bounding_box () const;
+
+ /** The render() method is likely to be identical in all containers
+ * (just call Item::render_children()). It can be overridden as necessary.
+ */
+ void render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
+};
+
+}
+
+#endif
#include "canvas/visibility.h"
+#include "canvas/interpolated_curve.h"
#include "canvas/poly_item.h"
#include "canvas/fill.h"
namespace ArdourCanvas {
-class LIBCANVAS_API Curve : public PolyItem, public Fill
+class XFadeCurve;
+
+class LIBCANVAS_API Curve : public PolyItem, public InterpolatedCurve
{
-public:
- Curve (Group *);
+ public:
+ Curve (Canvas*);
+ Curve (Item*);
- enum SplineType {
- CatmullRomUniform,
- CatmullRomCentripetal,
+ enum CurveFill {
+ None,
+ Inside,
+ Outside,
};
-
+
void compute_bounding_box () const;
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
void set (Points const &);
void set_points_per_segment (uint32_t n);
bool covers (Duple const &) const;
+ void set_fill_mode (CurveFill cf) { curve_fill = cf; }
private:
Points samples;
Points::size_type n_samples;
uint32_t points_per_segment;
- SplineType curve_type;
+ InterpolatedCurve::SplineType curve_type;
+ CurveFill curve_fill;
- double map_value (double) const;
void interpolate ();
-
- static void interpolate (const Points& coordinates, uint32_t points_per_segment, SplineType, bool closed, Points& results);
};
-
+
}
#endif
+++ /dev/null
-/*
- Copyright (C) 2011-2014 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 __canvas_drag_handle_h__
-#define __canvas_drag_handle_h__
-
-#include "canvas/rectangle.h"
-#include "canvas/circle.h"
-
-namespace ArdourCanvas
-{
-
-class LIBCANVAS_API DragHandle : public Rectangle
-{
- public:
- DragHandle (Group *, Rect const &, bool left_side);
- void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
-
- protected:
- bool _left_side;
-};
-
-}
-
-
-#endif /* __canvas_drag_handle_h__ */
#include <vector>
#include <stdint.h>
+#include <boost/noncopyable.hpp>
+
#include "canvas/visibility.h"
-#include "canvas/item.h"
+#include "canvas/types.h"
namespace ArdourCanvas {
-class LIBCANVAS_API Fill : virtual public Item
+class Item;
+
+class LIBCANVAS_API Fill : public boost::noncopyable
{
public:
- Fill (Group *);
+ Fill (Item& self);
+ virtual ~Fill() {}
virtual void set_fill_color (Color);
virtual void set_fill (bool);
protected:
void setup_fill_context (Cairo::RefPtr<Cairo::Context>) const;
void setup_gradient_context (Cairo::RefPtr<Cairo::Context>, Rect const &, Duple const &) const;
-
+
+ Item& _self;
Color _fill_color;
bool _fill;
bool _transparent;
*/
#include "canvas/visibility.h"
-#include "canvas/group.h"
#include "canvas/types.h"
+#include "canvas/container.h"
namespace ArdourCanvas {
class Line;
class Rectangle;
-class LIBCANVAS_API Flag : public Group
+class LIBCANVAS_API Flag : public Container
{
public:
- Flag (Group *, Distance, Color, Color, Duple);
+ Flag (Canvas *, Distance, Color, Color, Duple);
+ Flag (Item*, Distance, Color, Color, Duple);
void set_text (std::string const &);
void set_height (Distance);
bool covers (Duple const &) const;
private:
+ void setup (Distance height, Duple position);
+
Color _outline_color;
Color _fill_color;
Text* _text;
namespace ArdourCanvas {
class WaveView;
class Line;
+ class LineSet;
class Rectangle;
+ class Ruler;
class Polygon;
class PolyLine;
class GtkCanvas;
class GtkCanvasViewport;
class Text;
class Curve;
+ class ScrollGroup;
}
#endif /* __canvas_canvas_fwd_h__ */
+++ /dev/null
-/*
- Copyright (C) 2011-2013 Paul Davis
- Author: Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the 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 __CANVAS_GROUP_H__
-#define __CANVAS_GROUP_H__
-
-#include <list>
-#include <vector>
-
-#include "canvas/visibility.h"
-#include "canvas/item.h"
-#include "canvas/types.h"
-#include "canvas/lookup_table.h"
-
-namespace ArdourCanvas {
-
-class LIBCANVAS_API Group : public Item
-{
-public:
- explicit Group (Group *);
- explicit Group (Group *, Duple);
- ~Group ();
-
- void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
- virtual void compute_bounding_box () const;
-
- void add (Item *);
- void remove (Item *);
- void clear (bool with_delete = false);
- std::list<Item*> const & items () const {
- return _items;
- }
-
- void raise_child_to_top (Item *);
- void raise_child (Item *, int);
- void lower_child_to_bottom (Item *);
- void child_changed ();
-
- void add_items_at_point (Duple, std::vector<Item const *> &) const;
-
- void dump (std::ostream&) const;
-
- static int default_items_per_cell;
-
-protected:
-
- explicit Group (Canvas *);
-
-private:
- friend class ::OptimizingLookupTableTest;
-
- Group (Group const &);
- void ensure_lut () const;
- void invalidate_lut () const;
- void clear_items (bool with_delete);
-
- /* our items, from lowest to highest in the stack */
- std::list<Item*> _items;
-
- mutable LookupTable* _lut;
-};
-
-}
-
-#endif
class LIBCANVAS_API Image : public Item
{
public:
- Image (Group *, Cairo::Format, int width, int height);
+ Image (Canvas *, Cairo::Format, int width, int height);
+ Image (Item*, Cairo::Format, int width, int height);
struct Data {
Data (uint8_t *d, int w, int h, int s, Cairo::Format fmt)
--- /dev/null
+/*
+ Copyright (C) 2013 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 __CANVAS_INTERPOLATED_CURVE_H__
+#define __CANVAS_INTERPOLATED_CURVE_H__
+
+#include "canvas/visibility.h"
+#include "canvas/types.h"
+
+namespace ArdourCanvas {
+
+class LIBCANVAS_API InterpolatedCurve
+{
+public:
+ enum SplineType {
+ CatmullRomUniform,
+ CatmullRomCentripetal,
+ };
+
+protected:
+
+ /**
+ * This method will calculate the Catmull-Rom interpolation curve, returning
+ * it as a list of Coord coordinate objects. This method in particular
+ * adds the first and last control points which are not visible, but required
+ * for calculating the spline.
+ *
+ * @param coordinates The list of original straight line points to calculate
+ * an interpolation from.
+ * @param points_per_segment The integer number of equally spaced points to
+ * return along each curve. The actual distance between each
+ * point will depend on the spacing between the control points.
+ * @return The list of interpolated coordinates.
+ * @param curve_type Chordal (stiff), Uniform(floppy), or Centripetal(medium)
+ * @throws gov.ca.water.shapelite.analysis.CatmullRomException if
+ * points_per_segment is less than 2.
+ */
+ static void
+ interpolate (const Points& coordinates, uint32_t points_per_segment, SplineType curve_type, bool closed, Points& results)
+ {
+ if (points_per_segment < 2) {
+ return;
+ }
+
+ // Cannot interpolate curves given only two points. Two points
+ // is best represented as a simple line segment.
+ if (coordinates.size() < 3) {
+ results = coordinates;
+ return;
+ }
+
+ // Copy the incoming coordinates. We need to modify it during interpolation
+ Points vertices = coordinates;
+
+ // Test whether the shape is open or closed by checking to see if
+ // the first point intersects with the last point. M and Z are ignored.
+ if (closed) {
+ // Use the second and second from last points as control points.
+ // get the second point.
+ Duple p2 = vertices[1];
+ // get the point before the last point
+ Duple pn1 = vertices[vertices.size() - 2];
+
+ // insert the second from the last point as the first point in the list
+ // because when the shape is closed it keeps wrapping around to
+ // the second point.
+ vertices.insert(vertices.begin(), pn1);
+ // add the second point to the end.
+ vertices.push_back(p2);
+ } else {
+ // The shape is open, so use control points that simply extend
+ // the first and last segments
+
+ // Get the change in x and y between the first and second coordinates.
+ double dx = vertices[1].x - vertices[0].x;
+ double dy = vertices[1].y - vertices[0].y;
+
+ // Then using the change, extrapolate backwards to find a control point.
+ double x1 = vertices[0].x - dx;
+ double y1 = vertices[0].y - dy;
+
+ // Actaully create the start point from the extrapolated values.
+ Duple start (x1, y1);
+
+ // Repeat for the end control point.
+ int n = vertices.size() - 1;
+ dx = vertices[n].x - vertices[n - 1].x;
+ dy = vertices[n].y - vertices[n - 1].y;
+ double xn = vertices[n].x + dx;
+ double yn = vertices[n].y + dy;
+ Duple end (xn, yn);
+
+ // insert the start control point at the start of the vertices list.
+ vertices.insert (vertices.begin(), start);
+
+ // append the end control ponit to the end of the vertices list.
+ vertices.push_back (end);
+ }
+
+ // When looping, remember that each cycle requires 4 points, starting
+ // with i and ending with i+3. So we don't loop through all the points.
+
+ for (Points::size_type i = 0; i < vertices.size() - 3; i++) {
+
+ // Actually calculate the Catmull-Rom curve for one segment.
+ Points r;
+
+ _interpolate (vertices, i, points_per_segment, curve_type, r);
+
+ // Since the middle points are added twice, once for each bordering
+ // segment, we only add the 0 index result point for the first
+ // segment. Otherwise we will have duplicate points.
+
+ if (results.size() > 0) {
+ r.erase (r.begin());
+ }
+
+ // Add the coordinates for the segment to the result list.
+
+ results.insert (results.end(), r.begin(), r.end());
+ }
+ }
+
+private:
+ /**
+ * Calculate the same values but introduces the ability to "parameterize" the t
+ * values used in the calculation. This is based on Figure 3 from
+ * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
+ *
+ * @param p An array of double values of length 4, where interpolation
+ * occurs from p1 to p2.
+ * @param time An array of time measures of length 4, corresponding to each
+ * p value.
+ * @param t the actual interpolation ratio from 0 to 1 representing the
+ * position between p1 and p2 to interpolate the value.
+ */
+ static double
+ __interpolate (double p[4], double time[4], double t)
+ {
+ const double L01 = p[0] * (time[1] - t) / (time[1] - time[0]) + p[1] * (t - time[0]) / (time[1] - time[0]);
+ const double L12 = p[1] * (time[2] - t) / (time[2] - time[1]) + p[2] * (t - time[1]) / (time[2] - time[1]);
+ const double L23 = p[2] * (time[3] - t) / (time[3] - time[2]) + p[3] * (t - time[2]) / (time[3] - time[2]);
+ const double L012 = L01 * (time[2] - t) / (time[2] - time[0]) + L12 * (t - time[0]) / (time[2] - time[0]);
+ const double L123 = L12 * (time[3] - t) / (time[3] - time[1]) + L23 * (t - time[1]) / (time[3] - time[1]);
+ const double C12 = L012 * (time[2] - t) / (time[2] - time[1]) + L123 * (t - time[1]) / (time[2] - time[1]);
+ return C12;
+ }
+
+ /**
+ * Given a list of control points, this will create a list of points_per_segment
+ * points spaced uniformly along the resulting Catmull-Rom curve.
+ *
+ * @param points The list of control points, leading and ending with a
+ * coordinate that is only used for controling the spline and is not visualized.
+ * @param index The index of control point p0, where p0, p1, p2, and p3 are
+ * used in order to create a curve between p1 and p2.
+ * @param points_per_segment The total number of uniformly spaced interpolated
+ * points to calculate for each segment. The larger this number, the
+ * smoother the resulting curve.
+ * @param curve_type Clarifies whether the curve should use uniform, chordal
+ * or centripetal curve types. Uniform can produce loops, chordal can
+ * produce large distortions from the original lines, and centripetal is an
+ * optimal balance without spaces.
+ * @return the list of coordinates that define the CatmullRom curve
+ * between the points defined by index+1 and index+2.
+ */
+ static void
+ _interpolate (const Points& points, Points::size_type index, int points_per_segment, SplineType curve_type, Points& results)
+ {
+ double x[4];
+ double y[4];
+ double time[4];
+
+ for (int i = 0; i < 4; i++) {
+ x[i] = points[index + i].x;
+ y[i] = points[index + i].y;
+ time[i] = i;
+ }
+
+ double tstart = 1;
+ double tend = 2;
+
+ if (curve_type != CatmullRomUniform) {
+ double total = 0;
+ for (int i = 1; i < 4; i++) {
+ double dx = x[i] - x[i - 1];
+ double dy = y[i] - y[i - 1];
+ if (curve_type == CatmullRomCentripetal) {
+ total += pow (dx * dx + dy * dy, .25);
+ } else {
+ total += pow (dx * dx + dy * dy, .5);
+ }
+ time[i] = total;
+ }
+ tstart = time[1];
+ tend = time[2];
+ }
+
+ int segments = points_per_segment - 1;
+ results.push_back (points[index + 1]);
+
+ for (int i = 1; i < segments; i++) {
+ double xi = __interpolate (x, time, tstart + (i * (tend - tstart)) / segments);
+ double yi = __interpolate (y, time, tstart + (i * (tend - tstart)) / segments);
+ results.push_back (Duple (xi, yi));
+ }
+
+ results.push_back (points[index + 2]);
+ }
+};
+
+}
+
+#endif
#include "canvas/visibility.h"
#include "canvas/types.h"
+#include "canvas/fill.h"
+#include "canvas/outline.h"
+#include "canvas/lookup_table.h"
namespace ArdourCanvas
{
+struct Rect;
class Canvas;
-class Group;
-class Rect;
+class ScrollGroup;
/** The parent class for anything that goes on the canvas.
*
* and all except the `root group' have a pointer to their parent group.
*/
-class LIBCANVAS_API Item
+class LIBCANVAS_API Item : public Fill, public Outline
{
public:
Item (Canvas *);
- Item (Group *);
- Item (Group *, Duple);
+ Item (Item *);
+ Item (Item *, Duple const& p);
virtual ~Item ();
void redraw () const;
*/
virtual void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const = 0;
- virtual void add_items_at_point (Duple, std::vector<Item const *>& items) const {
- items.push_back (this);
- }
+ /** Adds one or more items to the vector @param items based on their
+ * covering @param point which is in **window** coordinates
+ *
+ * Note that Item::add_items_at_window_point() is only intended to be
+ * called on items already looked up in a LookupTable (i.e. by a
+ * parent) and thus known to cover @param point already.
+ *
+ * Derived classes may add more items than themselves (e.g. containers).
+ */
+ virtual void add_items_at_point (Duple /*point*/, std::vector<Item const *>& items) const;
virtual bool covers (Duple const &) const;
void ungrab ();
void unparent ();
- void reparent (Group *);
+ void reparent (Item *);
/** @return Parent group, or 0 if this is the root group */
- Group* parent () const {
+ Item* parent () const {
return _parent;
}
return _position;
}
+ Duple window_origin() const;
+ Duple canvas_origin() const;
+
+ ScrollGroup* scroll_parent() const { return _scroll_parent; }
+
boost::optional<Rect> bounding_box () const;
Coord height() const;
Coord width() const;
Rect item_to_parent (Rect const &) const;
Duple parent_to_item (Duple const &) const;
Rect parent_to_item (Rect const &) const;
- /* XXX: it's a pity these aren't the same form as item_to_parent etc.,
+
+ /* XXX: it's a pity these two aren't the same form as item_to_parent etc.,
but it makes a bit of a mess in the rest of the code if they are not.
*/
-
void canvas_to_item (Coord &, Coord &) const;
- Duple canvas_to_item (Duple const &) const;
void item_to_canvas (Coord &, Coord &) const;
- Rect item_to_canvas (Rect const &) const;
- Rect canvas_to_item (Rect const &) const;
- Duple item_to_canvas (Duple const &) const;
- Duple item_to_window (Duple const&) const;
+ Duple canvas_to_item (Duple const&) const;
+ Rect item_to_canvas (Rect const&) const;
+ Duple item_to_canvas (Duple const&) const;
+ Rect canvas_to_item (Rect const&) const;
+
+ Duple item_to_window (Duple const&, bool rounded = true) const;
Duple window_to_item (Duple const&) const;
Rect item_to_window (Rect const&) const;
-
+ Rect window_to_item (Rect const&) const;
+
void raise_to_top ();
void raise (int);
void lower_to_bottom ();
void set_data (std::string const &, void *);
void* get_data (std::string const &) const;
+
+ /* nested item ("grouping") API */
+ void add (Item *);
+ void remove (Item *);
+ void clear (bool with_delete = false);
+ std::list<Item*> const & items () const {
+ return _items;
+ }
+ void raise_child_to_top (Item *);
+ void raise_child (Item *, int);
+ void lower_child_to_bottom (Item *);
+ void child_changed ();
+
+ static int default_items_per_cell;
+
/* This is a sigc++ signal because it is solely
concerned with GUI stuff and is thus single-threaded
std::string whatami() const;
protected:
+ friend class Fill;
+ friend class Outline;
/** To be called at the beginning of any property change that
* may alter the bounding box of this item
Canvas* _canvas;
/** parent group; may be 0 if we are the root group or if we have been unparent()ed */
- Group* _parent;
+ Item* _parent;
+ /** scroll parent group; may be 0 if we are the root group or if we have been unparent()ed */
+ ScrollGroup* _scroll_parent;
/** position of this item in parent coordinates */
Duple _position;
/** true if this item is visible (ie to be drawn), otherwise false */
/* XXX: this is a bit grubby */
std::map<std::string, void *> _data;
+ /* nesting ("grouping") API */
+
+ void invalidate_lut () const;
+ void clear_items (bool with_delete);
+
+ void ensure_lut () const;
+ mutable LookupTable* _lut;
+ /* our items, from lowest to highest in the stack */
+ std::list<Item*> _items;
+
+ void add_child_bounding_boxes() const;
+ void render_children (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
+
private:
void init ();
bool _ignore_events;
+
+ Duple scroll_offset() const;
+ Duple position_offset() const;
+
+ void find_scroll_parent ();
};
extern LIBCANVAS_API std::ostream& operator<< (std::ostream&, const ArdourCanvas::Item&);
namespace ArdourCanvas {
-class LIBCANVAS_API Line : virtual public Item, public Outline
+class LIBCANVAS_API Line : public Item
{
-public:
- Line (Group *);
+ public:
+ Line (Canvas*);
+ Line (Item*);
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
void compute_bounding_box () const;
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#ifndef __CANVAS_LINESET_H__
+#define __CANVAS_LINESET_H__
+
+#include <vector>
+
#include "canvas/visibility.h"
#include "canvas/item.h"
Horizontal
};
- LineSet (Group *);
+ LineSet (Canvas*, Orientation o = Vertical);
+ LineSet (Item*, Orientation o = Vertical);
void compute_bounding_box () const;
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
bool covers (Duple const &) const;
- void set_height (Distance);
+ void set_extent (Distance);
+ Distance extent() const { return _extent; }
void add (Coord, Distance, Color);
void clear ();
struct Line {
- Line (Coord y_, Distance width_, Color color_) : y (y_), width (width_), color (color_) {}
+ Line (Coord p, Distance width_, Color color_) : pos (p), width (width_), color (color_) {}
- Coord y;
+ Coord pos;
Distance width;
Color color;
};
private:
- std::list<Line> _lines;
- Distance _height;
+ std::vector<Line> _lines;
+ Distance _extent;
+ Orientation _orientation;
};
}
+
+#endif /* __CANVAS_LINESET_H__ */
namespace ArdourCanvas {
class Item;
-class Group;
class LIBCANVAS_API LookupTable
{
public:
- LookupTable (Group const &);
+ LookupTable (Item const &);
virtual ~LookupTable ();
virtual std::vector<Item*> get (Rect const &) = 0;
protected:
- Group const & _group;
+ Item const & _item;
};
class LIBCANVAS_API DumbLookupTable : public LookupTable
{
public:
- DumbLookupTable (Group const &);
+ DumbLookupTable (Item const &);
std::vector<Item*> get (Rect const &);
std::vector<Item*> items_at_point (Duple const &) const;
class LIBCANVAS_API OptimizingLookupTable : public LookupTable
{
public:
- OptimizingLookupTable (Group const &, int);
+ OptimizingLookupTable (Item const &, int);
~OptimizingLookupTable ();
std::vector<Item*> get (Rect const &);
std::vector<Item*> items_at_point (Duple const &) const;
#include <stdint.h>
+#include <boost/noncopyable.hpp>
+
#include "canvas/visibility.h"
#include "canvas/types.h"
-#include "canvas/item.h"
namespace ArdourCanvas {
-class LIBCANVAS_API Outline : virtual public Item
+class Item;
+
+class LIBCANVAS_API Outline : public boost::noncopyable
{
public:
- Outline (Group *);
- virtual ~Outline () {}
+ Outline (Item& self);
+ virtual ~Outline() {}
Color outline_color () const {
return _outline_color;
protected:
void setup_outline_context (Cairo::RefPtr<Cairo::Context>) const;
-
- Color _outline_color;
+
+ Item& _self;
+ Color _outline_color;
Distance _outline_width;
- bool _outline;
+ bool _outline;
};
}
class LIBCANVAS_API Pixbuf : public Item
{
public:
- Pixbuf (Group *);
+ Pixbuf (Canvas*);
+ Pixbuf (Item*);
void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
void compute_bounding_box () const;
namespace ArdourCanvas {
-class LIBCANVAS_API PolyItem : virtual public Item, public Outline
+class LIBCANVAS_API PolyItem : public Item
{
public:
- PolyItem (Group *);
+ PolyItem (Canvas*);
+ PolyItem (Item*);
void compute_bounding_box () const;
class LIBCANVAS_API PolyLine : public PolyItem
{
public:
- PolyLine (Group *);
+ PolyLine (Canvas*);
+ PolyLine (Item*);
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
namespace ArdourCanvas {
-class LIBCANVAS_API Polygon : public PolyItem, public Fill
+class LIBCANVAS_API Polygon : public PolyItem
{
public:
- Polygon (Group *);
+ Polygon (Canvas*);
+ Polygon (Item*);
virtual ~Polygon();
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
#include "canvas/visibility.h"
#include "canvas/item.h"
#include "canvas/types.h"
-#include "canvas/outline.h"
-#include "canvas/fill.h"
namespace ArdourCanvas
{
-class LIBCANVAS_API Rectangle : virtual public Item, public Outline, public Fill
+class LIBCANVAS_API Rectangle : public Item
{
public:
- Rectangle (Group *);
- Rectangle (Group *, Rect const &);
+ Rectangle (Canvas*);
+ Rectangle (Canvas*, Rect const &);
+ Rectangle (Item*);
+ Rectangle (Item*, Rect const &);
void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
void compute_bounding_box () const;
#define __CANVAS_ROOT_GROUP_H__
#include "canvas/visibility.h"
-#include "canvas/group.h"
+#include "canvas/container.h"
namespace ArdourCanvas {
-class LIBCANVAS_API RootGroup : public Group
+class LIBCANVAS_API Root : public Container
{
private:
friend class Canvas;
- RootGroup (Canvas *);
+ Root (Canvas *);
void compute_bounding_box () const;
- void child_changed ();
};
}
--- /dev/null
+/*
+ Copyright (C) 2014 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 __CANVAS_RULER_H__
+#define __CANVAS_RULER_H__
+
+#include <string>
+#include <vector>
+
+#include <pangomm/fontdescription.h>
+
+#include "canvas/rectangle.h"
+
+namespace ArdourCanvas
+{
+
+class LIBCANVAS_API Ruler : public Rectangle
+{
+public:
+ struct Mark {
+ enum Style {
+ Major,
+ Minor,
+ Micro
+ };
+ std::string label;
+ double position;
+ Style style;
+ };
+
+ struct Metric {
+ Metric () : units_per_pixel (0) {}
+ virtual ~Metric() {}
+
+ double units_per_pixel;
+
+ /* lower and upper and sample positions, which are also canvas coordinates
+ */
+
+ virtual void get_marks (std::vector<Mark>&, double lower, double upper, int maxchars) const = 0;
+ };
+
+ Ruler (Canvas*, const Metric& m);
+ Ruler (Canvas*, const Metric& m, Rect const&);
+ Ruler (Item*, const Metric& m);
+ Ruler (Item*, const Metric& m, Rect const&);
+
+ void set_range (double lower, double upper);
+ void set_font_description (Pango::FontDescription);
+
+ void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
+
+private:
+ const Metric& _metric;
+
+ /* lower and upper and sample positions, which are also canvas coordinates
+ */
+
+ Coord _lower;
+ Coord _upper;
+
+ Pango::FontDescription* _font_description;
+ mutable std::vector<Mark> marks;
+ mutable bool _need_marks;
+};
+
+}
+
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2014 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 __CANVAS_SCROLL_GROUP_H__
+#define __CANVAS_SCROLL_GROUP_H__
+
+#include "canvas/container.h"
+
+namespace ArdourCanvas {
+
+/** A ScrollGroup has no contents of its own, but renders
+ * its children in a way that reflects the most recent
+ * call to its scroll_to() method.
+ */
+class LIBCANVAS_API ScrollGroup : public Container
+{
+ public:
+ enum ScrollSensitivity {
+ ScrollsVertically = 0x1,
+ ScrollsHorizontally = 0x2
+ };
+
+ ScrollGroup (Canvas*, ScrollSensitivity);
+ ScrollGroup (Item*, ScrollSensitivity);
+
+ void scroll_to (Duple const& d);
+ Duple scroll_offset() const { return _scroll_offset; }
+
+ bool covers_canvas (Duple const& d) const;
+ bool covers_window (Duple const& d) const;
+
+ void render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const;
+
+ private:
+ ScrollSensitivity _scroll_sensitivity;
+ Duple _scroll_offset;
+};
+
+}
+
+#endif
public:
- StatefulImage (Group*, const XMLNode&);
+ StatefulImage (Canvas*, const XMLNode&);
+ StatefulImage (Item*, const XMLNode&);
~StatefulImage ();
bool set_state (States::size_type);
class LIBCANVAS_API Text : public Item
{
public:
- Text (Group *);
+ Text (Canvas*);
+ Text (Item*);
~Text();
void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
--- /dev/null
+/*
+ Copyright (C) 2011-2013 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_canvas_tracking_text_h__
+#define __ardour_canvas_tracking_text_h__
+
+#include <string>
+#include "canvas/text.h"
+
+namespace ArdourCanvas {
+
+class LIBCANVAS_API TrackingText : public Text
+{
+ public:
+ TrackingText (Canvas*);
+ TrackingText (Item*);
+
+ void show_and_track (bool track_x, bool track_y);
+ void set_offset (Duple const &);
+ void set_x_offset (double);
+ void set_y_offset (double);
+
+ private:
+ bool track_x;
+ bool track_y;
+ Duple offset;
+
+ void pointer_motion (Duple const&);
+ void init ();
+};
+
+}
+
+#endif /* __ardour_canvas_tracking_text_h__ */
#include "canvas/visibility.h"
namespace Cairo {
- struct Context;
+ class Context;
}
namespace ArdourCanvas
extern LIBCANVAS_API void set_source_rgba (Cairo::RefPtr<Cairo::Context>, Color);
Distance LIBCANVAS_API distance_to_segment_squared (Duple const & p, Duple const & p1, Duple const & p2, double& t, Duple& at);
+
+ uint32_t LIBCANVAS_API contrasting_text_color (uint32_t c);
}
namespace ArdourCanvas {
-class LIBCANVAS_API WaveView : virtual public Item, public Outline, public Fill
+class LIBCANVAS_API WaveView : public Item
{
public:
+
enum Shape {
Normal,
- Rectified,
+ Rectified
};
-
+
+ struct CacheEntry {
+ int channel;
+ Coord height;
+ float amplitude;
+ Color fill_color;
+ Color outline_color;
+ framepos_t start;
+ framepos_t end;
+ Cairo::RefPtr<Cairo::ImageSurface> image;
+ CacheEntry() :
+ channel (0), height (0), amplitude(0), fill_color (0),
+ outline_color (0), start (0), end (0), image (0) {}
+ CacheEntry(int chan, Coord hght, float amp, Color fcol, Color ocol,
+ framepos_t strt, framepos_t ed, Cairo::RefPtr<Cairo::ImageSurface> img) :
+ channel (chan), height (hght), amplitude (amp), fill_color (fcol),
+ outline_color (ocol), start (strt), end (ed), image (img) {}
+ };
+
/* Displays a single channel of waveform data for the given Region.
x = 0 in the waveview corresponds to the first waveform datum taken
*/
- WaveView (Group *, boost::shared_ptr<ARDOUR::AudioRegion>);
+ WaveView (Canvas *, boost::shared_ptr<ARDOUR::AudioRegion>);
+ WaveView (Item*, boost::shared_ptr<ARDOUR::AudioRegion>);
~WaveView ();
void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
friend class ::WaveViewTest;
- void invalidate_image ();
+ static std::map <boost::shared_ptr<ARDOUR::AudioSource>, std::vector <CacheEntry> > _image_cache;
+ void consolidate_image_cache () const;
+ void invalidate_image_cache ();
boost::shared_ptr<ARDOUR::AudioRegion> _region;
int _channel;
bool _logscaled_independent;
bool _gradient_depth_independent;
double _amplitude_above_axis;
+ float _region_amplitude;
/** The `start' value to use for the region; we can't use the region's
* value as the crossfade editor needs to alter it.
*/
ARDOUR::frameoffset_t _region_start;
-
-
- mutable ARDOUR::framepos_t _sample_start;
- mutable ARDOUR::framepos_t _sample_end;
- mutable Cairo::RefPtr<Cairo::ImageSurface> _image;
- PBD::ScopedConnection invalidation_connection;
+ PBD::ScopedConnectionList invalidation_connection;
static double _global_gradient_depth;
static bool _global_logscaled;
static PBD::Signal0<void> VisualPropertiesChanged;
void handle_visual_property_change ();
+ void handle_clip_level_change ();
+
+ void get_image (Cairo::RefPtr<Cairo::ImageSurface>& image, framepos_t start, framepos_t end, double& image_offset) const;
- void ensure_cache (ARDOUR::framepos_t sample_start, ARDOUR::framepos_t sample_end) const;
- ArdourCanvas::Coord position (double) const;
- void draw_image (ARDOUR::PeakData*, int npeaks) const;
+ ArdourCanvas::Coord y_extent (double, bool) const;
+ void draw_image (Cairo::RefPtr<Cairo::ImageSurface>&, ARDOUR::PeakData*, int) const;
};
}
--- /dev/null
+/*
+ Copyright (C) 2011-2013 Paul Davis
+ Author: Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the 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 __CANVAS_WIDGET_H__
+#define __CANVAS_WIDGET_H__
+
+#include "canvas/visibility.h"
+#include "canvas/item.h"
+
+#include "gtkmm2ext/cairo_widget.h"
+
+namespace ArdourCanvas
+{
+
+class LIBCANVAS_API Widget : public Item
+{
+public:
+ Widget (Canvas*, CairoWidget&);
+ Widget (Item*, CairoWidget&);
+
+ void render (Rect const &, Cairo::RefPtr<Cairo::Context>) const;
+ void compute_bounding_box () const;
+
+ CairoWidget const & get () const {
+ return _widget;
+ }
+
+private:
+ CairoWidget& _widget;
+ bool event_proxy (GdkEvent*);
+};
+
+}
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2013 Paul Davis
+ Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the 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 __CANVAS_XFADECURVE_H__
+#define __CANVAS_XFADECURVE_H__
+
+#include "canvas/visibility.h"
+#include "canvas/item.h"
+#include "canvas/curve.h"
+
+namespace ArdourCanvas {
+
+class LIBCANVAS_API XFadeCurve : public Item, public InterpolatedCurve
+{
+public:
+ enum XFadePosition {
+ Start,
+ End,
+ };
+
+ XFadeCurve (Canvas *);
+ XFadeCurve (Canvas *, XFadePosition);
+ XFadeCurve (Item*);
+ XFadeCurve (Item*, XFadePosition);
+
+ void set_fade_position (XFadePosition xfp) { _xfadeposition = xfp; }
+
+ void compute_bounding_box () const;
+ void render (Rect const & area, Cairo::RefPtr<Cairo::Context>) const;
+
+ void set_points_per_segment (uint32_t n);
+ void set_inout (Points const & in, Points const & out);
+
+ void set_outline_color (Color c) {
+ begin_visual_change ();
+ _outline_color = c;
+ end_visual_change ();
+ };
+
+ void set_fill_color (Color c) {
+ begin_visual_change ();
+ _fill_color = c;
+ end_visual_change ();
+ }
+
+private:
+ struct CanvasCurve {
+ CanvasCurve() : n_samples(0) { }
+ Points points;
+ Points samples;
+ Points::size_type n_samples;
+ };
+
+ Cairo::Path * get_path(Rect const &, Cairo::RefPtr<Cairo::Context>, CanvasCurve const &) const;
+ void close_path(Rect const &, Cairo::RefPtr<Cairo::Context>, CanvasCurve const &p, bool) const;
+
+ uint32_t points_per_segment;
+
+ CanvasCurve _in;
+ CanvasCurve _out;
+
+ XFadePosition _xfadeposition;
+ Color _outline_color;
+ Color _fill_color;
+
+ void interpolate ();
+};
+
+}
+
+#endif
using namespace ArdourCanvas;
-Circle::Circle (Group* parent)
- : Item (parent)
- , Arc (parent)
+Circle::Circle (Canvas* c)
+ : Arc (c)
{
set_arc (360.0);
}
+Circle::Circle (Item * parent)
+ : Arc (parent)
+{
+ set_arc (360.0);
+}
+
+
--- /dev/null
+/*
+ Copyright (C) 2011-2014 Paul Davis
+ Author: Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the 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 "canvas/container.h"
+
+using namespace ArdourCanvas;
+
+Container::Container (Canvas* canvas)
+ : Item (canvas)
+{
+}
+
+Container::Container (Item* parent)
+ : Item (parent)
+{
+}
+
+
+Container::Container (Item* parent, Duple const & p)
+ : Item (parent, p)
+{
+}
+
+void
+Container::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+ Item::render_children (area, context);
+}
+
+void
+Container::compute_bounding_box () const
+{
+ _bounding_box = boost::none;
+ add_child_bounding_boxes ();
+ _bounding_box_dirty = false;
+}
+
using std::min;
using std::max;
-Curve::Curve (Group* parent)
- : Item (parent)
- , PolyItem (parent)
- , Fill (parent)
+Curve::Curve (Canvas* c)
+ : PolyItem (c)
, n_samples (0)
, points_per_segment (16)
, curve_type (CatmullRomCentripetal)
+ , curve_fill (None)
+{
+}
+
+Curve::Curve (Item* parent)
+ : PolyItem (parent)
+ , n_samples (0)
+ , points_per_segment (16)
+ , curve_type (CatmullRomCentripetal)
+ , curve_fill (None)
{
}
Curve::interpolate ()
{
samples.clear ();
- interpolate (_points, points_per_segment, CatmullRomCentripetal, false, samples);
+ InterpolatedCurve::interpolate (_points, points_per_segment, CatmullRomCentripetal, false, samples);
n_samples = samples.size();
}
-/* Cartmull-Rom code from http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/19283471#19283471
- *
- * Thanks to Ted for his Java version, which I translated into Ardour-idiomatic
- * C++ here.
- */
-
-/**
- * Calculate the same values but introduces the ability to "parameterize" the t
- * values used in the calculation. This is based on Figure 3 from
- * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
- *
- * @param p An array of double values of length 4, where interpolation
- * occurs from p1 to p2.
- * @param time An array of time measures of length 4, corresponding to each
- * p value.
- * @param t the actual interpolation ratio from 0 to 1 representing the
- * position between p1 and p2 to interpolate the value.
- */
-static double
-__interpolate (double p[4], double time[4], double t)
-{
- const double L01 = p[0] * (time[1] - t) / (time[1] - time[0]) + p[1] * (t - time[0]) / (time[1] - time[0]);
- const double L12 = p[1] * (time[2] - t) / (time[2] - time[1]) + p[2] * (t - time[1]) / (time[2] - time[1]);
- const double L23 = p[2] * (time[3] - t) / (time[3] - time[2]) + p[3] * (t - time[2]) / (time[3] - time[2]);
- const double L012 = L01 * (time[2] - t) / (time[2] - time[0]) + L12 * (t - time[0]) / (time[2] - time[0]);
- const double L123 = L12 * (time[3] - t) / (time[3] - time[1]) + L23 * (t - time[1]) / (time[3] - time[1]);
- const double C12 = L012 * (time[2] - t) / (time[2] - time[1]) + L123 * (t - time[1]) / (time[2] - time[1]);
- return C12;
-}
-
-/**
- * Given a list of control points, this will create a list of points_per_segment
- * points spaced uniformly along the resulting Catmull-Rom curve.
- *
- * @param points The list of control points, leading and ending with a
- * coordinate that is only used for controling the spline and is not visualized.
- * @param index The index of control point p0, where p0, p1, p2, and p3 are
- * used in order to create a curve between p1 and p2.
- * @param points_per_segment The total number of uniformly spaced interpolated
- * points to calculate for each segment. The larger this number, the
- * smoother the resulting curve.
- * @param curve_type Clarifies whether the curve should use uniform, chordal
- * or centripetal curve types. Uniform can produce loops, chordal can
- * produce large distortions from the original lines, and centripetal is an
- * optimal balance without spaces.
- * @return the list of coordinates that define the CatmullRom curve
- * between the points defined by index+1 and index+2.
- */
-static void
-_interpolate (const Points& points, Points::size_type index, int points_per_segment, Curve::SplineType curve_type, Points& results)
-{
- double x[4];
- double y[4];
- double time[4];
-
- for (int i = 0; i < 4; i++) {
- x[i] = points[index + i].x;
- y[i] = points[index + i].y;
- time[i] = i;
- }
-
- double tstart = 1;
- double tend = 2;
-
- if (curve_type != Curve::CatmullRomUniform) {
- double total = 0;
- for (int i = 1; i < 4; i++) {
- double dx = x[i] - x[i - 1];
- double dy = y[i] - y[i - 1];
- if (curve_type == Curve::CatmullRomCentripetal) {
- total += pow (dx * dx + dy * dy, .25);
- } else {
- total += pow (dx * dx + dy * dy, .5);
- }
- time[i] = total;
- }
- tstart = time[1];
- tend = time[2];
- }
-
- int segments = points_per_segment - 1;
- results.push_back (points[index + 1]);
-
- for (int i = 1; i < segments; i++) {
- double xi = __interpolate (x, time, tstart + (i * (tend - tstart)) / segments);
- double yi = __interpolate (y, time, tstart + (i * (tend - tstart)) / segments);
- results.push_back (Duple (xi, yi));
- }
-
- results.push_back (points[index + 2]);
-}
-
-/**
- * This method will calculate the Catmull-Rom interpolation curve, returning
- * it as a list of Coord coordinate objects. This method in particular
- * adds the first and last control points which are not visible, but required
- * for calculating the spline.
- *
- * @param coordinates The list of original straight line points to calculate
- * an interpolation from.
- * @param points_per_segment The integer number of equally spaced points to
- * return along each curve. The actual distance between each
- * point will depend on the spacing between the control points.
- * @return The list of interpolated coordinates.
- * @param curve_type Chordal (stiff), Uniform(floppy), or Centripetal(medium)
- * @throws gov.ca.water.shapelite.analysis.CatmullRomException if
- * points_per_segment is less than 2.
- */
-
-void
-Curve::interpolate (const Points& coordinates, uint32_t points_per_segment, SplineType curve_type, bool closed, Points& results)
-{
- if (points_per_segment < 2) {
- return;
- }
-
- // Cannot interpolate curves given only two points. Two points
- // is best represented as a simple line segment.
- if (coordinates.size() < 3) {
- results = coordinates;
- return;
- }
-
- // Copy the incoming coordinates. We need to modify it during interpolation
- Points vertices = coordinates;
-
- // Test whether the shape is open or closed by checking to see if
- // the first point intersects with the last point. M and Z are ignored.
- if (closed) {
- // Use the second and second from last points as control points.
- // get the second point.
- Duple p2 = vertices[1];
- // get the point before the last point
- Duple pn1 = vertices[vertices.size() - 2];
-
- // insert the second from the last point as the first point in the list
- // because when the shape is closed it keeps wrapping around to
- // the second point.
- vertices.insert(vertices.begin(), pn1);
- // add the second point to the end.
- vertices.push_back(p2);
- } else {
- // The shape is open, so use control points that simply extend
- // the first and last segments
-
- // Get the change in x and y between the first and second coordinates.
- double dx = vertices[1].x - vertices[0].x;
- double dy = vertices[1].y - vertices[0].y;
-
- // Then using the change, extrapolate backwards to find a control point.
- double x1 = vertices[0].x - dx;
- double y1 = vertices[0].y - dy;
-
- // Actaully create the start point from the extrapolated values.
- Duple start (x1, y1);
-
- // Repeat for the end control point.
- int n = vertices.size() - 1;
- dx = vertices[n].x - vertices[n - 1].x;
- dy = vertices[n].y - vertices[n - 1].y;
- double xn = vertices[n].x + dx;
- double yn = vertices[n].y + dy;
- Duple end (xn, yn);
-
- // insert the start control point at the start of the vertices list.
- vertices.insert (vertices.begin(), start);
-
- // append the end control ponit to the end of the vertices list.
- vertices.push_back (end);
- }
-
- // When looping, remember that each cycle requires 4 points, starting
- // with i and ending with i+3. So we don't loop through all the points.
-
- for (Points::size_type i = 0; i < vertices.size() - 3; i++) {
-
- // Actually calculate the Catmull-Rom curve for one segment.
- Points r;
-
- _interpolate (vertices, i, points_per_segment, curve_type, r);
-
- // Since the middle points are added twice, once for each bordering
- // segment, we only add the 0 index result point for the first
- // segment. Otherwise we will have duplicate points.
-
- if (results.size() > 0) {
- r.erase (r.begin());
- }
-
- // Add the coordinates for the segment to the result list.
-
- results.insert (results.end(), r.begin(), r.end());
- }
-}
-
-/** Given a fractional position within the x-axis range of the
- * curve, return the corresponding y-axis value
- */
-
-double
-Curve::map_value (double x) const
-{
- if (x > 0.0 && x < 1.0) {
-
- double f;
- Points::size_type index;
-
- /* linearly interpolate between two of our smoothed "samples"
- */
-
- x = x * (n_samples - 1);
- index = (Points::size_type) x; // XXX: should we explicitly use floor()?
- f = x - index;
-
- return (1.0 - f) * samples[index].y + f * samples[index+1].y;
-
- } else if (x >= 1.0) {
- return samples.back().y;
- } else {
- return samples.front().y;
- }
-}
-
void
Curve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
window_space = item_to_window (_points.back());
context->line_to (window_space.x, window_space.y);
- context->stroke ();
+
+ switch (curve_fill) {
+ case None:
+ context->stroke();
+ break;
+ case Inside:
+ context->stroke_preserve ();
+ window_space = item_to_window (Duple(_points.back().x, draw.height()));
+ context->line_to (window_space.x, window_space.y);
+ window_space = item_to_window (Duple(_points.front().x, draw.height()));
+ context->line_to (window_space.x, window_space.y);
+ context->close_path();
+ setup_fill_context(context);
+ context->fill ();
+ break;
+ case Outside:
+ context->stroke_preserve ();
+ window_space = item_to_window (Duple(_points.back().x, 0.0));
+ context->line_to (window_space.x, window_space.y);
+ window_space = item_to_window (Duple(_points.front().x, 0.0));
+ context->line_to (window_space.x, window_space.y);
+ context->close_path();
+ setup_fill_context(context);
+ context->fill ();
+ break;
+ }
} else {
draw.x1 = w2.x;
}
- /* full width of the curve */
- const double xextent = _points.back().x - _points.front().x;
- /* Determine where the first drawn point will be */
- Duple item_space = window_to_item (Duple (draw.x0, 0)); /* y value is irrelevant */
- /* determine the fractional offset of this location into the overall extent of the curve */
- const double xfract_offset = (item_space.x - _points.front().x)/xextent;
- const uint32_t pixels = draw.width ();
+ /* find left and right-most sample */
Duple window_space;
+ Points::size_type left = 0;
+ Points::size_type right = n_samples;
- /* draw the first point */
-
- for (uint32_t pixel = 0; pixel < pixels; ++pixel) {
-
- /* fractional distance into the total horizontal extent of the curve */
- double xfract = xfract_offset + (pixel / xextent);
- /* compute vertical coordinate (item-space) at that location */
- double y = map_value (xfract);
-
- /* convert to window space for drawing */
- window_space = item_to_window (Duple (0.0, y)); /* x-value is irrelevant */
-
- /* we are moving across the draw area pixel-by-pixel */
- window_space.x = draw.x0 + pixel;
-
- /* plot this point */
- if (pixel == 0) {
- context->move_to (window_space.x, window_space.y);
- } else {
- context->line_to (window_space.x, window_space.y);
- }
+ for (Points::size_type idx = 0; idx < n_samples - 1; ++idx) {
+ left = idx;
+ window_space = item_to_window (Duple (samples[idx].x, 0.0));
+ if (window_space.x >= draw.x0) break;
+ }
+ for (Points::size_type idx = n_samples; idx > left + 1; --idx) {
+ window_space = item_to_window (Duple (samples[idx].x, 0.0));
+ if (window_space.x <= draw.x1) break;
+ right = idx;
}
- context->stroke ();
+ /* draw line between samples */
+ window_space = item_to_window (Duple (samples[left].x, samples[left].y));
+ context->move_to (window_space.x, window_space.y);
+ for (uint32_t idx = left + 1; idx < right; ++idx) {
+ window_space = item_to_window (Duple (samples[idx].x, samples[idx].y));
+ context->line_to (window_space.x, window_space.y);
+ }
+
+ switch (curve_fill) {
+ case None:
+ context->stroke();
+ break;
+ case Inside:
+ context->stroke_preserve ();
+ window_space = item_to_window (Duple (samples[right-1].x, draw.height()));
+ context->line_to (window_space.x, window_space.y);
+ window_space = item_to_window (Duple (samples[left].x, draw.height()));
+ context->line_to (window_space.x, window_space.y);
+ context->close_path();
+ setup_fill_context(context);
+ context->fill ();
+ break;
+ case Outside:
+ context->stroke_preserve ();
+ window_space = item_to_window (Duple (samples[right-1].x, 0.0));
+ context->line_to (window_space.x, window_space.y);
+ window_space = item_to_window (Duple (samples[left].x, 0.0));
+ context->line_to (window_space.x, window_space.y);
+ context->close_path();
+ setup_fill_context(context);
+ context->fill ();
+ break;
+ }
context->restore ();
}
-#if 1
+#if 0
/* add points */
-
- setup_fill_context (context);
+ setup_outline_context (context);
for (Points::const_iterator p = _points.begin(); p != _points.end(); ++p) {
Duple window_space (item_to_window (*p));
context->arc (window_space.x, window_space.y, 5.0, 0.0, 2 * M_PI);
bool
Curve::covers (Duple const & pc) const
{
- Duple point = canvas_to_item (pc);
+ Duple point = window_to_item (pc);
/* O(N) N = number of points, and not accurate */
+++ /dev/null
-/*
- Copyright (C) 2014 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <iostream>
-#include <cairomm/context.h>
-#include "pbd/stacktrace.h"
-#include "pbd/compose.h"
-
-#include "canvas/drag_handle.h"
-
-using namespace ArdourCanvas;
-
-DragHandle::DragHandle (Group* g, Rect const & r, bool left_side)
- : Item (g)
- , Rectangle (g, r)
- , _left_side (left_side)
-{
-}
-
-void
-DragHandle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
-{
- Rectangle::render (area, context);
-
-#if 0
- Duple circle_center (_left_side ? x0() : x1(), (y1() - y0())/2.0);
- Duple window_circle_center = item_to_window (circle_center);
-
- context->set_source_rgba (1.0, 0.0, 0.0, 1.0);
-
- if (_left_side) {
- context->arc (window_circle_center.x, window_circle_center.y, 7.0, -M_PI/2.0, +M_PI/2.0);
- } else {
- context->arc_negative (window_circle_center.x, window_circle_center.y, 7.0, -M_PI/2.0, +M_PI/2.0);
- }
-
- context->fill ();
-#endif
-
-}
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <cairomm/cairomm.h>
+
#include "ardour/utils.h"
#include "pbd/compose.h"
#include "pbd/convert.h"
#include "canvas/fill.h"
+#include "canvas/item.h"
+#include "canvas/types.h"
#include "canvas/utils.h"
using namespace std;
using namespace ArdourCanvas;
-Fill::Fill (Group* parent)
- : Item (parent)
+Fill::Fill (Item& self)
+ : _self (self)
, _fill_color (0x000000ff)
, _fill (true)
, _transparent (false)
{
-
}
void
Fill::set_fill_color (Color color)
{
if (_fill_color != color) {
- begin_visual_change ();
+ _self.begin_visual_change ();
_fill_color = color;
double r, g, b, a;
_transparent = false;
}
- end_visual_change ();
+ _self.end_visual_change ();
}
}
Fill::set_fill (bool fill)
{
if (_fill != fill) {
- begin_visual_change ();
+ _self.begin_visual_change ();
_fill = fill;
- end_visual_change ();
+ _self.end_visual_change ();
}
}
void
Fill::set_gradient (StopList const & stops, bool vertical)
{
- begin_visual_change ();
+ _self.begin_visual_change ();
if (stops.empty()) {
_stops.clear ();
_vertical_gradient = vertical;
}
- end_visual_change ();
+ _self.end_visual_change ();
}
using namespace std;
using namespace ArdourCanvas;
-Flag::Flag (Group* parent, Distance height, Color outline_color, Color fill_color, Duple position)
- : Group (parent)
+Flag::Flag (Canvas* canvas, Distance height, Color outline_color, Color fill_color, Duple position)
+ : Container (canvas)
, _outline_color (outline_color)
, _fill_color (fill_color)
+{
+ setup (height, position);
+}
+
+Flag::Flag (Item* parent, Distance height, Color outline_color, Color fill_color, Duple position)
+ : Container (parent)
+ , _outline_color (outline_color)
+ , _fill_color (fill_color)
+{
+ setup (height, position);
+}
+
+void
+Flag::setup (Distance height, Duple position)
{
_text = new Text (this);
_text->set_alignment (Pango::ALIGN_CENTER);
+++ /dev/null
-/*
- Copyright (C) 2011-2013 Paul Davis
- Author: Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-*/
-
-#include <iostream>
-#include <cairomm/context.h>
-
-#include "pbd/stacktrace.h"
-#include "pbd/compose.h"
-
-#include "canvas/group.h"
-#include "canvas/types.h"
-#include "canvas/debug.h"
-#include "canvas/item.h"
-#include "canvas/canvas.h"
-
-using namespace std;
-using namespace ArdourCanvas;
-
-int Group::default_items_per_cell = 64;
-
-
-Group::Group (Canvas* canvas)
- : Item (canvas)
- , _lut (0)
-{
-
-}
-
-Group::Group (Group* parent)
- : Item (parent)
- , _lut (0)
-{
-
-}
-
-Group::Group (Group* parent, Duple position)
- : Item (parent, position)
- , _lut (0)
-{
-
-}
-
-Group::~Group ()
-{
- clear_items (true);
-}
-
-/** @param area Area to draw in this group's coordinates.
- * @param context Context, set up with its origin at this group's position.
- */
-void
-Group::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
-{
- ensure_lut ();
- vector<Item*> items = _lut->get (area);
-
-#ifdef CANVAS_DEBUG
- if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
- cerr << string_compose ("%1GROUP %2 @ %7 render %5 @ %6 %3 items out of %4\n",
- _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area, _position, this);
- }
-#endif
-
- ++render_depth;
-
- for (vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
-
- if (!(*i)->visible ()) {
-#ifdef CANVAS_DEBUG
- if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
- cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
- }
-#endif
- continue;
- }
-
- boost::optional<Rect> item_bbox = (*i)->bounding_box ();
-
- if (!item_bbox) {
-#ifdef CANVAS_DEBUG
- if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
- cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
- }
-#endif
- continue;
- }
-
- Rect item = (*i)->item_to_window (item_bbox.get());
- boost::optional<Rect> d = item.intersection (area);
-
- if (d) {
- Rect draw = d.get();
- if (draw.width() && draw.height()) {
-#ifdef CANVAS_DEBUG
- if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
- if (dynamic_cast<Group*>(*i) == 0) {
- cerr << _canvas->render_indent() << "render "
- << ' '
- << (*i)
- << ' '
- << (*i)->whatami()
- << ' '
- << (*i)->name
- << " item = "
- << item
- << " intersect = "
- << draw
- << " @ "
- << _position
- << endl;
- }
- }
-#endif
-
- (*i)->render (area, context);
- ++render_count;
- }
-
- } else {
-
-#ifdef CANVAS_DEBUG
- if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
- cerr << string_compose ("%1skip render of %2 %3, no intersection\n", _canvas->render_indent(), (*i)->whatami(),
- (*i)->name);
- }
-#endif
-
- }
- }
-
- --render_depth;
-}
-
-void
-Group::compute_bounding_box () const
-{
- Rect bbox;
- bool have_one = false;
-
- for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
-
- boost::optional<Rect> item_bbox = (*i)->bounding_box ();
-
- if (!item_bbox) {
- continue;
- }
-
- Rect group_bbox = (*i)->item_to_parent (item_bbox.get ());
- if (have_one) {
- bbox = bbox.extend (group_bbox);
- } else {
- bbox = group_bbox;
- have_one = true;
- }
- }
-
- if (!have_one) {
- _bounding_box = boost::optional<Rect> ();
- } else {
- _bounding_box = bbox;
- }
-
- _bounding_box_dirty = false;
-}
-
-void
-Group::add (Item* i)
-{
- /* XXX should really notify canvas about this */
-
- _items.push_back (i);
- invalidate_lut ();
- _bounding_box_dirty = true;
-
-
-}
-
-void
-Group::remove (Item* i)
-{
-
- if (i->parent() != this) {
- return;
- }
-
- /* we cannot call bounding_box() here because that will iterate over
- _items, one of which (the argument, i) may be in the middle of
- deletion, making it impossible to call compute_bounding_box()
- on it.
- */
-
- if (_bounding_box) {
- _pre_change_bounding_box = _bounding_box;
- } else {
- _pre_change_bounding_box = Rect();
- }
-
- i->unparent ();
- _items.remove (i);
- invalidate_lut ();
- _bounding_box_dirty = true;
-
- end_change ();
-}
-
-void
-Group::clear (bool with_delete)
-{
- begin_change ();
-
- clear_items (with_delete);
-
- invalidate_lut ();
- _bounding_box_dirty = true;
-
- end_change ();
-}
-
-void
-Group::clear_items (bool with_delete)
-{
- for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ) {
-
- list<Item*>::iterator tmp = i;
- Item *item = *i;
-
- ++tmp;
-
- /* remove from list before doing anything else, because we
- * don't want to find the item in _items during any activity
- * driven by unparent-ing or deletion.
- */
-
- _items.erase (i);
- item->unparent ();
-
- if (with_delete) {
- delete item;
- }
-
- i = tmp;
- }
-}
-
-void
-Group::raise_child_to_top (Item* i)
-{
- if (!_items.empty()) {
- if (_items.back() == i) {
- return;
- }
- }
-
- _items.remove (i);
- _items.push_back (i);
- invalidate_lut ();
-}
-
-void
-Group::raise_child (Item* i, int levels)
-{
- list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
- assert (j != _items.end ());
-
- ++j;
- _items.remove (i);
-
- while (levels > 0 && j != _items.end ()) {
- ++j;
- --levels;
- }
-
- _items.insert (j, i);
- invalidate_lut ();
-}
-
-void
-Group::lower_child_to_bottom (Item* i)
-{
- if (!_items.empty()) {
- if (_items.front() == i) {
- return;
- }
- }
- _items.remove (i);
- _items.push_front (i);
- invalidate_lut ();
-}
-
-void
-Group::ensure_lut () const
-{
- if (!_lut) {
- _lut = new DumbLookupTable (*this);
- }
-}
-
-void
-Group::invalidate_lut () const
-{
- delete _lut;
- _lut = 0;
-}
-
-void
-Group::child_changed ()
-{
- invalidate_lut ();
- _bounding_box_dirty = true;
-
- if (_parent) {
- _parent->child_changed ();
- }
-}
-
-void
-Group::add_items_at_point (Duple const point, vector<Item const *>& items) const
-{
- boost::optional<Rect> const bbox = bounding_box ();
-
- /* Point is in canvas coordinate system */
-
- if (!bbox || !item_to_canvas (bbox.get()).contains (point)) {
- return;
- }
-
- /* now recurse and add any items within our group that contain point */
-
- ensure_lut ();
- vector<Item*> our_items = _lut->items_at_point (point);
-
- if (!our_items.empty()) {
- /* this adds this group itself to the list of items at point */
- Item::add_items_at_point (point, items);
- }
-
- for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
- (*i)->add_items_at_point (point, items);
- }
-}
-
-void
-Group::dump (ostream& o) const
-{
-#ifdef CANVAS_DEBUG
- o << _canvas->indent();
- o << "Group " << this << " [" << name << ']';
- o << " @ " << position();
- o << " Items: " << _items.size();
- o << " Visible ? " << _visible;
-
- boost::optional<Rect> bb = bounding_box();
-
- if (bb) {
- o << endl << _canvas->indent() << " bbox: " << bb.get();
- o << endl << _canvas->indent() << " CANVAS bbox: " << item_to_canvas (bb.get());
- } else {
- o << " bbox unset";
- }
-
- o << endl;
-#endif
-
- ArdourCanvas::dump_depth++;
-
- for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
- o << **i;
- }
-
- ArdourCanvas::dump_depth--;
-}
using namespace ArdourCanvas;
-Image::Image (Group* group, Cairo::Format fmt, int width, int height)
- : Item (group)
+Image::Image (Canvas* canvas, Cairo::Format fmt, int width, int height)
+ : Item (canvas)
+ , _format (fmt)
+ , _width (width)
+ , _height (height)
+ , _need_render (false)
+{
+ DataReady.connect (data_connections, MISSING_INVALIDATOR, boost::bind (&Image::accept_data, this), gui_context());
+}
+
+Image::Image (Item* parent, Cairo::Format fmt, int width, int height)
+ : Item (parent)
, _format (fmt)
, _width (width)
, _height (height)
#include "ardour/utils.h"
-#include "canvas/group.h"
-#include "canvas/item.h"
#include "canvas/canvas.h"
#include "canvas/debug.h"
+#include "canvas/item.h"
+#include "canvas/scroll_group.h"
using namespace std;
using namespace PBD;
using namespace ArdourCanvas;
+int Item::default_items_per_cell = 64;
+
Item::Item (Canvas* canvas)
- : _canvas (canvas)
+ : Fill (*this)
+ , Outline (*this)
+ , _canvas (canvas)
, _parent (0)
+ , _scroll_parent (0)
+ , _visible (true)
+ , _bounding_box_dirty (true)
+ , _lut (0)
+ , _ignore_events (false)
{
- init ();
-}
+ DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
+}
-Item::Item (Group* parent)
- : _canvas (parent->canvas ())
+Item::Item (Item* parent)
+ : Fill (*this)
+ , Outline (*this)
+ , _canvas (parent->canvas())
, _parent (parent)
+ , _scroll_parent (0)
+ , _visible (true)
+ , _bounding_box_dirty (true)
+ , _lut (0)
+ , _ignore_events (false)
{
- init ();
-}
+ DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
+
+ if (parent) {
+ _parent->add (this);
+ }
-Item::Item (Group* parent, Duple position)
- : _canvas (parent->canvas())
+ find_scroll_parent ();
+}
+
+Item::Item (Item* parent, Duple const& p)
+ : Fill (*this)
+ , Outline (*this)
+ , _canvas (parent->canvas())
, _parent (parent)
- , _position (position)
+ , _scroll_parent (0)
+ , _position (p)
+ , _visible (true)
+ , _bounding_box_dirty (true)
+ , _lut (0)
+ , _ignore_events (false)
{
- init ();
-}
+ DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
-void
-Item::init ()
-{
- _visible = true;
- _bounding_box_dirty = true;
- _ignore_events = false;
-
- if (_parent) {
+ if (parent) {
_parent->add (this);
}
- DEBUG_TRACE (DEBUG::CanvasItems, string_compose ("new canvas item %1\n", this));
+ find_scroll_parent ();
+
}
Item::~Item ()
if (_canvas) {
_canvas->item_going_away (this, _bounding_box);
}
+
+ clear_items (true);
+ delete _lut;
+}
+
+Duple
+Item::canvas_origin () const
+{
+ return item_to_canvas (Duple (0,0));
+}
+
+Duple
+Item::window_origin () const
+{
+ /* This is slightly subtle. Our _position is in the coordinate space of
+ our parent. So to find out where that is in window coordinates, we
+ have to ask our parent.
+ */
+ if (_parent) {
+ return _parent->item_to_window (_position);
+ } else {
+ return _position;
+ }
}
ArdourCanvas::Rect
return r.translate (_position);
}
-ArdourCanvas::Rect
-Item::item_to_canvas (ArdourCanvas::Rect const & r) const
+Duple
+Item::scroll_offset () const
+{
+ if (_scroll_parent) {
+ return _scroll_parent->scroll_offset();
+ }
+ return Duple (0,0);
+}
+
+Duple
+Item::position_offset() const
{
Item const * i = this;
Duple offset;
i = i->parent();
}
- return r.translate (offset);
+ return offset;
+}
+
+ArdourCanvas::Rect
+Item::item_to_canvas (ArdourCanvas::Rect const & r) const
+{
+ return r.translate (position_offset());
}
ArdourCanvas::Duple
Item::item_to_canvas (ArdourCanvas::Duple const & d) const
{
- Item const * i = this;
- Duple offset;
-
- while (i) {
- offset = offset.translate (i->position());
- i = i->parent();
- }
-
- return d.translate (offset);
+ return d.translate (position_offset());
}
ArdourCanvas::Duple
-Item::canvas_to_item (ArdourCanvas::Duple const & d) const
+Item::canvas_to_item (ArdourCanvas::Duple const & r) const
{
- Item const * i = this;
- Duple offset;
-
- while (i) {
- offset = offset.translate (-(i->position()));
- i = i->parent();
- }
-
- return d.translate (offset);
+ return r.translate (-position_offset());
}
ArdourCanvas::Rect
-Item::canvas_to_item (ArdourCanvas::Rect const & d) const
+Item::canvas_to_item (ArdourCanvas::Rect const & r) const
{
- Item const * i = this;
- Duple offset;
-
- while (i) {
- offset = offset.translate (-(i->position()));
- i = i->parent();
- }
-
- return d.translate (offset);
+ return r.translate (-position_offset());
}
void
y = d.y;
}
+
Duple
-Item::item_to_window (ArdourCanvas::Duple const & d) const
+Item::item_to_window (ArdourCanvas::Duple const & d, bool rounded) const
{
- return _canvas->canvas_to_window (item_to_canvas (d));
+ Duple ret = item_to_canvas (d).translate (-scroll_offset());
+
+ if (rounded) {
+ ret.x = round (ret.x);
+ ret.y = round (ret.y);
+ }
+
+ return ret;
}
Duple
Item::window_to_item (ArdourCanvas::Duple const & d) const
{
- return _canvas->window_to_canvas (canvas_to_item (d));
+ return canvas_to_item (d.translate (scroll_offset()));
}
ArdourCanvas::Rect
Item::item_to_window (ArdourCanvas::Rect const & r) const
{
- return _canvas->canvas_to_window (item_to_canvas (r));
+ Rect ret = item_to_canvas (r).translate (-scroll_offset());
+
+ ret.x0 = round (ret.x0);
+ ret.x1 = round (ret.x1);
+ ret.y0 = round (ret.y0);
+ ret.y1 = round (ret.y1);
+
+ return ret;
+}
+
+ArdourCanvas::Rect
+Item::window_to_item (ArdourCanvas::Rect const & r) const
+{
+ return canvas_to_item (r.translate (scroll_offset()));
}
/** Set the position of this item in the parent's coordinates */
void
Item::raise_to_top ()
{
- assert (_parent);
- _parent->raise_child_to_top (this);
+ if (_parent) {
+ _parent->raise_child_to_top (this);
+ }
}
void
Item::raise (int levels)
{
- assert (_parent);
- _parent->raise_child (this, levels);
+ if (_parent) {
+ _parent->raise_child (this, levels);
+ }
}
void
Item::lower_to_bottom ()
{
- assert (_parent);
- _parent->lower_child_to_bottom (this);
+ if (_parent) {
+ _parent->lower_child_to_bottom (this);
+ }
}
void
{
if (_visible) {
_visible = false;
+
+ /* recompute parent bounding box, which may alter now that this
+ * child is hidden.
+ */
+
+ if (_parent) {
+ _parent->child_changed ();
+ }
+
_canvas->item_shown_or_hidden (this);
}
}
{
if (!_visible) {
_visible = true;
+
+ /* bounding box may have changed while we were hidden */
+
+ if (_parent) {
+ _parent->child_changed ();
+ }
+
_canvas->item_shown_or_hidden (this);
}
}
Item::unparent ()
{
_parent = 0;
+ _scroll_parent = 0;
}
void
-Item::reparent (Group* new_parent)
+Item::reparent (Item* new_parent)
{
- assert (_canvas == _parent->canvas());
+ if (new_parent == _parent) {
+ return;
+ }
+
+ assert (_canvas == new_parent->canvas());
if (_parent) {
_parent->remove (this);
_parent = new_parent;
_canvas = _parent->canvas ();
+
+ find_scroll_parent ();
+
_parent->add (this);
}
+void
+Item::find_scroll_parent ()
+{
+ Item const * i = this;
+ ScrollGroup const * last_scroll_group = 0;
+
+ /* Don't allow a scroll group to find itself as its own scroll parent
+ */
+
+ i = i->parent ();
+
+ while (i) {
+ ScrollGroup const * sg = dynamic_cast<ScrollGroup const *> (i);
+ if (sg) {
+ last_scroll_group = sg;
+ }
+ i = i->parent();
+ }
+
+ _scroll_parent = const_cast<ScrollGroup*> (last_scroll_group);
+}
+
bool
Item::common_ancestor_within (uint32_t limit, const Item& other) const
{
while (d1 != d2) {
if (d1 > d2) {
+ if (!i1) {
+ return false;
+ }
i1 = i1->parent();
d1--;
limit--;
} else {
+ if (!i2) {
+ return false;
+ }
i2 = i2->parent();
d2--;
limit--;
while (d1 != d2) {
if (d1 > d2) {
+ if (!i1) {
+ return 0;
+ }
i1 = i1->parent();
d1--;
} else {
+ if (!i2) {
+ return 0;
+ }
i2 = i2->parent();
d2--;
}
if (_bounding_box_dirty) {
compute_bounding_box ();
assert (!_bounding_box_dirty);
+ add_child_bounding_boxes ();
}
return _bounding_box;
Item::redraw () const
{
if (_visible && _bounding_box && _canvas) {
- _canvas->request_redraw (item_to_canvas (_bounding_box.get()));
+ _canvas->request_redraw (item_to_window (_bounding_box.get()));
}
}
void
Item::end_change ()
{
- _canvas->item_changed (this, _pre_change_bounding_box);
-
- if (_parent) {
- _parent->child_changed ();
+ if (_visible) {
+ _canvas->item_changed (this, _pre_change_bounding_box);
+
+ if (_parent) {
+ _parent->child_changed ();
+ }
}
}
void
Item::end_visual_change ()
{
- _canvas->item_visual_property_changed (this);
+ if (_visible) {
+ _canvas->item_visual_property_changed (this);
+ }
}
void
_ignore_events = ignore;
}
-void
-Item::dump (ostream& o) const
-{
- boost::optional<ArdourCanvas::Rect> bb = bounding_box();
-
- o << _canvas->indent() << whatami() << ' ' << this << " Visible ? " << _visible;
- o << " @ " << position();
-
-#ifdef CANVAS_DEBUG
- if (!name.empty()) {
- o << ' ' << name;
- }
-#endif
-
- if (bb) {
- o << endl << _canvas->indent() << "\tbbox: " << bb.get();
- o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
- } else {
- o << " bbox unset";
- }
-
- o << endl;
-}
-
std::string
Item::whatami () const
{
bool
Item::covers (Duple const & point) const
{
- Duple p = canvas_to_item (point);
+ Duple p = window_to_item (point);
if (_bounding_box_dirty) {
compute_bounding_box ();
return r.get().contains (p);
}
+/* nesting/grouping API */
+
+void
+Item::render_children (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+ if (_items.empty()) {
+ return;
+ }
+
+ ensure_lut ();
+ std::vector<Item*> items = _lut->get (area);
+
+#ifdef CANVAS_DEBUG
+ if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+ cerr << string_compose ("%1%7 %2 @ %7 render %5 @ %6 %3 items out of %4\n",
+ _canvas->render_indent(), (name.empty() ? string ("[unnamed]") : name), items.size(), _items.size(), area, _position, this,
+ whatami());
+ }
+#endif
+
+ ++render_depth;
+
+ for (std::vector<Item*>::const_iterator i = items.begin(); i != items.end(); ++i) {
+
+ if (!(*i)->visible ()) {
+#ifdef CANVAS_DEBUG
+ if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+ cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] invisible - skipped\n";
+ }
+#endif
+ continue;
+ }
+
+ boost::optional<Rect> item_bbox = (*i)->bounding_box ();
+
+ if (!item_bbox) {
+#ifdef CANVAS_DEBUG
+ if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+ cerr << _canvas->render_indent() << "Item " << (*i)->whatami() << " [" << (*i)->name << "] empty - skipped\n";
+ }
+#endif
+ continue;
+ }
+
+ Rect item = (*i)->item_to_window (item_bbox.get());
+ boost::optional<Rect> d = item.intersection (area);
+
+ if (d) {
+ Rect draw = d.get();
+ if (draw.width() && draw.height()) {
+#ifdef CANVAS_DEBUG
+ if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+ if (dynamic_cast<Container*>(*i) == 0) {
+ cerr << _canvas->render_indent() << "render "
+ << ' '
+ << (*i)
+ << ' '
+ << (*i)->whatami()
+ << ' '
+ << (*i)->name
+ << " item "
+ << item_bbox.get()
+ << " window = "
+ << item
+ << " intersect = "
+ << draw
+ << " @ "
+ << _position
+ << endl;
+ }
+ }
+#endif
+
+ (*i)->render (area, context);
+ ++render_count;
+ }
+
+ } else {
+
+#ifdef CANVAS_DEBUG
+ if (DEBUG_ENABLED(PBD::DEBUG::CanvasRender)) {
+ cerr << string_compose ("%1skip render of %2 %3, no intersection between %4 and %5\n", _canvas->render_indent(), (*i)->whatami(),
+ (*i)->name, item, area);
+ }
+#endif
+
+ }
+ }
+
+ --render_depth;
+}
+
+void
+Item::add_child_bounding_boxes() const
+{
+ boost::optional<Rect> self;
+ Rect bbox;
+ bool have_one = false;
+
+ if (_bounding_box) {
+ bbox = _bounding_box.get();
+ have_one = true;
+ }
+
+ for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
+
+ boost::optional<Rect> item_bbox = (*i)->bounding_box ();
+
+ if (!item_bbox) {
+ continue;
+ }
+
+ Rect group_bbox = (*i)->item_to_parent (item_bbox.get ());
+ if (have_one) {
+ bbox = bbox.extend (group_bbox);
+ } else {
+ bbox = group_bbox;
+ have_one = true;
+ }
+ }
+
+ if (!have_one) {
+ _bounding_box = boost::optional<Rect> ();
+ } else {
+ _bounding_box = bbox;
+ }
+}
+
+void
+Item::add (Item* i)
+{
+ /* XXX should really notify canvas about this */
+
+ _items.push_back (i);
+ i->reparent (this);
+ invalidate_lut ();
+ _bounding_box_dirty = true;
+}
+
+void
+Item::remove (Item* i)
+{
+
+ if (i->parent() != this) {
+ return;
+ }
+
+ /* we cannot call bounding_box() here because that will iterate over
+ _items, one of which (the argument, i) may be in the middle of
+ deletion, making it impossible to call compute_bounding_box()
+ on it.
+ */
+
+ if (_bounding_box) {
+ _pre_change_bounding_box = _bounding_box;
+ } else {
+ _pre_change_bounding_box = Rect();
+ }
+
+ i->unparent ();
+ _items.remove (i);
+ invalidate_lut ();
+ _bounding_box_dirty = true;
+
+ end_change ();
+}
+
+void
+Item::clear (bool with_delete)
+{
+ begin_change ();
+
+ clear_items (with_delete);
+
+ invalidate_lut ();
+ _bounding_box_dirty = true;
+
+ end_change ();
+}
+
+void
+Item::clear_items (bool with_delete)
+{
+ for (list<Item*>::iterator i = _items.begin(); i != _items.end(); ) {
+
+ list<Item*>::iterator tmp = i;
+ Item *item = *i;
+
+ ++tmp;
+
+ /* remove from list before doing anything else, because we
+ * don't want to find the item in _items during any activity
+ * driven by unparent-ing or deletion.
+ */
+
+ _items.erase (i);
+ item->unparent ();
+
+ if (with_delete) {
+ delete item;
+ }
+
+ i = tmp;
+ }
+}
+
+void
+Item::raise_child_to_top (Item* i)
+{
+ if (!_items.empty()) {
+ if (_items.back() == i) {
+ return;
+ }
+ }
+
+ _items.remove (i);
+ _items.push_back (i);
+ invalidate_lut ();
+}
+
+void
+Item::raise_child (Item* i, int levels)
+{
+ list<Item*>::iterator j = find (_items.begin(), _items.end(), i);
+ assert (j != _items.end ());
+
+ ++j;
+ _items.remove (i);
+
+ while (levels > 0 && j != _items.end ()) {
+ ++j;
+ --levels;
+ }
+
+ _items.insert (j, i);
+ invalidate_lut ();
+}
+
+void
+Item::lower_child_to_bottom (Item* i)
+{
+ if (!_items.empty()) {
+ if (_items.front() == i) {
+ return;
+ }
+ }
+ _items.remove (i);
+ _items.push_front (i);
+ invalidate_lut ();
+}
+
+void
+Item::ensure_lut () const
+{
+ if (!_lut) {
+ _lut = new DumbLookupTable (*this);
+ }
+}
+
+void
+Item::invalidate_lut () const
+{
+ delete _lut;
+ _lut = 0;
+}
+
+void
+Item::child_changed ()
+{
+ invalidate_lut ();
+ _bounding_box_dirty = true;
+
+ if (_parent) {
+ _parent->child_changed ();
+ }
+}
+
+void
+Item::add_items_at_point (Duple const point, vector<Item const *>& items) const
+{
+ boost::optional<Rect> const bbox = bounding_box ();
+
+ /* Point is in window coordinate system */
+
+ if (!bbox || !item_to_window (bbox.get()).contains (point)) {
+ return;
+ }
+
+ /* recurse and add any items within our group that contain point.
+ Our children are only considered visible if we are, and similarly
+ only if we do not ignore events.
+ */
+
+ vector<Item*> our_items;
+
+ if (!_items.empty() && visible() && !_ignore_events) {
+ ensure_lut ();
+ our_items = _lut->items_at_point (point);
+ }
+
+ if (!our_items.empty() || covers (point)) {
+ /* this adds this item itself to the list of items at point */
+ items.push_back (this);
+ }
+
+ for (vector<Item*>::iterator i = our_items.begin(); i != our_items.end(); ++i) {
+ (*i)->add_items_at_point (point, items);
+ }
+}
+
+void
+Item::dump (ostream& o) const
+{
+ boost::optional<ArdourCanvas::Rect> bb = bounding_box();
+
+ o << _canvas->indent() << whatami() << ' ' << this << " Visible ? " << _visible;
+ o << " @ " << position();
+
+#ifdef CANVAS_DEBUG
+ if (!name.empty()) {
+ o << ' ' << name;
+ }
+#endif
+
+ if (bb) {
+ o << endl << _canvas->indent() << "\tbbox: " << bb.get();
+ o << endl << _canvas->indent() << "\tCANVAS bbox: " << item_to_canvas (bb.get());
+ } else {
+ o << " bbox unset";
+ }
+
+ o << endl;
+
+ if (!_items.empty()) {
+
+#ifdef CANVAS_DEBUG
+ o << _canvas->indent();
+ o << " @ " << position();
+ o << " Items: " << _items.size();
+ o << " Visible ? " << _visible;
+
+ boost::optional<Rect> bb = bounding_box();
+
+ if (bb) {
+ o << endl << _canvas->indent() << " bbox: " << bb.get();
+ o << endl << _canvas->indent() << " CANVAS bbox: " << item_to_canvas (bb.get());
+ } else {
+ o << " bbox unset";
+ }
+
+ o << endl;
+#endif
+
+ ArdourCanvas::dump_depth++;
+
+ for (list<Item*>::const_iterator i = _items.begin(); i != _items.end(); ++i) {
+ o << **i;
+ }
+
+ ArdourCanvas::dump_depth--;
+ }
+}
+
ostream&
ArdourCanvas::operator<< (ostream& o, const Item& i)
{
using namespace std;
using namespace ArdourCanvas;
-Line::Line (Group* parent)
- : Item (parent)
- , Outline (parent)
+Line::Line (Canvas* c)
+ : Item (c)
{
+}
+Line::Line (Item* parent)
+ : Item (parent)
+{
}
void
bool
Line::covers (Duple const & point) const
{
- const Duple p = canvas_to_item (point);
+ const Duple p = window_to_item (point);
static const Distance threshold = 2.0;
/* this quick check works for vertical and horizontal lines, which are
double t;
Duple a (_points[0]);
Duple b (_points[1]);
- const Rect visible (_canvas->visible_area());
+ const Rect visible (window_to_item (_canvas->visible_area()));
/*
Clamp the line endpoints to the visible area of the canvas. If we do
using namespace std;
using namespace ArdourCanvas;
-/* XXX: hard-wired to horizontal only */
-
class LineSorter {
public:
- bool operator() (LineSet::Line& a, LineSet::Line& b) {
- return a.y < b.y;
+ bool operator() (LineSet::Line const & a, LineSet::Line const & b) {
+ return a.pos < b.pos;
}
};
-LineSet::LineSet (Group* parent)
- : Item (parent)
- , _height (0)
+LineSet::LineSet (Canvas* c, Orientation o)
+ : Item (c)
+ , _extent (0)
+ , _orientation (o)
{
}
+LineSet::LineSet (Item* parent, Orientation o)
+ : Item (parent)
+ , _extent (0)
+ , _orientation (o)
+{
+
+}
void
LineSet::compute_bounding_box () const
if (_lines.empty ()) {
_bounding_box = boost::optional<Rect> ();
} else {
- _bounding_box = Rect (0, _lines.front().y - (_lines.front().width/2.0), COORD_MAX, min (_height, _lines.back().y - (_lines.back().width/2.0)));
+
+ if (_orientation == Horizontal) {
+
+ _bounding_box = Rect (0, /* x0 */
+ _lines.front().pos - (_lines.front().width/2.0), /* y0 */
+ _extent, /* x1 */
+ _lines.back().pos - (_lines.back().width/2.0) /* y1 */
+ );
+
+ } else {
+
+ _bounding_box = Rect (_lines.front().pos - _lines.front().width/2.0, /* x0 */
+ 0, /* y0 */
+ _lines.back().pos + _lines.back().width/2.0, /* x1 */
+ _extent /* y1 */
+ );
+ }
}
+
_bounding_box_dirty = false;
}
void
-LineSet::set_height (Distance height)
+LineSet::set_extent (Distance e)
{
begin_change ();
- _height = height;
-
+ _extent = e;
_bounding_box_dirty = true;
+
end_change ();
}
{
/* area is in window coordinates */
- for (list<Line>::const_iterator i = _lines.begin(); i != _lines.end(); ++i) {
+ for (vector<Line>::const_iterator i = _lines.begin(); i != _lines.end(); ++i) {
+
+ Rect self;
+
+ if (_orientation == Horizontal) {
+ self = item_to_window (Rect (0, i->pos - (i->width/2.0), _extent, i->pos + (i->width/2.0)));
+ } else {
+ self = item_to_window (Rect (i->pos - (i->width/2.0), 0, i->pos + (i->width/2.0), _extent));
+ }
- Rect self = item_to_window (Rect (0, i->y - (i->width/2.0), COORD_MAX, i->y + (i->width/2.0)));
- boost::optional<Rect> intersect = self.intersection (area);
+ boost::optional<Rect> isect = self.intersection (area);
- if (!intersect) {
+ if (!isect) {
continue;
}
+ Rect intersection (isect.get());
+
set_source_rgba (context, i->color);
context->set_line_width (i->width);
- context->move_to (intersect->x0, self.y0 + ((self.y1 - self.y0)/2.0));
- context->line_to (intersect->x1, self.y0 + ((self.y1 - self.y0)/2.0));
+
+ /* Not 100% sure that the computation of the invariant
+ * positions (y and x) below work correctly if the line width
+ * is not 1.0, but visual inspection suggests it is OK.
+ */
+
+ if (_orientation == Horizontal) {
+ double y = self.y0 + ((self.y1 - self.y0)/2.0);
+ context->move_to (intersection.x0, y);
+ context->line_to (intersection.x1, y);
+ } else {
+ double x = self.x0 + ((self.x1 - self.x0)/2.0);
+ context->move_to (x, intersection.y0);
+ context->line_to (x, intersection.y1);
+ }
+
context->stroke ();
}
}
begin_change ();
_lines.push_back (Line (y, width, color));
- _lines.sort (LineSorter ());
+ sort (_lines.begin(), _lines.end(), LineSorter());
_bounding_box_dirty = true;
end_change ();
bool
LineSet::covers (Duple const & /*point*/) const
{
+ /* lines are ordered by position along primary axis, so binary search
+ * to find the place to start looking.
+ *
+ * XXX but not yet.
+ */
+
return false;
}
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "canvas/item.h"
#include "canvas/lookup_table.h"
-#include "canvas/group.h"
using namespace std;
using namespace ArdourCanvas;
-LookupTable::LookupTable (Group const & group)
- : _group (group)
+LookupTable::LookupTable (Item const & item)
+ : _item (item)
{
}
}
-DumbLookupTable::DumbLookupTable (Group const & group)
- : LookupTable (group)
+DumbLookupTable::DumbLookupTable (Item const & item)
+ : LookupTable (item)
{
}
vector<Item *>
DumbLookupTable::get (Rect const &)
{
- list<Item *> const & items = _group.items ();
+ list<Item *> const & items = _item.items ();
vector<Item *> vitems;
copy (items.begin(), items.end(), back_inserter (vitems));
return vitems;
vector<Item *>
DumbLookupTable::items_at_point (Duple const & point) const
{
- /* Point is in canvas coordinate system */
+ /* Point is in window coordinate system */
- list<Item *> const & items (_group.items ());
+ list<Item *> const & items (_item.items ());
vector<Item *> vitems;
for (list<Item *>::const_iterator i = items.begin(); i != items.end(); ++i) {
bool
DumbLookupTable::has_item_at_point (Duple const & point) const
{
- /* Point is in canvas coordinate system */
+ /* Point is in window coordinate system */
- list<Item *> const & items (_group.items ());
+ list<Item *> const & items (_item.items ());
vector<Item *> vitems;
for (list<Item *>::const_iterator i = items.begin(); i != items.end(); ++i) {
return false;
}
-OptimizingLookupTable::OptimizingLookupTable (Group const & group, int items_per_cell)
- : LookupTable (group)
+OptimizingLookupTable::OptimizingLookupTable (Item const & item, int items_per_cell)
+ : LookupTable (item)
, _items_per_cell (items_per_cell)
, _added (false)
{
- list<Item*> const & items = _group.items ();
+ list<Item*> const & items = _item.items ();
/* number of cells */
int const cells = items.size() / _items_per_cell;
_cells[i] = new Cell[_dimension];
}
- /* our group's bounding box in its coordinates */
- boost::optional<Rect> bbox = _group.bounding_box ();
+ /* our item's bounding box in its coordinates */
+ boost::optional<Rect> bbox = _item.bounding_box ();
if (!bbox) {
return;
}
continue;
}
- /* and in the group's coordinates */
- Rect const item_bbox_in_group = (*i)->item_to_parent (item_bbox.get ());
+ /* and in the item's coordinates */
+ Rect const item_bbox_in_item = (*i)->item_to_parent (item_bbox.get ());
int x0, y0, x1, y1;
- area_to_indices (item_bbox_in_group, x0, y0, x1, y1);
+ area_to_indices (item_bbox_in_item, x0, y0, x1, y1);
/* XXX */
assert (x0 >= 0);
//assert (y1 <= _dimension);
if (x0 > _dimension) {
- cout << "WARNING: item outside bbox by " << (item_bbox_in_group.x0 - bbox.get().x0) << "\n";
+ cout << "WARNING: item outside bbox by " << (item_bbox_in_item.x0 - bbox.get().x0) << "\n";
x0 = _dimension;
}
if (x1 > _dimension) {
- cout << "WARNING: item outside bbox by " << (item_bbox_in_group.x1 - bbox.get().x1) << "\n";
+ cout << "WARNING: item outside bbox by " << (item_bbox_in_item.x1 - bbox.get().x1) << "\n";
x1 = _dimension;
}
if (y0 > _dimension) {
- cout << "WARNING: item outside bbox by " << (item_bbox_in_group.y0 - bbox.get().y0) << "\n";
+ cout << "WARNING: item outside bbox by " << (item_bbox_in_item.y0 - bbox.get().y0) << "\n";
y0 = _dimension;
}
if (y1 > _dimension) {
- cout << "WARNING: item outside bbox by " << (item_bbox_in_group.y1 - bbox.get().y1) << "\n";
+ cout << "WARNING: item outside bbox by " << (item_bbox_in_item.y1 - bbox.get().y1) << "\n";
y1 = _dimension;
}
return false;
}
-/** @param area Area in our owning group's coordinates */
+/** @param area Area in our owning item's coordinates */
vector<Item*>
OptimizingLookupTable::get (Rect const & area)
{
#include "pbd/convert.h"
#include "ardour/utils.h"
+
+#include "canvas/item.h"
#include "canvas/outline.h"
#include "canvas/utils.h"
#include "canvas/debug.h"
using namespace ArdourCanvas;
-Outline::Outline (Group* parent)
- : Item (parent)
+Outline::Outline (Item& self)
+ : _self (self)
, _outline_color (0x000000ff)
, _outline_width (1.0)
, _outline (true)
{
-
}
void
Outline::set_outline_color (Color color)
{
if (color != _outline_color) {
- begin_visual_change ();
+ _self.begin_visual_change ();
_outline_color = color;
- end_visual_change ();
+ _self.end_visual_change ();
}
}
Outline::set_outline_width (Distance width)
{
if (width != _outline_width) {
- begin_change ();
+ _self.begin_change ();
_outline_width = width;
- _bounding_box_dirty = true;
- end_change ();
+ _self._bounding_box_dirty = true;
+ _self.end_change ();
}
}
Outline::set_outline (bool outline)
{
if (outline != _outline) {
- begin_change ();
+ _self.begin_change ();
_outline = outline;
- _bounding_box_dirty = true;
- end_change ();
+ _self._bounding_box_dirty = true;
+ _self.end_change ();
}
}
using namespace std;
using namespace ArdourCanvas;
-Pixbuf::Pixbuf (Group* g)
- : Item (g)
+Pixbuf::Pixbuf (Canvas* c)
+ : Item (c)
+{
+}
+
+Pixbuf::Pixbuf (Item* parent)
+ : Item (parent)
{
-
}
void
using namespace std;
using namespace ArdourCanvas;
-PolyItem::PolyItem (Group* parent)
- : Item (parent)
- , Outline (parent)
+PolyItem::PolyItem (Canvas* c)
+ : Item (c)
{
+}
+PolyItem::PolyItem (Item* parent)
+ : Item (parent)
+{
}
void
++i;
}
- _bounding_box = bbox.expand (_outline_width);
+ _bounding_box = bbox.expand (_outline_width + 0.5);
} else {
using namespace ArdourCanvas;
-PolyLine::PolyLine (Group* parent)
- : Item (parent)
- , PolyItem (parent)
+PolyLine::PolyLine (Canvas* c)
+ : PolyItem (c)
, _threshold (1.0)
{
+}
+PolyLine::PolyLine (Item* parent)
+ : PolyItem (parent)
+ , _threshold (1.0)
+{
}
void
bool
PolyLine::covers (Duple const & point) const
{
- Duple p = canvas_to_item (point);
+ Duple p = window_to_item (point);
const Points::size_type npoints = _points.size();
/* repeat for each line segment */
- const Rect visible (_canvas->visible_area());
+ const Rect visible (window_to_item (_canvas->visible_area()));
for (i = 1, j = 0; i < npoints; ++i, ++j) {
using namespace ArdourCanvas;
-Polygon::Polygon (Group* parent)
- : Item (parent)
- , PolyItem (parent)
- , Fill (parent)
+Polygon::Polygon (Canvas* c)
+ : PolyItem (c)
, multiple (0)
, constant (0)
, cached_size (0)
{
+}
+Polygon::Polygon (Item* parent)
+ : PolyItem (parent)
+ , multiple (0)
+ , constant (0)
+ , cached_size (0)
+{
}
Polygon::~Polygon ()
bool
Polygon::covers (Duple const & point) const
{
- Duple p = canvas_to_item (point);
+ Duple p = window_to_item (point);
Points::size_type npoints = _points.size();
using namespace std;
using namespace ArdourCanvas;
-Rectangle::Rectangle (Group* parent)
+Rectangle::Rectangle (Canvas* c)
+ : Item (c)
+ , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
+{
+}
+
+Rectangle::Rectangle (Canvas* c, Rect const & rect)
+ : Item (c)
+ , _rect (rect)
+ , _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
+{
+}
+
+Rectangle::Rectangle (Item* parent)
: Item (parent)
- , Outline (parent)
- , Fill (parent)
, _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
{
}
-Rectangle::Rectangle (Group* parent, Rect const & rect)
+Rectangle::Rectangle (Item* parent, Rect const & rect)
: Item (parent)
- , Outline (parent)
- , Fill (parent)
, _rect (rect)
, _outline_what ((What) (LEFT | RIGHT | TOP | BOTTOM))
{
-
}
void
Rectangle::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
- Rect self = item_to_window (_rect);
+ /* In general, a Rectangle will have a _position of (0,0) within its
+ parent, and its extent is actually defined by _rect. But in the
+ unusual case that _position is set to something other than (0,0),
+ we should take that into account when rendering.
+ */
+ Rect self = item_to_window (_rect.translate (_position));
boost::optional<Rect> r = self.intersection (area);
if (!r) {
using namespace std;
using namespace ArdourCanvas;
-RootGroup::RootGroup (Canvas* canvas)
- : Group (canvas)
+Root::Root (Canvas* canvas)
+ : Container (canvas)
{
#ifdef CANVAS_DEBUG
name = "ROOT";
}
void
-RootGroup::compute_bounding_box () const
+Root::compute_bounding_box () const
{
- Group::compute_bounding_box ();
+ Container::compute_bounding_box ();
if (_bounding_box) {
- _canvas->request_size (Duple (_bounding_box.get().width (), _bounding_box.get().height ()));
+ Rect r (_bounding_box.get());
+ _canvas->request_size (Duple (r.width (), r.height ()));
}
}
--- /dev/null
+/*
+ Copyright (C) 2014 Paul Davis
+ Author: Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <algorithm>
+#include <cairomm/context.h>
+
+#include <pangomm/layout.h>
+
+#include "canvas/ruler.h"
+#include "canvas/types.h"
+#include "canvas/debug.h"
+#include "canvas/utils.h"
+#include "canvas/canvas.h"
+
+using namespace std;
+using namespace ArdourCanvas;
+
+Ruler::Ruler (Canvas* c, const Metric& m)
+ : Rectangle (c)
+ , _metric (m)
+ , _lower (0)
+ , _upper (0)
+ , _need_marks (true)
+{
+}
+
+Ruler::Ruler (Canvas* c, const Metric& m, Rect const& r)
+ : Rectangle (c, r)
+ , _metric (m)
+ , _lower (0)
+ , _upper (0)
+ , _need_marks (true)
+{
+}
+
+Ruler::Ruler (Item* parent, const Metric& m)
+ : Rectangle (parent)
+ , _metric (m)
+ , _lower (0)
+ , _upper (0)
+ , _need_marks (true)
+{
+}
+
+Ruler::Ruler (Item* parent, const Metric& m, Rect const& r)
+ : Rectangle (parent, r)
+ , _metric (m)
+ , _lower (0)
+ , _upper (0)
+ , _need_marks (true)
+{
+}
+
+void
+Ruler::set_range (double l, double u)
+{
+ begin_visual_change ();
+ _lower = l;
+ _upper = u;
+ _need_marks = true;
+ end_visual_change ();
+}
+
+void
+Ruler::set_font_description (Pango::FontDescription fd)
+{
+ begin_visual_change ();
+ _font_description = new Pango::FontDescription (fd);
+ end_visual_change ();
+}
+
+void
+Ruler::render (Rect const & area, Cairo::RefPtr<Cairo::Context> cr) const
+{
+ if (_lower == _upper) {
+ /* nothing to draw */
+ return;
+ }
+
+ Rect self (item_to_window (get()));
+ boost::optional<Rect> i = self.intersection (area);
+
+ if (!i) {
+ return;
+ }
+
+ Rect intersection (i.get());
+
+ Distance height = self.height();
+
+ if (_need_marks) {
+ marks.clear ();
+ _metric.get_marks (marks, _lower, _upper, 50);
+ _need_marks = false;
+ }
+
+ /* draw background */
+
+ setup_fill_context (cr);
+ cr->rectangle (intersection.x0, intersection.y0, intersection.width(), intersection.height());
+ cr->fill ();
+
+ /* switch to outline context */
+
+ setup_outline_context (cr);
+
+ /* draw line on lower edge as a separator */
+
+ if (_outline_width == 1.0) {
+ /* Cairo single pixel line correction */
+ cr->move_to (self.x0, self.y1-0.5);
+ cr->line_to (self.x1, self.y1-0.5);
+ } else {
+ cr->move_to (self.x0, self.y1);
+ cr->line_to (self.x1, self.y1);
+ }
+ cr->stroke ();
+
+ /* draw ticks + text */
+
+ Glib::RefPtr<Pango::Layout> layout = Pango::Layout::create (cr);
+ if (_font_description) {
+ layout->set_font_description (*_font_description);
+ }
+
+ for (vector<Mark>::const_iterator m = marks.begin(); m != marks.end(); ++m) {
+ Duple pos;
+
+ pos.x = floor ((m->position - _lower) / _metric.units_per_pixel);
+ pos.y = self.y1; /* bottom edge */
+
+ if (_outline_width == 1.0) {
+ /* Cairo single pixel line correction */
+ cr->move_to (pos.x + 0.5, pos.y);
+ } else {
+ cr->move_to (pos.x, pos.y);
+ }
+
+ switch (m->style) {
+ case Mark::Major:
+ cr->rel_line_to (0, -height);
+ break;
+ case Mark::Minor:
+ cr->rel_line_to (0, -height/2.0);
+ break;
+ case Mark::Micro:
+ cr->rel_line_to (0, -height/4.0);
+ break;
+ }
+ cr->stroke ();
+
+ /* and the text */
+
+ if (!m->label.empty()) {
+ Pango::Rectangle logical;
+
+ layout->set_text (m->label);
+ logical = layout->get_pixel_logical_extents ();
+
+ cr->move_to (pos.x + 2.0, self.y0 + logical.get_y());
+ layout->show_in_cairo_context (cr);
+ }
+ }
+
+ /* done! */
+}
--- /dev/null
+/*
+ Copyright (C) 2014 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <iostream>
+
+#include "pbd/compose.h"
+
+#include "canvas/canvas.h"
+#include "canvas/debug.h"
+#include "canvas/scroll_group.h"
+
+using namespace std;
+using namespace ArdourCanvas;
+
+ScrollGroup::ScrollGroup (Canvas* c, ScrollSensitivity s)
+ : Container (c)
+ , _scroll_sensitivity (s)
+{
+}
+
+ScrollGroup::ScrollGroup (Item* parent, ScrollSensitivity s)
+ : Container (parent)
+ , _scroll_sensitivity (s)
+{
+}
+
+void
+ScrollGroup::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+ /* clip the draw to the area that this scroll group nominally occupies
+ * WITHOUT scroll offsets in effect
+ */
+
+ boost::optional<Rect> r = bounding_box();
+
+ if (!r) {
+ return;
+ }
+
+ Rect self (_position.x, _position.y, _position.x + r.get().width(), _position.y + r.get().height());
+
+ self.x1 = min (_position.x + _canvas->width(), self.x1);
+ self.y1 = min (_position.y + _canvas->height(), self.y1);
+
+ context->save ();
+ context->rectangle (self.x0, self.y0, self.width(), self.height());
+ context->clip ();
+
+ Container::render (area, context);
+
+ context->restore ();
+}
+
+void
+ScrollGroup::scroll_to (Duple const& d)
+{
+ if (_scroll_sensitivity & ScrollsHorizontally) {
+ _scroll_offset.x = d.x;
+ }
+
+ if (_scroll_sensitivity & ScrollsVertically) {
+ _scroll_offset.y = d.y;
+ }
+}
+
+bool
+ScrollGroup::covers_canvas (Duple const& d) const
+{
+ boost::optional<Rect> r = bounding_box ();
+
+ if (!r) {
+ return false;
+ }
+
+ return r->contains (d);
+}
+
+bool
+ScrollGroup::covers_window (Duple const& d) const
+{
+ boost::optional<Rect> r = bounding_box ();
+
+ if (!r) {
+ return false;
+ }
+
+ Rect w = r->translate (-_scroll_offset);
+
+ return w.contains (d);
+}
PBD::Searchpath StatefulImage::_image_search_path;
StatefulImage::ImageCache StatefulImage::_image_cache;
-StatefulImage::StatefulImage (Group* group, const XMLNode& node)
- : Item (group)
+StatefulImage::StatefulImage (Canvas* c, const XMLNode& node)
+ : Item (c)
, _state (0)
, _font (0)
, _text_x (0)
std::string path;
- if (!find_file_in_search_path (_image_search_path, name, path)) {
+ if (!find_file (_image_search_path, name, path)) {
error << string_compose (_("Image named %1 not found"),
name) << endmsg;
return ImageHandle();
using namespace std;
using namespace ArdourCanvas;
-Text::Text (Group* parent)
- : Item (parent)
+Text::Text (Canvas* c)
+ : Item (c)
, _color (0x000000ff)
, _font_description (0)
, _alignment (Pango::ALIGN_LEFT)
, _need_redraw (false)
, _clamped_width (COORD_MAX)
{
+ _outline = false;
+}
+Text::Text (Item* parent)
+ : Item (parent)
+ , _color (0x000000ff)
+ , _font_description (0)
+ , _alignment (Pango::ALIGN_LEFT)
+ , _width (0)
+ , _height (0)
+ , _need_redraw (false)
+ , _clamped_width (COORD_MAX)
+{
+ _outline = false;
}
Text::~Text ()
}
layout->set_alignment (_alignment);
-
+
int w;
int h;
- layout->get_size (w, h);
-
- _width = w / Pango::SCALE;
- _height = h / Pango::SCALE;
-
+ layout->get_pixel_size (w, h);
+
+ _width = w;
+ _height = h;
+
_image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, _width, _height);
Cairo::RefPtr<Cairo::Context> img_context = Cairo::Context::create (_image);
/* and draw, in the appropriate color of course */
- set_source_rgba (img_context, _color);
-
- layout->show_in_cairo_context (img_context);
+ if (_outline) {
+ set_source_rgba (img_context, _outline_color);
+ layout->update_from_cairo_context (img_context);
+ pango_cairo_layout_path (img_context->cobj(), layout->gobj());
+ img_context->stroke_preserve ();
+ set_source_rgba (img_context, _color);
+ img_context->fill ();
+ } else {
+ set_source_rgba (img_context, _color);
+ layout->show_in_cairo_context (img_context);
+ }
/* text has now been rendered in _image and is ready for blit in
* ::render
}
void
-Text::render (Rect const & /*area*/, Cairo::RefPtr<Cairo::Context> context) const
+Text::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
{
if (_text.empty()) {
return;
}
+ Rect self = item_to_window (Rect (0, 0, min (_clamped_width, (double)_image->get_width ()), _image->get_height ()));
+ boost::optional<Rect> i = self.intersection (area);
+
+ if (!i) {
+ return;
+ }
+
if (_need_redraw) {
redraw (context);
}
- Rect self = item_to_window (Rect (0, 0, min (_clamped_width, _width), _height));
-
- context->rectangle (self.x0, self.y0, self.width(), self.height());
+ Rect intersection (i.get());
+
+ context->rectangle (intersection.x0, intersection.y0, intersection.width(), intersection.height());
context->set_source (_image, self.x0, self.y0);
context->fill ();
}
begin_change ();
_color = color;
+ if (_outline) {
+ set_outline_color (contrasting_text_color (_color));
+ }
_need_redraw = true;
end_change ();
--- /dev/null
+/*
+ Copyright (C) 2011-2013 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <algorithm>
+
+#include "canvas/canvas.h"
+#include "canvas/tracking_text.h"
+
+using namespace ArdourCanvas;
+
+TrackingText::TrackingText (Canvas* c)
+ : Text (c)
+ , track_x (true)
+ , track_y (true)
+ , offset (Duple (10, 10))
+{
+ init ();
+}
+
+TrackingText::TrackingText (Item* p)
+ : Text (p)
+ , track_x (true)
+ , track_y (true)
+ , offset (Duple (10, 10))
+{
+ init ();
+}
+
+void
+TrackingText::init ()
+{
+ _canvas->MouseMotion.connect (sigc::mem_fun (*this, &TrackingText::pointer_motion));
+ set_ignore_events (true);
+ set_outline (true);
+ hide ();
+}
+
+void
+TrackingText::pointer_motion (Duple const & winpos)
+{
+ if (!_visible) {
+ return;
+ }
+
+ Duple pos (_parent->window_to_item (winpos));
+
+ if (!track_x) {
+ pos.x = position().x;
+ }
+
+ if (!track_y) {
+ pos.y = position().y;
+ }
+
+ pos = pos.translate (offset);
+
+ /* keep inside the window */
+
+ Rect r (0, 0, _canvas->width(), _canvas->height());
+
+ /* border of 200 pixels on the right, and 50 on all other sides */
+
+ const double border = 50.0;
+
+ r.x0 += border;
+ r.x1 = std::max (r.x0, (r.x1 - 200.0));
+ r.y0 += border;
+ r.y1 = std::max (r.y0, (r.y1 - border));
+
+ /* clamp */
+
+ if (pos.x < r.x0) {
+ pos.x = r.x0;
+ } else if (pos.x > r.x1) {
+ pos.x = r.x1;
+ }
+
+ if (pos.y < r.y0) {
+ pos.y = r.y0;
+ } else if (pos.y > r.y1) {
+ pos.y = r.y1;
+ }
+
+ /* move */
+
+ set_position (pos);
+}
+
+void
+TrackingText::show_and_track (bool tx, bool ty)
+{
+ track_x = tx;
+ track_y = ty;
+
+ bool was_visible = _visible;
+ show ();
+
+ if (!was_visible) {
+ /* move to current pointer location. do this after show() so that
+ * _visible is true, and thus ::pointer_motion() will do
+ * something.
+ */
+ Duple winpos;
+
+ if (!_canvas->get_mouse_position (winpos)) {
+ return;
+ }
+
+ pointer_motion (winpos);
+ }
+}
+
+void
+TrackingText::set_x_offset (double o)
+{
+ offset.x = o;
+}
+
+void
+TrackingText::set_y_offset (double o)
+{
+ offset.y = o;
+}
+
+void
+TrackingText::set_offset (Duple const & d)
+{
+ offset = d;
+}
return ((dpqx * dpqx) + (dpqy * dpqy));
}
+uint32_t
+ArdourCanvas::contrasting_text_color (uint32_t c)
+{
+ double r, g, b, a;
+ ArdourCanvas::color_to_rgba (c, r, g, b, a);
+
+ const double black_r = 0.0;
+ const double black_g = 0.0;
+ const double black_b = 0.0;
+
+ const double white_r = 1.0;
+ const double white_g = 1.0;
+ const double white_b = 1.0;
+
+ /* Use W3C contrast guideline calculation */
+
+ double white_contrast = (max (r, white_r) - min (r, white_r)) +
+ (max (g, white_g) - min (g, white_g)) +
+ (max (b, white_b) - min (b, white_b));
+
+ double black_contrast = (max (r, black_r) - min (r, black_r)) +
+ (max (g, black_g) - min (g, black_g)) +
+ (max (b, black_b) - min (b, black_b));
+
+ if (white_contrast > black_contrast) {
+ /* use white */
+ return ArdourCanvas::rgba_to_color (1.0, 1.0, 1.0, 1.0);
+ } else {
+ /* use black */
+ return ArdourCanvas::rgba_to_color (0.0, 0.0, 0.0, 1.0);
+ }
+}
using namespace ARDOUR;
using namespace ArdourCanvas;
+#define CACHE_HIGH_WATER (2)
+
+std::map <boost::shared_ptr<AudioSource>, std::vector<WaveView::CacheEntry> > WaveView::_image_cache;
double WaveView::_global_gradient_depth = 0.6;
bool WaveView::_global_logscaled = false;
WaveView::Shape WaveView::_global_shape = WaveView::Normal;
PBD::Signal0<void> WaveView::VisualPropertiesChanged;
PBD::Signal0<void> WaveView::ClipLevelChanged;
-WaveView::WaveView (Group* parent, boost::shared_ptr<ARDOUR::AudioRegion> region)
+WaveView::WaveView (Canvas* c, boost::shared_ptr<ARDOUR::AudioRegion> region)
+ : Item (c)
+ , _region (region)
+ , _channel (0)
+ , _samples_per_pixel (0)
+ , _height (64)
+ , _show_zero (false)
+ , _zero_color (0xff0000ff)
+ , _clip_color (0xff0000ff)
+ , _logscaled (_global_logscaled)
+ , _shape (_global_shape)
+ , _gradient_depth (_global_gradient_depth)
+ , _shape_independent (false)
+ , _logscaled_independent (false)
+ , _gradient_depth_independent (false)
+ , _amplitude_above_axis (1.0)
+ , _region_amplitude (_region->scale_amplitude ())
+ , _region_start (region->start())
+{
+ VisualPropertiesChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_visual_property_change, this));
+ ClipLevelChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_clip_level_change, this));
+}
+
+WaveView::WaveView (Item* parent, boost::shared_ptr<ARDOUR::AudioRegion> region)
: Item (parent)
- , Outline (parent)
- , Fill (parent)
, _region (region)
, _channel (0)
, _samples_per_pixel (0)
, _logscaled_independent (false)
, _gradient_depth_independent (false)
, _amplitude_above_axis (1.0)
+ , _region_amplitude (_region->scale_amplitude ())
, _region_start (region->start())
- , _sample_start (-1)
- , _sample_end (-1)
{
VisualPropertiesChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_visual_property_change, this));
+ ClipLevelChanged.connect_same_thread (invalidation_connection, boost::bind (&WaveView::handle_clip_level_change, this));
}
WaveView::~WaveView ()
{
+ invalidate_image_cache ();
}
void
}
if (changed) {
- invalidate_image ();
+ begin_visual_change ();
+ invalidate_image_cache ();
+ end_visual_change ();
}
}
+void
+WaveView::handle_clip_level_change ()
+{
+ begin_visual_change ();
+ invalidate_image_cache ();
+ end_visual_change ();
+}
+
void
WaveView::set_fill_color (Color c)
{
if (c != _fill_color) {
- invalidate_image ();
+ begin_visual_change ();
+ invalidate_image_cache ();
Fill::set_fill_color (c);
+ end_visual_change ();
}
}
WaveView::set_outline_color (Color c)
{
if (c != _outline_color) {
- invalidate_image ();
+ begin_visual_change ();
+ invalidate_image_cache ();
Outline::set_outline_color (c);
+ end_visual_change ();
}
}
if (samples_per_pixel != _samples_per_pixel) {
begin_change ();
+ invalidate_image_cache ();
_samples_per_pixel = samples_per_pixel;
-
_bounding_box_dirty = true;
end_change ();
-
- invalidate_image ();
}
}
void
WaveView::set_clip_level (double dB)
{
- _clip_level = dB_to_coefficient (dB);
- ClipLevelChanged ();
+ const double clip_level = dB_to_coefficient (dB);
+ if (clip_level != _clip_level) {
+ _clip_level = clip_level;
+ ClipLevelChanged ();
+ }
}
-struct LineTips {
- double top;
- double bot;
- bool clip_max;
- bool clip_min;
-
- LineTips() : top (0.0), bot (0.0), clip_max (false), clip_min (false) {}
-};
+void
+WaveView::invalidate_image_cache ()
+{
+ vector <uint32_t> deletion_list;
+ vector <CacheEntry> caches;
+
+ if (_image_cache.find (_region->audio_source ()) != _image_cache.end ()) {
+ caches = _image_cache.find (_region->audio_source ())->second;
+ } else {
+ return;
+ }
+
+ for (uint32_t i = 0; i < caches.size (); ++i) {
+
+ if (_channel != caches[i].channel
+ || _height != caches[i].height
+ || _region_amplitude != caches[i].amplitude
+ || _fill_color != caches[i].fill_color
+ || _outline_color != caches[i].outline_color) {
+
+ continue;
+ }
+
+ deletion_list.push_back (i);
+
+ }
+
+ while (deletion_list.size() > 0) {
+ caches[deletion_list.back ()].image.clear ();
+ caches.erase (caches.begin() + deletion_list.back());
+ deletion_list.pop_back();
+ }
+
+ if (caches.size () == 0) {
+ _image_cache.erase(_region->audio_source ());
+ } else {
+ _image_cache[_region->audio_source ()] = caches;
+ }
+
+}
void
-WaveView::draw_image (PeakData* _peaks, int n_peaks) const
+WaveView::consolidate_image_cache () const
+{
+ list <uint32_t> deletion_list;
+ vector <CacheEntry> caches;
+ uint32_t other_entries = 0;
+
+ if (_image_cache.find (_region->audio_source ()) != _image_cache.end ()) {
+ caches = _image_cache.find (_region->audio_source ())->second;
+ }
+
+ for (uint32_t i = 0; i < caches.size (); ++i) {
+
+ if (_channel != caches[i].channel
+ || _height != caches[i].height
+ || _region_amplitude != caches[i].amplitude
+ || _fill_color != caches[i].fill_color
+ || _outline_color != caches[i].outline_color) {
+
+ other_entries++;
+ continue;
+ }
+
+ framepos_t segment_start = caches[i].start;
+ framepos_t segment_end = caches[i].end;
+
+ for (uint32_t j = i; j < caches.size (); ++j) {
+
+ if (i == j || _channel != caches[j].channel
+ || _height != caches[i].height
+ || _region_amplitude != caches[i].amplitude
+ || _fill_color != caches[i].fill_color
+ || _outline_color != caches[i].outline_color) {
+
+ continue;
+ }
+
+ if (caches[j].start >= segment_start && caches[j].end <= segment_end) {
+
+ deletion_list.push_back (j);
+ }
+ }
+ }
+
+ deletion_list.sort ();
+ deletion_list.unique ();
+
+ while (deletion_list.size() > 0) {
+ caches[deletion_list.back ()].image.clear ();
+ caches.erase (caches.begin() + deletion_list.back ());
+ deletion_list.pop_back();
+ }
+
+ /* We don't care if this channel/height/amplitude has anything in the cache - just drop the Last Added entries
+ until we reach a size where there is a maximum of CACHE_HIGH_WATER + other entries.
+ */
+
+ while (caches.size() > CACHE_HIGH_WATER + other_entries) {
+ caches.front ().image.clear ();
+ caches.erase(caches.begin ());
+ }
+
+ if (caches.size () == 0) {
+ _image_cache.erase (_region->audio_source ());
+ } else {
+ _image_cache[_region->audio_source ()] = caches;
+ }
+}
+
+Coord
+WaveView::y_extent (double s, bool round_to_lower_edge) const
{
- _image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, n_peaks, _height);
+ /* it is important that this returns an integral value, so that we
+ can ensure correct single pixel behaviour.
+ */
+
+ Coord pos;
+
+ switch (_shape) {
+ case Rectified:
+ if (round_to_lower_edge) {
+ pos = ceil (_height - (s * _height));
+ } else {
+ pos = floor (_height - (s * _height));
+ }
+ break;
+ default:
+ if (round_to_lower_edge) {
+ pos = ceil ((1.0-s) * (_height/2.0));
+ } else {
+ pos = floor ((1.0-s) * (_height/2.0));
+ }
+ break;
+ }
- Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create (_image);
+ return min (_height, (max (0.0, pos)));
+}
+struct LineTips {
+ double top;
+ double bot;
+ double spread;
+ bool clip_max;
+ bool clip_min;
+
+ LineTips() : top (0.0), bot (0.0), clip_max (false), clip_min (false) {}
+};
+
+void
+WaveView::draw_image (Cairo::RefPtr<Cairo::ImageSurface>& image, PeakData* _peaks, int n_peaks) const
+{
+ Cairo::RefPtr<Cairo::Context> context = Cairo::Context::create (image);
boost::scoped_array<LineTips> tips (new LineTips[n_peaks]);
/* Clip level nominally set to -0.9dBFS to account for inter-sample
has been scaled by scale_amplitude() already.
*/
- const double clip_level = _clip_level * _region->scale_amplitude();
+ const double clip_level = _clip_level * _region_amplitude;
if (_shape == WaveView::Rectified) {
if (_logscaled) {
for (int i = 0; i < n_peaks; ++i) {
+
tips[i].bot = height();
- tips[i].top = position (alt_log_meter (fast_coefficient_to_dB (max (fabs (_peaks[i].max), fabs (_peaks[i].min)))));
+ const double p = alt_log_meter (fast_coefficient_to_dB (max (fabs (_peaks[i].max), fabs (_peaks[i].min))));
+ tips[i].top = y_extent (p, false);
+ tips[i].spread = (1.0 - p) * _height;
if (fabs (_peaks[i].max) >= clip_level) {
tips[i].clip_max = true;
tips[i].clip_min = true;
}
}
+
} else {for (int i = 0; i < n_peaks; ++i) {
+
tips[i].bot = height();
- tips[i].top = position (max (fabs (_peaks[i].max), fabs (_peaks[i].min)));
+ tips[i].top = y_extent (max (fabs (_peaks[i].max), fabs (_peaks[i].min)), true);
+ tips[i].spread = (1.0 - fabs(_peaks[i].max - _peaks[i].min)) * _height;
if (fabs (_peaks[i].max) >= clip_level) {
tips[i].clip_max = true;
if (_logscaled) {
for (int i = 0; i < n_peaks; ++i) {
- Coord top = _peaks[i].min;
- Coord bot = _peaks[i].max;
+ double top = _peaks[i].min;
+ double bot = _peaks[i].max;
if (fabs (top) >= clip_level) {
tips[i].clip_max = true;
}
- if (fabs (bot) >= clip_level) {
+ if (fabs (top) >= clip_level) {
tips[i].clip_min = true;
}
if (top > 0.0) {
- top = position (alt_log_meter (fast_coefficient_to_dB (top)));
+ top = alt_log_meter (fast_coefficient_to_dB (top));
} else if (top < 0.0) {
- top = position (-alt_log_meter (fast_coefficient_to_dB (-top)));
+ top =-alt_log_meter (fast_coefficient_to_dB (-top));
} else {
- top = position (0.0);
+ top = 0.0;
}
if (bot > 0.0) {
- bot = position (alt_log_meter (fast_coefficient_to_dB (bot)));
+ bot = alt_log_meter (fast_coefficient_to_dB (bot));
} else if (bot < 0.0) {
- bot = position (-alt_log_meter (fast_coefficient_to_dB (-bot)));
+ bot = -alt_log_meter (fast_coefficient_to_dB (-bot));
} else {
- bot = position (0.0);
+ bot = 0.0;
}
- tips[i].top = top;
- tips[i].bot = bot;
+ tips[i].spread = fabs (((1.0 - top) * (_height/2.0)) - ((1.0 - bot) * _height/2.0));
+ tips[i].top = y_extent (top, false);
+ tips[i].bot = y_extent (bot, true);
}
} else {
tips[i].clip_min = true;
}
- tips[i].top = position (_peaks[i].min);
- tips[i].bot = position (_peaks[i].max);
-
-
+ tips[i].top = y_extent (_peaks[i].min, false);
+ tips[i].bot = y_extent (_peaks[i].max, true);
+ tips[i].spread = fabs (((1.0 - _peaks[i].max) * (_height/2.0)) - ((1.0 - _peaks[i].min) * (_height/2.0)));
}
+
}
}
context->set_line_width (0.5);
context->translate (0.5, 0.0);
- /* draw the lines */
+ /* the height of the clip-indicator should be at most 7 pixels,
+ * or 5% of the height of the waveview item.
+ */
+
+ const double clip_height = min (7.0, ceil (_height * 0.05));
+
+ /* There are 3 possible components to draw at each x-axis position: the
+ waveform "line", the zero line and an outline/clip indicator. We
+ have to decide which of the 3 to draw at each position, pixel by
+ pixel. This makes the rendering less efficient but it is the only
+ way I can see to do this correctly.
+
+ With only 1 pixel of spread between the top and bottom of the line,
+ we just draw the upper outline/clip indicator.
+
+ With 2 pixels of spread, we draw the upper and lower outline clip
+ indicators.
+
+ With 3 pixels of spread we draw the upper and lower outline/clip
+ indicators and at least 1 pixel of the waveform line.
+
+ With 5 pixels of spread, we draw all components.
+
+ We can do rectified as two separate passes because we have a much
+ easier decision regarding whether to draw the waveform line. We
+ always draw the clip/outline indicators.
+ */
if (_shape == WaveView::Rectified) {
+
for (int i = 0; i < n_peaks; ++i) {
- context->move_to (i, tips[i].top); /* down 1 pixel */
- context->line_to (i, tips[i].bot);
- context->stroke ();
- }
- } else {
- for (int i = 0; i < n_peaks; ++i) {
- context->move_to (i, tips[i].top);
- context->line_to (i, tips[i].bot);
- context->stroke ();
+
+ /* waveform line */
+
+ if (tips[i].spread >= 2.0) {
+ context->move_to (i, tips[i].top);
+ context->line_to (i, tips[i].bot);
+ }
}
- }
- /* now add dots to the top and bottom of each line (this is
- * modelled on pyramix, except that we add clipping indicators.
- */
+ context->stroke ();
+
+ /* outline/clip indicators */
- if (_global_show_waveform_clipping) {
-
set_source_rgba (context, _outline_color);
- /* the height of the clip-indicator should be at most 7 pixels,
- or 5% of the height of the waveview item.
- */
- const double clip_height = min (7.0, ceil (_height * 0.05));
-
for (int i = 0; i < n_peaks; ++i) {
+
context->move_to (i, tips[i].top);
- bool show_top_clip = (_shape == WaveView::Rectified && (tips[i].clip_max || tips[i].clip_min)) ||
- tips[i].clip_max;
-
- if (show_top_clip) {
+ if (_global_show_waveform_clipping && (tips[i].clip_max || tips[i].clip_min)) {
+ /* clip-indicating upper terminal line */
set_source_rgba (context, _clip_color);
- context->rel_line_to (0, clip_height);
+ context->rel_line_to (0, min (clip_height, floor (tips[i].spread)));
context->stroke ();
set_source_rgba (context, _outline_color);
} else {
+ /* normal upper terminal dot */
+ context->rel_line_to (0, 1.0);
+ context->stroke ();
+ }
+ }
+
+ } else {
+
+ /* Note the use of cairo save/restore pairs to retain the drawing
+ context for the waveform lines, which is already set
+ correctly when we reach here.
+ */
+
+ for (int i = 0; i < n_peaks; ++i) {
+
+ /* waveform line */
+
+ if (tips[i].spread >= 3.0) {
+ context->move_to (i, tips[i].top);
+ context->line_to (i, tips[i].bot);
+ context->stroke ();
+ }
+
+ /* zero line */
+
+ if (tips[i].spread >= 5.0 && show_zero_line()) {
+ context->save ();
+ set_source_rgba (context, _zero_color);
+ context->move_to (i, _height/2.0);
+ context->rel_line_to (0, 0.5);
+ context->stroke ();
+ context->restore ();
+ }
+
+ context->save ();
+
+ /* upper outline/clip indicator */
+
+ context->move_to (i, tips[i].top);
+ if (_global_show_waveform_clipping && ((_shape == WaveView::Rectified && (tips[i].clip_max || tips[i].clip_min)) || tips[i].clip_max)) {
+ /* clip-indicating upper terminal line */
+ set_source_rgba (context, _clip_color);
+ context->rel_line_to (0, min (clip_height, floor (tips[i].spread)));
+ context->stroke ();
+ } else {
+ /* normal upper terminal dot */
+ set_source_rgba (context, _outline_color);
context->rel_line_to (0, 1.0);
context->stroke ();
}
- if (_shape != WaveView::Rectified) {
+ context->restore ();
+
+ if (tips[i].spread >= 2.0) {
+
+ /* lower outline/clip indicator */
+
+ context->save ();
context->move_to (i, tips[i].bot);
- if (tips[i].clip_min) {
+ if (_global_show_waveform_clipping && _shape != WaveView::Rectified && tips[i].clip_min) {
+ /* clip-indicating lower terminal line */
set_source_rgba (context, _clip_color);
- context->rel_line_to (0, -clip_height);
+ context->rel_line_to (0, -(min (clip_height, floor (tips[i].spread))));
context->stroke ();
- set_source_rgba (context, _outline_color);
} else {
+ /* normal lower terminal dot */
+ set_source_rgba (context, _outline_color);
context->rel_line_to (0, -1.0);
context->stroke ();
}
+
+ context->restore ();
}
}
}
-
- if (show_zero_line()) {
-
- set_source_rgba (context, _zero_color);
- context->set_line_width (1.0);
- context->move_to (0, position (0.0) + 0.5);
- context->line_to (n_peaks, position (0.0) + 0.5);
- context->stroke ();
- }
}
void
-WaveView::ensure_cache (framepos_t start, framepos_t end) const
+WaveView::get_image (Cairo::RefPtr<Cairo::ImageSurface>& image, framepos_t start, framepos_t end, double& image_offset) const
{
- if (_image && _sample_start <= start && _sample_end >= end) {
- /* cache already covers required range, do nothing */
- return;
+ vector <CacheEntry> caches;
+
+ if (_image_cache.find (_region->audio_source ()) != _image_cache.end ()) {
+
+ caches = _image_cache.find (_region->audio_source ())->second;
}
+ /* Find a suitable ImageSurface.
+ */
+ for (uint32_t i = 0; i < caches.size (); ++i) {
+
+ if (_channel != caches[i].channel
+ || _height != caches[i].height
+ || _region_amplitude != caches[i].amplitude
+ || _fill_color != caches[i].fill_color
+ || _outline_color != caches[i].outline_color) {
+
+ continue;
+ }
+
+ framepos_t segment_start = caches[i].start;
+ framepos_t segment_end = caches[i].end;
+
+ if (end <= segment_end && start >= segment_start) {
+ image_offset = (segment_start - _region->start()) / _samples_per_pixel;
+ image = caches[i].image;
+
+ return;
+ }
+ }
+
+ consolidate_image_cache ();
+
/* sample position is canonical here, and we want to generate
* an image that spans about twice the canvas width
*/
const framepos_t center = start + ((end - start) / 2);
- const framecnt_t canvas_samples = 2 * (_canvas->visible_area().width() * _samples_per_pixel);
+ const framecnt_t canvas_samples = _canvas->visible_area().width() * _samples_per_pixel; /* one canvas width */
/* we can request data from anywhere in the Source, between 0 and its length
*/
- _sample_start = max ((framepos_t) 0, (center - canvas_samples));
- _sample_end = min (center + canvas_samples, _region->source_length (0));
+ framepos_t sample_start = max ((framepos_t) 0, (center - canvas_samples));
+ framepos_t sample_end = min (center + canvas_samples, _region->source_length (0));
- const int n_peaks = llrintf ((_sample_end - _sample_start)/ (double) _samples_per_pixel);
+ const int n_peaks = llrintf ((sample_end - sample_start)/ (double) _samples_per_pixel);
boost::scoped_array<ARDOUR::PeakData> peaks (new PeakData[n_peaks]);
_region->read_peaks (peaks.get(), n_peaks,
- _sample_start, _sample_end - _sample_start,
+ sample_start, sample_end - sample_start,
_channel,
_samples_per_pixel);
- draw_image (peaks.get(), n_peaks);
+ image = Cairo::ImageSurface::create (Cairo::FORMAT_ARGB32, ((double)(sample_end - sample_start)) / _samples_per_pixel, _height);
+
+ draw_image (image, peaks.get(), n_peaks);
+
+ _image_cache[_region->audio_source ()].push_back (CacheEntry (_channel, _height, _region_amplitude, _fill_color, _outline_color, sample_start, sample_end, image));
+
+ image_offset = (sample_start - _region->start()) / _samples_per_pixel;
+
+ //cerr << "_image_cache size is : " << _image_cache.size() << " entries for this audiosource : " << _image_cache.find (_region->audio_source ())->second.size() << endl;
+
+ return;
}
void
return;
}
- Rect self = item_to_window (Rect (0.0, 0.0, _region->length() / _samples_per_pixel, _height));
+ Rect self = item_to_window (Rect (0.5, 0.0, _region->length() / _samples_per_pixel, _height));
boost::optional<Rect> d = self.intersection (area);
if (!d) {
// cerr << "Sample space: " << sample_start << " .. " << sample_end << endl;
- ensure_cache (sample_start, sample_end);
-
- // cerr << "Cache contains " << _cache->pixel_start() << " .. " << _cache->pixel_end() << " / "
- // << _cache->sample_start() << " .. " << _cache->sample_end()
- // << endl;
+ Cairo::RefPtr<Cairo::ImageSurface> image;
+ double image_offset = 0;
- double image_offset = (_sample_start - _region->start()) / _samples_per_pixel;
+ get_image (image, sample_start, sample_end, image_offset);
// cerr << "Offset into image to place at zero: " << image_offset << endl;
y = round (y);
context->device_to_user (x, y);
- context->set_source (_image, x, y);
+ context->set_source (image, x, y);
context->fill ();
}
{
if (height != _height) {
begin_change ();
-
+
+ invalidate_image_cache ();
_height = height;
-
+
_bounding_box_dirty = true;
end_change ();
-
- invalidate_image ();
}
}
{
if (channel != _channel) {
begin_change ();
-
+
+ invalidate_image_cache ();
_channel = channel;
-
+
_bounding_box_dirty = true;
end_change ();
-
- invalidate_image ();
}
}
-void
-WaveView::invalidate_image ()
-{
- begin_visual_change ();
-
- _image.clear ();
- _sample_start = -1;
- _sample_end = -1;
-
- end_visual_change ();
-}
-
-
void
WaveView::set_logscaled (bool yn)
{
if (_logscaled != yn) {
+ begin_visual_change ();
+ invalidate_image_cache ();
_logscaled = yn;
- invalidate_image ();
+ end_visual_change ();
}
}
void
WaveView::gain_changed ()
{
- invalidate_image ();
+ begin_visual_change ();
+ invalidate_image_cache ();
+ _region_amplitude = _region->scale_amplitude ();
+ end_visual_change ();
}
void
WaveView::set_zero_color (Color c)
{
if (_zero_color != c) {
+ begin_visual_change ();
+ invalidate_image_cache ();
_zero_color = c;
- invalidate_image ();
+ end_visual_change ();
}
}
WaveView::set_clip_color (Color c)
{
if (_clip_color != c) {
+ begin_visual_change ();
+ invalidate_image_cache ();
_clip_color = c;
- invalidate_image ();
+ end_visual_change ();
}
}
WaveView::set_show_zero_line (bool yn)
{
if (_show_zero != yn) {
+ begin_visual_change ();
+ invalidate_image_cache ();
_show_zero = yn;
- invalidate_image ();
+ end_visual_change ();
}
}
WaveView::set_shape (Shape s)
{
if (_shape != s) {
+ begin_visual_change ();
+ invalidate_image_cache ();
_shape = s;
- invalidate_image ();
+ end_visual_change ();
}
}
WaveView::set_amplitude_above_axis (double a)
{
if (_amplitude_above_axis != a) {
+ begin_visual_change ();
+ invalidate_image_cache ();
_amplitude_above_axis = a;
- invalidate_image ();
+ end_visual_change ();
}
}
end_change ();
}
-Coord
-WaveView::position (double s) const
-{
- /* it is important that this returns an integral value, so that we
- can ensure correct single pixel behaviour.
- */
-
- Coord pos;
-
- switch (_shape) {
- case Rectified:
- pos = floor (_height - (s * _height));
- default:
- pos = floor ((1.0-s) * (_height / 2.0));
- break;
- }
-
- return min (_height, (max (0.0, pos)));
-}
-
void
WaveView::set_global_gradient_depth (double depth)
{
--- /dev/null
+/*
+ Copyright (C) 2014 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <iostream>
+#include <cairomm/context.h>
+#include "pbd/stacktrace.h"
+#include "pbd/compose.h"
+
+#include "canvas/canvas.h"
+#include "canvas/widget.h"
+#include "canvas/debug.h"
+#include "canvas/utils.h"
+
+using namespace std;
+using namespace ArdourCanvas;
+
+Widget::Widget (Canvas* c, CairoWidget& w)
+ : Item (c)
+ , _widget (w)
+{
+ Event.connect (sigc::mem_fun (*this, &Widget::event_proxy));
+}
+
+Widget::Widget (Item* parent, CairoWidget& w)
+ : Item (parent)
+ , _widget (w)
+{
+ Event.connect (sigc::mem_fun (*this, &Widget::event_proxy));
+}
+
+bool
+Widget::event_proxy (GdkEvent* ev)
+{
+ /* XXX need to translate coordinate into widget's own coordinate space */
+ return _widget.event (ev);
+}
+
+void
+Widget::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+ // std::cerr << "Render widget\n";
+
+ if (!_bounding_box) {
+ std::cerr << "no bbox\n";
+ return;
+ }
+
+ Rect self = item_to_window (_bounding_box.get());
+ boost::optional<Rect> r = self.intersection (area);
+
+ if (!r) {
+ std::cerr << "no intersection\n";
+ return;
+ }
+
+ Rect draw = r.get ();
+ cairo_rectangle_t crect;
+ crect.x = draw.x0;
+ crect.y = draw.y0;
+ crect.height = draw.height();
+ crect.width = draw.width();
+
+ // std::cerr << "will draw " << draw << "\n";
+ context->save ();
+ context->translate (-draw.x0, -draw.y0);
+ //context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
+ // context->clip ();
+
+ _widget.render (context->cobj(), &crect);
+
+ context->restore ();
+}
+
+void
+Widget::compute_bounding_box () const
+{
+ std::cerr << "cbbox for widget\n";
+
+ GtkRequisition req;
+ Gtk::Allocation alloc;
+
+ _widget.size_request (req);
+
+ std::cerr << "widget wants " << req.width << " x " << req.height << "\n";
+
+ _bounding_box = Rect (0, 0, req.width, req.height);
+
+ /* make sure the widget knows that it got what it asked for */
+ alloc.set_x (0);
+ alloc.set_y (0);
+ alloc.set_width (req.width);
+ alloc.set_height (req.height);
+
+ _widget.size_allocate (alloc);
+
+ _bounding_box_dirty = false;
+}
+
'arrow.cc',
'canvas.cc',
'circle.cc',
+ 'container.cc',
'curve.cc',
'debug.cc',
- 'drag_handle.cc',
'item.cc',
'fill.cc',
'flag.cc',
- 'group.cc',
'image.cc',
'line.cc',
'line_set.cc',
'polygon.cc',
'rectangle.cc',
'root_group.cc',
+ 'ruler.cc',
+ 'scroll_group.cc',
'stateful_image.cc',
'text.cc',
+ 'tracking_text.cc',
'types.cc',
'utils.cc',
- 'wave_view.cc'
+ 'wave_view.cc',
+ 'widget.cc',
+ 'xfade_curve.cc',
]
def options(opt):
--- /dev/null
+/*
+ Copyright (C) 2013 Paul Davis
+ Copyright (C) 2014 Robin Gareus <robin@gareus.org>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <cmath>
+#include <exception>
+#include <algorithm>
+
+#include "canvas/xfade_curve.h"
+#include "canvas/interpolated_curve.h"
+#include "canvas/utils.h"
+
+using namespace ArdourCanvas;
+using std::min;
+using std::max;
+
+XFadeCurve::XFadeCurve (Canvas* c)
+ : Item (c)
+ , points_per_segment (32)
+ , _xfadeposition (Start)
+ , _outline_color (0x000000ff)
+ , _fill_color (0x22448880)
+{
+}
+
+XFadeCurve::XFadeCurve (Canvas* c, XFadePosition pos)
+ : Item (c)
+ , points_per_segment (32)
+ , _xfadeposition (pos)
+ , _outline_color (0x000000ff)
+ , _fill_color (0x22448880)
+{
+}
+
+XFadeCurve::XFadeCurve (Item* parent)
+ : Item (parent)
+ , points_per_segment (32)
+ , _xfadeposition (Start)
+ , _outline_color (0x000000ff)
+ , _fill_color (0x22448880)
+{
+}
+
+XFadeCurve::XFadeCurve (Item* parent, XFadePosition pos)
+ : Item (parent)
+ , points_per_segment (32)
+ , _xfadeposition (pos)
+ , _outline_color (0x000000ff)
+ , _fill_color (0x22448880)
+{
+}
+
+void
+XFadeCurve::compute_bounding_box () const
+{
+ if (!_in.points.empty() && !_out.points.empty()) {
+
+ Rect bbox;
+ Points::const_iterator i;
+
+ if (!_in.points.empty()) {
+ i = _in.points.begin();
+ bbox.x0 = bbox.x1 = i->x;
+ bbox.y0 = bbox.y1 = i->y;
+
+ ++i;
+
+ while (i != _in.points.end()) {
+ bbox.x0 = min (bbox.x0, i->x);
+ bbox.y0 = min (bbox.y0, i->y);
+ bbox.x1 = max (bbox.x1, i->x);
+ bbox.y1 = max (bbox.y1, i->y);
+ ++i;
+ }
+ } else {
+ i = _out.points.begin();
+ bbox.x0 = bbox.x1 = i->x;
+ bbox.y0 = bbox.y1 = i->y;
+ }
+
+ if (!_out.points.empty()) {
+ i = _out.points.begin();
+ while (i != _out.points.end()) {
+ bbox.x0 = min (bbox.x0, i->x);
+ bbox.y0 = min (bbox.y0, i->y);
+ bbox.x1 = max (bbox.x1, i->x);
+ bbox.y1 = max (bbox.y1, i->y);
+ ++i;
+ }
+ }
+
+ _bounding_box = bbox.expand (1.0);
+
+ } else {
+ _bounding_box = boost::optional<Rect> ();
+ }
+
+ _bounding_box_dirty = false;
+}
+
+void
+XFadeCurve::set_inout (Points const & in, Points const & out)
+{
+ if (_in.points == in && _out.points == out) {
+ return;
+ }
+ begin_change ();
+ _in.points = in;
+ _out.points = out;
+ _bounding_box_dirty = true;
+ interpolate ();
+ end_change ();
+}
+
+void
+XFadeCurve::set_points_per_segment (uint32_t n)
+{
+ points_per_segment = n;
+ interpolate ();
+ redraw ();
+}
+
+void
+XFadeCurve::interpolate ()
+{
+ _in.samples.clear ();
+ InterpolatedCurve::interpolate (_in.points, points_per_segment, CatmullRomCentripetal, false, _in.samples);
+ _in.n_samples = _in.samples.size();
+
+ _out.samples.clear ();
+ InterpolatedCurve::interpolate (_out.points, points_per_segment, CatmullRomCentripetal, false, _out.samples);
+ _out.n_samples = _out.samples.size();
+}
+
+Cairo::Path *
+XFadeCurve::get_path(Rect const & area, Cairo::RefPtr<Cairo::Context> context, CanvasCurve const &c) const
+{
+ assert(c.points.size() > 1);
+ context->begin_new_path ();
+ Duple window_space;
+
+ if (c.points.size () == 2) {
+
+ window_space = item_to_window (c.points.front(), false);
+ context->move_to (window_space.x, window_space.y);
+ window_space = item_to_window (c.points.back(), false);
+ context->line_to (window_space.x, window_space.y);
+
+ } else {
+
+ /* find left and right-most sample */
+ Points::size_type left = 0;
+ Points::size_type right = c.n_samples;
+
+ for (Points::size_type idx = 0; idx < c.n_samples - 1; ++idx) {
+ left = idx;
+ window_space = item_to_window (Duple (c.samples[idx].x, 0.0), false);
+ if (window_space.x >= area.x0) break;
+ }
+ for (Points::size_type idx = c.n_samples; idx > left + 1; --idx) {
+ window_space = item_to_window (Duple (c.samples[idx].x, 0.0), false);
+ if (window_space.x <= area.x1) break;
+ right = idx;
+ }
+
+ /* draw line between samples */
+ window_space = item_to_window (Duple (c.samples[left].x, c.samples[left].y), false);
+ context->move_to (window_space.x, window_space.y);
+ for (uint32_t idx = left + 1; idx < right; ++idx) {
+ window_space = item_to_window (Duple (c.samples[idx].x, c.samples[idx].y), false);
+ context->line_to (window_space.x, window_space.y);
+ }
+ }
+ return context->copy_path ();
+}
+
+void
+XFadeCurve::close_path(Rect const & area, Cairo::RefPtr<Cairo::Context> context, CanvasCurve const &c, bool inside) const
+{
+ Duple window_space;
+ if (inside) {
+ window_space = item_to_window (Duple(c.points.back().x, area.height()), false);
+ context->line_to (window_space.x, window_space.y);
+ window_space = item_to_window (Duple(c.points.front().x, area.height()), false);
+ context->line_to (window_space.x, window_space.y);
+ context->close_path();
+ } else {
+ window_space = item_to_window (Duple(c.points.back().x, 0.0), false);
+ context->line_to (window_space.x, window_space.y);
+ window_space = item_to_window (Duple(c.points.front().x, 0.0), false);
+ context->line_to (window_space.x, window_space.y);
+ context->close_path();
+ }
+}
+
+void
+XFadeCurve::render (Rect const & area, Cairo::RefPtr<Cairo::Context> context) const
+{
+ if (!_bounding_box) { return; }
+ if (_in.points.size() < 2) { return; }
+ if (_out.points.size() < 2) { return; }
+
+ Rect self = item_to_window (_bounding_box.get());
+ boost::optional<Rect> d = self.intersection (area);
+ assert (d);
+ Rect draw = d.get ();
+
+ context->save ();
+ context->rectangle (draw.x0, draw.y0, draw.width(), draw.height());
+ context->clip ();
+
+ /* expand drawing area by several pixels on each side to avoid cairo stroking effects at the boundary.
+ * they will still occur, but cairo's clipping will hide them.
+ */
+ draw = draw.expand (4.0);
+
+ Cairo::Path *path_in = get_path(draw, context, _in);
+ Cairo::Path *path_out = get_path(draw, context, _out);
+
+ Color outline_shaded = _outline_color;
+ outline_shaded = 0.5 * (outline_shaded & 0xff) + (outline_shaded & ~0xff);
+
+ Color fill_shaded = _fill_color;
+ fill_shaded = 0.5 * (fill_shaded & 0xff) + (fill_shaded & ~0xff);
+
+#define IS (_xfadeposition == Start)
+
+ /* fill primary fade */
+ context->begin_new_path ();
+ context->append_path (IS ? *path_in : *path_out);
+ close_path(draw, context, IS ?_in : _out, false);
+ set_source_rgba (context, _fill_color);
+ context->fill ();
+
+ /* fill background fade */
+ context->save ();
+ context->begin_new_path ();
+ context->append_path (IS ? *path_in : *path_out);
+ close_path(draw, context, IS ? _in : _out, true);
+ context->set_fill_rule (Cairo::FILL_RULE_EVEN_ODD);
+ context->clip ();
+ context->begin_new_path ();
+ context->append_path (IS ? *path_out: *path_in);
+ close_path(draw, context, IS ? _out : _in, true);
+ set_source_rgba (context, fill_shaded);
+ context->set_fill_rule (Cairo::FILL_RULE_WINDING);
+ context->fill ();
+ context->restore ();
+
+ /* draw lines over fills */
+ set_source_rgba (context, IS ? _outline_color : outline_shaded);
+ context->set_line_width (IS ? 1.0 : .5);
+
+ context->begin_new_path ();
+ context->append_path (*path_in);
+ context->stroke();
+
+ set_source_rgba (context, IS ? outline_shaded :_outline_color);
+ context->set_line_width (IS ? .5 : 1.0);
+
+ context->begin_new_path ();
+ context->append_path (*path_out);
+ context->stroke();
+
+ context->restore ();
+
+ delete path_in;
+ delete path_out;
+}
#include <cassert>
#include <list>
+#include <stdint.h>
+
#include <boost/pool/pool.hpp>
#include <boost/pool/pool_alloc.hpp>
+
#include <glibmm/threads.h>
+
#include "pbd/signals.h"
#include "evoral/visibility.h"
SMF() : _smf(0), _smf_track(0), _empty(true) {};
virtual ~SMF();
+ static bool test(const std::string& path);
int open(const std::string& path, int track=1) THROW_FILE_ERROR;
+ // XXX 19200 = 10 * Timecode::BBT_Time::ticks_per_beat
int create(const std::string& path, int track=1, uint16_t ppqn=19200) THROW_FILE_ERROR;
void close() THROW_FILE_ERROR;
const MusicalTime MinMusicalTime = DBL_MIN;
static inline bool musical_time_equal (MusicalTime a, MusicalTime b) {
- /* acceptable tolerance is 1 tick. Nice if there was no magic number here */
+ /* acceptable tolerance is 1 tick. Nice if there was no magic number here
+ * -> Timecode::BBT_Time::ticks_per_beat */
return fabs (a - b) <= (1.0/1920.0);
}
assert(inclusive ? x >= start : x > start);
return true;
} else {
- return false;
+ if (inclusive) {
+ x = next->when;
+ } else {
+ x = start;
+ }
+ _search_cache.left = x;
+ return true;
}
/* No points in the future, so no steps (towards them) in the future */
double tdelta = x - before->when;
double trange = after->when - before->when;
- return before->value + (vdelta * (tdelta / trange));
-
-#if 0
- double x2 = x * x;
- ControlEvent* ev = *range.second;
-
- return = ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x);
-#endif
-
+ if (_list.interpolation() == ControlList::Curved && after->coeff) {
+ ControlEvent* ev = after;
+ double x2 = x * x;
+ return ev->coeff[0] + (ev->coeff[1] * x) + (ev->coeff[2] * x2) + (ev->coeff[3] * x2 * x);
+ } else {
+ return before->value + (vdelta * (tdelta / trange));
+ }
}
/* x is a control point in the data */
}
}
+/** Attempt to open the SMF file just to see if it is valid.
+ *
+ * \return true on success
+ * false on failure
+ */
+bool
+SMF::test(const std::string& path)
+{
+ PBD::StdioFileDescriptor d (path, "r");
+ FILE* f = d.allocate ();
+ if (f == 0) {
+ return false;
+ }
+
+ smf_t* test_smf;
+ if ((test_smf = smf_load (f)) == NULL) {
+ return false;
+ }
+ smf_delete (test_smf);
+ return true;
+}
+
/** Attempt to open the SMF file for reading and/or writing.
*
* \return 0 on success
seq->start_write();
for (Notes::const_iterator i = test_notes.begin(); i != test_notes.end(); ++i) {
- uint8_t buffer[2];
+ uint8_t buffer[3];
Event<Time>* event = new Event<Time>(
DummyTypeMap::CONTROL, (*i)->on_event().time(), 3, buffer, true
);
#include <string.h>
#include <vector>
+#include "pbd/pbd.h"
+#include "pbd/transmitter.h"
+#include "pbd/receiver.h"
+
#include "ardour/filesystem_paths.h"
#ifdef LXVST_SUPPORT
#include "ardour/linux_vst_support.h"
#endif
#include "../ardour/filesystem_paths.cc"
#include "../ardour/directory_names.cc"
-#include "../pbd/error.cc"
-#include "../pbd/basename.cc"
-#include "../pbd/search_path.cc"
-#include "../pbd/transmitter.cc"
-#include "../pbd/whitespace.cc"
+
#ifdef LXVST_SUPPORT
void
vstfx_destroy_editor (VSTState* /*vstfx*/) { }
#endif
+using namespace PBD;
+
+class DummyReceiver : public Receiver {
+ protected:
+ void receive (Transmitter::Channel chn, const char * str) {
+ const char *prefix = "";
+ switch (chn) {
+ case Transmitter::Error:
+ prefix = "[ERROR]: ";
+ break;
+ case Transmitter::Info:
+ /* ignore */
+ return;
+ case Transmitter::Warning:
+ prefix = "[WARNING]: ";
+ break;
+ case Transmitter::Fatal:
+ prefix = "[FATAL]: ";
+ break;
+ case Transmitter::Throw:
+ abort ();
+ }
+
+ std::cerr << prefix << str << std::endl;
+
+ if (chn == Transmitter::Fatal) {
+ ::exit (1);
+ }
+ }
+};
+
+DummyReceiver dummy_receiver;
+
int main (int argc, char **argv) {
- if (argc != 2) {
- fprintf(stderr, "usage: %s <vst>\n", argv[0]);
+ char *dllpath = NULL;
+ if (argc == 3 && !strcmp("-f", argv[1])) {
+ dllpath = argv[2];
+ if (strstr (dllpath, ".so" ) || strstr(dllpath, ".dll")) {
+ vstfx_remove_infofile(dllpath);
+ vstfx_un_blacklist(dllpath);
+ }
+
+ }
+ else if (argc != 2) {
+ fprintf(stderr, "usage: %s [-f] <vst>\n", argv[0]);
return EXIT_FAILURE;
+ } else {
+ dllpath = argv[1];
}
- char *dllpath = argv[1];
+ PBD::init();
+
+ dummy_receiver.listen_to (error);
+ dummy_receiver.listen_to (info);
+ dummy_receiver.listen_to (fatal);
+ dummy_receiver.listen_to (warning);
+
std::vector<VSTInfo *> *infos = 0;
+
+ if (0) { }
#ifdef LXVST_SUPPORT
- if (strstr (dllpath, ".so")) {
+ else if (strstr (dllpath, ".so")) {
infos = vstfx_get_info_lx(dllpath);
}
#endif
#ifdef WINDOWS_VST_SUPPORT
- if (strstr (dllpath, ".dll")) {
+ else if (strstr (dllpath, ".dll")) {
infos = vstfx_get_info_fst(dllpath);
}
#endif
+ else {
+ fprintf(stderr, "'%s' is not a supported VST plugin.\n", dllpath);
+ }
+
+ PBD::cleanup();
if (!infos || infos->empty()) {
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
}
-
#include <vector>
#include <string>
#include <list>
+#include <stack>
#include <stdint.h>
+#include <boost/shared_ptr.hpp>
+
#include <gtk/gtkaccelmap.h>
#include <gtk/gtkuimanager.h>
#include <gtk/gtkactiongroup.h>
}
}
+struct ActionState {
+ GtkAction* action;
+ bool sensitive;
+ ActionState (GtkAction* a, bool s) : action (a), sensitive (s) {}
+};
+
+typedef std::vector<ActionState> ActionStates;
+
+static std::stack<boost::shared_ptr<ActionStates> > state_stack;
+
+static boost::shared_ptr<ActionStates>
+get_action_state ()
+{
+ boost::shared_ptr<ActionStates> state = boost::shared_ptr<ActionStates>(new ActionStates);
+
+ /* the C++ API for functions used here appears to be broken in
+ gtkmm2.6, so we fall back to the C level.
+ */
+
+ GList* list = gtk_ui_manager_get_action_groups (ActionManager::ui_manager->gobj());
+ GList* node;
+ GList* acts;
+
+ for (node = list; node; node = g_list_next (node)) {
+
+ GtkActionGroup* group = (GtkActionGroup*) node->data;
+
+ /* first pass: collect them all */
+
+ typedef std::list<Glib::RefPtr<Gtk::Action> > action_list;
+ action_list the_acts;
+
+ for (acts = gtk_action_group_list_actions (group); acts; acts = g_list_next (acts)) {
+ GtkAction* action = (GtkAction*) acts->data;
+
+ state->push_back (ActionState (action, gtk_action_get_sensitive (action)));
+ }
+ }
+
+ return state;
+}
+
+void
+ActionManager::push_action_state ()
+{
+ state_stack.push (get_action_state());
+}
+
+void
+ActionManager::pop_action_state ()
+{
+ if (state_stack.empty()) {
+ warning << string_compose (_("programming error: %1"), X_("ActionManager::pop_action_state called with empty stack")) << endmsg;
+ return;
+ }
+
+ boost::shared_ptr<ActionStates> as = state_stack.top ();
+ state_stack.pop ();
+
+ for (ActionStates::iterator i = as->begin(); i != as->end(); ++i) {
+ gtk_action_set_sensitive ((*i).action, (*i).sensitive);
+ }
+}
+
+void
+ActionManager::disable_all_actions ()
+{
+ push_action_state ();
+ boost::shared_ptr<ActionStates> as = state_stack.top ();
+
+ for (ActionStates::iterator i = as->begin(); i != as->end(); ++i) {
+ gtk_action_set_sensitive ((*i).action, false);
+ }
+}
+
void
ActionManager::add_action_group (RefPtr<ActionGroup> grp)
{
#include "pbd/abstract_ui.cc" /* instantiate the template */
+template class AbstractUI<Gtkmm2ext::UIRequest>;
+
UI::UI (string namestr, int *argc, char ***argv)
: AbstractUI<UIRequest> (namestr)
, _receiver (*this)
+ , errors (0)
{
theMain = new Main (argc, argv);
UI::~UI ()
{
_receiver.hangup ();
+ delete (errors);
}
bool
LIBGTKMM2EXT_API extern void check_toggleaction (std::string);
LIBGTKMM2EXT_API extern void uncheck_toggleaction (std::string);
LIBGTKMM2EXT_API extern void set_toggleaction_state (std::string, bool);
+
+
+ LIBGTKMM2EXT_API extern void push_action_state ();
+ LIBGTKMM2EXT_API extern void pop_action_state ();
+ LIBGTKMM2EXT_API extern void disable_all_actions ();
};
#endif /* __libgtkmm2ext_actions_h__ */
static void provide_background_for_cairo_widget (Gtk::Widget& w, const Gdk::Color& bg);
+ virtual void render (cairo_t *, cairo_rectangle_t*) = 0;
+
protected:
/** Render the widget to the given Cairo context */
- virtual void render (cairo_t *, cairo_rectangle_t*) = 0;
virtual bool on_expose_event (GdkEventExpose *);
void on_size_allocate (Gtk::Allocation &);
void on_state_changed (Gtk::StateType);
#ifndef __gtkmm2ext_idle_adjustment_h__
#define __gtkmm2ext_idle_adjustment_h__
+#include <stdint.h>
#include <sys/time.h>
+
#include <gtkmm/adjustment.h>
#include "gtkmm2ext/visibility.h"
private:
void underlying_adjustment_value_changed();
- struct timeval last_vc;
+ int64_t last_vc;
gint timeout_handler();
bool timeout_queued;
};
{
adj.signal_value_changed().connect (mem_fun (*this, &IdleAdjustment::underlying_adjustment_value_changed));
timeout_queued = 0;
- gettimeofday (&last_vc, 0);
+ last_vc = g_get_monotonic_time();
}
IdleAdjustment::~IdleAdjustment ()
void
IdleAdjustment::underlying_adjustment_value_changed ()
{
- gettimeofday (&last_vc, 0);
+ last_vc = g_get_monotonic_time();
if (timeout_queued) {
return;
gint
IdleAdjustment::timeout_handler ()
{
- struct timeval now;
- struct timeval tdiff;
+ int64_t now, tdiff;
+ now = g_get_monotonic_time();
+ tdiff = now - last_vc;
- gettimeofday (&now, 0);
+ std::cerr << "timer elapsed, diff = " << tdiff << " usec" << std::endl;
- timersub (&now, &last_vc, &tdiff);
-
- std::cerr << "timer elapsed, diff = " << tdiff.tv_sec << " + " << tdiff.tv_usec << std::endl;
-
- if (tdiff.tv_sec > 0 || tdiff.tv_usec > 250000) {
+ if (tdiff > 250000) {
std::cerr << "send signal\n";
value_changed ();
timeout_queued = false;
#include <glibmm.h>
#include <gdkmm.h>
-#include "pbd/pathscanner.h"
-
#include "gtkmm2ext/keyboard.h"
#include "gtkmm2ext/selector.h"
#include "gtkmm2ext/utils.h"
{
#ifdef ENABLE_NLS
(void) bindtextdomain(PACKAGE, localedir);
+ (void) bind_textdomain_codeset (PACKAGE, "UTF-8");
#endif
}
if (_command == MachineControl::cmdLocate) {
*b++ = 0x6; // byte count
*b++ = 0x1; // "TARGET" subcommand
- *b++ = _time.hours;
+ *b++ = _time.hours % 24;
*b++ = _time.minutes;
*b++ = _time.seconds;
*b++ = _time.frames;
Glib::PatternSpec pattern(string("*.midnam"));
vector<std::string> result;
- PBD::find_matching_files_in_directory (prefix, pattern, result);
+ PBD::find_files_matching_pattern (result, prefix, pattern);
cout << "Loading " << result.size() << " MIDI patches from " << prefix << endl;
RelativePath="..\cartesian.cc"
>
</File>
- <File
- RelativePath="..\clear_dir.cc"
- >
- </File>
<File
RelativePath="..\command.cc"
>
RelativePath="..\pathexpand.cc"
>
</File>
- <File
- RelativePath="..\pathscanner.cc"
- >
- </File>
<File
RelativePath="..\pbd.cc"
>
RelativePath="..\pbd\pathexpand.h"
>
</File>
- <File
- RelativePath="..\pbd\pathscanner.h"
- >
- </File>
<File
RelativePath="..\pbd\pbd.h"
>
+++ /dev/null
-/*
- Copyright (C) 2012 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.
-
-*/
-
-#ifdef COMPILER_MSVC
-#include <io.h> // Microsoft's nearest equivalent to <unistd.h>
-using PBD::readdir;
-using PBD::opendir;
-using PBD::closedir;
-#else
-#include <dirent.h>
-#include <unistd.h>
-#endif
-
-#include <string>
-#include <sys/stat.h>
-#include <errno.h>
-#include <string.h>
-
-#include <glib/gstdio.h>
-#include <glibmm/miscutils.h>
-
-#include "pbd/error.h"
-#include "pbd/compose.h"
-#include "pbd/clear_dir.h"
-
-#include "i18n.h"
-
-using namespace PBD;
-using namespace std;
-
-int
-PBD::clear_directory (const string& dir, size_t* size, vector<string>* paths)
-{
- struct dirent* dentry;
- struct stat statbuf;
- DIR* dead;
- int ret = 0;
-
- if ((dead = ::opendir (dir.c_str())) == 0) {
- return -1;
- }
-
- while ((dentry = ::readdir (dead)) != 0) {
-
- /* avoid '.' and '..' */
-
- if ((dentry->d_name[0] == '.' && dentry->d_name[1] == '\0') ||
- (dentry->d_name[2] == '\0' && dentry->d_name[0] == '.' && dentry->d_name[1] == '.')) {
- continue;
- }
-
- string fullpath = Glib::build_filename (dir, dentry->d_name);
-
- if (::stat (fullpath.c_str(), &statbuf)) {
- continue;
- }
-
- if (!S_ISREG (statbuf.st_mode)) {
- continue;
- }
-
- if (::g_unlink (fullpath.c_str())) {
- error << string_compose (_("cannot remove file %1 (%2)"), fullpath, strerror (errno))
- << endmsg;
- ret = 1;
- }
-
- if (paths) {
- paths->push_back (dentry->d_name);
- }
-
- if (size) {
- *size += statbuf.st_size;
- }
- }
-
- ::closedir (dead);
-
- return ret;
-}
-
-// rm -rf <dir> -- used to remove saved plugin state
-void
-PBD::remove_directory (const std::string& dir) {
- DIR* dead;
- struct dirent* dentry;
- struct stat statbuf;
-
- if ((dead = ::opendir (dir.c_str())) == 0) {
- return;
- }
-
- while ((dentry = ::readdir (dead)) != 0) {
- if(!strcmp(dentry->d_name, ".") || !strcmp(dentry->d_name, "..")) {
- continue;
- }
-
- string fullpath = Glib::build_filename (dir, dentry->d_name);
- if (::stat (fullpath.c_str(), &statbuf)) {
- continue;
- }
-
- if (S_ISDIR (statbuf.st_mode)) {
- remove_directory(fullpath);
- continue;
- }
-
- if (::g_unlink (fullpath.c_str())) {
- error << string_compose (_("cannot remove file %1 (%2)"), fullpath, strerror (errno)) << endmsg;
- }
- }
- if (::g_rmdir(dir.c_str())) {
- error << string_compose (_("cannot remove directory %1 (%2)"), dir, strerror (errno)) << endmsg;
- }
-}
* or it treats the file as a text stream and puts in
* line endings in etc
*/
-#ifdef WIN32
+#ifdef PLATFORM_WINDOWS
#define WRITE_FLAGS O_RDWR | O_CREAT | O_BINARY
#define READ_FLAGS O_RDONLY | O_BINARY
#else
/*
- Copyright (C) 2007 Tim Mayberry
+ Copyright (C) 2007-2014 Tim Mayberry
+ Copyright (C) 1998-2014 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
/* close(), read(), write() */
#ifdef COMPILER_MSVC
#include <io.h> // Microsoft's nearest equivalent to <unistd.h>
+#include <ardourext/misc.h>
#else
#include <unistd.h>
+#include <regex.h>
#endif
#include "pbd/compose.h"
+#include "pbd/file_manager.h"
#include "pbd/file_utils.h"
#include "pbd/debug.h"
#include "pbd/error.h"
-#include "pbd/pathscanner.h"
+#include "pbd/pathexpand.h"
#include "pbd/stl_delete.h"
#include "i18n.h"
namespace PBD {
void
-get_files_in_directory (const std::string& directory_path, vector<string>& result)
+run_functor_for_paths (vector<string>& result,
+ const Searchpath& paths,
+ bool (*functor)(const string &, void *),
+ void *arg,
+ bool pass_files_only,
+ bool pass_fullpath, bool return_fullpath,
+ bool recurse)
{
- if (!Glib::file_test (directory_path, Glib::FILE_TEST_IS_DIR)) return;
+ for (vector<string>::const_iterator i = paths.begin(); i != paths.end(); ++i) {
+ string expanded_path = path_expand (*i);
+ DEBUG_TRACE (DEBUG::FileUtils,
+ string_compose("Find files in expanded path: %1\n", expanded_path));
- try
- {
- Glib::Dir dir(directory_path);
- std::copy(dir.begin(), dir.end(), std::back_inserter(result));
- }
- catch (Glib::FileError& err)
- {
- warning << err.what() << endmsg;
- }
-}
+ if (!Glib::file_test (expanded_path, Glib::FILE_TEST_IS_DIR)) continue;
-void
-find_matching_files_in_directory (const std::string& directory,
- const Glib::PatternSpec& pattern,
- vector<std::string>& result)
-{
- vector<string> tmp_files;
+ try
+ {
+ Glib::Dir dir(expanded_path);
+
+ for (Glib::DirIterator di = dir.begin(); di != dir.end(); di++) {
+
+ string fullpath = Glib::build_filename (expanded_path, *di);
+ string basename = *di;
- get_files_in_directory (directory, tmp_files);
- result.reserve(tmp_files.size());
+ bool is_dir = Glib::file_test (fullpath, Glib::FILE_TEST_IS_DIR);
- for (vector<string>::iterator file_iter = tmp_files.begin();
- file_iter != tmp_files.end();
- ++file_iter)
- {
- if (!pattern.match(*file_iter)) continue;
+ if (is_dir && recurse) {
+ DEBUG_TRACE (DEBUG::FileUtils,
+ string_compose("Descending into directory: %1\n",
+ fullpath));
+ run_functor_for_paths (result, fullpath, functor, arg, pass_files_only,
+ pass_fullpath, return_fullpath, recurse);
+ }
- std::string full_path(directory);
- full_path = Glib::build_filename (full_path, *file_iter);
+ if (is_dir && pass_files_only) {
+ continue;
+ }
- DEBUG_TRACE (
- DEBUG::FileUtils,
- string_compose("Found file %1\n", full_path)
- );
+ string functor_str;
- result.push_back(full_path);
+ if (pass_fullpath) {
+ functor_str = fullpath;
+ } else {
+ functor_str = basename;
+ }
+
+ DEBUG_TRACE (DEBUG::FileUtils,
+ string_compose("Run Functor using string: %1\n", functor_str));
+
+ if (!functor(functor_str, arg)) {
+ continue;
+ }
+
+ DEBUG_TRACE (DEBUG::FileUtils,
+ string_compose("Found file %1 matching functor\n", functor_str));
+
+ if (return_fullpath) {
+ result.push_back(fullpath);
+ } else {
+ result.push_back(basename);
+ }
+ }
+ }
+ catch (Glib::FileError& err)
+ {
+ warning << err.what() << endmsg;
+ }
}
}
+static
+bool accept_all_files (string const &, void *)
+{
+ return true;
+}
+
void
-find_matching_files_in_directories (const vector<std::string>& paths,
- const Glib::PatternSpec& pattern,
- vector<std::string>& result)
+get_paths (vector<string>& result,
+ const Searchpath& paths,
+ bool files_only,
+ bool recurse)
{
- for (vector<std::string>::const_iterator path_iter = paths.begin();
- path_iter != paths.end();
- ++path_iter)
- {
- find_matching_files_in_directory (*path_iter, pattern, result);
- }
+ run_functor_for_paths (result, paths, accept_all_files, 0,
+ files_only, true, true, recurse);
}
void
-find_matching_files_in_search_path (const Searchpath& search_path,
- const Glib::PatternSpec& pattern,
- vector<std::string>& result)
+get_files (vector<string>& result, const Searchpath& paths)
{
- find_matching_files_in_directories (search_path, pattern, result);
+ return get_paths (result, paths, true, false);
}
+static
bool
-find_file_in_search_path(const Searchpath& search_path,
- const string& filename,
- std::string& result)
+pattern_filter (const string& str, void *arg)
+{
+ Glib::PatternSpec* pattern = (Glib::PatternSpec*)arg;
+ return pattern->match(str);
+}
+
+void
+find_files_matching_pattern (vector<string>& result,
+ const Searchpath& paths,
+ const Glib::PatternSpec& pattern)
+{
+ run_functor_for_paths (result, paths, pattern_filter,
+ const_cast<Glib::PatternSpec*>(&pattern),
+ true, false, true, false);
+}
+
+void
+find_files_matching_pattern (vector<string>& result,
+ const Searchpath& paths,
+ const string& pattern)
+{
+ Glib::PatternSpec tmp(pattern);
+ find_files_matching_pattern (result, paths, tmp);
+}
+
+bool
+find_file (const Searchpath& search_path,
+ const string& filename,
+ std::string& result)
{
vector<std::string> tmp;
- Glib::PatternSpec tmp_pattern(filename);
- find_matching_files_in_search_path (search_path, tmp_pattern, tmp);
+ find_files_matching_pattern (tmp, search_path, filename);
- if (tmp.size() == 0)
- {
- DEBUG_TRACE (
- DEBUG::FileUtils,
- string_compose("No file matching %1 found in Path: %2\n", filename, search_path.to_string())
- );
+ if (tmp.size() == 0) {
+ DEBUG_TRACE (DEBUG::FileUtils,
+ string_compose("No file matching %1 found in Path: %2\n",
+ filename, search_path.to_string()));
return false;
}
- if (tmp.size() != 1)
- {
- DEBUG_TRACE (
- DEBUG::FileUtils,
- string_compose("Found more that one file matching %1 in Path: %2\n", filename, search_path.to_string())
- );
+ if (tmp.size() != 1) {
+ DEBUG_TRACE (DEBUG::FileUtils,
+ string_compose("Found more that one file matching %1 in Path: %2\n",
+ filename, search_path.to_string()));
}
result = tmp.front();
- DEBUG_TRACE (
- DEBUG::FileUtils,
- string_compose("Found file %1 in Path: %2\n", filename, search_path.to_string())
- );
+ DEBUG_TRACE (DEBUG::FileUtils,
+ string_compose("Found file %1 in Path: %2\n",
+ filename, search_path.to_string()));
return true;
}
+static
+bool
+regexp_filter (const string& str, void *arg)
+{
+ regex_t* pattern = (regex_t*)arg;
+ return regexec (pattern, str.c_str(), 0, 0, 0) == 0;
+}
+
+void
+find_files_matching_regex (vector<string>& result,
+ const Searchpath& paths,
+ const std::string& regexp)
+{
+ int err;
+ char msg[256];
+ regex_t compiled_pattern;
+
+ if ((err = regcomp (&compiled_pattern, regexp.c_str(),
+ REG_EXTENDED|REG_NOSUB))) {
+
+ regerror (err, &compiled_pattern,
+ msg, sizeof (msg));
+
+ error << "Cannot compile soundfile regexp for use ("
+ << msg
+ << ")"
+ << endmsg;
+
+ return;
+ }
+
+ DEBUG_TRACE (DEBUG::FileUtils,
+ string_compose("Matching files using regexp: %1\n", regexp));
+
+ find_files_matching_filter (result, paths,
+ regexp_filter, &compiled_pattern,
+ true, true, false);
+
+ regfree (&compiled_pattern);
+}
+
+void
+find_paths_matching_filter (vector<string>& result,
+ const Searchpath& paths,
+ bool (*filter)(const string &, void *),
+ void *arg,
+ bool pass_fullpath, bool return_fullpath,
+ bool recurse)
+{
+ run_functor_for_paths (result, paths, filter, arg, false, pass_fullpath, return_fullpath, recurse);
+}
+
+void
+find_files_matching_filter (vector<string>& result,
+ const Searchpath& paths,
+ bool (*filter)(const string &, void *),
+ void *arg,
+ bool pass_fullpath, bool return_fullpath,
+ bool recurse)
+{
+ run_functor_for_paths (result, paths, filter, arg, true, pass_fullpath, return_fullpath, recurse);
+}
+
bool
copy_file(const std::string & from_path, const std::string & to_path)
{
if (!Glib::file_test (from_path, Glib::FILE_TEST_EXISTS)) return false;
- int fd_from = -1;
- int fd_to = -1;
+ FdFileDescriptor from_file(from_path, false, 0444);
+ FdFileDescriptor to_file(to_path, true, 0666);
+
+ int fd_from = from_file.allocate ();
+ int fd_to = to_file.allocate ();
char buf[4096]; // BUFSIZ ??
ssize_t nread;
- fd_from = ::open(from_path.c_str(), O_RDONLY);
- if (fd_from < 0) {
- goto copy_error;
- }
-
- fd_to = ::open(to_path.c_str(), O_WRONLY | O_CREAT, 0666);
- if (fd_to < 0) {
- goto copy_error;
+ if ((fd_from < 0) || (fd_to < 0)) {
+ error << string_compose (_("Unable to Open files %1 to %2 for Copying(%3)"),
+ from_path, to_path, g_strerror(errno))
+ << endmsg;
+ return false;
}
while (nread = ::read(fd_from, buf, sizeof(buf)), nread > 0) {
nread -= nwritten;
out_ptr += nwritten;
} else if (errno != EINTR) {
- goto copy_error;
+ error << string_compose (_("Unable to Copy files %1 to %2(%3)"),
+ from_path, to_path, g_strerror(errno))
+ << endmsg;
+ return false;
}
} while (nread > 0);
}
- if (nread == 0) {
- if (::close(fd_to)) {
- fd_to = -1;
- goto copy_error;
- }
- ::close(fd_from);
- return true;
- }
-
-copy_error:
- int saved_errno = errno;
-
- if (fd_from >= 0) {
- ::close(fd_from);
- }
- if (fd_to >= 0) {
- ::close(fd_to);
- }
-
- error << string_compose (_("Unable to Copy file %1 to %2 (%3)"),
- from_path, to_path, strerror(saved_errno))
- << endmsg;
- return false;
-}
-
-static
-bool accept_all_files (string const &, void *)
-{
return true;
}
void
copy_files(const std::string & from_path, const std::string & to_dir)
{
- PathScanner scanner;
- vector<string*>* files = scanner (from_path, accept_all_files, 0, true, false);
-
- if (files) {
- for (vector<string*>::iterator i = files->begin(); i != files->end(); ++i) {
- std::string from = Glib::build_filename (from_path, **i);
- std::string to = Glib::build_filename (to_dir, **i);
- copy_file (from, to);
- }
- vector_delete (files);
+ vector<string> files;
+ find_files_matching_filter (files, from_path, accept_all_files, 0, true, false);
+
+ for (vector<string>::iterator i = files.begin(); i != files.end(); ++i) {
+ std::string from = Glib::build_filename (from_path, *i);
+ std::string to = Glib::build_filename (to_dir, *i);
+ copy_file (from, to);
}
}
return true;
}
+int
+remove_directory_internal (const string& dir, size_t* size, vector<string>* paths,
+ bool just_remove_files)
+{
+ vector<string> tmp_paths;
+ GStatBuf statbuf;
+ int ret = 0;
+
+ get_paths (tmp_paths, dir, just_remove_files, true);
+
+ for (vector<string>::const_iterator i = tmp_paths.begin();
+ i != tmp_paths.end(); ++i) {
+
+ if (g_stat (i->c_str(), &statbuf)) {
+ continue;
+ }
+
+ if (::g_remove (i->c_str())) {
+ error << string_compose (_("cannot remove path %1 (%2)"), *i, strerror (errno))
+ << endmsg;
+ ret = 1;
+ }
+
+ if (paths) {
+ paths->push_back (Glib::path_get_basename(*i));
+ }
+
+ if (size) {
+ *size += statbuf.st_size;
+ }
+
+ }
+
+ return ret;
+}
+
+int
+clear_directory (const string& dir, size_t* size, vector<string>* paths)
+{
+ return remove_directory_internal (dir, size, paths, true);
+}
+
+// rm -rf <dir> -- used to remove saved plugin state
+void
+remove_directory (const std::string& dir)
+{
+ remove_directory_internal (dir, 0, 0, false);
+}
+
} // namespace PBD
+++ /dev/null
-/*
- Copyright (C) 1998-99 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$
-*/
-
-#ifdef COMPILER_MSVC
-#include <stdlib.h>
-#include <stdio.h>
-using PBD::readdir;
-using PBD::opendir;
-using PBD::closedir;
-#else
-#include <dirent.h>
-#include <cstdlib>
-#include <cstdio>
-#endif
-#include <cstring>
-#include <vector>
-#include <sys/types.h>
-#include <sys/stat.h>
-
-#include <glibmm/miscutils.h>
-
-#include "pbd/error.h"
-#include "pbd/pathexpand.h"
-#include "pbd/pathscanner.h"
-#include "pbd/stl_delete.h"
-
-using namespace std;
-using namespace PBD;
-
-vector<string *> *
-PathScanner::operator() (const string &dirpath, const string ®exp,
- bool match_fullpath, bool return_fullpath,
- long limit, bool recurse)
-
-{
- int err;
- char msg[256];
-
- if ((err = regcomp (&compiled_pattern, regexp.c_str(),
- REG_EXTENDED|REG_NOSUB))) {
-
- regerror (err, &compiled_pattern,
- msg, sizeof (msg));
-
- error << "Cannot compile soundfile regexp for use ("
- << msg
- << ")"
- << endmsg;
-
- return 0;
- }
-
- return run_scan (dirpath, &PathScanner::regexp_filter,
- (bool (*)(const string &, void *)) 0,
- 0,
- match_fullpath,
- return_fullpath,
- limit, recurse);
-}
-
-vector<string *> *
-PathScanner::run_scan (const string &dirpath,
- bool (PathScanner::*memberfilter)(const string &),
- bool (*filter)(const string &, void *),
- void *arg,
- bool match_fullpath, bool return_fullpath,
- long limit,
- bool recurse)
-{
- return run_scan_internal ((vector<string*>*) 0, dirpath, memberfilter, filter, arg, match_fullpath, return_fullpath, limit, recurse);
-}
-
-vector<string *> *
-PathScanner::run_scan_internal (vector<string *> *result,
- const string &dirpath,
- bool (PathScanner::*memberfilter)(const string &),
- bool (*filter)(const string &, void *),
- void *arg,
- bool match_fullpath, bool return_fullpath,
- long limit,
- bool recurse)
-{
- DIR *dir;
- struct dirent *finfo;
- char *pathcopy = strdup (search_path_expand (dirpath).c_str());
- char *thisdir;
- string fullpath;
- string search_str;
- string *newstr;
- long nfound = 0;
-
- if ((thisdir = strtok (pathcopy, G_SEARCHPATH_SEPARATOR_S)) == 0 ||
- strlen (thisdir) == 0) {
- free (pathcopy);
- return 0;
- }
-
- if (result == 0) {
- result = new vector<string *>;
- }
-
- do {
-
- if ((dir = opendir (thisdir)) == 0) {
- continue;
- }
-
- while ((finfo = readdir (dir)) != 0) {
-
- if ((finfo->d_name[0] == '.' && finfo->d_name[1] == '\0') ||
- (finfo->d_name[0] == '.' && finfo->d_name[1] == '.' && finfo->d_name[2] == '\0')) {
- continue;
- }
-
- fullpath = Glib::build_filename (thisdir, finfo->d_name);
-
- struct stat statbuf;
- if (stat (fullpath.c_str(), &statbuf) < 0) {
- continue;
- }
-
- if (statbuf.st_mode & S_IFDIR && recurse) {
- run_scan_internal (result, fullpath, memberfilter, filter, arg, match_fullpath, return_fullpath, limit, recurse);
- } else {
-
- if (match_fullpath) {
- search_str = fullpath;
- } else {
- search_str = finfo->d_name;
- }
-
- /* handle either type of function ptr */
-
- if (memberfilter) {
- if (!(this->*memberfilter)(search_str)) {
- continue;
- }
- } else {
- if (!filter(search_str, arg)) {
- continue;
- }
- }
-
- if (return_fullpath) {
- newstr = new string (fullpath);
- } else {
- newstr = new string (finfo->d_name);
- }
-
- result->push_back (newstr);
- nfound++;
- }
- }
- closedir (dir);
-
- } while ((limit < 0 || (nfound < limit)) && (thisdir = strtok (0, G_SEARCHPATH_SEPARATOR_S)));
-
- free (pathcopy);
- return result;
-}
-
-string *
-PathScanner::find_first (const string &dirpath,
- const string ®exp,
- bool match_fullpath,
- bool return_fullpath)
-{
- vector<string *> *res;
- string *ret;
- int err;
- char msg[256];
-
- if ((err = regcomp (&compiled_pattern, regexp.c_str(),
- REG_EXTENDED|REG_NOSUB))) {
-
- regerror (err, &compiled_pattern,
- msg, sizeof (msg));
-
- error << "Cannot compile soundfile regexp for use (" << msg << ")" << endmsg;
-
-
- return 0;
- }
-
- res = run_scan (dirpath,
- &PathScanner::regexp_filter,
- (bool (*)(const string &, void *)) 0,
- 0,
- match_fullpath,
- return_fullpath,
- 1);
-
- if (res->size() == 0) {
- ret = 0;
- } else {
- ret = res->front();
- }
- vector_delete (res);
- delete res;
- return ret;
-}
-
-string *
-PathScanner::find_first (const string &dirpath,
- bool (*filter)(const string &, void *),
- void * /*arg*/,
- bool match_fullpath,
- bool return_fullpath)
-{
- vector<string *> *res;
- string *ret;
-
- res = run_scan (dirpath,
- (bool (PathScanner::*)(const string &)) 0,
- filter,
- 0,
- match_fullpath,
- return_fullpath, 1);
-
- if (res->size() == 0) {
- ret = 0;
- } else {
- ret = res->front();
- }
- vector_delete (res);
- delete res;
- return ret;
-}
if (vec.buf[0]->invalidation) {
vec.buf[0]->invalidation->requests.remove (vec.buf[0]);
}
+ delete vec.buf[0];
i->second->increment_read_ptr (1);
}
}
*/
DEBUG_TRACE (PBD::DEBUG::AbstractUI, string_compose ("%1/%2 direct dispatch of request type %3\n", name(), pthread_name(), req->type));
do_request (req);
+ delete req;
} else {
/* If called from a different thread, we first check to see if
};
typedef typename RequestBuffer::rw_vector RequestBufferVector;
+#if defined(COMPILER_MINGW) && defined(PTW32_VERSION)
+ struct pthread_cmp
+ {
+ bool operator() (const ptw32_handle_t& thread1, const ptw32_handle_t& thread2)
+ {
+ return thread1.p < thread2.p;
+ }
+ };
+ typedef typename std::map<pthread_t,RequestBuffer*, pthread_cmp>::iterator RequestBufferMapIterator;
+ typedef std::map<pthread_t,RequestBuffer*, pthread_cmp> RequestBufferMap;
+#else
typedef typename std::map<pthread_t,RequestBuffer*>::iterator RequestBufferMapIterator;
typedef std::map<pthread_t,RequestBuffer*> RequestBufferMap;
+#endif
RequestBufferMap request_buffers;
static Glib::Threads::Private<RequestBuffer> per_thread_request_buffer;
+++ /dev/null
-/*
- Copyright (C) 2012 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 __pbd_clear_dir_h__
-#define __pbd_clear_dir_h__
-
-#include <string>
-#include <vector>
-#include <sys/types.h>
-
-#include "pbd/libpbd_visibility.h"
-
-namespace PBD {
- LIBPBD_API int clear_directory (const std::string&, size_t* = 0, std::vector<std::string>* = 0);
- LIBPBD_API void remove_directory (const std::string& dir);
-}
-
-#endif /* __pbd_clear_dir_h__ */
namespace PBD {
/**
- * Get a list of files in a directory.
- * @note You must join path with result to get the absolute path
- * to the file.
+ * Get a list of path entries in a directory or within a directory tree
+ * if recursing.
+ * @note paths in result will be absolute
*
- * @param path An Absolute path to a directory
- * @param result A vector of filenames.
+ * @param result A vector of absolute paths to the directory entries in filename
+ * encoding.
+ * @param paths A Searchpath
+ * @param files_only Only include file entries in result
+ * @param recurse Recurse into child directories
*/
LIBPBD_API void
-get_files_in_directory (const std::string& path,
- std::vector<std::string>& result);
+get_paths (std::vector<std::string>& result,
+ const Searchpath& paths,
+ bool files_only = true,
+ bool recurse = false);
/**
- * Takes a directory path and returns all the files in the directory
- * matching a particular pattern.
+ * Get a list of files in a Searchpath.
+ * @note paths in result will be absolute.
*
- * @param directory A directory path
- * @param pattern A Glib::PatternSpec used to match the files.
- * @param result A vector in which to place the resulting matches.
+ * @param path A Searchpath
+ * @param result A vector of paths to files.
*/
LIBPBD_API void
-find_matching_files_in_directory (const std::string& directory,
- const Glib::PatternSpec& pattern,
- std::vector<std::string>& result);
+get_files (std::vector<std::string>& result,
+ const Searchpath& paths);
/**
- * Takes a number of directory paths and returns all the files matching
- * a particular pattern.
+ * Takes a Searchpath and returns all the files contained in the
+ * directory paths that match a particular pattern.
*
- * @param paths A vector containing the Absolute paths
- * @param pattern A Glib::PatternSpec used to match the files
* @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param pattern A Glib::PatternSpec used to match the files.
*/
LIBPBD_API void
-find_matching_files_in_directories (const std::vector<std::string>& directory_paths,
- const Glib::PatternSpec& pattern,
- std::vector<std::string>& result);
+find_files_matching_pattern (std::vector<std::string>& result,
+ const Searchpath& paths,
+ const Glib::PatternSpec& pattern);
/**
- * Takes a Searchpath and puts a list of all the files in the search path
- * that match pattern into the result vector.
+ * Takes a Searchpath and returns all the files contained in the
+ * directory paths that match a particular pattern.
+ *
+ * This is a convenience method to avoid explicitly declaring
+ * temporary variables such as:
+ * find_files_matching_pattern (result, paths, string("*.ext"))
*
- * @param search_path A Searchpath
- * @param pattern A Glib::PatternSpec used to match the files
* @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param pattern A string representing the Glib::PatternSpec used
+ * to match the files.
*/
LIBPBD_API void
-find_matching_files_in_search_path (const Searchpath& search_path,
- const Glib::PatternSpec& pattern,
- std::vector<std::string>& result);
+find_files_matching_pattern (std::vector<std::string>& result,
+ const Searchpath& paths,
+ const std::string& pattern);
/**
- * Takes a search path and a file name and place the full path
+ * Takes a search path and a file name and places the full path
* to that file in result if it is found within the search path.
*
+ * @note the parameter order of this function doesn't match the
+ * others. At the time of writing it is the most widely used file
+ * utility function so I didn't change it but it may be worth
+ * doing at some point if it causes any confusion.
+ *
* @return true If file is found within the search path.
*/
LIBPBD_API bool
-find_file_in_search_path (const Searchpath& search_path,
- const std::string& filename,
- std::string& result);
+find_file (const Searchpath& search_path,
+ const std::string& filename,
+ std::string& result);
+
+
+/**
+ * Find files in paths that match a regular expression
+ * @note This function does not recurse.
+ *
+ * @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param regexp A regular expression
+ */
+LIBPBD_API void
+find_files_matching_regex (std::vector<std::string>& results,
+ const Searchpath& paths,
+ const std::string& regexp);
+
+/**
+ * Find paths in a Searchpath that match a supplied filter(functor)
+ * @note results include files and directories.
+ *
+ * @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param filter A functor to use to filter paths
+ * @param arg additonal argument to filter if required
+ * @param pass_fullpath pass the full path to the filter or just the basename
+ * @param return_fullpath put the full path in results or just the basename
+ * @param recurse Recurse into child directories to find paths.
+ */
+LIBPBD_API void
+find_paths_matching_filter (std::vector<std::string>& results,
+ const Searchpath& paths,
+ bool (*filter)(const std::string &, void *),
+ void *arg,
+ bool pass_fullpath,
+ bool return_fullpath,
+ bool recurse = false);
+
+/**
+ * Find paths in a Searchpath that match a supplied filter(functor)
+ * @note results include only files.
+ *
+ * @param result A vector in which to place the resulting matches.
+ * @param paths A Searchpath
+ * @param filter A functor to use to filter paths
+ * @param arg additonal argument to filter if required
+ * @param pass_fullpath pass the full path to the filter or just the basename
+ * @param return_fullpath put the full path in results or just the basename
+ * @param recurse Recurse into child directories to find files.
+ */
+LIBPBD_API void
+find_files_matching_filter (std::vector<std::string>& results,
+ const Searchpath& paths,
+ bool (*filter)(const std::string &, void *),
+ void *arg,
+ bool pass_fullpath,
+ bool return_fullpath,
+ bool recurse = false);
/**
* Attempt to copy the contents of the file from_path to a new file
/// @return true if path at p exists and is writable, false otherwise
LIBPBD_API bool exists_and_writable(const std::string & p);
+/**
+ * Remove all the files in a directory recursively leaving the directory
+ * structure in place.
+ * @note dir will not be removed
+ *
+ * @param dir The directory to clear of files.
+ * @param size of removed files in bytes.
+ * @param list of files that were removed.
+ */
+LIBPBD_API int clear_directory (const std::string& dir, size_t* size = 0,
+ std::vector<std::string>* removed_files = 0);
+
+/**
+ * Remove all the contents of a directory recursively.
+ * @note dir will not be removed
+ *
+ * @param dir The directory to remove files from.
+ */
+LIBPBD_API void remove_directory (const std::string& dir);
+
} // namespace PBD
#endif
#define localtime_r( _clock, _result ) \
( *(_result) = *localtime( (_clock) ), (_result) )
-#elif defined __MINGW64__
+#elif defined COMPILER_MINGW
# ifdef localtime_r
# undef localtime_r
+++ /dev/null
-/*
- Copyright (C) 2000-2007 Paul Davis
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef __libmisc_pathscanner_h__
-#define __libmisc_pathscanner_h__
-
-#include <vector>
-#include <string>
-#ifdef COMPILER_MSVC
-#include <ardourext/misc.h>
-#else
-#include <regex.h>
-#endif
-
-#include "pbd/libpbd_visibility.h"
-
-class LIBPBD_API PathScanner
-
-{
- public:
- std::vector<std::string *> *operator() (const std::string &dirpath,
- bool (*filter)(const std::string &, void *arg),
- void *arg,
- bool match_fullpath = true,
- bool return_fullpath = true,
- long limit = -1,
- bool recurse = false) {
- return run_scan (dirpath,
- (bool (PathScanner::*)(const std::string &)) 0,
- filter,
- arg,
- match_fullpath,
- return_fullpath,
- limit, recurse);
- }
-
- std::vector<std::string *> *operator() (const std::string &dirpath,
- const std::string ®exp,
- bool match_fullpath = true,
- bool return_fullpath = true,
- long limit = -1,
- bool recurse = false);
-
- std::string *find_first (const std::string &dirpath,
- const std::string ®exp,
- bool match_fullpath = true,
- bool return_fullpath = true);
-
- std::string *find_first (const std::string &dirpath,
- bool (*filter)(const std::string &, void *),
- void *arg,
- bool match_fullpath = true,
- bool return_fullpath = true);
-
- private:
- regex_t compiled_pattern;
-
- bool regexp_filter (const std::string &str) {
- return regexec (&compiled_pattern, str.c_str(), 0, 0, 0) == 0;
- }
-
- std::vector<std::string *> *run_scan (const std::string &dirpath,
- bool (PathScanner::*mfilter) (const std::string &),
- bool (*filter)(const std::string &, void *),
- void *arg,
- bool match_fullpath,
- bool return_fullpath,
- long limit,
- bool recurse = false);
-
- std::vector<std::string *> *run_scan_internal (std::vector<std::string*>*,
- const std::string &dirpath,
- bool (PathScanner::*mfilter) (const std::string &),
- bool (*filter)(const std::string &, void *),
- void *arg,
- bool match_fullpath,
- bool return_fullpath,
- long limit,
- bool recurse = false);
-};
-
-#endif // __libmisc_pathscanner_h__
#include <string>
#include <pthread.h>
#include <signal.h>
+#include <map>
+
#ifdef NOPBD /* unit-test outside ardour */
#include <sigc++/bind.h>
#include <sigc++/signal.h>
*
*/
SystemExec (std::string c, char ** a);
+
+ /** similar to \ref SystemExec but expects a whole command line, and
+ * handles some simple escape sequences.
+ *
+ * @param command complete command-line to be executed
+ * @param subs a map of <char, std::string> listing the % substitutions to
+ * be made.
+ *
+ * creates an argv array from the given command string, splitting into
+ * parameters at spaces.
+ * "\ " is non-splitting space, "\\" (and "\" at end of command) as "\",
+ * for "%<char>", <char> is looked up in subs and the corresponding string
+ * substituted. "%%" (and "%" at end of command)
+ * returns an argv array suitable for creating a new SystemExec with
+ */
+ SystemExec (std::string command, const std::map<char, std::string> subs);
+
virtual ~SystemExec ();
/** fork and execute the given program
int nicelevel; ///< process nice level - defaults to 0
void make_argp(std::string);
+ void make_argp_escaped(std::string command, const std::map<char, std::string> subs);
void make_envp();
char **argp;
#else
pid_t pid;
#endif
+ void init ();
pthread_mutex_t write_lock;
int fdin; ///< file-descriptor for writing to child's STDIN. This variable is identical to pin[1] but also used as status check if the stdin pipe is open: <0 means closed.
void
Searchpath::add_directory (const std::string& directory_path)
{
- if (!directory_path.empty()) {
- push_back(directory_path);
+ if (directory_path.empty()) {
+ return;
}
+ for (vector<std::string>::const_iterator i = begin(); i != end(); ++i) {
+ if (*i == directory_path) {
+ return;
+ }
+ }
+ push_back(directory_path);
}
void
#include <sys/resource.h>
#endif
+#include <glibmm/miscutils.h>
#define USE_VFORK
+#include "pbd/file_utils.h"
+#include "pbd/search_path.h"
#include "pbd/system_exec.h"
using namespace std;
}
#endif /* not on windows, nor vfork */
-
-SystemExec::SystemExec (std::string c, std::string a)
- : cmd(c)
+void
+SystemExec::init ()
{
pthread_mutex_init(&write_lock, NULL);
thread_active=false;
pin[1] = -1;
nicelevel = 0;
envp = NULL;
- argp = NULL;
#ifdef PLATFORM_WINDOWS
stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
#endif
+}
+
+SystemExec::SystemExec (std::string c, std::string a)
+ : cmd(c)
+{
+ init ();
+
+ argp = NULL;
make_envp();
make_argp(a);
}
SystemExec::SystemExec (std::string c, char **a)
: cmd(c) , argp(a)
{
- pthread_mutex_init(&write_lock, NULL);
- thread_active=false;
- pid = 0;
- pin[1] = -1;
- nicelevel = 0;
- envp = NULL;
+ init ();
+
#ifdef PLATFORM_WINDOWS
- stdinP[0] = stdinP[1] = INVALID_HANDLE_VALUE;
- stdoutP[0] = stdoutP[1] = INVALID_HANDLE_VALUE;
- stderrP[0] = stderrP[1] = INVALID_HANDLE_VALUE;
make_wargs(a);
#endif
make_envp();
}
+SystemExec::SystemExec (std::string command, const std::map<char, std::string> subs)
+{
+ init ();
+ make_argp_escaped(command, subs);
+ if (!find_file (Searchpath (Glib::getenv ("PATH")), argp[0], cmd)) {
+ // not found in path - use as-is
+ cmd = argp[0];
+ }
+
+ // Glib::find_program_in_path () is only available in Glib >= 2.28
+ // cmd = Glib::find_program_in_path (argp[0]);
+
+ make_envp();
+}
+
+void
+SystemExec::make_argp_escaped(std::string command, const std::map<char, std::string> subs)
+{
+
+ int inquotes = 0;
+ int n = 0;
+ size_t i = 0;
+ std::string arg = "";
+
+ argp = (char **) malloc(sizeof(char *));
+
+ for (i = 0; i <= command.length(); i++) { // include terminating '\0'
+ char c = command.c_str()[i];
+ if (inquotes) {
+ if (c == '"') {
+ inquotes = 0;
+ } else {
+ // still in quotes - just copy
+ arg += c;
+ }
+ } else switch (c) {
+ case '%' :
+ c = command.c_str()[++i];
+ if (c == '%' || c == '\0') {
+ // "%%", "%" at end-of-string => "%"
+ arg += '%';
+ } else {
+ // search subs for string to substitute for char
+ std::map<char, std::string>::const_iterator s = subs.find(c);
+ if (s != subs.end()) {
+ // found substitution
+ arg += s->second;
+ } else {
+ // not a valid substitution, just copy
+ arg += '%';
+ arg += c;
+ }
+ }
+ break;
+ case '\\':
+ c = command.c_str()[++i];
+ switch (c) {
+ case ' ' :
+ case '"' : arg += c; break; // "\\", "\" at end-of-string => "\"
+ case '\0':
+ case '\\': arg += '\\'; break;
+ default : arg += '\\'; arg += c; break;
+ }
+ break;
+ case '"' :
+ inquotes = 1;
+ break;
+ case ' ' :
+ case '\t':
+ case '\0':
+ if (arg.length() > 0) {
+ // if there wasn't already a space or tab, start a new parameter
+ argp = (char **) realloc(argp, (n + 2) * sizeof(char *));
+ argp[n++] = strdup (arg.c_str());
+ arg = "";
+ }
+ break;
+ default :
+ arg += c;
+ break;
+ }
+ }
+ argp[n] = NULL;
+}
+
SystemExec::~SystemExec ()
{
terminate ();
*cp2 = '\0';
argp[argn++] = strdup(cp1);
cp1 = cp2 + 1;
- argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
+ argp = (char **) realloc(argp, (argn + 1) * sizeof(char *));
}
}
if (cp2 != cp1) {
close_stdin();
if (pid) {
- ::usleep(50000);
+ ::usleep(200000);
sched_yield();
wait(WNOHANG);
}
if (pid) {
::kill(pid, SIGTERM);
- usleep(50000);
+ ::usleep(250000);
sched_yield();
wait(WNOHANG);
}
+#include "filesystem_test.h"
+
#include <unistd.h>
#include <stdlib.h>
-#include "filesystem_test.h"
+
+#include <glibmm/miscutils.h>
+#include <glibmm/fileutils.h>
+
#include "pbd/file_utils.h"
+#include "test_common.h"
+
using namespace std;
+using namespace PBD;
CPPUNIT_TEST_SUITE_REGISTRATION (FilesystemTest);
#endif
}
+void
+FilesystemTest::testCopyFileASCIIFilename ()
+{
+ string testdata_path;
+ CPPUNIT_ASSERT (find_file (test_search_path (), "RosegardenPatchFile.xml", testdata_path));
+
+ string output_path = test_output_directory ("CopyFile");
+
+ output_path = Glib::build_filename (output_path, "RosegardenPatchFile.xml");
+
+ cerr << endl;
+ cerr << "CopyFile test output path: " << output_path << endl;
+
+ CPPUNIT_ASSERT (PBD::copy_file (testdata_path, output_path));
+}
+
+void
+FilesystemTest::testCopyFileUTF8Filename ()
+{
+ vector<string> i18n_files;
+
+ Searchpath i18n_path(test_search_path());
+ i18n_path.add_subdirectory_to_paths("i18n_test");
+
+ PBD::find_files_matching_pattern (i18n_files, i18n_path, "*.tst");
+
+ cerr << endl;
+ cerr << "Copying " << i18n_files.size() << " test files from: "
+ << i18n_path.to_string () << endl;
+
+ for (vector<string>::iterator i = i18n_files.begin(); i != i18n_files.end(); ++i) {
+ string input_path = *i;
+ string output_file = Glib::path_get_basename(*i);
+ string output_path = test_output_directory ("CopyFile");
+ output_path = Glib::build_filename (output_path, output_file);
+
+ cerr << "Copying test file: " << input_path
+ << " To " << output_path << endl;
+
+ CPPUNIT_ASSERT (PBD::copy_file (input_path, output_path));
+ }
+}
+
+void
+FilesystemTest::testFindFilesMatchingPattern ()
+{
+ vector<string> patch_files;
+
+ PBD::find_files_matching_pattern (patch_files, test_search_path (), "*PatchFile*");
+
+ CPPUNIT_ASSERT(test_search_path ().size() == 1);
+
+ CPPUNIT_ASSERT(patch_files.size() == 2);
+}
+
+string
+create_test_directory (std::string test_dir)
+{
+ vector<string> test_files;
+ vector<string> i18n_files;
+
+ Searchpath spath(test_search_path());
+ PBD::get_files (test_files, spath);
+
+ spath.add_subdirectory_to_paths("i18n_test");
+
+ PBD::get_files (i18n_files, spath);
+
+ string output_dir = test_output_directory (test_dir);
+
+ CPPUNIT_ASSERT (test_search_path().size () != 0);
+
+ string test_dir_path = test_search_path()[0];
+
+ cerr << endl;
+ cerr << "Copying " << test_files.size() << " test files from: "
+ << test_dir_path << " to " << output_dir << endl;
+
+ PBD::copy_files (test_dir_path, output_dir);
+
+ vector<string> copied_files;
+
+ PBD::get_files (copied_files, output_dir);
+
+ CPPUNIT_ASSERT (copied_files.size() == test_files.size());
+
+ string subdir_path = Glib::build_filename (output_dir, "subdir");
+
+ CPPUNIT_ASSERT (g_mkdir_with_parents (subdir_path.c_str(), 0755) == 0);
+
+ cerr << endl;
+ cerr << "Copying " << i18n_files.size() << " i18n test files to: "
+ << subdir_path << endl;
+
+ for (vector<string>::iterator i = i18n_files.begin(); i != i18n_files.end(); ++i) {
+ string input_filepath = *i;
+ string output_filename = Glib::path_get_basename(*i);
+ string output_filepath = Glib::build_filename (subdir_path, output_filename);
+
+ CPPUNIT_ASSERT (PBD::copy_file (input_filepath, output_filepath));
+ }
+
+ copied_files.clear();
+ PBD::get_files (copied_files, subdir_path);
+
+ CPPUNIT_ASSERT (copied_files.size() == i18n_files.size());
+
+ return output_dir;
+}
+
+void
+FilesystemTest::testClearDirectory ()
+{
+ string output_dir_path = create_test_directory ("ClearDirectory");
+
+ vector<string> files_in_output_dir;
+
+ PBD::get_paths (files_in_output_dir, output_dir_path, true, true);
+
+ size_t removed_file_size = 0;
+ vector<string> removed_files;
+
+ CPPUNIT_ASSERT (PBD::clear_directory (output_dir_path, &removed_file_size, &removed_files) ==0);
+
+ cerr << "Removed " << removed_files.size() << " files of total size: "
+ << removed_file_size << endl;
+
+ CPPUNIT_ASSERT (removed_files.size () == files_in_output_dir.size ());
+
+ string subdir_path = Glib::build_filename (output_dir_path, "subdir");
+
+ // make sure the directory structure is still there
+ CPPUNIT_ASSERT (Glib::file_test (subdir_path, Glib::FILE_TEST_IS_DIR));
+}
+
+void
+FilesystemTest::testRemoveDirectory ()
+{
+ string output_dir_path = create_test_directory ("RemoveDirectory");
+
+ vector<string> files_in_output_dir;
+
+ PBD::get_paths (files_in_output_dir, output_dir_path, false, true);
+
+ CPPUNIT_ASSERT (files_in_output_dir.size () != 0);
+
+ PBD::remove_directory (output_dir_path);
+
+ // doesn't actually remove directory though...just contents
+ CPPUNIT_ASSERT (Glib::file_test (output_dir_path, Glib::FILE_TEST_IS_DIR));
+
+ files_in_output_dir.clear ();
+
+ PBD::get_paths (files_in_output_dir, output_dir_path, false, true);
+
+ CPPUNIT_ASSERT (files_in_output_dir.size () == 0);
+}
{
CPPUNIT_TEST_SUITE (FilesystemTest);
CPPUNIT_TEST (testPathIsWithin);
+ CPPUNIT_TEST (testCopyFileASCIIFilename);
+ CPPUNIT_TEST (testCopyFileUTF8Filename);
+ CPPUNIT_TEST (testFindFilesMatchingPattern);
+ CPPUNIT_TEST (testClearDirectory);
+ CPPUNIT_TEST (testRemoveDirectory);
CPPUNIT_TEST_SUITE_END ();
public:
void testPathIsWithin ();
-
+ void testCopyFileASCIIFilename ();
+ void testCopyFileUTF8Filename ();
+ void testFindFilesMatchingPattern ();
+ void testClearDirectory ();
+ void testRemoveDirectory ();
};
--- /dev/null
+Ardour Test file
+Language: English
--- /dev/null
+Ardour Test file
+Language: Croatian
--- /dev/null
+Ardour Test file
+Language: Russian
--- /dev/null
+Ardour Test file
+Language: Armenian
--- /dev/null
+Ardour Test file
+Language: Bengali
--- /dev/null
+Ardour Test file
+Language: Thai
--- /dev/null
+Ardour Test file
+Language: Japanese
--- /dev/null
+Ardour Test file
+Language: Chinese (Simplified)
CPPUNIT_ASSERT (true);
}
-class Receiver : public PBD::ScopedConnectionList
+class AReceiver : public PBD::ScopedConnectionList
{
public:
- Receiver (Emitter* e) {
- e->Fred.connect_same_thread (*this, boost::bind (&Receiver::receiver, this));
+ AReceiver (Emitter* e) {
+ e->Fred.connect_same_thread (*this, boost::bind (&AReceiver::receiver, this));
}
void receiver () {
SignalsTest::testScopedConnectionList ()
{
Emitter* e = new Emitter;
- Receiver* r = new Receiver (e);
+ AReceiver* r = new AReceiver (e);
N = 0;
e->emit ();
675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include "test_common.h"
+
+#include <glibmm/fileutils.h>
#include <glibmm/miscutils.h>
-#include "test_common.h"
+#include <sstream>
+
+using namespace std;
/**
* This allows tests to find the data files they require by looking
return Glib::getenv("PBD_TEST_PATH");
#endif
}
+
+std::string
+test_output_directory (std::string prefix)
+{
+ std::string tmp_dir = Glib::build_filename (g_get_tmp_dir(), "pbd_test");
+ std::string dir_name;
+ std::string new_test_dir;
+ do {
+ ostringstream oss;
+ oss << prefix;
+ oss << g_random_int ();
+ dir_name = oss.str();
+ new_test_dir = Glib::build_filename (tmp_dir, dir_name);
+ if (Glib::file_test (new_test_dir, Glib::FILE_TEST_EXISTS)) continue;
+ } while (g_mkdir_with_parents (new_test_dir.c_str(), 0755) != 0);
+ return new_test_dir;
+}
+
+void
+get_utf8_test_strings (std::vector<std::string>& result)
+{
+ // These are all translations of "Ardour" from google translate
+ result.push_back ("Ardour"); // Reference
+ result.push_back ("\320\277\321\213\320\273"); // Russian
+ result.push_back ("\305\276ar"); // Croatian
+ result.push_back ("\340\270\204\340\270\247\340\270\262\340\270\241\340\270\201\340\270\243\340\270\260\340\270\225\340\270\267\340\270\255\340\270\243\340\270\267\340\270\255\340\270\243\340\271\211\340\270\231"); // Thai
+ result.push_back ("\325\245\325\274\325\241\325\266\325\244"); // Armenian
+ result.push_back ("\340\246\254\340\247\215\340\246\257\340\246\227\340\247\215\340\246\260\340\246\244\340\246\276"); // Bengali
+ result.push_back ("\346\203\205\347\206\261"); // Japanese
+ result.push_back ("\347\203\255\346\203\205"); // Chinese (Simplified)
+}
PBD::Searchpath test_search_path ();
+std::string test_output_directory (std::string prefix);
+
+void get_utf8_test_strings (std::vector<std::string>& results);
+
#endif
#include <glibmm/thread.h>
#include "scalar_properties.h"
+#include "pbd/pbd.h"
+#include "pbd/error.h"
+#include "pbd/textreceiver.h"
int
main ()
{
- Glib::thread_init();
+ TextReceiver text_receiver ("pbd_test");
+
+ if (!PBD::init ()) return 1;
+
+ text_receiver.listen_to (PBD::error);
+ text_receiver.listen_to (PBD::info);
+ text_receiver.listen_to (PBD::fatal);
+ text_receiver.listen_to (PBD::warning);
ScalarPropertiesTest::make_property_quarks ();
CppUnit::CompilerOutputter compileroutputter (&collectedresults, std::cerr);
compileroutputter.write ();
-
+
+ PBD::cleanup ();
+
return collectedresults.wasSuccessful () ? 0 : 1;
}
// cout << "Test 1: RosegardenPatchFile.xml: Find all banks in the file" << endl;
std::string testdata_path;
- CPPUNIT_ASSERT (find_file_in_search_path (test_search_path (), "RosegardenPatchFile.xml", testdata_path));
+ CPPUNIT_ASSERT (find_file (test_search_path (), "RosegardenPatchFile.xml", testdata_path));
XMLTree doc(testdata_path);
// "//bank" gives as last element an empty element libxml bug????
// We have to allocate a new document here, or we get segfaults
std::string testsession_path;
- CPPUNIT_ASSERT (find_file_in_search_path (test_search_path (), "TestSession.ardour", testsession_path));
+ CPPUNIT_ASSERT (find_file (test_search_path (), "TestSession.ardour", testsession_path));
XMLTree doc2(testsession_path);
result = doc2.find("/Session/Sources/Source[contains(@captured-for, 'Guitar')]");
// cout << endl << endl << "Test 5: ProtoolsPatchFile.midnam: Get Banks and Patches for 'Name Set 1'" << endl;
std::string testmidnam_path;
- CPPUNIT_ASSERT (find_file_in_search_path (test_search_path (), "ProtoolsPatchFile.midnam", testmidnam_path));
+ CPPUNIT_ASSERT (find_file (test_search_path (), "ProtoolsPatchFile.midnam", testmidnam_path));
// We have to allocate a new document here, or we get segfaults
XMLTree doc3(testmidnam_path);
'convert.cc',
'controllable.cc',
'controllable_descriptor.cc',
- 'clear_dir.cc',
'cpus.cc',
'debug.cc',
'enumwriter.cc',
'mountpoint.cc',
'openuri.cc',
'pathexpand.cc',
- 'pathscanner.cc',
'pbd.cc',
'pool.cc',
'property_list.cc',
# Boost headers
autowaf.check_header(conf, 'cxx', 'boost/shared_ptr.hpp')
autowaf.check_header(conf, 'cxx', 'boost/weak_ptr.hpp')
- if conf.env['WINDOWS_VST_SUPPORT'] == True and Options.options.dist_target == 'mingw':
+ if Options.options.dist_target == 'mingw':
conf.check(compiler='cxx',
lib='ole32',
mandatory=True,
framepos_t last_where;
ARDOUR::gain_t last_track_gain;
uint32_t last_meter_fill;
- struct timeval last_wheel_motion;
+ uint64_t last_wheel_motion;
int last_wheel_dir;
Glib::Mutex io_lock;
#include "pbd/controllable_descriptor.h"
#include "pbd/error.h"
#include "pbd/failed_constructor.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
#include "pbd/xml++.h"
#include "midi++/port.h"
void
GenericMidiControlProtocol::reload_maps ()
{
- vector<string *> *midi_maps;
- PathScanner scanner;
+ vector<string> midi_maps;
Searchpath spath (system_midi_map_search_path());
spath += user_midi_map_directory ();
- midi_maps = scanner (spath.to_string(), midi_map_filter, 0, false, true);
+ find_files_matching_filter (midi_maps, spath, midi_map_filter, 0, false, true);
- if (!midi_maps) {
+ if (midi_maps.empty()) {
cerr << "No MIDI maps found using " << spath.to_string() << endl;
return;
}
- for (vector<string*>::iterator i = midi_maps->begin(); i != midi_maps->end(); ++i) {
- string fullpath = *(*i);
+ for (vector<string>::iterator i = midi_maps.begin(); i != midi_maps.end(); ++i) {
+ string fullpath = *i;
XMLTree tree;
map_info.push_back (mi);
}
-
- delete midi_maps;
}
void
float control_min = controllable->lower ();
float control_max = controllable->upper ();
- const float control_range = control_max - control_min;
+ float control_range = control_max - control_min;
if (controllable->is_toggle()) {
if (val >= (control_min + (control_range/2.0f))) {
} else {
return 0;
}
+ } else {
+ AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
+ if (actl) {
+ control_min = actl->internal_to_interface(control_min);
+ control_max = actl->internal_to_interface(control_max);
+ control_range = control_max - control_min;
+ val = actl->internal_to_interface(val);
+ }
}
return (val - control_min) / control_range * max_value_for_type ();
float control_min = controllable->lower ();
float control_max = controllable->upper ();
- const float control_range = control_max - control_min;
-
+ float control_range = control_max - control_min;
+
+ AutomationControl *actl = dynamic_cast<AutomationControl*> (controllable);
+ if (actl) {
+ if (fv == 0.f) return control_min;
+ if (fv == 1.f) return control_max;
+ control_min = actl->internal_to_interface(control_min);
+ control_max = actl->internal_to_interface(control_max);
+ control_range = control_max - control_min;
+ return actl->interface_to_internal((fv * control_range) + control_min);
+ }
return (fv * control_range) + control_min;
}
#include "pbd/xml++.h"
#include "pbd/error.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
#include "pbd/convert.h"
+#include "pbd/stl_delete.h"
#include "ardour/filesystem_paths.h"
{
DeviceInfo di;
vector<string> s;
- vector<string *> *devinfos;
- PathScanner scanner;
+ vector<string> devinfos;
Searchpath spath (devinfo_search_path());
- devinfos = scanner (spath.to_string(), devinfo_filter, 0, false, true);
+ find_files_matching_filter (devinfos, spath, devinfo_filter, 0, false, true);
device_info.clear ();
- if (!devinfos) {
+ if (devinfos.empty()) {
error << "No MCP device info files found using " << spath.to_string() << endmsg;
std::cerr << "No MCP device info files found using " << spath.to_string() << std::endl;
return;
}
- if (devinfos->empty()) {
- error << "No MCP device info files found using " << spath.to_string() << endmsg;
- std::cerr << "No MCP device info files found using " << spath.to_string() << std::endl;
- return;
- }
-
- for (vector<string*>::iterator i = devinfos->begin(); i != devinfos->end(); ++i) {
- string fullpath = *(*i);
+ for (vector<string>::iterator i = devinfos.begin(); i != devinfos.end(); ++i) {
+ string fullpath = *i;
XMLTree tree;
-
if (!tree.read (fullpath.c_str())) {
continue;
}
device_info[di.name()] = di;
}
}
-
- delete devinfos;
}
std::ostream& operator<< (std::ostream& os, const Mackie::DeviceInfo& di)
#include "pbd/xml++.h"
#include "pbd/error.h"
-#include "pbd/pathscanner.h"
+#include "pbd/file_utils.h"
+#include "pbd/stl_delete.h"
#include "pbd/replace_all.h"
#include "ardour/filesystem_paths.h"
{
DeviceProfile dp;
vector<string> s;
- vector<string *> *devprofiles;
- PathScanner scanner;
+ vector<string> devprofiles;
Searchpath spath (devprofile_search_path());
- devprofiles = scanner (spath.to_string(), devprofile_filter, 0, false, true);
+ find_files_matching_filter (devprofiles, spath, devprofile_filter, 0, false, true);
device_profiles.clear ();
- if (!devprofiles) {
+ if (devprofiles.empty()) {
error << "No MCP device info files found using " << spath.to_string() << endmsg;
return;
}
- if (devprofiles->empty()) {
- error << "No MCP device info files found using " << spath.to_string() << endmsg;
- return;
- }
-
- for (vector<string*>::iterator i = devprofiles->begin(); i != devprofiles->end(); ++i) {
- string fullpath = *(*i);
+ for (vector<string>::iterator i = devprofiles.begin(); i != devprofiles.end(); ++i) {
+ string fullpath = *i;
XMLTree tree;
device_profiles[dp.name()] = dp;
}
}
-
- delete devprofiles;
}
int
*/
unsigned long start()
{
-#ifdef _WIN32
- _start = (unsigned long)::GetTickCount();
-#else
- gettimeofday ( &_start, 0 );
-#endif
- running = true;
-#ifdef _WIN32
- return _start;
-#else
- return ( _start.tv_sec * 1000000 + _start.tv_usec ) / 1000;
-#endif
+ _start = g_get_monotonic_time();
+ return _start / 1000;
}
/**
*/
unsigned long stop()
{
-#ifdef _WIN32
- _stop = (unsigned long)::GetTickCount();
-#else
- gettimeofday ( &_stop, 0 );
-#endif
- running = false;
+ _stop = g_get_monotonic_time();
return elapsed();
}
{
if ( running )
{
-#ifdef _WIN32
- DWORD current = ::GetTickCount();
- return current - _start;
-#else
- struct timeval current;
- gettimeofday ( ¤t, 0 );
- return (
- ( current.tv_sec * 1000000 + current.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
- ) / 1000
- ;
-#endif
+ uint64_t now = g_get_monotonic_time();
+ return (now - _start) / 1000;
}
else
{
-#ifdef _WIN32
- return _stop - _start;
-#else
- return (
- ( _stop.tv_sec * 1000000 + _stop.tv_usec ) - ( _start.tv_sec * 1000000 + _start.tv_usec )
- ) / 1000
- ;
-#endif
+ return (_stop - _start) / 1000;
}
}
}
private:
-#ifdef _WIN32
- unsigned long _start;
- unsigned long _stop;
-#else
- struct timeval _start;
- struct timeval _stop;
-#endif
+ uint64_t _start;
+ uint64_t _stop;
bool running;
};
std::string url_file;
- if (find_file_in_search_path (ardour_config_search_path(), "osc_url", url_file)) {
+ if (find_file (ardour_config_search_path(), "osc_url", url_file)) {
_osc_url_file = url_file;
ofstream urlfile;
prev_track ();
}
- timerclear (&last_wheel_motion);
+ last_wheel_motion = 0;
} else if ((buttonmask & ButtonPrev) || (buttonmask & ButtonNext)) {
prev_marker ();
}
- timerclear (&last_wheel_motion);
+ last_wheel_motion = 0;
} else if (buttonmask & ButtonShift) {
}
}
- timerclear (&last_wheel_motion);
+ last_wheel_motion = 0;
} else {
TranzportControlProtocol::scrub ()
{
float speed;
- struct timeval now;
- struct timeval delta;
+ uint64_t now;
int dir;
- gettimeofday (&now, 0);
+ now = g_get_monotonic_time();
if (_datawheel < WheelDirectionThreshold) {
dir = 1;
/* changed direction, start over */
speed = 0.1f;
} else {
- if (timerisset (&last_wheel_motion)) {
-
- timersub (&now, &last_wheel_motion, &delta);
-
+ if (last_wheel_motion != 0) {
/* 10 clicks per second => speed == 1.0 */
- speed = 100000.0f / (delta.tv_sec * 1000000 + delta.tv_usec);
+ speed = 100000.0f / (float) (now - last_wheel_motion)
} else {
def build(bld):
bld.shlib (
source = [ 'src/time.cc', 'src/bbt_time.cc' ],
- target = 'libtimecode',
+ name = 'libtimecode',
+ target = 'timecode',
includes = ['.'],
export_includes = ['.'],
defines = [ 'LIBTIMECODE_DLL_EXPORTS' ]
+/*
+ * Copyright (C) 2013-2014 Robin Gareus <robin@gareus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
// int64 abs()
#ifdef __cplusplus // Normal 'C' doesn't permit over-ridden functions !!
-inline uint64_t abs(int64_t val)
+inline int64_t abs(int64_t val) throw()
{
if (val < 0)
return val * (-1);
# vfork wrapper
cp $BUILD_ROOT/libs/vfork/ardour-exec-wrapper $APPLIB/
+# ALSA device reservation tool (if available)
+cp $BUILD_ROOT/libs/ardouralsautil/ardour-request-device $APPLIB/
+
OURLIBDIR=$BUILD_ROOT/libs
-OURLIBS=$OURLIBDIR/vamp-sdk:$OURLIBDIR/surfaces/control_protocol:$OURLIBDIR/ardour:$OURLIBDIR/midi++2:$OURLIBDIR/pbd:$OURLIBDIR/rubberband:$OURLIBDIR/soundtouch:$OURLIBDIR/gtkmm2ext:$OURLIBDIR/sigc++2:$OURLIBDIR/glibmm2:$OURLIBDIR/gtkmm2/atk:$OURLIBDIR/gtkmm2/pango:$OURLIBDIR/gtkmm2/gdk:$OURLIBDIR/gtkmm2/gtk:$OURLIBDIR/canvas:$OURLIBDIR/libsndfile:$OURLIBDIR/evoral:$OURLIBDIR/evoral/src/libsmf:$OURLIBDIR/audiographer:$OURLIBDIR/timecode:$OURLIBDIR/taglib:$OURLIBDIR/libltc:$OURLIBDIR/qm-dsp
+OURLIBS=$OURLIBDIR/vamp-sdk:$OURLIBDIR/surfaces/control_protocol:$OURLIBDIR/ardour:$OURLIBDIR/midi++2:$OURLIBDIR/pbd:$OURLIBDIR/rubberband:$OURLIBDIR/soundtouch:$OURLIBDIR/gtkmm2ext:$OURLIBDIR/sigc++2:$OURLIBDIR/glibmm2:$OURLIBDIR/gtkmm2/atk:$OURLIBDIR/gtkmm2/pango:$OURLIBDIR/gtkmm2/gdk:$OURLIBDIR/gtkmm2/gtk:$OURLIBDIR/canvas:$OURLIBDIR/libsndfile:$OURLIBDIR/evoral:$OURLIBDIR/evoral/src/libsmf:$OURLIBDIR/audiographer:$OURLIBDIR/timecode:$OURLIBDIR/taglib:$OURLIBDIR/libltc:$OURLIBDIR/qm-dsp:$OURLIBDIR/ardouralsautil
echo $OURLIBS${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
cp $BUILD_ROOT/libs/fst/ardour-vst-scanner* $Frameworks/ || true
# vfork wrapper
-cp $BUILD_ROOT/libs/vfork/ardour-exec-wrapper $Frameworks/
+mkdir $Frameworks/vfork
+cp $BUILD_ROOT/libs/vfork/ardour-exec-wrapper $Frameworks/vfork/
while [ true ] ; do
echo "installing video tools.."
HARVID_VERSION=$(curl -s -S http://ardour.org/files/video-tools/harvid_version.txt)
XJADEO_VERSION=$(curl -s -S http://ardour.org/files/video-tools/xjadeo_version.txt)
+ MULTIARCH=osx
echo "copying harvid and xjadeo ..."
rsync -Pa \
name
Memcheck:Leak
fun:*alloc
+ ...
obj:/usr/lib/*.so.*
+ ...
}
{
name
Memcheck:Leak
fun:*alloc
+ ...
obj:/usr/lib/*-linux-gnu/*.so.*
+ ...
}
{
name
. ./print-env.sh
cd $BASE || exit 1
-./waf configure --prefix="/" --bindir="/" --configdir="/share" --noconfirm --no-lv2 --test --single-tests --dist-target=mingw "$@"
+./waf configure --prefix="/" --bindir="/" --configdir="/share" --noconfirm --test --single-tests --dist-target=mingw "$@"
. ./print-env.sh
cd $BASE || exit 1
-./waf configure --prefix="/" --bindir="/" --configdir="/share" --noconfirm --no-lv2 --test --single-tests --dist-target=mingw "$@"
+./waf configure --prefix="/" --bindir="/" --configdir="/share" --noconfirm --test --single-tests --dist-target=mingw "$@"
. ./print-env.sh
cd $BASE || exit 1
-./waf configure --prefix="/" --bindir="/" --configdir="/share" --optimize --noconfirm --no-lv2 --dist-target=mingw "$@"
+./waf configure --prefix="/" --bindir="/" --configdir="/share" --optimize --noconfirm --dist-target=mingw "$@"
. ./print-env.sh
cd $BASE || exit 1
-./waf configure --prefix="/" --bindir="/" --configdir="/share" --optimize --noconfirm --no-lv2 --dist-target=mingw "$@"
+./waf configure --prefix="/" --bindir="/" --configdir="/share" --optimize --noconfirm --dist-target=mingw "$@"
--- /dev/null
+#!/bin/bash
+
+function copydll () {
+ if [ -f $MINGW_ROOT/bin/$1 ] ; then
+ cp $MINGW_ROOT/bin/$1 $2 || return 1
+ return 0
+ fi
+
+ echo "ERROR: File $1 does not exist"
+ return 1
+}
--- /dev/null
+#!/bin/bash\r
+\r
+function copydll () {\r
+ if [ -f $GTK/bin/$1 ] ; then\r
+ echo "cp $GTK/bin/$1 $2"\r
+ cp $GTK/bin/$1 $2 || return 1\r
+ return 0\r
+ fi\r
+ \r
+ if [ -f $GTK/lib/$1 ] ; then\r
+ echo "cp $GTK/lib/$1 $2"\r
+ cp $GTK/lib/$1 $2 || return 1\r
+ return 0\r
+ fi\r
+ \r
+ if [ -f $A3/bin/$1 ] ; then\r
+ echo "cp $A3/bin/$1 $2"\r
+ cp $A3/bin/$1 $2 || return 1\r
+ return 0\r
+ fi\r
+\r
+ if [ -f $A3/lib/$1 ] ; then\r
+ echo "$A3/lib/$1 $2"\r
+ cp $A3/lib/$1 $2 || return 1\r
+ return 0\r
+ fi\r
+ if which $1 ; then \r
+ echo "cp `which $1` $2"\r
+ cp `which $1` $2 || return 1\r
+ return 0\r
+ fi\r
+ \r
+ echo "there is no $1"\r
+ return 1\r
+}\r
#!/bin/bash
-BASE=$(readlink -f $0)
-BASE=$(dirname $BASE) # up one
-BASE=$(dirname $BASE) # up one more
-BASE=$(dirname $BASE) # up one more
+if [ -z "$ARCH" ]; then
+ echo "ARCH not set defaulting to win32"
+ ARCH=win32
+elif [ "$ARCH" == "win32" ]; then
+ echo "ARCH set to win32"
+elif [ "$ARCH" == "win64" ]; then
+ echo "ARCH set to win64"
+else
+ echo "ARCH set invalid value aborting..."
+ exit 1
+fi
+
+if [ "$ARCH" == "win32" ]; then
+ HOST=i686-w64-mingw32
+else
+ HOST=x86_64-w64-mingw32
+fi
-HOST=i686-w64-mingw32
MINGW_ROOT=/usr/$HOST/sys-root/mingw
export PKG_CONFIG_PREFIX=$MINGW_ROOT
export WINRC=$HOST-windres
export STRIP=$HOST-strip
+BASE=$(readlink -f $0)
+BASE=$(dirname $BASE) # up one
+BASE=$(dirname $BASE) # up one more
+BASE=$(dirname $BASE) # up one more
+
BUILD_DIR=$BASE/build
BUILD_CACHE_FILE=$BUILD_DIR/c4che/_cache.py
TOOLS_DIR=$BASE/tools/windows_packaging
# Figure out the Build Type
if [ x$DEBUG = xT ]; then
- PACKAGE_DIR="$APPNAME-${release_version}-win32-dbg"
+ PACKAGE_DIR="$APPNAME-${release_version}-$ARCH-dbg"
else
- PACKAGE_DIR="$APPNAME-${release_version}-win32"
+ PACKAGE_DIR="$APPNAME-${release_version}-$ARCH"
fi
if grep -q "BUILD_TESTS = True" $BUILD_CACHE_FILE; then
--- /dev/null
+#!/bin/bash
+
+DLLS='
+jack-0.dll
+jackserver-0.dll
+libatk-1.0-0.dll
+libatkmm-1.6-1.dll
+libbz2-1.dll
+libcairo-2.dll
+libcairo-gobject-2.dll
+libcairomm-1.0-1.dll
+libcairo-script-interpreter-2.dll
+libcppunit-1-12-1.dll
+libcrypto-10.dll
+libcurl-4.dll
+libexpat-1.dll
+libfftw3-3.dll
+libfftw3f-3.dll
+libfontconfig-1.dll
+libfreetype-6.dll
+libgailutil-18.dll
+libgcc_s_sjlj-1.dll
+libgdkmm-2.4-1.dll
+libgdk_pixbuf-2.0-0.dll
+libgdk-win32-2.0-0.dll
+libgio-2.0-0.dll
+libgiomm-2.4-1.dll
+libglib-2.0-0.dll
+libglibmm-2.4-1.dll
+libglibmm_generate_extra_defs-2.4-1.dll
+libgmodule-2.0-0.dll
+libgnurx-0.dll
+libgobject-2.0-0.dll
+libgthread-2.0-0.dll
+libgtkmm-2.4-1.dll
+libgtk-win32-2.0-0.dll
+libharfbuzz-0.dll
+iconv.dll
+libFLAC-8.dll
+libogg-0.dll
+libvorbis-0.dll
+libvorbisenc-2.dll
+libffi-6.dll
+libidn-11.dll
+libintl-8.dll
+liblo-7.dll
+libpango-1.0-0.dll
+libpangocairo-1.0-0.dll
+libpangoft2-1.0-0.dll
+libpangomm-1.4-1.dll
+libpangowin32-1.0-0.dll
+libpixman-1-0.dll
+libpng15-15.dll
+rubberband-2.dll
+libsamplerate-0.dll
+libsigc-2.0-0.dll
+libsndfile-1.dll
+libssh2-1.dll
+libssl-10.dll
+libstdc++-6.dll
+libtag.dll
+libxml2-2.dll
+pthreadGC2.dll
+portaudio-2.dll
+vamp-hostsdk-3.dll
+vamp-sdk-2.dll
+zlib1.dll
+lilv-0.dll
+sratom-0-0.dll
+serd-0-0.dll
+sord-0-0.dll
+'
+
+WITH_JACK='TRUE'
+WITH_LV2='TRUE'
+
+. ./copydll-fedora.sh
+. ./package.sh
--- /dev/null
+#!/bin/bash
+
+DLLS='
+jack-0.dll
+jackserver-0.dll
+libatk-1.0-0.dll
+libatkmm-1.6-1.dll
+libbz2-1.dll
+libcairo-2.dll
+libcairo-gobject-2.dll
+libcairomm-1.0-1.dll
+libcairo-script-interpreter-2.dll
+libcppunit-1-12-1.dll
+libcrypto-10.dll
+libcurl-4.dll
+libexpat-1.dll
+libfftw3-3.dll
+libfftw3f-3.dll
+libfontconfig-1.dll
+libfreetype-6.dll
+libgailutil-18.dll
+libgcc_s_sjlj-1.dll
+libgdkmm-2.4-1.dll
+libgdk_pixbuf-2.0-0.dll
+libgdk-win32-2.0-0.dll
+libgio-2.0-0.dll
+libgiomm-2.4-1.dll
+libglib-2.0-0.dll
+libglibmm-2.4-1.dll
+libglibmm_generate_extra_defs-2.4-1.dll
+libgmodule-2.0-0.dll
+libgnurx-0.dll
+libgobject-2.0-0.dll
+libgthread-2.0-0.dll
+libgtkmm-2.4-1.dll
+libgtk-win32-2.0-0.dll
+libharfbuzz-0.dll
+iconv.dll
+libFLAC-8.dll
+libogg-0.dll
+libvorbis-0.dll
+libvorbisenc-2.dll
+libffi-6.dll
+libidn-11.dll
+libintl-8.dll
+liblo-7.dll
+libpango-1.0-0.dll
+libpangocairo-1.0-0.dll
+libpangoft2-1.0-0.dll
+libpangomm-1.4-1.dll
+libpangowin32-1.0-0.dll
+libpixman-1-0.dll
+libpng16-16.dll
+rubberband-2.dll
+libsamplerate-0.dll
+libsigc-2.0-0.dll
+libsndfile-1.dll
+libssh2-1.dll
+libssl-10.dll
+libstdc++-6.dll
+libtag.dll
+libxml2-2.dll
+libwinpthread-1.dll
+portaudio-2.dll
+vamp-hostsdk-3.dll
+vamp-sdk-2.dll
+zlib1.dll
+lilv-0.dll
+sratom-0-0.dll
+serd-0-0.dll
+sord-0-0.dll
+'
+
+WITH_JACK='TRUE'
+WITH_LV2='TRUE'
+
+. ./copydll-fedora.sh
+. ./package.sh
. ./print-env.sh
+if [ -z "$DLLS" ]; then
+ echo "ERROR: DLLS variable is not defined..."
+ exit 1
+fi
+
cd $BASE || exit 1
if ! test -f $BUILD_CACHE_FILE; then
cp -r $BASE/libs/ardour/test/data $PACKAGE_DIR/ardour_testdata
fi
-echo "Copying mingw config files to $PACKAGE_DIR ..."
-# just copy it all for now
-cp -r $MINGW_ROOT/etc $PACKAGE_DIR
+echo "Copying config files to $PACKAGE_DIR ..."
+mkdir -p $PACKAGE_DIR/etc
+cp -RL $MINGW_ROOT/etc/fonts $PACKAGE_DIR/etc
+cp -RL $MINGW_ROOT/etc/gtk-2.0 $PACKAGE_DIR/etc
+cp -RL $MINGW_ROOT/etc/pango $PACKAGE_DIR/etc
-cp -r $MINGW_ROOT/lib/gtk-2.0 $PACKAGE_DIR/lib
-cp -r $MINGW_ROOT/lib/gdk-pixbuf-2.0 $PACKAGE_DIR/lib
+cp -R $MINGW_ROOT/lib/gtk-2.0 $PACKAGE_DIR/lib
+cp -R $MINGW_ROOT/lib/gdk-pixbuf-2.0 $PACKAGE_DIR/lib
cp $TOOLS_DIR/loaders.cache $PACKAGE_DIR/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
-mkdir -p $PACKAGE_DIR/lib/pango/1.6.0/modules
-cp -r $MINGW_ROOT/lib/pango/1.6.0/modules/*.dll $PACKAGE_DIR/lib/pango/1.6.0/modules
+mkdir -p $PACKAGE_DIR/lib/pango/1.8.0/modules
+cp -r $MINGW_ROOT/lib/pango/1.8.0/modules/*.dll $PACKAGE_DIR/lib/pango/1.8.0/modules
+
cp $TOOLS_DIR/pango.modules $PACKAGE_DIR/etc/pango
cp $TOOLS_DIR/README $PACKAGE_DIR
-DLLS='
-jack-0.dll
-jackserver-0.dll
-libart_lgpl_2-2.dll
-libatk-1.0-0.dll
-libatkmm-1.6-1.dll
-libbz2-1.dll
-libcairo-2.dll
-libcairo-gobject-2.dll
-libcairomm-1.0-1.dll
-libcairo-script-interpreter-2.dll
-libcppunit-1-12-1.dll
-libcrypto-10.dll
-libcurl-4.dll
-libexpat-1.dll
-libfftw3-3.dll
-libfftw3f-3.dll
-libfontconfig-1.dll
-libfreetype-6.dll
-libgailutil-18.dll
-libgcc_s_sjlj-1.dll
-libgdkmm-2.4-1.dll
-libgdk_pixbuf-2.0-0.dll
-libgdk-win32-2.0-0.dll
-libgio-2.0-0.dll
-libgiomm-2.4-1.dll
-libglib-2.0-0.dll
-libglibmm-2.4-1.dll
-libglibmm_generate_extra_defs-2.4-1.dll
-libgmodule-2.0-0.dll
-libgnomecanvas-2-0.dll
-libgnomecanvasmm-2.6-1.dll
-libgnurx-0.dll
-libgobject-2.0-0.dll
-libgthread-2.0-0.dll
-libgtkmm-2.4-1.dll
-libgtk-win32-2.0-0.dll
-libharfbuzz-0.dll
-libiconv-2.dll
-iconv.dll
-libFLAC-8.dll
-libogg-0.dll
-libvorbis-0.dll
-libvorbisenc-2.dll
-libffi-6.dll
-libidn-11.dll
-libintl-8.dll
-liblo-7.dll
-libpango-1.0-0.dll
-libpangocairo-1.0-0.dll
-libpangoft2-1.0-0.dll
-libpangomm-1.4-1.dll
-libpangowin32-1.0-0.dll
-libpixman-1-0.dll
-libpng15-15.dll
-libsamplerate-0.dll
-libsigc-2.0-0.dll
-libsndfile-1.dll
-libssh2-1.dll
-libssl-10.dll
-libstdc++-6.dll
-libxml2-2.dll
-pthreadGC2.dll
-zlib1.dll
-'
-
echo "Copying mingw shared libraries to $PACKAGE_DIR ..."
for i in $DLLS;
do
-cp $MINGW_ROOT/bin/$i $PACKAGE_DIR
+ copydll "$i" "$PACKAGE_DIR" || exit 1
done
-echo "Copying JACK server and drivers to $PACKAGE_DIR ..."
+if test x$WITH_JACK != x; then
+ echo "Copying JACK server and drivers to $PACKAGE_DIR ..."
+ cp $MINGW_ROOT/bin/jackd.exe $PACKAGE_DIR
+ cp -r $MINGW_ROOT/bin/jack $PACKAGE_DIR
+fi
-cp $MINGW_ROOT/bin/jackd.exe $PACKAGE_DIR
-cp -r $MINGW_ROOT/bin/jack $PACKAGE_DIR
-cp $MINGW_ROOT/bin/libportaudio-2.dll $PACKAGE_DIR
+if test x$WITH_LV2 != x; then
+ echo "Moving Bundled LV2 $PACKAGE_DIR ..."
+ mv $PACKAGE_DIR/lib/lv2 $PACKAGE_DIR/lib/ardour3/LV2
+fi
SRC_DIRS='
libs/ardour
do
cp -r -p $BASE/$i $PACKAGE_SRC_DIR/libs
done
-
- echo "Copying JACK utility programs to $PACKAGE_DIR ..."
- cp $MINGW_ROOT/bin/jack_*.exe $PACKAGE_DIR
+
+ if test x$WITH_JACK != x; then
+ echo "Copying JACK utility programs to $PACKAGE_DIR ..."
+ cp $MINGW_ROOT/bin/jack_*.exe $PACKAGE_DIR
+ fi
+
+ if test x$WITH_LV2 != x; then
+ echo "Copying LV2 utility programs to $PACKAGE_DIR ..."
+ cp $MINGW_ROOT/bin/lilv-bench.exe $PACKAGE_DIR
+ cp $MINGW_ROOT/bin/lv2info.exe $PACKAGE_DIR
+ cp $MINGW_ROOT/bin/lv2ls.exe $PACKAGE_DIR
+ fi
#echo "Copying any debug files to $PACKAGE_DIR ..."
#cp $MINGW_ROOT/bin/*.debug $PACKAGE_DIR
-# Pango Modules file
-# Automatically generated file, do not edit
-#
-# ModulesPath = Z:\usr\i686-pc-mingw32\sys-root\mingw\lib\pango\1.6.0\modules
-#
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-thai-fc.dll" ThaiScriptEngineFc PangoEngineShape PangoRenderFc thai:* lao:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-basic-win32.dll" BasicScriptEngineWin32 PangoEngineShape PangoRenderWin32 common:
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" devaScriptEngineFc PangoEngineShape PangoRenderFc devanagari:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" bengScriptEngineFc PangoEngineShape PangoRenderFc bengali:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" guruScriptEngineFc PangoEngineShape PangoRenderFc gurmukhi:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" gujrScriptEngineFc PangoEngineShape PangoRenderFc gujarati:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" oryaScriptEngineFc PangoEngineShape PangoRenderFc oriya:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" tamlScriptEngineFc PangoEngineShape PangoRenderFc tamil:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" teluScriptEngineFc PangoEngineShape PangoRenderFc telugu:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" kndaScriptEngineFc PangoEngineShape PangoRenderFc kannada:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" mlymScriptEngineFc PangoEngineShape PangoRenderFc malayalam:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-fc.dll" sinhScriptEngineFc PangoEngineShape PangoRenderFc sinhala:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-syriac-fc.dll" SyriacScriptEngineFc PangoEngineShape PangoRenderFc syriac:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-khmer-fc.dll" KhmerScriptEngineFc PangoEngineShape PangoRenderFc khmer:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-arabic-lang.dll" ArabicScriptEngineLang PangoEngineLang PangoRenderNone arabic:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-basic-fc.dll" BasicScriptEngineFc PangoEngineShape PangoRenderFc latin:* cyrillic:* greek:* armenian:* georgian:* runic:* ogham:* bopomofo:* cherokee:* coptic:* deseret:* ethiopic:* gothic:* han:* hiragana:* katakana:* old-italic:* canadian-aboriginal:* yi:* braille:* cypriot:* limbu:* osmanya:* shavian:* linear-b:* ugaritic:* glagolitic:* cuneiform:* phoenician:* common:
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-arabic-fc.dll" ArabicScriptEngineFc PangoEngineShape PangoRenderFc arabic:* nko:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" devaIndicScriptEngineLang PangoEngineLang PangoRenderNone devanagari:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" bengIndicScriptEngineLang PangoEngineLang PangoRenderNone bengali:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" guruIndicScriptEngineLang PangoEngineLang PangoRenderNone gurmukhi:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" gujrIndicScriptEngineLang PangoEngineLang PangoRenderNone gujarati:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" oryaIndicScriptEngineLang PangoEngineLang PangoRenderNone oriya:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" tamlIndicScriptEngineLang PangoEngineLang PangoRenderNone tamil:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" teluIndicScriptEngineLang PangoEngineLang PangoRenderNone telugu:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" kndaIndicScriptEngineLang PangoEngineLang PangoRenderNone kannada:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" mlymIndicScriptEngineLang PangoEngineLang PangoRenderNone malayalam:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-indic-lang.dll" sinhIndicScriptEngineLang PangoEngineLang PangoRenderNone sinhala:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-hebrew-fc.dll" HebrewScriptEngineFc PangoEngineShape PangoRenderFc hebrew:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-tibetan-fc.dll" TibetanScriptEngineFc PangoEngineShape PangoRenderFc tibetan:*
-"..\\..\\lib\\pango\\1.6.0\\modules\\pango-hangul-fc.dll" HangulScriptEngineFc PangoEngineShape PangoRenderFc hangul:*
+# Pango Modules file\r
+# Automatically generated file, do not edit\r
+#\r
+# ModulesPath = Z:\usr\i686-w64-mingw32\sys-root\mingw\lib\pango\1.8.0\modules\r
+#\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-arabic-lang.dll" ArabicScriptEngineLang PangoEngineLang PangoRenderNone arabic:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-basic-fc.dll" BasicScriptEngineFc PangoEngineShape PangoRenderFc common:\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" devaIndicScriptEngineLang PangoEngineLang PangoRenderNone devanagari:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" bengIndicScriptEngineLang PangoEngineLang PangoRenderNone bengali:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" guruIndicScriptEngineLang PangoEngineLang PangoRenderNone gurmukhi:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" gujrIndicScriptEngineLang PangoEngineLang PangoRenderNone gujarati:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" oryaIndicScriptEngineLang PangoEngineLang PangoRenderNone oriya:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" tamlIndicScriptEngineLang PangoEngineLang PangoRenderNone tamil:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" teluIndicScriptEngineLang PangoEngineLang PangoRenderNone telugu:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" kndaIndicScriptEngineLang PangoEngineLang PangoRenderNone kannada:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" mlymIndicScriptEngineLang PangoEngineLang PangoRenderNone malayalam:*\r
+"..\\..\\lib\\pango\\1.8.0\\modules\\pango-indic-lang.dll" sinhIndicScriptEngineLang PangoEngineLang PangoRenderNone sinhala:*\r
+\r
# Running Ardour requires these 3 variables to be set
#
-export ARDOUR_DATA_PATH=@DATADIR@/ardour3
-export ARDOUR_CONFIG_PATH=@SYSCONFDIR@/ardour3
-export ARDOUR_DLL_PATH=@LIBDIR@/ardour3
+export ARDOUR_DATA_PATH=@DATADIR@
+export ARDOUR_CONFIG_PATH=@CONFDIR@
+export ARDOUR_DLL_PATH=@LIBDIR@
-export GTK_PATH=@LIBDIR@/ardour3${GTK_PATH:+:$GTK_PATH}
-export LD_LIBRARY_PATH=@LIBDIR@/ardour3${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
+export GTK_PATH=@LIBDIR@${GTK_PATH:+:$GTK_PATH}
+export LD_LIBRARY_PATH=@LIBDIR@${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
-exec wine @LIBDIR@/ardour3/ardour-@VERSION@-vst.exe.so "$@"
+exec wine @LIBDIR@/ardour-@VERSION@-vst.exe.so "$@"
#
# rev is now of the form MAJOR.MINOR-rev-commit
+# or, if right at the same rev as a release, MAJOR.MINOR
#
parts = rev.split ('.')
MAJOR = parts[0]
other = parts[1].split ('-')
MINOR = other[0]
-MICRO = other[1]
+if len(other) > 1:
+ MICRO = other[1]
+else:
+ MICRO = '0'
V = MAJOR + '.' + MINOR + '.' + MICRO
#
# shared helper binaries (plugin-scanner, exec-wrapper)
'libs/fst',
'libs/vfork',
+ 'libs/ardouralsautil',
]
i18n_children = [
# waf adds -O0 -g itself. thanks waf!
is_clang = conf.env['CXX'][0].endswith('clang++')
- if conf.options.cxx11:
- conf.check_cxx(cxxflags=["-std=c++11"])
- cxx_flags.append('-std=c++11')
- if platform == "darwin":
- cxx_flags.append('-stdlib=libc++')
- link_flags.append('-lc++')
- # Prevents visibility issues in standard headers
- conf.define("_DARWIN_C_SOURCE", 1)
-
if conf.options.asan:
conf.check_cxx(cxxflags=["-fsanitize=address", "-fno-omit-frame-pointer"], linkflags=["-fsanitize=address"])
cxx_flags.append('-fsanitize=address')
conf.env['build_target'] = 'snowleopard'
elif re.search ("^11[.]", version) != None:
conf.env['build_target'] = 'lion'
- else:
+ elif re.search ("^12[.]", version) != None:
conf.env['build_target'] = 'mountainlion'
+ else:
+ conf.env['build_target'] = 'mavericks' # 13.0.0
else:
match = re.search(
"(?P<cpu>i[0-6]86|x86_64|powerpc|ppc|ppc64|arm|s390x?)",
#
compiler_flags.append ('-U__STRICT_ANSI__')
- if cpu == 'powerpc' and conf.env['build_target'] != 'none':
- #
- # Apple/PowerPC optimization options
- #
- # -mcpu=7450 does not reliably work with gcc 3.*
- #
- if opt.dist_target == 'panther' or opt.dist_target == 'tiger':
- if platform == 'darwin':
- # optimization_flags.extend ([ "-mcpu=7450", "-faltivec"])
- # to support g3s but still have some optimization for above
- compiler_flags.extend ([ "-mcpu=G3", "-mtune=7450"])
- else:
- compiler_flags.extend ([ "-mcpu=7400", "-maltivec", "-mabi=altivec"])
- else:
- compiler_flags.extend([ "-mcpu=750", "-mmultiple" ])
- compiler_flags.extend (["-mhard-float", "-mpowerpc-gfxopt"])
- optimization_flags.extend (["-Os"])
+ if conf.options.cxx11 or conf.env['build_target'] == 'mavericks':
+ conf.check_cxx(cxxflags=["-std=c++11"])
+ cxx_flags.append('-std=c++11')
+ if platform == "darwin":
+ cxx_flags.append('-stdlib=libc++')
+ linker_flags.append('-lc++')
+ # Prevents visibility issues in standard headers
+ conf.define("_DARWIN_C_SOURCE", 1)
- elif ((re.search ("i[0-9]86", cpu) != None) or (re.search ("x86_64", cpu) != None)) and conf.env['build_target'] != 'none':
+ if ((re.search ("i[0-9]86", cpu) != None) or (re.search ("x86_64", cpu) != None)) and conf.env['build_target'] != 'none':
#
if conf.env['DEBUG_DENORMAL_EXCEPTION']:
compiler_flags.append('-DDEBUG_DENORMAL_EXCEPTION')
- if opt.universal:
- if opt.generic:
- print ('Specifying Universal and Generic builds at the same time is not supported')
- sys.exit (1)
- else:
- if not Options.options.nocarbon:
- compiler_flags.extend(("-arch", "i386", "-arch", "ppc"))
- linker_flags.extend(("-arch", "i386", "-arch", "ppc"))
- else:
- compiler_flags.extend(
- ("-arch", "x86_64", "-arch", "i386", "-arch", "ppc"))
- linker_flags.extend(
- ("-arch", "x86_64", "-arch", "i386", "-arch", "ppc"))
- else:
- if opt.generic:
- compiler_flags.extend(('-arch', 'i386'))
- linker_flags.extend(('-arch', 'i386'))
+ if opt.generic:
+ compiler_flags.extend(('-arch', 'i386'))
+ linker_flags.extend(('-arch', 'i386'))
#
# warnings flags
help='Whether to build for TRX')
opt.add_option('--arch', type='string', action='store', dest='arch',
help='Architecture-specific compiler flags')
+ opt.add_option('--with-dummy', action='store_true', default=False, dest='build_dummy',
+ help='Build the dummy backend (no audio/MIDI I/O, useful for profiling)')
+ opt.add_option('--with-alsabackend', action='store_true', default=False, dest='build_alsabackend',
+ help='Build the ALSA backend')
opt.add_option('--backtrace', action='store_true', default=True, dest='backtrace',
help='Compile with -rdynamic -- allow obtaining backtraces from within Ardour')
opt.add_option('--no-carbon', action='store_true', default=False, dest='nocarbon',
opt.add_option('--depstack-root', type='string', default='~', dest='depstack_root',
help='Directory/folder where dependency stack trees (gtk, a3) can be found (defaults to ~)')
opt.add_option('--dist-target', type='string', default='auto', dest='dist_target',
- help='Specify the target for cross-compiling [auto,none,x86,i386,i686,x86_64,powerpc,tiger,leopard,mingw]')
+ help='Specify the target for cross-compiling [auto,none,x86,i386,i686,x86_64,tiger,leopard,mingw]')
opt.add_option('--fpu-optimization', action='store_true', default=True, dest='fpu_optimization',
help='Build runtime checked assembler code (default)')
opt.add_option('--no-fpu-optimization', action='store_false', dest='fpu_optimization')
help="Build a single executable for each unit test")
#opt.add_option('--tranzport', action='store_true', default=False, dest='tranzport',
# help='Compile with support for Frontier Designs Tranzport (if libusb is available)')
- opt.add_option('--universal', action='store_true', default=False, dest='universal',
- help='Compile as universal binary (OS X ONLY, requires that external libraries are universal)')
opt.add_option('--generic', action='store_true', default=False, dest='generic',
help='Compile with -arch i386 (OS X ONLY)')
opt.add_option('--versioned', action='store_true', default=False, dest='versioned',
# executing a test program is n/a when cross-compiling
if Options.options.dist_target != 'mingw':
+ conf.check_cc(function_name='dlopen', header_name='dlfcn.h', lib='dl', uselib_store='DL')
conf.check_cxx(fragment = "#include <boost/version.hpp>\nint main(void) { return (BOOST_VERSION >= 103900 ? 0 : 1); }\n",
execute = "1",
mandatory = True,
okmsg = 'ok',
errmsg = 'too old\nPlease install boost version 1.39 or higher.')
+ if re.search ("linux", sys.platform) != None and Options.options.dist_target != 'mingw':
+ autowaf.check_pkg(conf, 'alsa', uselib_store='ALSA')
+
autowaf.check_pkg(conf, 'glib-2.0', uselib_store='GLIB', atleast_version='2.2', mandatory=True)
autowaf.check_pkg(conf, 'gthread-2.0', uselib_store='GTHREAD', atleast_version='2.2', mandatory=True)
autowaf.check_pkg(conf, 'glibmm-2.4', uselib_store='GLIBMM', atleast_version='2.32.0', mandatory=True)
# TODO put this only where it is needed
conf.env.append_value('LIB', 'regex')
- if Options.options.dist_target != 'mingw':
- conf.check_cc(function_name='dlopen', header_name='dlfcn.h', lib='dl', uselib_store='DL')
-
- conf.check_cxx(fragment = "#include <boost/version.hpp>\nint main(void) { return (BOOST_VERSION >= 103900 ? 0 : 1); }\n",
- execute = "1",
- mandatory = True,
- msg = 'Checking for boost library >= 1.39',
- okmsg = 'ok',
- errmsg = 'too old\nPlease install boost version 1.39 or higher.')
-
# Tell everyone that this is a waf build
conf.env.append_value('CFLAGS', '-DWAF_BUILD')
conf.env['DEBUG_DENORMAL_EXCEPTION'] = True
if opts.build_tests:
autowaf.check_pkg(conf, 'cppunit', uselib_store='CPPUNIT', atleast_version='1.12.0', mandatory=True)
+ if opts.build_alsabackend:
+ conf.env['BUILD_ALSABACKEND'] = True
+ if opts.build_dummy:
+ conf.env['BUILD_DUMMYBACKEND'] = True
set_compiler_flags (conf, Options.options)
write_config_text('Use External Libraries', conf.is_defined('USE_EXTERNAL_LIBS'))
write_config_text('Library exports hidden', conf.is_defined('EXPORT_VISIBILITY_HIDDEN'))
+ write_config_text('ALSA Backend', opts.build_alsabackend)
+ write_config_text('ALSA DBus Reservation', conf.is_defined('HAVE_DBUS'))
write_config_text('Architecture flags', opts.arch)
write_config_text('Aubio', conf.is_defined('HAVE_AUBIO'))
write_config_text('AudioUnits', conf.is_defined('AUDIOUNIT_SUPPORT'))
write_config_text('Build target', conf.env['build_target'])
write_config_text('CoreAudio', conf.is_defined('HAVE_COREAUDIO'))
write_config_text('Debug RT allocations', conf.is_defined('DEBUG_RT_ALLOC'))
+ write_config_text('Dummy backend', opts.build_dummy)
write_config_text('Process thread timing', conf.is_defined('PT_TIMING'))
write_config_text('Denormal exceptions', conf.is_defined('DEBUG_DENORMAL_EXCEPTION'))
write_config_text('FLAC', conf.is_defined('HAVE_FLAC'))
write_config_text('Translation', opts.nls)
# write_config_text('Tranzport', opts.tranzport)
write_config_text('Unit tests', conf.env['BUILD_TESTS'])
- write_config_text('Universal binary', opts.universal)
write_config_text('Generic x86 CPU', opts.generic)
write_config_text('Windows VST support', opts.windows_vst)
write_config_text('Wiimote support', conf.is_defined('BUILD_WIIMOTE'))
for i in children:
bld.recurse(i)
- bld.install_files (bld.env['SYSCONFDIR'], 'ardour_system.rc')
+ bld.install_files (bld.env['CONFDIR'], 'ardour_system.rc')
if bld.env['RUN_TESTS']:
bld.add_post_fun(test)