diff options
Diffstat (limited to 'libs/ardour/audio_track.cc')
-rw-r--r-- | libs/ardour/audio_track.cc | 1063 |
1 files changed, 1063 insertions, 0 deletions
diff --git a/libs/ardour/audio_track.cc b/libs/ardour/audio_track.cc new file mode 100644 index 0000000000..b0c1d96e7b --- /dev/null +++ b/libs/ardour/audio_track.cc @@ -0,0 +1,1063 @@ +/* + Copyright (C) 2002 Paul Davis + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + $Id$ +*/ +#include <pbd/error.h> +#include <sigc++/retype.h> +#include <sigc++/retype_return.h> +#include <sigc++/bind.h> + +#include <ardour/audio_track.h> +#include <ardour/diskstream.h> +#include <ardour/session.h> +#include <ardour/redirect.h> +#include <ardour/audioregion.h> +#include <ardour/route_group_specialized.h> +#include <ardour/insert.h> +#include <ardour/audioplaylist.h> +#include <ardour/panner.h> +#include <ardour/utils.h> + + + +#include "i18n.h" + +using namespace std; +//using namespace sigc; +using namespace ARDOUR; + +AudioTrack::AudioTrack (Session& sess, string name, Route::Flag flag) + : Route (sess, name, 1, -1, -1, -1, flag), + diskstream (0), + _midi_rec_enable_control (*this, _session.midi_port()) +{ + DiskStream::Flag dflags = DiskStream::Flag (0); + + if (_flags & Hidden) { + dflags = DiskStream::Flag (dflags | DiskStream::Hidden); + } else { + dflags = DiskStream::Flag (dflags | DiskStream::Recordable); + } + + DiskStream* ds = new DiskStream (_session, name, dflags); + + set_diskstream (*ds, this); + + _declickable = true; + _freeze_record.state = NoFreeze; + _saved_meter_point = _meter_point; + + // we do this even though Route already did it in it's init + reset_midi_control (_session.midi_port(), _session.get_midi_control()); + +} + +AudioTrack::AudioTrack (Session& sess, const XMLNode& node) + : Route (sess, "to be renamed", 0, 0, -1, -1), + diskstream (0), + _midi_rec_enable_control (*this, _session.midi_port()) +{ + _freeze_record.state = NoFreeze; + set_state (node); + _declickable = true; + _saved_meter_point = _meter_point; + + // we do this even though Route already did it in it's init + reset_midi_control (_session.midi_port(), _session.get_midi_control()); +} + +AudioTrack::~AudioTrack () +{ + if (diskstream) { + diskstream->unref(); + } +} + +int +AudioTrack::deprecated_use_diskstream_connections () +{ + if (diskstream->deprecated_io_node == 0) { + return 0; + } + + const XMLProperty* prop; + XMLNode& node (*diskstream->deprecated_io_node); + + /* don't do this more than once. */ + + diskstream->deprecated_io_node = 0; + + set_input_minimum (-1); + set_input_maximum (-1); + set_output_minimum (-1); + set_output_maximum (-1); + + if ((prop = node.property ("gain")) != 0) { + set_gain (atof (prop->value().c_str()), this); + _gain = _desired_gain; + } + + if ((prop = node.property ("input-connection")) != 0) { + Connection* c = _session.connection_by_name (prop->value()); + + if (c == 0) { + error << compose(_("Unknown connection \"%1\" listed for input of %2"), prop->value(), _name) << endmsg; + + if ((c = _session.connection_by_name (_("in 1"))) == 0) { + error << _("No input connections available as a replacement") + << endmsg; + return -1; + } else { + info << compose (_("Connection %1 was not available - \"in 1\" used instead"), prop->value()) + << endmsg; + } + } + + use_input_connection (*c, this); + + } else if ((prop = node.property ("inputs")) != 0) { + if (set_inputs (prop->value())) { + error << compose(_("improper input channel list in XML node (%1)"), prop->value()) << endmsg; + return -1; + } + } + + return 0; +} + +int +AudioTrack::set_diskstream (DiskStream& ds, void *src) +{ + if (diskstream) { + diskstream->unref(); + } + + diskstream = &ds.ref(); + diskstream->set_io (*this); + + if (diskstream->deprecated_io_node) { + + if (!connecting_legal) { + ConnectingLegal.connect (mem_fun (*this, &AudioTrack::deprecated_use_diskstream_connections)); + } else { + deprecated_use_diskstream_connections (); + } + } + + diskstream->set_record_enabled (false, this); + diskstream->monitor_input (false); + + ic_connection.disconnect(); + ic_connection = input_changed.connect (mem_fun (*diskstream, &DiskStream::handle_input_change)); + + diskstream_changed (src); /* EMIT SIGNAL */ + + return 0; +} + +int +AudioTrack::use_diskstream (string name) +{ + DiskStream *dstream; + + if ((dstream = _session.diskstream_by_name (name)) == 0) { + error << compose(_("AudioTrack: diskstream \"%1\" not known by session"), name) << endmsg; + return -1; + } + + return set_diskstream (*dstream, this); +} + +int +AudioTrack::use_diskstream (id_t id) +{ + DiskStream *dstream; + + if ((dstream = _session.diskstream_by_id (id)) == 0) { + error << compose(_("AudioTrack: diskstream \"%1\" not known by session"), id) << endmsg; + return -1; + } + + return set_diskstream (*dstream, this); +} + +bool +AudioTrack::record_enabled () const +{ + return diskstream->record_enabled (); +} + +void +AudioTrack::set_record_enable (bool yn, void *src) +{ + if (_freeze_record.state == Frozen) { + return; + } + + if (_mix_group && src != _mix_group && _mix_group->is_active()) { + _mix_group->apply (&AudioTrack::set_record_enable, yn, _mix_group); + return; + } + + /* keep track of the meter point as it was before we rec-enabled */ + + if (!diskstream->record_enabled()) { + _saved_meter_point = _meter_point; + } + + diskstream->set_record_enabled (yn, src); + + if (diskstream->record_enabled()) { + set_meter_point (MeterInput, this); + } else { + set_meter_point (_saved_meter_point, this); + } +} + +void +AudioTrack::set_meter_point (MeterPoint p, void *src) +{ + Route::set_meter_point (p, src); +} + +XMLNode& +AudioTrack::state(bool full_state) +{ + XMLNode& track_state (Route::state (full_state)); + char buf[64]; + + /* we don't return diskstream state because we don't + own the diskstream exclusively. control of the diskstream + state is ceded to the Session, even if we create the + diskstream. + */ + + snprintf (buf, sizeof (buf), "%" PRIu64, diskstream->id()); + track_state.add_property ("diskstream-id", buf); + + return track_state; +} + +int +AudioTrack::set_state (const XMLNode& node) +{ + const XMLProperty *prop; + XMLNodeConstIterator iter; + XMLNodeList midi_kids; + + if (Route::set_state (node)) { + return -1; + } + + midi_kids = node.children ("MIDI"); + + for (iter = midi_kids.begin(); iter != midi_kids.end(); ++iter) { + + XMLNodeList kids; + XMLNodeConstIterator miter; + XMLNode* child; + + kids = (*iter)->children (); + + for (miter = kids.begin(); miter != kids.end(); ++miter) { + + child =* miter; + + if (child->name() == "rec_enable") { + + MIDI::eventType ev = MIDI::on; /* initialize to keep gcc happy */ + MIDI::byte additional = 0; /* ditto */ + MIDI::channel_t chn = 0; /* ditto */ + + if (get_midi_node_info (child, ev, chn, additional)) { + _midi_rec_enable_control.set_control_type (chn, ev, additional); + } else { + error << compose(_("MIDI rec_enable control specification for %1 is incomplete, so it has been ignored"), _name) << endmsg; + } + } + } + } + + + if ((prop = node.property ("diskstream-id")) == 0) { + + /* some old sessions use the diskstream name rather than the ID */ + + if ((prop = node.property ("diskstream")) == 0) { + fatal << _("programming error: AudioTrack given state without diskstream!") << endmsg; + /*NOTREACHED*/ + return -1; + } + + if (use_diskstream (prop->value())) { + return -1; + } + + } else { + + id_t id = strtoull (prop->value().c_str(), 0, 10); + + if (use_diskstream (id)) { + return -1; + } + } + + pending_state = const_cast<XMLNode*> (&node); + + _session.StateReady.connect (mem_fun (*this, &AudioTrack::set_state_part_two)); + + return 0; +} + +XMLNode& +AudioTrack::get_state() +{ + XMLNode& root (Route::get_state()); + XMLNode* freeze_node; + char buf[32]; + + if (_freeze_record.playlist) { + XMLNode* inode; + + freeze_node = new XMLNode (X_("freeze-info")); + freeze_node->add_property ("playlist", _freeze_record.playlist->name()); + snprintf (buf, sizeof (buf), "%d", (int) _freeze_record.state); + freeze_node->add_property ("state", buf); + + for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) { + inode = new XMLNode (X_("insert")); + snprintf (buf, sizeof (buf), "%" PRIu64, (*i)->id); + inode->add_property (X_("id"), buf); + inode->add_child_copy ((*i)->state); + + freeze_node->add_child_nocopy (*inode); + } + + root.add_child_nocopy (*freeze_node); + } + + /* Alignment: act as a proxy for the diskstream */ + + XMLNode* align_node = new XMLNode (X_("alignment")); + switch (diskstream->alignment_style()) { + case ExistingMaterial: + snprintf (buf, sizeof (buf), X_("existing")); + break; + case CaptureTime: + snprintf (buf, sizeof (buf), X_("capture")); + break; + } + align_node->add_property (X_("style"), buf); + root.add_child_nocopy (*align_node); + + /* MIDI control */ + + MIDI::channel_t chn; + MIDI::eventType ev; + MIDI::byte additional; + XMLNode* midi_node = 0; + XMLNode* child; + XMLNodeList midikids; + + midikids = root.children ("MIDI"); + if (!midikids.empty()) { + midi_node = midikids.front(); + } + else { + midi_node = root.add_child ("MIDI"); + } + + if (_midi_rec_enable_control.get_control_info (chn, ev, additional) && midi_node) { + + child = midi_node->add_child ("rec_enable"); + set_midi_node_info (child, ev, chn, additional); + } + + + return root; +} + +void +AudioTrack::set_state_part_two () +{ + XMLNode* fnode; + XMLProperty* prop; + LocaleGuard lg (X_("POSIX")); + + /* This is called after all session state has been restored but before + have been made ports and connections are established. + */ + + if (pending_state == 0) { + return; + } + + if ((fnode = find_named_node (*pending_state, X_("freeze-info"))) != 0) { + + + _freeze_record.have_mementos = false; + _freeze_record.state = Frozen; + + for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) { + delete *i; + } + _freeze_record.insert_info.clear (); + + if ((prop = fnode->property (X_("playlist"))) != 0) { + Playlist* pl = _session.playlist_by_name (prop->value()); + if (pl) { + _freeze_record.playlist = dynamic_cast<AudioPlaylist*> (pl); + } else { + _freeze_record.playlist = 0; + _freeze_record.state = NoFreeze; + return; + } + } + + if ((prop = fnode->property (X_("state"))) != 0) { + _freeze_record.state = (FreezeState) atoi (prop->value().c_str()); + } + + XMLNodeConstIterator citer; + XMLNodeList clist = fnode->children(); + + for (citer = clist.begin(); citer != clist.end(); ++citer) { + if ((*citer)->name() != X_("insert")) { + continue; + } + + if ((prop = (*citer)->property (X_("id"))) == 0) { + continue; + } + + FreezeRecordInsertInfo* frii = new FreezeRecordInsertInfo (*((*citer)->children().front())); + frii->insert = 0; + sscanf (prop->value().c_str(), "%llu", &frii->id); + _freeze_record.insert_info.push_back (frii); + } + } + + /* Alignment: act as a proxy for the diskstream */ + + if ((fnode = find_named_node (*pending_state, X_("alignment"))) != 0) { + + if ((prop = fnode->property (X_("style"))) != 0) { + if (prop->value() == "existing") { + diskstream->set_persistent_align_style (ExistingMaterial); + } else if (prop->value() == "capture") { + diskstream->set_persistent_align_style (CaptureTime); + } + } + } + return; +} + +uint32_t +AudioTrack::n_process_buffers () +{ + return max ((uint32_t) diskstream->n_channels(), redirect_max_outs); +} + +void +AudioTrack::passthru_silence (jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t nframes, jack_nframes_t offset, int declick, bool meter) +{ + uint32_t nbufs = n_process_buffers (); + process_output_buffers (_session.get_silent_buffers (nbufs), nbufs, start_frame, end_frame, nframes, offset, true, declick, meter); +} + +int +AudioTrack::no_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, + bool session_state_changing, bool can_record, bool rec_monitors_input) +{ + if (n_outputs() == 0) { + return 0; + } + + if (!_active) { + silence (nframes, offset); + return 0; + } + + if (session_state_changing) { + + /* XXX is this safe to do against transport state changes? */ + + passthru_silence (start_frame, end_frame, nframes, offset, 0, false); + return 0; + } + + diskstream->check_record_status (start_frame, nframes, can_record); + + bool send_silence; + + if (_have_internal_generator) { + /* since the instrument has no input streams, + there is no reason to send any signal + into the route. + */ + send_silence = true; + } else { + + if (_session.get_auto_input()) { + if (Config->get_no_sw_monitoring()) { + send_silence = true; + } else { + send_silence = false; + } + } else { + if (diskstream->record_enabled()) { + if (Config->get_no_sw_monitoring()) { + send_silence = true; + } else { + send_silence = false; + } + } else { + send_silence = true; + } + } + } + + apply_gain_automation = false; + + if (send_silence) { + + /* if we're sending silence, but we want the meters to show levels for the signal, + meter right here. + */ + + if (_have_internal_generator) { + passthru_silence (start_frame, end_frame, nframes, offset, 0, true); + } else { + if (_meter_point == MeterInput) { + just_meter_input (start_frame, end_frame, nframes, offset); + } + passthru_silence (start_frame, end_frame, nframes, offset, 0, false); + } + + } else { + + /* we're sending signal, but we may still want to meter the input. + */ + + passthru (start_frame, end_frame, nframes, offset, 0, (_meter_point == MeterInput)); + } + + return 0; +} + +int +AudioTrack::roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, int declick, + bool can_record, bool rec_monitors_input) +{ + int dret; + Sample* b; + Sample* tmpb; + jack_nframes_t transport_frame; + + automation_snapshot (start_frame); + + if (n_outputs() == 0 && _redirects.empty()) { + return 0; + } + + if (!_active) { + silence (nframes, offset); + return 0; + } + + transport_frame = _session.transport_frame(); + + if ((nframes = check_initial_delay (nframes, offset, transport_frame)) == 0) { + /* need to do this so that the diskstream sets its + playback distance to zero, thus causing diskstream::commit + to do nothing. + */ + return diskstream->process (transport_frame, 0, 0, can_record, rec_monitors_input); + } + + _silent = false; + apply_gain_automation = false; + + if ((dret = diskstream->process (transport_frame, nframes, offset, can_record, rec_monitors_input)) != 0) { + + silence (nframes, offset); + + return dret; + } + + /* special condition applies */ + + if (_meter_point == MeterInput) { + just_meter_input (start_frame, end_frame, nframes, offset); + } + + if (diskstream->record_enabled() && !can_record && !_session.get_auto_input()) { + + /* not actually recording, but we want to hear the input material anyway, + at least potentially (depending on monitoring options) + */ + + passthru (start_frame, end_frame, nframes, offset, 0, true); + + } else if ((b = diskstream->playback_buffer(0)) != 0) { + + /* + XXX is it true that the earlier test on n_outputs() + means that we can avoid checking it again here? i think + so, because changing the i/o configuration of an IO + requires holding the AudioEngine lock, which we hold + while in the process() tree. + */ + + + /* copy the diskstream data to all output buffers */ + + vector<Sample*>& bufs = _session.get_passthru_buffers (); + uint32_t limit = n_process_buffers (); + + uint32_t n; + uint32_t i; + + + for (i = 0, n = 1; i < limit; ++i, ++n) { + memcpy (bufs[i], b, sizeof (Sample) * nframes); + if (n < diskstream->n_channels()) { + tmpb = diskstream->playback_buffer(n); + if (tmpb!=0) { + b = tmpb; + } + } + } + + /* don't waste time with automation if we're recording */ + + if (!diskstream->record_enabled()) { + + if (gain_automation_playback()) { + apply_gain_automation = _gain_automation_curve.rt_safe_get_vector (start_frame, end_frame, _session.gain_automation_buffer(), nframes); + } + } + + process_output_buffers (bufs, limit, start_frame, end_frame, nframes, offset, (!_session.get_record_enabled() || _session.get_recording_plugins()), declick, (_meter_point != MeterInput)); + + } else { + /* problem with the diskstream; just be quiet for a bit */ + silence (nframes, offset); + } + + return 0; +} + +int +AudioTrack::silent_roll (jack_nframes_t nframes, jack_nframes_t start_frame, jack_nframes_t end_frame, jack_nframes_t offset, + bool can_record, bool rec_monitors_input) +{ + if (n_outputs() == 0 && _redirects.empty()) { + return 0; + } + + if (!_active) { + silence (nframes, offset); + return 0; + } + + _silent = true; + apply_gain_automation = false; + + silence (nframes, offset); + + return diskstream->process (_session.transport_frame() + offset, nframes, offset, can_record, rec_monitors_input); +} + +void +AudioTrack::toggle_monitor_input () +{ + for (vector<Port*>::iterator i = _inputs.begin(); i != _inputs.end(); ++i) { + (*i)->request_monitor_input(!(*i)->monitoring_input()); + } +} + +int +AudioTrack::set_name (string str, void *src) +{ + if (record_enabled() && _session.actively_recording()) { + /* this messes things up if done while recording */ + return -1; + } + + diskstream->set_name (str, src); + return IO::set_name (str, src); +} + +int +AudioTrack::export_stuff (vector<Sample*>& buffers, uint32_t nbufs, jack_nframes_t start, jack_nframes_t nframes) +{ + gain_t gain_automation[nframes]; + gain_t gain_buffer[nframes]; + float mix_buffer[nframes]; + RedirectList::iterator i; + bool post_fader_work = false; + gain_t this_gain = _gain; + vector<Sample*>::iterator bi; + Sample * b; + + LockMonitor rlock (redirect_lock, __LINE__, __FILE__); + + if (diskstream->playlist()->read (buffers[0], mix_buffer, gain_buffer, start, nframes) != nframes) { + return -1; + } + + uint32_t n=1; + bi = buffers.begin(); + b = buffers[0]; + ++bi; + for (; bi != buffers.end(); ++bi, ++n) { + if (n < diskstream->n_channels()) { + if (diskstream->playlist()->read ((*bi), mix_buffer, gain_buffer, start, nframes, n) != nframes) { + return -1; + } + b = (*bi); + } + else { + /* duplicate last across remaining buffers */ + memcpy ((*bi), b, sizeof (Sample) * nframes); + } + } + + + /* note: only run inserts during export. other layers in the machinery + will already have checked that there are no external port inserts. + */ + + for (i = _redirects.begin(); i != _redirects.end(); ++i) { + Insert *insert; + + if ((insert = dynamic_cast<Insert*>(*i)) != 0) { + switch (insert->placement()) { + case PreFader: + insert->run (buffers, nbufs, nframes, 0); + break; + case PostFader: + post_fader_work = true; + break; + } + } + } + + if (_gain_automation_curve.automation_state() == Play) { + + _gain_automation_curve.get_vector (start, start + nframes, gain_automation, nframes); + + for (bi = buffers.begin(); bi != buffers.end(); ++bi) { + Sample *b = *bi; + for (jack_nframes_t n = 0; n < nframes; ++n) { + b[n] *= gain_automation[n]; + } + } + + } else { + + for (bi = buffers.begin(); bi != buffers.end(); ++bi) { + Sample *b = *bi; + for (jack_nframes_t n = 0; n < nframes; ++n) { + b[n] *= this_gain; + } + } + } + + if (post_fader_work) { + + for (i = _redirects.begin(); i != _redirects.end(); ++i) { + PluginInsert *insert; + + if ((insert = dynamic_cast<PluginInsert*>(*i)) != 0) { + switch ((*i)->placement()) { + case PreFader: + break; + case PostFader: + insert->run (buffers, nbufs, nframes, 0); + break; + } + } + } + } + + return 0; +} + +void +AudioTrack::set_latency_delay (jack_nframes_t longest_session_latency) +{ + Route::set_latency_delay (longest_session_latency); + diskstream->set_roll_delay (_roll_delay); +} + +jack_nframes_t +AudioTrack::update_total_latency () +{ + _own_latency = 0; + + for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + if ((*i)->active ()) { + _own_latency += (*i)->latency (); + } + } + + set_port_latency (_own_latency); + + return _own_latency; +} + +void +AudioTrack::bounce (InterThreadInfo& itt) +{ + vector<Source*> srcs; + _session.write_one_track (*this, 0, _session.current_end_frame(), false, srcs, itt); +} + + +void +AudioTrack::bounce_range (jack_nframes_t start, jack_nframes_t end, InterThreadInfo& itt) +{ + vector<Source*> srcs; + _session.write_one_track (*this, start, end, false, srcs, itt); +} + +void +AudioTrack::freeze (InterThreadInfo& itt) +{ + Insert* insert; + vector<Source*> srcs; + string new_playlist_name; + Playlist* new_playlist; + string dir; + AudioRegion* region; + string region_name; + + if ((_freeze_record.playlist = diskstream->playlist()) == 0) { + return; + } + + uint32_t n = 1; + + while (n < ULONG_MAX) { + + string candidate; + + candidate = compose ("<F%2>%1", _freeze_record.playlist->name(), n); + + if (_session.playlist_by_name (candidate) == 0) { + new_playlist_name = candidate; + break; + } + + ++n; + + } + + if (n == ULONG_MAX) { + error << compose (X_("There Are too many frozen versions of playlist \"%1\"" + " to create another one"), _freeze_record.playlist->name()) + << endmsg; + return; + } + + if (_session.write_one_track (*this, 0, _session.current_end_frame(), true, srcs, itt)) { + return; + } + + _freeze_record.insert_info.clear (); + _freeze_record.have_mementos = true; + + { + LockMonitor lm (redirect_lock, __LINE__, __FILE__); + + for (RedirectList::iterator r = _redirects.begin(); r != _redirects.end(); ++r) { + + if ((insert = dynamic_cast<Insert*>(*r)) != 0) { + + FreezeRecordInsertInfo* frii = new FreezeRecordInsertInfo ((*r)->get_state()); + + frii->insert = insert; + frii->id = insert->id(); + frii->memento = (*r)->get_memento(); + + _freeze_record.insert_info.push_back (frii); + + /* now deactivate the insert */ + + insert->set_active (false, this); + } + } + } + + new_playlist = new AudioPlaylist (_session, new_playlist_name, false); + region_name = new_playlist_name; + + /* create a new region from all filesources, keep it private */ + + region = new AudioRegion (srcs, 0, srcs[0]->length(), + region_name, 0, + (AudioRegion::Flag) (AudioRegion::WholeFile|AudioRegion::DefaultFlags), + false); + + new_playlist->set_orig_diskstream_id (diskstream->id()); + new_playlist->add_region (*region, 0); + new_playlist->set_frozen (true); + region->set_locked (true); + + diskstream->use_playlist (dynamic_cast<AudioPlaylist*>(new_playlist)); + diskstream->set_record_enabled (false, this); + + _freeze_record.state = Frozen; + FreezeChange(); /* EMIT SIGNAL */ +} + +void +AudioTrack::unfreeze () +{ + if (_freeze_record.playlist) { + diskstream->use_playlist (_freeze_record.playlist); + + if (_freeze_record.have_mementos) { + + for (vector<FreezeRecordInsertInfo*>::iterator i = _freeze_record.insert_info.begin(); i != _freeze_record.insert_info.end(); ++i) { + (*i)->memento (); + } + + } else { + + LockMonitor lm (redirect_lock, __LINE__, __FILE__); + for (RedirectList::iterator i = _redirects.begin(); i != _redirects.end(); ++i) { + for (vector<FreezeRecordInsertInfo*>::iterator ii = _freeze_record.insert_info.begin(); ii != _freeze_record.insert_info.end(); ++ii) { + if ((*ii)->id == (*i)->id()) { + (*i)->set_state (((*ii)->state)); + break; + } + } + } + } + + _freeze_record.playlist = 0; + } + + _freeze_record.state = UnFrozen; + FreezeChange (); /* EMIT SIGNAL */ +} + +AudioTrack::FreezeRecord::~FreezeRecord () +{ + for (vector<FreezeRecordInsertInfo*>::iterator i = insert_info.begin(); i != insert_info.end(); ++i) { + delete *i; + } +} + +AudioTrack::FreezeState +AudioTrack::freeze_state() const +{ + return _freeze_record.state; +} + + +void +AudioTrack::reset_midi_control (MIDI::Port* port, bool on) +{ + MIDI::channel_t chn; + MIDI::eventType ev; + MIDI::byte extra; + + Route::reset_midi_control (port, on); + + _midi_rec_enable_control.get_control_info (chn, ev, extra); + if (!on) { + chn = -1; + } + _midi_rec_enable_control.midi_rebind (port, chn); +} + +void +AudioTrack::send_all_midi_feedback () +{ + if (_session.get_midi_feedback()) { + + Route::send_all_midi_feedback(); + + _midi_rec_enable_control.send_feedback (record_enabled()); + } +} + + +AudioTrack::MIDIRecEnableControl::MIDIRecEnableControl (AudioTrack& s, MIDI::Port* port) + : MIDI::Controllable (port, 0), track (s), setting(false) +{ + last_written = false; /* XXX need a good out of bound value */ +} + +void +AudioTrack::MIDIRecEnableControl::set_value (float val) +{ + bool bval = ((val >= 0.5f) ? true: false); + + setting = true; + track.set_record_enable (bval, this); + setting = false; +} + +void +AudioTrack::MIDIRecEnableControl::send_feedback (bool value) +{ + + if (!setting && get_midi_feedback()) { + MIDI::byte val = (MIDI::byte) (value ? 127: 0); + MIDI::channel_t ch = 0; + MIDI::eventType ev = MIDI::none; + MIDI::byte additional = 0; + MIDI::EventTwoBytes data; + + if (get_control_info (ch, ev, additional)) { + data.controller_number = additional; + data.value = val; + + track._session.send_midi_message (get_port(), ev, ch, data); + } + } + +} + +MIDI::byte* +AudioTrack::MIDIRecEnableControl::write_feedback (MIDI::byte* buf, int32_t& bufsize, bool val, bool force) +{ + if (get_midi_feedback()) { + + MIDI::channel_t ch = 0; + MIDI::eventType ev = MIDI::none; + MIDI::byte additional = 0; + + if (get_control_info (ch, ev, additional)) { + if (val != last_written || force) { + *buf++ = ev & ch; + *buf++ = additional; /* controller number */ + *buf++ = (MIDI::byte) (val ? 127: 0); + last_written = val; + bufsize -= 3; + } + } + } + + return buf; +} |