2 Copyright (C) 2008 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "ardour/export_processor.h"
23 #include "pbd/error.h"
24 #include "pbd/filesystem.h"
26 #include "ardour/session.h"
27 #include "ardour/audiofile_tagger.h"
28 #include "ardour/broadcast_info.h"
29 #include "ardour/export_failed.h"
30 #include "ardour/export_filename.h"
31 #include "ardour/export_status.h"
32 #include "ardour/export_format_specification.h"
41 sigc::signal
<void, Glib::ustring
> ExportProcessor::WritingFile
;
43 ExportProcessor::ExportProcessor (Session
& session
) :
45 status (session
.get_export_status()),
46 blocksize (session
.get_block_size()),
47 frame_rate (session
.frame_rate())
52 ExportProcessor::~ExportProcessor ()
58 ExportProcessor::reset ()
70 ExportProcessor::prepare (FormatPtr format
, FilenamePtr fname
, uint32_t chans
, bool split
, nframes_t start
)
75 /* Reset just to be sure all references are dropped */
79 /* Get parameters needed later on */
85 broadcast_info
= format
->has_broadcast_info();
86 normalize
= format
->normalize();
87 trim_beginning
= format
->trim_beginning();
88 trim_end
= format
->trim_end();
89 silence_beginning
= format
->silence_beginning();
90 silence_end
= format
->silence_end();
94 src
.reset (new SampleRateConverter (channels
, frame_rate
, format
->sample_rate(), format
->src_quality()));
96 /* Construct export pipe to temp file */
98 status
->stage
= export_PostProcess
;
101 /* Normalizing => we need a normalizer, peak reader and tempfile */
103 normalizer
.reset (new Normalizer (channels
, format
->normalize_target()));
105 peak_reader
.reset (new PeakReader (channels
));
106 temp_file
.reset (new ExportTempFile (channels
, format
->sample_rate()));
108 src
->pipe_to (peak_reader
);
109 peak_reader
->pipe_to (temp_file
);
111 } else if (trim_beginning
|| trim_end
) {
112 /* Not normalizing, but silence will be trimmed => need for a tempfile */
114 temp_file
.reset (new ExportTempFile (channels
, format
->sample_rate()));
115 src
->pipe_to (temp_file
);
118 /* Due to complexity and time running out, a tempfile will be created for this also... */
120 temp_file
.reset (new ExportTempFile (channels
, format
->sample_rate()));
121 src
->pipe_to (temp_file
);
124 /* Ensure directory exists */
126 sys::path
folder (filename
->get_folder());
127 if (!sys::exists (folder
)) {
128 if (!sys::create_directory (folder
)) {
129 throw ExportFailed (X_("sys::create_directory failed for export dir"));
133 /* prep file sinks */
136 filename
->include_channel
= true;
137 for (uint32_t chn
= 1; chn
<= channels
; ++chn
) {
138 filename
->set_channel (chn
);
139 ExportFileFactory::FilePair pair
= ExportFileFactory::create (format
, 1, filename
->get_path (format
));
140 file_sinks
.push_back (pair
.first
);
141 writer_list
.push_back (pair
.second
);
142 WritingFile (filename
->get_path (format
));
146 ExportFileFactory::FilePair pair
= ExportFileFactory::create (format
, channels
, filename
->get_path (format
));
147 file_sinks
.push_back (pair
.first
);
148 writer_list
.push_back (pair
.second
);
149 WritingFile (filename
->get_path (format
));
152 /* Set position info */
154 nframes_t start_position
= ((double) format
->sample_rate() / frame_rate
) * start
+ 0.5;
156 for (FileWriterList::iterator it
= writer_list
.begin(); it
!= writer_list
.end(); ++it
) {
157 (*it
)->set_position (start_position
);
160 /* set broadcast info if necessary */
162 if (broadcast_info
) {
163 for (FileWriterList::iterator it
= writer_list
.begin(); it
!= writer_list
.end(); ++it
) {
166 bci
.set_from_session (session
, (*it
)->position());
168 boost::shared_ptr
<SndfileWriterBase
> sndfile_ptr
;
169 if ((sndfile_ptr
= boost::dynamic_pointer_cast
<SndfileWriterBase
> (*it
))) {
170 if (!bci
.write_to_file (sndfile_ptr
->get_sndfile())) {
171 std::cerr
<< bci
.get_error() << std::endl
;
174 if (!bci
.write_to_file ((*it
)->filename())) {
175 std::cerr
<< bci
.get_error() << std::endl
;
185 ExportProcessor::process (float * data
, nframes_t frames
)
187 nframes_t frames_written
= src
->write (data
, frames
);
188 temp_file_length
+= frames_written
;
189 return frames_written
;
193 ExportProcessor::prepare_post_processors ()
195 /* Set end of input and do last write */
197 src
->set_end_of_input ();
198 src
->write (&dummy
, 0);
200 /* Trim and add silence */
202 temp_file
->trim_beginning (trim_beginning
);
203 temp_file
->trim_end (trim_end
);
205 temp_file
->set_silence_beginning (silence_beginning
);
206 temp_file
->set_silence_end (silence_end
);
208 /* Set up normalizer */
211 normalizer
->set_peak (peak_reader
->get_peak ());
216 ExportProcessor::write_files ()
220 status
->stage
= export_Write
;
221 temp_file_position
= 0;
223 uint32_t buffer_size
= 4096; // TODO adjust buffer size?
224 float * buf
= new float[channels
* buffer_size
];
227 FloatSinkPtr disk_sink
;
230 disk_sink
= boost::dynamic_pointer_cast
<FloatSink
> (normalizer
);
231 normalizer
->pipe_to (file_sinks
[0]);
233 disk_sink
= file_sinks
[0];
238 /* Get buffers for each channel separately */
240 std::vector
<float *> chan_bufs
;
242 for (uint32_t i
= 0; i
< channels
; ++i
) {
243 chan_bufs
.push_back(new float[buffer_size
]);
246 /* de-interleave and write files */
248 while ((frames_read
= temp_file
->read (buf
, buffer_size
)) > 0) {
249 for (uint32_t channel
= 0; channel
< channels
; ++channel
) {
250 for (uint32_t i
= 0; i
< buffer_size
; ++i
) {
251 chan_bufs
[channel
][i
] = buf
[channel
+ (channels
* i
)];
254 normalizer
->pipe_to (file_sinks
[channel
]);
256 disk_sink
= file_sinks
[channel
];
258 disk_sink
->write (chan_bufs
[channel
], frames_read
);
261 if (status
->aborted()) { break; }
262 temp_file_position
+= frames_read
;
263 status
->progress
= (float) temp_file_position
/ temp_file_length
;
268 for (std::vector
<float *>::iterator it
= chan_bufs
.begin(); it
!= chan_bufs
.end(); ++it
) {
273 while ((frames_read
= temp_file
->read (buf
, buffer_size
)) > 0) {
274 disk_sink
->write (buf
, frames_read
);
276 if (status
->aborted()) { break; }
277 temp_file_position
+= frames_read
;
278 status
->progress
= (float) temp_file_position
/ temp_file_length
;
284 /* Tag files if necessary and send exported signal */
287 for (FileWriterList::iterator it
= writer_list
.begin(); it
!= writer_list
.end(); ++it
) {
289 AudiofileTagger::tag_file ((*it
)->filename(), session
.metadata());
291 session
.Exported ((*it
)->filename(), session
.name());
295 }; // namespace ARDOUR