From 5ccc6e7595dd1c134d6b25ea7d142c3921f901eb Mon Sep 17 00:00:00 2001 From: Jonathan Moore Liles Date: Tue, 6 Aug 2013 00:03:19 -0700 Subject: [PATCH] Mixer: Implement basic groups. --- mixer/doc/MANUAL.html | 159 +++++++++++--- mixer/doc/MANUAL.mu | 145 +++++++++++++ mixer/src/AUX_Module.C | 6 +- mixer/src/Chain.C | 133 +++++------- mixer/src/Chain.H | 28 ++- mixer/src/Controller_Module.C | 38 ++-- mixer/src/Controller_Module.H | 1 - mixer/src/Engine/Engine.C | 163 --------------- mixer/src/Group.C | 274 +++++++++++++++++++++++++ mixer/src/{Engine/Engine.H => Group.H} | 64 ++++-- mixer/src/JACK_Module.C | 153 +++++--------- mixer/src/JACK_Module.H | 8 +- mixer/src/Meter_Indicator_Module.C | 1 - mixer/src/Mixer.C | 59 +++++- mixer/src/Mixer.H | 11 +- mixer/src/Mixer_Strip.C | 152 +++++++++++++- mixer/src/Mixer_Strip.H | 12 +- mixer/src/Module.C | 159 +++++++++++++- mixer/src/Module.H | 20 +- mixer/src/Plugin_Module.C | 12 +- mixer/src/Spatializer_Module.C | 24 +-- mixer/src/main.C | 4 +- mixer/src/midi-mapper.C | 4 +- mixer/wscript | 2 +- nonlib/JACK/Client.C | 38 +++- nonlib/JACK/Client.H | 7 +- nonlib/JACK/Port.C | 130 +++++++----- nonlib/JACK/Port.H | 20 +- timeline/src/Control_Sequence.C | 13 +- timeline/src/Engine/Engine.C | 1 + timeline/src/Engine/Engine.H | 2 +- timeline/src/Engine/Track.C | 21 +- timeline/src/Track.H | 1 - timeline/src/main.C | 12 +- 34 files changed, 1314 insertions(+), 563 deletions(-) delete mode 100644 mixer/src/Engine/Engine.C create mode 100644 mixer/src/Group.C rename mixer/src/{Engine/Engine.H => Group.H} (66%) diff --git a/mixer/doc/MANUAL.html b/mixer/doc/MANUAL.html index 19b924e..c2341d0 100644 --- a/mixer/doc/MANUAL.html +++ b/mixer/doc/MANUAL.html @@ -24,31 +24,45 @@ Jonathan Moore Liles <male@tuxfamily.org&

1. User Manual

@@ -80,7 +94,100 @@ After the project has been created. Hit a or choose Mixer/Add Strip

The display options, found in the Options/Display 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.

-

1.1.2. Mixer Strips

+

1.1.2. 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. +

+

1.1.2.1. 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. +

+

1.1.2.2. 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. +

+

+

+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 VerGroupsDSP Load
JACK1N39%
JACK1Y27%
JACK2N24%
JACK2Y31%
+

+AMD FX-8350 @ 4.2Ghz 64-bit -r 48000 -p 256 -n 2 (5.3ms) +

+
+ + + + + +
JACK VerGroupsDSP Load
JACK1N28%
JACK1Y12%
JACK2N12%
JACK2Y11%
+

+AMD FX-8350 @ 4.2Ghz 64-bit -r 48000 -p 128 -n 2 (2.7ms) +

+
+ + + + + +
JACK VerGroupsDSP Load
JACK1N29%
JACK1Y17%
JACK2N17%
JACK2Y17%
+

+AMD FX-8350 @ 4.2Ghz 64-bit -r 48000 -p 32 -n 2 (0.7ms) +

+
+ + + + + +
JACK VerGroupsDSP Load
JACK1Nx
JACK1Yx
JACK2N43%
JACK2Y41%
+

+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. +

+

1.1.2.3. 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. +

+

1.1.2.4. 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. +

+

1.1.2.5. 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. +

+

1.1.2.6. Removing a Group

+

+Groups are destroyed automatically as soon as they contain zero strips. +

+

1.1.3. Mixer Strips

Fig. 1.3. Mixer Strip @@ -101,19 +208,19 @@ The fader view comprises a large gain control and digital peak meter indicator.

To see how an audio signal traveling through this strip will be processed, switch to its signal view.

-

1.1.2.1. Navigation

+

1.1.3.1. Navigation

A strip is focused when you click on it. Focus can be moved among strips with the Tab and Shift-Tab keys.

-

1.1.2.2. Control

+

1.1.3.2. Control

The focused strip can be moved in the display order via the [ and ] keys. Delete removes a strip (with confirmation dialog). n and w set the focused strip's width to narrow or wide, respectively, and f and s switch between fader and signal views. The strip's context menu can be invoked without the mouse by hitting the Menu key (assuming your keyboard has one).

-

1.1.2.3. Signal Chain

+

1.1.3.3. Signal Chain

The signal chain view of a mixer strip provides a way to view and manipulate the signal processing of a mixer strip.

-
1.1.2.3.1. Modules
+
1.1.3.3.1. Modules
Fig. 1.4. Modules @@ -148,7 +255,7 @@ Non Mixer has several built-in modules. They are:
Plugin
Hosts a LADSPA plugin
-
1.1.2.3.1.1. OSC Control
+
1.1.3.3.1.1. OSC Control

The input parameters of all modules are controllable via OSC, regardless of whether the parameter is set as controllable.

@@ -185,7 +292,7 @@ For the second instance of the Gain module on the strip named 'Foo'.

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.

-
1.1.2.3.1.2. Manipulation
+
1.1.3.3.1.2. Manipulation

Left-clicking on a module brings up a Module Parameter Editor window for the selected module.

