Revert internals of the last layering-related commit, and go back a slightly-cleaned-up version of how it was before. Remove all layering modes; only option now is add-is-higher. Move-add-higher could easily be re-added if anyone uses it.

git-svn-id: svn://localhost/ardour2/branches/3.0@11111 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
Carl Hetherington 2011-12-29 22:14:15 +00:00
parent cabb76cce6
commit b177514930
36 changed files with 1001 additions and 2413 deletions

View file

@ -1,6 +1,7 @@
#!/bin/sh #!/bin/sh
for f in basic-layering explicit-layering1 explicit-layering2 tricky-explicit-layering; do for f in basic-layering layering-order-1 layering-order-2; do
echo "$f"
inkscape -z --export-area-drawing -f $f.svg --export-pdf $f.pdf inkscape -z --export-area-drawing -f $f.svg --export-pdf $f.pdf
done done

View file

@ -1,322 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="explicit-layering.svg">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;">
<path
id="path3618"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective4058"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4089"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4120"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4151"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4365"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4386"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-6"
style="overflow:visible">
<path
id="path3618-4"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4449"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-5"
style="overflow:visible">
<path
id="path3618-1"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4668"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696-1"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.633643"
inkscape:cx="191.36241"
inkscape:cy="670.78783"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2816"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
units="mm"
spacingx="5mm"
spacingy="5mm" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend);color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 88.582677,432.28344 159.448823,0"
id="path3592" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.20872"
y="435.23877"
id="text4042"><tspan
sodipodi:role="line"
id="tspan4044"
x="255.20872"
y="435.23877">time</tspan></text>
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818"
width="106.29921"
height="35.433071"
x="88.582687"
y="308.2677" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2"
width="88.582687"
height="35.433071"
x="124.01574"
y="343.70078" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2"
width="70.86615"
height="35.433071"
x="177.16534"
y="379.13385" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2-4"
width="35.433086"
height="35.433071"
x="265.74802"
y="379.13385" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="137.33429"
y="330.26825"
id="text4320"><tspan
sodipodi:role="line"
id="tspan4322"
x="137.33429"
y="330.26825">A</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="164.22108"
y="365.51532"
id="text4324"><tspan
sodipodi:role="line"
id="tspan4326"
x="164.22108"
y="365.51532">B</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="208.36243"
y="400.94839"
id="text4328"><tspan
sodipodi:role="line"
id="tspan4330"
x="208.36243"
y="400.94839">C</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="279.05457"
y="400.94839"
id="text4332"><tspan
sodipodi:role="line"
id="tspan4334"
x="279.05457"
y="400.94839">D</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 318.89764,432.28344 0,-124.01575"
id="path3592-8"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="307.30875"
y="301.75519"
id="text4406"><tspan
sodipodi:role="line"
id="tspan4408"
x="307.30875"
y="301.75519">layer</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,397.273 13.54507,0"
id="path4439" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="401.58279"
id="text4656"><tspan
sodipodi:role="line"
id="tspan4658"
x="328.09393"
y="401.58279">0</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 312.32197,361.87109 13.54507,0"
id="path4439-1" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.02194"
y="366.18088"
id="text4656-0-0"><tspan
sodipodi:role="line"
id="tspan4658-6-3"
x="328.02194"
y="366.18088">1</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,325.98423 13.54507,0"
id="path4439-1-6" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="329.49646"
id="text4742"><tspan
sodipodi:role="line"
id="tspan4744"
x="328.09393"
y="329.49646">2</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -1,322 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="explicit-layering2.svg">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;">
<path
id="path3618"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective4058"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4089"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4120"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4151"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4365"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4386"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-6"
style="overflow:visible">
<path
id="path3618-4"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4449"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-5"
style="overflow:visible">
<path
id="path3618-1"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4668"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696-1"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.633643"
inkscape:cx="191.36241"
inkscape:cy="670.02843"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid2816"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
units="mm"
spacingx="5mm"
spacingy="5mm" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend);color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 88.582677,432.28344 159.448823,0"
id="path3592" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.20872"
y="435.23877"
id="text4042"><tspan
sodipodi:role="line"
id="tspan4044"
x="255.20872"
y="435.23877">time</tspan></text>
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818"
width="106.29921"
height="35.433071"
x="88.58268"
y="343.70078" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2"
width="88.582687"
height="35.433071"
x="124.01575"
y="379.13385" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2"
width="70.86615"
height="35.433071"
x="177.16534"
y="308.2677" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2-4"
width="35.433086"
height="35.433071"
x="265.74802"
y="379.13385" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="137.33429"
y="365.70132"
id="text4320"><tspan
sodipodi:role="line"
id="tspan4322"
x="137.33429"
y="365.70132">A</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="164.22108"
y="400.94839"
id="text4324"><tspan
sodipodi:role="line"
id="tspan4326"
x="164.22108"
y="400.94839">B</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="208.36243"
y="330.08224"
id="text4328"><tspan
sodipodi:role="line"
id="tspan4330"
x="208.36243"
y="330.08224">C</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="279.05457"
y="400.94839"
id="text4332"><tspan
sodipodi:role="line"
id="tspan4334"
x="279.05457"
y="400.94839">D</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 318.89764,432.28344 0,-124.01575"
id="path3592-8"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="307.30875"
y="301.75519"
id="text4406"><tspan
sodipodi:role="line"
id="tspan4408"
x="307.30875"
y="301.75519">layer</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,397.273 13.54507,0"
id="path4439" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="401.58279"
id="text4656"><tspan
sodipodi:role="line"
id="tspan4658"
x="328.09393"
y="401.58279">0</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 312.32197,361.87109 13.54507,0"
id="path4439-1" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.02194"
y="366.18088"
id="text4656-0-0"><tspan
sodipodi:role="line"
id="tspan4658-6-3"
x="328.02194"
y="366.18088">1</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,325.98423 13.54507,0"
id="path4439-1-6" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="329.49646"
id="text4742"><tspan
sodipodi:role="line"
id="tspan4744"
x="328.09393"
y="329.49646">2</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -0,0 +1,201 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="New document 1">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;">
<path
id="path3913"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.4179431"
inkscape:cx="170.5"
inkscape:cy="941"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="color:#000000;fill:none;stroke:#00ff00;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985"
width="72.585144"
height="29.6516"
x="78.1614"
y="21.362185" />
<rect
style="color:#000000;fill:none;stroke:#ff0003;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7"
width="72.585144"
height="29.6516"
x="117.60985"
y="82.473022" />
<rect
style="color:#000000;fill:none;stroke:#0054ff;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-4"
width="72.585144"
height="29.6516"
x="200.92497"
y="51.917603" />
<rect
style="color:#000000;fill:none;stroke:#fff400;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0"
width="72.585144"
height="29.6516"
x="42.5"
y="113.02844" />
<rect
style="color:#000000;fill:none;stroke:#ff00de;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0-5"
width="72.585144"
height="29.6516"
x="123.29042"
y="143.58386" />
<rect
style="color:#000000;fill:none;stroke:#ff7100;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0-5-5"
width="72.585144"
height="29.6516"
x="221.75375"
y="174.13928" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.11232"
y="193.33777"
id="text3857"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3859"
x="255.11232"
y="193.33777">0</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="156.577"
y="162.39966"
id="text3861"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3863"
x="156.577"
y="162.39966">1</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="75.858574"
y="131.84424"
id="text3865"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3867"
x="75.858574"
y="131.84424">2</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="150.96841"
y="101.16282"
id="text3869"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3871"
x="150.96841"
y="101.16282">3</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="234.28354"
y="70.793404"
id="text3873"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3875"
x="234.28354"
y="70.793404">4</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="111.51997"
y="40.045986"
id="text3877"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3879"
x="111.51997"
y="40.045986">5</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
d="m 324.24253,185.09612 0,-149.300453"
id="path3881"
inkscape:connector-curvature="0" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="330.85974"
y="98.245438"
id="text4327"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4329"
x="330.85974"
y="98.245438">increasing</tspan><tspan
sodipodi:role="line"
x="330.85974"
y="113.24544"
id="tspan4331">layering</tspan><tspan
sodipodi:role="line"
x="330.85974"
y="128.24544"
id="tspan4333">order</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.5 KiB

View file

@ -0,0 +1,240 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.2 r9819"
sodipodi:docname="layer-order-2.svg">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Mend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Mend"
style="overflow:visible;">
<path
id="path3913"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(0.6) rotate(180) translate(0,0)" />
</marker>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.4179431"
inkscape:cx="170.5"
inkscape:cy="941"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<g
id="g4432">
<g
id="g4403">
<rect
style="color:#000000;fill:none;stroke:#ff7100;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0-5-5"
width="72.585144"
height="29.6516"
x="221.75375"
y="174.13928" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.11232"
y="193.33777"
id="text3857"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3859"
x="255.11232"
y="193.33777">0</tspan></text>
</g>
<g
transform="translate(0,30.55542)"
id="g4398">
<rect
style="color:#000000;fill:none;stroke:#ff00de;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0-5"
width="72.585144"
height="29.6516"
x="123.29042"
y="143.58386" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="156.577"
y="162.39966"
id="text3861"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3863"
x="156.577"
y="162.39966">1</tspan></text>
</g>
<g
transform="translate(0,61.11084)"
id="g4393">
<rect
style="color:#000000;fill:none;stroke:#fff400;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-0"
width="72.585144"
height="29.6516"
x="42.5"
y="113.02844" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="75.858574"
y="131.84424"
id="text3865"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3867"
x="75.858574"
y="131.84424">2</tspan></text>
</g>
</g>
<g
id="g4421"
transform="translate(0,0.38900757)">
<g
transform="translate(0,55.00543)"
id="g4388">
<rect
style="color:#000000;fill:none;stroke:#ff0003;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7"
width="72.585144"
height="29.6516"
x="117.60985"
y="82.473022" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="150.96841"
y="101.16282"
id="text3869"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3871"
x="150.96841"
y="101.16282">3</tspan></text>
</g>
<g
transform="translate(0,85.56085)"
id="g4382">
<rect
style="color:#000000;fill:none;stroke:#0054ff;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985-7-4"
width="72.585144"
height="29.6516"
x="200.92497"
y="51.917603" />
<g
id="g4378">
<text
sodipodi:linespacing="125%"
id="text3873"
y="70.793404"
x="234.28354"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
xml:space="preserve"><tspan
y="70.793404"
x="234.28354"
id="tspan3875"
sodipodi:role="line">4</tspan></text>
</g>
</g>
</g>
<g
id="g4446">
<g
transform="translate(0,80.233484)"
id="g4414">
<g
id="g4408">
<g
id="g4373">
<rect
style="color:#000000;fill:none;stroke:#00ff00;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2985"
width="72.585144"
height="29.6516"
x="78.1614"
y="21.362185" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="111.51997"
y="40.045986"
id="text3877"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3879"
x="111.51997"
y="40.045986">5</tspan></text>
</g>
</g>
</g>
</g>
<path
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Mend)"
d="m 324.24253,185.09612 0,-78.57919"
id="path3881"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="330.85974"
y="140.01648"
id="text4327"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4329"
x="330.85974"
y="140.01648">increasing</tspan><tspan
sodipodi:role="line"
x="330.85974"
y="155.01648"
id="tspan4333">layer</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -1,19 +1,19 @@
\documentclass{article} \documentclass{article}
\title{Region Layering} \title{Region layering}
\author{} \author{}
\date{} \date{}
\usepackage{graphicx} \usepackage{graphicx,amsmath}
\begin{document} \begin{document}
\maketitle \maketitle
\section{Introduction} \section{Introduction}
When regions overlap in time, we need to decide which one should be When regions overlap in time, we need to decide which one should be
played. Ardour has a few options to set how this decision is made. played.
\subsection{Layers} \section{Layers}
Each region on a playlist is on a \emph{layer}. All overlapping regions Each region on a playlist is on a \emph{layer}. All overlapping regions
are on a unique layer, and when overlaps exist the highest-layered are on a unique layer, and when overlaps exist the highest-layered
@ -37,114 +37,168 @@ This follows the basic rule that, at any given point, the region on
the highest layer will be played. the highest layer will be played.
\section{Choice of layering} \section{Which layer does a region go on?}
There are two main decisions to be made with regards to how a playlist should be layered: The logic to decide which layer a region goes onto is somewhat complicated.
This section describes it in hand-wavey and more technical terms.
\begin{itemize}
\item Given overlapping regions, what order should they be layered in?
\item When should layering be changed?
\end{itemize}
\subsection{Layering order} \subsection{Hand-wavey description}
Ardour provides three-and-a-half ways to decide on the order in which A playlist maintains an internal \emph{layering order} for regions. This order
regions are layered. The most basic choice is: is not directly visible in Ardour, but it's useful to understand it
nonetheless. Figure~\ref{fig:layering-order-1} gives a rough idea of what this
\begin{itemize} means.
\item \emph{Later is higher} --- regions which are later in time will
be on higher layers.
\item \emph{Most recently added is higher} --- regions which were more
recently added to the playlist will be on higher layers.
\item \emph{Most recently edited or added is higher} --- regions which
were more recently edited or added to the playlist will be on
higher layers.
\end{itemize}
This choice can be set per-session from the \emph{Session Properties} dialogue
box.
\subsubsection{Explicit ordering}
There are also cases when none of these rules should apply. If, for
example, you want to put a given region at the top of the stack (on
the highest layer), this is possible using the region `raise to top'
command. Following such a command (called an `explicit layering'),
the regions on the playlist may no longer obey any of the standard
ordering rules.
This situation also arises when editing tracks using the `stacked' layer mode.
In this mode, almost all layering is explicit. When starting a region drag,
the other regions on a track spread apart vertically to allow the dragged
region to be dropped in any position within the region stack. The normal
layering rules will only be followed if a region is dropped on top of another;
in all other cases, explicit layering will be used to put the region wherever
it was dropped.
\subsection{When to update layering}
There are two distinct approaches to updating layering:
\begin{itemize}
\item Update whenever any region edit is performed.
\item Update only when a region is edited such that a new overlap has been set up.
\end{itemize}
The approach to use is optional, and can be set in \emph{Session Properties}.
This decision only has consequences when an explicit layering command has
been used. Consider the case in Figure~\ref{fig:explicit-layering1}.
\begin{figure}[ht] \begin{figure}[ht]
\begin{center} \begin{center}
\includegraphics{explicit-layering1.pdf} \includegraphics{layering-order-1.pdf}
\end{center} \end{center}
\caption{Explicit layering: stage 1} \caption{Layering order}
\label{fig:explicit-layering1} \label{fig:layering-order-1}
\end{figure} \end{figure}
Given that arrangement, imagine that we perform a `raise to top' on region $C$. Here we see 6 regions; as the layering order value increases, the region will
This results in the arrangement in Figure~\ref{fig:explicit-layering2}. be placed on a higher layer.
Every time any region is moved, added or edited, a \emph{relayer} occurs. This
collapses the regions down into layers. For our example, this would result in
the arrangement in Figure~\ref{fig:layering-order-2}.
\begin{figure}[ht] \begin{figure}[ht]
\begin{center} \begin{center}
\includegraphics{explicit-layering2.pdf} \includegraphics{layering-order-2.pdf}
\end{center} \end{center}
\caption{Explicit layering: stage 2} \caption{Layering}
\label{fig:explicit-layering2} \label{fig:layering-order-2}
\end{figure} \end{figure}
Imagine now that region $C$ is moved very slightly to the left, so The relayer operation takes each region, in the layering order, and puts it
that it still overlaps both $A$ and $B$. If we are updating whenever on the lowest possible layer that it can be on without overlap.
any region edit is performed, this will result in a relayer; the
regions' arrangement will go back to that in
Figure~\ref{fig:explicit-layering1}.
If, on the other hand, we only relayer when a new overlap is set up,
the region layering will remain as in
Figure~\ref{fig:explicit-layering2}. Before the edit, regions $A$,
$B$ and $C$ overlapped; after the edit, the situation is the same, so
no relayering is performed.
Another, more complex, example is shown in Figure~\ref{fig:tricky-explicit-layering}. \subsubsection{Layering order}
Given that arrangement, the remaining question is how the layering order is
arrived at. The rules are as follows:
\begin{itemize}
\item When a region is added to a playlist, it goes above the current highest
region in the layering order.
\item In `overlaid' track mode, moving or editing regions does not change the
layering order. Hence, moving regions about will maintain their position in
the layering order. Changing overlaps may change the \emph{layer} that the
region ends up on, but not the order in which they will be layered.
\item In `stacked' track mode, moving regions places the region on the layer
that they are dropped on. This is achieved by modifying the layering order
for the region that is moved, so that when the relayer operation happens the
region ends up on the desired layer.
\item When regions are `raised' or `lowered' in the stack, the layering order
is modified to achieve the desired layer change.
\end{itemize}
The upshot of all this is that regions should maintain their expected layering
order, unless that order is explicitly change using `stacked' mode or by
explicit layering commands like `raise' or `lower'.
\subsection{Technical description}
Each region on a playlist has three layering-related properties: its current
layer $c$ (an integer) and its layering index $i$ (also an integer). It also
has an \emph{optional} pending layer $p$ which is fractional.
Whenever a region is added, moved, trimmed, etc.\ we run a \emph{relayer}. This
does the following:
\begin{enumerate}
\item Take a list of all regions and remove those who have a value for $p$.
\item Sort the remainder in ascending order of $i$.
\item Insert the regions which have a value for $p$ in the correct place in the
list by comparing $c$ of those in the list to $p$ of the inserted region.
\item Iterate over the resulting list, putting each region on the lowest available
layer, setting its current layer $c$, and clearing $p$.
\item If any region had a pending layer, iterate through the region list again
giving each region a new layering index $i$ ascending from 0.
\end{enumerate}
The pending layer $p$ is set up in the following situations:
\begin{enumerate}
\item When a region is added to the playlist, $p$ is set to $\infty$.
\item When a region is raised to the top of the playlist, $p$ is set to $\infty$.
\item When a region is raised one step in the playlist, $p$ is set to $c + 1.5$.
\item When a region is lowered to the bottom of the playlist, $p$ is set to $-0.5$.
\item When a region is lowered one step int the playlist, $p$ is set to $c - 1.5$.
\item When a region is explicitly put between layers $A$ and $B$ in `stacked'
mode, $p$ is set to $(A + B) / 2$.
\end{enumerate}
The idea of this approach is that the layering indices $i$ are used to keep a
current state of the stack, and this state is used to maintain region
relationships. Setting $p$ will alter these relationships, after which the
layering indices $i$ are updated to reflect the new status quo.
It is not sufficient to use current layer $c$ as the state of the stack.
Consider two overlapping regions $P$ and $Q$, with $P$ on layer~0 and $Q$ on
layer~1. Now raise $P$ to the top of the stack, so that $Q$ is on layer~0 and
$P$ on layer~1. Move $P$ away from $Q$ (in overlaid mode) so that both regions
are on layer~0. Now drag $P$ back over $Q$. One would expect $P$ to return to
the top of the stack, since it was explicitly raised earlier. However, if the
relayer operation were to compare $c$ for each region, they would be identical;
the information that $P$ was once higher than $Q$ has been lost.
\section{Stacked mode}
When a track is being displayed in \emph{stacked} mode, regions are spread out
vertically to indicate their layering, like in Figure~\ref{fig:stacked}.
\begin{figure}[ht] \begin{figure}[ht]
\begin{center} \begin{center}
\includegraphics{tricky-explicit-layering.pdf} \includegraphics[scale=0.5]{stacked.png}
\end{center} \end{center}
\caption{More complex explicit layering} \caption{A track in stacked mode}
\label{fig:tricky-explicit-layering} \label{fig:stacked}
\end{figure} \end{figure}
% XXX: this makes no sense In this mode, layering is performed \emph{explicitly}. In other words, the
user's immediate actions decide which layer a region should be put on. When a
region move drag is started in stacked mode, the regions separate further out
vertically, to leave space between each layer, as shown in
Figure~\ref{fig:stacked-drag}.
Here, imagine that $C$ has been moved to the top of the stack with an explicit \begin{figure}[ht]
`raise to top' command. Now consider an extension of $C$ so that its \begin{center}
right-hand edge overlaps $D$. If we are relayering only on new overlaps, this \includegraphics[scale=0.5]{stacked-drag.png}
case presents one new overlap (that of $C$ with $D$). In this case, $C$ is \end{center}
moved according to the current layering rules so that it is correct with \caption{A track in stacked mode during a drag}
respect to $D$. In addition, $A$ and $B$ are re-layered so that the relation \label{fig:stacked-drag}
of $C$ to $A$ and $B$ is preserved. \end{figure}
The region(s) being dragged can then be dropped in any location, horizontally
and vertically, and the regions will be layered accordingly.
\section{Overlaid mode}
When a track is being displayed in \emph{overlaid} mode, regions are
displayed on top of one another, like in Figure~\ref{fig:overlaid}.
\begin{figure}[ht]
\begin{center}
\includegraphics[scale=0.5]{overlaid.png}
\end{center}
\caption{A track in overlaid mode}
\label{fig:overlaid}
\end{figure}
In this mode, drags of regions maintain the same \emph{layer ordering}, even if the layers may
change.
\end{document} \end{document}

