Mixer: Implement basic groups.

This commit is contained in:
Jonathan Moore Liles 2013-08-06 00:03:19 -07:00
parent d2b2c0808d
commit 5ccc6e7595
34 changed files with 1314 additions and 563 deletions

View File

@ -24,31 +24,45 @@ Jonathan Moore Liles <a href="mailto:male@tuxfamily.org">&lt;male@tuxfamily.org&
<ul><li><a href="#n:1.1.1.">1.1.1. Display Options</a> <ul><li><a href="#n:1.1.1.">1.1.1. Display Options</a>
<li><a href="#n:1.1.2.">1.1.2. Mixer Strips</a> <li><a href="#n:1.1.2.">1.1.2. Mixer Groups</a>
<ul><li><a href="#n:1.1.2.1.">1.1.2.1. Navigation</a> <ul><li><a href="#n:1.1.2.1.">1.1.2.1. How to Choose Groupings</a>
<li><a href="#n:1.1.2.2.">1.1.2.2. Control</a> <li><a href="#n:1.1.2.2.">1.1.2.2. Considering JACK Overhead</a>
<li><a href="#n:1.1.2.3.">1.1.2.3. Signal Chain</a> <li><a href="#n:1.1.2.3.">1.1.2.3. Creating a New Group</a>
<ul><li><a href="#n:1.1.2.3.1.">1.1.2.3.1. Modules</a> <li><a href="#n:1.1.2.4.">1.1.2.4. Adding a Strip to an Existing Group</a>
<ul><li><a href="#n:1.1.2.3.1.1.">1.1.2.3.1.1. OSC Control</a> <li><a href="#n:1.1.2.5.">1.1.2.5. Removing a Strip from a Group</a>
<li><a href="#n:1.1.2.3.1.2.">1.1.2.3.1.2. Manipulation</a> <li><a href="#n:1.1.2.6.">1.1.2.6. Removing a Group</a>
<li><a href="#n:1.1.2.3.1.3.">1.1.2.3.1.3. Module Parameter Editor</a> </ul><li><a href="#n:1.1.3.">1.1.3. Mixer Strips</a>
<li><a href="#n:1.1.2.3.1.4.">1.1.2.3.1.4. Controls</a> <ul><li><a href="#n:1.1.3.1.">1.1.3.1. Navigation</a>
<ul><li><a href="#n:1.1.2.3.1.4.1.">1.1.2.3.1.4.1. Control Voltages</a> <li><a href="#n:1.1.3.2.">1.1.3.2. Control</a>
</ul><li><a href="#n:1.1.2.3.1.5.">1.1.2.3.1.5. Spatialization </a> <li><a href="#n:1.1.3.3.">1.1.3.3. Signal Chain</a>
</ul></ul></ul><li><a href="#n:1.1.3.">1.1.3. Projects</a> <ul><li><a href="#n:1.1.3.3.1.">1.1.3.3.1. Modules</a>
<ul><li><a href="#n:1.1.3.1.">1.1.3.1. JACK I/O</a> <ul><li><a href="#n:1.1.3.3.1.1.">1.1.3.3.1.1. OSC Control</a>
<li><a href="#n:1.1.3.3.1.2.">1.1.3.3.1.2. Manipulation</a>
<li><a href="#n:1.1.3.3.1.3.">1.1.3.3.1.3. Module Parameter Editor</a>
<li><a href="#n:1.1.3.3.1.4.">1.1.3.3.1.4. Controls</a>
<ul><li><a href="#n:1.1.3.3.1.4.1.">1.1.3.3.1.4.1. Control Voltages</a>
</ul><li><a href="#n:1.1.3.3.1.5.">1.1.3.3.1.5. Spatialization </a>
</ul></ul></ul><li><a href="#n:1.1.4.">1.1.4. Projects</a>
<ul><li><a href="#n:1.1.4.1.">1.1.4.1. JACK I/O</a>
</ul></ul></ul></ul></ul><hr></div> </ul></ul></ul></ul></ul><hr></div>
<h1 id="n:1.">1. User Manual</h1> <h1 id="n:1.">1. User Manual</h1>
@ -80,7 +94,100 @@ After the project has been created. Hit <tt>a</tt> or choose <tt>Mixer/Add Strip
<p> <p>
The display options, found in the <tt>Options/Display</tt> submenu may be adjusted to suit your needs. Set the color scheme, widget style, and other graphic options to your liking. These options are global and affect all projects. The display options, found in the <tt>Options/Display</tt> submenu may be adjusted to suit your needs. Set the color scheme, widget style, and other graphic options to your liking. These options are global and affect all projects.
</p> </p>
<h3 id="n:1.1.2.">1.1.2. Mixer Strips</h3> <h3 id="n:1.1.2.">1.1.2. Mixer Groups</h3>
<p>
Groups serve several purposes. Firstly, they allow for some organization of strips. Groups also allow parallel relationships of mixer strips to be made explicit. This has important performance implications in JACK2. Non Mixer supports an unlimited number of groups, each of which can contain an unlimited number of mixer strips.
</p>
<h4 id="n:1.1.2.1.">1.1.2.1. How to Choose Groupings</h4>
<p>
All strips in a group should be completely parallel with no feedback loop connections. A typical group might be named 'Input' and contain all input strips (strips that accept input from Non Timeline and have outputs all connecting to some master bus).
</p>
<p>
To put it another way, if you have 100 inputs strips with identical output configurations (e.g. stereo or B-Format), that all connect to a master bus, then you have a candidate for a group.
</p>
<h4 id="n:1.1.2.2.">1.1.2.2. Considering JACK Overhead</h4>
<p>
JACK provides immense flexibility. But, as in most situations, that flexibility comes with a cost. In JACK the cost is a context switch per client. This applies <i>even for many clients which belong to the same process</i>, as in Non Mixer. Various factors go into determining the price of a context switch on any given system. It's not very expensive, but it does add up. It becomes problematic in sessions involving many clients (think 100s), each of which having a small DSP load (often smaller than the cost of JACK's context context switch). JACK <b>could</b> be smart enough to recognize that some clients belong to the same process and could be executed serially without requiring a context switch, but at the time of writing neither JACK1 nor JACK2's scheduling is that smart.
</p>
<p>
If you're mixing a normal song (couple of dozen tracks) at low latency, this overhead will probably account for less than 1% of the total DSP load. If you're mixing an entire orchestra at ultra-low latency, then it might account for a quarter or more of the total DSP load.
</p>
<p>
Groups mitigate this cost by reducing the number of JACK clients required for a mix. Strips in a group will execute serially without context switches or thread synchronization--reducing the total JACK overhead. However, if you have several groups, then they may all by run in parallel by JACK2.
</p>
<p>
<p>
To illustrate this point here are some figures from an actual song session including the whole Non suite plus a sampler, a synth and an ambisonics convolution reverb with a total of 13 strips in 4 groups in different configurations on the same system.
</p>
<p>
JACK's DSP load figures are interpreted thus: if at a 2.7ms software latency setting the average time a proces cycle takes to complete is 2.7ms, then the DSP load is 100%. The usable ceiling on DSP load is 80%. This is true for both JACK1 and JACK2. The difference is that JACK2 may use all available CPU cores to execute the graph \(if there are enough clients in parallel signal flow\).
</p>
<p>
32-bit Intel Core2 Duo @1.6Ghz -r 48000 -p 256 -n 2 (5.3ms)
</p>
<center><div class="fig table"><table border=1>
<tr><th>JACK Ver</th><th>Groups</th><th>DSP Load</th></tr>
<tr><td>JACK1</td><td>N</td><td>39%</td></tr>
<tr><td>JACK1</td><td>Y</td><td>27%</td></tr>
<tr><td>JACK2</td><td>N</td><td>24%</td></tr>
<tr><td>JACK2</td><td>Y</td><td>31%</td></tr>
</table></div></center>
<p>
AMD FX-8350 @ 4.2Ghz 64-bit -r 48000 -p 256 -n 2 (5.3ms)
</p>
<center><div class="fig table"><table border=1>
<tr><th>JACK Ver</th><th>Groups</th><th>DSP Load</th></tr>
<tr><td>JACK1</td><td>N</td><td>28%</td></tr>
<tr><td>JACK1</td><td>Y</td><td>12%</td></tr>
<tr><td>JACK2</td><td>N</td><td>12%</td></tr>
<tr><td>JACK2</td><td>Y</td><td>11%</td></tr>
</table></div></center>
<p>
AMD FX-8350 @ 4.2Ghz 64-bit -r 48000 -p 128 -n 2 (2.7ms)
</p>
<center><div class="fig table"><table border=1>
<tr><th>JACK Ver</th><th>Groups</th><th>DSP Load</th></tr>
<tr><td>JACK1</td><td>N</td><td>29%</td></tr>
<tr><td>JACK1</td><td>Y</td><td>17%</td></tr>
<tr><td>JACK2</td><td>N</td><td>17%</td></tr>
<tr><td>JACK2</td><td>Y</td><td>17%</td></tr>
</table></div></center>
<p>
AMD FX-8350 @ 4.2Ghz 64-bit -r 48000 -p 32 -n 2 (0.7ms)
</p>
<center><div class="fig table"><table border=1>
<tr><th>JACK Ver</th><th>Groups</th><th>DSP Load</th></tr>
<tr><td>JACK1</td><td>N</td><td>x</td></tr>
<tr><td>JACK1</td><td>Y</td><td>x</td></tr>
<tr><td>JACK2</td><td>N</td><td>43%</td></tr>
<tr><td>JACK2</td><td>Y</td><td>41%</td></tr>
</table></div></center>
<p>
As you can see, for multiprocessor systems, JACK2 clearly has an advantage even without grouping.
</p>
<p>
Of course, results will vary depending on the system and the mix. On the dual core system, performance actually degraded with JACK2 when using groups--this is because the number of parallel flows that JACK2 detected was reduced and the second core was being under utilized. Similarly, the performance of the 8-core AMD system doesn't seem that great even in the ungrouped mode--this is because the DSP load of each individual client is around the same as the cost of the context switching. It's a wash either way (if each strip had more or more complex modules on it, then the ungrouped mode would probably perform better). Since JACK1 cannot take advantage of more than 1 CPU core, there is no benefit to parallelism and grouped mode always outperforms ungrouped mode.
</p>
<p>
So, for maximum capacity the combination of a multicore CPU with JACK2 and mixer groups is best.
</p>
<h4 id="n:1.1.2.3.">1.1.2.3. Creating a New Group</h4>
<p>
Groups can be created by selecting the group dropdown on any mixer strip and choosing 'New Group'. A window will popup asking for a group name. Group names must be unique. The group will then be created and the selected strip added to it.
</p>
<h4 id="n:1.1.2.4.">1.1.2.4. Adding a Strip to an Existing Group</h4>
<p>
To add a strip to an existing group, simply select a group name from the group dropdown on the strip.
</p>
<h4 id="n:1.1.2.5.">1.1.2.5. Removing a Strip from a Group</h4>
<p>
Select '---' from the group dropdown. The strip will be removed from the group and will run in an independent JACK client.
</p>
<h4 id="n:1.1.2.6.">1.1.2.6. Removing a Group</h4>
<p>
Groups are destroyed automatically as soon as they contain zero strips.
</p>
<h3 id="n:1.1.3.">1.1.3. Mixer Strips</h3>
<center><div class="fig image"><table id="Fig.1.3" border=1> <center><div class="fig image"><table id="Fig.1.3" border=1>
<caption> <caption>
<strong>Fig. 1.3.</strong> Mixer Strip <strong>Fig. 1.3.</strong> Mixer Strip
@ -101,19 +208,19 @@ The fader view comprises a large gain control and digital peak meter indicator.
<p> <p>
To see how an audio signal traveling through this strip will be processed, switch to its <i>signal</i> view. To see how an audio signal traveling through this strip will be processed, switch to its <i>signal</i> view.
</p> </p>
<h4 id="n:1.1.2.1.">1.1.2.1. Navigation</h4> <h4 id="n:1.1.3.1.">1.1.3.1. Navigation</h4>
<p> <p>
A strip is focused when you click on it. Focus can be moved among strips with the <tt>Tab</tt> and <tt>Shift-Tab</tt> keys. A strip is focused when you click on it. Focus can be moved among strips with the <tt>Tab</tt> and <tt>Shift-Tab</tt> keys.
</p> </p>
<h4 id="n:1.1.2.2.">1.1.2.2. Control</h4> <h4 id="n:1.1.3.2.">1.1.3.2. Control</h4>
<p> <p>
The focused strip can be moved in the display order via the <tt>[</tt> and <tt>]</tt> keys. <tt>Delete</tt> removes a strip (with confirmation dialog). <tt>n</tt> and <tt>w</tt> set the focused strip's width to <i>narrow</i> or <i>wide</i>, respectively, and <tt>f</tt> and <tt>s</tt> switch between <i>fader</i> and <i>signal</i> views. The strip's context menu can be invoked without the mouse by hitting the <tt>Menu</tt> key (assuming your keyboard has one). The focused strip can be moved in the display order via the <tt>[</tt> and <tt>]</tt> keys. <tt>Delete</tt> removes a strip (with confirmation dialog). <tt>n</tt> and <tt>w</tt> set the focused strip's width to <i>narrow</i> or <i>wide</i>, respectively, and <tt>f</tt> and <tt>s</tt> switch between <i>fader</i> and <i>signal</i> views. The strip's context menu can be invoked without the mouse by hitting the <tt>Menu</tt> key (assuming your keyboard has one).
</p> </p>
<h4 id="n:1.1.2.3.">1.1.2.3. Signal Chain</h4> <h4 id="n:1.1.3.3.">1.1.3.3. Signal Chain</h4>
<p> <p>
The signal chain view of a mixer strip provides a way to view and manipulate the signal processing of a mixer strip. The signal chain view of a mixer strip provides a way to view and manipulate the signal processing of a mixer strip.
</p> </p>
<h5 id="n:1.1.2.3.1.">1.1.2.3.1. Modules</h5> <h5 id="n:1.1.3.3.1.">1.1.3.3.1. Modules</h5>
<center><div class="fig image"><table id="Fig.1.4" border=1> <center><div class="fig image"><table id="Fig.1.4" border=1>
<caption> <caption>
<strong>Fig. 1.4.</strong> Modules <strong>Fig. 1.4.</strong> Modules
@ -148,7 +255,7 @@ Non Mixer has several built-in modules. They are:
<dt><em>Plugin</em></dt> <dt><em>Plugin</em></dt>
<dd>Hosts a LADSPA plugin</dd> <dd>Hosts a LADSPA plugin</dd>
</dl> </dl>
<h6 id="n:1.1.2.3.1.1.">1.1.2.3.1.1. OSC Control</h6> <h6 id="n:1.1.3.3.1.1.">1.1.3.3.1.1. OSC Control</h6>
<p> <p>
The input parameters of all modules are controllable via OSC, regardless of whether the parameter is set as controllable. The input parameters of all modules are controllable via OSC, regardless of whether the parameter is set as controllable.
</p> </p>
@ -185,7 +292,7 @@ For the second instance of the Gain module on the strip named 'Foo'.
<p> <p>
Non-DAW accesses these same signals via a more advanced signal routing layer on top of OSC. Any module parameter is easily controlled via Control Sequences in Non-DAW without the need to specify an OSC URL. Non-DAW accesses these same signals via a more advanced signal routing layer on top of OSC. Any module parameter is easily controlled via Control Sequences in Non-DAW without the need to specify an OSC URL.
</p> </p>
<h6 id="n:1.1.2.3.1.2.">1.1.2.3.1.2. Manipulation</h6> <h6 id="n:1.1.3.3.1.2.">1.1.3.3.1.2. Manipulation</h6>
<p> <p>
Left-clicking on a module brings up a Module Parameter Editor window for the selected module. Left-clicking on a module brings up a Module Parameter Editor window for the selected module.
</p> </p>
@ -201,7 +308,7 @@ Control+Right-clicking on a module causes it to be removed from the chain (modul
<p> <p>
The focused module may also be controlled via the keyboard. <tt>Menu</tt> brings up the context menu for the focused module. <tt>Space</tt> opens the module parameter editor, <tt>b</tt> toggles the bypassed state, and <tt>Delete</tt> removes the module from the chain (without confirmation!). <tt>Control-X</tt>, <tt>Control-C</tt> and <tt>Control-V</tt>, cut, copy, and paste modules, respectively. Modules may be copied within or across chain boundaries. The normal module I/O constraints also apply to pasted modules. The focused module may also be controlled via the keyboard. <tt>Menu</tt> brings up the context menu for the focused module. <tt>Space</tt> opens the module parameter editor, <tt>b</tt> toggles the bypassed state, and <tt>Delete</tt> removes the module from the chain (without confirmation!). <tt>Control-X</tt>, <tt>Control-C</tt> and <tt>Control-V</tt>, cut, copy, and paste modules, respectively. Modules may be copied within or across chain boundaries. The normal module I/O constraints also apply to pasted modules.
</p> </p>
<h6 id="n:1.1.2.3.1.3.">1.1.2.3.1.3. Module Parameter Editor</h6> <h6 id="n:1.1.3.3.1.3.">1.1.3.3.1.3. Module Parameter Editor</h6>
<center><div class="fig image"><table id="Fig.1.5" border=1> <center><div class="fig image"><table id="Fig.1.5" border=1>
<caption> <caption>
<strong>Fig. 1.5.</strong> Module Parameter Editor <strong>Fig. 1.5.</strong> Module Parameter Editor
@ -229,7 +336,7 @@ The Module Parameter Editor is used to alter the values of a module's parameters
<p> <p>
Underneath each control is a bind button. Clicking adds a new control to the chain's <i>Controls</i> view and binds it to the parameter in question. For simplicity, only one control at a time may be bound to a given parameter. Underneath each control is a bind button. Clicking adds a new control to the chain's <i>Controls</i> view and binds it to the parameter in question. For simplicity, only one control at a time may be bound to a given parameter.
</p> </p>
<h6 id="n:1.1.2.3.1.4.">1.1.2.3.1.4. Controls</h6> <h6 id="n:1.1.3.3.1.4.">1.1.3.3.1.4. Controls</h6>
<center><div class="fig image"><table id="Fig.1.8" border=1> <center><div class="fig image"><table id="Fig.1.8" border=1>
<caption> <caption>
<strong>Fig. 1.8.</strong> Control View <strong>Fig. 1.8.</strong> Control View
@ -248,7 +355,7 @@ events. Hold down the `Ctrl` key while scrolling the mousewheel to
achieve finer resolution. achieve finer resolution.
</td></table> </td></table>
</div> </div>
<h7 id="n:1.1.2.3.1.4.1.">1.1.2.3.1.4.1. Control Voltages</h7> <h7 id="n:1.1.3.3.1.4.1.">1.1.3.3.1.4.1. Control Voltages</h7>
<p> <p>
The control voltage concept should be familiar to anyone who has experience with analog modular synthesizers. MIDI, while having definite advantages in many respects, multiplexes control data in such a way as to make connecting one MIDI control to a parameter involve a significant inconvenience, usually requiring the adjustment of settings on both ends of the connection in order to separate the control data streams. The control voltage concept should be familiar to anyone who has experience with analog modular synthesizers. MIDI, while having definite advantages in many respects, multiplexes control data in such a way as to make connecting one MIDI control to a parameter involve a significant inconvenience, usually requiring the adjustment of settings on both ends of the connection in order to separate the control data streams.
</p> </p>
@ -267,7 +374,7 @@ of parameter automation, as LADSPA plugins are incapable of
processing Control Voltage signals at full audio resolution anyway. processing Control Voltage signals at full audio resolution anyway.
</td></table> </td></table>
</div> </div>
<h6 id="n:1.1.2.3.1.5.">1.1.2.3.1.5. Spatialization </h6> <h6 id="n:1.1.3.3.1.5.">1.1.3.3.1.5. Spatialization </h6>
<center><div class="fig image"><table id="Fig.1.9" border=1> <center><div class="fig image"><table id="Fig.1.9" border=1>
<caption> <caption>
<strong>Fig. 1.9.</strong> Spatialization Control on a Strip <strong>Fig. 1.9.</strong> Spatialization Control on a Strip
@ -291,7 +398,7 @@ The spatialization control may be visualized as moving the sound source across t
<p> <p>
The output of the spatializing plugin may be routed into a decoding plugin following it the same strip or, more usefully, the output of a number of Ambisonic panning plugins on different strips may be routed (through JACK) into a single master decoder instance on a final strip. The output of the spatializing plugin may be routed into a decoding plugin following it the same strip or, more usefully, the output of a number of Ambisonic panning plugins on different strips may be routed (through JACK) into a single master decoder instance on a final strip.
</p> </p>
<h3 id="n:1.1.3.">1.1.3. Projects</h3> <h3 id="n:1.1.4.">1.1.4. Projects</h3>
<p> <p>
A Non-Mixer project is a directory where Non-Mixer keeps the strip settings, project specific settings, and some meta-data. A project is completely self-contained. You can rename a project as simply as: A Non-Mixer project is a directory where Non-Mixer keeps the strip settings, project specific settings, and some meta-data. A project is completely self-contained. You can rename a project as simply as:
</p> </p>
@ -299,7 +406,7 @@ A Non-Mixer project is a directory where Non-Mixer keeps the strip settings, pro
$ mv Project-A Project-B $ mv Project-A Project-B
</pre></td></tr> </pre></td></tr>
</table></div> </table></div>
<h4 id="n:1.1.3.1.">1.1.3.1. JACK I/O</h4> <h4 id="n:1.1.4.1.">1.1.4.1. JACK I/O</h4>
<p> <p>
Each mixer strip is presented as a separate JACK "client". This helps to avoid the necessity of internally duplicating JACK's routing logic and, with JACK2, permits the possibility of parallel execution of mixer strip signal chains. Each mixer strip is presented as a separate JACK "client". This helps to avoid the necessity of internally duplicating JACK's routing logic and, with JACK2, permits the possibility of parallel execution of mixer strip signal chains.
</p> </p>

View File

@ -31,6 +31,151 @@
to suit your needs. Set the color scheme, widget style, and other graphic to suit your needs. Set the color scheme, widget style, and other graphic
options to your liking. These options are global and affect all projects. options to your liking. These options are global and affect all projects.
::: Mixer Groups
Groups serve several purposes. Firstly, they allow for some
organization of strips. Groups also allow parallel relationships of
mixer strips to be made explicit. This has important performance
implications in JACK2. Non Mixer supports an unlimited number of
groups, each of which can contain an unlimited number of mixer
strips.
:::: How to Choose Groupings
All strips in a group should be completely parallel with no feedback
loop connections. A typical group might be named 'Input' and contain
all input strips (strips that accept input from Non Timeline and
have outputs all connecting to some master bus).
To put it another way, if you have 100 inputs strips with identical
output configurations (e.g. stereo or B-Format), that all connect to
a master bus, then you have a candidate for a group.
:::: Considering JACK Overhead
JACK provides immense flexibility. But, as in most situations, that
flexibility comes with a cost. In JACK the cost is a context switch
per client. This applies /even for many clients which belong to the
same process/, as in Non Mixer. Various factors go into determining
the price of a context switch on any given system. It's not very
expensive, but it does add up. It becomes problematic in sessions
involving many clients (think 100s), each of which having a small
DSP load (often smaller than the cost of JACK's context context
switch). JACK *could* be smart enough to recognize that some clients
belong to the same process and could be executed serially without
requiring a context switch, but at the time of writing neither JACK1
nor JACK2's scheduling is that smart.
If you're mixing a normal song (couple of dozen tracks) at low
latency, this overhead will probably account for less than 1% of the
total DSP load. If you're mixing an entire orchestra at ultra-low
latency, then it might account for a quarter or more of the total
DSP load.
Groups mitigate this cost by reducing the number of JACK clients
required for a mix. Strips in a group will execute serially without
context switches or thread synchronization--reducing the total JACK
overhead. However, if you have several groups, then they may all by
run in parallel by JACK2.
A mixer which uses a single JACK client (which is basically the way
everything other than Non Mixer has been designed) is not a viable
solution by this author's definition, because such a mixer cannot be
from/to any other JACK clients without introducing an extra period
of latency.
To illustrate this point here are some figures from an actual song
session including the whole Non suite plus a sampler, a synth and an
ambisonics convolution reverb with a total of 13 strips in 4 groups
in different configurations on the same system.
JACK's DSP load figures are interpreted thus: if at a 2.7ms software
latency setting the average time a proces cycle takes to complete is
2.7ms, then the DSP load is 100%. The usable ceiling on DSP load is
80%. This is true for both JACK1 and JACK2. The difference is that
JACK2 may use all available CPU cores to execute the graph \(if
there are enough clients in parallel signal flow\).
32-bit Intel Core2 Duo @1.6Ghz -r 48000 -p 256 -n 2 (5.3ms)
[[ JACK Ver, Groups, DSP Load
[[ JACK1, N, 39%
[[ JACK1, Y, 27%
[[ JACK2, N, 24%
[[ JACK2, Y, 31%
AMD FX-8350 @ 4.2Ghz 64-bit -r 48000 -p 256 -n 2 (5.3ms)
[[ JACK Ver, Groups, DSP Load
[[ JACK1, N, 28%
[[ JACK1, Y, 12%
[[ JACK2, N, 12%
[[ JACK2, Y, 11%
AMD FX-8350 @ 4.2Ghz 64-bit -r 48000 -p 128 -n 2 (2.7ms)
[[ JACK Ver, Groups, DSP Load
[[ JACK1, N, 29%
[[ JACK1, Y, 17%
[[ JACK2, N, 17%
[[ JACK2, Y, 17%
AMD FX-8350 @ 4.2Ghz 64-bit -r 48000 -p 32 -n 2 (0.7ms)
[[ JACK Ver, Groups, DSP Load
[[ JACK1, N, x
[[ JACK1, Y, x
[[ JACK2, N, 43%
[[ JACK2, Y, 41%
As you can see, for multiprocessor systems, JACK2 clearly has an
advantage even without grouping.
Of course, results will vary depending on the system and the mix. On
the dual core system, performance actually degraded with JACK2 when
using groups--this is because the number of parallel flows that
JACK2 detected was reduced and the second core was being under
utilized. Similarly, the performance of the 8-core AMD system
doesn't seem that great even in the ungrouped mode--this is because
the DSP load of each individual client is around the same as the
cost of the context switching. It's a wash either way (if each strip
had more or more complex modules on it, then the ungrouped mode
would probably perform better). Since JACK1 cannot take advantage of
more than 1 CPU core, there is no benefit to parallelism and grouped
mode always outperforms ungrouped mode.
So, for maximum capacity the combination of a multicore CPU with
JACK2 and mixer groups is best.
# All strips in a group *MUST* have the same output configuration. All
# outputs will be mixed together by identity. That is, the 'AUX \(A\)'
# outputs of each strip will be mixed together into a single 'AUX \(A\)'
# output of the group. A strip within a group whose output
# configuration differs from the group configuration will be marked as
# invalid and will not be executed.
:::: Creating a New Group
Groups can be created by selecting the group dropdown on any mixer
strip and choosing 'New Group'. A window will popup asking for a
group name. Group names must be unique. The group will then be
created and the selected strip added to it.
:::: Adding a Strip to an Existing Group
To add a strip to an existing group, simply select a group name from
the group dropdown on the strip.
:::: Removing a Strip from a Group
Select '---' from the group dropdown. The strip will be removed from
the group and will run in an independent JACK client.
:::: Removing a Group
Groups are destroyed automatically as soon as they contain zero
strips.
::: Mixer Strips ::: Mixer Strips
/ Mixer Strip / Mixer Strip

View File

@ -122,7 +122,7 @@ AUX_Module::process ( nframes_t nframes )
for ( unsigned int i = 0; i < audio_input.size(); ++i ) for ( unsigned int i = 0; i < audio_input.size(); ++i )
{ {
if ( audio_input[i].connected() ) if ( audio_input[i].connected() )
buffer_copy_and_apply_gain_buffer( (sample_t*)jack_output[i].buffer( nframes ), (sample_t*)audio_input[i].buffer(), gainbuf, nframes ); buffer_copy_and_apply_gain_buffer( (sample_t*)aux_audio_output[i].buffer(), (sample_t*)audio_input[i].buffer(), gainbuf, nframes );
} }
} }
@ -131,7 +131,7 @@ AUX_Module::process ( nframes_t nframes )
for ( unsigned int i = 0; i < audio_input.size(); ++i ) for ( unsigned int i = 0; i < audio_input.size(); ++i )
{ {
if ( audio_input[i].connected() ) if ( audio_input[i].connected() )
buffer_copy_and_apply_gain( (sample_t*)jack_output[i].buffer( nframes ), (sample_t*)audio_input[i].buffer(), nframes, gt ); buffer_copy_and_apply_gain( (sample_t*)aux_audio_output[i].buffer(), (sample_t*)audio_input[i].buffer(), nframes, gt );
} }
} }
} }
@ -140,7 +140,7 @@ AUX_Module::process ( nframes_t nframes )
for ( unsigned int i = 0; i < audio_input.size(); ++i ) for ( unsigned int i = 0; i < audio_input.size(); ++i )
{ {
if ( audio_input[i].connected() ) if ( audio_input[i].connected() )
buffer_fill_with_silence( (sample_t*)jack_output[i].buffer( nframes ), nframes ); buffer_fill_with_silence( (sample_t*)aux_audio_output[i].buffer(), nframes );
} }
} }
} }

View File

@ -72,11 +72,11 @@
#include "FL/test_press.H" #include "FL/test_press.H"
#include "debug.h" #include "debug.h"
#include "Engine/Engine.H" #include "Group.H"
#include "Mixer_Strip.H" #include "Mixer_Strip.H"
#include <dsp.h> #include <dsp.h>
#include "Mixer.H"
extern char *instance_name; extern char *instance_name;
@ -87,7 +87,6 @@ Chain::Chain ( ) : Fl_Group( 0, 0, 100, 100, "")
{ {
_deleting = false; _deleting = false;
_engine = NULL;
int X = 0; int X = 0;
int Y = 0; int Y = 0;
@ -174,18 +173,23 @@ Chain::~Chain ( )
_deleting = true; _deleting = true;
engine()->lock(); client()->lock();
for ( unsigned int i = scratch_port.size(); i--; ) for ( unsigned int i = scratch_port.size(); i--; )
delete[] (sample_t*)scratch_port[i].buffer(); delete[] (sample_t*)scratch_port[i].buffer();
/* if we leave this up to FLTK, it will happen after we've /* if we leave this up to FLTK, it will happen after we've
already destroyed the engine */ already destroyed the client */
modules_pack->clear(); modules_pack->clear();
controls_pack->clear(); controls_pack->clear();
delete _engine; client()->unlock();
_engine = NULL; }
Group *
Chain::client ( void )
{
return strip()->group();
} }
@ -302,7 +306,7 @@ Chain::remove ( Controller_Module *m )
{ {
DMESSAGE( "Removing controller module from chain" ); DMESSAGE( "Removing controller module from chain" );
engine()->lock(); client()->lock();
m->disconnect(); m->disconnect();
@ -311,7 +315,7 @@ Chain::remove ( Controller_Module *m )
build_process_queue(); build_process_queue();
engine()->unlock(); client()->unlock();
redraw(); redraw();
} }
@ -340,7 +344,7 @@ Chain::remove ( Module *m )
fl_alert( "Can't remove module at this point because the resultant chain is invalid" ); fl_alert( "Can't remove module at this point because the resultant chain is invalid" );
} }
engine()->lock(); client()->lock();
strip()->handle_module_removed( m ); strip()->handle_module_removed( m );
@ -348,7 +352,7 @@ Chain::remove ( Module *m )
configure_ports(); configure_ports();
engine()->unlock(); client()->unlock();
} }
/* determine number of output ports, signal if changed. */ /* determine number of output ports, signal if changed. */
@ -357,7 +361,7 @@ Chain::configure_ports ( void )
{ {
int nouts = 0; int nouts = 0;
engine()->lock(); client()->lock();
for ( int i = 0; i < modules(); ++i ) for ( int i = 0; i < modules(); ++i )
{ {
@ -378,15 +382,15 @@ Chain::configure_ports ( void )
for ( unsigned int i = 0; i < req_buffers; ++i ) for ( unsigned int i = 0; i < req_buffers; ++i )
{ {
Module::Port p( NULL, Module::Port::OUTPUT, Module::Port::AUDIO ); Module::Port p( NULL, Module::Port::OUTPUT, Module::Port::AUDIO );
p.connect_to( new sample_t[engine()->nframes()] ); p.connect_to( new sample_t[client()->nframes()] );
buffer_fill_with_silence( (sample_t*)p.buffer(), engine()->nframes() ); buffer_fill_with_silence( (sample_t*)p.buffer(), client()->nframes() );
scratch_port.push_back( p ); scratch_port.push_back( p );
} }
} }
build_process_queue(); build_process_queue();
engine()->unlock(); client()->unlock();
parent()->redraw(); parent()->redraw();
} }
@ -454,47 +458,40 @@ Chain::maximum_name_length ( void )
return JACK::Client::maximum_name_length() - ( strlen( instance_name ) + 1 ); return JACK::Client::maximum_name_length() - ( strlen( instance_name ) + 1 );
} }
void
Chain::freeze_ports ( void )
{
for ( int i = 0; i < modules(); i++ )
{
Module *m = module(i);
m->freeze_ports();
}
}
void
Chain::thaw_ports ( void )
{
for ( int i = 0; i < modules(); i++ )
{
Module *m = module(i);
m->thaw_ports();
}
}
/* rename chain... we have to let our modules know our name has /* rename chain... we have to let our modules know our name has
* changed so they can take the appropriate action (in particular the * changed so they can take the appropriate action (in particular the
* JACK module). */ * JACK module). */
void void
Chain::name ( const char *name ) Chain::name ( const char *name )
{ {
char ename[512];
snprintf( ename, sizeof(ename), "%s/%s", instance_name, name );
if ( ! _engine )
{
_engine = new Engine( &Chain::process, this );
engine()->buffer_size_callback( &Chain::buffer_size, this );
engine()->port_connect_callback( &Chain::port_connect, this );
engine()->sample_rate_changed_callback( &Chain::sample_rate_change, this );
Module::set_sample_rate( engine()->sample_rate() );
const char *jack_name = engine()->init( ename );
if ( ! jack_name )
{
_engine = NULL;
fl_alert( "Could not create JACK client. Perhaps the sound device already in use. In any event, now I'll die." );
exit( 1 );
return;
}
}
else
{
DMESSAGE( "Renaming JACK client from \"%s\" to \"%s\"", _name, ename );
_name = engine()->name( ename );
/* FIXME: discarding the name jack picked is technically wrong! */
}
_name = name; _name = name;
if ( strip()->group() )
{
if ( strip()->group()->single() )
strip()->group()->name(name);
}
for ( int i = 0; i < modules(); ++i ) for ( int i = 0; i < modules(); ++i )
{ {
module( i )->handle_chain_name_changed(); module( i )->handle_chain_name_changed();
@ -522,7 +519,7 @@ Chain::add ( Controller_Module *m )
bool bool
Chain::insert ( Module *m, Module *n ) Chain::insert ( Module *m, Module *n )
{ {
engine()->lock(); client()->lock();
if ( !m ) if ( !m )
{ {
@ -581,7 +578,7 @@ Chain::insert ( Module *m, Module *n )
configure_ports(); configure_ports();
engine()->unlock(); client()->unlock();
DMESSAGE( "Module \"%s\" has %i:%i audio and %i:%i control ports", DMESSAGE( "Module \"%s\" has %i:%i audio and %i:%i control ports",
n->name(), n->name(),
@ -595,7 +592,7 @@ Chain::insert ( Module *m, Module *n )
err: err:
engine()->unlock(); client()->unlock();
DMESSAGE( "Insert failed" ); DMESSAGE( "Insert failed" );
@ -606,13 +603,13 @@ err:
void void
Chain::add_control ( Controller_Module *m ) Chain::add_control ( Controller_Module *m )
{ {
engine()->lock(); client()->lock();
controls_pack->add( m ); controls_pack->add( m );
configure_ports(); configure_ports();
engine()->unlock(); client()->unlock();
controls_pack->redraw(); controls_pack->redraw();
} }
@ -797,15 +794,9 @@ Chain::do_export ( const char *filename )
/**********/ /**********/
/* Engine */ /* Client */
/**********/ /**********/
void
Chain::process ( nframes_t nframes, void *v )
{
((Chain*)v)->process( nframes );
}
void void
Chain::process ( nframes_t nframes ) Chain::process ( nframes_t nframes )
{ {
@ -817,12 +808,6 @@ Chain::process ( nframes_t nframes )
} }
} }
void
Chain::buffer_size ( nframes_t nframes, void *v )
{
((Chain*)v)->buffer_size( nframes );
}
void void
Chain::buffer_size ( nframes_t nframes ) Chain::buffer_size ( nframes_t nframes )
{ {
@ -840,12 +825,6 @@ Chain::buffer_size ( nframes_t nframes )
} }
} }
int
Chain::sample_rate_change ( nframes_t nframes, void *v )
{
return ((Chain*)v)->sample_rate_change( nframes );
}
int int
Chain::sample_rate_change ( nframes_t nframes ) Chain::sample_rate_change ( nframes_t nframes )
{ {
@ -860,12 +839,6 @@ Chain::sample_rate_change ( nframes_t nframes )
return 0; return 0;
} }
void
Chain::port_connect ( jack_port_id_t a, jack_port_id_t b, int connect, void *v )
{
((Chain*)v)->port_connect( a, b, connect );
}
/* handle jack port connection change */ /* handle jack port connection change */
void void
Chain::port_connect ( jack_port_id_t a, jack_port_id_t b, int connect ) Chain::port_connect ( jack_port_id_t a, jack_port_id_t b, int connect )
@ -875,8 +848,8 @@ Chain::port_connect ( jack_port_id_t a, jack_port_id_t b, int connect )
/* this is called from JACK non-RT thread... */ /* this is called from JACK non-RT thread... */
if ( jack_port_is_mine( engine()->jack_client(), jack_port_by_id( engine()->jack_client(), a ) ) || if ( jack_port_is_mine( client()->jack_client(), jack_port_by_id( client()->jack_client(), a ) ) ||
jack_port_is_mine( engine()->jack_client(), jack_port_by_id( engine()->jack_client(), b ) )) jack_port_is_mine( client()->jack_client(), jack_port_by_id( client()->jack_client(), b ) ))
{ {
Fl::awake( Chain::update_connection_status, this ); Fl::awake( Chain::update_connection_status, this );
} }

View File

@ -28,11 +28,11 @@
#include <vector> #include <vector>
#include <list> #include <list>
#include "Loggable.H" #include "Loggable.H"
#include "Group.H"
class Mixer_Strip; class Mixer_Strip;
class Fl_Flowpack; class Fl_Flowpack;
class Fl_Flip_Button; class Fl_Flip_Button;
class Engine;
class Controller_Module; class Controller_Module;
class Chain : public Fl_Group, public Loggable { class Chain : public Fl_Group, public Loggable {
@ -50,8 +50,6 @@ class Chain : public Fl_Group, public Loggable {
std::vector <Module::Port> scratch_port; std::vector <Module::Port> scratch_port;
Engine *_engine;
Fl_Callback *_configure_outputs_callback; Fl_Callback *_configure_outputs_callback;
void *_configure_outputs_userdata; void *_configure_outputs_userdata;
@ -69,18 +67,6 @@ private:
void build_process_queue ( void ); void build_process_queue ( void );
void add_to_process_queue ( Module *m ); void add_to_process_queue ( Module *m );
static void process ( nframes_t, void * );
void process ( nframes_t );
static void buffer_size ( nframes_t nframes, void *v );
void buffer_size ( nframes_t nframes );
static int sample_rate_change ( nframes_t nframes, void *v );
int sample_rate_change ( nframes_t nframes );
static void port_connect ( jack_port_id_t a, jack_port_id_t b, int connect, void *v );
void port_connect ( jack_port_id_t a, jack_port_id_t b, int connect );
static void update_connection_status ( void *v ); static void update_connection_status ( void *v );
void update_connection_status ( void ); void update_connection_status ( void );
@ -91,6 +77,11 @@ protected:
public: public:
void port_connect ( jack_port_id_t a, jack_port_id_t b, int connect );
void buffer_size ( nframes_t nframes );
int sample_rate_change ( nframes_t nframes );
void process ( nframes_t );
Chain ( int X, int Y, int W, int H, const char *L = 0 ); Chain ( int X, int Y, int W, int H, const char *L = 0 );
Chain ( ); Chain ( );
virtual ~Chain ( ); virtual ~Chain ( );
@ -140,7 +131,12 @@ public:
static unsigned int maximum_name_length ( void ); static unsigned int maximum_name_length ( void );
Engine *engine ( void ) const { return _engine; } Group *client ( void );
void freeze_ports ( void );
void thaw_ports ( void );
// void client ( Client * );
LOG_CREATE_FUNC( Chain ); LOG_CREATE_FUNC( Chain );
}; };

View File

@ -38,7 +38,6 @@
#include "FL/test_press.H" #include "FL/test_press.H"
#include "FL/menu_popup.H" #include "FL/menu_popup.H"
#include "Engine/Engine.H"
#include "Chain.H" #include "Chain.H"
#include "OSC/Endpoint.H" #include "OSC/Endpoint.H"
@ -195,40 +194,31 @@ Controller_Module::set ( Log_Entry &e )
void void
Controller_Module::mode ( Mode m ) Controller_Module::mode ( Mode m )
{ {
if( mode() != CV && m == CV ) if( mode() != CV && m == CV )
{ {
if ( control_output[0].connected() ) if ( control_output[0].connected() )
{ {
chain()->engine()->lock(); chain()->client()->lock();
Port *p = control_output[0].connected_port(); Port *p = control_output[0].connected_port();
JACK::Port po( chain()->engine(), JACK::Port::Input, JACK::Port::Audio, p->name(), 0, "CV" ); char prefix[512];
snprintf( prefix, sizeof(prefix), "CV-%s", p->name() );
if ( ! po.activate() ) add_aux_audio_input( prefix, 0 );
{
fl_alert( "Could not activate JACK port \"%s\"", po.name() );
chain()->engine()->unlock();
return;
}
if ( po.valid() ) chain()->client()->unlock();
{
jack_input.push_back( po );
}
chain()->engine()->unlock();
} }
} }
else if ( mode() == CV && m != CV ) else if ( mode() == CV && m != CV )
{ {
chain()->engine()->lock(); chain()->client()->lock();
jack_input.back().shutdown(); delete aux_audio_input.back().jack_port();
jack_input.pop_back();
chain()->engine()->unlock(); aux_audio_input.pop_back();
chain()->client()->unlock();
} }
_mode = m ; _mode = m ;
@ -909,7 +899,7 @@ Controller_Module::command_remove ( void )
} }
/**********/ /**********/
/* Engine */ /* Client */
/**********/ /**********/
void void
@ -928,7 +918,7 @@ Controller_Module::process ( nframes_t nframes )
if ( mode() == CV ) if ( mode() == CV )
{ {
f = *((float*)jack_input[0].buffer( nframes )); f = *((float*)aux_audio_input[0].jack_port()->buffer( nframes ));
const Port *p = control_output[0].connected_port(); const Port *p = control_output[0].connected_port();

View File

@ -116,7 +116,6 @@ private:
char *generate_osc_path ( void ); char *generate_osc_path ( void );
void change_osc_path ( char *path ); void change_osc_path ( char *path );
std::vector<JACK::Port> jack_input;
Mode _mode; Mode _mode;
Type _type; Type _type;

View File

@ -1,163 +0,0 @@
/*******************************************************************************/
/* Copyright (C) 2008 Jonathan Moore Liles */
/* */
/* This program is free software; you can redistribute it and/or modify it */
/* under the terms of the GNU General Public License as published by the */
/* Free Software Foundation; either version 2 of the License, or (at your */
/* option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
/* more details. */
/* */
/* You should have received a copy of the GNU General Public License along */
/* with This program; see the file COPYING. If not,write to the Free Software */
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/*******************************************************************************/
#include "Engine.H"
// #include "../Mixer.H" // for process()
/* This is the home of the JACK process callback */
// #include "const.h"
#include "debug.h"
#include "Thread.H"
Engine::Engine ( void (*process_callback)(nframes_t nframes, void *), void *user_data ) : _thread( "RT" )
{
_process_callback = process_callback;
_process_callback_user_data = user_data;
_buffer_size_callback = 0;
_buffers_dropped = 0;
_port_connect_callback = 0;
_sample_rate_changed_callback = 0;
}
Engine::~Engine ( )
{
deactivate();
}
void
Engine::buffer_size_callback ( void ( *buffer_size_callback ) ( nframes_t, void * ), void *user_data )
{
_buffer_size_callback = buffer_size_callback;
_buffer_size_callback_user_data = user_data;
}
void
Engine::sample_rate_changed_callback ( int ( *sample_rate_changed_callback ) ( nframes_t, void * ), void *user_data )
{
_sample_rate_changed_callback = sample_rate_changed_callback;
_sample_rate_changed_callback_user_data = user_data;
}
void
Engine::port_connect_callback ( void ( *port_connect_callback ) ( jack_port_id_t a, jack_port_id_t b, int connect, void *arg), void *user_data )
{
_port_connect_callback = port_connect_callback;
_port_connect_callback_user_data = user_data;
}
/*************/
/* Callbacks */
/*************/
/* THREAD: RT */
/** This is the jack xrun callback */
int
Engine::xrun ( void )
{
return 0;
}
/* THREAD: RT */
void
Engine::freewheel ( bool starting )
{
if ( starting )
DMESSAGE( "entering freewheeling mode" );
else
DMESSAGE( "leaving freewheeling mode" );
}
/* THREAD: RT (non-RT) */
int
Engine::buffer_size ( nframes_t nframes )
{
/* JACK calls this in the RT thread, even though it's a
* non-realtime operation. This mucks up our ability to do
* THREAD_ASSERT, so just lie and say this is the UI thread... */
_thread.set( "UI" );
_buffer_size_callback( nframes, _buffer_size_callback_user_data );
_thread.set( "RT" );
return 0;
}
/* THREAD: ?? */
void
Engine::port_connect( jack_port_id_t a, jack_port_id_t b, int connect )
{
if ( _port_connect_callback )
_port_connect_callback( a, b, connect, _port_connect_callback_user_data );
}
/* THREAD: RT */
int
Engine::process ( nframes_t nframes )
{
/* FIXME: wrong place for this */
_thread.set( "RT" );
if ( ! trylock() )
{
/* the data structures we need to access here (tracks and
* their ports, but not track contents) may be in an
* inconsistent state at the moment. Just punt and drop this
* buffer. */
++_buffers_dropped;
return 0;
}
_process_callback(nframes, _process_callback_user_data);
unlock();
return 0;
}
int
Engine::sample_rate_changed ( nframes_t srate )
{
if ( _sample_rate_changed_callback )
return _sample_rate_changed_callback( srate, _sample_rate_changed_callback_user_data );
return 0;
}
/* TRHEAD: RT */
void
Engine::thread_init ( void )
{
_thread.set( "RT" );
}
/* THREAD: RT */
void
Engine::shutdown ( void )
{
}

274
mixer/src/Group.C Normal file
View File

@ -0,0 +1,274 @@
/*******************************************************************************/
/* Copyright (C) 2013 Jonathan Moore Liles */
/* */
/* This program is free software; you can redistribute it and/or modify it */
/* under the terms of the GNU General Public License as published by the */
/* Free Software Foundation; either version 2 of the License, or (at your */
/* option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
/* more details. */
/* */
/* You should have received a copy of the GNU General Public License along */
/* with This program; see the file COPYING. If not,write to the Free Software */
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/*******************************************************************************/
#include <Mixer.H>
#include "Group.H"
#include "Chain.H"
#include "Mixer_Strip.H"
#include "Module.H"
extern char *instance_name;
Group::Group ( )
{
_single =false;
_name = NULL;
}
Group::Group ( const char *name, bool single ) : Loggable ( !single )
{
_single = single;
_name = strdup(name);
// this->name( name );
/* FIXME: handle client creation error */
/* if ( ! jack_name ) */
/* { */
/* _engine = NULL; */
/* // fl_alert( "Could not create JACK client. Perhaps the sound device already in use. In any event, now I'll die." ); */
/* exit( 1 ); */
/* // return false; */
/* } */
}
Group::~Group ( )
{
DMESSAGE( "Destroying group" );
if ( _name )
free( _name );
deactivate();
}
void
Group::get ( Log_Entry &e ) const
{
e.add( ":name", name() );
}
void
Group::set ( Log_Entry &e )
{
for ( int i = 0; i < e.size(); ++i )
{
const char *s, *v;
e.get( i, &s, &v );
if ( ! ( strcmp( s, ":name" ) ) )
{
bool add = false;
if (!_name )
add = true;
_name = strdup(v);
if ( add )
mixer->add_group(this);
}
}
}
/*************/
/* Callbacks */
/*************/
/* THREAD: RT */
/** This is the jack xrun callback */
int
Group::xrun ( void )
{
return 0;
}
/* THREAD: RT */
void
Group::freewheel ( bool starting )
{
if ( starting )
DMESSAGE( "entering freewheeling mode" );
else
DMESSAGE( "leaving freewheeling mode" );
}
/* THREAD: RT (non-RT) */
int
Group::buffer_size ( nframes_t nframes )
{
/* JACK calls this in the RT thread, even though it's a
* non-realtime operation. This mucks up our ability to do
* THREAD_ASSERT, so just lie and say this is the UI thread... */
_thread.set( "UI" );
for ( std::list<Mixer_Strip*>::iterator i = strips.begin();
i != strips.end();
i++ )
{
if ( (*i)->chain() )
(*i)->chain()->buffer_size(nframes);
}
_thread.set( "RT" );
return 0;
}
/* THREAD: ?? */
void
Group::port_connect( jack_port_id_t a, jack_port_id_t b, int connect )
{
for ( std::list<Mixer_Strip*>::iterator i = strips.begin();
i != strips.end();
i++ )
{
if ( (*i)->chain() )
(*i)->chain()->port_connect( a, b, connect);
}
}
/* THREAD: RT */
int
Group::process ( nframes_t nframes )
{
/* FIXME: wrong place for this */
_thread.set( "RT" );
if ( ! trylock() )
{
/* the data structures we need to access here (tracks and
* their ports, but not track contents) may be in an
* inconsistent state at the moment. Just punt and drop this
* buffer. */
++_buffers_dropped;
return 0;
}
/* since feedback loops are forbidden and outputs are
* summed, we don't care what order these are processed
* in */
for ( std::list<Mixer_Strip*>::iterator i = strips.begin();
i != strips.end();
i++ )
{
if ( (*i)->chain() )
(*i)->chain()->process(nframes);
}
unlock();
return 0;
}
int
Group::sample_rate_changed ( nframes_t srate )
{
for ( std::list<Mixer_Strip*>::iterator i = strips.begin();
i != strips.end();
i++ )
{
if ( (*i)->chain() )
(*i)->chain()->sample_rate_change(srate);
}
return 0;
}
/* TRHEAD: RT */
void
Group::thread_init ( void )
{
_thread.set( "RT" );
}
/* THREAD: RT */
void
Group::shutdown ( void )
{
}
/*******************/
/* Group interface */
/*******************/
void
Group::name ( const char *n )
{
if ( _name )
free( _name );
char ename[512];
_name = strdup( n );
if ( _single )
snprintf( ename, sizeof(ename), "%s/%s", instance_name, n );
else
snprintf( ename, sizeof(ename), "%s (%s)", instance_name, n );
if ( !active() )
{
const char *jack_name = Client::init( ename );
Module::set_sample_rate( sample_rate() );
}
else
{
Client::name( ename );
}
}
void
Group::add ( Mixer_Strip *o )
{
lock();
if ( ! active() )
{
/* to call init */
char *n = strdup(name());
name(n);
free(n);
}
if ( o->chain() )
o->chain()->thaw_ports();
strips.push_back(o);
unlock();
}
void
Group::remove ( Mixer_Strip *o )
{
lock();
strips.remove(o);
if ( o->chain() )
o->chain()->freeze_ports();
if ( strips.size() == 0 && active() )
{
Client::close();
}
unlock();
}

View File

@ -1,6 +1,6 @@
/*******************************************************************************/ /*******************************************************************************/
/* Copyright (C) 2008 Jonathan Moore Liles */ /* Copyright (C) 2013 Jonathan Moore Liles */
/* */ /* */
/* This program is free software; you can redistribute it and/or modify it */ /* This program is free software; you can redistribute it and/or modify it */
/* under the terms of the GNU General Public License as published by the */ /* under the terms of the GNU General Public License as published by the */
@ -19,6 +19,9 @@
#pragma once #pragma once
#include <list>
class Mixer_Strip;
#include "Mutex.H" #include "Mutex.H"
class Port; class Port;
@ -26,26 +29,18 @@ class Port;
#include "JACK/Client.H" #include "JACK/Client.H"
#include "Thread.H" #include "Thread.H"
#include "Loggable.H"
class Engine : public JACK::Client, public Mutex class Group : public Loggable, public JACK::Client, public Mutex
{ {
bool _single;
char *_name;
Thread _thread; /* only used for thread checking */ Thread _thread; /* only used for thread checking */
int _buffers_dropped; /* buffers dropped because of locking */ int _buffers_dropped; /* buffers dropped because of locking */
/* int _buffers_dropped; /\* buffers dropped because of locking *\/ */ /* int _buffers_dropped; /\* buffers dropped because of locking *\/ */
void ( * _process_callback ) ( nframes_t, void * );
void *_process_callback_user_data;
void ( * _buffer_size_callback ) ( nframes_t, void * );
void *_buffer_size_callback_user_data;
int ( * _sample_rate_changed_callback ) ( nframes_t, void * );
void *_sample_rate_changed_callback_user_data;
void ( * _port_connect_callback ) ( jack_port_id_t a, jack_port_id_t b, int connect, void * );
void *_port_connect_callback_user_data;
int sample_rate_changed ( nframes_t srate ); int sample_rate_changed ( nframes_t srate );
void shutdown ( void ); void shutdown ( void );
int process ( nframes_t nframes ); int process ( nframes_t nframes );
@ -54,11 +49,19 @@ class Engine : public JACK::Client, public Mutex
int buffer_size ( nframes_t nframes ); int buffer_size ( nframes_t nframes );
void thread_init ( void ); void thread_init ( void );
void port_connect ( jack_port_id_t a, jack_port_id_t b, int connect ); void port_connect ( jack_port_id_t a, jack_port_id_t b, int connect );
Engine ( const Engine &rhs );
Engine & operator = ( const Engine &rhs ); /* not allowed */
Group ( const Group &rhs );
Group & operator = ( const Group &rhs );
void request_locate ( nframes_t frame ); void request_locate ( nframes_t frame );
protected:
virtual void get ( Log_Entry &e ) const;
virtual void set ( Log_Entry &e );
private: private:
friend class Port; friend class Port;
@ -66,11 +69,28 @@ private:
public: public:
Engine ( void (*process_callback) (nframes_t, void *), void *user_data ); LOG_CREATE_FUNC( Group );
~Engine ( );
int nstrips ( void ) const { return strips.size(); }
int dropped ( void ) const { return _buffers_dropped; } int dropped ( void ) const { return _buffers_dropped; }
void buffer_size_callback ( void ( *buffer_size_callback ) ( nframes_t, void * ), void *user_data );
void sample_rate_changed_callback ( int ( *sample_rate_changed_callback ) ( nframes_t, void * ), void *user_data ); Group ( );
void port_connect_callback ( void ( *port_connect_callback ) ( jack_port_id_t a, jack_port_id_t b, int connect, void *arg), void *user_data ); Group ( const char * name, bool single );
virtual ~Group ( );
bool single ( void ) const { return _single; }
const char * name ( void ) const { return _name; }
void name ( const char *n );
std::list<Mixer_Strip*> strips;
/* static void process ( nframes_t nframes, void *v ); */
/* void process ( nframes_t nframes ); */
void add (Mixer_Strip*);
void remove (Mixer_Strip*);
/* Engine *engine ( void ) { return _engine; } */
}; };

View File

@ -28,7 +28,6 @@
#include "dsp.h" #include "dsp.h"
#include "Engine/Engine.H"
#include "Chain.H" #include "Chain.H"
#include "JACK_Module.H" #include "JACK_Module.H"
@ -46,6 +45,9 @@ static Fl_PNG_Image *output_connector_image = NULL;
extern char *instance_name; extern char *instance_name;
#include "Mixer.H"
#include "Group.H"
static JACK_Module *receptive_to_drop = NULL; static JACK_Module *receptive_to_drop = NULL;
@ -197,18 +199,18 @@ JACK_Module::draw ( void )
} }
static std::list<std::string> static std::list<std::string>
get_connections_for_ports ( std::vector<JACK::Port> ports ) get_connections_for_ports ( std::vector<Module::Port> ports )
{ {
std::list<std::string> names; std::list<std::string> names;
for ( unsigned int i = 0; i < ports.size(); ++i ) for ( unsigned int i = 0; i < ports.size(); ++i )
{ {
const char **connections = ports[i].connections(); const char **connections = ports[i].jack_port()->connections();
if ( ! connections ) if ( ! connections )
return names; return names;
bool is_output = ports[i].direction() == JACK::Port::Output; bool is_output = ports[i].jack_port()->direction() == JACK::Port::Output;
for ( const char **c = connections; *c; c++ ) for ( const char **c = connections; *c; c++ )
{ {
@ -225,6 +227,15 @@ get_connections_for_ports ( std::vector<JACK::Port> ports )
strip_name = s; strip_name = s;
} }
else else
if ( 2 == sscanf( *c, "Non-Mixer.%a[^:(] (%a[^:)]):", &client_id, &strip_name ) )
{
free( client_id );
char *s = NULL;
asprintf( &s, "%s%s", is_output ? "@r" : "", strip_name );
free( strip_name );
strip_name = s;
}
else
if ( 2 == sscanf( *c, "Non-Timeline.%a[^:/]:%a[^/]/", &client_id, &strip_name ) ) if ( 2 == sscanf( *c, "Non-Timeline.%a[^:/]:%a[^/]/", &client_id, &strip_name ) )
{ {
free( client_id ); free( client_id );
@ -281,8 +292,8 @@ get_connections_for_ports ( std::vector<JACK::Port> ports )
void void
JACK_Module::update_connection_status ( void ) JACK_Module::update_connection_status ( void )
{ {
std::list<std::string> output_names = get_connections_for_ports( jack_output ); std::list<std::string> output_names = get_connections_for_ports( aux_audio_output );
std::list<std::string> input_names = get_connections_for_ports( jack_input ); std::list<std::string> input_names = get_connections_for_ports( aux_audio_input );
connection_display->clear(); connection_display->clear();
@ -349,41 +360,15 @@ JACK_Module::can_support_inputs ( int )
void void
JACK_Module::remove_jack_outputs ( void ) JACK_Module::remove_aux_audio_outputs ( void )
{ {
for ( unsigned int i = jack_output.size(); i--; ) for ( unsigned int i = aux_audio_output.size(); i--; )
{ {
jack_output.back().shutdown(); aux_audio_output.back().jack_port()->shutdown();
jack_output.pop_back(); aux_audio_output.pop_back();
} }
} }
bool
JACK_Module::add_jack_output ( const char *prefix, int n )
{
JACK::Port *po = NULL;
if ( !prefix )
po = new JACK::Port( chain()->engine(), JACK::Port::Output, JACK::Port::Audio, n );
else
po = new JACK::Port( chain()->engine(), JACK::Port::Output, JACK::Port::Audio, prefix, n );
if ( ! po->activate() )
{
jack_port_activation_error( po );
return false;
}
if ( po->valid() )
{
jack_output.push_back( *po );
}
delete po;
return true;
}
bool bool
JACK_Module::configure_inputs ( int n ) JACK_Module::configure_inputs ( int n )
{ {
@ -395,33 +380,16 @@ JACK_Module::configure_inputs ( int n )
output_connection_handle->show(); output_connection_handle->show();
} }
int on = audio_input.size(); int on = audio_input.size();
if ( n > on ) if ( n > on )
{ {
for ( int i = on; i < n; ++i ) for ( int i = on; i < n; ++i )
{ {
JACK::Port *po = NULL; if ( add_aux_audio_output(_prefix, i ) )
if ( !_prefix )
po = new JACK::Port( chain()->engine(), JACK::Port::Output, JACK::Port::Audio, i );
else
po = new JACK::Port( chain()->engine(), JACK::Port::Output, JACK::Port::Audio, _prefix, i );
if ( ! po->activate() )
{
jack_port_activation_error( po );
return false;
}
if ( po->valid() )
{ {
add_port( Port( this, Port::INPUT, Port::AUDIO ) ); add_port( Port( this, Port::INPUT, Port::AUDIO ) );
jack_output.push_back( *po );
} }
delete po;
} }
} }
else else
@ -430,13 +398,14 @@ JACK_Module::configure_inputs ( int n )
{ {
audio_input.back().disconnect(); audio_input.back().disconnect();
audio_input.pop_back(); audio_input.pop_back();
jack_output.back().shutdown(); aux_audio_output.back().jack_port()->shutdown();
jack_output.pop_back(); delete aux_audio_output.back().jack_port();
aux_audio_output.pop_back();
} }
} }
_connection_handle_outputs[0][0] = 0; _connection_handle_outputs[0][0] = 0;
_connection_handle_outputs[0][1] = jack_output.size(); _connection_handle_outputs[0][1] = aux_audio_output.size();
if ( is_default() ) if ( is_default() )
control_input[0].control_value_no_callback( n ); control_input[0].control_value_no_callback( n );
@ -444,12 +413,6 @@ JACK_Module::configure_inputs ( int n )
return true; return true;
} }
void
JACK_Module::jack_port_activation_error ( JACK::Port *p )
{
fl_alert( "Could not activate JACK port \"%s\"", p->name() );
}
bool bool
JACK_Module::configure_outputs ( int n ) JACK_Module::configure_outputs ( int n )
{ {
@ -464,26 +427,10 @@ JACK_Module::configure_outputs ( int n )
{ {
for ( int i = on; i < n; ++i ) for ( int i = on; i < n; ++i )
{ {
JACK::Port *po = NULL; if ( add_aux_audio_input(_prefix, i ) )
if ( !_prefix )
po = new JACK::Port( chain()->engine(), JACK::Port::Input, JACK::Port::Audio, i );
else
po = new JACK::Port( chain()->engine(), JACK::Port::Input, JACK::Port::Audio, _prefix, i );
if ( ! po->activate() )
{
jack_port_activation_error( po );
return false;
}
if ( po->valid() )
{ {
add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); add_port( Port( this, Port::OUTPUT, Port::AUDIO ) );
jack_input.push_back( *po );
} }
delete po;
} }
} }
else else
@ -492,8 +439,9 @@ JACK_Module::configure_outputs ( int n )
{ {
audio_output.back().disconnect(); audio_output.back().disconnect();
audio_output.pop_back(); audio_output.pop_back();
jack_input.back().shutdown(); aux_audio_input.back().jack_port()->shutdown();
jack_input.pop_back(); delete aux_audio_input.back().jack_port();
aux_audio_input.pop_back();
} }
} }
@ -582,10 +530,11 @@ JACK_Module::handle ( int m )
s[0] = 0; s[0] = 0;
for ( unsigned int i = _connection_handle_outputs[connection_handle][0]; for ( unsigned int i = _connection_handle_outputs[connection_handle][0];
i < jack_output.size() && i < _connection_handle_outputs[connection_handle][1]; ++i ) i < aux_audio_output.size() && i < _connection_handle_outputs[connection_handle][1]; ++i )
{ {
char *s2; char *s2;
asprintf(&s2, "jack.port://%s/%s:%s\r\n", instance_name, chain()->name(), jack_output[i].name() ); asprintf(&s2, "jack.port://%s\r\n",
aux_audio_output[i].jack_port()->jack_name() );
s = (char*)realloc( s, strlen( s ) + strlen( s2 ) + 1 ); s = (char*)realloc( s, strlen( s ) + strlen( s2 ) + 1 );
strcat( s, s2 ); strcat( s, s2 );
@ -633,7 +582,7 @@ JACK_Module::handle ( int m )
if ( this == receptive_to_drop ) if ( this == receptive_to_drop )
return 1; return 1;
if ( jack_input.size() ) if ( aux_audio_input.size() )
{ {
receptive_to_drop = this; receptive_to_drop = this;
@ -674,11 +623,11 @@ JACK_Module::handle ( int m )
text += end; text += end;
} }
for ( unsigned int i = 0; i < jack_input.size() && i < port_names.size(); i++) for ( unsigned int i = 0; i < aux_audio_input.size() && i < port_names.size(); i++)
{ {
const char *pn = port_names[i].c_str(); const char *pn = port_names[i].c_str();
JACK::Port *ji = &jack_input[i]; JACK::Port *ji = aux_audio_input[i].jack_port();
if ( ji->connected_to( pn ) ) if ( ji->connected_to( pn ) )
{ {
@ -712,17 +661,21 @@ JACK_Module::process ( nframes_t nframes )
for ( unsigned int i = 0; i < audio_input.size(); ++i ) for ( unsigned int i = 0; i < audio_input.size(); ++i )
{ {
if ( audio_input[i].connected() ) if ( audio_input[i].connected() )
buffer_copy( (sample_t*)jack_output[i].buffer( nframes ), {
buffer_copy( (sample_t*)aux_audio_output[i].jack_port()->buffer(nframes),
(sample_t*)audio_input[i].buffer(), (sample_t*)audio_input[i].buffer(),
nframes ); nframes );
}
} }
for ( unsigned int i = 0; i < audio_output.size(); ++i ) for ( unsigned int i = 0; i < audio_output.size(); ++i )
{ {
if ( audio_output[i].connected() ) if ( audio_output[i].connected() )
{
buffer_copy( (sample_t*)audio_output[i].buffer(), buffer_copy( (sample_t*)audio_output[i].buffer(),
(sample_t*)jack_input[i].buffer( nframes ), (sample_t*)aux_audio_input[i].jack_port()->buffer(nframes),
nframes ); nframes );
} }
} }
}

View File

@ -42,11 +42,6 @@ protected:
_prefix = strdup( s ); _prefix = strdup( s );
} }
std::vector<JACK::Port> jack_input;
std::vector<JACK::Port> jack_output;
static void jack_port_activation_error ( JACK::Port *p );
Fl_Button * dec_button; Fl_Button * dec_button;
Fl_Button * inc_button; Fl_Button * inc_button;
Fl_Browser * connection_display; Fl_Browser * connection_display;
@ -76,8 +71,7 @@ public:
virtual int handle ( int m ); virtual int handle ( int m );
virtual int can_support_inputs ( int ); virtual int can_support_inputs ( int );
bool add_jack_output ( const char *prefix, int n ); void remove_aux_audio_outputs ( void );
void remove_jack_outputs ( void );
virtual bool configure_inputs ( int n ); virtual bool configure_inputs ( int n );
virtual bool configure_outputs ( int n ); virtual bool configure_outputs ( int n );

View File

@ -32,7 +32,6 @@
#include "FL/Fl_Labelpad_Group.H" #include "FL/Fl_Labelpad_Group.H"
#include "FL/Fl_Scalepack.H" #include "FL/Fl_Scalepack.H"
#include "Engine/Engine.H"
#include "Chain.H" #include "Chain.H"
#include "DPM.H" #include "DPM.H"

View File

@ -29,7 +29,6 @@
#include <FL/fl_ask.H> #include <FL/fl_ask.H>
#include <FL/Fl.H> #include <FL/Fl.H>
#include <FL/New_Project_Dialog.H> #include <FL/New_Project_Dialog.H>
#include "Engine/Engine.H"
#include <FL/Fl_Flowpack.H> #include <FL/Fl_Flowpack.H>
#include "Project.H" #include "Project.H"
#include <FL/Fl_Menu_Settings.H> #include <FL/Fl_Menu_Settings.H>
@ -39,7 +38,7 @@
#include <FL/Fl_Value_SliderX.H> #include <FL/Fl_Value_SliderX.H>
#include <Spatialization_Console.H> #include <Spatialization_Console.H>
#include "file.h" #include "file.h"
#include "Group.H"
#include <string.h> #include <string.h>
#include "debug.h" #include "debug.h"
#include <unistd.h> #include <unistd.h>
@ -60,6 +59,7 @@ extern char *instance_name;
#include "NSM.H" #include "NSM.H"
#include <FL/Fl_Tooltip.H> #include <FL/Fl_Tooltip.H>
#include "Chain.H"
extern NSM_Client *nsm; extern NSM_Client *nsm;
@ -499,6 +499,8 @@ Mixer::Mixer ( int X, int Y, int W, int H, const char *L ) :
/* Fl_Tooltip::color( fl_color_add_alpha( FL_DARK1, 0 ) ); */ /* Fl_Tooltip::color( fl_color_add_alpha( FL_DARK1, 0 ) ); */
// fl_tooltip_docked = 1; // fl_tooltip_docked = 1;
// _groups.resize(16);
_rows = 1; _rows = 1;
_strip_height = 0; _strip_height = 0;
box( FL_FLAT_BOX ); box( FL_FLAT_BOX );
@ -713,6 +715,24 @@ Mixer::~Mixer ( )
mixer_strips->clear(); mixer_strips->clear();
} }
void
Mixer::add_group ( Group *g )
{
groups.push_back( g );
for ( int i = mixer_strips->children(); i--; )
((Mixer_Strip*)mixer_strips->child(i))->update_group_choice();
}
void
Mixer::remove_group ( Group *g )
{
groups.remove(g);
for ( int i = mixer_strips->children(); i--; )
((Mixer_Strip*)mixer_strips->child(i))->update_group_choice();
}
void Mixer::resize ( int X, int Y, int W, int H ) void Mixer::resize ( int X, int Y, int W, int H )
{ {
Fl_Group::resize( X, Y, W, H ); Fl_Group::resize( X, Y, W, H );
@ -755,7 +775,6 @@ Mixer::insert ( Mixer_Strip *ms, Mixer_Strip *before )
{ {
// mixer_strips->remove( ms ); // mixer_strips->remove( ms );
mixer_strips->insert( *ms, before ); mixer_strips->insert( *ms, before );
// scroll->redraw(); // scroll->redraw();
} }
void void
@ -796,6 +815,8 @@ void Mixer::remove ( Mixer_Strip *ms )
mixer_strips->remove( ms ); mixer_strips->remove( ms );
ms->group()->remove( ms );
if ( parent() ) if ( parent() )
parent()->redraw(); parent()->redraw();
} }
@ -821,7 +842,7 @@ Mixer::contains ( Mixer_Strip *ms )
void void
Mixer::rows ( int ideal_rows ) Mixer::rows ( int ideal_rows )
{ {
int sh; int sh = 0;
int actual_rows = 1; int actual_rows = 1;
@ -919,6 +940,31 @@ Mixer::get_unique_track_name ( const char *name )
return strdup( pat ); return strdup( pat );
} }
Group *
Mixer::group_by_name ( const char *name )
{
for ( std::list<Group*>::iterator i = groups.begin();
i != groups.end();
i++ )
if ( !strcmp( (*i)->name(), name ))
return *i;
return NULL;
}
char *
Mixer::get_unique_group_name ( const char *name )
{
char pat[256];
strcpy( pat, name );
for ( int i = 1; group_by_name( pat ); ++i )
snprintf( pat, sizeof( pat ), "%s.%d", name, i );
return strdup( pat );
}
void void
Mixer::handle_dirty ( int d, void *v ) Mixer::handle_dirty ( int d, void *v )
{ {
@ -939,6 +985,9 @@ Mixer::snapshot ( void )
if ( spatialization_console ) if ( spatialization_console )
spatialization_console->log_create(); spatialization_console->log_create();
for ( std::list<Group*>::iterator i = groups.begin(); i != groups.end(); ++i )
(*i)->log_create();
for ( int i = 0; i < mixer_strips->children(); ++i ) for ( int i = 0; i < mixer_strips->children(); ++i )
((Mixer_Strip*)mixer_strips->child( i ))->log_children(); ((Mixer_Strip*)mixer_strips->child( i ))->log_children();
} }
@ -1086,6 +1135,8 @@ Mixer::command_load ( const char *path, const char *display_name )
load_project_settings(); load_project_settings();
Project::close();
if ( Project::open( path ) ) if ( Project::open( path ) )
{ {
// fl_alert( "Error opening project specified on commandline: %s", Project::errstr( err ) ); // fl_alert( "Error opening project specified on commandline: %s", Project::errstr( err ) );

View File

@ -34,6 +34,7 @@ class Fl_Menu_Bar;
class Spatialization_Console; class Spatialization_Console;
namespace OSC { class Endpoint; } namespace OSC { class Endpoint; }
#include <lo/lo.h> #include <lo/lo.h>
class Group;
class Mixer : public Fl_Group class Mixer : public Fl_Group
{ {
@ -88,8 +89,17 @@ private:
static void update_cb ( void * ); static void update_cb ( void * );
void update_cb ( void ); void update_cb ( void );
public: public:
Group * group_by_name ( const char * name );
char *get_unique_group_name ( const char *name );
std::list <Group*> groups;
Group *group ( int n );
void add_group ( Group *g );
void remove_group ( Group *g );
void update_menu ( void ); void update_menu ( void );
static Spatialization_Console *spatialization_console; static Spatialization_Console *spatialization_console;
@ -114,7 +124,6 @@ public:
virtual void resize ( int X, int Y, int W, int H ); virtual void resize ( int X, int Y, int W, int H );
void new_strip ( void ); void new_strip ( void );
void process ( unsigned int nframes );
void add ( Mixer_Strip *ms ); void add ( Mixer_Strip *ms );
void remove ( Mixer_Strip *ms ); void remove ( Mixer_Strip *ms );
void move_left ( Mixer_Strip *ms ); void move_left ( Mixer_Strip *ms );

View File

@ -34,7 +34,6 @@
#include "Project.H" #include "Project.H"
#include "Mixer_Strip.H" #include "Mixer_Strip.H"
#include "Engine/Engine.H"
#include <dsp.h> #include <dsp.h>
#include <string.h> #include <string.h>
#include "debug.h" #include "debug.h"
@ -58,6 +57,8 @@
#include "FL/test_press.H" #include "FL/test_press.H"
#include "FL/menu_popup.H" #include "FL/menu_popup.H"
#include <FL/Fl_File_Chooser.H> #include <FL/Fl_File_Chooser.H>
#include <FL/Fl_Choice.H>
#include "Group.H"
extern Mixer *mixer; extern Mixer *mixer;
@ -71,6 +72,8 @@ Mixer_Strip::Mixer_Strip( const char *strip_name ) : Fl_Group( 0, 0, 120, 600 )
init(); init();
_group = new Group(strip_name, true);
chain( new Chain() ); chain( new Chain() );
_chain->initialize_with_default(); _chain->initialize_with_default();
@ -96,17 +99,17 @@ Mixer_Strip::~Mixer_Strip ( )
{ {
DMESSAGE( "Destroying mixer strip" ); DMESSAGE( "Destroying mixer strip" );
_chain->engine()->lock(); // _chain->engine()->lock();
log_destroy();
mixer->remove( this );
/* make sure this gets destroyed before the chain */ /* make sure this gets destroyed before the chain */
fader_tab->clear(); fader_tab->clear();
delete _chain; delete _chain;
_chain = NULL; _chain = NULL;
log_destroy();
mixer->remove( this );
} }
@ -121,6 +124,11 @@ Mixer_Strip::get ( Log_Entry &e ) const
/* since the default controllers aren't logged, we have to store /* since the default controllers aren't logged, we have to store
* this setting as part of the mixer strip */ * this setting as part of the mixer strip */
e.add( ":gain_mode", gain_controller->mode() ); e.add( ":gain_mode", gain_controller->mode() );
if ( ! _group->single() )
e.add( ":group", _group );
else
e.add( ":group", (Loggable*)0 );
} }
@ -154,7 +162,26 @@ Mixer_Strip::set ( Log_Entry &e )
{ {
_gain_controller_mode = atoi( v ); _gain_controller_mode = atoi( v );
} }
else if ( ! strcmp( s, ":group" ) )
{
int i;
sscanf( v, "%X", &i );
if ( i )
{
Group *t = (Group*)Loggable::find( i );
assert( t );
group( t );
} }
else
group( 0 );
}
}
if ( ! _group )
group(0);
if ( ! mixer->contains( this ) ) if ( ! mixer->contains( this ) )
mixer->add( this ); mixer->add( this );
@ -204,6 +231,8 @@ Mixer_Strip::chain ( Chain *c )
c->configure_outputs_callback( configure_outputs, this ); c->configure_outputs_callback( configure_outputs, this );
c->name( name() ); c->name( name() );
/* FIXME: don't hardcode this list of modules */
spatialization_controller->chain( c );
gain_controller->chain( c ); gain_controller->chain( c );
jack_input_controller->chain( c ); jack_input_controller->chain( c );
meter_indicator->chain( c ); meter_indicator->chain( c );
@ -253,12 +282,76 @@ void Mixer_Strip::cb_handle(Fl_Widget* o) {
if ( parent() ) if ( parent() )
parent()->parent()->redraw(); parent()->parent()->redraw();
} }
else if ( o == group_choice )
{
// group(group_choice->value());
Group *g = NULL;
if ( group_choice->value() == group_choice->size() - 2 )
{
/* create a new group */
const char *s = fl_input( "Name for Group:" );
if ( !s )
return;
char *n = mixer->get_unique_group_name( s );
g = new Group( n, false );
free( n );
mixer->add_group( g );
}
else
{
g = (Group*)group_choice->mvalue()->user_data();
}
group(g);
}
} }
void Mixer_Strip::cb_handle(Fl_Widget* o, void* v) { void Mixer_Strip::cb_handle(Fl_Widget* o, void* v) {
((Mixer_Strip*)(v))->cb_handle(o); ((Mixer_Strip*)(v))->cb_handle(o);
} }
void
Mixer_Strip::group ( Group *g )
{
if ( !g && _group && _group->single() )
return;
if ( _group )
{
_group->remove(this);
if ( ! _group->nstrips() )
{
if ( ! _group->single() )
mixer->remove_group( _group );
delete _group;
_group = NULL;
}
}
if ( ! g )
g = new Group(name(), true);
const Fl_Menu_Item *menu = group_choice->menu();
for ( unsigned int i = 0; menu[i].text; i++ )
if ( menu[i].user_data() == g )
group_choice->value( i );
// group_choice->color( (Fl_Color)n );
// group_choice->value( n );
_group = g;
g->add(this);
}
void void
Mixer_Strip::name ( const char *name ) Mixer_Strip::name ( const char *name )
{ {
@ -375,6 +468,7 @@ Mixer_Strip::init ( )
_gain_controller_mode = 0; _gain_controller_mode = 0;
_chain = 0; _chain = 0;
_group = 0;
box( FL_FLAT_BOX ); box( FL_FLAT_BOX );
labeltype( FL_NO_LABEL ); labeltype( FL_NO_LABEL );
@ -433,6 +527,14 @@ Mixer_Strip::init ( )
o->end(); o->end();
} // Fl_Group* o } // Fl_Group* o
{ Fl_Choice* o = group_choice = new Fl_Choice(61, 183, 45, 22);
o->labeltype(FL_NO_LABEL);
o->labelsize(10);
o->textsize(10);
o->add("---");
o->value(0);
o->callback( ((Fl_Callback*)cb_handle), this );
}
{ Fl_Flip_Button* o = tab_button = new Fl_Flip_Button(61, 183, 45, 22, "fader/signal"); { Fl_Flip_Button* o = tab_button = new Fl_Flip_Button(61, 183, 45, 22, "fader/signal");
o->type(1); o->type(1);
o->labelsize( 14 ); o->labelsize( 14 );
@ -519,11 +621,49 @@ Mixer_Strip::init ( )
size( 96, h() ); size( 96, h() );
update_group_choice();
// redraw(); // redraw();
// _chain->configure_ports(); // _chain->configure_ports();
} }
void
Mixer_Strip::update_group_choice ( void )
{
Fl_Choice *o = group_choice;
o->clear();
o->add( "---" );
for ( std::list<Group*>::iterator i = mixer->groups.begin(); i != mixer->groups.end(); )
{
Group *g = *i;
i++;
if ( i == mixer->groups.end() )
{
o->add( g->name(), 0, 0, (void*)g, FL_MENU_DIVIDER );
break;
}
else
o->add( g->name(), 0, 0, (void*)g );
}
o->add( "New Group" );
const Fl_Menu_Item *menu = o->menu();
if ( ! group() || ( group() && group()->single() ) )
o->value(0);
else
{
for ( unsigned int i = 0; menu[i].text; i++ )
if ( menu[i].user_data() == group() )
o->value( i );
}
}
void void
Mixer_Strip::draw ( void ) Mixer_Strip::draw ( void )
{ {

View File

@ -32,7 +32,6 @@
#include <FL/Fl_Value_Slider.H> #include <FL/Fl_Value_Slider.H>
#include <FL/Fl_Counter.H> #include <FL/Fl_Counter.H>
#include <FL/Fl_Progress.H> #include <FL/Fl_Progress.H>
//#include "Fader.H"
#include <JACK/Port.H> #include <JACK/Port.H>
@ -48,6 +47,8 @@ class Fl_Flip_Button;
class Fl_Input; class Fl_Input;
class Fl_Menu_; class Fl_Menu_;
class Fl_Menu_Button; class Fl_Menu_Button;
class Fl_Choice;
class Group;
class Mixer_Strip : public Fl_Group, public Loggable { class Mixer_Strip : public Fl_Group, public Loggable {
@ -60,6 +61,7 @@ public:
virtual ~Mixer_Strip(); virtual ~Mixer_Strip();
void chain ( Chain *c ); void chain ( Chain *c );
Chain *chain ( void ) { return _chain; }
virtual void log_children ( void ) const; virtual void log_children ( void ) const;
@ -92,6 +94,7 @@ private:
Fl_Flip_Button *tab_button; Fl_Flip_Button *tab_button;
Fl_Button *close_button; Fl_Button *close_button;
Fl_Input *name_field; Fl_Input *name_field;
Fl_Choice *group_choice;
Fl_Flowpack *controls_pack; Fl_Flowpack *controls_pack;
Fl_Group *tab_group; Fl_Group *tab_group;
@ -100,6 +103,7 @@ private:
Fl_Pack *panner_pack; Fl_Pack *panner_pack;
Chain *_chain; Chain *_chain;
Group *_group;
Fl_Box *spatialization_label; Fl_Box *spatialization_label;
Controller_Module *gain_controller; Controller_Module *gain_controller;
@ -141,8 +145,14 @@ protected:
public: public:
void update_group_choice ( void );
Controller_Module *spatializer ( void ); Controller_Module *spatializer ( void );
Group *group ( void ) { return _group;}
// int group ( void ) const;
void group ( Group * );
void send_feedback ( void ); void send_feedback ( void );
int number ( void ) const; int number ( void ) const;
static bool import_strip ( const char *filename ); static bool import_strip ( const char *filename );

View File

@ -73,7 +73,7 @@ Module::Module ( ) : Fl_Group( 0, 0, 50, 50, "Unnamed" )
Module::~Module ( ) Module::~Module ( )
{ {
/* we assume that the engine for this chain is already locked */ /* we assume that the client for this chain is already locked */
if ( _editor ) if ( _editor )
{ {
@ -880,6 +880,21 @@ Module::handle_chain_name_changed ( )
control_input[i].update_osc_port(); control_input[i].update_osc_port();
} }
if ( ! chain()->strip()->group()->single() )
{
/* we have to rename our JACK ports... */
for ( unsigned int i = 0; i < aux_audio_input.size(); i++ )
{
aux_audio_input[i].jack_port()->trackname( chain()->name() );
aux_audio_input[i].jack_port()->rename();
}
for ( unsigned int i = 0; i < aux_audio_output.size(); i++ )
{
aux_audio_output[i].jack_port()->trackname( chain()->name() );
aux_audio_output[i].jack_port()->rename();
}
}
} }
int int
@ -964,6 +979,148 @@ Module::handle ( int m )
return 0; return 0;
} }
/*************/
/* AUX Ports */
/*************/
static char *
generate_port_name ( const char *aux, int direction, int n )
{
char *s;
asprintf( &s, "%s%s%s-%i",
aux ? aux : "",
aux ? "/" : "",
direction == JACK::Port::Input ? "in" : "out",
n + 1 );
return s;
}
static void
jack_port_activation_error ( JACK::Port *p )
{
fl_alert( "Could not activate JACK port \"%s\"", p->name() );
}
/* freeze/disconnect all jack ports--used when changing groups */
void
Module::freeze_ports ( void )
{
// pass it along to our connected Controller_Modules, if any.
for ( int i = 0; i < ncontrol_inputs(); ++i )
{
if ( control_input[i].connected() )
control_input[i].connected_port()->module()->freeze_ports();
}
for ( unsigned int i = 0; i < aux_audio_input.size(); ++i )
{
aux_audio_input[i].jack_port()->freeze();
aux_audio_input[i].jack_port()->shutdown();
}
for ( unsigned int i = 0; i < aux_audio_output.size(); ++i )
{
aux_audio_output[i].jack_port()->freeze();
aux_audio_output[i].jack_port()->shutdown();
}
}
/* rename and thaw all jack ports--used when changing groups */
void
Module::thaw_ports ( void )
{
// pass it along to our connected Controller_Modules, if any.
for ( int i = 0; i < ncontrol_inputs(); ++i )
{
if ( control_input[i].connected() )
control_input[i].connected_port()->module()->thaw_ports();
}
const char *trackname = chain()->strip()->group()->single() ? NULL : chain()->name();
for ( unsigned int i = 0; i < aux_audio_input.size(); ++i )
{
/* if we're entering a group we need to add the chain name
* prefix and if we're leaving one, we need to remove it */
aux_audio_input[i].jack_port()->client( chain()->client() );
aux_audio_input[i].jack_port()->trackname( trackname );
aux_audio_input[i].jack_port()->thaw();
}
for ( unsigned int i = 0; i < aux_audio_output.size(); ++i )
{
/* if we're entering a group we won't actually be using our
* JACK output ports anymore, just mixing into the group outputs */
aux_audio_output[i].jack_port()->client( chain()->client() );
aux_audio_output[i].jack_port()->trackname( trackname );
aux_audio_output[i].jack_port()->thaw();
}
}
bool
Module::add_aux_port ( bool input, const char *prefix, int i )
{
const char *trackname = chain()->strip()->group()->single() ? NULL : chain()->name();
JACK::Port::direction_e direction = input ? JACK::Port::Input : JACK::Port::Output;
char *portname = generate_port_name( prefix, direction, i );
JACK::Port *po = new JACK::Port( chain()->client(), trackname, portname, direction, JACK::Port::Audio );
free(portname);
if ( ! po->activate() )
{
jack_port_activation_error( po );
return false;
}
if ( po->valid() )
{
if ( input )
{
Module::Port mp( (Module*)this, Module::Port::INPUT, Module::Port::AUX_AUDIO );
mp.jack_port( po );
aux_audio_input.push_back( mp );
}
else
{
Module::Port mp( (Module*)this, Module::Port::OUTPUT, Module::Port::AUX_AUDIO );
mp.jack_port( po );
aux_audio_output.push_back( mp );
}
}
else
{
delete po;
return false;
}
return true;
}
bool
Module::add_aux_audio_output( const char *prefix, int i )
{
return add_aux_port ( false, prefix, i );
}
bool
Module::add_aux_audio_input( const char *prefix, int i )
{
return add_aux_port ( true, prefix, i );
}
/************/ /************/
/* Commands */ /* Commands */

View File

@ -91,7 +91,7 @@ public:
public: public:
enum Direction { INPUT, OUTPUT }; enum Direction { INPUT, OUTPUT };
enum Type { AUDIO, CONTROL }; enum Type { AUDIO, CONTROL, AUX_AUDIO };
/* hints for control ports (specifically control inputs) */ /* hints for control ports (specifically control inputs) */
struct Hints struct Hints
@ -138,6 +138,7 @@ public:
_unscaled_signal = 0; _unscaled_signal = 0;
_by_number_path = 0; _by_number_path = 0;
_by_number_number = -1; _by_number_number = -1;
_jack_port = 0;
} }
Port ( const Port& p ) Port ( const Port& p )
@ -154,6 +155,7 @@ public:
_unscaled_signal = p._unscaled_signal; _unscaled_signal = p._unscaled_signal;
_by_number_path = 0; _by_number_path = 0;
_by_number_number = -1; _by_number_number = -1;
_jack_port = p._jack_port;
} }
virtual ~Port ( ) virtual ~Port ( )
@ -273,6 +275,9 @@ public:
/* FIXME: do something! */ /* FIXME: do something! */
} }
void jack_port ( JACK::Port *v ) { _jack_port = v; }
JACK::Port *jack_port ( void ) { return _jack_port; }
private: private:
char *generate_osc_path ( void ); char *generate_osc_path ( void );
@ -285,7 +290,8 @@ public:
void *_buf; void *_buf;
nframes_t _nframes; nframes_t _nframes;
Module *_module; Module *_module;
/* used for auxilliary I/Os */
JACK::Port *_jack_port;
OSC::Signal *_scaled_signal; OSC::Signal *_scaled_signal;
OSC::Signal *_unscaled_signal; OSC::Signal *_unscaled_signal;
@ -348,6 +354,8 @@ public:
std::vector<Port> audio_output; std::vector<Port> audio_output;
std::vector<Port> control_input; std::vector<Port> control_input;
std::vector<Port> control_output; std::vector<Port> control_output;
std::vector<Port> aux_audio_input;
std::vector<Port> aux_audio_output;
void add_port ( const Port &p ) void add_port ( const Port &p )
{ {
@ -477,8 +485,16 @@ protected:
virtual void get ( Log_Entry &e ) const; virtual void get ( Log_Entry &e ) const;
virtual void set ( Log_Entry &e ); virtual void set ( Log_Entry &e );
bool add_aux_port ( bool input, const char *prefix, int n );
public: public:
void freeze_ports ( void );
void thaw_ports ( void );
bool add_aux_audio_output ( const char *prefix, int n );
bool add_aux_audio_input ( const char *prefix, int n );
static void set_sample_rate ( nframes_t srate ) { _sample_rate = srate; } static void set_sample_rate ( nframes_t srate ) { _sample_rate = srate; }
void command_open_parameter_editor(); void command_open_parameter_editor();

View File

@ -41,7 +41,7 @@
#include "LADSPAInfo.h" #include "LADSPAInfo.h"
#include "Chain.H" #include "Chain.H"
#include "Engine/Engine.H" //#include "Client/Client.H"
#include <dsp.h> #include <dsp.h>
@ -684,7 +684,7 @@ Plugin_Module::activate ( void )
FATAL( "Attempt to activate already active plugin" ); FATAL( "Attempt to activate already active plugin" );
if ( chain() ) if ( chain() )
chain()->engine()->lock(); chain()->client()->lock();
if ( _idata->descriptor->activate ) if ( _idata->descriptor->activate )
for ( unsigned int i = 0; i < _idata->handle.size(); ++i ) for ( unsigned int i = 0; i < _idata->handle.size(); ++i )
@ -693,7 +693,7 @@ Plugin_Module::activate ( void )
_bypass = false; _bypass = false;
if ( chain() ) if ( chain() )
chain()->engine()->unlock(); chain()->client()->unlock();
} }
void void
@ -705,7 +705,7 @@ Plugin_Module::deactivate( void )
DMESSAGE( "Deactivating plugin \"%s\"", label() ); DMESSAGE( "Deactivating plugin \"%s\"", label() );
if ( chain() ) if ( chain() )
chain()->engine()->lock(); chain()->client()->lock();
_bypass = true; _bypass = true;
@ -714,7 +714,7 @@ Plugin_Module::deactivate( void )
_idata->descriptor->deactivate( _idata->handle[i] ); _idata->descriptor->deactivate( _idata->handle[i] );
if ( chain() ) if ( chain() )
chain()->engine()->unlock(); chain()->client()->unlock();
} }
void void
@ -743,7 +743,7 @@ Plugin_Module::handle_port_connection_change ( void )
/**********/ /**********/
/* Engine */ /* Client */
/**********/ /**********/
void void

View File

@ -512,7 +512,7 @@ Spatializer_Module::draw ( void )
int spacing, offset; int spacing, offset;
int ni = jack_output.size(); int ni = aux_audio_output.size();
spacing = h() / ni; spacing = h() / ni;
offset = spacing / 2; offset = spacing / 2;
@ -566,9 +566,9 @@ Spatializer_Module::process ( nframes_t nframes )
/* send to late reverb */ /* send to late reverb */
if ( i == 0 ) if ( i == 0 )
buffer_copy( (sample_t*)jack_output[0].buffer(nframes), buf, nframes ); buffer_copy( (sample_t*)aux_audio_output[0].jack_port()->buffer(nframes), buf, nframes );
else else
buffer_mix( (sample_t*)jack_output[0].buffer(nframes), buf, nframes ); buffer_mix( (sample_t*)aux_audio_output[0].jack_port()->buffer(nframes), buf, nframes );
/* /\* FIXME: use smoothed value... *\/ */ /* /\* FIXME: use smoothed value... *\/ */
/* buffer_apply_gain( (sample_t*)jack_output[0].buffer(nframes), nframes, 1.0f / sqrt(D) ); */ /* buffer_apply_gain( (sample_t*)jack_output[0].buffer(nframes), nframes, 1.0f / sqrt(D) ); */
@ -606,7 +606,7 @@ Spatializer_Module::process ( nframes_t nframes )
/* send to early reverb */ /* send to early reverb */
for ( int i = 4; i--; ) for ( int i = 4; i--; )
buffer_copy( (sample_t*)jack_output[1 + i].buffer(nframes), buffer_copy( (sample_t*)aux_audio_output[1 + i].jack_port()->buffer(nframes),
(sample_t*)audio_output[0 + i].buffer(), (sample_t*)audio_output[0 + i].buffer(),
nframes ); nframes );
@ -678,7 +678,7 @@ Spatializer_Module::configure_inputs ( int n )
if ( n == 0 ) if ( n == 0 )
{ {
remove_jack_outputs(); remove_aux_audio_outputs();
audio_output.clear(); audio_output.clear();
audio_input.clear(); audio_input.clear();
} }
@ -692,20 +692,20 @@ Spatializer_Module::configure_inputs ( int n )
} }
} }
if ( jack_output.size() != 5 ) if ( aux_audio_output.size() != 5 )
{ {
add_jack_output( "late reverb", 0 ); add_aux_audio_output( "late reverb", 0 );
add_jack_output( "early reverb", 0 ); add_aux_audio_output( "early reverb", 0 );
add_jack_output( "early reverb", 1 ); add_aux_audio_output( "early reverb", 1 );
add_jack_output( "early reverb", 2 ); add_aux_audio_output( "early reverb", 2 );
add_jack_output( "early reverb", 3 ); add_aux_audio_output( "early reverb", 3 );
} }
} }
_connection_handle_outputs[0][0] = 0; _connection_handle_outputs[0][0] = 0;
_connection_handle_outputs[0][1] = 1; _connection_handle_outputs[0][1] = 1;
_connection_handle_outputs[1][0] = 1; _connection_handle_outputs[1][0] = 1;
_connection_handle_outputs[1][1] = jack_output.size(); _connection_handle_outputs[1][1] = aux_audio_output.size();
return true; return true;
} }

View File

@ -55,6 +55,7 @@
#include "AUX_Module.H" #include "AUX_Module.H"
#include "NSM.H" #include "NSM.H"
#include "Spatialization_Console.H" #include "Spatialization_Console.H"
#include "Group.H"
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
@ -161,6 +162,7 @@ main ( int argc, char **argv )
LOG_REGISTER_CREATE( Controller_Module ); LOG_REGISTER_CREATE( Controller_Module );
LOG_REGISTER_CREATE( AUX_Module ); LOG_REGISTER_CREATE( AUX_Module );
LOG_REGISTER_CREATE( Spatialization_Console ); LOG_REGISTER_CREATE( Spatialization_Console );
LOG_REGISTER_CREATE( Group );
signal( SIGPIPE, SIG_IGN ); signal( SIGPIPE, SIG_IGN );

View File

@ -529,8 +529,8 @@ create_engine ( void )
return false; return false;
} }
engine->midi_input_port = new JACK::Port( engine, "midi-in", JACK::Port::Input, JACK::Port::MIDI ); engine->midi_input_port = new JACK::Port( engine, NULL, "midi-in", JACK::Port::Input, JACK::Port::MIDI );
engine->midi_output_port = new JACK::Port( engine, "midi-out", JACK::Port::Output, JACK::Port::MIDI ); engine->midi_output_port = new JACK::Port( engine, NULL, "midi-out", JACK::Port::Output, JACK::Port::MIDI );
if ( !engine->midi_input_port->activate() ) if ( !engine->midi_input_port->activate() )
{ {

View File

@ -46,7 +46,6 @@ def build(bld):
src/Chain.C src/Chain.C
src/Controller_Module.C src/Controller_Module.C
src/DPM.C src/DPM.C
src/Engine/Engine.C
src/Gain_Module.C src/Gain_Module.C
src/Spatializer_Module.C src/Spatializer_Module.C
src/JACK_Module.C src/JACK_Module.C
@ -65,6 +64,7 @@ src/NSM.C
src/Panner.C src/Panner.C
src/Plugin_Module.C src/Plugin_Module.C
src/Project.C src/Project.C
src/Group.C
src/main.C src/main.C
src/Spatialization_Console.C src/Spatialization_Console.C
''', ''',

View File

@ -35,6 +35,7 @@ namespace JACK
Client::Client ( ) Client::Client ( )
{ {
_active = false;
_freewheeling = false; _freewheeling = false;
_zombified = false; _zombified = false;
_client = NULL; _client = NULL;
@ -43,7 +44,7 @@ namespace JACK
Client::~Client ( ) Client::~Client ( )
{ {
jack_client_close( _client ); close();
} }
/** Tell JACK to stop calling process callback. This MUST be called in /** Tell JACK to stop calling process callback. This MUST be called in
@ -51,7 +52,10 @@ namespace JACK
void void
Client::deactivate ( ) Client::deactivate ( )
{ {
if ( _active )
jack_deactivate( _client ); jack_deactivate( _client );
_active = false;
} }
@ -138,6 +142,14 @@ namespace JACK
return ((Client*)arg)->sample_rate_changed( srate ); return ((Client*)arg)->sample_rate_changed( srate );
} }
void
Client::activate ( void )
{
jack_activate( _client );
_active = true;
}
/** Connect to JACK using client name /client_name/. Return a static /** Connect to JACK using client name /client_name/. Return a static
* pointer to actual name as reported by JACK */ * pointer to actual name as reported by JACK */
const char * const char *
@ -167,15 +179,13 @@ namespace JACK
jack_on_shutdown( _client, &Client::shutdown, this ); jack_on_shutdown( _client, &Client::shutdown, this );
jack_activate( _client ); activate();
// _sample_rate = frame_rate(); // _sample_rate = frame_rate();
return jack_get_client_name( _client ); return jack_get_client_name( _client );
} }
/* THREAD: RT */ /* THREAD: RT */
/** enter or leave freehweeling mode */ /** enter or leave freehweeling mode */
void void
@ -186,6 +196,11 @@ namespace JACK
// WARNING( "Unkown error while setting freewheeling mode" ); // WARNING( "Unkown error while setting freewheeling mode" );
} }
const char *
Client::jack_name ( void ) const
{
return jack_get_client_name( _client );
}
void void
Client::port_added ( Port *p ) Client::port_added ( Port *p )
@ -235,8 +250,13 @@ namespace JACK
void void
Client::close ( void ) Client::close ( void )
{ {
jack_deactivate( _client ); deactivate();
if ( _client )
{
DMESSAGE( "Closing JACK client" );
jack_client_close( _client ); jack_client_close( _client );
}
_client = NULL; _client = NULL;
} }

View File

@ -43,6 +43,7 @@ namespace JACK
volatile int _xruns; volatile int _xruns;
volatile bool _freewheeling; volatile bool _freewheeling;
volatile bool _zombified; volatile bool _zombified;
volatile bool _active;
static int sample_rate_changed ( nframes_t srate, void *arg ); static int sample_rate_changed ( nframes_t srate, void *arg );
virtual int sample_rate_changed ( nframes_t srate ) { return 0; } virtual int sample_rate_changed ( nframes_t srate ) { return 0; }
@ -73,7 +74,9 @@ namespace JACK
protected: protected:
bool active ( void ) const { return _active; }
void deactivate ( void ); void deactivate ( void );
void activate ( void );
private: private:
@ -97,6 +100,8 @@ namespace JACK
const char * init ( const char *client_name, unsigned int opts = 0 ); const char * init ( const char *client_name, unsigned int opts = 0 );
const char * name ( const char * ); const char * name ( const char * );
const char *jack_name ( void ) const;
void close ( void ); void close ( void );
nframes_t nframes ( void ) const { return jack_get_buffer_size( _client ); } nframes_t nframes ( void ) const { return jack_get_buffer_size( _client ); }
// float frame_rate ( void ) const { return jack_get_sample_rate( _client ); } // float frame_rate ( void ) const { return jack_get_sample_rate( _client ); }

View File

@ -31,7 +31,7 @@
namespace JACK namespace JACK
{ {
static char *name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ); /* static char *name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ); */
int int
Port::max_name ( void ) Port::max_name ( void )
@ -49,8 +49,11 @@ namespace JACK
_port = rhs._port; _port = rhs._port;
_direction = rhs._direction; _direction = rhs._direction;
_type = rhs._type; _type = rhs._type;
_name = NULL;
_name = strdup( rhs._name ); _name = strdup( rhs._name );
_trackname = NULL;
if ( rhs._trackname )
_trackname = strdup( rhs._trackname );
_client->port_added( this ); _client->port_added( this );
} }
@ -62,6 +65,7 @@ namespace JACK
_client = client; _client = client;
_port = port; _port = port;
_name = strdup( jack_port_name( port ) ); _name = strdup( jack_port_name( port ) );
_trackname = NULL;
_direction = ( jack_port_flags( _port ) & JackPortIsOutput ) ? Output : Input; _direction = ( jack_port_flags( _port ) & JackPortIsOutput ) ? Output : Input;
const char *type = jack_port_type( _port ); const char *type = jack_port_type( _port );
@ -73,51 +77,55 @@ namespace JACK
} }
Port::Port ( JACK::Client *client, const char *name, direction_e dir, type_e type ) Port::Port ( JACK::Client *client, const char *trackname, const char *name, direction_e dir, type_e type )
{ {
_port = 0; _port = 0;
_terminal = 0; _terminal = 0;
_name = NULL; _name = NULL;
_trackname = NULL;
_connections = NULL; _connections = NULL;
_client = client; _client = client;
_direction = dir; _direction = dir;
_type = type; _type = type;
_trackname = NULL;
if ( trackname )
_trackname = strdup( trackname );
_name = strdup( name ); _name = strdup( name );
_client->port_added( this ); _client->port_added( this );
} }
Port::Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype ) /* Port::Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype ) */
{ /* { */
_port = 0; /* _port = 0; */
_terminal = 0; /* _terminal = 0; */
_name = NULL; /* _name = NULL; */
_connections = NULL; /* _connections = NULL; */
_client = client; /* _client = client; */
_name = name_for_port( dir, base, n, subtype ); /* _name = name_for_port( dir, base, n, subtype ); */
_direction = dir; /* _direction = dir; */
_type = type; /* _type = type; */
_client->port_added( this ); /* _client->port_added( this ); */
} /* } */
Port::Port ( JACK::Client *client, direction_e dir, type_e type, int n, const char *subtype ) /* Port::Port ( JACK::Client *client, direction_e dir, type_e type, int n, const char *subtype ) */
{ /* { */
_port = 0; /* _port = 0; */
_terminal = 0; /* _terminal = 0; */
_name = NULL; /* _name = NULL; */
_connections = NULL; /* _connections = NULL; */
_client = client; /* _client = client; */
_name = name_for_port( dir, NULL, n, subtype ); /* _name = name_for_port( dir, NULL, n, subtype ); */
_direction = dir; /* _direction = dir; */
_type = type; /* _type = type; */
_client->port_added( this ); /* _client->port_added( this ); */
} /* } */
Port::~Port ( ) Port::~Port ( )
{ {
@ -128,6 +136,12 @@ namespace JACK
free( _name ); free( _name );
_name = NULL; _name = NULL;
} }
if ( _trackname )
{
free( _trackname );
_trackname = NULL;
}
} }
/* sort input before output and then by alpha */ /* sort input before output and then by alpha */
@ -141,20 +155,20 @@ namespace JACK
} }
static char * /* static char * */
name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ) /* name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ) */
{ /* { */
char *pname; /* char *pname; */
const char *dir_s = dir == Port::Output ? "out" : "in"; /* const char *dir_s = dir == Port::Output ? "out" : "in"; */
if ( type ) /* if ( type ) */
asprintf( &pname, "%s-%s%s%s-%d", type, base ? base : "", base ? "/" : "", dir_s, n + 1 ); /* asprintf( &pname, "%s-%s%s%s-%d", type, base ? base : "", base ? "/" : "", dir_s, n + 1 ); */
else /* else */
asprintf( &pname, "%s%s%s-%d", base ? base : "", base ? "/" : "", dir_s, n + 1 ); /* asprintf( &pname, "%s%s%s-%d", base ? base : "", base ? "/" : "", dir_s, n + 1 ); */
return pname; /* return pname; */
} /* } */
bool bool
Port::activate ( void ) Port::activate ( void )
@ -171,8 +185,12 @@ namespace JACK
if ( _terminal ) if ( _terminal )
flags |= JackPortIsTerminal; flags |= JackPortIsTerminal;
DMESSAGE( "Activating port name %s", _name ); char jackname[max_name()];
_port = jack_port_register( _client->jack_client(), _name,
snprintf( jackname, sizeof(jackname), "%s%s%s", _trackname ? _trackname : "", _trackname ? "/" : "", _name );
DMESSAGE( "Activating port name %s", jackname );
_port = jack_port_register( _client->jack_client(), jackname,
_type == Audio ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE, _type == Audio ? JACK_DEFAULT_AUDIO_TYPE : JACK_DEFAULT_MIDI_TYPE,
flags, flags,
0 ); 0 );
@ -231,25 +249,39 @@ namespace JACK
_port = 0; _port = 0;
} }
/** rename port */
bool void
Port::name ( const char *name ) Port::name ( const char *name )
{ {
if ( _name ) if ( _name )
free( _name ); free( _name );
_name = strdup( name ); _name = strdup( name );
}
return 0 == jack_port_set_name( _port, name ); void
Port::trackname ( const char *trackname )
{
if ( _trackname )
free( _trackname );
_trackname = NULL;
if ( trackname )
_trackname = strdup( trackname );
} }
bool bool
Port::name ( const char *base, int n, const char *type ) Port::rename ( void )
{ {
char *s = name_for_port( this->direction(), base, n, type ); char jackname[max_name()];
bool b = name( s );
free(s); snprintf( jackname, sizeof(jackname), "%s%s%s", _trackname ? _trackname : "", _trackname ? "/" : "", _name );
return b;
if ( _port )
return 0 == jack_port_set_name( _port, jackname );
else
return false;
} }
void void

View File

@ -28,7 +28,9 @@ namespace JACK
class Port class Port
{ {
jack_port_t *_port; jack_port_t *_port;
char *_trackname;
char *_name; char *_name;
JACK::Client *_client; JACK::Client *_client;
/* FIXME: reference count? */ /* FIXME: reference count? */
@ -47,23 +49,27 @@ namespace JACK
static int max_name ( void ); static int max_name ( void );
Port ( JACK::Client *client, jack_port_t *port ); Port ( JACK::Client *client, jack_port_t *port );
Port ( JACK::Client *client, const char *name, direction_e dir, type_e type ); /* Port ( JACK::Client *client, const char *name, direction_e dir, type_e type ); */
Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype=0 ); Port ( JACK::Client *client, const char *trackname, const char *name, direction_e dir, type_e type );
Port ( JACK::Client *client, direction_e dir, type_e type, int n, const char *subtype=0 ); /* Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype=0 ); */
/* Port ( JACK::Client *client, direction_e dir, type_e type, int n, const char *subtype=0 ); */
// Port ( ); // Port ( );
~Port ( ); ~Port ( );
Port ( const Port & rhs ); Port ( const Port & rhs );
bool valid ( void ) const { return _port; } bool valid ( void ) const { return _port; }
bool connected ( void ) const { return jack_port_connected( _port ); } bool connected ( void ) const { return jack_port_connected( _port ); }
direction_e direction ( void ) const { return _direction; } direction_e direction ( void ) const { return _direction; }
type_e type ( void ) const { return _type; } type_e type ( void ) const { return _type; }
const char * name ( void ) const { return _name; } const char * name ( void ) const { return _name; }
bool name ( const char *name ); const char * trackname ( void ) const { return _trackname; }
bool name ( const char *base, int n, const char *type=0 ); void name ( const char *name );
void trackname ( const char *trackname );
bool rename ( void );
const char * jack_name ( void ) const { return jack_port_name( _port ); }
// bool name ( const char *base, int n, const char *type=0 );
nframes_t total_latency ( void ) const; nframes_t total_latency ( void ) const;
nframes_t latency ( void ) const; nframes_t latency ( void ) const;
@ -85,6 +91,8 @@ namespace JACK
bool connections ( const char **port_names ); bool connections ( const char **port_names );
void freeze ( void ); void freeze ( void );
void thaw ( void ); void thaw ( void );
JACK::Client * client ( void ) const { return _client; }
void client ( JACK::Client *c ) { _client = c; }
private: private:

View File

@ -169,19 +169,22 @@ void
Control_Sequence::update_port_name ( void ) Control_Sequence::update_port_name ( void )
{ {
bool needs_activation = false; bool needs_activation = false;
char s[512];
snprintf( s, sizeof(s), "%s-cv", name() );
if ( ! _output ) if ( ! _output )
{ {
_output = new JACK::Port( engine, JACK::Port::Output, JACK::Port::Audio, track()->name(), track()->ncontrols(), "cv" ); _output = new JACK::Port( engine, track()->name(), s, JACK::Port::Output, JACK::Port::Audio );
_output->terminal( true ); _output->terminal( true );
needs_activation = true; needs_activation = true;
} }
if ( name() ) if ( name() )
{ {
char n[1024]; _output->trackname( track()->name() );
snprintf( n, sizeof(n), "%s/%s-cv", track()->name(), name() ); _output->name( s );
_output->rename();
_output->name( n );
} }
if ( needs_activation ) if ( needs_activation )

View File

@ -40,6 +40,7 @@ Engine::Engine ( ) : _thread( "RT" )
Engine::~Engine ( ) Engine::~Engine ( )
{ {
DMESSAGE( "Deleting engine" );
/* We have to deactivate here in order to avoid our process /* We have to deactivate here in order to avoid our process
callback is being invoked after we're already destroyed, but callback is being invoked after we're already destroyed, but
before the base class is */ before the base class is */

View File

@ -56,7 +56,7 @@ private:
public: public:
Engine ( ); Engine ( );
~Engine ( ); virtual ~Engine ( );
int dropped ( void ) const { return _buffers_dropped; } int dropped ( void ) const { return _buffers_dropped; }

View File

@ -53,11 +53,16 @@ void
Track::update_port_names ( void ) Track::update_port_names ( void )
{ {
for ( unsigned int i = 0; i < output.size(); ++i ) for ( unsigned int i = 0; i < output.size(); ++i )
output[ i ].name( name(), i ); {
output[ i ].trackname( name() );
output[ i ].rename();
}
for ( unsigned int i = 0; i < input.size(); ++i ) for ( unsigned int i = 0; i < input.size(); ++i )
input[ i ].name( name(), i ); {
input[ i ].trackname( name() );
input[ i ].rename();
}
/* /\* tell any attached control sequences to do the same *\/ */ /* /\* tell any attached control sequences to do the same *\/ */
/* for ( int i = control->children(); i-- ) */ /* for ( int i = control->children(); i-- ) */
/* ((Control_Sequence*)control->child( i ))->update_port_names(); */ /* ((Control_Sequence*)control->child( i ))->update_port_names(); */
@ -86,7 +91,10 @@ Track::configure_outputs ( int n )
{ {
for ( int i = on; i < n; ++i ) for ( int i = on; i < n; ++i )
{ {
JACK::Port p( engine, JACK::Port::Output, JACK::Port::Audio, name(), i ); char s[512];
snprintf( s, sizeof(s), "out-%i", i + 1 );
JACK::Port p( engine, name(), s, JACK::Port::Output, JACK::Port::Audio );
p.terminal(true); p.terminal(true);
@ -141,7 +149,10 @@ Track::configure_inputs ( int n )
{ {
for ( int i = on; i < n; ++i ) for ( int i = on; i < n; ++i )
{ {
JACK::Port p( engine, JACK::Port::Input, JACK::Port::Audio, name(), i ); char s[512];
snprintf( s, sizeof(s), "in-%i", i + 1 );
JACK::Port p( engine, name(), s, JACK::Port::Input, JACK::Port::Audio );
p.terminal( true ); p.terminal( true );

View File

@ -114,7 +114,6 @@ private:
void command_configure_channels ( int n ); void command_configure_channels ( int n );
void update_port_names ( void ); void update_port_names ( void );
const char *name_for_port( JACK::Port::type_e type, int n );
Track ( ); Track ( );
void init ( void ); void init ( void );

View File

@ -273,18 +273,18 @@ main ( int argc, char **argv )
/* cleanup for valgrind's sake */ /* cleanup for valgrind's sake */
if ( engine )
{
delete engine;
engine = NULL;
}
delete timeline; delete timeline;
timeline = NULL; timeline = NULL;
delete tle; delete tle;
tle = NULL; tle = NULL;
if ( engine )
{
delete engine;
engine = NULL;
}
nsm_free( nsm ); nsm_free( nsm );
nsm = NULL; nsm = NULL;