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