2008-05-22 09:05:49 +02:00
/*******************************************************************************/
/* Copyright (C) 2008 Jonathan Moore Liles */
/* */
/* This program is free software; you can redistribute it and/or modify it */
/* under the terms of the GNU General Public License as published by the */
/* Free Software Foundation; either version 2 of the License, or (at your */
/* option) any later version. */
/* */
/* This program is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for */
/* more details. */
/* */
/* You should have received a copy of the GNU General Public License along */
/* with This program; see the file COPYING. If not,write to the Free Software */
/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
/*******************************************************************************/
/**********/
/* Engine */
/**********/
# include "../Audio_Region.H"
# include "Audio_File.H"
# include "dsp.h"
2008-07-30 04:30:45 +02:00
# include "const.h"
# include "const.h"
2010-02-01 07:25:16 +01:00
# include "debug.h"
# include "Thread.H"
2008-06-02 03:13:18 +02:00
2012-06-07 08:43:36 +02:00
/** Apply a (portion of) fade from /start/ to a buffer up to size /nframes/. */
2008-05-22 09:05:49 +02:00
void
2012-06-07 08:43:36 +02:00
Audio_Region : : Fade : : apply ( sample_t * buf , Audio_Region : : Fade : : fade_dir_e dir , nframes_t start , nframes_t nframes ) const
2008-05-22 09:05:49 +02:00
{
// printf( "apply fade %s: start=%ld end=%lu\n", dir == Fade::Out ? "out" : "in", start, end );
if ( ! nframes )
return ;
2012-06-07 08:43:36 +02:00
nframes_t n = nframes ;
2008-05-22 09:05:49 +02:00
2012-06-07 08:43:36 +02:00
const double inc = increment ( ) ;
double fi = start / ( double ) length ;
2008-05-22 09:05:49 +02:00
if ( dir = = Fade : : Out )
{
fi = 1.0f - fi ;
for ( ; n - - ; fi - = inc )
* ( buf + + ) * = gain ( fi ) ;
}
else
for ( ; n - - ; fi + = inc )
* ( buf + + ) * = gain ( fi ) ;
}
2013-08-23 03:02:11 +02:00
void
Audio_Region : : Fade : : apply_interleaved ( sample_t * buf , Audio_Region : : Fade : : fade_dir_e dir , nframes_t start , nframes_t nframes , int channels ) const
{
// printf( "apply fade %s: start=%ld end=%lu\n", dir == Fade::Out ? "out" : "in", start, end );
if ( ! nframes )
return ;
nframes_t n = nframes ;
const double inc = increment ( ) ;
double fi = start / ( double ) length ;
2016-12-07 02:19:12 +01:00
if ( n > length - start )
/* don't try to apply fade to more samples than specified by the fade length... */
n = length - start ;
/* ASSERT( nframes < length - start, "Attempt to apply fade to more samples than its length" ); */
2013-08-23 03:02:11 +02:00
if ( dir = = Fade : : Out )
{
2016-12-07 02:19:12 +01:00
fi = 1.0 - fi ;
2013-08-23 03:02:11 +02:00
for ( ; n - - ; fi - = inc )
{
const float g = gain ( fi ) ;
for ( int i = channels ; i - - ; )
* ( buf + + ) * = g ;
}
}
else
for ( ; n - - ; fi + = inc )
{
const float g = gain ( fi ) ;
for ( int i = channels ; i - - ; )
* ( buf + + ) * = g ;
}
}
2016-12-09 08:27:46 +01:00
static
void
apply_fade ( sample_t * buf , const int channels , Audio_Region : : Fade fade , const nframes_t bS , const nframes_t bE , const nframes_t edge , Audio_Region : : Fade : : fade_dir_e dir )
{
const nframes_t bSS = dir = = Audio_Region : : Fade : : Out ? bS + fade . length : bS ;
const nframes_t fade_start = bSS > edge ? bSS - edge : 0 ;
const nframes_t fade_offset = bSS > edge ? 0 : edge - bSS ;
fade . apply_interleaved ( buf + ( channels * fade_offset ) , dir , fade_start , ( bE - bS ) - fade_offset , channels ) ;
} ;
2013-08-23 03:02:11 +02:00
/** read the overlapping at /pos/ for /nframes/ of this region into
/ buf / , where / pos / is in timeline frames . / buf / is an interleaved
buffer of / channels / channels */
2008-05-22 09:05:49 +02:00
/* this runs in the diskstream thread. */
nframes_t
2013-08-23 03:02:11 +02:00
Audio_Region : : read ( sample_t * buf , bool buf_is_empty , nframes_t pos , nframes_t nframes , int channels ) const
2008-05-22 09:05:49 +02:00
{
2008-06-02 03:13:18 +02:00
THREAD_ASSERT ( Playback ) ;
2008-05-22 09:05:49 +02:00
const Range r = _range ;
2020-10-07 08:45:37 +02:00
/* ASSERT( r.legnth > 0, "Region has zero length!" ); */
2016-12-07 02:19:12 +01:00
const nframes_t rS = r . start ;
2020-10-07 08:45:37 +02:00
const nframes_t rE = r . start + r . length ; /* one more than the last frame of the region! */
2016-12-07 02:19:12 +01:00
const nframes_t bS = pos ;
2020-10-07 08:45:37 +02:00
const nframes_t bE = pos + nframes ; /* one more than the last frame of the buffer! */
2016-12-07 02:19:12 +01:00
/* do nothing if region isn't inside buffer */
if ( bS > rE | | bE < rS )
2008-05-22 09:05:49 +02:00
return 0 ;
2013-08-23 03:02:11 +02:00
sample_t * cbuf = NULL ;
if ( buf_is_empty & & channels = = _clip - > channels ( ) )
{
/* in this case we don't need a temp buffer */
cbuf = buf ;
}
else
{
/* temporary buffer to hold interleaved samples from the clip */
cbuf = buffer_alloc ( _clip - > channels ( ) * nframes ) ;
memset ( cbuf , 0 , _clip - > channels ( ) * sizeof ( sample_t ) * nframes ) ;
}
2008-05-22 09:05:49 +02:00
/* calculate offsets into file and sample buffer */
2016-12-09 08:27:46 +01:00
const nframes_t sO = bS < rS ? 0 : bS - rS ; /* offset into source */
const nframes_t bO = bS < rS ? rS - bS : 0 ; /* offset into buffer (when region start is after beginning of buffer) */
nframes_t cnt = nframes ; /* number of frames to read */
2008-05-22 09:05:49 +02:00
2016-12-07 02:19:12 +01:00
/* if region ends within this buffer, don't read beyond end */
if ( bE > rE )
cnt = nframes - ( bE - rE ) ;
2008-05-22 09:05:49 +02:00
2016-12-09 08:27:46 +01:00
cnt - = bO ;
2012-06-13 05:53:21 +02:00
const nframes_t len = cnt ;
2008-05-22 09:05:49 +02:00
2013-08-23 03:02:11 +02:00
/* FIXME: keep the declick defults someplace else */
Fade declick ;
declick . length = ( float ) timeline - > sample_rate ( ) * 0.01f ;
declick . type = Fade : : Sigmoid ;
2016-12-07 02:19:12 +01:00
/* FIXME: what was this for? */
if ( bO > = nframes )
2013-08-23 03:02:11 +02:00
{
cnt = 0 ;
goto done ;
}
2016-12-07 02:19:12 +01:00
/* FIXME: what was this for? */
2008-05-22 09:05:49 +02:00
if ( len = = 0 )
2013-08-23 03:02:11 +02:00
{
cnt = 0 ;
goto done ;
}
2008-05-22 09:05:49 +02:00
/* now that we know how much and where to read, get on with it */
// printf( "reading region ofs = %lu, sofs = %lu, %lu-%lu\n", ofs, sofs, start, end );
2008-05-31 22:29:01 +02:00
if ( _loop )
{
2016-12-09 08:27:46 +01:00
if ( _loop < nframes )
{
/* very small loop or very large buffer... */
WARNING ( " Loop size (%lu) is smaller than buffer size (%lu). Behavior undefined. " , _loop , nframes ) ;
}
2020-10-07 08:45:37 +02:00
const nframes_t lO = sO % _loop , /* how far we are into the loop */
nthloop = sO / _loop , /* which loop iteration */
seam_L = rS + ( nthloop * _loop ) , /* receding seam */
seam_R = rS + ( ( nthloop + 1 ) * _loop ) ; /* upcoming seam */
2013-08-23 03:02:11 +02:00
/* read interleaved channels */
2020-10-07 08:45:37 +02:00
if (
/* this buffer contains a loop boundary, which lies on neither the first or the last frame, and therefore requires a splice of two reads from separate parts of the region. */
seam_R > bS & & seam_R < bE
& &
/* but if the end seam of the loop is also the end seam of the region, we only need one read */
seam_R ! = rE
& &
/* also, if the seam is within the buffer, but beyond the end of the region (as in last loop iteration), do the simpler read in the else clause */
seam_R < = rE
)
2008-05-31 23:00:00 +02:00
{
2020-10-07 08:45:37 +02:00
/* this buffer covers a loop boundary */
2008-05-31 23:00:00 +02:00
/* read the first part */
2020-10-07 08:45:37 +02:00
cnt = _clip - > read (
cbuf + ( _clip - > channels ( ) * bO ) , /* buf */
- 1 , /* chan */
r . offset + lO , /* start */
( seam_R - bS ) - bO /* len */
) ;
/* ASSERT( len > cnt, "Error in region looping calculations" ); */
2008-05-31 23:00:00 +02:00
/* read the second part */
2020-10-07 08:45:37 +02:00
cnt + = _clip - > read (
cbuf + ( _clip - > channels ( ) * ( bO + cnt ) ) , /* buf */
- 1 , /* chan */
r . offset + 0 , /* start */
( len - cnt ) - bO /* len */
) ;
2008-05-31 22:29:01 +02:00
2016-12-09 08:27:46 +01:00
/* assert( cnt == len ); */
2008-05-31 23:00:00 +02:00
}
else
2016-12-09 08:27:46 +01:00
/* buffer contains no loop seam, perform straight read. */
cnt = _clip - > read ( cbuf + ( _clip - > channels ( ) * bO ) , - 1 , r . offset + lO , cnt ) ;
2012-06-23 06:13:05 +02:00
2016-12-09 08:27:46 +01:00
for ( int i = 0 ; i < 2 ; i + + )
2012-06-23 06:13:05 +02:00
{
2016-12-09 08:27:46 +01:00
nframes_t seam = i ? seam_R : seam_L ;
2012-06-23 06:13:05 +02:00
2016-12-09 08:27:46 +01:00
if ( seam ! = rS & & seam ! = rE ) /* not either end of the region */
{
if ( seam > = bS & & seam < = bE + declick . length )
/* fade out previous loop segment */
apply_fade ( cbuf , _clip - > channels ( ) , declick , bS , bE , seam , Fade : : Out ) ;
if ( seam < = bE & & seam + declick . length > = bS )
/* fade in next loop segment */
apply_fade ( cbuf , _clip - > channels ( ) , declick , bS , bE , seam , Fade : : In ) ;
}
2012-06-23 06:13:05 +02:00
}
2008-05-31 22:29:01 +02:00
}
else
2016-12-07 02:19:12 +01:00
{
// DMESSAGE("Clip read, rL=%lu, b0=%lu, sO=%lu, r.offset=%lu, len=%lu",r.length,bO,sO,r.offset,len);
cnt = _clip - > read ( cbuf + ( _clip - > channels ( ) * bO ) , - 1 , sO + r . offset , len ) ;
}
2008-05-22 09:05:49 +02:00
2012-06-13 05:53:21 +02:00
if ( ! cnt )
2013-08-23 03:02:11 +02:00
goto done ;
2012-06-13 05:53:21 +02:00
2008-05-22 09:05:49 +02:00
/* apply gain */
2013-08-23 03:02:11 +02:00
/* just do the whole buffer so we can use the alignment optimized
* version when we ' re in the middle of a region , this will be full
* anyway */
buffer_apply_gain ( cbuf , nframes * _clip - > channels ( ) , _scale ) ;
2008-05-22 09:05:49 +02:00
2016-12-07 02:19:12 +01:00
/* perform fade/declicking if necessary */
2008-05-22 09:05:49 +02:00
{
2012-06-07 08:43:36 +02:00
assert ( cnt < = nframes ) ;
2008-05-22 09:05:49 +02:00
Fade fade ;
2020-10-24 07:08:13 +02:00
/* disabling fade also disables de-clicking for perfectly abutted edits. */
if ( fade . type ! = Fade : : Disabled )
{
fade = declick < _fade_in ? _fade_in : declick ;
/* do fade in if necessary */
if ( sO < fade . length )
apply_fade ( cbuf , _clip - > channels ( ) , fade , bS , bE , rS , Fade : : In ) ;
fade = declick < _fade_out ? _fade_out : declick ;
/* do fade out if necessary */
if ( sO + cnt + fade . length > r . length )
apply_fade ( cbuf , _clip - > channels ( ) , fade , bS , bE , rE , Fade : : Out ) ;
}
2013-08-23 03:02:11 +02:00
}
if ( buf ! = cbuf )
{
/* now interleave the clip channels into the playback buffer */
for ( int i = 0 ; i < channels & & i < _clip - > channels ( ) ; i + + )
{
if ( buf_is_empty )
buffer_interleaved_copy ( buf , cbuf , i , i , channels , _clip - > channels ( ) , nframes ) ;
else
buffer_interleaved_mix ( buf , cbuf , i , i , channels , _clip - > channels ( ) , nframes ) ;
}
}
done :
if ( buf ! = cbuf )
{
free ( cbuf ) ;
2008-05-22 09:05:49 +02:00
}
return cnt ;
}
/** prepare for capturing */
void
Audio_Region : : prepare ( void )
{
2012-06-16 03:53:40 +02:00
THREAD_ASSERT ( Capture ) ;
DMESSAGE ( " Preparing capture region " ) ;
2013-03-14 02:00:03 +01:00
// log_start();
2008-05-22 09:05:49 +02:00
}
2013-04-06 02:13:51 +02:00
class SequenceRedrawRequest {
public :
nframes_t start ;
nframes_t length ;
Sequence * sequence ;
} ;
static
void
sequence_redraw_request_handle ( void * v )
{
THREAD_ASSERT ( UI ) ;
SequenceRedrawRequest * o = ( SequenceRedrawRequest * ) v ;
o - > sequence - > damage ( FL_DAMAGE_USER1 , timeline - > offset_to_x ( o - > start ) , o - > sequence - > y ( ) , timeline - > ts_to_x ( o - > length ) , o - > sequence - > h ( ) ) ;
delete o ;
} ;
2008-05-22 09:05:49 +02:00
/** write /nframes/ from /buf/ to source. /buf/ is interleaved and
must match the channel layout of the write source ! */
nframes_t
Audio_Region : : write ( nframes_t nframes )
{
2008-06-02 03:13:18 +02:00
THREAD_ASSERT ( Capture ) ;
2008-05-22 09:05:49 +02:00
if ( 0 = = ( timeline - > ts_to_x ( _range . length ) % 20 ) )
{
2012-04-02 03:27:27 +02:00
int W = 20 ;
2008-05-22 09:05:49 +02:00
if ( W )
{
2013-04-06 02:13:51 +02:00
SequenceRedrawRequest * o = new SequenceRedrawRequest ( ) ;
o - > sequence = sequence ( ) ;
o - > start = _range . start + ( _range . length - timeline - > x_to_ts ( 20 ) ) ;
o - > length = timeline - > x_to_ts ( 20 ) ;
Fl : : awake ( sequence_redraw_request_handle , o ) ;
2008-05-22 09:05:49 +02:00
}
}
2013-10-25 03:21:45 +02:00
timeline - > sequence_lock . wrlock ( ) ;
2012-04-02 03:27:27 +02:00
_range . length + = nframes ;
2013-10-25 03:21:45 +02:00
timeline - > sequence_lock . unlock ( ) ;
2008-05-22 09:05:49 +02:00
return nframes ;
}
/** finalize region capture. Assumes that this *is* a captured region
2012-06-23 06:13:05 +02:00
and that no other regions refer to the same source */
2008-05-22 09:05:49 +02:00
bool
Audio_Region : : finalize ( nframes_t frame )
{
2008-06-02 03:13:18 +02:00
THREAD_ASSERT ( Capture ) ;
2008-05-28 03:45:46 +02:00
DMESSAGE ( " finalizing capture region " ) ;
2013-10-25 03:21:45 +02:00
timeline - > sequence_lock . wrlock ( ) ;
2008-05-22 09:05:49 +02:00
_range . length = frame - _range . start ;
2013-10-25 03:21:45 +02:00
timeline - > sequence_lock . unlock ( ) ;
2008-05-22 09:05:49 +02:00
_clip - > close ( ) ;
_clip - > open ( ) ;
2013-03-14 02:00:03 +01:00
log_create ( ) ;
// log_end();
2008-05-22 09:05:49 +02:00
return true ;
}