2 Copyright (C) 2004-2007 Paul Davis
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.
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.
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.
23 #include "pbd/error.h"
25 #include "ardour/types.h"
26 #include "ardour/stretch.h"
27 #include "ardour/audiofilesource.h"
28 #include "ardour/session.h"
29 #include "ardour/audioregion.h"
34 using namespace ARDOUR
;
36 using namespace soundtouch
;
38 STStretch::STStretch (Session
& s
, TimeFXRequest
& req
)
44 /* the soundtouch code wants a *tempo* change percentage, which is
45 of opposite sign to the length change.
48 percentage
= -tsr
.time_fraction
;
50 st
.setSampleRate (s
.frame_rate());
52 st
.setTempoChange (percentage
);
53 st
.setPitchSemiTones (0);
56 st
.setSetting(SETTING_USE_QUICKSEEK
, tsr
.quick_seek
);
57 st
.setSetting(SETTING_USE_AA_FILTER
, tsr
.antialias
);
62 STStretch::~STStretch ()
67 STStretch::run (boost::shared_ptr
<Region
> a_region
)
70 framecnt_t total_frames
;
73 const framecnt_t bufsize
= 16384;
74 gain_t
*gain_buffer
= 0;
83 boost::shared_ptr
<AudioRegion
> region
= boost::dynamic_pointer_cast
<AudioRegion
>(a_region
);
85 total_frames
= region
->length() * region
->n_channels();
88 /* the name doesn't need to be super-precise, but allow for 2 fractional
89 digits just to disambiguate close but not identical stretches.
92 snprintf (suffix
, sizeof (suffix
), "@%d", (int) floor (tsr
.time_fraction
* 100.0f
));
94 /* create new sources */
96 if (make_new_sources (region
, nsrcs
, suffix
)) {
100 gain_buffer
= new gain_t
[bufsize
];
101 buffer
= new Sample
[bufsize
];
103 // soundtouch throws runtime_error on error
106 for (uint32_t i
= 0; i
< nsrcs
.size(); ++i
) {
108 boost::shared_ptr
<AudioSource
> asrc
109 = boost::dynamic_pointer_cast
<AudioSource
>(nsrcs
[i
]);
112 framecnt_t this_read
= 0;
116 while (!tsr
.cancel
&& pos
< region
->length()) {
117 framecnt_t this_time
;
119 this_time
= min (bufsize
, region
->length() - pos
);
121 /* read from the master (original) sources for the region,
122 not the ones currently in use, in case it's already been
126 if ((this_read
= region
->master_read_at (buffer
, buffer
, gain_buffer
, pos
+ region
->position(), this_time
)) != this_time
) {
127 error
<< string_compose (_("tempoize: error reading data from %1"), asrc
->name()) << endmsg
;
134 tsr
.progress
= (float) done
/ total_frames
;
136 st
.putSamples (buffer
, this_read
);
138 while ((this_read
= st
.receiveSamples (buffer
, bufsize
)) > 0 && !tsr
.cancel
) {
139 if (asrc
->write (buffer
, this_read
) != this_read
) {
140 error
<< string_compose (_("error writing tempo-adjusted data to %1"), asrc
->name()) << endmsg
;
150 while (!tsr
.cancel
&& (this_read
= st
.receiveSamples (buffer
, bufsize
)) > 0) {
151 if (asrc
->write (buffer
, this_read
) != this_read
) {
152 error
<< string_compose (_("error writing tempo-adjusted data to %1"), asrc
->name()) << endmsg
;
158 } catch (runtime_error
& err
) {
159 error
<< _("timefx code failure. please notify ardour-developers.") << endmsg
;
160 error
<< err
.what() << endmsg
;
164 new_name
= region
->name();
165 at
= new_name
.find ('@');
167 // remove any existing stretch indicator
169 if (at
!= string::npos
&& at
> 2) {
170 new_name
= new_name
.substr (0, at
- 1);
175 ret
= finish (region
, nsrcs
, new_name
);
177 /* now reset ancestral data for each new region */
179 for (vector
<boost::shared_ptr
<Region
> >::iterator x
= results
.begin(); x
!= results
.end(); ++x
) {
180 framepos_t astart
= (*x
)->ancestral_start();
181 framepos_t alength
= (*x
)->ancestral_length();
185 // note: tsr.fraction is a percentage of original length. 100 = no change,
186 // 50 is half as long, 200 is twice as long, etc.
188 float stretch
= (*x
)->stretch() * (tsr
.time_fraction
/100.0);
190 start
= (framepos_t
) floor (astart
+ ((astart
- (*x
)->start()) / stretch
));
191 length
= (framecnt_t
) floor (alength
/ stretch
);
193 (*x
)->set_ancestral_data (start
, length
, stretch
, (*x
)->shift());
198 delete [] gain_buffer
;
201 if (ret
|| tsr
.cancel
) {
202 for (SourceList::iterator si
= nsrcs
.begin(); si
!= nsrcs
.end(); ++si
) {
203 (*si
)->mark_for_remove ();