Update NTK.
[nondaw.git] / timeline / src / Engine / Record_DS.C
blob629a49e4b80ceeb908124ddac96de84265fde833
2 /*******************************************************************************/
3 /* Copyright (C) 2008 Jonathan Moore Liles                                     */
4 /*                                                                             */
5 /* This program is free software; you can redistribute it and/or modify it     */
6 /* under the terms of the GNU General Public License as published by the       */
7 /* Free Software Foundation; either version 2 of the License, or (at your      */
8 /* option) any later version.                                                  */
9 /*                                                                             */
10 /* This program is distributed in the hope that it will be useful, but WITHOUT */
11 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       */
12 /* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for   */
13 /* more details.                                                               */
14 /*                                                                             */
15 /* You should have received a copy of the GNU General Public License along     */
16 /* with This program; see the file COPYING.  If not,write to the Free Software */
17 /* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
18 /*******************************************************************************/
20 /* Handles streaming from track inputs to disk */
22 /* FIXME: we shouldn't depend on these */
23 #include "../Timeline.H" // for locking
24 #include "../Audio_Sequence.H"
25 #include "../Track.H"
27 // #include "Port.H"
28 #include "Record_DS.H"
29 #include "Engine.H"
30 #include "dsp.h"
32 #include "const.h"
33 #include "debug.h"
34 #include "Thread.H"
36 const Audio_Region *
37 Record_DS::capture_region ( void ) const
39     if ( _capture )
40         return _capture->region;
41     else
42         return NULL;
45 Track::Capture *
46 Record_DS::capture ( void )
48     return _capture;
51 /** write /nframes/ from buf to the capture file of the attached track */
52 void
53 Record_DS::write_block ( sample_t *buf, nframes_t nframes )
55     THREAD_ASSERT( Capture );
57     /* stupid chicken/egg */
58     if ( ! ( timeline && sequence() ) )
59         return;
61 //    timeline->wrlock();
63     track()->write( _capture, buf, nframes );
65     _frames_written += nframes;
67 //    timeline->unlock();
70 #define AVOID_UNNECESSARY_COPYING 1
72 void
73 Record_DS::disk_thread ( void )
75     _thread.name( "Capture" );
77     DMESSAGE( "capture thread running..." );
79     track()->record( _capture, _frame );
81     const nframes_t nframes = _nframes * _disk_io_blocks;
83     /* buffer to hold the interleaved data returned by the track reader */
84     sample_t *buf = new sample_t[ nframes * channels() ];
85 #ifndef AVOID_UNNECESSARY_COPYING
86     sample_t *cbuf = new sample_t[ nframes ];
87 #endif
89     const size_t block_size = nframes * sizeof( sample_t );
91     int blocks_ready = 0;
93     while ( wait_for_block() )
94     {
95         if ( ++blocks_ready < _disk_io_blocks )
96             continue;
97         else
98             blocks_ready = 0;
100         /* pull data from the per-channel ringbuffers and interlace it */
101         for ( int i = channels(); i--; )
102         {
104 #ifdef AVOID_UNNECESSARY_COPYING
106             /* interleave direcectly from the ringbuffer to avoid
107              * unnecessary copying */
109             jack_ringbuffer_data_t rbd[2];
111             memset( rbd, 0, sizeof( rbd ) );
113             jack_ringbuffer_get_read_vector( _rb[ i ], rbd );
115             if ( rbd[ 0 ].len >= block_size )
116             {
117                 /* it'll all fit in one go */
118                 buffer_interleave_one_channel( buf, (sample_t*)rbd[ 0 ].buf, i, channels(), nframes );
119             }
120             else if ( rbd[ 0 ].len + rbd[ 1 ].len >= block_size )
121             {
122                 /* there's enough space in the ringbuffer, but it's not contiguous */
124                 assert( ! ( rbd[ 0 ].len % sizeof( sample_t )  ) );
126                 const nframes_t f = rbd[ 0 ].len / sizeof( sample_t );
128                 /* do the first half */
129                 buffer_deinterleave_one_channel( (sample_t*)rbd[ 0 ].buf, buf, i, channels(), f );
130                 buffer_interleave_one_channel( buf, (sample_t*)rbd[ 0 ].buf, i, channels(), f );
132                 assert( rbd[ 1 ].len >= ( nframes - f ) * sizeof( sample_t ) );
134                 /* do the second half */
135                 buffer_interleave_one_channel( buf + f, (sample_t*)rbd[ 0 ].buf, i, channels(), nframes - f );
137             }
138             else
139                 ++_xruns;
141             jack_ringbuffer_read_advance( _rb[ i ], block_size );
142 #else
143             if ( jack_ringbuffer_read( _rb[ i ], (char*)cbuf, block_size ) < block_size )
144                 ++_xruns;
146             buffer_interleave_one_channel( buf, cbuf, i, channels(), nframes );
147 #endif
149         }
151         write_block( buf, nframes );
153     }
155     DMESSAGE( "capture thread terminating" );
157     /* flush what remains in the buffer out to disk */
159     {
160         /* use JACk sized blocks for this last bit */
161         const nframes_t nframes = _nframes;
162         const size_t block_size = _nframes * sizeof( sample_t );
164 #ifdef AVOID_UNNECESSARY_COPYING
165         sample_t *cbuf = new sample_t[ nframes ];
166 #endif
168         while ( blocks_ready-- > 0 || ( ! sem_trywait( &_blocks ) && errno != EAGAIN ) )
169         {
171                 for ( int i = channels(); i--; )
172                 {
173                     jack_ringbuffer_read( _rb[ i ], (char*)cbuf, block_size );
175                     buffer_interleave_one_channel( buf, cbuf, i, channels(), nframes );
176                 }
178                 const nframes_t frames_remaining = (_stop_frame - _frame ) - _frames_written;
180                 if ( frames_remaining < nframes )
181                 {
182                     /* this is the last block, might be partial  */
183                     write_block( buf, frames_remaining );
184                     break;
185                 }
186                 else
187                     write_block( buf, nframes );
188         }
190 #ifdef AVOID_UNNECESSARY_COPYING
191         delete[] cbuf;
192 #endif
194     }
196     delete[] buf;
197 #ifndef AVOID_UNNECESSARY_COPYING
198     delete[] cbuf;
199 #endif
201     DMESSAGE( "finalzing capture" );
203     Track::Capture *c = _capture;
205     _capture = NULL;
207     /* now finalize the recording */
209     track()->finalize( c, _stop_frame );
211     delete c;
213     _terminate = false;
215     DMESSAGE( "capture thread gone" );
217     _thread.exit();
221 /** begin recording */
222 void
223 Record_DS::start ( nframes_t frame )
225     THREAD_ASSERT( UI );
227     if ( _recording )
228     {
229         WARNING( "programming error: attempt to start recording while recording is still in progress" );
230         return;
231     }
233 /*     /\* FIXME: safe to do this here? *\/ */
234 /*     flush(); */
236     DMESSAGE( "recording started at frame %lu", (unsigned long)frame);
238     _frame = frame;
240     _capture = new Track::Capture;
242     run();
244     _recording = true;
248 /** finalize the recording process. */
249 void
250 Record_DS::stop ( nframes_t frame )
252     THREAD_ASSERT( UI );
254     if ( ! _recording )
255     {
256         WARNING( "programming error: attempt to stop recording when no recording is being made" );
257         return;
258     }
260     _recording = false;
262     _stop_frame = frame;
264 //    detach();
266     DMESSAGE( "recording finished" );
270 #include "../Transport.H"
271 extern Transport *transport;
273 /** read from the attached track's ports and stuff the ringbuffers */
274 nframes_t
275 Record_DS::process ( nframes_t nframes )
277     THREAD_ASSERT( RT );
279     if ( ! _recording )
280         return 0;
282      if ( transport->frame < _frame  )
283          return 0;
285 /*    DMESSAGE( "recording actually happening at %lu (start frame %lu)", (unsigned long)transport->frame, (unsigned long)_frame); */
287     nframes_t offset = 0;
289     if ( _frame > transport->frame &&
290          _frame < transport->frame + nframes )
291     {
292         /* The record start frame falls somewhere within the current
293            buffer.  We must discard the unneeded portion and only
294            stuff the part requested into the ringbuffer. */
296         offset = _frame - transport->frame;
298 /*         DMESSAGE( "offset = %lu", (unsigned long)offset ); */
299     }
301     const size_t offset_size = offset * sizeof( sample_t );
302     const size_t block_size = ( nframes * sizeof( sample_t ) ) - offset_size;
304     for ( int i = channels(); i--;  )
305     {
306         /* read the entire input buffer */
307         void *buf = track()->input[ i ].buffer( nframes );
309 /*         if ( buffer_is_digital_black( (sample_t*)buf, nframes ) ) */
310 /*              DWARNING( "recording an entirely blank buffer" ); */
312         /* FIXME: this results in a ringbuffer size that is no longer
313          necessarily a multiple of nframes...  how will the other side
314          handle that? */
315         if ( jack_ringbuffer_write( _rb[ i ], (char*)buf + offset, block_size ) < block_size )
316         {
317             ++_xruns;
318             memset( buf, 0, block_size );
319             /* FIXME: we need to resync somehow */
320         }
321     }
323     block_processed();
325     /* FIXME: bogus */
326     return nframes;