Expose a new lua hook for resetting a plugin processor ( convenience func only: this...
[ardour.git] / libs / ardour / diskstream.cc
1 /*
2     Copyright (C) 2000-2006 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 */
19
20 #include <cassert>
21 #include <cstdio>
22 #include <unistd.h>
23 #include <cmath>
24 #include <cerrno>
25 #include <string>
26 #include <climits>
27 #include <fcntl.h>
28 #include <cstdlib>
29 #include <ctime>
30 #include <sys/stat.h>
31
32 #include <glibmm/threads.h>
33
34 #include "pbd/error.h"
35 #include "pbd/basename.h"
36 #include "pbd/memento_command.h"
37 #include "pbd/xml++.h"
38 #include "pbd/stacktrace.h"
39
40 #include "ardour/debug.h"
41 #include "ardour/diskstream.h"
42 #include "ardour/io.h"
43 #include "ardour/pannable.h"
44 #include "ardour/profile.h"
45 #include "ardour/playlist.h"
46 #include "ardour/session.h"
47 #include "ardour/track.h"
48
49 #include "pbd/i18n.h"
50 #include <locale.h>
51
52 using namespace std;
53 using namespace ARDOUR;
54 using namespace PBD;
55
56 ARDOUR::framecnt_t Diskstream::disk_read_chunk_frames = default_disk_read_chunk_frames ();
57 ARDOUR::framecnt_t Diskstream::disk_write_chunk_frames = default_disk_write_chunk_frames ();
58
59 PBD::Signal0<void>                Diskstream::DiskOverrun;
60 PBD::Signal0<void>                Diskstream::DiskUnderrun;
61
62 Diskstream::Diskstream (Session &sess, const string &name, Flag flag)
63         : SessionObject(sess, name)
64         , i_am_the_modifier (0)
65         , _track (0)
66         , _record_enabled (0)
67             , _record_safe (0)
68         , _visible_speed (1.0f)
69         , _actual_speed (1.0f)
70         , _buffer_reallocation_required (false)
71         , _seek_required (false)
72         , capture_start_frame (0)
73         , capture_captured (0)
74         , was_recording (false)
75         , adjust_capture_position (0)
76         , _capture_offset (0)
77         , _roll_delay (0)
78         , first_recordable_frame (max_framepos)
79         , last_recordable_frame (max_framepos)
80         , last_possibly_recording (0)
81         , _alignment_style (ExistingMaterial)
82         , _alignment_choice (Automatic)
83         , _slaved (false)
84         , loop_location (0)
85         , overwrite_frame (0)
86         , overwrite_offset (0)
87         , _pending_overwrite (false)
88         , overwrite_queued (false)
89         , wrap_buffer_size (0)
90         , speed_buffer_size (0)
91         , _speed (1.0)
92         , _target_speed (_speed)
93         , file_frame (0)
94         , playback_sample (0)
95         , in_set_state (false)
96         , _flags (flag)
97         , deprecated_io_node (0)
98 {
99 }
100
101 Diskstream::Diskstream (Session& sess, const XMLNode& /*node*/)
102         : SessionObject(sess, "unnamed diskstream")
103         , i_am_the_modifier (0)
104         , _track (0)
105         , _record_enabled (0)
106             , _record_safe (0)
107         , _visible_speed (1.0f)
108         , _actual_speed (1.0f)
109         , _buffer_reallocation_required (false)
110         , _seek_required (false)
111         , capture_start_frame (0)
112         , capture_captured (0)
113         , was_recording (false)
114         , adjust_capture_position (0)
115         , _capture_offset (0)
116         , _roll_delay (0)
117         , first_recordable_frame (max_framepos)
118         , last_recordable_frame (max_framepos)
119         , last_possibly_recording (0)
120         , _alignment_style (ExistingMaterial)
121         , _alignment_choice (Automatic)
122         , _slaved (false)
123         , loop_location (0)
124         , overwrite_frame (0)
125         , overwrite_offset (0)
126         , _pending_overwrite (false)
127         , overwrite_queued (false)
128         , wrap_buffer_size (0)
129         , speed_buffer_size (0)
130         , _speed (1.0)
131         , _target_speed (_speed)
132         , file_frame (0)
133         , playback_sample (0)
134         , in_set_state (false)
135         , _flags (Recordable)
136         , deprecated_io_node (0)
137 {
138 }
139
140 Diskstream::~Diskstream ()
141 {
142         DEBUG_TRACE (DEBUG::Destruction, string_compose ("Diskstream %1 deleted\n", _name));
143
144         if (_playlist) {
145                 _playlist->release ();
146         }
147
148         delete deprecated_io_node;
149 }
150
151 bool
152 Diskstream::non_layered () const
153 {
154         return _session.config.get_layered_record_mode();
155 }
156
157 void
158 Diskstream::set_track (Track* t)
159 {
160         _track = t;
161         _io = _track->input();
162
163         ic_connection.disconnect();
164         _io->changed.connect_same_thread (ic_connection, boost::bind (&Diskstream::handle_input_change, this, _1, _2));
165
166         if (_io->n_ports() != ChanCount::ZERO) {
167                 input_change_pending.type = IOChange::Type (IOChange::ConfigurationChanged|IOChange::ConnectionsChanged);
168                 non_realtime_input_change ();
169         }
170
171         _track->Destroyed.connect_same_thread (*this, boost::bind (&Diskstream::route_going_away, this));
172 }
173
174 void
175 Diskstream::handle_input_change (IOChange change, void * /*src*/)
176 {
177         Glib::Threads::Mutex::Lock lm (state_lock);
178
179         if (change.type & (IOChange::ConfigurationChanged|IOChange::ConnectionsChanged)) {
180
181                 /* rather than handle this here on a DS-by-DS basis we defer to the
182                    session transport/butler thread, and let it tackle
183                    as many diskstreams as need it in one shot. this avoids many repeated
184                    takings of the audioengine process lock.
185                 */
186
187                 if (!(input_change_pending.type & change.type)) {
188                         input_change_pending.type = IOChange::Type (input_change_pending.type | change.type);
189                         _session.request_input_change_handling ();
190                 }
191         }
192 }
193
194 void
195 Diskstream::non_realtime_set_speed ()
196 {
197         if (_buffer_reallocation_required)
198         {
199                 Glib::Threads::Mutex::Lock lm (state_lock);
200                 allocate_temporary_buffers ();
201
202                 _buffer_reallocation_required = false;
203         }
204
205         if (_seek_required) {
206                 if (speed() != 1.0f || speed() != -1.0f) {
207                         seek ((framepos_t) (_session.transport_frame() * (double) speed()), true);
208                 }
209                 else {
210                         seek (_session.transport_frame(), true);
211                 }
212
213                 _seek_required = false;
214         }
215 }
216
217 bool
218 Diskstream::realtime_set_speed (double sp, bool global)
219 {
220         bool changed = false;
221         double new_speed = sp * _session.transport_speed();
222
223         if (_visible_speed != sp) {
224                 _visible_speed = sp;
225                 changed = true;
226         }
227
228         if (new_speed != _actual_speed) {
229
230                 framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() *
231                                                                   fabs (new_speed)) + 2;
232
233                 if (required_wrap_size > wrap_buffer_size) {
234                         _buffer_reallocation_required = true;
235                 }
236
237                 _actual_speed = new_speed;
238                 _target_speed = fabs(_actual_speed);
239         }
240
241         if (changed) {
242                 if (!global) {
243                         _seek_required = true;
244                 }
245                 SpeedChanged (); /* EMIT SIGNAL */
246         }
247
248         return _buffer_reallocation_required || _seek_required;
249 }
250
251 void
252 Diskstream::set_capture_offset ()
253 {
254         if (_io == 0) {
255                 /* can't capture, so forget it */
256                 return;
257         }
258
259         switch (_alignment_style) {
260         case ExistingMaterial:
261                 _capture_offset = _io->latency();
262                 break;
263
264         case CaptureTime:
265         default:
266                 _capture_offset = 0;
267                 break;
268         }
269 #ifdef MIXBUS
270         framecnt_t port_offset;
271         if (_track->mixbus_internal_bounce (port_offset)) {
272                 /* _capture_offset may become negative, but the sum
273                  * _capture_offset + existing_material_offset
274                  * will be postive.
275                  */
276                 _capture_offset -= port_offset;
277         }
278 #endif
279
280         DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: using IO latency, capture offset set to %2 with style = %3\n", name(), _capture_offset, enum_2_string (_alignment_style)));
281 }
282
283
284 void
285 Diskstream::set_align_style (AlignStyle a, bool force)
286 {
287         if (record_enabled() && _session.actively_recording()) {
288                 return;
289         }
290
291         if ((a != _alignment_style) || force) {
292                 _alignment_style = a;
293                 set_capture_offset ();
294                 AlignmentStyleChanged ();
295         }
296 }
297
298 void
299 Diskstream::set_align_choice (AlignChoice a, bool force)
300 {
301         if (record_enabled() && _session.actively_recording()) {
302                 return;
303         }
304
305         if ((a != _alignment_choice) || force) {
306                 _alignment_choice = a;
307
308                 switch (_alignment_choice) {
309                         case Automatic:
310                                 set_align_style_from_io ();
311                                 break;
312                         case UseExistingMaterial:
313                                 set_align_style (ExistingMaterial);
314                                 break;
315                         case UseCaptureTime:
316                                 set_align_style (CaptureTime);
317                                 break;
318                 }
319         }
320 }
321
322 int
323 Diskstream::set_loop (Location *location)
324 {
325         if (location) {
326                 if (location->start() >= location->end()) {
327                         error << string_compose(_("Location \"%1\" not valid for track loop (start >= end)"), location->name()) << endl;
328                         return -1;
329                 }
330         }
331
332         loop_location = location;
333
334         LoopSet (location); /* EMIT SIGNAL */
335         return 0;
336 }
337
338 /** Get the start position (in session frames) of the nth capture in the current pass */
339 ARDOUR::framepos_t
340 Diskstream::get_capture_start_frame (uint32_t n) const
341 {
342         Glib::Threads::Mutex::Lock lm (capture_info_lock);
343
344         if (capture_info.size() > n) {
345                 /* this is a completed capture */
346                 return capture_info[n]->start;
347         } else {
348                 /* this is the currently in-progress capture */
349                 return capture_start_frame;
350         }
351 }
352
353 ARDOUR::framecnt_t
354 Diskstream::get_captured_frames (uint32_t n) const
355 {
356         Glib::Threads::Mutex::Lock lm (capture_info_lock);
357
358         if (capture_info.size() > n) {
359                 /* this is a completed capture */
360                 return capture_info[n]->frames;
361         } else {
362                 /* this is the currently in-progress capture */
363                 return capture_captured;
364         }
365 }
366
367 void
368 Diskstream::set_roll_delay (ARDOUR::framecnt_t nframes)
369 {
370         _roll_delay = nframes;
371 }
372
373 int
374 Diskstream::use_playlist (boost::shared_ptr<Playlist> playlist)
375 {
376         if (!playlist) {
377                 return 0;
378         }
379
380         bool prior_playlist = false;
381
382         {
383                 Glib::Threads::Mutex::Lock lm (state_lock);
384
385                 if (playlist == _playlist) {
386                         return 0;
387                 }
388
389                 playlist_connections.drop_connections ();
390
391                 if (_playlist) {
392                         _playlist->release();
393                         prior_playlist = true;
394                 }
395
396                 _playlist = playlist;
397                 _playlist->use();
398
399                 if (!in_set_state && destructive() && recordable()) {
400                         reset_write_sources (false);
401                 }
402
403                 _playlist->ContentsChanged.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_modified, this));
404                 _playlist->LayeringChanged.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_modified, this));
405                 _playlist->DropReferences.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_deleted, this, boost::weak_ptr<Playlist>(_playlist)));
406                 _playlist->RangesMoved.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_ranges_moved, this, _1, _2));
407         }
408
409         /* don't do this if we've already asked for it *or* if we are setting up
410            the diskstream for the very first time - the input changed handling will
411            take care of the buffer refill.
412         */
413
414         if (!overwrite_queued && prior_playlist) {
415                 _session.request_overwrite_buffer (_track);
416                 overwrite_queued = true;
417         }
418
419         PlaylistChanged (); /* EMIT SIGNAL */
420         _session.set_dirty ();
421
422         return 0;
423 }
424
425 void
426 Diskstream::playlist_changed (const PropertyChange&)
427 {
428         playlist_modified ();
429 }
430
431 void
432 Diskstream::playlist_modified ()
433 {
434         if (!i_am_the_modifier && !overwrite_queued) {
435                 _session.request_overwrite_buffer (_track);
436                 overwrite_queued = true;
437         }
438 }
439
440 void
441 Diskstream::playlist_deleted (boost::weak_ptr<Playlist> wpl)
442 {
443         boost::shared_ptr<Playlist> pl (wpl.lock());
444
445         if (pl == _playlist) {
446
447                 /* this catches an ordering issue with session destruction. playlists
448                    are destroyed before diskstreams. we have to invalidate any handles
449                    we have to the playlist.
450                 */
451
452                 if (_playlist) {
453                         _playlist.reset ();
454                 }
455         }
456 }
457
458 bool
459 Diskstream::set_name (const string& str)
460 {
461         if (_name != str) {
462                 assert(playlist());
463                 playlist()->set_name (str);
464                 SessionObject::set_name(str);
465         }
466         return true;
467 }
468
469 bool
470 Diskstream::set_write_source_name (const std::string& str) {
471         _write_source_name = str;
472         return true;
473 }
474
475 XMLNode&
476 Diskstream::get_state ()
477 {
478         XMLNode* node = new XMLNode ("Diskstream");
479         char buf[64];
480         LocaleGuard lg;
481
482         node->add_property ("flags", enum_2_string (_flags));
483         node->add_property ("playlist", _playlist->name());
484         node->add_property("name", _name);
485         id().print (buf, sizeof (buf));
486         node->add_property("id", buf);
487         snprintf (buf, sizeof(buf), "%f", _visible_speed);
488         node->add_property ("speed", buf);
489         node->add_property ("capture-alignment", enum_2_string (_alignment_choice));
490         node->add_property ("record-safe", _record_safe ? "yes" : "no");
491
492         if (_extra_xml) {
493                 node->add_child_copy (*_extra_xml);
494         }
495
496         return *node;
497 }
498
499 int
500 Diskstream::set_state (const XMLNode& node, int /*version*/)
501 {
502         XMLProperty const * prop;
503
504         if ((prop = node.property ("name")) != 0) {
505                 _name = prop->value();
506         }
507
508         if (deprecated_io_node) {
509                 set_id (*deprecated_io_node);
510         } else {
511                 set_id (node);
512         }
513
514         if ((prop = node.property ("flags")) != 0) {
515                 _flags = Flag (string_2_enum (prop->value(), _flags));
516         }
517
518         if (Profile->get_trx() && (_flags & Destructive)) {
519                 error << string_compose (_("%1: this session uses destructive tracks, which are not supported"), PROGRAM_NAME) << endmsg;
520                 return -1;
521         }
522
523         if ((prop = node.property (X_("capture-alignment"))) != 0) {
524                 set_align_choice (AlignChoice (string_2_enum (prop->value(), _alignment_choice)), true);
525         } else {
526                 set_align_choice (Automatic, true);
527         }
528
529         if ((prop = node.property ("playlist")) == 0) {
530                 return -1;
531         }
532
533         if (find_and_use_playlist (prop->value())) {
534                 return -1;
535         }
536
537         if ((prop = node.property ("speed")) != 0) {
538                 double sp = atof (prop->value().c_str());
539
540                 if (realtime_set_speed (sp, false)) {
541                         non_realtime_set_speed ();
542                 }
543         }
544
545         if ((prop = node.property ("record-safe")) != 0) {
546             _record_safe = PBD::string_is_affirmative (prop->value()) ? 1 : 0;
547         }
548
549         return 0;
550 }
551
552 void
553 Diskstream::playlist_ranges_moved (list< Evoral::RangeMove<framepos_t> > const & movements_frames, bool from_undo)
554 {
555         /* If we're coming from an undo, it will have handled
556            automation undo (it must, since automation-follows-regions
557            can lose automation data).  Hence we can do nothing here.
558         */
559
560         if (from_undo) {
561                 return;
562         }
563
564         if (!_track || Config->get_automation_follows_regions () == false) {
565                 return;
566         }
567
568         list< Evoral::RangeMove<double> > movements;
569
570         for (list< Evoral::RangeMove<framepos_t> >::const_iterator i = movements_frames.begin();
571              i != movements_frames.end();
572              ++i) {
573
574                 movements.push_back(Evoral::RangeMove<double>(i->from, i->length, i->to));
575         }
576
577         /* move panner automation */
578         boost::shared_ptr<Pannable> pannable = _track->pannable();
579         Evoral::ControlSet::Controls& c (pannable->controls());
580
581         for (Evoral::ControlSet::Controls::iterator ci = c.begin(); ci != c.end(); ++ci) {
582                 boost::shared_ptr<AutomationControl> ac = boost::dynamic_pointer_cast<AutomationControl>(ci->second);
583                 if (!ac) {
584                         continue;
585                 }
586                 boost::shared_ptr<AutomationList> alist = ac->alist();
587                 if (!alist->size()) {
588                         continue;
589                 }
590                 XMLNode & before = alist->get_state ();
591                 bool const things_moved = alist->move_ranges (movements);
592                 if (things_moved) {
593                         _session.add_command (new MementoCommand<AutomationList> (
594                                                       *alist.get(), &before, &alist->get_state ()));
595                 }
596         }
597
598         /* move processor automation */
599         _track->foreach_processor (boost::bind (&Diskstream::move_processor_automation, this, _1, movements_frames));
600 }
601
602 void
603 Diskstream::move_processor_automation (boost::weak_ptr<Processor> p, list< Evoral::RangeMove<framepos_t> > const & movements_frames)
604 {
605         boost::shared_ptr<Processor> processor (p.lock ());
606         if (!processor) {
607                 return;
608         }
609
610         list< Evoral::RangeMove<double> > movements;
611         for (list< Evoral::RangeMove<framepos_t> >::const_iterator i = movements_frames.begin(); i != movements_frames.end(); ++i) {
612                 movements.push_back(Evoral::RangeMove<double>(i->from, i->length, i->to));
613         }
614
615         set<Evoral::Parameter> const a = processor->what_can_be_automated ();
616
617         for (set<Evoral::Parameter>::const_iterator i = a.begin (); i != a.end (); ++i) {
618                 boost::shared_ptr<AutomationList> al = processor->automation_control(*i)->alist();
619                 if (!al->size()) {
620                         continue;
621                 }
622                 XMLNode & before = al->get_state ();
623                 bool const things_moved = al->move_ranges (movements);
624                 if (things_moved) {
625                         _session.add_command (
626                                 new MementoCommand<AutomationList> (
627                                         *al.get(), &before, &al->get_state ()
628                                         )
629                                 );
630                 }
631         }
632 }
633
634 void
635 Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
636 {
637         int possibly_recording;
638         int rolling;
639         int change;
640         const int transport_rolling = 0x4;
641         const int track_rec_enabled = 0x2;
642         const int global_rec_enabled = 0x1;
643         const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled);
644
645         /* merge together the 3 factors that affect record status, and compute
646          * what has changed.
647          */
648
649         rolling = _session.transport_speed() != 0.0f;
650         possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record;
651         change = possibly_recording ^ last_possibly_recording;
652
653         if (possibly_recording == last_possibly_recording) {
654                 return;
655         }
656
657         const framecnt_t existing_material_offset = _session.worst_playback_latency();
658
659         if (possibly_recording == fully_rec_enabled) {
660
661                 if (last_possibly_recording == fully_rec_enabled) {
662                         return;
663                 }
664
665                 capture_start_frame = _session.transport_frame();
666                 first_recordable_frame = capture_start_frame + _capture_offset;
667                 last_recordable_frame = max_framepos;
668
669                 DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %7 (%9) FRF = %2 CSF = %4 CO = %5, EMO = %6 RD = %8 WOL %10 WTL %11\n",
670                                                                       name(), first_recordable_frame, last_recordable_frame, capture_start_frame,
671                                                                       _capture_offset,
672                                                                       existing_material_offset,
673                                                                       transport_frame,
674                                                                       _roll_delay,
675                                                                       _session.transport_frame(),
676                                                                       _session.worst_output_latency(),
677                                                                       _session.worst_track_latency()));
678
679
680                 if (_alignment_style == ExistingMaterial) {
681                         first_recordable_frame += existing_material_offset;
682                         DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tshift FRF by EMO %1\n",
683                                                                               first_recordable_frame));
684                 }
685
686                 prepare_record_status (capture_start_frame);
687
688         } else {
689
690                 if (last_possibly_recording == fully_rec_enabled) {
691
692                         /* we were recording last time */
693
694                         if (change & transport_rolling) {
695
696                                 /* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We
697                                  * had to set it there because we likely rolled past the stopping point to declick out,
698                                  * and then backed up.
699                                  */
700
701                         } else {
702                                 /* punch out */
703
704                                 last_recordable_frame = _session.transport_frame() + _capture_offset;
705
706                                 if (_alignment_style == ExistingMaterial) {
707                                         last_recordable_frame += existing_material_offset;
708                                 }
709                         }
710                 }
711         }
712
713         last_possibly_recording = possibly_recording;
714 }
715
716 void
717 Diskstream::route_going_away ()
718 {
719         _io.reset ();
720 }
721
722 void
723 Diskstream::calculate_record_range (Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes,
724                                     framecnt_t & rec_nframes, framecnt_t & rec_offset)
725 {
726         switch (ot) {
727         case Evoral::OverlapNone:
728                 rec_nframes = 0;
729                 break;
730
731         case Evoral::OverlapInternal:
732                 /*     ----------    recrange
733                  *       |---|       transrange
734                  */
735                 rec_nframes = nframes;
736                 rec_offset = 0;
737                 break;
738
739         case Evoral::OverlapStart:
740                 /*    |--------|    recrange
741                  *  -----|          transrange
742                  */
743                 rec_nframes = transport_frame + nframes - first_recordable_frame;
744                 if (rec_nframes) {
745                         rec_offset = first_recordable_frame - transport_frame;
746                 }
747                 break;
748
749         case Evoral::OverlapEnd:
750                 /*    |--------|    recrange
751                  *       |--------  transrange
752                  */
753                 rec_nframes = last_recordable_frame - transport_frame;
754                 rec_offset = 0;
755                 break;
756
757         case Evoral::OverlapExternal:
758                 /*    |--------|    recrange
759                  *  --------------  transrange
760                  */
761                 rec_nframes = last_recordable_frame - first_recordable_frame;
762                 rec_offset = first_recordable_frame - transport_frame;
763                 break;
764         }
765
766         DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 rec? %2 @ %3 (for %4) FRF %5 LRF %6 : rf %7 @ %8\n",
767                                                               _name, enum_2_string (ot), transport_frame, nframes,
768                                                               first_recordable_frame, last_recordable_frame, rec_nframes, rec_offset));
769 }
770
771 void
772 Diskstream::prepare_to_stop (framepos_t transport_frame, framepos_t audible_frame)
773 {
774         switch (_alignment_style) {
775         case ExistingMaterial:
776                 last_recordable_frame = transport_frame + _capture_offset;
777                 DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to %2 + %3 = %4\n", _name, transport_frame, _capture_offset, last_recordable_frame));
778                 break;
779
780         case CaptureTime:
781                 last_recordable_frame = audible_frame; // note that capture_offset is zero
782                 /* we may already have captured audio before the last_recordable_frame (audible frame),
783                    so deal with this.
784                 */
785                 if (last_recordable_frame > capture_start_frame) {
786                         capture_captured = min (capture_captured, last_recordable_frame - capture_start_frame);
787                 }
788                 DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to audible frame @ %2\n", _name, audible_frame));
789                 break;
790         }
791
792 }
793
794 void
795 Diskstream::engage_record_enable ()
796 {
797         g_atomic_int_set (&_record_enabled, 1);
798 }
799
800 void
801 Diskstream::disengage_record_enable ()
802 {
803         g_atomic_int_set (&_record_enabled, 0);
804 }
805
806 void
807 Diskstream::engage_record_safe ()
808 {
809         g_atomic_int_set (&_record_safe, 1);
810 }
811
812 void
813 Diskstream::disengage_record_safe ()
814 {
815         g_atomic_int_set (&_record_safe, 0);
816 }
817
818 framecnt_t
819 Diskstream::default_disk_read_chunk_frames()
820 {
821         return 65536;
822 }
823
824 framecnt_t
825 Diskstream::default_disk_write_chunk_frames ()
826 {
827         return 65536;
828 }
829
830 void
831 Diskstream::set_buffering_parameters (BufferingPreset bp)
832 {
833         framecnt_t read_chunk_size;
834         framecnt_t read_buffer_size;
835         framecnt_t write_chunk_size;
836         framecnt_t write_buffer_size;
837
838         if (!get_buffering_presets (bp, read_chunk_size, read_buffer_size, write_chunk_size, write_buffer_size)) {
839                 return;
840         }
841
842         disk_read_chunk_frames = read_chunk_size;
843         disk_write_chunk_frames = write_chunk_size;
844         Config->set_audio_capture_buffer_seconds (write_buffer_size);
845         Config->set_audio_playback_buffer_seconds (read_buffer_size);
846
847         cerr << "Set buffering params to " << disk_read_chunk_frames << '|' << disk_write_chunk_frames << '|'
848              << Config->get_audio_playback_buffer_seconds() << '|'
849              << Config->get_audio_capture_buffer_seconds ()
850              << endl;
851 }
852
853 bool
854 Diskstream::get_buffering_presets (BufferingPreset bp,
855                                    framecnt_t& read_chunk_size,
856                                    framecnt_t& read_buffer_size,
857                                    framecnt_t& write_chunk_size,
858                                    framecnt_t& write_buffer_size)
859 {
860         switch (bp) {
861         case Small:
862                 read_chunk_size = 65536;  /* samples */
863                 write_chunk_size = 65536; /* samples */
864                 read_buffer_size = 5;  /* seconds */
865                 write_buffer_size = 5; /* seconds */
866                 break;
867
868         case Medium:
869                 read_chunk_size = 262144;  /* samples */
870                 write_chunk_size = 131072; /* samples */
871                 read_buffer_size = 10;  /* seconds */
872                 write_buffer_size = 10; /* seconds */
873                 break;
874
875         case Large:
876                 read_chunk_size = 524288; /* samples */
877                 write_chunk_size = 131072; /* samples */
878                 read_buffer_size = 20; /* seconds */
879                 write_buffer_size = 20; /* seconds */
880                 break;
881
882         default:
883                 return false;
884         }
885
886         return true;
887 }