View file

@ -1,323 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.47 r22583"
sodipodi:docname="tricky-explicit-layering1.svg">
<defs
id="defs4">
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0.0"
refX="0.0"
id="Arrow2Lend"
style="overflow:visible;">
<path
id="path3618"
style="font-size:12.0;fill-rule:evenodd;stroke-width:0.62500000;stroke-linejoin:round;"
d="M 8.7185878,4.0337352 L -2.2072895,0.016013256 L 8.7185884,-4.0017078 C 6.9730900,-1.6296469 6.9831476,1.6157441 8.7185878,4.0337352 z "
transform="scale(1.1) rotate(180) translate(1,0)" />
</marker>
<inkscape:perspective
sodipodi:type="inkscape:persp3d"
inkscape:vp_x="0 : 526.18109 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_z="744.09448 : 526.18109 : 1"
inkscape:persp3d-origin="372.04724 : 350.78739 : 1"
id="perspective10" />
<inkscape:perspective
id="perspective4058"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4089"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4120"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4151"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4365"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4386"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-6"
style="overflow:visible">
<path
id="path3618-4"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4449"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<marker
inkscape:stockid="Arrow2Lend"
orient="auto"
refY="0"
refX="0"
id="Arrow2Lend-5"
style="overflow:visible">
<path
id="path3618-1"
style="font-size:12px;fill-rule:evenodd;stroke-width:0.625;stroke-linejoin:round"
d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
</marker>
<inkscape:perspective
id="perspective4668"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
<inkscape:perspective
id="perspective4696-1"
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
inkscape:vp_z="1 : 0.5 : 1"
inkscape:vp_y="0 : 1000 : 0"
inkscape:vp_x="0 : 0.5 : 1"
sodipodi:type="inkscape:persp3d" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.633643"
inkscape:cx="191.36241"
inkscape:cy="670.78783"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:window-width="1280"
inkscape:window-height="949"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
gridtolerance="10">
<inkscape:grid
type="xygrid"
id="grid2816"
empspacing="5"
visible="true"
enabled="true"
snapvisiblegridlinesonly="true"
units="mm"
spacingx="5mm"
spacingy="5mm" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#Arrow2Lend);color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 88.582677,432.28344 159.448823,0"
id="path3592" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="255.20872"
y="435.23877"
id="text4042"><tspan
sodipodi:role="line"
id="tspan4044"
x="255.20872"
y="435.23877">time</tspan></text>
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818"
width="106.29921"
height="35.433071"
x="88.582695"
y="343.70078" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2"
width="88.582687"
height="35.433071"
x="124.01575"
y="379.13385" />
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2-4"
width="70.866142"
height="35.433071"
x="230.31496"
y="379.13385" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="137.33429"
y="365.70132"
id="text4320"><tspan
sodipodi:role="line"
id="tspan4322"
x="137.33429"
y="365.70132">A</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="164.22108"
y="400.94839"
id="text4324"><tspan
sodipodi:role="line"
id="tspan4326"
x="164.22108"
y="400.94839">B</tspan></text>
<rect
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
id="rect2818-2-2"
width="44.545933"
height="35.433071"
x="177.16534"
y="308.2677" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="195.20232"
y="330.08224"
id="text4328"><tspan
sodipodi:role="line"
id="tspan4330"
x="195.20232"
y="330.08224">C</tspan></text>
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="261.33801"
y="400.94839"
id="text4332"><tspan
sodipodi:role="line"
id="tspan4334"
x="261.33801"
y="400.94839">D</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:url(#Arrow2Lend);visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 318.89764,432.28344 0,-124.01575"
id="path3592-8"
sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="307.30875"
y="301.75519"
id="text4406"><tspan
sodipodi:role="line"
id="tspan4408"
x="307.30875"
y="301.75519">layer</tspan></text>
<path
style="fill:none;stroke:#000000;stroke-width:0.62500000000000000;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;color:#000000;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;marker:none;marker-end:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,397.273 13.54507,0"
id="path4439" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="401.58279"
id="text4656"><tspan
sodipodi:role="line"
id="tspan4658"
x="328.09393"
y="401.58279">0</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 312.32197,361.87109 13.54507,0"
id="path4439-1" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.02194"
y="366.18088"
id="text4656-0-0"><tspan
sodipodi:role="line"
id="tspan4658-6-3"
x="328.02194"
y="366.18088">1</tspan></text>
<path
style="color:#000000;fill:none;stroke:#000000;stroke-width:0.625;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
d="m 311.84462,325.98423 13.54507,0"
id="path4439-1-6" />
<text
xml:space="preserve"
style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:LMRoman12;-inkscape-font-specification:LMRoman12"
x="328.09393"
y="329.49646"
id="text4742"><tspan
sodipodi:role="line"
id="tspan4744"
x="328.09393"
y="329.49646">2</tspan></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 13 KiB

View file

@ -812,7 +812,7 @@ AudioStreamView::update_content_height (CrossfadeView* cv)
switch (_layer_display) { switch (_layer_display) {
case Overlaid: case Overlaid:
cv->set_y (0); cv->set_y (0);
cv->set_height (height); cv->set_heights (height, height);
break; break;
case Stacked: case Stacked:
@ -827,10 +827,10 @@ AudioStreamView::update_content_height (CrossfadeView* cv)
if (_layer_display == Stacked) { if (_layer_display == Stacked) {
cv->set_y ((_layers - high - 1) * h); cv->set_y ((_layers - high - 1) * h);
cv->set_height ((high - low + 1) * h); cv->set_heights ((high - low + 1) * h, h);
} else { } else {
cv->set_y (((_layers - high) * 2 - 1) * h); cv->set_y (((_layers - high) * 2 - 1) * h);
cv->set_height (((high - low) * 2 + 1) * h); cv->set_heights (((high - low) * 2 + 1) * h, h);
} }
} }
} }

View file

