string mix_template)
: _engine (eng),
+ mmc (0),
_mmc_port (default_mmc_port),
_mtc_port (default_mtc_port),
_midi_port (default_midi_port),
pending_events (2048),
+ state_tree (0),
+ _send_smpte_update (false),
+ midi_thread (pthread_t (0)),
midi_requests (128), // the size of this should match the midi request pool size
diskstreams (new DiskstreamList),
routes (new RouteList),
auditioner ((Auditioner*) 0),
_total_free_4k_blocks (0),
_click_io ((IO*) 0),
+ click_data (0),
+ click_emphasis_data (0),
main_outs (0)
{
bool new_session;
throw failed_constructor();
}
- cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl;
+ info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (1)" << endl;
n_physical_audio_outputs = _engine.n_physical_audio_outputs();
n_physical_audio_inputs = _engine.n_physical_audio_inputs();
nframes_t initial_length)
: _engine (eng),
+ mmc (0),
_mmc_port (default_mmc_port),
_mtc_port (default_mtc_port),
_midi_port (default_midi_port),
pending_events (2048),
+ state_tree (0),
+ _send_smpte_update (false),
+ midi_thread (pthread_t (0)),
midi_requests (16),
diskstreams (new DiskstreamList),
routes (new RouteList),
+ auditioner ((Auditioner *) 0),
_total_free_4k_blocks (0),
+ _click_io ((IO *) 0),
+ click_data (0),
+ click_emphasis_data (0),
main_outs (0)
{
throw failed_constructor();
}
- cerr << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl;
+ info << "Loading session " << fullpath << " using snapshot " << snapshot_name << " (2)" << endl;
n_physical_audio_outputs = _engine.n_physical_audio_outputs();
n_physical_audio_inputs = _engine.n_physical_audio_inputs();
_history.clear ();
/* clear state tree so that no references to objects are held any more */
-
- if (state_tree) {
- delete state_tree;
- }
+ delete state_tree;
terminate_butler_thread ();
terminate_midi_thread ();
- if (click_data && click_data != default_click) {
+ if (click_data != default_click) {
delete [] click_data;
}
- if (click_emphasis_data && click_emphasis_data != default_click_emphasis) {
+ if (click_emphasis_data != default_click_emphasis) {
delete [] click_emphasis_data;
}
i = tmp;
}
- if (butler_mixdown_buffer) {
- delete [] butler_mixdown_buffer;
- }
-
- if (butler_gain_buffer) {
- delete [] butler_gain_buffer;
- }
-
Crossfade::set_buffer_size (0);
- if (mmc) {
- delete mmc;
- }
+ delete mmc;
}
void
void
Session::when_engine_running ()
{
- string first_physical_output;
-
/* we don't want to run execute this again */
BootMessage (_("Set block size and sample rate"));
} else {
- /* default state for Click */
-
- first_physical_output = _engine.get_nth_physical_audio_output (0);
+ /* default state for Click: dual-mono to first 2 physical outputs */
- if (first_physical_output.length()) {
- if (_click_io->add_output_port (first_physical_output, this)) {
- // relax, even though its an error
- } else {
- _clicking = Config->get_clicking ();
+ for (int physport = 0; physport < 2; ++physport) {
+ string physical_output = _engine.get_nth_physical_audio_output (physport);
+
+ if (physical_output.length()) {
+ if (_click_io->add_output_port (physical_output, this)) {
+ // relax, even though its an error
+ }
}
}
+
+ if (_click_io->n_outputs() > 0) {
+ _clicking = Config->get_clicking ();
+ }
}
}
if (transport_rolling() && play_loop) {
- //if (_transport_frame < location->start() || _transport_frame > location->end()) {
+ // if (_transport_frame > location->end()) {
- if (_transport_frame > location->end()) {
+ if (_transport_frame < location->start() || _transport_frame > location->end()) {
// relocate to beginning of loop
clear_events (Event::LocateRoll);
nframes_t offset;
nframes_t tf;
+ if (_transport_speed == 0.0f && non_realtime_work_pending()) {
+ return last_stop_frame;
+ }
+
/* the first of these two possible settings for "offset"
mean that the audible frame is stationary until
audio emerges from the latency compensation
} else {
tf = _transport_frame;
}
-
- if (_transport_speed == 0) {
- ret = tf;
- goto block_retrograde;
- }
-
- if (tf < offset) {
- ret = 0;
- goto block_retrograde;
- }
-
+
ret = tf;
if (!non_realtime_work_pending()) {
/* MOVING */
- /* take latency into account */
-
- if (_transport_speed > 0.0) {
- /* forwards */
- ret -= offset;
- } else {
- /* backwards */
- ret += offset;
- }
+ /* check to see if we have passed the first guaranteed
+ audible frame past our last stopping position. if not,
+ the return that last stopping point because in terms
+ of audible frames, we have not moved yet.
+ */
- }
+ if (_transport_speed > 0.0f) {
- /* do not allow retrograde motion near startup or a direction change
- caused by latency correction. we detect this by the asking if the present
- and previously-noted transport speed (and thus direction) are the same.
- */
+ if (!play_loop || !have_looped) {
+ if (tf < last_stop_frame + offset) {
+ return last_stop_frame;
+
+ }
+ }
+
- block_retrograde:
- if ((af_last_transport_speed >= 0.0) == (_transport_speed >= 0.0)) {
+ /* forwards */
+ ret -= offset;
- if (_transport_speed > 0.0) {
- if (ret < af_last_frame) {
- ret = af_last_frame;
- }
+ } else if (_transport_speed < 0.0f) {
+
+ /* XXX wot? no backward looping? */
- } else if (_transport_speed < 0.0) {
- if (ret > af_last_frame) {
- ret = af_last_frame;
+ if (tf > last_stop_frame - offset) {
+ return last_stop_frame;
+ } else {
+ /* backwards */
+ ret += offset;
}
- }
- }
-
- af_last_frame = ret;
- af_last_transport_speed = _transport_speed;
+ }
+ }
return ret;
}
catch (AudioEngine::PortRegistrationFailure& pfe) {
- error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg;
+ error << pfe.what() << endmsg;
if (track) {
/* we need to get rid of this, since the track failed to be created */
<< endmsg;
goto failure;
}
-
+ /*
for (uint32_t x = 0; n_physical_audio_inputs && x < bus->n_inputs(); ++x) {
-
+
port = "";
-
+
if (Config->get_input_auto_connect() & AutoConnectPhysical) {
- port = physinputs[((n+x)%n_physical_audio_inputs)];
+ port = physinputs[((n+x)%n_physical_audio_inputs)];
}
if (port.length() && bus->connect_input (bus->input (x), port, this)) {
break;
}
}
-
+ */
for (uint32_t x = 0; n_physical_audio_outputs && x < bus->n_outputs(); ++x) {
port = "";
}
catch (AudioEngine::PortRegistrationFailure& pfe) {
- error << _("No more JACK ports are available. You will need to stop Ardour and restart JACK with ports if you need this many tracks.") << endmsg;
+ error << pfe.what() << endmsg;
goto failure;
}
}
+Session::RouteList
+Session::new_route_from_template (uint32_t how_many, const std::string& template_path)
+{
+ char name[32];
+ RouteList ret;
+ uint32_t control_id;
+ XMLTree tree;
+ uint32_t number = 1;
+
+ if (!tree.read (template_path.c_str())) {
+ return ret;
+ }
+
+ XMLNode* node = tree.root();
+
+ control_id = ntracks() + nbusses() + 1;
+
+ while (how_many) {
+
+ XMLNode node_copy (*node); // make a copy so we can change the name if we need to
+
+ std::string node_name = IO::name_from_state (*node_copy.children().front());
+
+ /* generate a new name by adding a number to the end of the template name */
+
+ do {
+ snprintf (name, sizeof (name), "%s %" PRIu32, node_name.c_str(), number);
+
+ number++;
+
+ if (route_by_name (name) == 0) {
+ break;
+ }
+
+ } while (number < UINT_MAX);
+
+ if (number == UINT_MAX) {
+ fatal << _("Session: UINT_MAX routes? impossible!") << endmsg;
+ /*NOTREACHED*/
+ }
+
+ IO::set_name_in_state (*node_copy.children().front(), name);
+
+ Track::zero_diskstream_id_in_xml (node_copy);
+
+ try {
+ shared_ptr<Route> route (XMLRouteFactory (node_copy));
+
+ if (route == 0) {
+ error << _("Session: cannot create track/bus from template description") << endmsg;
+ goto out;
+ }
+
+ if (boost::dynamic_pointer_cast<Track>(route)) {
+ /* force input/output change signals so that the new diskstream
+ picks up the configuration of the route. During session
+ loading this normally happens in a different way.
+ */
+ route->input_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this);
+ route->output_changed (IOChange (ConfigurationChanged|ConnectionsChanged), this);
+ }
+
+ route->set_remote_control_id (control_id);
+ ++control_id;
+
+ ret.push_back (route);
+ }
+
+ catch (failed_constructor &err) {
+ error << _("Session: could not create new route from template") << endmsg;
+ goto out;
+ }
+
+ catch (AudioEngine::PortRegistrationFailure& pfe) {
+ error << pfe.what() << endmsg;
+ goto out;
+ }
+
+ --how_many;
+ }
+
+ out:
+ if (!ret.empty()) {
+ add_routes (ret, true);
+ }
+
+ return ret;
+}
+
+boost::shared_ptr<Route>
+Session::new_video_track (string name)
+{
+ uint32_t control_id = ntracks() + nbusses() + 1;
+ shared_ptr<Route> new_route (
+ new Route ( *this, name, -1, -1, -1, -1, Route::Flag(0), ARDOUR::DataType::NIL));
+ new_route->set_remote_control_id (control_id);
+
+ RouteList rl;
+ rl.push_back (new_route);
+ {
+ RCUWriter<RouteList> writer (routes);
+ shared_ptr<RouteList> r = writer.get_copy ();
+ r->insert (r->end(), rl.begin(), rl.end());
+ resort_routes_using (r);
+ }
+ return new_route;
+}
+
void
Session::add_routes (RouteList& new_routes, bool save)
{
/* don't mess with busses */
- if (dynamic_cast<AudioTrack*>((*i).get()) == 0) {
+ if (boost::dynamic_pointer_cast<AudioTrack>(*i) == 0) {
continue;
}
/* don't mess with tracks */
- if (dynamic_cast<AudioTrack*>((*i).get()) != 0) {
+ if (boost::dynamic_pointer_cast<AudioTrack>(*i) != 0) {
continue;
}
}
bool is_track = false;
bool signal = false;
- /* caller must hold RouteLock */
-
/* this is where we actually implement solo by changing
the solo mute setting of each track.
*/
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if ((*i)->soloed()) {
mute = true;
- if (dynamic_cast<AudioTrack*>((*i).get())) {
+ if (boost::dynamic_pointer_cast<AudioTrack>(*i)) {
is_track = true;
}
break;
/* only alter track solo mute */
- if (dynamic_cast<AudioTrack*>((*i).get())) {
+ if (boost::dynamic_pointer_cast<AudioTrack>(*i)) {
if ((*i)->soloed()) {
(*i)->set_solo_mute (!mute);
} else {
} else {
/* only alter bus solo mute */
-
- if (!dynamic_cast<AudioTrack*>((*i).get())) {
+
+ if (!boost::dynamic_pointer_cast<AudioTrack>(*i)) {
if ((*i)->soloed()) {
*/
update_route_solo_state();
}
-
+
+void
+Session::catch_up_on_solo_mute_override ()
+{
+ if (Config->get_solo_model() != InverseMute) {
+ return;
+ }
+
+ /* this is called whenever the param solo-mute-override is
+ changed.
+ */
+ shared_ptr<RouteList> r = routes.reader ();
+
+ for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
+ (*i)->catch_up_on_solo_mute_override ();
+ }
+}
+
shared_ptr<Route>
Session::route_by_name (string name)
{
Session::set_all_solo (bool yn)
{
shared_ptr<RouteList> r = routes.reader ();
-
+
for (RouteList::iterator i = r->begin(); i != r->end(); ++i) {
if (!(*i)->hidden()) {
(*i)->set_solo (yn, this);
boost::shared_ptr<Region>
Session::write_one_audio_track (AudioTrack& track, nframes_t start, nframes_t end,
- bool overwrite, vector<boost::shared_ptr<AudioSource> >& srcs, InterThreadInfo& itt)
+ bool overwrite, vector<boost::shared_ptr<AudioSource> >& srcs, InterThreadInfo& itt, bool enable_processing)
{
boost::shared_ptr<Region> result;
boost::shared_ptr<Playlist> playlist;
// any bigger than this seems to cause stack overflows in called functions
const nframes_t chunk_size = (128 * 1024)/4;
- g_atomic_int_set (&processing_prohibited, 1);
+ // block all process callback handling
+
+ block_processing ();
/* call tree *MUST* hold route_lock */
this_chunk = min (to_do, chunk_size);
- if (track.export_stuff (buffers, nchans, start, this_chunk)) {
+ if (track.export_stuff (buffers, nchans, start, this_chunk, enable_processing)) {
goto out;
}
free (*i);
}
- g_atomic_int_set (&processing_prohibited, 0);
+ unblock_processing ();
itt.done = true;