diff options
Diffstat (limited to 'libs/ardour/session_events.cc')
-rw-r--r-- | libs/ardour/session_events.cc | 453 |
1 files changed, 453 insertions, 0 deletions
diff --git a/libs/ardour/session_events.cc b/libs/ardour/session_events.cc new file mode 100644 index 0000000000..5fc8cd7535 --- /dev/null +++ b/libs/ardour/session_events.cc @@ -0,0 +1,453 @@ +/* + Copyright (C) 1999-2004 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. + +*/ + +#include <cmath> +#include <unistd.h> + +#include <ardour/timestamps.h> + +#include <pbd/error.h> +#include <glibmm/thread.h> + +#include <ardour/ardour.h> +#include <ardour/session.h> +#include <ardour/audio_diskstream.h> + +#include "i18n.h" + +using namespace ARDOUR; +using namespace PBD; + +MultiAllocSingleReleasePool Session::Event::pool ("event", sizeof (Session::Event), 512); + +static const char* event_names[] = { + "SetTransportSpeed", + "SetDiskstreamSpeed", + "Locate", + "LocateRoll", + "LocateRollLocate", + "SetLoop", + "PunchIn", + "PunchOut", + "RangeStop", + "RangeLocate", + "Overwrite", + "SetSlaveSource", + "Audition", + "InputConfigurationChange", + "SetAudioRange", + "SetMusicRange", + "SetPlayRange", + "StopOnce", + "AutoLoop" +}; + +void +Session::add_event (nframes_t frame, Event::Type type, nframes_t target_frame) +{ + Event* ev = new Event (type, Event::Add, frame, target_frame, 0); + queue_event (ev); +} + +void +Session::remove_event (nframes_t frame, Event::Type type) +{ + Event* ev = new Event (type, Event::Remove, frame, 0, 0); + queue_event (ev); +} + +void +Session::replace_event (Event::Type type, nframes_t frame, nframes_t target) +{ + Event* ev = new Event (type, Event::Replace, frame, target, 0); + queue_event (ev); +} + +void +Session::clear_events (Event::Type type) +{ + Event* ev = new Event (type, Event::Clear, 0, 0, 0); + queue_event (ev); +} + + +void +Session::dump_events () const +{ + cerr << "EVENT DUMP" << endl; + for (Events::const_iterator i = events.begin(); i != events.end(); ++i) { + cerr << "\tat " << (*i)->action_frame << ' ' << (*i)->type << " target = " << (*i)->target_frame << endl; + } + cerr << "Next event: "; + + if ((Events::const_iterator) next_event == events.end()) { + cerr << "none" << endl; + } else { + cerr << "at " << (*next_event)->action_frame << ' ' + << (*next_event)->type << " target = " + << (*next_event)->target_frame << endl; + } + cerr << "Immediate events pending:\n"; + for (Events::const_iterator i = immediate_events.begin(); i != immediate_events.end(); ++i) { + cerr << "\tat " << (*i)->action_frame << ' ' << (*i)->type << " target = " << (*i)->target_frame << endl; + } + cerr << "END EVENT_DUMP" << endl; +} + +void +Session::queue_event (Event* ev) +{ + if (_state_of_the_state & Loading) { + merge_event (ev); + } else { + pending_events.write (&ev, 1); + } +} + +void +Session::merge_event (Event* ev) +{ + switch (ev->action) { + case Event::Remove: + _remove_event (ev); + delete ev; + return; + + case Event::Replace: + _replace_event (ev); + return; + + case Event::Clear: + _clear_event_type (ev->type); + delete ev; + return; + + case Event::Add: + break; + } + + /* try to handle immediate events right here */ + + if (ev->action_frame == 0) { + process_event (ev); + return; + } + + switch (ev->type) { + case Event::AutoLoop: + case Event::StopOnce: + _clear_event_type (ev->type); + break; + + default: + for (Events::iterator i = events.begin(); i != events.end(); ++i) { + if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) { + error << string_compose(_("Session: cannot have two events of type %1 at the same frame (%2)."), + event_names[ev->type], ev->action_frame) << endmsg; + return; + } + } + } + + events.insert (events.begin(), ev); + events.sort (Event::compare); + next_event = events.begin(); + set_next_event (); +} + +/** @return true when @a ev is deleted. */ +bool +Session::_replace_event (Event* ev) +{ + bool ret = false; + Events::iterator i; + + /* private, used only for events that can only exist once in the queue */ + + for (i = events.begin(); i != events.end(); ++i) { + if ((*i)->type == ev->type) { + (*i)->action_frame = ev->action_frame; + (*i)->target_frame = ev->target_frame; + if ((*i) == ev) { + ret = true; + } + delete ev; + break; + } + } + + if (i == events.end()) { + events.insert (events.begin(), ev); + } + + events.sort (Event::compare); + next_event = events.end(); + set_next_event (); + + return ret; +} + +/** @return true when @a ev is deleted. */ +bool +Session::_remove_event (Session::Event* ev) +{ + bool ret = false; + Events::iterator i; + + for (i = events.begin(); i != events.end(); ++i) { + if ((*i)->type == ev->type && (*i)->action_frame == ev->action_frame) { + if ((*i) == ev) { + ret = true; + } + + delete *i; + if (i == next_event) { + ++next_event; + } + events.erase (i); + break; + } + } + + if (i != events.end()) { + set_next_event (); + } + + return ret; +} + +void +Session::_clear_event_type (Event::Type type) +{ + Events::iterator i, tmp; + + for (i = events.begin(); i != events.end(); ) { + + tmp = i; + ++tmp; + + if ((*i)->type == type) { + delete *i; + if (i == next_event) { + ++next_event; + } + events.erase (i); + } + + i = tmp; + } + + for (i = immediate_events.begin(); i != immediate_events.end(); ) { + + tmp = i; + ++tmp; + + if ((*i)->type == type) { + delete *i; + immediate_events.erase (i); + } + + i = tmp; + } + + set_next_event (); +} + +void +Session::set_next_event () +{ + if (events.empty()) { + next_event = events.end(); + return; + } + + if (next_event == events.end()) { + next_event = events.begin(); + } + + if ((*next_event)->action_frame > _transport_frame) { + next_event = events.begin(); + } + + for (; next_event != events.end(); ++next_event) { + if ((*next_event)->action_frame >= _transport_frame) { + break; + } + } +} + +void +Session::process_event (Event* ev) +{ + bool remove = true; + bool del = true; + + /* if we're in the middle of a state change (i.e. waiting + for the butler thread to complete the non-realtime + part of the change), we'll just have to queue this + event for a time when the change is complete. + */ + + if (non_realtime_work_pending()) { + + /* except locates, which we have the capability to handle */ + + if (ev->type != Event::Locate) { + immediate_events.insert (immediate_events.end(), ev); + _remove_event (ev); + return; + } + } + + //printf("Processing event: %s\n", event_names[ev->type]); + + switch (ev->type) { + case Event::SetLoop: + set_play_loop (ev->yes_or_no); + break; + + case Event::AutoLoop: + if (play_loop) { + start_locate (ev->target_frame, true, false, Config->get_seamless_loop()); + } + remove = false; + del = false; + break; + + case Event::Locate: + if (ev->yes_or_no) { + // cerr << "forced locate to " << ev->target_frame << endl; + locate (ev->target_frame, false, true, false); + } else { + // cerr << "soft locate to " << ev->target_frame << endl; + start_locate (ev->target_frame, false, true, false); + } + _send_smpte_update = true; + break; + + case Event::LocateRoll: + if (ev->yes_or_no) { + // cerr << "forced locate to+roll " << ev->target_frame << endl; + locate (ev->target_frame, true, true, false); + } else { + // cerr << "soft locate to+roll " << ev->target_frame << endl; + start_locate (ev->target_frame, true, true, false); + } + _send_smpte_update = true; + break; + + case Event::LocateRollLocate: + // locate is handled by ::request_roll_at_and_return() + _requested_return_frame = ev->target_frame; + cerr << "Set RRF " << ev->target_frame << endl; + request_locate (ev->target2_frame, true); + break; + + + case Event::SetTransportSpeed: + set_transport_speed (ev->speed, ev->yes_or_no); + break; + + case Event::PunchIn: + // cerr << "PunchIN at " << transport_frame() << endl; + if (Config->get_punch_in() && record_status() == Enabled) { + enable_record (); + } + remove = false; + del = false; + break; + + case Event::PunchOut: + // cerr << "PunchOUT at " << transport_frame() << endl; + if (Config->get_punch_out()) { + step_back_from_record (); + } + remove = false; + del = false; + break; + + case Event::StopOnce: + if (!non_realtime_work_pending()) { + stop_transport (ev->yes_or_no); + _clear_event_type (Event::StopOnce); + } + remove = false; + del = false; + break; + + case Event::RangeStop: + if (!non_realtime_work_pending()) { + stop_transport (ev->yes_or_no); + } + remove = false; + del = false; + break; + + case Event::RangeLocate: + start_locate (ev->target_frame, true, true, false); + remove = false; + del = false; + break; + + case Event::Overwrite: + overwrite_some_buffers (static_cast<Diskstream*>(ev->ptr)); + break; + + case Event::SetDiskstreamSpeed: + set_diskstream_speed (static_cast<Diskstream*> (ev->ptr), ev->speed); + break; + + case Event::SetSlaveSource: + set_slave_source (ev->slave); + break; + + case Event::Audition: + set_audition (ev->region); + // drop reference to region + ev->region.reset (); + break; + + case Event::InputConfigurationChange: + post_transport_work = PostTransportWork (post_transport_work | PostTransportInputChange); + schedule_butler_transport_work (); + break; + + case Event::SetAudioRange: + current_audio_range = ev->audio_range; + setup_auto_play (); + break; + + case Event::SetPlayRange: + set_play_range (ev->yes_or_no); + break; + + default: + fatal << string_compose(_("Programming error: illegal event type in process_event (%1)"), ev->type) << endmsg; + /*NOTREACHED*/ + break; + }; + + if (remove) { + del = del && !_remove_event (ev); + } + + if (del) { + delete ev; + } +} |