@ -57,7 +57,8 @@ CrossfadeView::CrossfadeView (ArdourCanvas::Group *parent,
crossfade (xf), crossfade (xf),
left_view (lview), left_view (lview),
right_view (rview), right_view (rview),
_all_in_view (false) _all_in_view (false),
_child_height (0)
{ {
_valid = true; _valid = true;
_visible = true; _visible = true;
@ -109,13 +110,15 @@ CrossfadeView::reset_width_dependent_items (double pixel_width)
} }
void void
CrossfadeView::set_height (double h) CrossfadeView::set_heights (double fade_height, double child_height)
{ {
if (h > TimeAxisView::preset_height (HeightSmall)) { if (child_height > TimeAxisViewItem::NAME_HIGHLIGHT_THRESH) {
h -= NAME_HIGHLIGHT_SIZE; fade_height -= NAME_HIGHLIGHT_SIZE;
child_height -= NAME_HIGHLIGHT_SIZE;
} }
TimeAxisViewItem::set_height (h); TimeAxisViewItem::set_height (fade_height);
_child_height = child_height;
redraw_curves (); redraw_curves ();
} }
@ -203,7 +206,9 @@ CrossfadeView::redraw_curves ()
for (int i = 0, pci = 0; i < npoints; ++i) { for (int i = 0, pci = 0; i < npoints; ++i) {
Art::Point &p = (*points)[pci++]; Art::Point &p = (*points)[pci++];
p.set_x (xoff + i + 1); p.set_x (xoff + i + 1);
p.set_y (_height - ((_height - 2) * vec[i]));
double const ho = crossfade->in()->layer() > crossfade->out()->layer() ? _child_height : _height;
p.set_y (ho - ((_child_height - 2) * vec[i]));
} }
fade_in->property_points() = *points; fade_in->property_points() = *points;
@ -213,7 +218,9 @@ CrossfadeView::redraw_curves ()
for (int i = 0, pci = 0; i < npoints; ++i) { for (int i = 0, pci = 0; i < npoints; ++i) {
Art::Point &p = (*points)[pci++]; Art::Point &p = (*points)[pci++];
p.set_x (xoff + i + 1); p.set_x (xoff + i + 1);
p.set_y (_height - ((_height - 2) * vec[i]));
double const ho = crossfade->in()->layer() < crossfade->out()->layer() ? _child_height : _height;
p.set_y (ho - ((_child_height - 2) * vec[i]));
} }
fade_out->property_points() = *points; fade_out->property_points() = *points;

View file

@ -48,7 +48,7 @@ public:
AudioRegionView& left_view; // and these too AudioRegionView& left_view; // and these too
AudioRegionView& right_view; AudioRegionView& right_view;
void set_height (double); void set_heights (double, double);
bool valid() const { return _valid; } bool valid() const { return _valid; }
bool visible() const { return _visible; } bool visible() const { return _visible; }
@ -68,6 +68,7 @@ private:
bool _valid; bool _valid;
bool _visible; bool _visible;
bool _all_in_view; bool _all_in_view;
double _child_height;
ArdourCanvas::Line *fade_in; ArdourCanvas::Line *fade_in;
ArdourCanvas::Line *fade_out; ArdourCanvas::Line *fade_out;

View file

@ -2475,7 +2475,7 @@ Editor::get_state ()
/** @param y y offset from the top of all trackviews. /** @param y y offset from the top of all trackviews.
* @return pair: TimeAxisView that y is over, layer index. * @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 * TimeAxisView may be 0. Layer index is the layer number if the TimeAxisView is valid and is
* in stacked region display mode, otherwise 0. * in stacked or expanded region display mode, otherwise 0.
*/ */
std::pair<TimeAxisView *, double> std::pair<TimeAxisView *, double>
Editor::trackview_by_y_position (double y) Editor::trackview_by_y_position (double y)

View file

@ -488,10 +488,6 @@ RegionMotionDrag::start_grab (GdkEvent* event, Gdk::Cursor* cursor)
pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ()); pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
_last_pointer_time_axis_view = find_time_axis_view (tv.first); _last_pointer_time_axis_view = find_time_axis_view (tv.first);
_last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second; _last_pointer_layer = tv.first->layer_display() == Overlaid ? 0 : tv.second;
if (tv.first->view()->layer_display() == Stacked) {
tv.first->view()->set_layer_display (Expanded);
}
} }
double double
@ -578,7 +574,6 @@ RegionMotionDrag::y_movement_allowed (int delta_track, double delta_layer) const
/* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded' /* Note that we allow layer to be up to 0.5 below zero, as this is used by `Expanded'
mode to allow the user to place a region below another on layer 0. mode to allow the user to place a region below another on layer 0.
*/ */
if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) { if (delta_track == 0 && (l < -0.5 || l >= int (to->view()->layers()))) {
/* Off the top or bottom layer; note that we only refuse if the track hasn't changed. /* Off the top or bottom layer; note that we only refuse if the track hasn't changed.
If it has, the layers will be munged later anyway, so it's ok. If it has, the layers will be munged later anyway, so it's ok.
@ -599,6 +594,10 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
/* Find the TimeAxisView that the pointer is now over */ /* Find the TimeAxisView that the pointer is now over */
pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ()); pair<TimeAxisView*, double> const tv = _editor->trackview_by_y_position (_drags->current_pointer_y ());
if (first_move && tv.first->view()->layer_display() == Stacked) {
tv.first->view()->set_layer_display (Expanded);
}
/* Bail early if we're not over a track */ /* Bail early if we're not over a track */
RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first); RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv.first);
if (!rtv || !rtv->is_track()) { if (!rtv || !rtv->is_track()) {
@ -646,21 +645,21 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
if (first_move) { if (first_move) {
rv->get_time_axis_view().hide_dependent_views (*rv); rv->get_time_axis_view().hide_dependent_views (*rv);
/* Absolutely no idea why this is necessary, but it is; without
it, the region view disappears after the reparent.
*/
_editor->update_canvas_now ();
/* Reparent to a non scrolling group so that we can keep the /* Reparent to a non scrolling group so that we can keep the
region selection above all time axis views. region selection above all time axis views.
Reparenting means that we will have to move the region view Reparenting means that we will have to move the region view
later, as the two parent groups have different coordinates. later, as the two parent groups have different coordinates.
*/ */
rv->get_canvas_group()->reparent (*(_editor->_region_motion_group)); rv->get_canvas_group()->reparent (*(_editor->_region_motion_group));
rv->fake_set_opaque (true); rv->fake_set_opaque (true);
if (!rv->get_time_axis_view().hidden()) {
/* the track that this region view is on is hidden, so hide the region too */
rv->get_canvas_group()->hide ();
}
} }
/* If we have moved tracks, we'll fudge the layer delta so that the /* If we have moved tracks, we'll fudge the layer delta so that the
@ -680,12 +679,12 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move)
if (tv->view()->layer_display() == Stacked) { if (tv->view()->layer_display() == Stacked) {
tv->view()->set_layer_display (Expanded); tv->view()->set_layer_display (Expanded);
} }
/* We're only allowed to go -ve in layer on Expanded views */ /* We're only allowed to go -ve in layer on Expanded views */
if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) { if (tv->view()->layer_display() != Expanded && (i->layer + this_delta_layer) < 0) {
this_delta_layer = - i->layer; this_delta_layer = - i->layer;
} }
/* Set height */ /* Set height */
rv->set_height (tv->view()->child_height ()); rv->set_height (tv->view()->child_height ());
@ -826,12 +825,13 @@ RegionMotionDrag::finished (GdkEvent *, bool)
(*i)->view()->set_layer_display (Stacked); (*i)->view()->set_layer_display (Stacked);
} }
} }
} }
void void
RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred) RegionMoveDrag::finished (GdkEvent* ev, bool movement_occurred)
{ {
RegionMotionDrag::finished (ev, movement_occurred); RegionMotionDrag::finished (ev, movement_occurred);
if (!movement_occurred) { if (!movement_occurred) {
/* just a click */ /* just a click */
return; return;
@ -965,9 +965,6 @@ RegionMoveDrag::finished_no_copy (
RegionSelection new_views; RegionSelection new_views;
PlaylistSet modified_playlists; PlaylistSet modified_playlists;
PlaylistSet frozen_playlists; PlaylistSet frozen_playlists;
list<pair<boost::shared_ptr<Region>, double> > pending_explicit_relayers;
Playlist::RegionList pending_implicit_relayers;
set<RouteTimeAxisView*> views_to_update; set<RouteTimeAxisView*> views_to_update;
if (_brushing) { if (_brushing) {
@ -1049,12 +1046,8 @@ RegionMoveDrag::finished_no_copy (
boost::shared_ptr<Playlist> playlist = dest_rtv->playlist(); boost::shared_ptr<Playlist> playlist = dest_rtv->playlist();
bool const explicit_relayer = dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded; if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
rv->region()->set_pending_layer (dest_layer);
if (explicit_relayer) {
pending_explicit_relayers.push_back (make_pair (rv->region (), dest_layer));
} else {
pending_implicit_relayers.push_back (rv->region ());
} }
/* freeze playlist to avoid lots of relayering in the case of a multi-region drag */ /* freeze playlist to avoid lots of relayering in the case of a multi-region drag */
@ -1063,7 +1056,6 @@ RegionMoveDrag::finished_no_copy (
if (r.second) { if (r.second) {
playlist->freeze (); playlist->freeze ();
playlist->suspend_relayer ();
} }
/* this movement may result in a crossfade being modified, so we need to get undo /* this movement may result in a crossfade being modified, so we need to get undo
@ -1119,24 +1111,10 @@ RegionMoveDrag::finished_no_copy (
_editor->selection->set (new_views); _editor->selection->set (new_views);
} }
/* We can't use the normal mechanism for relayering, as some regions may require an explicit relayer for (set<boost::shared_ptr<Playlist> >::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
rather than an implicit one. So we thaw before resuming relayering, then do the relayers
that we require.
*/
for (PlaylistSet::iterator p = frozen_playlists.begin(); p != frozen_playlists.end(); ++p) {
(*p)->thaw(); (*p)->thaw();
(*p)->resume_relayer ();
} }
for (list<pair<boost::shared_ptr<Region>, double> >::iterator i = pending_explicit_relayers.begin(); i != pending_explicit_relayers.end(); ++i) {
i->first->playlist()->relayer (i->first, i->second);
}
for (Playlist::RegionList::iterator i = pending_implicit_relayers.begin(); i != pending_implicit_relayers.end(); ++i) {
(*i)->playlist()->relayer (*i);
}
/* write commands for the accumulated diffs for all our modified playlists */ /* write commands for the accumulated diffs for all our modified playlists */
add_stateful_diff_commands_for_playlists (modified_playlists); add_stateful_diff_commands_for_playlists (modified_playlists);
@ -1144,11 +1122,11 @@ RegionMoveDrag::finished_no_copy (
/* We have futzed with the layering of canvas items on our streamviews. /* We have futzed with the layering of canvas items on our streamviews.
If any region changed layer, this will have resulted in the stream If any region changed layer, this will have resulted in the stream
views being asked to set up their region views, and all will be views being asked to set up their region views, and all will be well.
well. If not, we might now have badly-ordered region views. Ask If not, we might now have badly-ordered region views. Ask the StreamViews
the Streamviews involved to sort themselves out, just in case. involved to sort themselves out, just in case.
*/ */
for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) { for (set<RouteTimeAxisView*>::iterator i = views_to_update.begin(); i != views_to_update.end(); ++i) {
(*i)->view()->playlist_layered ((*i)->track ()); (*i)->view()->playlist_layered ((*i)->track ());
} }
@ -1214,7 +1192,7 @@ RegionMoveDrag::insert_region_into_playlist (
dest_playlist->add_region (region, where); dest_playlist->add_region (region, where);
if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) { if (dest_rtv->view()->layer_display() == Stacked || dest_rtv->view()->layer_display() == Expanded) {
dest_playlist->relayer (region, dest_layer); region->set_pending_layer (dest_layer);
} }
c.disconnect (); c.disconnect ();
@ -1268,7 +1246,7 @@ RegionMotionDrag::aborted (bool)
(*i)->view()->set_layer_display (Stacked); (*i)->view()->set_layer_display (Stacked);
} }
} }
for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) { for (list<DraggingView>::const_iterator i = _views.begin(); i != _views.end(); ++i) {
RegionView* rv = i->view; RegionView* rv = i->view;
TimeAxisView* tv = &(rv->get_time_axis_view ()); TimeAxisView* tv = &(rv->get_time_axis_view ());

View file

@ -249,7 +249,7 @@ struct DraggingView
* or -1 if it is not visible. * or -1 if it is not visible.
*/ */
int time_axis_view; int time_axis_view;
/** Layer that this region is currently being displayed on. This is a double /** layer that this region is currently being displayed on. This is a double
rather than a layer_t as we use fractional layers during drags to allow the user rather than a layer_t as we use fractional layers during drags to allow the user
to indicate a new layer to put a region on. to indicate a new layer to put a region on.
*/ */

View file

@ -262,29 +262,6 @@ SessionOptionEditor::SessionOptionEditor (Session* s)
/* Misc */ /* Misc */
add_option (_("Misc"), new OptionEditorHeading (_("Layering (in overlaid mode)")));
ComboOption<LayerModel>* lm = new ComboOption<LayerModel> (
"layer-model",
_("Layering model"),
sigc::mem_fun (*_session_config, &SessionConfiguration::get_layer_model),
sigc::mem_fun (*_session_config, &SessionConfiguration::set_layer_model)
);
lm->add (LaterHigher, _("later is higher"));
lm->add (AddOrBoundsChangeHigher, _("most recently edited or added is higher"));
lm->add (AddHigher, _("most recently added is higher"));
add_option (_("Misc"), new BoolOption (
"relayer-on-all-edits",
_("Relayer regions after every edit"),
sigc::mem_fun (*_session_config, &SessionConfiguration::get_relayer_on_all_edits),
sigc::mem_fun (*_session_config, &SessionConfiguration::set_relayer_on_all_edits)
));
add_option (_("Misc"), lm);
add_option (_("Misc"), new OptionEditorHeading (_("MIDI Options"))); add_option (_("Misc"), new OptionEditorHeading (_("MIDI Options")));
add_option (_("Misc"), new BoolOption ( add_option (_("Misc"), new BoolOption (

View file

@ -181,7 +181,7 @@ StreamView::add_region_view (boost::weak_ptr<Region> wr)
add_region_view_internal (r, true); add_region_view_internal (r, true);
if (_layer_display == Stacked) { if (_layer_display == Stacked || _layer_display == Expanded) {
update_contents_height (); update_contents_height ();
} }
} }
@ -301,7 +301,7 @@ StreamView::playlist_layered (boost::weak_ptr<Track> wtr)
_layers = tr->playlist()->top_layer() + 1; _layers = tr->playlist()->top_layer() + 1;
} }
if (_layer_display == Stacked || _layer_display == Expanded) { if (_layer_display == Stacked) {
update_contents_height (); update_contents_height ();
update_coverage_frames (); update_coverage_frames ();
} else { } else {
@ -574,7 +574,7 @@ StreamView::child_height () const
case Expanded: case Expanded:
return height / (_layers * 2 + 1); return height / (_layers * 2 + 1);
} }
/* NOTREACHED */ /* NOTREACHED */
return height; return height;
} }

View file

@ -1196,7 +1196,7 @@ TimeAxisView::color_handler ()
* If the covering object is a child axis, then the child is returned. * If the covering object is a child axis, then the child is returned.
* TimeAxisView is 0 otherwise. * TimeAxisView is 0 otherwise.
* Layer index is the layer number (possibly fractional) if the TimeAxisView is valid * Layer index is the layer number (possibly fractional) if the TimeAxisView is valid
* and is in stacked or expanded region display mode, otherwise 0. * and is in stacked or expanded * region display mode, otherwise 0.
*/ */
std::pair<TimeAxisView*, double> std::pair<TimeAxisView*, double>
TimeAxisView::covers_y_position (double y) TimeAxisView::covers_y_position (double y)
@ -1215,7 +1215,7 @@ TimeAxisView::covers_y_position (double y)
case Stacked: case Stacked:
if (view ()) { if (view ()) {
/* compute layer */ /* compute layer */
l = floor ((_y_position + height - y) / (view()->child_height ())); l = layer_t ((_y_position + height - y) / (view()->child_height ()));
/* clamp to max layers to be on the safe side; sometimes the above calculation /* clamp to max layers to be on the safe side; sometimes the above calculation
returns a too-high value */ returns a too-high value */
if (l >= view()->layers ()) { if (l >= view()->layers ()) {
@ -1225,7 +1225,7 @@ TimeAxisView::covers_y_position (double y)
break; break;
case Expanded: case Expanded:
if (view ()) { if (view ()) {
int n = floor ((_y_position + height - y) / (view()->child_height ())); int const n = floor ((_y_position + height - y) / (view()->child_height ()));
l = n * 0.5 - 0.5; l = n * 0.5 - 0.5;
if (l >= (view()->layers() - 0.5)) { if (l >= (view()->layers() - 0.5)) {
l = view()->layers() - 0.5; l = view()->layers() - 0.5;

View file

@ -44,9 +44,6 @@
#include "ardour/session_object.h" #include "ardour/session_object.h"
#include "ardour/data_type.h" #include "ardour/data_type.h"
class PlaylistOverlapCacheTest;
class PlaylistLayeringTest;
namespace ARDOUR { namespace ARDOUR {
class Session; class Session;
@ -225,10 +222,7 @@ public:
framepos_t find_next_top_layer_position (framepos_t) const; framepos_t find_next_top_layer_position (framepos_t) const;
uint32_t combine_ops() const { return _combine_ops; } uint32_t combine_ops() const { return _combine_ops; }
void relayer (boost::shared_ptr<Region>); uint64_t highest_layering_index () const;
void relayer (boost::shared_ptr<Region>, double);
void suspend_relayer ();
void resume_relayer ();
protected: protected:
friend class Session; friend class Session;
@ -355,7 +349,7 @@ public:
boost::shared_ptr<Playlist> cut (framepos_t start, framecnt_t cnt, bool result_is_hidden); boost::shared_ptr<Playlist> cut (framepos_t start, framecnt_t cnt, bool result_is_hidden);
boost::shared_ptr<Playlist> copy (framepos_t start, framecnt_t cnt, bool result_is_hidden); boost::shared_ptr<Playlist> copy (framepos_t start, framecnt_t cnt, bool result_is_hidden);
void relayer (RegionList const &); void relayer ();
void begin_undo (); void begin_undo ();
void end_undo (); void end_undo ();
@ -382,71 +376,6 @@ public:
with its constituent regions with its constituent regions
*/ */
virtual void pre_uncombine (std::vector<boost::shared_ptr<Region> >&, boost::shared_ptr<Region>) {} virtual void pre_uncombine (std::vector<boost::shared_ptr<Region> >&, boost::shared_ptr<Region>) {}
private:
friend class ::PlaylistOverlapCacheTest;
friend class ::PlaylistLayeringTest;
/** A class which is used to store temporary (fractional)
* layer assignments for some regions.
*/
class TemporaryLayers
{
public:
void set (boost::shared_ptr<Region>, double);
double get (boost::shared_ptr<Region>) const;
private:
typedef std::map<boost::shared_ptr<Region>, double> Map;
Map _map;
};
/** Class to sort by temporary layer, for use with std::list<>::sort() */
class SortByTemporaryLayer
{
public:
SortByTemporaryLayer (TemporaryLayers const & t)
: _temporary_layers (t) {}
bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) const {
return _temporary_layers.get (a) < _temporary_layers.get (b);
}
private:
Playlist::TemporaryLayers const & _temporary_layers;
};
/** A cache of what overlaps what, for a given playlist in a given state.
* Divides a playlist up into time periods and notes which regions cover those
* periods, so that get() is reasonably quick.
*/
class OverlapCache
{
public:
OverlapCache (Playlist *);
RegionList get (Evoral::Range<framepos_t>) const;
private:
std::pair<int, int> cache_indices (Evoral::Range<framepos_t>) const;
double _division_size;
std::vector<RegionList> _cache;
Evoral::Range<framepos_t> _range;
static int const _divisions;
};
TemporaryLayers compute_temporary_layers (RegionList const &);
void commit_temporary_layers (TemporaryLayers const &);
RegionList recursive_regions_touched (boost::shared_ptr<Region>, OverlapCache const &, boost::shared_ptr<Region>) const;
void recursive_regions_touched_sub (boost::shared_ptr<Region>, OverlapCache const &, boost::shared_ptr<Region>, RegionList &) const;
void timestamp_layer_op (LayerOp, boost::shared_ptr<Region>);
uint64_t layer_op_counter;
bool _relayer_suspended;
}; };
} /* namespace ARDOUR */ } /* namespace ARDOUR */

View file

@ -65,6 +65,7 @@ namespace Properties {
extern PBD::PropertyDescriptor<float> stretch; extern PBD::PropertyDescriptor<float> stretch;
extern PBD::PropertyDescriptor<float> shift; extern PBD::PropertyDescriptor<float> shift;
extern PBD::PropertyDescriptor<PositionLockStyle> position_lock_style; extern PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
extern PBD::PropertyDescriptor<uint64_t> layering_index;
}; };
class Playlist; class Playlist;
@ -112,7 +113,6 @@ class Region
framepos_t start () const { return _start; } framepos_t start () const { return _start; }
framecnt_t length () const { return _length; } framecnt_t length () const { return _length; }
layer_t layer () const { return _layer; } layer_t layer () const { return _layer; }
Evoral::Range<framepos_t> bounds () const;
framecnt_t source_length(uint32_t n) const; framecnt_t source_length(uint32_t n) const;
uint32_t max_source_level () const; uint32_t max_source_level () const;
@ -202,7 +202,7 @@ class Region
void cut_front (framepos_t new_position); void cut_front (framepos_t new_position);
void cut_end (framepos_t new_position); void cut_end (framepos_t new_position);
void set_layer (layer_t l); /* ONLY Playlist should call this */ void set_layer (layer_t l); /* ONLY Playlist can call this */
void raise (); void raise ();
void lower (); void lower ();
void raise_to_top (); void raise_to_top ();
@ -252,8 +252,8 @@ class Region
virtual boost::shared_ptr<Region> get_parent() const; virtual boost::shared_ptr<Region> get_parent() const;
uint64_t last_layer_op (LayerOp) const; uint64_t layering_index () const { return _layering_index; }
void set_last_layer_op (LayerOp, uint64_t); void set_layering_index (uint64_t when) { _layering_index = when; }
virtual bool is_dependent() const { return false; } virtual bool is_dependent() const { return false; }
virtual bool depends_on (boost::shared_ptr<Region> /*other*/) const { return false; } virtual bool depends_on (boost::shared_ptr<Region> /*other*/) const { return false; }
@ -294,12 +294,11 @@ class Region
void invalidate_transients (); void invalidate_transients ();
void drop_sources (); void set_pending_layer (double);
bool reset_pending_layer ();
boost::optional<double> pending_layer () const;
/** @return our bounds the last time our relayer() method was called */ void drop_sources ();
Evoral::Range<framepos_t> last_relayer_bounds () const {
return Evoral::Range<framepos_t> (_last_relayer_bounds_from, _last_relayer_bounds_to);
}
protected: protected:
friend class RegionFactory; friend class RegionFactory;
@ -384,18 +383,15 @@ class Region
PBD::Property<float> _stretch; PBD::Property<float> _stretch;
PBD::Property<float> _shift; PBD::Property<float> _shift;
PBD::EnumProperty<PositionLockStyle> _position_lock_style; PBD::EnumProperty<PositionLockStyle> _position_lock_style;
PBD::Property<uint64_t> _layering_index;
/* XXX: could use a Evoral::Range<> but I'm too lazy to make PBD::Property serialize such a thing nicely */
PBD::Property<framepos_t> _last_relayer_bounds_from; ///< from of our bounds last time relayer() was called
PBD::Property<framepos_t> _last_relayer_bounds_to; ///< to of our bounds last time relayer() was called
PBD::Property<uint64_t> _last_layer_op_add;
PBD::Property<uint64_t> _last_layer_op_bounds_change;
framecnt_t _last_length; framecnt_t _last_length;
framepos_t _last_position; framepos_t _last_position;
mutable RegionEditState _first_edit; mutable RegionEditState _first_edit;
Timecode::BBT_Time _bbt_time; Timecode::BBT_Time _bbt_time;
boost::optional<double> _pending_layer;
void register_properties (); void register_properties ();
void use_sources (SourceList const &); void use_sources (SourceList const &);

View file

@ -36,22 +36,6 @@ struct RegionSortByLayer {
} }
}; };
struct RegionSortByAdd {
bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
return (
(a->last_layer_op (LayerOpAdd) < b->last_layer_op (LayerOpAdd))
);
}
};
struct RegionSortByAddOrBounds {
bool operator() (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
uint64_t const p = std::max (a->last_layer_op (LayerOpAdd), a->last_layer_op (LayerOpBoundsChange));
uint64_t const q = std::max (b->last_layer_op (LayerOpAdd), b->last_layer_op (LayerOpBoundsChange));
return p < q;
}
};
} // namespace } // namespace
#endif /* __libardour_region_sorters_h__ */ #endif /* __libardour_region_sorters_h__ */

View file

@ -47,8 +47,6 @@ CONFIG_VARIABLE_SPECIAL(std::string, audio_search_path, "audio-search-path", "",
CONFIG_VARIABLE_SPECIAL(std::string, midi_search_path, "midi-search-path", "", search_path_expand) CONFIG_VARIABLE_SPECIAL(std::string, midi_search_path, "midi-search-path", "", search_path_expand)
CONFIG_VARIABLE (std::string, bwf_country_code, "bwf-country-code", "US") CONFIG_VARIABLE (std::string, bwf_country_code, "bwf-country-code", "US")
CONFIG_VARIABLE (std::string, bwf_organization_code, "bwf-organization-code", "US") CONFIG_VARIABLE (std::string, bwf_organization_code, "bwf-organization-code", "US")
CONFIG_VARIABLE (LayerModel, layer_model, "layer-model", AddOrBoundsChangeHigher)
CONFIG_VARIABLE (bool, relayer_on_all_edits, "relayer-on-all-edits", true)
CONFIG_VARIABLE (std::string, auditioner_output_left, "auditioner-output-left", "default") CONFIG_VARIABLE (std::string, auditioner_output_left, "auditioner-output-left", "default")
CONFIG_VARIABLE (std::string, auditioner_output_right, "auditioner-output-right", "default") CONFIG_VARIABLE (std::string, auditioner_output_right, "auditioner-output-right", "default")
CONFIG_VARIABLE (bool, timecode_source_is_synced, "timecode-source-is-synced", true) CONFIG_VARIABLE (bool, timecode_source_is_synced, "timecode-source-is-synced", true)

View file

@ -409,12 +409,6 @@ namespace ARDOUR {
ShortCrossfade ShortCrossfade
}; };
enum LayerModel {
LaterHigher,
AddOrBoundsChangeHigher,
AddHigher
};
enum ListenPosition { enum ListenPosition {
AfterFaderListen, AfterFaderListen,
PreFaderListen PreFaderListen
@ -592,11 +586,6 @@ namespace ARDOUR {
FadeLogB FadeLogB
}; };
enum LayerOp {
LayerOpAdd,
LayerOpBoundsChange
};
} // namespace ARDOUR } // namespace ARDOUR
@ -613,7 +602,6 @@ std::istream& operator>>(std::istream& o, ARDOUR::PFLPosition& sf);
std::istream& operator>>(std::istream& o, ARDOUR::AFLPosition& sf); std::istream& operator>>(std::istream& o, ARDOUR::AFLPosition& sf);
std::istream& operator>>(std::istream& o, ARDOUR::RemoteModel& sf); 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::ListenPosition& sf);
std::istream& operator>>(std::istream& o, ARDOUR::LayerModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::InsertMergePolicy& 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::CrossfadeModel& sf);
std::istream& operator>>(std::istream& o, ARDOUR::SyncSource& sf); std::istream& operator>>(std::istream& o, ARDOUR::SyncSource& sf);
@ -634,7 +622,6 @@ std::ostream& operator<<(std::ostream& o, const ARDOUR::PFLPosition& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::AFLPosition& sf); std::ostream& operator<<(std::ostream& o, const ARDOUR::AFLPosition& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::RemoteModel& 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::ListenPosition& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::LayerModel& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::InsertMergePolicy& 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::CrossfadeModel& sf);
std::ostream& operator<<(std::ostream& o, const ARDOUR::SyncSource& sf); std::ostream& operator<<(std::ostream& o, const ARDOUR::SyncSource& sf);

