use new FPU singleton pattern in libs/ardour
[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->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 (X_("C"));
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         const XMLProperty* 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
572                 XMLNode & before = alist->get_state ();
573                 bool const things_moved = alist->move_ranges (movements);
574                 if (things_moved) {
575                         _session.add_command (new MementoCommand<AutomationList> (
576                                                       *alist.get(), &before, &alist->get_state ()));
577                 }
578         }
579
580         /* move processor automation */
581         _track->foreach_processor (boost::bind (&Diskstream::move_processor_automation, this, _1, movements_frames));
582 }
583
584 void
585 Diskstream::move_processor_automation (boost::weak_ptr<Processor> p, list< Evoral::RangeMove<framepos_t> > const & movements_frames)
586 {
587         boost::shared_ptr<Processor> processor (p.lock ());
588         if (!processor) {
589                 return;
590         }
591
592         list< Evoral::RangeMove<double> > movements;
593         for (list< Evoral::RangeMove<framepos_t> >::const_iterator i = movements_frames.begin(); i != movements_frames.end(); ++i) {
594                 movements.push_back(Evoral::RangeMove<double>(i->from, i->length, i->to));
595         }
596
597         set<Evoral::Parameter> const a = processor->what_can_be_automated ();
598
599         for (set<Evoral::Parameter>::const_iterator i = a.begin (); i != a.end (); ++i) {
600                 boost::shared_ptr<AutomationList> al = processor->automation_control(*i)->alist();
601                 XMLNode & before = al->get_state ();
602                 bool const things_moved = al->move_ranges (movements);
603                 if (things_moved) {
604                         _session.add_command (
605                                 new MementoCommand<AutomationList> (
606                                         *al.get(), &before, &al->get_state ()
607                                         )
608                                 );
609                 }
610         }
611 }
612
613 void
614 Diskstream::check_record_status (framepos_t transport_frame, bool can_record)
615 {
616         int possibly_recording;
617         int rolling;
618         int change;
619         const int transport_rolling = 0x4;
620         const int track_rec_enabled = 0x2;
621         const int global_rec_enabled = 0x1;
622         const int fully_rec_enabled = (transport_rolling|track_rec_enabled|global_rec_enabled);
623
624         /* merge together the 3 factors that affect record status, and compute
625          * what has changed.
626          */
627
628         rolling = _session.transport_speed() != 0.0f;
629         possibly_recording = (rolling << 2) | ((int)record_enabled() << 1) | (int)can_record;
630         change = possibly_recording ^ last_possibly_recording;
631
632         if (possibly_recording == last_possibly_recording) {
633                 return;
634         }
635
636         const framecnt_t existing_material_offset = _session.worst_playback_latency();
637
638         if (possibly_recording == fully_rec_enabled) {
639
640                 if (last_possibly_recording == fully_rec_enabled) {
641                         return;
642                 }
643
644                 capture_start_frame = _session.transport_frame();
645                 first_recordable_frame = capture_start_frame + _capture_offset;
646                 last_recordable_frame = max_framepos;
647
648                 DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1: @ %7 (%9) FRF = %2 CSF = %4 CO = %5, EMO = %6 RD = %8 WOL %10 WTL %11\n",
649                                                                       name(), first_recordable_frame, last_recordable_frame, capture_start_frame,
650                                                                       _capture_offset,
651                                                                       existing_material_offset,
652                                                                       transport_frame,
653                                                                       _roll_delay,
654                                                                       _session.transport_frame(),
655                                                                       _session.worst_output_latency(),
656                                                                       _session.worst_track_latency()));
657
658
659                 if (_alignment_style == ExistingMaterial) {
660                         first_recordable_frame += existing_material_offset;
661                         DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("\tshift FRF by EMO %1\n",
662                                                                               first_recordable_frame));
663                 }
664
665                 prepare_record_status (capture_start_frame);
666
667         } else {
668
669                 if (last_possibly_recording == fully_rec_enabled) {
670
671                         /* we were recording last time */
672
673                         if (change & transport_rolling) {
674
675                                 /* transport-change (stopped rolling): last_recordable_frame was set in ::prepare_to_stop(). We
676                                  * had to set it there because we likely rolled past the stopping point to declick out,
677                                  * and then backed up.
678                                  */
679
680                         } else {
681                                 /* punch out */
682
683                                 last_recordable_frame = _session.transport_frame() + _capture_offset;
684
685                                 if (_alignment_style == ExistingMaterial) {
686                                         last_recordable_frame += existing_material_offset;
687                                 }
688                         }
689                 }
690         }
691
692         last_possibly_recording = possibly_recording;
693 }
694
695 void
696 Diskstream::route_going_away ()
697 {
698         _io.reset ();
699 }
700
701 void
702 Diskstream::calculate_record_range (Evoral::OverlapType ot, framepos_t transport_frame, framecnt_t nframes,
703                                     framecnt_t & rec_nframes, framecnt_t & rec_offset)
704 {
705         switch (ot) {
706         case Evoral::OverlapNone:
707                 rec_nframes = 0;
708                 break;
709
710         case Evoral::OverlapInternal:
711                 /*     ----------    recrange
712                  *       |---|       transrange
713                  */
714                 rec_nframes = nframes;
715                 rec_offset = 0;
716                 break;
717
718         case Evoral::OverlapStart:
719                 /*    |--------|    recrange
720                  *  -----|          transrange
721                  */
722                 rec_nframes = transport_frame + nframes - first_recordable_frame;
723                 if (rec_nframes) {
724                         rec_offset = first_recordable_frame - transport_frame;
725                 }
726                 break;
727
728         case Evoral::OverlapEnd:
729                 /*    |--------|    recrange
730                  *       |--------  transrange
731                  */
732                 rec_nframes = last_recordable_frame - transport_frame;
733                 rec_offset = 0;
734                 break;
735
736         case Evoral::OverlapExternal:
737                 /*    |--------|    recrange
738                  *  --------------  transrange
739                  */
740                 rec_nframes = last_recordable_frame - first_recordable_frame;
741                 rec_offset = first_recordable_frame - transport_frame;
742                 break;
743         }
744
745         DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose ("%1 rec? %2 @ %3 (for %4) FRF %5 LRF %6 : rf %7 @ %8\n",
746                                                               _name, enum_2_string (ot), transport_frame, nframes,
747                                                               first_recordable_frame, last_recordable_frame, rec_nframes, rec_offset));
748 }
749
750 void
751 Diskstream::prepare_to_stop (framepos_t transport_frame, framepos_t audible_frame)
752 {
753         switch (_alignment_style) {
754         case ExistingMaterial:
755                 last_recordable_frame = transport_frame + _capture_offset;
756                 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));
757                 break;
758
759         case CaptureTime:
760                 last_recordable_frame = audible_frame; // note that capture_offset is zero
761                 /* we may already have captured audio before the last_recordable_frame (audible frame),
762                    so deal with this.
763                 */
764                 if (last_recordable_frame > capture_start_frame) {
765                         capture_captured = min (capture_captured, last_recordable_frame - capture_start_frame);
766                 }
767                 DEBUG_TRACE (DEBUG::CaptureAlignment, string_compose("%1: prepare to stop sets last recordable frame to audible frame @ %2\n", _name, audible_frame));
768                 break;
769         }
770
771 }
772
773 void
774 Diskstream::engage_record_enable ()
775 {
776         g_atomic_int_set (&_record_enabled, 1);
777 }
778
779 void
780 Diskstream::disengage_record_enable ()
781 {
782         g_atomic_int_set (&_record_enabled, 0);
783 }
784
785 void
786 Diskstream::engage_record_safe ()
787 {
788         g_atomic_int_set (&_record_safe, 1);
789 }
790
791 void
792 Diskstream::disengage_record_safe ()
793 {
794         g_atomic_int_set (&_record_safe, 0);
795 }
796
797 framecnt_t
798 Diskstream::default_disk_read_chunk_frames()
799 {
800         return 65536;
801 }       
802
803 framecnt_t
804 Diskstream::default_disk_write_chunk_frames ()
805 {
806         return 65536;
807 }
808
809 void
810 Diskstream::set_buffering_parameters (BufferingPreset bp)
811 {
812         framecnt_t read_chunk_size;
813         framecnt_t read_buffer_size;
814         framecnt_t write_chunk_size;
815         framecnt_t write_buffer_size;
816
817         if (!get_buffering_presets (bp, read_chunk_size, read_buffer_size, write_chunk_size, write_buffer_size)) {
818                 return;
819         }
820         
821         disk_read_chunk_frames = read_chunk_size;
822         disk_write_chunk_frames = write_chunk_size;
823         Config->set_audio_capture_buffer_seconds (write_buffer_size);
824         Config->set_audio_playback_buffer_seconds (read_buffer_size);
825
826         cerr << "Set buffering params to " << disk_read_chunk_frames << '|' << disk_write_chunk_frames << '|'
827              << Config->get_audio_playback_buffer_seconds() << '|'
828              << Config->get_audio_capture_buffer_seconds ()
829              << endl;
830 }
831
832 bool
833 Diskstream::get_buffering_presets (BufferingPreset bp,
834                                    framecnt_t& read_chunk_size,
835                                    framecnt_t& read_buffer_size,
836                                    framecnt_t& write_chunk_size,
837                                    framecnt_t& write_buffer_size)
838 {
839         switch (bp) {
840         case Small:
841                 read_chunk_size = 65536;  /* samples */
842                 write_chunk_size = 65536; /* samples */
843                 read_buffer_size = 5;  /* seconds */
844                 write_buffer_size = 5; /* seconds */
845                 break;
846
847         case Medium:
848                 read_chunk_size = 262144;  /* samples */
849                 write_chunk_size = 131072; /* samples */
850                 read_buffer_size = 10;  /* seconds */
851                 write_buffer_size = 10; /* seconds */
852                 break;
853
854         case Large:
855                 read_chunk_size = 524288; /* samples */
856                 write_chunk_size = 131072; /* samples */
857                 read_buffer_size = 20; /* seconds */
858                 write_buffer_size = 20; /* seconds */
859                 break;
860
861         default:
862                 return false;
863         }
864
865         return true;
866 }
867