+void
+Session::queue_event (SessionEvent* ev)
+{
+ if (_state_of_the_state & Deletion) {
+ return;
+ } else if (_state_of_the_state & Loading) {
+ merge_event (ev);
+ } else {
+ pending_events.write (&ev, 1);
+ }
+}
+
+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 (SessionEvent* 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 != SessionEvent::Locate) {
+ immediate_events.insert (immediate_events.end(), ev);
+ _remove_event (ev);
+ return;
+ }
+ }
+
+ DEBUG_TRACE (DEBUG::SessionEvents, string_compose ("Processing event: %1 @ %2\n", enum_2_string (ev->type), _transport_frame));
+
+ switch (ev->type) {
+ case SessionEvent::SetLoop:
+ set_play_loop (ev->yes_or_no);
+ break;
+
+ case SessionEvent::AutoLoop:
+ if (play_loop) {
+ start_locate (ev->target_frame, true, false, Config->get_seamless_loop());
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case SessionEvent::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_timecode_update = true;
+ break;
+
+ case SessionEvent::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_timecode_update = true;
+ break;
+
+ case SessionEvent::LocateRollLocate:
+ // locate is handled by ::request_roll_at_and_return()
+ _requested_return_frame = ev->target_frame;
+ request_locate (ev->target2_frame, true);
+ break;
+
+
+ case SessionEvent::SetTransportSpeed:
+ set_transport_speed (ev->speed, ev->yes_or_no, ev->second_yes_or_no);
+ break;
+
+ case SessionEvent::PunchIn:
+ // cerr << "PunchIN at " << transport_frame() << endl;
+ if (config.get_punch_in() && record_status() == Enabled) {
+ enable_record ();
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case SessionEvent::PunchOut:
+ // cerr << "PunchOUT at " << transport_frame() << endl;
+ if (config.get_punch_out()) {
+ step_back_from_record ();
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case SessionEvent::StopOnce:
+ if (!non_realtime_work_pending()) {
+ stop_transport (ev->yes_or_no);
+ _clear_event_type (SessionEvent::StopOnce);
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case SessionEvent::RangeStop:
+ if (!non_realtime_work_pending()) {
+ stop_transport (ev->yes_or_no);
+ }
+ remove = false;
+ del = false;
+ break;
+
+ case SessionEvent::RangeLocate:
+ start_locate (ev->target_frame, true, true, false);
+ remove = false;
+ del = false;
+ break;
+
+ case SessionEvent::Overwrite:
+ overwrite_some_buffers (static_cast<Track*>(ev->ptr));
+ break;
+
+ case SessionEvent::SetTrackSpeed:
+ set_track_speed (static_cast<Track*> (ev->ptr), ev->speed);
+ break;
+
+ case SessionEvent::SetSyncSource:
+ use_sync_source (ev->slave);
+ break;
+
+ case SessionEvent::Audition:
+ set_audition (ev->region);
+ // drop reference to region
+ ev->region.reset ();
+ break;
+
+ case SessionEvent::InputConfigurationChange:
+ add_post_transport_work (PostTransportInputChange);
+ _butler->schedule_transport_work ();
+ break;
+
+ case SessionEvent::SetPlayAudioRange:
+ set_play_range (ev->audio_range, (ev->speed == 1.0f));
+ break;
+
+ case SessionEvent::RealTimeOperation:
+ process_rtop (ev);
+ del = false; // other side of RT request needs to clean up
+ break;
+
+ case SessionEvent::AdjustPlaybackBuffering:
+ schedule_playback_buffering_adjustment ();
+ break;
+
+ case SessionEvent::AdjustCaptureBuffering:
+ schedule_capture_buffering_adjustment ();
+ break;
+
+ case SessionEvent::SetTimecodeTransmission:
+ g_atomic_int_set (&_suspend_timecode_transmission, ev->yes_or_no ? 0 : 1);
+ 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;
+ }
+}
+
+framepos_t
+Session::compute_stop_limit () const
+{
+ if (!Config->get_stop_at_session_end ()) {
+ return max_framepos;
+ }
+
+ bool const punching_in = (config.get_punch_in () && _locations->auto_punch_location());
+ bool const punching_out = (config.get_punch_out () && _locations->auto_punch_location());
+
+ if (actively_recording ()) {
+ /* permanently recording */
+ return max_framepos;
+ } else if (punching_in && !punching_out) {
+ /* punching in but never out */
+ return max_framepos;
+ } else if (punching_in && punching_out && _locations->auto_punch_location()->end() > current_end_frame()) {
+ /* punching in and punching out after session end */
+ return max_framepos;
+ }
+
+ return current_end_frame ();
+}