View file

@ -1510,6 +1510,8 @@ AudioDiskstream::transport_stopped_wallclock (struct tm& when, time_t twhen, boo
} }
i_am_the_modifier++; i_am_the_modifier++;
region->set_pending_layer (max_layer);
_playlist->add_region (region, (*ci)->start, 1, non_layered()); _playlist->add_region (region, (*ci)->start, 1, non_layered());
i_am_the_modifier--; i_am_the_modifier--;

View file

@ -73,7 +73,6 @@ setup_enum_writer ()
RemoteModel _RemoteModel; RemoteModel _RemoteModel;
DenormalModel _DenormalModel; DenormalModel _DenormalModel;
CrossfadeModel _CrossfadeModel; CrossfadeModel _CrossfadeModel;
LayerModel _LayerModel;
InsertMergePolicy _InsertMergePolicy; InsertMergePolicy _InsertMergePolicy;
ListenPosition _ListenPosition; ListenPosition _ListenPosition;
SampleFormat _SampleFormat; SampleFormat _SampleFormat;
@ -264,11 +263,6 @@ setup_enum_writer ()
REGISTER_ENUM (ShortCrossfade); REGISTER_ENUM (ShortCrossfade);
REGISTER (_CrossfadeModel); REGISTER (_CrossfadeModel);
REGISTER_ENUM (LaterHigher);
REGISTER_ENUM (AddOrBoundsChangeHigher);
REGISTER_ENUM (AddHigher);
REGISTER (_LayerModel);
REGISTER_ENUM (InsertMergeReject); REGISTER_ENUM (InsertMergeReject);
REGISTER_ENUM (InsertMergeRelax); REGISTER_ENUM (InsertMergeRelax);
REGISTER_ENUM (InsertMergeReplace); REGISTER_ENUM (InsertMergeReplace);
@ -711,19 +705,6 @@ std::ostream& operator<<(std::ostream& o, const ListenPosition& var)
std::string s = enum_2_string (var); std::string s = enum_2_string (var);
return o << s; return o << s;
} }
std::istream& operator>>(std::istream& o, LayerModel& var)
{
std::string s;
o >> s;
var = (LayerModel) string_2_enum (s, var);
return o;
}
std::ostream& operator<<(std::ostream& o, const LayerModel& var)
{
std::string s = enum_2_string (var);
return o << s;
}
std::istream& operator>>(std::istream& o, InsertMergePolicy& var) std::istream& operator>>(std::istream& o, InsertMergePolicy& var)
{ {

View file

@ -30,7 +30,6 @@
#include "pbd/convert.h" #include "pbd/convert.h"
#include "pbd/failed_constructor.h" #include "pbd/failed_constructor.h"
#include "pbd/stacktrace.h"
#include "pbd/stateful_diff_command.h" #include "pbd/stateful_diff_command.h"
#include "pbd/xml++.h" #include "pbd/xml++.h"
@ -187,8 +186,6 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, string namestr, boo
in_partition = false; in_partition = false;
subcnt = 0; subcnt = 0;
_frozen = other->_frozen; _frozen = other->_frozen;
layer_op_counter = other->layer_op_counter;
} }
Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide) Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, framecnt_t cnt, string str, bool hide)
@ -256,6 +253,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> other, framepos_t start, f
plist.add (Properties::length, len); plist.add (Properties::length, len);
plist.add (Properties::name, new_name); plist.add (Properties::name, new_name);
plist.add (Properties::layer, region->layer()); plist.add (Properties::layer, region->layer());
plist.add (Properties::layering_index, region->layering_index());
new_region = RegionFactory::RegionFactory::create (region, plist); new_region = RegionFactory::RegionFactory::create (region, plist);
@ -318,9 +316,7 @@ Playlist::init (bool hide)
in_partition = false; in_partition = false;
subcnt = 0; subcnt = 0;
_frozen = false; _frozen = false;
layer_op_counter = 0;
_combine_ops = 0; _combine_ops = 0;
_relayer_suspended = false;
_session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this)); _session.history().BeginUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::begin_undo, this));
_session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this)); _session.history().EndUndoRedo.connect_same_thread (*this, boost::bind (&Playlist::end_undo, this));
@ -485,8 +481,6 @@ Playlist::notify_region_moved (boost::shared_ptr<Region> r)
{ {
Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ()); Evoral::RangeMove<framepos_t> const move (r->last_position (), r->length (), r->position ());
/* We could timestamp the region's layer op here, but we're doing it in region_bounds_changed */
if (holding_state ()) { if (holding_state ()) {
pending_range_moves.push_back (move); pending_range_moves.push_back (move);
@ -503,8 +497,6 @@ Playlist::notify_region_moved (boost::shared_ptr<Region> r)
void void
Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r) Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
{ {
timestamp_layer_op (LayerOpBoundsChange, r);
if (r->position() >= r->last_position()) { if (r->position() >= r->last_position()) {
/* trimmed shorter */ /* trimmed shorter */
return; return;
@ -528,8 +520,6 @@ Playlist::notify_region_start_trimmed (boost::shared_ptr<Region> r)
void void
Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r) Playlist::notify_region_end_trimmed (boost::shared_ptr<Region> r)
{ {
timestamp_layer_op (LayerOpBoundsChange, r);
if (r->length() < r->last_length()) { if (r->length() < r->last_length()) {
/* trimmed shorter */ /* trimmed shorter */
} }
@ -556,8 +546,6 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
as though it could be. as though it could be.
*/ */
timestamp_layer_op (LayerOpAdd, r);
if (holding_state()) { if (holding_state()) {
pending_adds.insert (r); pending_adds.insert (r);
pending_contents_change = true; pending_contents_change = true;
@ -566,7 +554,6 @@ Playlist::notify_region_added (boost::shared_ptr<Region> r)
pending_contents_change = false; pending_contents_change = false;
RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */ RegionAdded (boost::weak_ptr<Region> (r)); /* EMIT SIGNAL */
ContentsChanged (); /* EMIT SIGNAL */ ContentsChanged (); /* EMIT SIGNAL */
relayer (r);
} }
} }
@ -584,35 +571,31 @@ Playlist::flush_notifications (bool from_undo)
in_flush = true; in_flush = true;
/* We have:
pending_bounds: regions whose bounds position and/or length changes
pending_removes: regions which were removed
pending_adds: regions which were added
pending_length: true if the playlist length might have changed
pending_contents_change: true if there was almost any change in the playlist
pending_range_moves: details of periods of time that have been moved about (when regions have been moved)
*/
if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) { if (!pending_bounds.empty() || !pending_removes.empty() || !pending_adds.empty()) {
regions_changed = true; regions_changed = true;
} }
/* Make a list of regions that need relayering */ /* we have no idea what order the regions ended up in pending
RegionList regions_to_relayer; bounds (it could be based on selection order, for example).
so, to preserve layering in the "most recently moved is higher"
model, sort them by existing layer, then timestamp them.
*/
// RegionSortByLayer cmp;
// pending_bounds.sort (cmp);
for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) { for (RegionList::iterator r = pending_bounds.begin(); r != pending_bounds.end(); ++r) {
regions_to_relayer.push_back (*r);
dependent_checks_needed.insert (*r); dependent_checks_needed.insert (*r);
} }
for (s = pending_removes.begin(); s != pending_removes.end(); ++s) { for (s = pending_removes.begin(); s != pending_removes.end(); ++s) {
remove_dependents (*s); remove_dependents (*s);
// cerr << _name << " sends RegionRemoved\n";
RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */ RegionRemoved (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
} }
for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
// cerr << _name << " sends RegionAdded\n";
/* don't emit RegionAdded signal until relayering is done, /* don't emit RegionAdded signal until relayering is done,
so that the region is fully setup by the time so that the region is fully setup by the time
anyone hear's that its been added anyone hear's that its been added
@ -621,14 +604,18 @@ Playlist::flush_notifications (bool from_undo)
} }
if (regions_changed || pending_contents_change) { if (regions_changed || pending_contents_change) {
if (!in_set_state) {
relayer ();
}
pending_contents_change = false; pending_contents_change = false;
// cerr << _name << " sends 5 contents change @ " << get_microseconds() << endl;
ContentsChanged (); /* EMIT SIGNAL */ ContentsChanged (); /* EMIT SIGNAL */
// cerr << _name << "done contents change @ " << get_microseconds() << endl;
} }
for (s = pending_adds.begin(); s != pending_adds.end(); ++s) { for (s = pending_adds.begin(); s != pending_adds.end(); ++s) {
(*s)->clear_changes (); (*s)->clear_changes ();
RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */ RegionAdded (boost::weak_ptr<Region> (*s)); /* EMIT SIGNAL */
regions_to_relayer.push_back (*s);
} }
for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) { for (s = dependent_checks_needed.begin(); s != dependent_checks_needed.end(); ++s) {
@ -643,14 +630,6 @@ Playlist::flush_notifications (bool from_undo)
RegionsExtended (pending_region_extensions); RegionsExtended (pending_region_extensions);
} }
if (!regions_to_relayer.empty () && !from_undo) {
relayer (regions_to_relayer);
}
if (pending_layering) {
LayeringChanged (); /* EMIT SIGNAL */
}
clear_pending (); clear_pending ();
in_flush = false; in_flush = false;
@ -686,6 +665,7 @@ Playlist::flush_notifications (bool from_undo)
} }
if (itimes >= 1) { if (itimes >= 1) {
region->set_pending_layer (DBL_MAX);
add_region_internal (region, pos); add_region_internal (region, pos);
pos += region->length(); pos += region->length();
--itimes; --itimes;
@ -698,6 +678,7 @@ Playlist::flush_notifications (bool from_undo)
for (int i = 0; i < itimes; ++i) { for (int i = 0; i < itimes; ++i) {
boost::shared_ptr<Region> copy = RegionFactory::create (region, true); boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
copy->set_pending_layer (DBL_MAX);
add_region_internal (copy, pos); add_region_internal (copy, pos);
pos += region->length(); pos += region->length();
} }
@ -718,6 +699,7 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::layer, region->layer()); plist.add (Properties::layer, region->layer());
boost::shared_ptr<Region> sub = RegionFactory::create (region, plist); boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
sub->set_pending_layer (DBL_MAX);
add_region_internal (sub, pos); add_region_internal (sub, pos);
} }
} }
@ -758,6 +740,11 @@ Playlist::flush_notifications (bool from_undo)
possibly_splice_unlocked (position, region->length(), region); possibly_splice_unlocked (position, region->length(), region);
if (!holding_state ()) {
/* layers get assigned from XML state, and are not reset during undo/redo */
relayer ();
}
/* we need to notify the existence of new region before checking dependents. Ick. */ /* we need to notify the existence of new region before checking dependents. Ick. */
notify_region_added (region); notify_region_added (region);
@ -780,6 +767,7 @@ Playlist::flush_notifications (bool from_undo)
_splicing = true; _splicing = true;
remove_region_internal (old); remove_region_internal (old);
newr->set_pending_layer (newr->layer ());
add_region_internal (newr, pos); add_region_internal (newr, pos);
_splicing = old_sp; _splicing = old_sp;
@ -817,6 +805,7 @@ Playlist::flush_notifications (bool from_undo)
possibly_splice_unlocked (pos, -distance); possibly_splice_unlocked (pos, -distance);
if (!holding_state ()) { if (!holding_state ()) {
relayer ();
remove_dependents (region); remove_dependents (region);
} }
@ -873,16 +862,12 @@ Playlist::flush_notifications (bool from_undo)
* start and end if cutting == true. Regions that lie entirely within start and end are always * start and end if cutting == true. Regions that lie entirely within start and end are always
* removed. * removed.
*/ */
void void
Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist) Playlist::partition_internal (framepos_t start, framepos_t end, bool cutting, RegionList& thawlist)
{ {
RegionList new_regions; RegionList new_regions;
/* Don't relayer regions that are created during this operation; leave them
on the same region as the original.
*/
suspend_relayer ();
{ {
RegionLock rlock (this); RegionLock rlock (this);
@ -961,7 +946,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::start, current->start() + (pos2 - pos1)); plist.add (Properties::start, current->start() + (pos2 - pos1));
plist.add (Properties::length, pos3 - pos2); plist.add (Properties::length, pos3 - pos2);
plist.add (Properties::name, new_name); plist.add (Properties::name, new_name);
plist.add (Properties::layer, current->layer()); plist.add (Properties::layer, current->layer ());
plist.add (Properties::layering_index, current->layering_index ());
plist.add (Properties::automatic, true); plist.add (Properties::automatic, true);
plist.add (Properties::left_of_split, true); plist.add (Properties::left_of_split, true);
plist.add (Properties::right_of_split, true); plist.add (Properties::right_of_split, true);
@ -980,7 +966,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::start, current->start() + (pos3 - pos1)); plist.add (Properties::start, current->start() + (pos3 - pos1));
plist.add (Properties::length, pos4 - pos3); plist.add (Properties::length, pos4 - pos3);
plist.add (Properties::name, new_name); plist.add (Properties::name, new_name);
plist.add (Properties::layer, current->layer()); plist.add (Properties::layer, current->layer ());
plist.add (Properties::layering_index, current->layering_index ());
plist.add (Properties::automatic, true); plist.add (Properties::automatic, true);
plist.add (Properties::right_of_split, true); plist.add (Properties::right_of_split, true);
@ -1018,7 +1005,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::start, current->start() + (pos2 - pos1)); plist.add (Properties::start, current->start() + (pos2 - pos1));
plist.add (Properties::length, pos4 - pos2); plist.add (Properties::length, pos4 - pos2);
plist.add (Properties::name, new_name); plist.add (Properties::name, new_name);
plist.add (Properties::layer, current->layer()); plist.add (Properties::layer, current->layer ());
plist.add (Properties::layering_index, current->layering_index ());
plist.add (Properties::automatic, true); plist.add (Properties::automatic, true);
plist.add (Properties::left_of_split, true); plist.add (Properties::left_of_split, true);
@ -1061,7 +1049,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::start, current->start()); plist.add (Properties::start, current->start());
plist.add (Properties::length, pos3 - pos1); plist.add (Properties::length, pos3 - pos1);
plist.add (Properties::name, new_name); plist.add (Properties::name, new_name);
plist.add (Properties::layer, current->layer()); plist.add (Properties::layer, current->layer ());
plist.add (Properties::layering_index, current->layering_index ());
plist.add (Properties::automatic, true); plist.add (Properties::automatic, true);
plist.add (Properties::right_of_split, true); plist.add (Properties::right_of_split, true);
@ -1108,8 +1097,6 @@ Playlist::flush_notifications (bool from_undo)
for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) { for (RegionList::iterator i = new_regions.begin(); i != new_regions.end(); ++i) {
check_dependents (*i, false); check_dependents (*i, false);
} }
resume_relayer ();
} }
boost::shared_ptr<Playlist> boost::shared_ptr<Playlist>
@ -1210,7 +1197,7 @@ Playlist::flush_notifications (bool from_undo)
int itimes = (int) floor (times); int itimes = (int) floor (times);
framepos_t pos = position; framepos_t pos = position;
framecnt_t const shift = other->_get_extent().second; framecnt_t const shift = other->_get_extent().second;
layer_t top_layer = regions.size(); layer_t top = top_layer ();
while (itimes--) { while (itimes--) {
for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) { for (RegionList::iterator i = other->regions.begin(); i != other->regions.end(); ++i) {
@ -1220,7 +1207,7 @@ Playlist::flush_notifications (bool from_undo)
the ordering they had in the original playlist. the ordering they had in the original playlist.
*/ */
copy_of_region->set_layer (copy_of_region->layer() + top_layer); copy_of_region->set_pending_layer (copy_of_region->layer() + top);
add_region_internal (copy_of_region, (*i)->position() + pos); add_region_internal (copy_of_region, (*i)->position() + pos);
} }
pos += shift; pos += shift;
@ -1242,6 +1229,7 @@ Playlist::flush_notifications (bool from_undo)
while (itimes--) { while (itimes--) {
boost::shared_ptr<Region> copy = RegionFactory::create (region, true); boost::shared_ptr<Region> copy = RegionFactory::create (region, true);
copy->set_pending_layer (DBL_MAX);
add_region_internal (copy, pos); add_region_internal (copy, pos);
pos += region->length(); pos += region->length();
} }
@ -1259,6 +1247,7 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::name, name); plist.add (Properties::name, name);
boost::shared_ptr<Region> sub = RegionFactory::create (region, plist); boost::shared_ptr<Region> sub = RegionFactory::create (region, plist);
sub->set_pending_layer (DBL_MAX);
add_region_internal (sub, pos); add_region_internal (sub, pos);
} }
} }
@ -1360,6 +1349,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::length, before); plist.add (Properties::length, before);
plist.add (Properties::name, before_name); plist.add (Properties::name, before_name);
plist.add (Properties::left_of_split, true); plist.add (Properties::left_of_split, true);
plist.add (Properties::layering_index, region->layering_index ());
plist.add (Properties::layer, region->layer ());
/* note: we must use the version of ::create with an offset here, /* note: we must use the version of ::create with an offset here,
since it supplies that offset to the Region constructor, which since it supplies that offset to the Region constructor, which
@ -1377,6 +1368,8 @@ Playlist::flush_notifications (bool from_undo)
plist.add (Properties::length, after); plist.add (Properties::length, after);
plist.add (Properties::name, after_name); plist.add (Properties::name, after_name);
plist.add (Properties::right_of_split, true); plist.add (Properties::right_of_split, true);
plist.add (Properties::layering_index, region->layering_index ());
plist.add (Properties::layer, region->layer ());
/* same note as above */ /* same note as above */
right = RegionFactory::create (region, before, plist); right = RegionFactory::create (region, before, plist);
@ -1470,8 +1463,6 @@ Playlist::flush_notifications (bool from_undo)
if (what_changed.contains (Properties::position)) { if (what_changed.contains (Properties::position)) {
timestamp_layer_op (LayerOpBoundsChange, region);
/* remove it from the list then add it back in /* remove it from the list then add it back in
the right place again. the right place again.
*/ */
@ -1512,7 +1503,7 @@ Playlist::flush_notifications (bool from_undo)
pending_bounds.push_back (region); pending_bounds.push_back (region);
} else { } else {
notify_contents_changed (); notify_contents_changed ();
relayer (region); relayer ();
check_dependents (region, false); check_dependents (region, false);
} }
} }
@ -1572,9 +1563,9 @@ Playlist::flush_notifications (bool from_undo)
notify_region_start_trimmed (region); notify_region_start_trimmed (region);
} }
if (what_changed.contains (Properties::layer)) { /* don't notify about layer changes, since we are the only object that can initiate
notify_layering_changed (); them, and we notify in ::relayer()
} */
if (what_changed.contains (our_interests)) { if (what_changed.contains (our_interests)) {
save = true; save = true;
@ -2134,7 +2125,6 @@ Playlist::flush_notifications (bool from_undo)
return -1; return -1;
} }
suspend_relayer ();
freeze (); freeze ();
plist = node.properties(); plist = node.properties();
@ -2196,7 +2186,7 @@ Playlist::flush_notifications (bool from_undo)
} }
add_region (region, region->position(), 1.0); add_region (region, region->position(), 1.0);
region->resume_property_changes (); region->resume_property_changes ();
} }
@ -2217,7 +2207,6 @@ Playlist::flush_notifications (bool from_undo)
thaw (); thaw ();
notify_contents_changed (); notify_contents_changed ();
resume_relayer ();
in_set_state--; in_set_state--;
first_set_state = false; first_set_state = false;
@ -2345,277 +2334,189 @@ Playlist::set_edit_mode (EditMode mode)
_edit_mode = mode; _edit_mode = mode;
} }
/** Relayer a region. See the other relayer() methods for commentary. */ struct RelayerSort {
bool operator () (boost::shared_ptr<Region> a, boost::shared_ptr<Region> b) {
return a->layering_index() < b->layering_index();
}
};
void void
Playlist::relayer (boost::shared_ptr<Region> region) Playlist::relayer ()
{ {
if (_relayer_suspended) { /* never compute layers when changing state for undo/redo or setting from XML */
if (in_update || in_set_state) {
return; return;
} }
RegionList r;
r.push_back (region);
relayer (r);
}
Playlist::TemporaryLayers bool changed = false;
Playlist::compute_temporary_layers (RegionList const & relayer_regions)
{
TemporaryLayers temporary_layers;
OverlapCache cache (this);
for (RegionList::const_iterator i = relayer_regions.begin(); i != relayer_regions.end(); ++i) { /* Build up a new list of regions on each layer, stored in a set of lists
each of which represent some period of time on some layer. The idea
is to avoid having to search the entire region list to establish whether
each region overlaps another */
DEBUG_TRACE (DEBUG::Layering, string_compose ("Compute temporary layer for %1\n", (*i)->name())); /* how many pieces to divide this playlist's time up into */
int const divisions = 512;
/* current_overlaps: regions that overlap *i now */
RegionList current_overlaps = cache.get ((*i)->bounds ());
current_overlaps.remove (*i);
DEBUG_TRACE (DEBUG::Layering, "Current overlaps:\n"); /* find the start and end positions of the regions on this playlist */
for (RegionList::iterator j = current_overlaps.begin(); j != current_overlaps.end(); ++j) { framepos_t start = INT64_MAX;
DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1\n", (*j)->name())); framepos_t end = 0;
} for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
start = min (start, (*i)->position());
end = max (end, (*i)->position() + (*i)->length());
}
/* hence the size of each time division */
double const division_size = (end - start) / double (divisions);
vector<vector<RegionList> > layers;
layers.push_back (vector<RegionList> (divisions));
RegionList copy = regions.rlist();
RegionList pending;
/* Remove regions with pending relayers */
for (RegionList::iterator i = copy.begin(); i != copy.end(); ) {
RegionList::iterator j = i;
++j;
/* overlaps_to_preserve: regions that overlap *i now, but which aren't being if ((*i)->pending_layer()) {
worked on during this relayer: these will have their relationship with pending.push_back (*i);
*i preserved. copy.erase (i);
*/ }
RegionList overlaps_to_preserve;
/* overlaps_to_check: regions that overlap *i now, and must be checked to i = j;
see if *i is still on the correct layer with respect to them (according }
to current layering rules).
*/
RegionList overlaps_to_check;
if (_session.config.get_relayer_on_all_edits () || (*i)->last_layer_op (LayerOpAdd) > (*i)->last_layer_op (LayerOpBoundsChange)) { /* Sort the remainder */
/* We're configured to relayer on all edits, or this region has had copy.sort (RelayerSort ());
no edit since it was added to the playlist, so we're relayering
the whole lot; in this case there are no `overlaps_to_preserve'. /* Re-insert the pending layers in the right places */
*/ for (RegionList::iterator i = pending.begin(); i != pending.end(); ++i) {
overlaps_to_check = current_overlaps; RegionList::iterator j = copy.begin();
} else { while (j != copy.end ()) {
/* We're only relayering new overlaps; find them */ if ((*j)->pending_layer().get_value_or ((*j)->layer ()) > (*i)->pending_layer().get ()) {
RegionList last_overlaps = cache.get ((*i)->last_relayer_bounds ()); break;
last_overlaps.remove (*i);
for (RegionList::const_iterator j = current_overlaps.begin(); j != current_overlaps.end(); ++j) {
if (find (last_overlaps.begin(), last_overlaps.end(), *j) == last_overlaps.end()) {
/* This is a new overlap, which must be checked */
overlaps_to_check.push_back (*j);
} else {
/* This is an existing overlap, which must be preserved */
overlaps_to_preserve.push_back (*j);
}
} }
}
if (overlaps_to_check.empty ()) {
/* There are no overlaps to check, so just leave everything as it is */
continue;
}
DEBUG_TRACE (DEBUG::Layering, "Overlaps to check:\n");
for (RegionList::iterator j = overlaps_to_check.begin(); j != overlaps_to_check.end(); ++j) {
DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1\n", (*j)->name()));
}
/* Put *i on our overlaps_to_check_list */
overlaps_to_check.push_back (*i);
/* And sort it according to the current layer model */
switch (_session.config.get_layer_model()) {
case LaterHigher:
overlaps_to_check.sort (RegionSortByPosition ());
break;
case AddOrBoundsChangeHigher:
overlaps_to_check.sort (RegionSortByAddOrBounds ());
break;
case AddHigher:
overlaps_to_check.sort (RegionSortByAdd ());
break;
}
/* Now find *i in our overlaps_to_check list; within this list it will be in the
right place wrt the current layering rules, so we can work out the layers of the
nearest regions below and above.
*/
double previous_layer = -DBL_MAX;
double next_layer = DBL_MAX;
RegionList::const_iterator j = overlaps_to_check.begin();
while (*j != *i) {
previous_layer = temporary_layers.get (*j);
++j; ++j;
} }
copy.insert (j, *i);
}
/* we must have found *i */ bool had_pending = false;
assert (j != overlaps_to_check.end ());
++j; for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) {
if (j != overlaps_to_check.end ()) {
next_layer = temporary_layers.get (*j); /* reset the pending layer for every region now that we're relayering */
if ((*i)->reset_pending_layer ()) {
had_pending = true;
} }
if (next_layer < previous_layer) { /* find the time divisions that this region covers; if there are no regions on the list,
/* If this happens, it means that it's impossible to put *i between overlaps_to_check division_size will equal 0 and in this case we'll just say that
in a way that satisfies the current layering rule. So we'll punt and put *i start_division = end_division = 0.
above previous_layer.
*/
next_layer = DBL_MAX;
}
/* Now we know where *i and overlaps_to_preserve should go: between previous_layer and
next_layer.
*/ */
int start_division = 0;
int end_division = 0;
DEBUG_TRACE (DEBUG::Layering, string_compose ("%1 and deps need to go between %2 and %3\n", (*i)->name(), previous_layer, next_layer)); if (division_size > 0) {
start_division = floor ( ((*i)->position() - start) / division_size);
end_division = floor ( ((*i)->position() + (*i)->length() - start) / division_size );
if (end_division == divisions) {
end_division--;
}
}
/* Recurse into overlaps_to_preserve to find dependents */ assert (divisions == 0 || end_division < divisions);
RegionList recursed_overlaps_to_preserve;
/* find the lowest layer that this region can go on */
for (RegionList::const_iterator k = overlaps_to_preserve.begin(); k != overlaps_to_preserve.end(); ++k) { size_t j = layers.size();
recursed_overlaps_to_preserve.push_back (*k); while (j > 0) {
RegionList touched = recursive_regions_touched (*k, cache, *i); /* try layer j - 1; it can go on if it overlaps no other region
for (RegionList::iterator m = touched.begin(); m != touched.end(); ++m) { that is already on that layer
if (find (recursed_overlaps_to_preserve.begin(), recursed_overlaps_to_preserve.end(), *m) == recursed_overlaps_to_preserve.end()) { */
recursed_overlaps_to_preserve.push_back (*m);
bool overlap = false;
for (int k = start_division; k <= end_division; ++k) {
RegionList::iterator l = layers[j-1][k].begin ();
while (l != layers[j-1][k].end()) {
if ((*l)->overlap_equivalent (*i)) {
overlap = true;
break;
}
l++;
}
if (overlap) {
break;
} }
} }
}
/* Put *i into the overlaps_to_preserve list */ if (overlap) {
recursed_overlaps_to_preserve.push_back (*i); /* overlap, so we must use layer j */
break;
/* Sort it by layer, so that we preserve layering */
recursed_overlaps_to_preserve.sort (SortByTemporaryLayer (temporary_layers));
/* Divide available space up into chunks so that we can relayer everything in that space */
double const space = (next_layer - previous_layer) / (recursed_overlaps_to_preserve.size() + 1);
/* And relayer */
int m = 1;
for (RegionList::const_iterator k = recursed_overlaps_to_preserve.begin(); k != recursed_overlaps_to_preserve.end(); ++k) {
temporary_layers.set (*k, previous_layer + space * m);
++m;
}
}
return temporary_layers;
}
/** Take a list of temporary layer indices and set up the layers of all regions
* based on them.
*/
void
Playlist::commit_temporary_layers (TemporaryLayers const & temporary_layers)
{
/* Sort all the playlist's regions by layer, ascending */
RegionList all_regions = regions.rlist ();
all_regions.sort (SortByTemporaryLayer (temporary_layers));
DEBUG_TRACE (DEBUG::Layering, "Commit layering:\n");
for (RegionList::iterator i = all_regions.begin(); i != all_regions.end(); ++i) {
/* Go through the regions that we have already layered and hence work
out the maximum layer index that is in used at some point during
region *i.
*/
layer_t max_layer_here = 0;
bool have_overlap = false;
for (RegionList::iterator j = all_regions.begin(); j != i; ++j) {
if ((*j)->overlap_equivalent (*i)) {
max_layer_here = max ((*j)->layer (), max_layer_here);
have_overlap = true;
} }
--j;
} }
if (have_overlap) { if (j == layers.size()) {
/* *i overlaps something, so put it on the next available layer */ /* we need a new layer for this region */
(*i)->set_layer (max_layer_here + 1); layers.push_back (vector<RegionList> (divisions));
} else {
/* no overlap, so put on the bottom layer */
(*i)->set_layer (0);
} }
DEBUG_TRACE (DEBUG::Layering, string_compose ("\t%1 temporary %2 committed %3\n", (*i)->name(), temporary_layers.get (*i), (*i)->layer()));
}
}
/** Relayer a list of regions. /* put a reference to this region in each of the divisions that it exists in */
* for (int k = start_division; k <= end_division; ++k) {
* Taking each region R in turn, this method examines the regions O that overlap R in time. layers[j][k].push_back (*i);
* If the session configuration option "relayer-on-all-moves" is false, we reduce O so that }
* it contains only those regions with which new overlaps have been formed since the last
* relayer.
*
* We then change the layer of R and its indirect overlaps so that R meets the current
* Session layer model with respect to O. See doc/layering.
*/
void if ((*i)->layer() != j) {
Playlist::relayer (RegionList const & relayer_regions) changed = true;
{ }
if (_relayer_suspended) {
return; (*i)->set_layer (j);
} }
/* We do this in two parts: first; compute `temporary layer' indices for if (changed) {
regions on the playlist. These are (possibly) fractional indices, which notify_layering_changed ();
are a convenient means of working with things when you want to insert layers
between others.
*/
TemporaryLayers temporary_layers = compute_temporary_layers (relayer_regions);
/* Second, we fix up these temporary layers and `commit' them by writing
them to the regions involved.
*/
commit_temporary_layers (temporary_layers);
}
/** Put a region on some fractional layer and sort everything else out around it.
* This can be used to force a region into some layering; for example, calling
* this method with temporary_layer == -0.5 will put the region at the bottom of
* the stack.
*/
void
Playlist::relayer (boost::shared_ptr<Region> region, double temporary_layer)
{
if (_relayer_suspended) {
return;
} }
TemporaryLayers t; if (had_pending) {
t.set (region, temporary_layer); uint64_t i = 0;
commit_temporary_layers (t); for (RegionList::iterator j = copy.begin(); j != copy.end(); ++j) {
(*j)->set_layering_index (i++);
}
}
} }
void void
Playlist::raise_region (boost::shared_ptr<Region> region) Playlist::raise_region (boost::shared_ptr<Region> region)
{ {
relayer (region, region->layer() + 1.5); region->set_pending_layer (region->layer() + 1.5);
relayer ();
} }
void void
Playlist::lower_region (boost::shared_ptr<Region> region) Playlist::lower_region (boost::shared_ptr<Region> region)
{ {
relayer (region, region->layer() - 1.5); region->set_pending_layer (region->layer() - 1.5);
relayer ();
} }
void void
Playlist::raise_region_to_top (boost::shared_ptr<Region> region) Playlist::raise_region_to_top (boost::shared_ptr<Region> region)
{ {
relayer (region, max_layer); region->set_pending_layer (DBL_MAX);
relayer ();
} }
void void
Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region) Playlist::lower_region_to_bottom (boost::shared_ptr<Region> region)
{ {
relayer (region, -0.5); region->set_pending_layer (-0.5);
relayer ();
} }
void void
@ -2750,24 +2651,9 @@ Playlist::set_frozen (bool yn)
_frozen = yn; _frozen = yn;
} }
void
Playlist::timestamp_layer_op (LayerOp op, boost::shared_ptr<Region> region)
{
region->set_last_layer_op (op, ++layer_op_counter);
}
/** Find the next or previous region after `region' (next if dir > 0, previous otherwise)
* and swap its position with `region'.
*/
void void
Playlist::shuffle (boost::shared_ptr<Region> region, int dir) Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
{ {
/* As regards layering, the calls we make to set_position() will
perform layering as if the regions had been moved, which I think
is about right.
*/
bool moved = false; bool moved = false;
if (region->locked()) { if (region->locked()) {
@ -2872,9 +2758,13 @@ Playlist::shuffle (boost::shared_ptr<Region> region, int dir)
_shuffling = false; _shuffling = false;
if (moved) { if (moved) {
relayer ();
check_dependents (region, false); check_dependents (region, false);
notify_contents_changed(); notify_contents_changed();
} }
} }
bool bool
@ -3293,145 +3183,16 @@ Playlist::set_orig_track_id (const PBD::ID& id)
_orig_track_id = id; _orig_track_id = id;
} }
/** Set the temporary layer for a region */ uint64_t
void Playlist::highest_layering_index () const
Playlist::TemporaryLayers::set (boost::shared_ptr<Region> r, double l)
{ {
_map[r] = l; RegionLock rlock (const_cast<Playlist *> (this));
}
/** Return the temporary layer for a region, if one has been specified uint64_t h = 0;
* to this TemporaryLayers object; if not return the region's current for (RegionList::const_iterator i = regions.begin(); i != regions.end(); ++i) {
* layer. h = max (h, (*i)->layering_index ());
*/
double
Playlist::TemporaryLayers::get (boost::shared_ptr<Region> r) const
{
Map::const_iterator i = _map.find (r);
if (i != _map.end ()) {
return i->second;
} }
return double (r->layer ()); return h;
}
int const Playlist::OverlapCache::_divisions = 512;
/** Set up an OverlapCache for a playlist; the cache will only be valid until
* the Playlist is changed.
*/
Playlist::OverlapCache::OverlapCache (Playlist* playlist)
: _range (0, 0)
{
/* Find the start and end positions of the regions on this playlist */
_range = Evoral::Range<framepos_t> (max_framepos, 0);
RegionList const & rl = playlist->region_list().rlist ();
for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
Evoral::Range<framepos_t> const b = (*i)->bounds ();
_range.from = min (_range.from, b.from);
_range.to = max (_range.to, b.to);
}
/* Hence the size of each time divison */
_division_size = (_range.to - _range.from) / double (_divisions);
_cache.resize (_divisions);
/* Build the cache */
for (RegionList::const_iterator i = rl.begin(); i != rl.end(); ++i) {
pair<int, int> ind = cache_indices ((*i)->bounds ());
for (int j = ind.first; j < ind.second; ++j) {
_cache[j].push_back (*i);
}
}
}
/** @param range Range, in frames.
* @return From and to cache indices for (to is exclusive).
*/
pair<int, int>
Playlist::OverlapCache::cache_indices (Evoral::Range<framepos_t> range) const
{
range.from = max (range.from, _range.from);
range.to = min (range.to, _range.to);
pair<int, int> const p = make_pair (
floor ((range.from - _range.from) / _division_size),
ceil ((range.to - _range.from) / _division_size)
);
assert (p.first >= 0);
assert (p.second <= _divisions);
return p;
}
/** Return the regions that overlap a given range. The returned list
* is not guaranteed to be in the same order as the Playlist that it was
* generated from.
*/
Playlist::RegionList
Playlist::OverlapCache::get (Evoral::Range<framepos_t> range) const
{
if (_range.from == max_framepos) {
return RegionList ();
}
RegionList r;
pair<int, int> ind = cache_indices (range);
for (int i = ind.first; i < ind.second; ++i) {
for (RegionList::const_iterator j = _cache[i].begin(); j != _cache[i].end(); ++j) {
if ((*j)->coverage (range.from, range.to) != OverlapNone) {
r.push_back (*j);
}
}
}
r.sort ();
r.unique ();
return r;
}
void
Playlist::suspend_relayer ()
{
_relayer_suspended = true;
}
void
Playlist::resume_relayer ()
{
_relayer_suspended = false;
}
/** Examine a region and return regions which overlap it, and also those which overlap those which overlap etc.
* @param ignore Optional region which should be treated as if it doesn't exist (ie not returned in the list,
* and not recursed into).
*/
Playlist::RegionList
Playlist::recursive_regions_touched (boost::shared_ptr<Region> region, OverlapCache const & cache, boost::shared_ptr<Region> ignore) const
{
RegionList touched;
recursive_regions_touched_sub (region, cache, ignore, touched);
touched.remove (region);
return touched;
}
/** Recursive sub-routine of recursive_regions_touched */
void
Playlist::recursive_regions_touched_sub (
boost::shared_ptr<Region> region, OverlapCache const & cache, boost::shared_ptr<Region> ignore, RegionList & touched
) const
{
RegionList r = cache.get (region->bounds ());
for (RegionList::iterator i = r.begin(); i != r.end(); ++i) {
if (find (touched.begin(), touched.end(), *i) == touched.end() && *i != ignore) {
touched.push_back (*i);
recursive_regions_touched_sub (*i, cache, ignore, touched);
}
}
} }

