Add a scratch buffer for automation.
[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 #include "pbd/enum_convert.h"
40 #include "pbd/types_convert.h"
41
42 #include "ardour/debug.h"
43 #include "ardour/diskstream.h"
44 #include "ardour/io.h"
45 #include "ardour/pannable.h"
46 #include "ardour/profile.h"
47 #include "ardour/playlist.h"
48 #include "ardour/session.h"
49 #include "ardour/track.h"
50 #include "ardour/types_convert.h"
51
52 #include "pbd/i18n.h"
53 #include <locale.h>
54
55 using namespace std;
56 using namespace ARDOUR;
57 using namespace PBD;
58
59 namespace PBD {
60         DEFINE_ENUM_CONVERT(Diskstream::Flag);
61 }
62
63 ARDOUR::framecnt_t Diskstream::disk_read_chunk_frames = default_disk_read_chunk_frames ();
64 ARDOUR::framecnt_t Diskstream::disk_write_chunk_frames = default_disk_write_chunk_frames ();
65
66 PBD::Signal0<void>                Diskstream::DiskOverrun;
67 PBD::Signal0<void>                Diskstream::DiskUnderrun;
68
69 Diskstream::Diskstream (Session &sess, const string &name, Flag flag)
70         : SessionObject(sess, name)
71         , i_am_the_modifier (0)
72         , _track (0)
73         , _record_enabled (0)
74             , _record_safe (0)
75         , _visible_speed (1.0f)
76         , _actual_speed (1.0f)
77         , _buffer_reallocation_required (false)
78         , _seek_required (false)
79         , capture_start_frame (0)
80         , capture_captured (0)
81         , was_recording (false)
82         , adjust_capture_position (0)
83         , _capture_offset (0)
84         , _roll_delay (0)
85         , first_recordable_frame (max_framepos)
86         , last_recordable_frame (max_framepos)
87         , last_possibly_recording (0)
88         , _alignment_style (ExistingMaterial)
89         , _alignment_choice (Automatic)
90         , _slaved (false)
91         , loop_location (0)
92         , overwrite_frame (0)
93         , overwrite_offset (0)
94         , _pending_overwrite (false)
95         , overwrite_queued (false)
96         , wrap_buffer_size (0)
97         , speed_buffer_size (0)
98         , _speed (1.0)
99         , _target_speed (_speed)
100         , file_frame (0)
101         , playback_sample (0)
102         , in_set_state (false)
103         , _flags (flag)
104         , deprecated_io_node (0)
105 {
106 }
107
108 Diskstream::Diskstream (Session& sess, const XMLNode& /*node*/)
109         : SessionObject(sess, "unnamed diskstream")
110         , i_am_the_modifier (0)
111         , _track (0)
112         , _record_enabled (0)
113             , _record_safe (0)
114         , _visible_speed (1.0f)
115         , _actual_speed (1.0f)
116         , _buffer_reallocation_required (false)
117         , _seek_required (false)
118         , capture_start_frame (0)
119         , capture_captured (0)
120         , was_recording (false)
121         , adjust_capture_position (0)
122         , _capture_offset (0)
123         , _roll_delay (0)
124         , first_recordable_frame (max_framepos)
125         , last_recordable_frame (max_framepos)
126         , last_possibly_recording (0)
127         , _alignment_style (ExistingMaterial)
128         , _alignment_choice (Automatic)
129         , _slaved (false)
130         , loop_location (0)
131         , overwrite_frame (0)
132         , overwrite_offset (0)
133         , _pending_overwrite (false)
134         , overwrite_queued (false)
135         , wrap_buffer_size (0)
136         , speed_buffer_size (0)
137         , _speed (1.0)
138         , _target_speed (_speed)
139         , file_frame (0)
140         , playback_sample (0)
141         , in_set_state (false)
142         , _flags (Recordable)
143         , deprecated_io_node (0)
144 {
145 }
146
147 Diskstream::~Diskstream ()
148 {
149         DEBUG_TRACE (DEBUG::Destruction, string_compose ("Diskstream %1 deleted\n", _name));
150
151         if (_playlist) {
152                 _playlist->release ();
153         }
154
155         delete deprecated_io_node;
156 }
157
158 bool
159 Diskstream::non_layered () const
160 {
161         return _session.config.get_layered_record_mode();
162 }
163
164 void
165 Diskstream::set_track (Track* t)
166 {
167         _track = t;
168         _io = _track->input();
169
170         ic_connection.disconnect();
171         _io->changed.connect_same_thread (ic_connection, boost::bind (&Diskstream::handle_input_change, this, _1, _2));
172
173         if (_io->n_ports() != ChanCount::ZERO) {
174                 input_change_pending.type = IOChange::Type (IOChange::ConfigurationChanged|IOChange::ConnectionsChanged);
175                 non_realtime_input_change ();
176         }
177
178         _track->Destroyed.connect_same_thread (*this, boost::bind (&Diskstream::route_going_away, this));
179 }
180
181 void
182 Diskstream::handle_input_change (IOChange change, void * /*src*/)
183 {
184         Glib::Threads::Mutex::Lock lm (state_lock);
185
186         if (change.type & (IOChange::ConfigurationChanged|IOChange::ConnectionsChanged)) {
187
188                 /* rather than handle this here on a DS-by-DS basis we defer to the
189                    session transport/butler thread, and let it tackle
190                    as many diskstreams as need it in one shot. this avoids many repeated
191                    takings of the audioengine process lock.
192                 */
193
194                 if (!(input_change_pending.type & change.type)) {
195                         input_change_pending.type = IOChange::Type (input_change_pending.type | change.type);
196                         _session.request_input_change_handling ();
197                 }
198         }
199 }
200
201 void
202 Diskstream::non_realtime_set_speed ()
203 {
204         if (_buffer_reallocation_required)
205         {
206                 Glib::Threads::Mutex::Lock lm (state_lock);
207                 allocate_temporary_buffers ();
208
209                 _buffer_reallocation_required = false;
210         }
211
212         if (_seek_required) {
213                 if (speed() != 1.0f || speed() != -1.0f) {
214                         seek ((framepos_t) (_session.transport_frame() * (double) speed()), true);
215                 }
216                 else {
217                         seek (_session.transport_frame(), true);
218                 }
219
220                 _seek_required = false;
221         }
222 }
223
224 bool
225 Diskstream::realtime_set_speed (double sp, bool global)
226 {
227         bool changed = false;
228         double new_speed = sp * _session.transport_speed();
229
230         if (_visible_speed != sp) {
231                 _visible_speed = sp;
232                 changed = true;
233         }
234
235         if (new_speed != _actual_speed) {
236
237                 framecnt_t required_wrap_size = (framecnt_t) ceil (_session.get_block_size() *
238                                                                   fabs (new_speed)) + 2;
239
240                 if (required_wrap_size > wrap_buffer_size) {
241                         _buffer_reallocation_required = true;
242                 }
243
244                 _actual_speed = new_speed;
245                 _target_speed = fabs(_actual_speed);
246         }
247
248         if (changed) {
249                 if (!global) {
250                         _seek_required = true;
251                 }
252                 SpeedChanged (); /* EMIT SIGNAL */
253         }
254
255         return _buffer_reallocation_required || _seek_required;
256 }
257
258 void
259 Diskstream::set_capture_offset ()
260 {
261         if (_io == 0) {
262                 /* can't capture, so forget it */
263                 return;
264         }
265
266         switch (_alignment_style) {
267         case ExistingMaterial:
268                 _capture_offset = _io->latency();
269                 break;
270
271         case CaptureTime:
272         default:
273                 _capture_offset = 0;
274                 break;
275         }
276 #ifdef MIXBUS
277         framecnt_t port_offset;
278         if (_track->mixbus_internal_bounce (port_offset)) {
279                 /* _capture_offset may become negative, but the sum
280                  * _capture_offset + existing_material_offset
281                  * will be postive.
282                  */
283                 _capture_offset -= port_offset;
284         }
285 #endif
286
287         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)));
288 }
289
290
291 void
292 Diskstream::set_align_style (AlignStyle a, bool force)
293 {
294         if (record_enabled() && _session.actively_recording()) {
295                 return;
296         }
297
298         if ((a != _alignment_style) || force) {
299                 _alignment_style = a;
300                 set_capture_offset ();
301                 AlignmentStyleChanged ();
302         }
303 }
304
305 void
306 Diskstream::set_align_choice (AlignChoice a, bool force)
307 {
308         if (record_enabled() && _session.actively_recording()) {
309                 return;
310         }
311
312         if ((a != _alignment_choice) || force) {
313                 _alignment_choice = a;
314
315                 switch (_alignment_choice) {
316                         case Automatic:
317                                 set_align_style_from_io ();
318                                 break;
319                         case UseExistingMaterial:
320                                 set_align_style (ExistingMaterial);
321                                 break;
322                         case UseCaptureTime:
323                                 set_align_style (CaptureTime);
324                                 break;
325                 }
326         }
327 }
328
329 int
330 Diskstream::set_loop (Location *location)
331 {
332         if (location) {
333                 if (location->start() >= location->end()) {
334                         error << string_compose(_("Location \"%1\" not valid for track loop (start >= end)"), location->name()) << endl;
335                         return -1;
336                 }
337         }
338
339         loop_location = location;
340
341         LoopSet (location); /* EMIT SIGNAL */
342         return 0;
343 }
344
345 /** Get the start position (in session frames) of the nth capture in the current pass */
346 ARDOUR::framepos_t
347 Diskstream::get_capture_start_frame (uint32_t n) const
348 {
349         Glib::Threads::Mutex::Lock lm (capture_info_lock);
350
351         if (capture_info.size() > n) {
352                 /* this is a completed capture */
353                 return capture_info[n]->start;
354         } else {
355                 /* this is the currently in-progress capture */
356                 return capture_start_frame;
357         }
358 }
359
360 ARDOUR::framecnt_t
361 Diskstream::get_captured_frames (uint32_t n) const
362 {
363         Glib::Threads::Mutex::Lock lm (capture_info_lock);
364
365         if (capture_info.size() > n) {
366                 /* this is a completed capture */
367                 return capture_info[n]->frames;
368         } else {
369                 /* this is the currently in-progress capture */
370                 return capture_captured;
371         }
372 }
373
374 void
375 Diskstream::set_roll_delay (ARDOUR::framecnt_t nframes)
376 {
377         _roll_delay = nframes;
378 }
379
380 int
381 Diskstream::use_playlist (boost::shared_ptr<Playlist> playlist)
382 {
383         if (!playlist) {
384                 return 0;
385         }
386
387         bool prior_playlist = false;
388
389         {
390                 Glib::Threads::Mutex::Lock lm (state_lock);
391
392                 if (playlist == _playlist) {
393                         return 0;
394                 }
395
396                 playlist_connections.drop_connections ();
397
398                 if (_playlist) {
399                         _playlist->release();
400                         prior_playlist = true;
401                 }
402
403                 _playlist = playlist;
404                 _playlist->use();
405
406                 if (!in_set_state && destructive() && recordable()) {
407                         reset_write_sources (false);
408                 }
409
410                 _playlist->ContentsChanged.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_modified, this));
411                 _playlist->LayeringChanged.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_modified, this));
412                 _playlist->DropReferences.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_deleted, this, boost::weak_ptr<Playlist>(_playlist)));
413                 _playlist->RangesMoved.connect_same_thread (playlist_connections, boost::bind (&Diskstream::playlist_ranges_moved, this, _1, _2));
414         }
415
416         /* don't do this if we've already asked for it *or* if we are setting up
417            the diskstream for the very first time - the input changed handling will
418            take care of the buffer refill.
419         */
420
421         if (!overwrite_queued && prior_playlist) {
422                 _session.request_overwrite_buffer (_track);
423                 overwrite_queued = true;
424         }
425
426         PlaylistChanged (); /* EMIT SIGNAL */
427         _session.set_dirty ();
428
429         return 0;
430 }
431
432 void
433 Diskstream::playlist_changed (const PropertyChange&)
434 {
435         playlist_modified ();
436 }
437
438 void
439 Diskstream::playlist_modified ()
440 {
441         if (!i_am_the_modifier && !overwrite_queued) {
442                 _session.request_overwrite_buffer (_track);
443                 overwrite_queued = true;
444         }
445 }
446
447 void
448 Diskstream::playlist_deleted (boost::weak_ptr<Playlist> wpl)
449 {
450         boost::shared_ptr<Playlist> pl (wpl.lock());
451
452         if (pl == _playlist) {
453
454                 /* this catches an ordering issue with session destruction. playlists
455                    are destroyed before diskstreams. we have to invalidate any handles
456                    we have to the playlist.
457                 */
458
459                 if (_playlist) {
460                         _playlist.reset ();
461                 }
462         }
463 }
464
465 bool
466 Diskstream::set_name (const string& str)
467 {
468         if (_name != str) {
469                 assert(playlist());
470                 playlist()->set_name (str);
471                 SessionObject::set_name(str);
472         }
473         return true;
474 }
475
476 bool
477 Diskstream::set_write_source_name (const std::string& str) {
478         _write_source_name = str;
479         return true;
480 }
481
482 XMLNode&
483 Diskstream::get_state ()
484 {
485         XMLNode* node = new XMLNode ("Diskstream");
486         LocaleGuard lg;
487
488         node->set_property ("flags", _flags);
489         node->set_property ("playlist", _playlist->name());
490         node->set_property ("name", name());
491         node->set_property ("id", id ());
492         node->set_property ("speed", _visible_speed);
493         node->set_property ("capture-alignment", _alignment_choice);
494         node->set_property ("record-safe", _record_safe);
495
496         if (_extra_xml) {
497                 node->add_child_copy (*_extra_xml);
498         }
499         return *node;
500 }
501
502 int
503 Diskstream::set_state (const XMLNode& node, int /*version*/)
504 {
505         std::string name;
506         if (node.get_property ("name", name)) {
507                 _name = name;
508         }
509
510         if (deprecated_io_node) {
511                 set_id (*deprecated_io_node);
512         } else {
513                 set_id (node);
514         }
515
516         node.get_property ("flags", _flags);
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         AlignChoice achoice = Automatic;
524         node.get_property (X_("capture-alignment"), achoice);
525         set_align_choice (achoice, true);
526
527         XMLProperty const * prop;
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         double sp;
538         if (node.get_property ("speed", sp)) {
539                 if (realtime_set_speed (sp, false)) {
540                         non_realtime_set_speed ();
541                 }
542         }
543
544         bool record_safe;
545         if (node.get_property ("record-safe", record_safe)) {
546           _record_safe = record_safe ? 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 }