@@ -201,7 +308,7 @@ Control+Right-clicking on a module causes it to be removed from the chain (modul

The focused module may also be controlled via the keyboard. Menu brings up the context menu for the focused module. Space opens the module parameter editor, b toggles the bypassed state, and Delete removes the module from the chain (without confirmation!). Control-X, Control-C and Control-V, 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.

-
1.1.2.3.1.3. Module Parameter Editor
+
1.1.3.3.1.3. Module Parameter Editor
Fig. 1.5. Module Parameter Editor @@ -229,7 +336,7 @@ The Module Parameter Editor is used to alter the values of a module's parameters

Underneath each control is a bind button. Clicking adds a new control to the chain's Controls view and binds it to the parameter in question. For simplicity, only one control at a time may be bound to a given parameter.

-
1.1.2.3.1.4. Controls
+
1.1.3.3.1.4. Controls
Fig. 1.8. Control View @@ -248,7 +355,7 @@ events. Hold down the `Ctrl` key while scrolling the mousewheel to achieve finer resolution.
-1.1.2.3.1.4.1. Control Voltages +1.1.3.3.1.4.1. Control Voltages

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.

@@ -267,7 +374,7 @@ of parameter automation, as LADSPA plugins are incapable of processing Control Voltage signals at full audio resolution anyway.
-
1.1.2.3.1.5. Spatialization
+
1.1.3.3.1.5. Spatialization
Fig. 1.9. Spatialization Control on a Strip @@ -291,7 +398,7 @@ The spatialization control may be visualized as moving the sound source across t

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.

-

1.1.3. Projects

+

1.1.4. Projects

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:

@@ -299,7 +406,7 @@ A Non-Mixer project is a directory where Non-Mixer keeps the strip settings, pro $ mv Project-A Project-B
-

1.1.3.1. JACK I/O

+

1.1.4.1. JACK I/O

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.

diff --git a/mixer/doc/MANUAL.mu b/mixer/doc/MANUAL.mu index 77e1144..33f839d 100644 --- a/mixer/doc/MANUAL.mu +++ b/mixer/doc/MANUAL.mu @@ -31,6 +31,151 @@ 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. +::: 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 Strip diff --git a/mixer/src/AUX_Module.C b/mixer/src/AUX_Module.C index 9c7e4e6..0c6f46f 100644 --- a/mixer/src/AUX_Module.C +++ b/mixer/src/AUX_Module.C @@ -122,7 +122,7 @@ AUX_Module::process ( nframes_t nframes ) for ( unsigned int i = 0; i < audio_input.size(); ++i ) { 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 ) { 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 ) { 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 ); } } } diff --git a/mixer/src/Chain.C b/mixer/src/Chain.C index 3814983..52b1123 100644 --- a/mixer/src/Chain.C +++ b/mixer/src/Chain.C @@ -72,11 +72,11 @@ #include "FL/test_press.H" #include "debug.h" -#include "Engine/Engine.H" +#include "Group.H" #include "Mixer_Strip.H" #include - +#include "Mixer.H" extern char *instance_name; @@ -87,7 +87,6 @@ Chain::Chain ( ) : Fl_Group( 0, 0, 100, 100, "") { _deleting = false; - _engine = NULL; int X = 0; int Y = 0; @@ -174,18 +173,23 @@ Chain::~Chain ( ) _deleting = true; - engine()->lock(); + client()->lock(); for ( unsigned int i = scratch_port.size(); i--; ) delete[] (sample_t*)scratch_port[i].buffer(); /* if we leave this up to FLTK, it will happen after we've - already destroyed the engine */ + already destroyed the client */ modules_pack->clear(); controls_pack->clear(); - delete _engine; - _engine = NULL; + client()->unlock(); +} + +Group * +Chain::client ( void ) +{ + return strip()->group(); } @@ -302,7 +306,7 @@ Chain::remove ( Controller_Module *m ) { DMESSAGE( "Removing controller module from chain" ); - engine()->lock(); + client()->lock(); m->disconnect(); @@ -311,7 +315,7 @@ Chain::remove ( Controller_Module *m ) build_process_queue(); - engine()->unlock(); + client()->unlock(); redraw(); } @@ -340,7 +344,7 @@ Chain::remove ( Module *m ) fl_alert( "Can't remove module at this point because the resultant chain is invalid" ); } - engine()->lock(); + client()->lock(); strip()->handle_module_removed( m ); @@ -348,7 +352,7 @@ Chain::remove ( Module *m ) configure_ports(); - engine()->unlock(); + client()->unlock(); } /* determine number of output ports, signal if changed. */ @@ -357,7 +361,7 @@ Chain::configure_ports ( void ) { int nouts = 0; - engine()->lock(); + client()->lock(); for ( int i = 0; i < modules(); ++i ) { @@ -378,15 +382,15 @@ Chain::configure_ports ( void ) for ( unsigned int i = 0; i < req_buffers; ++i ) { Module::Port p( NULL, Module::Port::OUTPUT, Module::Port::AUDIO ); - p.connect_to( new sample_t[engine()->nframes()] ); - buffer_fill_with_silence( (sample_t*)p.buffer(), engine()->nframes() ); + p.connect_to( new sample_t[client()->nframes()] ); + buffer_fill_with_silence( (sample_t*)p.buffer(), client()->nframes() ); scratch_port.push_back( p ); } } build_process_queue(); - engine()->unlock(); + client()->unlock(); parent()->redraw(); } @@ -454,47 +458,40 @@ Chain::maximum_name_length ( void ) 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 * changed so they can take the appropriate action (in particular the * JACK module). */ void 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; + if ( strip()->group() ) + { + if ( strip()->group()->single() ) + strip()->group()->name(name); + } + for ( int i = 0; i < modules(); ++i ) { module( i )->handle_chain_name_changed(); @@ -522,7 +519,7 @@ Chain::add ( Controller_Module *m ) bool Chain::insert ( Module *m, Module *n ) { - engine()->lock(); + client()->lock(); if ( !m ) { @@ -581,7 +578,7 @@ Chain::insert ( Module *m, Module *n ) configure_ports(); - engine()->unlock(); + client()->unlock(); DMESSAGE( "Module \"%s\" has %i:%i audio and %i:%i control ports", n->name(), @@ -595,7 +592,7 @@ Chain::insert ( Module *m, Module *n ) err: - engine()->unlock(); + client()->unlock(); DMESSAGE( "Insert failed" ); @@ -606,13 +603,13 @@ err: void Chain::add_control ( Controller_Module *m ) { - engine()->lock(); + client()->lock(); controls_pack->add( m ); configure_ports(); - engine()->unlock(); + client()->unlock(); 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 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 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 Chain::sample_rate_change ( nframes_t nframes ) { @@ -860,12 +839,6 @@ Chain::sample_rate_change ( nframes_t nframes ) 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 */ void 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... */ - if ( jack_port_is_mine( engine()->jack_client(), jack_port_by_id( engine()->jack_client(), a ) ) || - jack_port_is_mine( engine()->jack_client(), jack_port_by_id( engine()->jack_client(), b ) )) + if ( jack_port_is_mine( client()->jack_client(), jack_port_by_id( client()->jack_client(), a ) ) || + jack_port_is_mine( client()->jack_client(), jack_port_by_id( client()->jack_client(), b ) )) { Fl::awake( Chain::update_connection_status, this ); } diff --git a/mixer/src/Chain.H b/mixer/src/Chain.H index d95bfd4..3946d6f 100644 --- a/mixer/src/Chain.H +++ b/mixer/src/Chain.H @@ -28,11 +28,11 @@ #include #include #include "Loggable.H" +#include "Group.H" class Mixer_Strip; class Fl_Flowpack; class Fl_Flip_Button; -class Engine; class Controller_Module; class Chain : public Fl_Group, public Loggable { @@ -50,8 +50,6 @@ class Chain : public Fl_Group, public Loggable { std::vector scratch_port; - Engine *_engine; - Fl_Callback *_configure_outputs_callback; void *_configure_outputs_userdata; @@ -69,18 +67,6 @@ private: void build_process_queue ( void ); 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 ); void update_connection_status ( void ); @@ -91,6 +77,11 @@ protected: 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 ( ); virtual ~Chain ( ); @@ -140,7 +131,12 @@ public: 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 ); }; diff --git a/mixer/src/Controller_Module.C b/mixer/src/Controller_Module.C index d3929c7..a987675 100644 --- a/mixer/src/Controller_Module.C +++ b/mixer/src/Controller_Module.C @@ -38,7 +38,6 @@ #include "FL/test_press.H" #include "FL/menu_popup.H" -#include "Engine/Engine.H" #include "Chain.H" #include "OSC/Endpoint.H" @@ -195,40 +194,31 @@ Controller_Module::set ( Log_Entry &e ) void Controller_Module::mode ( Mode m ) { - if( mode() != CV && m == CV ) { if ( control_output[0].connected() ) { - chain()->engine()->lock(); + chain()->client()->lock(); Port *p = control_output[0].connected_port(); + + char prefix[512]; + snprintf( prefix, sizeof(prefix), "CV-%s", p->name() ); + + add_aux_audio_input( prefix, 0 ); - JACK::Port po( chain()->engine(), JACK::Port::Input, JACK::Port::Audio, p->name(), 0, "CV" ); - - if ( ! po.activate() ) - { - fl_alert( "Could not activate JACK port \"%s\"", po.name() ); - chain()->engine()->unlock(); - return; - } - - if ( po.valid() ) - { - jack_input.push_back( po ); - } - - chain()->engine()->unlock(); + chain()->client()->unlock(); } } else if ( mode() == CV && m != CV ) { - chain()->engine()->lock(); + chain()->client()->lock(); + + delete aux_audio_input.back().jack_port(); - jack_input.back().shutdown(); - jack_input.pop_back(); + aux_audio_input.pop_back(); - chain()->engine()->unlock(); + chain()->client()->unlock(); } _mode = m ; @@ -909,7 +899,7 @@ Controller_Module::command_remove ( void ) } /**********/ -/* Engine */ +/* Client */ /**********/ void @@ -928,7 +918,7 @@ Controller_Module::process ( nframes_t nframes ) 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(); diff --git a/mixer/src/Controller_Module.H b/mixer/src/Controller_Module.H index 72347ee..26c5fd4 100644 --- a/mixer/src/Controller_Module.H +++ b/mixer/src/Controller_Module.H @@ -116,7 +116,6 @@ private: char *generate_osc_path ( void ); void change_osc_path ( char *path ); - std::vector jack_input; Mode _mode; Type _type; diff --git a/mixer/src/Engine/Engine.C b/mixer/src/Engine/Engine.C deleted file mode 100644 index 8e24a2c..0000000 --- a/mixer/src/Engine/Engine.C +++ /dev/null @@ -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 ) -{ -} diff --git a/mixer/src/Group.C b/mixer/src/Group.C new file mode 100644 index 0000000..ce21a18 --- /dev/null +++ b/mixer/src/Group.C @@ -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 +#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::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::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::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::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(); +} + diff --git a/mixer/src/Engine/Engine.H b/mixer/src/Group.H similarity index 66% rename from mixer/src/Engine/Engine.H rename to mixer/src/Group.H index 3b3f528..1d1ec72 100644 --- a/mixer/src/Engine/Engine.H +++ b/mixer/src/Group.H @@ -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 */ /* under the terms of the GNU General Public License as published by the */ @@ -19,6 +19,9 @@ #pragma once +#include +class Mixer_Strip; + #include "Mutex.H" class Port; @@ -26,26 +29,18 @@ class Port; #include "JACK/Client.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 */ 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 ); void shutdown ( void ); int process ( nframes_t nframes ); @@ -54,23 +49,48 @@ class Engine : public JACK::Client, public Mutex int buffer_size ( nframes_t nframes ); void thread_init ( void ); 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 ); + +protected: + + virtual void get ( Log_Entry &e ) const; + virtual void set ( Log_Entry &e ); + private: friend class Port; friend class Transport; -public: +public: - Engine ( void (*process_callback) (nframes_t, void *), void *user_data ); - ~Engine ( ); + LOG_CREATE_FUNC( Group ); + int nstrips ( void ) const { return strips.size(); } 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 ); - 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 ( ); + 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 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; } */ }; + diff --git a/mixer/src/JACK_Module.C b/mixer/src/JACK_Module.C index b617e51..49295dd 100644 --- a/mixer/src/JACK_Module.C +++ b/mixer/src/JACK_Module.C @@ -28,7 +28,6 @@ #include "dsp.h" -#include "Engine/Engine.H" #include "Chain.H" #include "JACK_Module.H" @@ -46,6 +45,9 @@ static Fl_PNG_Image *output_connector_image = NULL; extern char *instance_name; +#include "Mixer.H" +#include "Group.H" + static JACK_Module *receptive_to_drop = NULL; @@ -197,18 +199,18 @@ JACK_Module::draw ( void ) } static std::list -get_connections_for_ports ( std::vector ports ) +get_connections_for_ports ( std::vector ports ) { std::list names; 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 ) 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++ ) { @@ -225,6 +227,15 @@ get_connections_for_ports ( std::vector ports ) strip_name = s; } 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 ) ) { free( client_id ); @@ -281,8 +292,8 @@ get_connections_for_ports ( std::vector ports ) void JACK_Module::update_connection_status ( void ) { - std::list output_names = get_connections_for_ports( jack_output ); - std::list input_names = get_connections_for_ports( jack_input ); + std::list output_names = get_connections_for_ports( aux_audio_output ); + std::list input_names = get_connections_for_ports( aux_audio_input ); connection_display->clear(); @@ -349,52 +360,25 @@ JACK_Module::can_support_inputs ( int ) 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(); - jack_output.pop_back(); + aux_audio_output.back().jack_port()->shutdown(); + 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 JACK_Module::configure_inputs ( int n ) { - if ( n > 0 ) - { - if ( is_default() ) - control_input[0].hints.minimum = 1; - - output_connection_handle->show(); - } + if ( n > 0 ) + { + if ( is_default() ) + control_input[0].hints.minimum = 1; + output_connection_handle->show(); + } int on = audio_input.size(); @@ -402,26 +386,10 @@ JACK_Module::configure_inputs ( int n ) { for ( int i = on; i < n; ++i ) { - JACK::Port *po = NULL; - - 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() ) + if ( add_aux_audio_output(_prefix, i ) ) { add_port( Port( this, Port::INPUT, Port::AUDIO ) ); - jack_output.push_back( *po ); } - - delete po; } } else @@ -430,13 +398,14 @@ JACK_Module::configure_inputs ( int n ) { audio_input.back().disconnect(); audio_input.pop_back(); - jack_output.back().shutdown(); - jack_output.pop_back(); + aux_audio_output.back().jack_port()->shutdown(); + delete aux_audio_output.back().jack_port(); + aux_audio_output.pop_back(); } } _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() ) control_input[0].control_value_no_callback( n ); @@ -444,46 +413,24 @@ JACK_Module::configure_inputs ( int n ) return true; } -void -JACK_Module::jack_port_activation_error ( JACK::Port *p ) -{ - fl_alert( "Could not activate JACK port \"%s\"", p->name() ); -} - bool JACK_Module::configure_outputs ( int n ) { - int on = audio_output.size(); + int on = audio_output.size(); - if ( n > 0 ) - { - input_connection_handle->show(); - } + if ( n > 0 ) + { + input_connection_handle->show(); + } if ( n > on ) { for ( int i = on; i < n; ++i ) { - JACK::Port *po = NULL; - - 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() ) + if ( add_aux_audio_input(_prefix, i ) ) { add_port( Port( this, Port::OUTPUT, Port::AUDIO ) ); - jack_input.push_back( *po ); } - - delete po; } } else @@ -492,8 +439,9 @@ JACK_Module::configure_outputs ( int n ) { audio_output.back().disconnect(); audio_output.pop_back(); - jack_input.back().shutdown(); - jack_input.pop_back(); + aux_audio_input.back().jack_port()->shutdown(); + delete aux_audio_input.back().jack_port(); + aux_audio_input.pop_back(); } } @@ -582,10 +530,11 @@ JACK_Module::handle ( int m ) s[0] = 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; - 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 ); strcat( s, s2 ); @@ -633,7 +582,7 @@ JACK_Module::handle ( int m ) if ( this == receptive_to_drop ) return 1; - if ( jack_input.size() ) + if ( aux_audio_input.size() ) { receptive_to_drop = this; @@ -674,11 +623,11 @@ JACK_Module::handle ( int m ) 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(); - JACK::Port *ji = &jack_input[i]; + JACK::Port *ji = aux_audio_input[i].jack_port(); 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 ) { 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(), nframes ); + } } for ( unsigned int i = 0; i < audio_output.size(); ++i ) { if ( audio_output[i].connected() ) + { 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 ); + } } } diff --git a/mixer/src/JACK_Module.H b/mixer/src/JACK_Module.H index 583b4a3..f080766 100644 --- a/mixer/src/JACK_Module.H +++ b/mixer/src/JACK_Module.H @@ -42,11 +42,6 @@ protected: _prefix = strdup( s ); } - std::vector jack_input; - std::vector jack_output; - - static void jack_port_activation_error ( JACK::Port *p ); - Fl_Button * dec_button; Fl_Button * inc_button; Fl_Browser * connection_display; @@ -76,8 +71,7 @@ public: virtual int handle ( int m ); virtual int can_support_inputs ( int ); - bool add_jack_output ( const char *prefix, int n ); - void remove_jack_outputs ( void ); + void remove_aux_audio_outputs ( void ); virtual bool configure_inputs ( int n ); virtual bool configure_outputs ( int n ); diff --git a/mixer/src/Meter_Indicator_Module.C b/mixer/src/Meter_Indicator_Module.C index 276afa3..35b8897 100644 --- a/mixer/src/Meter_Indicator_Module.C +++ b/mixer/src/Meter_Indicator_Module.C @@ -32,7 +32,6 @@ #include "FL/Fl_Labelpad_Group.H" #include "FL/Fl_Scalepack.H" -#include "Engine/Engine.H" #include "Chain.H" #include "DPM.H" diff --git a/mixer/src/Mixer.C b/mixer/src/Mixer.C index b01824a..8e962b1 100644 --- a/mixer/src/Mixer.C +++ b/mixer/src/Mixer.C @@ -29,7 +29,6 @@ #include #include #include -#include "Engine/Engine.H" #include #include "Project.H" #include @@ -39,7 +38,7 @@ #include #include #include "file.h" - +#include "Group.H" #include #include "debug.h" #include @@ -60,6 +59,7 @@ extern char *instance_name; #include "NSM.H" #include +#include "Chain.H" 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_docked = 1; +// _groups.resize(16); + _rows = 1; _strip_height = 0; box( FL_FLAT_BOX ); @@ -713,6 +715,24 @@ Mixer::~Mixer ( ) 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 ) { 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->insert( *ms, before ); - // scroll->redraw(); } void @@ -795,6 +814,8 @@ void Mixer::remove ( Mixer_Strip *ms ) MESSAGE( "Remove mixer strip \"%s\"", ms->name() ); mixer_strips->remove( ms ); + + ms->group()->remove( ms ); if ( parent() ) parent()->redraw(); @@ -821,7 +842,7 @@ Mixer::contains ( Mixer_Strip *ms ) void Mixer::rows ( int ideal_rows ) { - int sh; + int sh = 0; int actual_rows = 1; @@ -919,6 +940,31 @@ Mixer::get_unique_track_name ( const char *name ) return strdup( pat ); } +Group * +Mixer::group_by_name ( const char *name ) +{ + for ( std::list::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 Mixer::handle_dirty ( int d, void *v ) { @@ -939,6 +985,9 @@ Mixer::snapshot ( void ) if ( spatialization_console ) spatialization_console->log_create(); + for ( std::list::iterator i = groups.begin(); i != groups.end(); ++i ) + (*i)->log_create(); + for ( int i = 0; i < mixer_strips->children(); ++i ) ((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(); + Project::close(); + if ( Project::open( path ) ) { // fl_alert( "Error opening project specified on commandline: %s", Project::errstr( err ) ); diff --git a/mixer/src/Mixer.H b/mixer/src/Mixer.H index 9343347..c6c8bf3 100644 --- a/mixer/src/Mixer.H +++ b/mixer/src/Mixer.H @@ -34,6 +34,7 @@ class Fl_Menu_Bar; class Spatialization_Console; namespace OSC { class Endpoint; } #include +class Group; class Mixer : public Fl_Group { @@ -88,8 +89,17 @@ private: static void update_cb ( void * ); void update_cb ( void ); + public: + Group * group_by_name ( const char * name ); + char *get_unique_group_name ( const char *name ); + + std::list groups; + Group *group ( int n ); + void add_group ( Group *g ); + void remove_group ( Group *g ); + void update_menu ( void ); static Spatialization_Console *spatialization_console; @@ -114,7 +124,6 @@ public: virtual void resize ( int X, int Y, int W, int H ); void new_strip ( void ); - void process ( unsigned int nframes ); void add ( Mixer_Strip *ms ); void remove ( Mixer_Strip *ms ); void move_left ( Mixer_Strip *ms ); diff --git a/mixer/src/Mixer_Strip.C b/mixer/src/Mixer_Strip.C index b53f061..935ad5e 100644 --- a/mixer/src/Mixer_Strip.C +++ b/mixer/src/Mixer_Strip.C @@ -34,7 +34,6 @@ #include "Project.H" #include "Mixer_Strip.H" -#include "Engine/Engine.H" #include #include #include "debug.h" @@ -58,6 +57,8 @@ #include "FL/test_press.H" #include "FL/menu_popup.H" #include +#include +#include "Group.H" extern Mixer *mixer; @@ -71,6 +72,8 @@ Mixer_Strip::Mixer_Strip( const char *strip_name ) : Fl_Group( 0, 0, 120, 600 ) init(); + _group = new Group(strip_name, true); + chain( new Chain() ); _chain->initialize_with_default(); @@ -96,17 +99,17 @@ Mixer_Strip::~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 */ fader_tab->clear(); delete _chain; _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 * this setting as part of the mixer strip */ e.add( ":gain_mode", gain_controller->mode() ); + if ( ! _group->single() ) + e.add( ":group", _group ); + else + e.add( ":group", (Loggable*)0 ); + } @@ -154,8 +162,27 @@ Mixer_Strip::set ( Log_Entry &e ) { _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 ) ) mixer->add( this ); } @@ -204,6 +231,8 @@ Mixer_Strip::chain ( Chain *c ) c->configure_outputs_callback( configure_outputs, this ); c->name( name() ); + /* FIXME: don't hardcode this list of modules */ + spatialization_controller->chain( c ); gain_controller->chain( c ); jack_input_controller->chain( c ); meter_indicator->chain( c ); @@ -253,12 +282,76 @@ void Mixer_Strip::cb_handle(Fl_Widget* o) { if ( parent() ) 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) { ((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 Mixer_Strip::name ( const char *name ) { @@ -375,6 +468,7 @@ Mixer_Strip::init ( ) _gain_controller_mode = 0; _chain = 0; + _group = 0; box( FL_FLAT_BOX ); labeltype( FL_NO_LABEL ); @@ -433,6 +527,14 @@ Mixer_Strip::init ( ) o->end(); } // 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"); o->type(1); o->labelsize( 14 ); @@ -519,11 +621,49 @@ Mixer_Strip::init ( ) size( 96, h() ); + update_group_choice(); // redraw(); // _chain->configure_ports(); } +void +Mixer_Strip::update_group_choice ( void ) +{ + Fl_Choice *o = group_choice; + + o->clear(); + o->add( "---" ); + + for ( std::list::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 Mixer_Strip::draw ( void ) { diff --git a/mixer/src/Mixer_Strip.H b/mixer/src/Mixer_Strip.H index 2395a14..4f40ae1 100644 --- a/mixer/src/Mixer_Strip.H +++ b/mixer/src/Mixer_Strip.H @@ -32,7 +32,6 @@ #include #include #include -//#include "Fader.H" #include @@ -48,6 +47,8 @@ class Fl_Flip_Button; class Fl_Input; class Fl_Menu_; class Fl_Menu_Button; +class Fl_Choice; +class Group; class Mixer_Strip : public Fl_Group, public Loggable { @@ -60,6 +61,7 @@ public: virtual ~Mixer_Strip(); void chain ( Chain *c ); + Chain *chain ( void ) { return _chain; } virtual void log_children ( void ) const; @@ -92,6 +94,7 @@ private: Fl_Flip_Button *tab_button; Fl_Button *close_button; Fl_Input *name_field; + Fl_Choice *group_choice; Fl_Flowpack *controls_pack; Fl_Group *tab_group; @@ -100,6 +103,7 @@ private: Fl_Pack *panner_pack; Chain *_chain; + Group *_group; Fl_Box *spatialization_label; Controller_Module *gain_controller; @@ -141,8 +145,14 @@ protected: public: + void update_group_choice ( void ); + Controller_Module *spatializer ( void ); + Group *group ( void ) { return _group;} + + // int group ( void ) const; + void group ( Group * ); void send_feedback ( void ); int number ( void ) const; static bool import_strip ( const char *filename ); diff --git a/mixer/src/Module.C b/mixer/src/Module.C index f6ccd4c..53d9fa9 100644 --- a/mixer/src/Module.C +++ b/mixer/src/Module.C @@ -73,7 +73,7 @@ Module::Module ( ) : Fl_Group( 0, 0, 50, 50, "Unnamed" ) 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 ) { @@ -880,6 +880,21 @@ Module::handle_chain_name_changed ( ) 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 @@ -964,6 +979,148 @@ Module::handle ( int m ) 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 */ diff --git a/mixer/src/Module.H b/mixer/src/Module.H index 6db21c4..f71850a 100644 --- a/mixer/src/Module.H +++ b/mixer/src/Module.H @@ -91,7 +91,7 @@ public: public: enum Direction { INPUT, OUTPUT }; - enum Type { AUDIO, CONTROL }; + enum Type { AUDIO, CONTROL, AUX_AUDIO }; /* hints for control ports (specifically control inputs) */ struct Hints @@ -138,6 +138,7 @@ public: _unscaled_signal = 0; _by_number_path = 0; _by_number_number = -1; + _jack_port = 0; } Port ( const Port& p ) @@ -154,6 +155,7 @@ public: _unscaled_signal = p._unscaled_signal; _by_number_path = 0; _by_number_number = -1; + _jack_port = p._jack_port; } virtual ~Port ( ) @@ -273,6 +275,9 @@ public: /* FIXME: do something! */ } + void jack_port ( JACK::Port *v ) { _jack_port = v; } + JACK::Port *jack_port ( void ) { return _jack_port; } + private: char *generate_osc_path ( void ); @@ -285,7 +290,8 @@ public: void *_buf; nframes_t _nframes; Module *_module; - + /* used for auxilliary I/Os */ + JACK::Port *_jack_port; OSC::Signal *_scaled_signal; OSC::Signal *_unscaled_signal; @@ -348,6 +354,8 @@ public: std::vector audio_output; std::vector control_input; std::vector control_output; + std::vector aux_audio_input; + std::vector aux_audio_output; void add_port ( const Port &p ) { @@ -477,8 +485,16 @@ protected: virtual void get ( Log_Entry &e ) const; virtual void set ( Log_Entry &e ); + bool add_aux_port ( bool input, const char *prefix, int n ); + 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; } void command_open_parameter_editor(); diff --git a/mixer/src/Plugin_Module.C b/mixer/src/Plugin_Module.C index fbe4ae2..c981c91 100644 --- a/mixer/src/Plugin_Module.C +++ b/mixer/src/Plugin_Module.C @@ -41,7 +41,7 @@ #include "LADSPAInfo.h" #include "Chain.H" -#include "Engine/Engine.H" +//#include "Client/Client.H" #include @@ -684,7 +684,7 @@ Plugin_Module::activate ( void ) FATAL( "Attempt to activate already active plugin" ); if ( chain() ) - chain()->engine()->lock(); + chain()->client()->lock(); if ( _idata->descriptor->activate ) for ( unsigned int i = 0; i < _idata->handle.size(); ++i ) @@ -693,7 +693,7 @@ Plugin_Module::activate ( void ) _bypass = false; if ( chain() ) - chain()->engine()->unlock(); + chain()->client()->unlock(); } void @@ -705,7 +705,7 @@ Plugin_Module::deactivate( void ) DMESSAGE( "Deactivating plugin \"%s\"", label() ); if ( chain() ) - chain()->engine()->lock(); + chain()->client()->lock(); _bypass = true; @@ -714,7 +714,7 @@ Plugin_Module::deactivate( void ) _idata->descriptor->deactivate( _idata->handle[i] ); if ( chain() ) - chain()->engine()->unlock(); + chain()->client()->unlock(); } void @@ -743,7 +743,7 @@ Plugin_Module::handle_port_connection_change ( void ) /**********/ -/* Engine */ +/* Client */ /**********/ void diff --git a/mixer/src/Spatializer_Module.C b/mixer/src/Spatializer_Module.C index aa16753..234a679 100644 --- a/mixer/src/Spatializer_Module.C +++ b/mixer/src/Spatializer_Module.C @@ -512,7 +512,7 @@ Spatializer_Module::draw ( void ) int spacing, offset; - int ni = jack_output.size(); + int ni = aux_audio_output.size(); spacing = h() / ni; offset = spacing / 2; @@ -566,9 +566,9 @@ Spatializer_Module::process ( nframes_t nframes ) /* send to late reverb */ 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 - 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... *\/ */ /* 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 */ 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(), nframes ); @@ -678,7 +678,7 @@ Spatializer_Module::configure_inputs ( int n ) if ( n == 0 ) { - remove_jack_outputs(); + remove_aux_audio_outputs(); audio_output.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_jack_output( "early reverb", 0 ); - add_jack_output( "early reverb", 1 ); - add_jack_output( "early reverb", 2 ); - add_jack_output( "early reverb", 3 ); + add_aux_audio_output( "late reverb", 0 ); + add_aux_audio_output( "early reverb", 0 ); + add_aux_audio_output( "early reverb", 1 ); + add_aux_audio_output( "early reverb", 2 ); + add_aux_audio_output( "early reverb", 3 ); } } _connection_handle_outputs[0][0] = 0; _connection_handle_outputs[0][1] = 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; } diff --git a/mixer/src/main.C b/mixer/src/main.C index d7614b1..902e112 100644 --- a/mixer/src/main.C +++ b/mixer/src/main.C @@ -55,6 +55,7 @@ #include "AUX_Module.H" #include "NSM.H" #include "Spatialization_Console.H" +#include "Group.H" #include #include @@ -161,6 +162,7 @@ main ( int argc, char **argv ) LOG_REGISTER_CREATE( Controller_Module ); LOG_REGISTER_CREATE( AUX_Module ); LOG_REGISTER_CREATE( Spatialization_Console ); + LOG_REGISTER_CREATE( Group ); signal( SIGPIPE, SIG_IGN ); @@ -258,7 +260,7 @@ main ( int argc, char **argv ) } Plugin_Module::spawn_discover_thread(); - + mixer->init_osc( osc_port ); char *nsm_url = getenv( "NSM_URL" ); diff --git a/mixer/src/midi-mapper.C b/mixer/src/midi-mapper.C index bb5f1ad..ce9ce2a 100644 --- a/mixer/src/midi-mapper.C +++ b/mixer/src/midi-mapper.C @@ -529,8 +529,8 @@ create_engine ( void ) return false; } - engine->midi_input_port = new JACK::Port( engine, "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_input_port = new JACK::Port( engine, NULL, "midi-in", JACK::Port::Input, 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() ) { diff --git a/mixer/wscript b/mixer/wscript index eb56ece..abae91e 100644 --- a/mixer/wscript +++ b/mixer/wscript @@ -46,7 +46,6 @@ def build(bld): src/Chain.C src/Controller_Module.C src/DPM.C -src/Engine/Engine.C src/Gain_Module.C src/Spatializer_Module.C src/JACK_Module.C @@ -65,6 +64,7 @@ src/NSM.C src/Panner.C src/Plugin_Module.C src/Project.C +src/Group.C src/main.C src/Spatialization_Console.C ''', diff --git a/nonlib/JACK/Client.C b/nonlib/JACK/Client.C index fbe2ae2..380d1c9 100644 --- a/nonlib/JACK/Client.C +++ b/nonlib/JACK/Client.C @@ -35,6 +35,7 @@ namespace JACK Client::Client ( ) { + _active = false; _freewheeling = false; _zombified = false; _client = NULL; @@ -43,7 +44,7 @@ namespace JACK Client::~Client ( ) { - jack_client_close( _client ); + close(); } /** Tell JACK to stop calling process callback. This MUST be called in @@ -51,9 +52,12 @@ namespace JACK void Client::deactivate ( ) { - jack_deactivate( _client ); - } + if ( _active ) + jack_deactivate( _client ); + _active = false; + } + /*******************/ /* Static Wrappers */ @@ -138,6 +142,14 @@ namespace JACK 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 * pointer to actual name as reported by JACK */ const char * @@ -167,15 +179,13 @@ namespace JACK jack_on_shutdown( _client, &Client::shutdown, this ); - jack_activate( _client ); + activate(); // _sample_rate = frame_rate(); return jack_get_client_name( _client ); } - - /* THREAD: RT */ /** enter or leave freehweeling mode */ void @@ -185,7 +195,12 @@ namespace JACK ; // WARNING( "Unkown error while setting freewheeling mode" ); } - + + const char * + Client::jack_name ( void ) const + { + return jack_get_client_name( _client ); + } void Client::port_added ( Port *p ) @@ -235,8 +250,13 @@ namespace JACK void Client::close ( void ) { - jack_deactivate( _client ); - jack_client_close( _client ); + deactivate(); + + if ( _client ) + { + DMESSAGE( "Closing JACK client" ); + jack_client_close( _client ); + } _client = NULL; } diff --git a/nonlib/JACK/Client.H b/nonlib/JACK/Client.H index 799225f..e753b4f 100644 --- a/nonlib/JACK/Client.H +++ b/nonlib/JACK/Client.H @@ -43,6 +43,7 @@ namespace JACK volatile int _xruns; volatile bool _freewheeling; volatile bool _zombified; + volatile bool _active; static int sample_rate_changed ( nframes_t srate, void *arg ); virtual int sample_rate_changed ( nframes_t srate ) { return 0; } @@ -72,8 +73,10 @@ namespace JACK void thaw_ports ( void ); protected: - + + bool active ( void ) const { return _active; } void deactivate ( void ); + void activate ( void ); private: @@ -97,6 +100,8 @@ namespace JACK const char * init ( const char *client_name, unsigned int opts = 0 ); const char * name ( const char * ); + const char *jack_name ( void ) const; + void close ( void ); nframes_t nframes ( void ) const { return jack_get_buffer_size( _client ); } // float frame_rate ( void ) const { return jack_get_sample_rate( _client ); } diff --git a/nonlib/JACK/Port.C b/nonlib/JACK/Port.C index 9ecf1a4..647f158 100644 --- a/nonlib/JACK/Port.C +++ b/nonlib/JACK/Port.C @@ -31,7 +31,7 @@ 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 Port::max_name ( void ) @@ -49,8 +49,11 @@ namespace JACK _port = rhs._port; _direction = rhs._direction; _type = rhs._type; + _name = NULL; _name = strdup( rhs._name ); - + _trackname = NULL; + if ( rhs._trackname ) + _trackname = strdup( rhs._trackname ); _client->port_added( this ); } @@ -62,6 +65,7 @@ namespace JACK _client = client; _port = port; _name = strdup( jack_port_name( port ) ); + _trackname = NULL; _direction = ( jack_port_flags( _port ) & JackPortIsOutput ) ? Output : Input; 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; _terminal = 0; _name = NULL; + _trackname = NULL; _connections = NULL; _client = client; _direction = dir; _type = type; + _trackname = NULL; + + if ( trackname ) + _trackname = strdup( trackname ); _name = strdup( name ); _client->port_added( this ); - } - Port::Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype ) - { - _port = 0; - _terminal = 0; - _name = NULL; - _connections = NULL; - _client = client; + /* Port::Port ( JACK::Client *client, direction_e dir, type_e type, const char *base, int n, const char *subtype ) */ + /* { */ + /* _port = 0; */ + /* _terminal = 0; */ + /* _name = NULL; */ + /* _connections = NULL; */ + /* _client = client; */ - _name = name_for_port( dir, base, n, subtype ); - _direction = dir; - _type = type; + /* _name = name_for_port( dir, base, n, subtype ); */ + /* _direction = dir; */ + /* _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 = 0; - _terminal = 0; - _name = NULL; - _connections = NULL; - _client = client; + /* Port::Port ( JACK::Client *client, direction_e dir, type_e type, int n, const char *subtype ) */ + /* { */ + /* _port = 0; */ + /* _terminal = 0; */ + /* _name = NULL; */ + /* _connections = NULL; */ + /* _client = client; */ - _name = name_for_port( dir, NULL, n, subtype ); - _direction = dir; - _type = type; + /* _name = name_for_port( dir, NULL, n, subtype ); */ + /* _direction = dir; */ + /* _type = type; */ - _client->port_added( this ); - } + /* _client->port_added( this ); */ + /* } */ Port::~Port ( ) { @@ -128,6 +136,12 @@ namespace JACK free( _name ); _name = NULL; } + if ( _trackname ) + { + free( _trackname ); + _trackname = NULL; + } + } /* sort input before output and then by alpha */ @@ -141,20 +155,20 @@ namespace JACK } - static char * - name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ) - { - char *pname; + /* static char * */ + /* name_for_port ( Port::direction_e dir, const char *base, int n, const char *type ) */ + /* { */ + /* char *pname; */ - const char *dir_s = dir == Port::Output ? "out" : "in"; + /* const char *dir_s = dir == Port::Output ? "out" : "in"; */ - if ( type ) - asprintf( &pname, "%s-%s%s%s-%d", type, base ? base : "", base ? "/" : "", dir_s, n + 1 ); - else - asprintf( &pname, "%s%s%s-%d", base ? base : "", base ? "/" : "", dir_s, n + 1 ); + /* if ( type ) */ + /* asprintf( &pname, "%s-%s%s%s-%d", type, base ? base : "", base ? "/" : "", dir_s, n + 1 ); */ + /* else */ + /* asprintf( &pname, "%s%s%s-%d", base ? base : "", base ? "/" : "", dir_s, n + 1 ); */ - return pname; - } + /* return pname; */ + /* } */ bool Port::activate ( void ) @@ -171,8 +185,12 @@ namespace JACK if ( _terminal ) flags |= JackPortIsTerminal; - DMESSAGE( "Activating port name %s", _name ); - _port = jack_port_register( _client->jack_client(), _name, + char jackname[max_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, flags, 0 ); @@ -231,25 +249,39 @@ namespace JACK _port = 0; } -/** rename port */ - bool + + void Port::name ( const char *name ) { if ( _name ) free( _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 - Port::name ( const char *base, int n, const char *type ) + Port::rename ( void ) { - char *s = name_for_port( this->direction(), base, n, type ); - bool b = name( s ); - free(s); - return b; + char jackname[max_name()]; + + snprintf( jackname, sizeof(jackname), "%s%s%s", _trackname ? _trackname : "", _trackname ? "/" : "", _name ); + + if ( _port ) + return 0 == jack_port_set_name( _port, jackname ); + else + return false; } void diff --git a/nonlib/JACK/Port.H b/nonlib/JACK/Port.H index 6388465..8d2558d 100644 --- a/nonlib/JACK/Port.H +++ b/nonlib/JACK/Port.H @@ -28,7 +28,9 @@ namespace JACK class Port { jack_port_t *_port; + char *_trackname; char *_name; + JACK::Client *_client; /* FIXME: reference count? */ @@ -47,23 +49,27 @@ namespace JACK static int max_name ( void ); 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, 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 ( JACK::Client *client, const char *name, direction_e dir, type_e type ); */ + 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, 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 ( const Port & rhs ); - bool valid ( void ) const { return _port; } bool connected ( void ) const { return jack_port_connected( _port ); } direction_e direction ( void ) const { return _direction; } type_e type ( void ) const { return _type; } const char * name ( void ) const { return _name; } - bool name ( const char *name ); - bool name ( const char *base, int n, const char *type=0 ); + const char * trackname ( void ) const { return _trackname; } + 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 latency ( void ) const; @@ -85,6 +91,8 @@ namespace JACK bool connections ( const char **port_names ); void freeze ( void ); void thaw ( void ); + JACK::Client * client ( void ) const { return _client; } + void client ( JACK::Client *c ) { _client = c; } private: diff --git a/timeline/src/Control_Sequence.C b/timeline/src/Control_Sequence.C index e76f39c..036c819 100644 --- a/timeline/src/Control_Sequence.C +++ b/timeline/src/Control_Sequence.C @@ -169,19 +169,22 @@ void Control_Sequence::update_port_name ( void ) { bool needs_activation = false; + + char s[512]; + snprintf( s, sizeof(s), "%s-cv", name() ); + 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 ); needs_activation = true; } if ( name() ) { - char n[1024]; - snprintf( n, sizeof(n), "%s/%s-cv", track()->name(), name() ); - - _output->name( n ); + _output->trackname( track()->name() ); + _output->name( s ); + _output->rename(); } if ( needs_activation ) diff --git a/timeline/src/Engine/Engine.C b/timeline/src/Engine/Engine.C index 3257bcf..ea9b227 100644 --- a/timeline/src/Engine/Engine.C +++ b/timeline/src/Engine/Engine.C @@ -40,6 +40,7 @@ Engine::Engine ( ) : _thread( "RT" ) Engine::~Engine ( ) { + DMESSAGE( "Deleting engine" ); /* We have to deactivate here in order to avoid our process callback is being invoked after we're already destroyed, but before the base class is */ diff --git a/timeline/src/Engine/Engine.H b/timeline/src/Engine/Engine.H index 703990a..cea7713 100644 --- a/timeline/src/Engine/Engine.H +++ b/timeline/src/Engine/Engine.H @@ -56,7 +56,7 @@ private: public: Engine ( ); - ~Engine ( ); + virtual ~Engine ( ); int dropped ( void ) const { return _buffers_dropped; } diff --git a/timeline/src/Engine/Track.C b/timeline/src/Engine/Track.C index 45217ff..1976502 100644 --- a/timeline/src/Engine/Track.C +++ b/timeline/src/Engine/Track.C @@ -53,11 +53,16 @@ void Track::update_port_names ( void ) { 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 ) - input[ i ].name( name(), i ); - + { + input[ i ].trackname( name() ); + input[ i ].rename(); + } /* /\* tell any attached control sequences to do the same *\/ */ /* for ( int i = control->children(); i-- ) */ /* ((Control_Sequence*)control->child( i ))->update_port_names(); */ @@ -86,7 +91,10 @@ Track::configure_outputs ( int n ) { 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); @@ -141,7 +149,10 @@ Track::configure_inputs ( int n ) { 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 ); diff --git a/timeline/src/Track.H b/timeline/src/Track.H index 49bf812..b09ecd5 100644 --- a/timeline/src/Track.H +++ b/timeline/src/Track.H @@ -114,7 +114,6 @@ private: void command_configure_channels ( int n ); void update_port_names ( void ); - const char *name_for_port( JACK::Port::type_e type, int n ); Track ( ); void init ( void ); diff --git a/timeline/src/main.C b/timeline/src/main.C index 66eac9c..a341240 100644 --- a/timeline/src/main.C +++ b/timeline/src/main.C @@ -273,18 +273,18 @@ main ( int argc, char **argv ) /* cleanup for valgrind's sake */ - if ( engine ) - { - delete engine; - engine = NULL; - } - delete timeline; timeline = NULL; delete tle; tle = NULL; + if ( engine ) + { + delete engine; + engine = NULL; + } + nsm_free( nsm ); nsm = NULL;