View file

@ -73,10 +73,7 @@ namespace ARDOUR {
PBD::PropertyDescriptor<float> stretch; PBD::PropertyDescriptor<float> stretch;
PBD::PropertyDescriptor<float> shift; PBD::PropertyDescriptor<float> shift;
PBD::PropertyDescriptor<PositionLockStyle> position_lock_style; PBD::PropertyDescriptor<PositionLockStyle> position_lock_style;
PBD::PropertyDescriptor<framepos_t> last_relayer_bounds_from; PBD::PropertyDescriptor<uint64_t> layering_index;
PBD::PropertyDescriptor<framepos_t> last_relayer_bounds_to;
PBD::PropertyDescriptor<uint64_t> last_layer_op_add;
PBD::PropertyDescriptor<uint64_t> last_layer_op_bounds_change;
} }
} }
@ -131,14 +128,8 @@ Region::make_property_quarks ()
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id)); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for shift = %1\n", Properties::shift.property_id));
Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style")); Properties::position_lock_style.property_id = g_quark_from_static_string (X_("positional-lock-style"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id)); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for position_lock_style = %1\n", Properties::position_lock_style.property_id));
Properties::last_relayer_bounds_from.property_id = g_quark_from_static_string (X_("last-relayer-bounds-from")); Properties::layering_index.property_id = g_quark_from_static_string (X_("layering-index"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_relayer_bounds_from = %1\n", Properties::last_relayer_bounds_from.property_id)); DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for layering_index = %1\n", Properties::layering_index.property_id));
Properties::last_relayer_bounds_to.property_id = g_quark_from_static_string (X_("last-relayer-bounds-to"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_relayer_bounds_to = %1\n", Properties::last_relayer_bounds_to.property_id));
Properties::last_layer_op_add.property_id = g_quark_from_static_string (X_("last-layer-op-add"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_layer_op_add = %1\n", Properties::last_layer_op_add.property_id));
Properties::last_layer_op_bounds_change.property_id = g_quark_from_static_string (X_("last-layer-op-bounds-change"));
DEBUG_TRACE (DEBUG::Properties, string_compose ("quark for last_layer_op_bounds_change = %1\n", Properties::last_layer_op_bounds_change.property_id));
} }
void void
@ -169,10 +160,7 @@ Region::register_properties ()
add_property (_stretch); add_property (_stretch);
add_property (_shift); add_property (_shift);
add_property (_position_lock_style); add_property (_position_lock_style);
add_property (_last_relayer_bounds_from); add_property (_layering_index);
add_property (_last_relayer_bounds_to);
add_property (_last_layer_op_add);
add_property (_last_layer_op_bounds_change);
} }
#define REGION_DEFAULT_STATE(s,l) \ #define REGION_DEFAULT_STATE(s,l) \
@ -199,10 +187,7 @@ Region::register_properties ()
, _stretch (Properties::stretch, 1.0) \ , _stretch (Properties::stretch, 1.0) \
, _shift (Properties::shift, 1.0) \ , _shift (Properties::shift, 1.0) \
, _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \ , _position_lock_style (Properties::position_lock_style, _type == DataType::AUDIO ? AudioTime : MusicTime) \
, _last_relayer_bounds_from (Properties::last_relayer_bounds_from, 0) \ , _layering_index (Properties::layering_index, 0)
, _last_relayer_bounds_to (Properties::last_relayer_bounds_to, 0) \
, _last_layer_op_add (Properties::last_layer_op_add, 0) \
, _last_layer_op_bounds_change (Properties::last_layer_op_bounds_change, 0)
#define REGION_COPY_STATE(other) \ #define REGION_COPY_STATE(other) \
_sync_marked (Properties::sync_marked, other->_sync_marked) \ _sync_marked (Properties::sync_marked, other->_sync_marked) \
@ -228,10 +213,7 @@ Region::register_properties ()
, _stretch (Properties::stretch, other->_stretch) \ , _stretch (Properties::stretch, other->_stretch) \
, _shift (Properties::shift, other->_shift) \ , _shift (Properties::shift, other->_shift) \
, _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \ , _position_lock_style (Properties::position_lock_style, other->_position_lock_style) \
, _last_relayer_bounds_from (Properties::last_relayer_bounds_from, other->_last_relayer_bounds_from) \ , _layering_index (Properties::layering_index, other->_layering_index)
, _last_relayer_bounds_to (Properties::last_relayer_bounds_to, other->_last_relayer_bounds_to) \
, _last_layer_op_add (Properties::last_layer_op_add, other->_last_layer_op_add) \
, _last_layer_op_bounds_change (Properties::last_layer_op_bounds_change, other->_last_layer_op_bounds_change)
/* derived-from-derived constructor (no sources in constructor) */ /* derived-from-derived constructor (no sources in constructor) */
Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type) Region::Region (Session& s, framepos_t start, framecnt_t length, const string& name, DataType type)
@ -243,6 +225,7 @@ Region::Region (Session& s, framepos_t start, framecnt_t length, const string& n
, _first_edit (EditChangesNothing) , _first_edit (EditChangesNothing)
{ {
register_properties (); register_properties ();
/* no sources at this point */ /* no sources at this point */
} }
@ -1134,12 +1117,9 @@ Region::set_layer (layer_t l)
{ {
if (_layer != l) { if (_layer != l) {
_layer = l; _layer = l;
send_change (Properties::layer); send_change (Properties::layer);
} }
Evoral::Range<framepos_t> const b = bounds ();
_last_relayer_bounds_from = b.from;
_last_relayer_bounds_to = b.to;
} }
XMLNode& XMLNode&
@ -1330,19 +1310,6 @@ Region::send_change (const PropertyChange& what_changed)
} }
} }
void
Region::set_last_layer_op (LayerOp op, uint64_t when)
{
switch (op) {
case LayerOpAdd:
_last_layer_op_add = when;
break;
case LayerOpBoundsChange:
_last_layer_op_bounds_change = when;
break;
}
}
bool bool
Region::overlap_equivalent (boost::shared_ptr<const Region> other) const Region::overlap_equivalent (boost::shared_ptr<const Region> other) const
{ {
@ -1683,24 +1650,22 @@ Region::post_set (const PropertyChange& pc)
} }
} }
uint64_t void
Region::last_layer_op (LayerOp op) const Region::set_pending_layer (double l)
{ {
switch (op) { _pending_layer = l;
case LayerOpAdd:
return _last_layer_op_add;
case LayerOpBoundsChange:
return _last_layer_op_bounds_change;
}
/* NOTREACHED */
return 0;
} }
Evoral::Range<framepos_t> bool
Region::bounds () const Region::reset_pending_layer ()
{ {
return Evoral::Range<framepos_t> (_position, _position + _length); bool const had = _pending_layer;
_pending_layer = boost::optional<double> ();
return had;
} }
boost::optional<double>
Region::pending_layer () const
{
return _pending_layer;
}

