the return of VST support
[ardour.git] / libs / ardour / session_process.cc
1 /*
2     Copyright (C) 1999-2002 Paul Davis 
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <cmath>
22 #include <cerrno>
23 #include <algorithm>
24 #include <unistd.h>
25
26 #include <pbd/error.h>
27  
28 #include <glibmm/thread.h>
29
30 #include <ardour/ardour.h>
31 #include <ardour/session.h>
32 #include <ardour/timestamps.h>
33 #include <ardour/audio_diskstream.h>
34 #include <ardour/audioengine.h>
35 #include <ardour/slave.h>
36 #include <ardour/auditioner.h>
37 #include <ardour/cycles.h>
38 #include <ardour/cycle_timer.h>
39
40 #include "i18n.h"
41
42 using namespace ARDOUR;
43 using namespace PBD;
44 using namespace std;
45
46 void
47 Session::process (jack_nframes_t nframes)
48 {
49         if (synced_to_jack() && waiting_to_start) {
50                 if ( _engine.transport_state() == AudioEngine::TransportRolling) {
51                         actually_start_transport ();
52                 }
53         }
54
55         if (non_realtime_work_pending()) {
56                 if (g_atomic_int_get (&butler_should_do_transport_work) == 0) {
57                         post_transport ();
58                 } 
59         } 
60         
61         (this->*process_function) (nframes);
62 }
63
64 void
65 Session::prepare_diskstreams ()
66 {
67         for (AudioDiskstreamList::iterator i = audio_diskstreams.begin(); i != audio_diskstreams.end(); ++i) {
68                 (*i)->prepare ();
69         }
70 }
71
72 int
73 Session::no_roll (jack_nframes_t nframes, jack_nframes_t offset)
74 {
75         jack_nframes_t end_frame = _transport_frame + nframes;
76         int ret = 0;
77         bool declick = get_transport_declick_required();
78
79         if (_click_io) {
80                 _click_io->silence (nframes, offset);
81         }
82
83         /* XXX we're supposed to have the route_lock while doing this.
84            this is really bad ...
85         */
86
87         if (g_atomic_int_get (&processing_prohibited)) {
88                 for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
89                         (*i)->silence (nframes, offset);
90                 }
91                 return 0;
92         }
93
94         for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
95                 
96                 if ((*i)->hidden()) {
97                         continue;
98                 }
99                 
100                 (*i)->set_pending_declick (declick);
101                 
102                 if ((*i)->no_roll (nframes, _transport_frame, end_frame, offset, non_realtime_work_pending(), 
103                                    actively_recording(), declick)) {
104                         error << string_compose(_("Session: error in no roll for %1"), (*i)->name()) << endmsg;
105                         ret = -1;
106                         break;
107                 }
108         }
109
110         return ret;
111 }
112
113 int
114 Session::process_routes (jack_nframes_t nframes, jack_nframes_t offset)
115 {
116         bool record_active;
117         int  declick = get_transport_declick_required();
118         bool rec_monitors = get_rec_monitors_input();
119
120         if (transport_sub_state & StopPendingCapture) {
121                 /* force a declick out */
122                 declick = -1;
123         }
124
125         record_active = actively_recording(); // || (get_record_enabled() && get_punch_in());
126
127         for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
128
129                 int ret;
130
131                 if ((*i)->hidden()) {
132                         continue;
133                 }
134
135                 (*i)->set_pending_declick (declick);
136
137                 if ((ret = (*i)->roll (nframes, _transport_frame, _transport_frame + nframes, offset, declick, record_active, rec_monitors)) < 0) {
138
139                         /* we have to do this here. Route::roll() for an AudioTrack will have called AudioDiskstream::process(),
140                            and the DS will expect AudioDiskstream::commit() to be called. but we're aborting from that
141                            call path, so make sure we release any outstanding locks here before we return failure.
142                         */
143
144                         for (AudioDiskstreamList::iterator ids = audio_diskstreams.begin(); ids != audio_diskstreams.end(); ++ids) {
145                                 (*ids)->recover ();
146                         }
147
148                         stop_transport ();
149                         return -1;
150                 } 
151         }
152
153         return 0;
154 }
155
156 int
157 Session::silent_process_routes (jack_nframes_t nframes, jack_nframes_t offset)
158 {
159         bool record_active = actively_recording();
160         int  declick = get_transport_declick_required();
161         bool rec_monitors = get_rec_monitors_input();
162
163         if (transport_sub_state & StopPendingCapture) {
164                 /* force a declick out */
165                 declick = -1;
166         }
167
168         for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
169
170                 int ret;
171
172                 if ((*i)->hidden()) {
173                         continue;
174                 }
175
176                 if ((ret = (*i)->silent_roll (nframes, _transport_frame, _transport_frame + nframes, offset, record_active, rec_monitors)) < 0) {
177                         
178                         /* we have to do this here. Route::roll() for an AudioTrack will have called AudioDiskstream::process(),
179                            and the DS will expect AudioDiskstream::commit() to be called. but we're aborting from that
180                            call path, so make sure we release any outstanding locks here before we return failure.
181                         */
182
183                         for (AudioDiskstreamList::iterator ids = audio_diskstreams.begin(); ids != audio_diskstreams.end(); ++ids) {
184                                 (*ids)->recover ();
185                         }
186
187                         stop_transport ();
188                         return -1;
189                 } 
190         }
191
192         return 0;
193 }
194
195 void
196 Session::commit_diskstreams (jack_nframes_t nframes, bool &needs_butler)
197 {
198         int dret;
199         float pworst = 1.0f;
200         float cworst = 1.0f;
201
202         for (AudioDiskstreamList::iterator i = audio_diskstreams.begin(); i != audio_diskstreams.end(); ++i) {
203
204                 if ((*i)->hidden()) {
205                         continue;
206                 }
207                 
208                 /* force all diskstreams not handled by a Route to call do their stuff.
209                  */
210
211                 if ((dret = (*i)->process (_transport_frame, nframes, 0, actively_recording(), get_rec_monitors_input())) == 0) {
212                         if ((*i)->commit (nframes)) {
213                                 needs_butler = true;
214                         }
215
216                 } else if (dret < 0) {
217                         (*i)->recover();
218                 }
219                 
220                 pworst = min (pworst, (*i)->playback_buffer_load());
221                 cworst = min (cworst, (*i)->capture_buffer_load());
222         }
223
224         uint32_t pmin = g_atomic_int_get (&_playback_load);
225         uint32_t pminold = g_atomic_int_get (&_playback_load_min);
226         uint32_t cmin = g_atomic_int_get (&_capture_load);
227         uint32_t cminold = g_atomic_int_get (&_capture_load_min);
228
229         g_atomic_int_set (&_playback_load, (uint32_t) floor (pworst * 100.0f));
230         g_atomic_int_set (&_capture_load, (uint32_t) floor (cworst * 100.0f));
231         g_atomic_int_set (&_playback_load_min, min (pmin, pminold));
232         g_atomic_int_set (&_capture_load_min, min (cmin, cminold));
233
234         if (actively_recording()) {
235                 set_dirty();
236         }
237 }
238
239 void
240 Session::process_with_events (jack_nframes_t nframes)
241 {
242         Event* ev;
243         jack_nframes_t this_nframes;
244         jack_nframes_t end_frame;
245         jack_nframes_t offset;
246         bool session_needs_butler = false;
247         jack_nframes_t stop_limit;
248         long           frames_moved;
249
250         if (auditioner) {
251                 auditioner->silence (nframes, 0);
252         }
253
254         while (pending_events.read (&ev, 1) == 1) {
255                 merge_event (ev);
256         }
257
258         /* if we are not in the middle of a state change,
259            and there are immediate events queued up,
260            process them. 
261         */
262
263         while (!non_realtime_work_pending() && !immediate_events.empty()) {
264                 Event *ev = immediate_events.front ();
265                 immediate_events.pop_front ();
266                 process_event (ev);
267         }
268
269         if (!process_can_proceed()) {
270                 no_roll (nframes, 0);
271                 return;
272         }
273                 
274         if (events.empty() || next_event == events.end()) {
275                 process_without_events (nframes);
276                 return;
277         }
278
279         end_frame = _transport_frame + nframes;
280
281         {
282                 Glib::RWLock::ReaderLock rm (route_lock, Glib::TRY_LOCK);
283                 Glib::RWLock::ReaderLock dsm (diskstream_lock, Glib::TRY_LOCK);
284         
285                 Event* this_event;
286                 Events::iterator the_next_one;
287
288                 if (!rm.locked() || !dsm.locked() || (post_transport_work & (PostTransportLocate|PostTransportStop))) {
289                         no_roll (nframes, 0);
290                         return;
291                 }
292                 
293                 if (!_exporting && _slave) {
294                         if (!follow_slave (nframes, 0)) {
295                                 return;
296                         }
297                 } 
298
299                 if (_transport_speed == 0) {
300                         no_roll (nframes, 0);
301                         return;
302                 }
303
304                 if (actively_recording()) {
305                         stop_limit = max_frames;
306                 } else {
307
308                         if (Config->get_stop_at_session_end()) {
309                                 stop_limit = current_end_frame();
310                         } else {
311                                 stop_limit = max_frames;
312                         }
313                 }
314
315                 if (maybe_stop (stop_limit)) {
316                         no_roll (nframes, 0);
317                         return;
318                 } 
319
320                 this_event = *next_event;
321                 the_next_one = next_event;
322                 ++the_next_one;
323
324                 offset = 0;
325
326                 while (nframes) {
327
328                         if (this_event == 0 || this_event->action_frame > end_frame || this_event->action_frame < _transport_frame) {
329
330                                 this_nframes = nframes;
331                                 
332                         } else {
333                                 
334                                 /* compute nframes to next event */
335
336                                 if (this_event->action_frame < end_frame) {
337                                         this_nframes = nframes - (end_frame - this_event->action_frame);
338                                 } else {
339                                         this_nframes = nframes;
340                                 }
341                         }
342
343                         if (this_nframes) {
344                                 
345                                 click (_transport_frame, nframes, offset);
346                                 
347                                 /* now process frames between now and the first event in this block */
348                                 prepare_diskstreams ();
349
350                                 if (process_routes (this_nframes, offset)) {
351                                         no_roll (nframes, 0);
352                                         return;
353                                 }
354                                 
355                                 commit_diskstreams (this_nframes, session_needs_butler);
356
357                                 nframes -= this_nframes;
358                                 offset += this_nframes;
359                                 
360                                 frames_moved = (jack_nframes_t) floor (_transport_speed * this_nframes);
361                         
362                                 if (frames_moved < 0) {
363                                         decrement_transport_position (-frames_moved);
364                                 } else {
365                                         increment_transport_position (frames_moved);
366                                 }
367
368                                 maybe_stop (stop_limit);
369                                 check_declick_out ();
370                         }
371
372                         /* now handle this event and all others scheduled for the same time */
373                         
374                         while (this_event && this_event->action_frame == _transport_frame) {
375                                 process_event (this_event);
376
377                                 if (the_next_one == events.end()) {
378                                         this_event = 0;
379                                 } else {
380                                         this_event = *the_next_one;
381                                         ++the_next_one;
382                                 }
383                         } 
384
385                         /* if an event left our state changing, do the right thing */
386
387                         if (non_realtime_work_pending()) {
388                                 no_roll (nframes, offset);
389                                 break;
390                         }
391
392                         /* this is necessary to handle the case of seamless looping */
393                         /* not sure if it will work in conjuction with varispeed */
394                         end_frame = _transport_frame + nframes;
395                         
396                 }
397
398                 set_next_event ();
399
400         } /* implicit release of route lock */
401
402
403         if (session_needs_butler) {
404                 summon_butler ();
405         } 
406         
407         if (!_engine.freewheeling() && send_mtc) {
408                 send_midi_time_code_in_another_thread ();
409         }
410
411         return;
412 }               
413
414 void
415 Session::reset_slave_state ()
416 {
417         average_slave_delta = 1800;
418         delta_accumulator_cnt = 0;
419         have_first_delta_accumulator = false;
420         slave_state = Stopped;
421 }
422
423 bool
424 Session::transport_locked () const
425 {
426         Slave* sl = _slave;
427
428         if (!locate_pending() && ((_slave_type == None) || (sl && sl->ok() && sl->locked()))) {
429                 return true;
430         }
431
432         return false;
433 }
434
435 bool
436 Session::follow_slave (jack_nframes_t nframes, jack_nframes_t offset)
437 {
438         float slave_speed;
439         jack_nframes_t slave_transport_frame;
440         jack_nframes_t this_delta;
441         int dir;
442         bool starting;
443
444         if (!_slave->ok()) {
445                 stop_transport ();
446                 set_slave_source (None, 0);
447                 goto noroll;
448         }
449         
450         _slave->speed_and_position (slave_speed, slave_transport_frame);
451
452         if (!_slave->locked()) {
453                 goto noroll;
454         }
455
456         if (slave_transport_frame > _transport_frame) {
457                 this_delta = slave_transport_frame - _transport_frame;
458                 dir = 1;
459         } else {
460                 this_delta = _transport_frame - slave_transport_frame;
461                 dir = -1;
462         }
463
464         if ((starting = _slave->starting())) {
465                 slave_speed = 0.0f;
466         }
467
468 #if 0
469         cerr << "delta = " << (int) (dir * this_delta)
470              << " speed = " << slave_speed 
471              << " ts = " << _transport_speed 
472              << " M@"<< slave_transport_frame << " S@" << _transport_frame 
473              << " avgdelta = " << average_slave_delta 
474              << endl;
475 #endif  
476
477         if (Config->get_timecode_source_is_synced()) {
478
479                 /* if the TC source is synced, then we assume that its 
480                    speed is binary: 0.0 or 1.0
481                 */
482
483                 if (slave_speed != 0.0f) {
484                         slave_speed = 1.0f;
485                 } 
486
487         } else {
488
489                 /* TC source is able to drift relative to us (slave)
490                    so we need to keep track of the drift and adjust
491                    our speed to remain locked.
492                 */
493
494                 if (delta_accumulator_cnt >= delta_accumulator_size) {
495                         have_first_delta_accumulator = true;
496                         delta_accumulator_cnt = 0;
497                 }
498
499                 if (delta_accumulator_cnt != 0 || this_delta < _current_frame_rate) {
500                         delta_accumulator[delta_accumulator_cnt++] = dir*this_delta;
501                 }
502                 
503                 if (have_first_delta_accumulator) {
504                         average_slave_delta = 0;
505                         for (int i = 0; i < delta_accumulator_size; ++i) {
506                                 average_slave_delta += delta_accumulator[i];
507                         }
508                         average_slave_delta /= delta_accumulator_size;
509                         if (average_slave_delta < 0) {
510                                 average_dir = -1;
511                                 average_slave_delta = -average_slave_delta;
512                         } else {
513                                 average_dir = 1;
514                         }
515                         // cerr << "avgdelta = " << average_slave_delta*average_dir << endl;
516                 }
517         }
518
519         if (slave_speed != 0.0f) {
520
521                 /* slave is running */
522
523                 switch (slave_state) {
524                 case Stopped:
525                         if (_slave->requires_seekahead()) {
526                                 slave_wait_end = slave_transport_frame + _current_frame_rate;
527                                 locate (slave_wait_end, false, false);
528                                 slave_state = Waiting;
529                                 starting = true;
530
531                         } else {
532
533                                 slave_state = Running;
534
535                                 Location* al = _locations.auto_loop_location();
536
537                                 if (al && auto_loop && (slave_transport_frame < al->start() || slave_transport_frame > al->end())) {
538                                         // cancel looping
539                                         request_auto_loop(false);
540                                 }
541
542                                 if (slave_transport_frame != _transport_frame) {
543                                         locate (slave_transport_frame, false, false);
544                                 }
545                         }
546                         break;
547
548                 case Waiting:
549                         break;
550
551                 default:
552                         break;
553
554                 }
555
556                 if (slave_state == Waiting) {
557
558                         // cerr << "waiting at " << slave_transport_frame << endl;
559                         Glib::RWLock::ReaderLock dsm (diskstream_lock, Glib::TRY_LOCK);
560                                 
561                         if (dsm.locked() && slave_transport_frame >= slave_wait_end) {
562                                 // cerr << "\tstart at " << _transport_frame << endl;
563
564                                 slave_state = Running;
565
566                                 bool ok = true;
567                                 jack_nframes_t frame_delta = slave_transport_frame - _transport_frame;
568                                 
569                                 for (AudioDiskstreamList::iterator i = audio_diskstreams.begin(); i != audio_diskstreams.end(); ++i) {
570                                         if (!(*i)->can_internal_playback_seek (frame_delta)) {
571                                                 ok = false;
572                                                 break;
573                                         }
574                                 }
575
576                                 if (ok) {
577                                         for (AudioDiskstreamList::iterator i = audio_diskstreams.begin(); i != audio_diskstreams.end(); ++i) {
578                                                 (*i)->internal_playback_seek (frame_delta);
579                                         }
580                                         _transport_frame += frame_delta;
581                                        
582                                 } else {
583                                         // cerr << "cannot micro-seek\n";
584                                         /* XXX what? */
585                                 }
586
587                                 memset (delta_accumulator, 0, sizeof (jack_nframes_t) * delta_accumulator_size);
588                                 average_slave_delta = 0;
589                                 this_delta = 0;
590                         }
591                 }
592                 
593                 if (slave_state == Running && _transport_speed == 0.0f) {
594                         
595                         // cerr << "slave starts transport\n";
596                         
597                         start_transport ();
598                 } 
599
600         } else {
601
602                 /* slave has stopped */
603
604                 if (_transport_speed != 0.0f) {
605
606                         // cerr << "slave stops transport: " << slave_speed << " frame: " << slave_transport_frame 
607                         // << " tf = " << _transport_frame
608                         // << endl;
609                         
610                         if (_slave_type == JACK) {
611                                 last_stop_frame = _transport_frame;
612                         }
613
614                         stop_transport();
615                 }
616
617                 if (slave_transport_frame != _transport_frame) {
618                         // cerr << "slave stopped, move to " << slave_transport_frame << endl;
619                         force_locate (slave_transport_frame, false);
620                 }
621
622                 slave_state = Stopped;
623         }
624
625         if (slave_state == Running && !Config->get_timecode_source_is_synced()) {
626
627
628                 if (_transport_speed != 0.0f) {
629                         
630                         /* 
631                            note that average_dir is +1 or -1 
632                         */
633                         
634                         const float adjust_seconds = 1.0f;
635                         float delta;
636
637                         //if (average_slave_delta == 0) {
638                                 delta = this_delta;
639                                 delta *= dir;
640 //                      } else {
641 //                              delta = average_slave_delta;
642 //                              delta *= average_dir;
643 //                      }
644
645                         float adjusted_speed = slave_speed +
646                                 (delta / (adjust_seconds * _current_frame_rate));
647                         
648                         // cerr << "adjust using " << delta
649                         // << " towards " << adjusted_speed
650                         // << " ratio = " << adjusted_speed / slave_speed
651                         // << " current = " << _transport_speed
652                         // << " slave @ " << slave_speed
653                         // << endl;
654                         
655                         request_transport_speed (adjusted_speed);
656                         
657 #if 1
658                         if ((jack_nframes_t) average_slave_delta > _slave->resolution()) {
659                                 // cerr << "not locked\n";
660                                 goto silent_motion;
661                         }
662 #endif
663                 }
664         } 
665
666         if (!starting && !non_realtime_work_pending()) {
667                 /* speed is set, we're locked, and good to go */
668                 return true;
669         }
670
671   silent_motion:
672
673         if (slave_speed && _transport_speed) {
674
675                 /* something isn't right, but we should move with the master
676                    for now.
677                 */
678
679                 bool need_butler;
680                 
681                 Glib::RWLock::ReaderLock dsm (diskstream_lock, Glib::TRY_LOCK);
682                 if (!dsm.locked()) {
683                         goto noroll;
684                 }
685
686                 
687                 prepare_diskstreams ();
688                 silent_process_routes (nframes, offset);
689                 commit_diskstreams (nframes, need_butler);
690
691                 if (need_butler) {
692                         summon_butler ();
693                 }
694                 
695                 jack_nframes_t frames_moved = (long) floor (_transport_speed * nframes);
696                 
697                 if (frames_moved < 0) {
698                         decrement_transport_position (-frames_moved);
699                 } else {
700                         increment_transport_position (frames_moved);
701                 }
702                 
703                 jack_nframes_t stop_limit;
704                 
705                 if (actively_recording()) {
706                         stop_limit = max_frames;
707                 } else {
708                         if (Config->get_stop_at_session_end()) {
709                                 stop_limit = current_end_frame();
710                         } else {
711                                 stop_limit = max_frames;
712                         }
713                 }
714
715                 maybe_stop (stop_limit);
716         }
717
718   noroll:
719         /* don't move at all */
720         no_roll (nframes, 0);
721         return false;
722 }
723
724 void
725 Session::process_without_events (jack_nframes_t nframes)
726 {
727         bool session_needs_butler = false;
728         jack_nframes_t stop_limit;
729         long frames_moved;
730         
731         {
732                 Glib::RWLock::ReaderLock rm (route_lock, Glib::TRY_LOCK);
733                 Glib::RWLock::ReaderLock dsm (diskstream_lock, Glib::TRY_LOCK);
734
735                 if (!rm.locked() || !dsm.locked() || (post_transport_work & (PostTransportLocate|PostTransportStop))) {
736                         no_roll (nframes, 0);
737                         return;
738                 }
739
740                 if (!_exporting && _slave) {
741                         if (!follow_slave (nframes, 0)) {
742                                 return;
743                         }
744                 } 
745
746                 if (_transport_speed == 0) {
747                         no_roll (nframes, 0);
748                         return;
749                 }
750                 
751                 if (actively_recording()) {
752                         stop_limit = max_frames;
753                 } else {
754                         if (Config->get_stop_at_session_end()) {
755                                 stop_limit = current_end_frame();
756                         } else {
757                                 stop_limit = max_frames;
758                         }
759                 }
760                 
761                 if (maybe_stop (stop_limit)) {
762                         no_roll (nframes, 0);
763                         return;
764                 } 
765
766                 click (_transport_frame, nframes, 0);
767
768                 prepare_diskstreams ();
769         
770                 frames_moved = (long) floor (_transport_speed * nframes);
771
772                 if (process_routes (nframes, 0)) {
773                         no_roll (nframes, 0);
774                         return;
775                 }
776
777                 commit_diskstreams (nframes, session_needs_butler);
778
779                 if (frames_moved < 0) {
780                         decrement_transport_position (-frames_moved);
781                 } else {
782                         increment_transport_position (frames_moved);
783                 }
784                 
785                 maybe_stop (stop_limit);
786                 check_declick_out ();
787
788         } /* implicit release of route lock */
789
790         if (session_needs_butler) {
791                 summon_butler ();
792         } 
793         
794         if (!_engine.freewheeling() && send_mtc) {
795                 send_midi_time_code_in_another_thread ();
796         }
797
798         return;
799 }               
800
801 void
802 Session::process_audition (jack_nframes_t nframes)
803 {
804         Glib::RWLock::ReaderLock rm (route_lock, Glib::TRY_LOCK);
805         Event* ev;
806
807         if (rm.locked()) {
808                 for (RouteList::iterator i = routes.begin(); i != routes.end(); ++i) {
809                         if (!(*i)->hidden()) {
810                                 (*i)->silence (nframes, 0);
811                         }
812                 }
813         }
814         
815         if (auditioner->play_audition (nframes) > 0) {
816                 summon_butler ();
817         } 
818
819         while (pending_events.read (&ev, 1) == 1) {
820                 merge_event (ev);
821         }
822
823         /* if we are not in the middle of a state change,
824            and there are immediate events queued up,
825            process them. 
826         */
827
828         while (!non_realtime_work_pending() && !immediate_events.empty()) {
829                 Event *ev = immediate_events.front ();
830                 immediate_events.pop_front ();
831                 process_event (ev);
832         }
833
834         if (!auditioner->active()) {
835                 process_function = &Session::process_with_events;
836         }
837 }
838