new files from sakari, missed last time
[ardour.git] / libs / ardour / export_processor.cc
1 /*
2     Copyright (C) 2008 Paul Davis
3     Author: Sakari Bergen
4
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.
9
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.
14
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.
18
19 */
20
21 #include <ardour/export_processor.h>
22
23 #include <pbd/error.h>
24 #include <pbd/filesystem.h>
25
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>
33 #include <ardour/sndfile_helpers.h>
34
35 #include "i18n.h"
36
37 using namespace PBD;
38
39 namespace ARDOUR
40 {
41
42 sigc::signal<void, Glib::ustring> ExportProcessor::WritingFile;
43
44 ExportProcessor::ExportProcessor (Session & session) :
45   session (session),
46   status (session.export_status),
47   blocksize (session.get_block_size()),
48   frame_rate (session.frame_rate())
49 {
50         reset ();
51 }
52
53 ExportProcessor::~ExportProcessor ()
54 {
55
56 }
57
58 void
59 ExportProcessor::reset ()
60 {
61         file_sinks.clear();
62         writer_list.clear();
63         filename.reset();
64         normalizer.reset();
65         src.reset();
66         peak_reader.reset();
67         temp_file.reset();
68 }
69
70 int
71 ExportProcessor::prepare (FormatPtr format, FilenamePtr fname, uint32_t chans, bool split, nframes_t start)
72 {
73         session.export_status.format++;
74         temp_file_length = 0;
75
76         /* Reset just to be sure all references are dropped */
77         
78         reset();
79         
80         /* Get parameters needed later on */
81         
82         channels = chans;
83         split_files = split;
84         filename = fname;
85         tag = format->tag();
86         broadcast_info = format->has_broadcast_info();
87         normalize = format->normalize();
88         trim_beginning = format->trim_beginning();
89         trim_end = format->trim_end();
90         silence_beginning = format->silence_beginning();
91         silence_end = format->silence_end();
92         
93         /* SRC */
94         
95         src.reset (new SampleRateConverter (channels, frame_rate, format->sample_rate(), format->src_quality()));
96         
97         /* Construct export pipe to temp file */
98         
99         status.stage = export_PostProcess;
100         
101         if (normalize) {
102                 /* Normalizing => we need a normalizer, peak reader and tempfile */
103                 
104                 normalizer.reset (new Normalizer (channels, format->normalize_target()));
105         
106                 peak_reader.reset (new PeakReader (channels));
107                 temp_file.reset (new ExportTempFile (channels, format->sample_rate()));
108                 
109                 src->pipe_to (peak_reader);
110                 peak_reader->pipe_to (temp_file);
111         
112         } else if (trim_beginning || trim_end) {
113                 /* Not normalizing, but silence will be trimmed => need for a tempfile */
114         
115                 temp_file.reset (new ExportTempFile (channels, format->sample_rate()));
116                 src->pipe_to (temp_file);
117                 
118         } else {
119                 /* Due to complexity and time running out, a tempfile will be created for this also... */
120                 
121                 temp_file.reset (new ExportTempFile (channels, format->sample_rate()));
122                 src->pipe_to (temp_file);
123         }
124
125         /* File writer(s) */
126         
127         FloatSinkPtr (ExportProcessor::*prep_function) (FormatPtr, uint32_t, ustring const &);
128         
129         switch (format->type()) {
130           case ExportFormatBase::T_Sndfile:
131                 prep_function = &ExportProcessor::prepare_sndfile_writer;
132                 break;
133
134           default:
135                 throw ExportFailed (_("Export failed due to a programming error"), "Invalid format given for ExportProcessor::prepare!");
136                 break;
137         
138         }
139         
140         /* Ensure directory exists */
141         
142         sys::path folder (filename->get_folder());
143         if (!sys::exists (folder)) {
144                 if (!sys::create_directory (folder)) {
145                         throw ExportFailed ("Export could not create the directory you requested for", "sys::create_directory failed for export dir");
146                 }
147         }
148         
149         /* prep file sinks */
150         
151         if (split) {
152                 filename->include_channel = true;
153                 for (uint32_t chn = 1; chn <= channels; ++chn) {
154                         filename->set_channel (chn);
155                         file_sinks.push_back ((this->*prep_function) (format, 1, filename->get_path (format)));
156                         WritingFile (filename->get_path (format));
157                 }
158
159         } else {
160                 file_sinks.push_back ((this->*prep_function) (format, channels, filename->get_path (format)));
161                 WritingFile (filename->get_path (format));
162         }
163         
164         /* Set position info */
165         
166         nframes_t start_position = ((double) format->sample_rate() / frame_rate) * start + 0.5;
167         
168         for (FileWriterList::iterator it = writer_list.begin(); it != writer_list.end(); ++it) {
169                 (*it)->set_position (start_position);
170         }
171         
172         /* set broadcast info if necessary */
173         
174         if (broadcast_info) {
175                 for (FileWriterList::iterator it = writer_list.begin(); it != writer_list.end(); ++it) {
176                 
177                         BroadcastInfo bci;
178                         bci.set_from_session (session, (*it)->position());
179                         
180                         boost::shared_ptr<SndfileWriterBase> sndfile_ptr;
181                         if ((sndfile_ptr = boost::dynamic_pointer_cast<SndfileWriterBase> (*it))) {
182                                 if (!bci.write_to_file (sndfile_ptr->get_sndfile())) {
183                                         std::cerr << bci.get_error() << std::endl;
184                                 }
185                         } else {
186                                 if (!bci.write_to_file ((*it)->filename())) {
187                                         std::cerr << bci.get_error() << std::endl;
188                                 }
189                         }
190                 }
191         }
192
193         return 0;
194 }
195
196 nframes_t
197 ExportProcessor::process (float * data, nframes_t frames)
198 {
199         nframes_t frames_written = src->write (data, frames);
200         temp_file_length += frames_written;
201         return frames_written;
202 }
203
204 void
205 ExportProcessor::prepare_post_processors ()
206 {
207         /* Set end of input and do last write */
208         float  dummy;
209         src->set_end_of_input ();
210         src->write (&dummy, 0);
211         
212         /* Trim and add silence */
213         
214         temp_file->trim_beginning (trim_beginning);
215         temp_file->trim_end (trim_end);
216         
217         temp_file->set_silence_beginning (silence_beginning);
218         temp_file->set_silence_end (silence_end);
219         
220         /* Set up normalizer */
221         
222         if (normalize) {
223                 normalizer->set_peak (peak_reader->get_peak ());
224         }
225 }
226
227 void
228 ExportProcessor::write_files ()
229 {
230         /* Write to disk */
231         
232         status.stage = export_Write;
233         temp_file_position = 0;
234         
235         uint32_t buffer_size = 4096; // TODO adjust buffer size?
236         float * buf = new float[channels * buffer_size];
237         int frames_read;
238         
239         FloatSinkPtr disk_sink;
240         
241         if (normalize) {
242                 disk_sink = boost::dynamic_pointer_cast<FloatSink> (normalizer);
243                 normalizer->pipe_to (file_sinks[0]);
244         } else {
245                 disk_sink = file_sinks[0];
246         }
247         
248         if (split_files) {
249                 
250                 /* Get buffers for each channel separately */
251                 
252                 std::vector<float *> chan_bufs;
253                 
254                 for (uint32_t i = 0; i < channels; ++i) {
255                         chan_bufs.push_back(new float[buffer_size]);
256                 }
257                 
258                 /* de-interleave and write files */
259                 
260                 while ((frames_read = temp_file->read (buf, buffer_size)) > 0) {
261                         for (uint32_t channel = 0; channel < channels; ++channel) {
262                                 for (uint32_t i = 0; i < buffer_size; ++i) {
263                                         chan_bufs[channel][i] = buf[channel + (channels * i)];
264                                 }
265                                 if (normalize) {
266                                         normalizer->pipe_to (file_sinks[channel]);
267                                 } else {
268                                         disk_sink = file_sinks[channel];
269                                 }
270                                 disk_sink->write (chan_bufs[channel], frames_read);
271                         }
272                         
273                         if (status.aborted()) { break; }
274                         temp_file_position += frames_read;
275                         status.progress = (float) temp_file_position / temp_file_length;
276                 }
277                 
278                 /* Clean up */
279                 
280                 for (std::vector<float *>::iterator it = chan_bufs.begin(); it != chan_bufs.end(); ++it) {
281                         delete[] *it;
282                 }
283                 
284         } else {
285                 while ((frames_read = temp_file->read (buf, buffer_size)) > 0) {
286                         disk_sink->write (buf, frames_read);
287                         
288                         if (status.aborted()) { break; }
289                         temp_file_position += frames_read;
290                         status.progress = (float) temp_file_position / temp_file_length;
291                 }
292         }
293         
294         delete [] buf;
295         
296         /* Tag files if necessary and send exported signal */
297         
298         
299         for (FileWriterList::iterator it = writer_list.begin(); it != writer_list.end(); ++it) {
300                 if (tag) {
301                         AudiofileTagger::tag_file ((*it)->filename(), session.metadata());
302                 }
303                 session.Exported ((*it)->filename(), session.name());
304         }
305 }
306
307 ExportProcessor::FloatSinkPtr
308 ExportProcessor::prepare_sndfile_writer (FormatPtr format, uint32_t channels, ustring const & filename)
309 {
310         int real_format = format->format_id() | format->sample_format() | format->endianness();
311
312         uint32_t data_width = sndfile_data_width (real_format);
313
314         if (data_width == 8 || data_width == 16) {
315         
316                 ShortConverterPtr sfc = ShortConverterPtr (new SampleFormatConverter<short> (channels, format->dither_type(), data_width));
317                 ShortWriterPtr sfw = ShortWriterPtr (new SndfileWriter<short> (channels, format->sample_rate(), real_format, filename));
318                 
319                 writer_list.push_back (boost::static_pointer_cast<ExportFileWriter> (sfw));
320                 
321                 sfc->pipe_to (sfw);
322                 return boost::static_pointer_cast<FloatSink> (sfc);
323
324         } else if (data_width == 24 || data_width == 32) {
325         
326                 IntConverterPtr sfc = IntConverterPtr (new SampleFormatConverter<int> (channels, format->dither_type(), data_width));
327                 IntWriterPtr sfw = IntWriterPtr (new SndfileWriter<int> (channels, format->sample_rate(), real_format, filename));
328                 
329                 writer_list.push_back (boost::static_pointer_cast<ExportFileWriter> (sfw));
330                 
331                 sfc->pipe_to (sfw);
332                 return boost::static_pointer_cast<FloatSink> (sfc);
333
334         } else {
335         
336                 FloatConverterPtr sfc = FloatConverterPtr (new SampleFormatConverter<float> (channels, format->dither_type(), data_width));
337                 FloatWriterPtr sfw = FloatWriterPtr (new SndfileWriter<float> (channels, format->sample_rate(), real_format, filename));
338                 
339                 writer_list.push_back (boost::static_pointer_cast<ExportFileWriter> (sfw));
340                 
341                 sfc->pipe_to (sfw);
342                 return boost::static_pointer_cast<FloatSink> (sfc);
343         }
344
345 }
346
347 }; // namespace ARDOUR