View file

@ -19,8 +19,6 @@ export ARDOUR_PANNER_PATH=$libs/panners/2in2out:$libs/panners/1in2out:$libs/pann
if [ "$1" == "--debug" ]; then if [ "$1" == "--debug" ]; then
gdb ./libs/ardour/run-tests gdb ./libs/ardour/run-tests
elif [ "$1" == "--valgrind" ]; then
valgrind --tool="memcheck" ./libs/ardour/run-tests
else else
./libs/ardour/run-tests ./libs/ardour/run-tests
fi fi

View file

@ -974,27 +974,7 @@ int
Session::load_options (const XMLNode& node) Session::load_options (const XMLNode& node)
{ {
LocaleGuard lg (X_("POSIX")); LocaleGuard lg (X_("POSIX"));
config.set_variables (node);
/* Copy the node */
XMLNode node_copy = node;
/* XXX: evil hack: fix up sessions from before the layering alterations
(during A3 beta)
*/
XMLNodeList children = node_copy.children ();
for (XMLNodeIterator i = children.begin(); i != children.end(); ++i) {
XMLProperty* p = (*i)->property (X_("name"));
if (p && p->name() == X_("name") && p->value() == X_("layer-model") ) {
p = (*i)->property (X_("value"));
if (p && p->value() == X_("MoveAddHigher")) {
(*i)->add_property (X_("value"), X_("AddOrBoundsChangeHigher"));
}
}
}
config.set_variables (node_copy);
return 0; return 0;
} }

