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