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