View file

@ -1,14 +1,13 @@
#include "pbd/compose.h"
#include "midi++/manager.h" #include "midi++/manager.h"
#include "pbd/textreceiver.h"
#include "pbd/compose.h"
#include "ardour/session.h"
#include "ardour/audioengine.h"
#include "ardour/playlist_factory.h" #include "ardour/playlist_factory.h"
#include "ardour/source_factory.h" #include "ardour/source_factory.h"
#include "ardour/region.h" #include "ardour/region.h"
#include "ardour/region_factory.h" #include "ardour/region_factory.h"
#include "ardour/session.h"
#include "ardour/audiosource.h"
#include "ardour/audioengine.h"
#include "playlist_layering_test.h" #include "playlist_layering_test.h"
#include "test_receiver.h"
CPPUNIT_TEST_SUITE_REGISTRATION (PlaylistLayeringTest); CPPUNIT_TEST_SUITE_REGISTRATION (PlaylistLayeringTest);
@ -16,28 +15,68 @@ using namespace std;
using namespace ARDOUR; using namespace ARDOUR;
using namespace PBD; using namespace PBD;
int const PlaylistLayeringTest::num_regions = 6; class TestReceiver : 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:
/* this isn't supposed to happen */
abort ();
}
/* note: iostreams are already thread-safe: no external
lock required.
*/
cout << prefix << str << endl;
if (chn == Transmitter::Fatal) {
exit (9);
}
}
};
TestReceiver test_receiver;
void void
PlaylistLayeringTest::setUp () PlaylistLayeringTest::setUp ()
{ {
TestNeedingSession::setUp (); string const test_session_path = "libs/ardour/test/playlist_layering_test";
string const test_wav_path = "libs/ardour/test/test.wav"; string const test_wav_path = "libs/ardour/test/playlist_layering_test/playlist_layering_test.wav";
system (string_compose ("rm -rf %1", test_session_path).c_str());
init (false, true);
SessionEvent::create_per_thread_pool ("test", 512);
test_receiver.listen_to (error);
test_receiver.listen_to (info);
test_receiver.listen_to (fatal);
test_receiver.listen_to (warning);
AudioEngine* engine = new AudioEngine ("test", "");
MIDI::Manager::create (engine->jack ());
CPPUNIT_ASSERT (engine->start () == 0);
_session = new Session (*engine, test_session_path, "playlist_layering_test");
engine->set_session (_session);
_playlist = PlaylistFactory::create (DataType::AUDIO, *_session, "test"); _playlist = PlaylistFactory::create (DataType::AUDIO, *_session, "test");
_source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, 44100); _source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, 44100);
system ("pwd");
/* Must write some data to our source, otherwise regions which use it will
be limited in whether they can be trimmed or not.
*/
boost::shared_ptr<AudioSource> a = boost::dynamic_pointer_cast<AudioSource> (_source);
Sample silence[512];
memset (silence, 0, 512 * sizeof (Sample));
a->write (silence, 512);
_region = new boost::shared_ptr<Region>[num_regions];
} }
void void
@ -45,303 +84,45 @@ PlaylistLayeringTest::tearDown ()
{ {
_playlist.reset (); _playlist.reset ();
_source.reset (); _source.reset ();
for (int i = 0; i < num_regions; ++i) { for (int i = 0; i < 16; ++i) {
_region[i].reset (); _region[i].reset ();
} }
delete[] _region; AudioEngine::instance()->remove_session ();
delete _session;
TestNeedingSession::tearDown (); EnumWriter::destroy ();
MIDI::Manager::destroy ();
AudioEngine::destroy ();
} }
void void
PlaylistLayeringTest::create_short_regions () PlaylistLayeringTest::create_three_short_regions ()
{ {
PropertyList plist; PropertyList plist;
plist.add (Properties::start, 0); plist.add (Properties::start, 0);
plist.add (Properties::length, 100); plist.add (Properties::length, 100);
for (int i = 0; i < num_regions; ++i) { for (int i = 0; i < 3; ++i) {
_region[i] = RegionFactory::create (_source, plist); _region[i] = RegionFactory::create (_source, plist);
_region[i]->set_name (string_compose ("%1", char (int ('A') + i)));
} }
} }
void void
PlaylistLayeringTest::laterHigher_relayerOnAll_Test () PlaylistLayeringTest::basicsTest ()
{ {
_session->config.set_layer_model (LaterHigher); create_three_short_regions ();
_session->config.set_relayer_on_all_edits (true);
create_short_regions ();
/* three overlapping regions */ _playlist->add_region (_region[0], 0);
_playlist->add_region (_region[A], 0); _playlist->add_region (_region[1], 10);
_playlist->add_region (_region[B], 10); _playlist->add_region (_region[2], 20);
_playlist->add_region (_region[C], 20);
/* and another non-overlapping one */
_playlist->add_region (_region[D], 200);
/* LaterHigher means that they should be arranged thus */ CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[0]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ()); CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[1]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ()); CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[2]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
_region[A]->set_position (5); _region[0]->set_position (5);
/* Region move should have no effect in LaterHigher mode */ /* region move should have no effect */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ()); CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[0]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ()); CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[1]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ()); CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[2]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> bottom should give C A B, not touching D */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> top should go back to A B C, not touching D */
_region[C]->raise_to_top ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
}
void
PlaylistLayeringTest::addHigher_relayerOnAll_Test ()
{
_session->config.set_layer_model (AddHigher);
_session->config.set_relayer_on_all_edits (true);
create_short_regions ();
/* three overlapping regions */
_playlist->add_region (_region[A], 0);
_playlist->add_region (_region[B], 10);
_playlist->add_region (_region[C], 20);
/* and another non-overlapping one */
_playlist->add_region (_region[D], 200);
/* AddHigher means that they should be arranged thus */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
_region[A]->set_position (5);
/* region move should have no effect in AddHigher mode */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> bottom should give C A B, not touching D */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> top should go back to A B C, not touching D */
_region[C]->raise_to_top ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
}
void
PlaylistLayeringTest::addOrBoundsHigher_relayerOnAll_Test ()
{
_session->config.set_layer_model (AddOrBoundsChangeHigher);
_session->config.set_relayer_on_all_edits (true);
create_short_regions ();
/* three overlapping regions */
_playlist->add_region (_region[A], 0);
_playlist->add_region (_region[B], 10);
_playlist->add_region (_region[C], 20);
/* and another non-overlapping one */
_playlist->add_region (_region[D], 200);
/* AddOrBoundsHigher means that they should be arranged thus */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* region move should put A on top for B C A, not touching D */
_region[A]->set_position (5);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> bottom should give C B A, not touching D */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> top should go back to B A C, not touching D */
_region[C]->raise_to_top ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Put C on the bottom */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Now move it slightly, and it should go back to the top again */
_region[C]->set_position (21);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Put C back on the bottom */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Trim it slightly, and it should go back to the top again */
_region[C]->trim_front (23);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Same with the end */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
_region[C]->trim_end (118);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
}
void
PlaylistLayeringTest::addOrBoundsHigher_relayerWhenNecessary_Test ()
{
_session->config.set_layer_model (AddOrBoundsChangeHigher);
_session->config.set_relayer_on_all_edits (false);
create_short_regions ();
/* three overlapping regions */
_playlist->add_region (_region[A], 0);
_playlist->add_region (_region[B], 10);
_playlist->add_region (_region[C], 20);
/* and another non-overlapping one */
_playlist->add_region (_region[D], 200);
/* AddOrBoundsHigher means that they should be arranged thus */
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
_region[A]->set_position (5);
/* region move should not have changed anything, since in
this mode we only relayer when there is a new overlap
*/
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> bottom should give C A B, not touching D */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* C -> top should go back to A B C, not touching D */
_region[C]->raise_to_top ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Put C on the bottom */
_region[C]->lower_to_bottom ();
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
/* Now move it slightly, and it should stay where it is */
_region[C]->set_position (21);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
}
void
PlaylistLayeringTest::lastLayerOpTest ()
{
create_short_regions ();
_playlist->add_region (_region[A], 0);
CPPUNIT_ASSERT_EQUAL (_playlist->layer_op_counter, _region[A]->last_layer_op (LayerOpAdd));
uint64_t const last_add = _region[A]->last_layer_op (LayerOpAdd);
_region[A]->set_position (42);
CPPUNIT_ASSERT_EQUAL (_playlist->layer_op_counter, _region[A]->last_layer_op (LayerOpBoundsChange));
CPPUNIT_ASSERT_EQUAL (last_add, _region[A]->last_layer_op (LayerOpAdd));
_region[A]->trim_front (46);
CPPUNIT_ASSERT_EQUAL (_playlist->layer_op_counter, _region[A]->last_layer_op (LayerOpBoundsChange));
CPPUNIT_ASSERT_EQUAL (last_add, _region[A]->last_layer_op (LayerOpAdd));
_region[A]->trim_end (102);
CPPUNIT_ASSERT_EQUAL (_playlist->layer_op_counter, _region[A]->last_layer_op (LayerOpBoundsChange));
CPPUNIT_ASSERT_EQUAL (last_add, _region[A]->last_layer_op (LayerOpAdd));
}
void
PlaylistLayeringTest::recursiveRelayerTest ()
{
_session->config.set_layer_model (AddOrBoundsChangeHigher);
_session->config.set_relayer_on_all_edits (false);
create_short_regions ();
_playlist->add_region (_region[A], 100);
_playlist->add_region (_region[B], 125);
_playlist->add_region (_region[C], 50);
_playlist->add_region (_region[D], 250);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[C]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
_region[A]->set_position (200);
CPPUNIT_ASSERT_EQUAL (layer_t (0), _region[D]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (1), _region[A]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (2), _region[B]->layer ());
CPPUNIT_ASSERT_EQUAL (layer_t (3), _region[C]->layer ());
} }

View file

@ -1,6 +1,5 @@
#include <cppunit/TestFixture.h> #include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/HelperMacros.h>
#include "test_needing_session.h"
namespace ARDOUR { namespace ARDOUR {
class Session; class Session;
@ -8,42 +7,23 @@ namespace ARDOUR {
class Source; class Source;
} }
class PlaylistLayeringTest : public TestNeedingSession class PlaylistLayeringTest : public CppUnit::TestFixture
{ {
CPPUNIT_TEST_SUITE (PlaylistLayeringTest); CPPUNIT_TEST_SUITE (PlaylistLayeringTest);
CPPUNIT_TEST (lastLayerOpTest); CPPUNIT_TEST (basicsTest);
CPPUNIT_TEST (addHigher_relayerOnAll_Test);
CPPUNIT_TEST (addOrBoundsHigher_relayerOnAll_Test);
CPPUNIT_TEST (laterHigher_relayerOnAll_Test);
CPPUNIT_TEST (addOrBoundsHigher_relayerWhenNecessary_Test);
CPPUNIT_TEST (recursiveRelayerTest);
CPPUNIT_TEST_SUITE_END (); CPPUNIT_TEST_SUITE_END ();
public: public:
void setUp (); void setUp ();
void tearDown (); void tearDown ();
void lastLayerOpTest (); void basicsTest ();
void addHigher_relayerOnAll_Test ();
void addOrBoundsHigher_relayerOnAll_Test ();
void laterHigher_relayerOnAll_Test ();
void addOrBoundsHigher_relayerWhenNecessary_Test ();
void recursiveRelayerTest ();
private: private:
void create_short_regions (); void create_three_short_regions ();
static int const num_regions;
enum {
A = 0,
B,
C,
D,
E,
F
};
ARDOUR::Session* _session;
boost::shared_ptr<ARDOUR::Playlist> _playlist; boost::shared_ptr<ARDOUR::Playlist> _playlist;
boost::shared_ptr<ARDOUR::Source> _source; boost::shared_ptr<ARDOUR::Source> _source;
boost::shared_ptr<ARDOUR::Region>* _region; boost::shared_ptr<ARDOUR::Region> _region[16];
}; };

View file

@ -1,119 +0,0 @@
#include "pbd/compose.h"
#include "ardour/playlist.h"
#include "ardour/playlist_factory.h"
#include "ardour/source_factory.h"
#include "ardour/region.h"
#include "ardour/region_sorters.h"
#include "ardour/region_factory.h"
#include "playlist_overlap_cache_test.h"
using namespace std;
using namespace PBD;
using namespace ARDOUR;
CPPUNIT_TEST_SUITE_REGISTRATION (PlaylistOverlapCacheTest);
void
PlaylistOverlapCacheTest::tearDown ()
{
_playlist.reset ();
_source.reset ();
TestNeedingSession::tearDown ();
}
void
PlaylistOverlapCacheTest::basicTest ()
{
string const test_wav_path = "libs/ardour/test/test.wav";
_playlist = PlaylistFactory::create (DataType::AUDIO, *_session, "test");
_source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, 44100);
PropertyList plist;
plist.add (Properties::length, 256);
boost::shared_ptr<Region> regionA = RegionFactory::create (_source, plist);
regionA->set_name ("A");
_playlist->add_region (regionA, 0);
{
Playlist::OverlapCache cache (_playlist.get ());
Playlist::RegionList rl = cache.get (Evoral::Range<framepos_t> (0, 256));
CPPUNIT_ASSERT_EQUAL (size_t (1), rl.size ());
CPPUNIT_ASSERT_EQUAL (regionA, rl.front ());
rl = cache.get (Evoral::Range<framepos_t> (-1000, 1000));
CPPUNIT_ASSERT_EQUAL (size_t (1), rl.size ());
CPPUNIT_ASSERT_EQUAL (regionA, rl.front ());
}
boost::shared_ptr<Region> regionB = RegionFactory::create (_source, plist);
regionA->set_name ("B");
_playlist->add_region (regionB, 53);
{
Playlist::OverlapCache cache (_playlist.get ());
Playlist::RegionList rl = cache.get (Evoral::Range<framepos_t> (0, 256));
CPPUNIT_ASSERT_EQUAL (size_t (2), rl.size ());
rl.sort (RegionSortByPosition ());
CPPUNIT_ASSERT_EQUAL (regionA, rl.front ());
CPPUNIT_ASSERT_EQUAL (regionB, rl.back ());
rl = cache.get (Evoral::Range<framepos_t> (260, 274));
CPPUNIT_ASSERT_EQUAL (size_t (1), rl.size ());
CPPUNIT_ASSERT_EQUAL (regionB, rl.front ());
}
}
void
PlaylistOverlapCacheTest::stressTest ()
{
string const test_wav_path = "libs/ardour/test/test.wav";
_playlist = PlaylistFactory::create (DataType::AUDIO, *_session, "test");
_source = SourceFactory::createWritable (DataType::AUDIO, *_session, test_wav_path, "", false, 44100);
srand (42);
int const num_regions = rand () % 256;
for (int i = 0; i < num_regions; ++i) {
PropertyList plist;
plist.add (Properties::length, rand () % 32768);
boost::shared_ptr<Region> r = RegionFactory::create (_source, plist);
r->set_name (string_compose ("%1", i));
_playlist->add_region (r, rand() % 32768);
}
Playlist::OverlapCache cache (_playlist.get ());
int const tests = rand () % 256;
for (int i = 0; i < tests; ++i) {
framepos_t const start = rand () % 32768;
framepos_t const length = rand () % 32768;
framepos_t const end = start + length;
Playlist::RegionList cached = cache.get (Evoral::Range<framepos_t> (start, end));
Playlist::RegionList actual;
Playlist::RegionList regions = _playlist->region_list().rlist();
for (Playlist::RegionList::iterator j = regions.begin(); j != regions.end(); ++j) {
if ((*j)->coverage (start, end) != OverlapNone) {
actual.push_back (*j);
}
}
cached.sort (RegionSortByPosition ());
actual.sort (RegionSortByPosition ());
CPPUNIT_ASSERT_EQUAL (actual.size (), cached.size ());
Playlist::RegionList::iterator j = actual.begin ();
Playlist::RegionList::iterator k = cached.begin ();
for (; j != actual.end(); ++j, ++k) {
CPPUNIT_ASSERT_EQUAL (*j, *k);
}
}
}

View file

@ -1,20 +0,0 @@
#include "test_needing_session.h"
class PlaylistOverlapCacheTest : public TestNeedingSession
{
public:
CPPUNIT_TEST_SUITE (PlaylistOverlapCacheTest);
CPPUNIT_TEST (basicTest);
CPPUNIT_TEST (stressTest);
CPPUNIT_TEST_SUITE_END ();
public:
void tearDown ();
void basicTest ();
void stressTest ();
private:
boost::shared_ptr<ARDOUR::Playlist> _playlist;
boost::shared_ptr<ARDOUR::Source> _source;
};

View file

@ -1,48 +0,0 @@
#include "midi++/manager.h"
#include "pbd/textreceiver.h"
#include "pbd/compose.h"
#include "pbd/enumwriter.h"
#include "ardour/session.h"
#include "ardour/audioengine.h"
#include "test_needing_session.h"
#include "test_receiver.h"
using namespace std;
using namespace ARDOUR;
using namespace PBD;
TestReceiver test_receiver;
void
TestNeedingSession::setUp ()
{
string const test_session_path = "libs/ardour/test/test_session";
system (string_compose ("rm -rf %1", test_session_path).c_str());
init (false, true);
SessionEvent::create_per_thread_pool ("test", 512);
test_receiver.listen_to (error);
test_receiver.listen_to (info);
test_receiver.listen_to (fatal);
test_receiver.listen_to (warning);
AudioEngine* engine = new AudioEngine ("test", "");
MIDI::Manager::create (engine->jack ());
CPPUNIT_ASSERT (engine->start () == 0);
_session = new Session (*engine, test_session_path, "test_session");
engine->set_session (_session);
}
void
TestNeedingSession::tearDown ()
{
AudioEngine::instance()->remove_session ();
delete _session;
EnumWriter::destroy ();
MIDI::Manager::destroy ();
AudioEngine::destroy ();
}

View file

@ -1,16 +0,0 @@
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
namespace ARDOUR {
class Session;
}
class TestNeedingSession : public CppUnit::TestFixture
{
public:
void setUp ();
void tearDown ();
protected:
ARDOUR::Session* _session;
};

View file

@ -1,37 +0,0 @@
#include "pbd/receiver.h"
class TestReceiver : 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:
/* this isn't supposed to happen */
abort ();
}
/* note: iostreams are already thread-safe: no external
lock required.
*/
std::cout << prefix << str << std::endl;
if (chn == Transmitter::Fatal) {
exit (9);
}
}
};

View file

@ -431,8 +431,6 @@ def build(bld):
test/framepos_plus_beats_test.cc test/framepos_plus_beats_test.cc
test/framepos_minus_beats_test.cc test/framepos_minus_beats_test.cc
test/playlist_layering_test.cc test/playlist_layering_test.cc
test/playlist_overlap_cache_test.cc
test/test_needing_session.cc
test/testrunner.cc test/testrunner.cc
'''.split() '''.split()

View file

@ -21,8 +21,8 @@
</para> </para>
<para> <para>
Of course, nothing in digital audio is ever quite that simple, and so Of course, nothing in digital audio is ever quite that simple, and so of
there are some complications: course there are some complications:
</para> </para>
<section id="layers-crossfades"> <section id="layers-crossfades">
@ -38,14 +38,14 @@
<section id="region-opacity"> <section id="region-opacity">
<title> Region Opacity </title> <title> Region Opacity </title>
<para> <para>
With a nod to image manipulation programs, Ardour allows you to In a perverse nod to image manipulation programs, Ardour allows you to
make regions transparent. By default, all regions are created opaque, make regions transparent. By default, all regions are created opaque,
which means that when they are playing, no region below them are which means that when they are playing, no region below them are
audible. However, if you change the region to be transparent, the audible. However, if you change the region to be transparent, the
region will be audible together with any regions below it. This region will be audible together with any regions below it. This
capability should probably not be abused; if you really want to mix capability should probably not be abused - if you really want to mix
sounds together in this way, they should probably be on their own sounds together in this way, they should probably live in their own
tracks. Occasionally though, this can be a useful trick. tracks. Occasionally though, this can be useful trick.
</para> </para>
<para> <para>
@ -55,74 +55,70 @@
</para> </para>
</section> </section>
<section id="choice-of-layering"> <section id="layering-styles">
<title>Choice of layering</title> <title> Layering Styles </title>
<para>
When you are recording new material for a track, its typical to want
to new material recorded "over" existing material in the track to be
what you hear on playback. For example, if you overdub part of a
guitar solo, you normally want the overdub to be audible, not hidden
by the old version that was already there. By contrast, when editing
using splitting/trimming/moving of regions to create a particular
arrangement along the timeline, many people find that they want
regions that start later on the timeline to be the ones that are
audible.
</para>
<para> <para>
There are two main decisions to be made with regard to how a playlist To facilitate these two contradictory desires, Ardour features three
should be layered: different styles for assigning regions to layers.
</para> </para>
<variablelist> <variablelist>
<title></title>
<varlistentry> <varlistentry>
Given overlapping regions, which order should they be layered in? <term>Most recently added regions are higher</term>
<listitem>
<para>
Use this style when recording/overdubbing new material. Edits of
any kind do not modify the layering.
</para>
</listitem>
</varlistentry> </varlistentry>
<varlistentry> <varlistentry>
When should layering be changed? <term>Most recently added/moved/trimmed regions are higher</term>
<listitem>
<para>
Use this style when recording/overdubbing new material, but you
want basic edits to cause regions to rise to the top.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Later regions are higher</term>
<listitem>
<para>
Use this style when rearranging and editing regions.
</para>
</listitem>
</varlistentry> </varlistentry>
</variablelist> </variablelist>
<section id="layering-order">
<title>Layering Order</title>
<para>
Ardour provides three-and-a-half ways to decide on the order in which regions are layered. The most basic choice is:
</para>
<variablelist>
<title></title>
<varlistentry>
<term>Most recently added regions are higher</term>
<listitem>
<para>
Regions which are later in time will be on higher layers.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Most recently added or edited regions are higher</term>
<listitem>
<para>
Regions which were more recently edited or added to the playlist
will be on higher layers.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Later regions are higher</term>
<listitem>
<para>
Regions which were more recently added to the playlist will be on higher
layers.
</para>
</listitem>
</varlistentry>
</variablelist>
</section>
<para> <para>
A new session has the layering style set to "Most recently edited or A new session has the layering style set to "Most recently
added regions are higher". To change the layering style, open the added/moved/trimmed regions are higher". To change the layering style,
<emphasis>Session Properties</emphasis> dialogue and choose your layering open the <emphasis>options editor</emphasis> and select the
style from the "Misc" page. Changing the layering style only affects "Layers&amp;Fades" page. There is an option there to select the style
future edits to the playlist; the existing layering of all playlists is you want. Layering style may be changed at any time. The existing
preserved when changing the layering mode. layering of all playlists is not changed when changing the layering
model.
</para> </para>
</section> </section>
<section id="modifying-layering-by-hand"> <section id="modifying-layering-by-hand">
<title>Modifying Layering Explicitly</title> <title> Modifying Layering By Hand </title>
<para> <para>
If you want a particular region to be the uppermost when the current If you want a particular region to be the uppermost when the current
layering style has put it on a lower layer, context click on the layering style has put it on a lower layer, context click on the