ebf6ff53b811cee2acebe8b095bcda1f55ff2e5d
[ardour.git] / libs / ardour / export_file_io.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 <string.h>
22
23 #include "ardour/export_file_io.h"
24
25 #include "ardour/export_failed.h"
26 #include "pbd/failed_constructor.h"
27
28 #include "i18n.h"
29
30 using namespace PBD;
31
32 namespace ARDOUR
33 {
34
35 /* SndfileWriterBase */
36
37 SndfileWriterBase::SndfileWriterBase (int channels, nframes_t samplerate, int format, string const & path) :
38   ExportFileWriter (path)
39 {
40         char errbuf[256];
41         
42         sf_info.channels = channels;
43         sf_info.samplerate = samplerate;
44         sf_info.format = format;
45         
46         if (!sf_format_check (&sf_info)) {
47                 throw ExportFailed (X_("Invalid format given for SndfileWriter!"));
48         }
49         
50         if (path.length() == 0) {
51                 throw ExportFailed (X_("No output file specified for SndFileWriter"));
52         }
53
54         /* TODO add checks that the directory path exists, and also 
55            check if we are overwriting an existing file...
56         */
57         
58         // Open file TODO make sure we have enough disk space for the output 
59         if (path.compare ("temp")) {
60                 if ((sndfile = sf_open (path.c_str(), SFM_WRITE, &sf_info)) == 0) {
61                         sf_error_str (0, errbuf, sizeof (errbuf) - 1);
62                         throw ExportFailed (string_compose(X_("Cannot open output file \"%1\" for SndFileWriter (%2)"), path, errbuf));
63                 }
64         } else {
65                 FILE * file;
66                 if (!(file = tmpfile ())) {
67                         throw ExportFailed (X_("Cannot open tempfile"));
68                 }
69                 sndfile = sf_open_fd (fileno(file), SFM_RDWR, &sf_info, true);
70         }
71 }
72
73 SndfileWriterBase::~SndfileWriterBase ()
74 {
75         sf_close (sndfile);
76 }
77
78 /* SndfileWriter */
79
80 template <typename T>
81 SndfileWriter<T>::SndfileWriter (int channels, nframes_t samplerate, int format, string const & path) :
82   SndfileWriterBase (channels, samplerate, format, path)
83 {
84         // init write function
85         init ();
86 }
87
88 template <>
89 void
90 SndfileWriter<float>::init ()
91 {
92         write_func = &sf_writef_float;
93 }
94
95 template <>
96 void
97 SndfileWriter<int>::init ()
98 {
99         write_func = &sf_writef_int;
100 }
101
102 template <>
103 void
104 SndfileWriter<short>::init ()
105 {
106         write_func = &sf_writef_short;
107 }
108
109 template <typename T>
110 nframes_t
111 SndfileWriter<T>::write (T * data, nframes_t frames)
112 {
113         char errbuf[256];
114         nframes_t written = (*write_func) (sndfile, data, frames);
115         if (written != frames) {
116                 sf_error_str (sndfile, errbuf, sizeof (errbuf) - 1);
117                 throw ExportFailed (string_compose(_("Could not write data to output file (%1)"), errbuf));
118         }
119         
120         if (GraphSink<T>::end_of_input) {
121                 sf_write_sync (sndfile);
122         }
123         
124         return frames;
125 }
126
127 template class SndfileWriter<short>;
128 template class SndfileWriter<int>;
129 template class SndfileWriter<float>;
130
131 /* ExportTempFile */
132
133 ExportTempFile::ExportTempFile (uint32_t channels, nframes_t samplerate) :
134   SndfileWriter<float> (channels, samplerate, SF_FORMAT_RAW | SF_FORMAT_FLOAT | SF_ENDIAN_FILE, "temp"),
135   channels (channels),
136   reading (false),
137   start (0),
138   end (0),
139   beginning_processed (false),
140   end_processed (false),
141   silence_beginning (0),
142   silence_end (0),
143   end_set (false)
144 {
145 }
146
147 nframes_t
148 ExportTempFile::read (float * data, nframes_t frames)
149 {
150         nframes_t frames_read = 0;
151         nframes_t to_read = 0;
152         sf_count_t read_status = 0;
153         
154         /* Initialize state at first read */
155         
156         if (!reading) {
157                 if (!end_set) {
158                         end = get_length();
159                         end_set = true;
160                 }
161                 locate_to (start);
162                 reading = true;
163         }
164         
165         /* Add silence to beginning */
166         
167         if (silence_beginning > 0) {
168                 if (silence_beginning >= frames) {
169                         memset (data, 0, channels * frames * sizeof (float));
170                         silence_beginning -= frames;
171                         return frames;
172                 }
173                 
174                 memset (data, 0, channels * silence_beginning * sizeof (float));
175                 
176                 frames_read += silence_beginning;
177                 data += channels * silence_beginning;
178                 silence_beginning = 0;
179         }
180         
181         /* Read file, but don't read past end */
182         
183         if (get_read_position() >= end) {
184                 // File already read, do nothing!
185         } else {
186                 if ((get_read_position() + (frames - frames_read)) > end) {
187                         to_read = end - get_read_position();
188                 } else {
189                         to_read = frames - frames_read;
190                 }
191                 
192                 read_status = sf_readf_float (sndfile, data, to_read);
193                 
194                 frames_read += to_read;
195                 data += channels * to_read;
196         }
197         
198         /* Check for errors */
199         
200         if (read_status != to_read) {
201                 throw ExportFailed (X_("Error reading temporary export file, export might not be complete!"));
202         }
203         
204         /* Add silence at end */
205         
206         if (silence_end > 0) {
207                 to_read = frames - frames_read;
208                 if (silence_end < to_read) {
209                         to_read = silence_end;
210                 }
211                 
212                 memset (data, 0, channels * to_read * sizeof (float));
213                 silence_end -= to_read;
214                 frames_read += to_read;
215         }
216         
217         return frames_read;
218 }
219
220 nframes_t
221 ExportTempFile::trim_beginning (bool yn)
222 {
223         if (!yn) {
224                 start = 0;
225                 return start;
226         }
227
228         if (!beginning_processed) {
229                 process_beginning ();
230         }
231         
232         start = silent_frames_beginning;
233         return start;
234         
235 }
236
237 nframes_t
238 ExportTempFile::trim_end (bool yn)
239 {
240         end_set = true;
241
242         if (!yn) {
243                 end = get_length();
244                 return end;
245         }
246
247         if (!end_processed) {
248                 process_end ();
249         }
250         
251         end = silent_frames_end;
252         return end;
253 }
254
255
256 void
257 ExportTempFile::process_beginning ()
258 {
259         nframes_t frames = 1024;
260         nframes_t frames_read;
261         float * buf = new float[channels * frames];
262         
263         nframes_t pos = 0;
264         locate_to (pos);
265         
266         while ((frames_read = _read (buf, frames)) > 0) {
267                 for (nframes_t i = 0; i < frames_read; i++) {
268                         for (uint32_t chn = 0; chn < channels; ++chn) {
269                                 if (buf[chn + i * channels] != 0.0f) {
270                                         --pos;
271                                         goto out;
272                                 }
273                         }
274                         ++pos;
275                 }
276         }
277         
278         out:
279         
280         silent_frames_beginning = pos;
281         beginning_processed = true;
282         
283         delete [] buf;
284 }
285
286 void
287 ExportTempFile::process_end ()
288 {
289         nframes_t frames = 1024;
290         nframes_t frames_read;
291         float * buf = new float[channels * frames];
292         
293         nframes_t pos = get_length() - 1;
294         
295         while (pos > 0) {
296                 if (pos > frames) {
297                         locate_to (pos - frames);
298                         frames_read = _read (buf, frames);
299                 } else {
300                         // Last time reading
301                         locate_to (0);
302                         frames_read = _read (buf, pos);
303                 }
304                 
305                 for (nframes_t i = frames_read; i > 0; --i) {
306                         for (uint32_t chn = 0; chn < channels; ++chn) {
307                                 if (buf[chn + (i - 1) * channels] != 0.0f) {
308                                         goto out;
309                                 }
310                         }
311                         --pos;
312                 }
313         }
314         
315         out:
316         
317         silent_frames_end = pos;
318         end_processed = true;
319         
320         delete [] buf;
321 }
322
323 void
324 ExportTempFile::set_silence_beginning (nframes_t frames)
325 {
326         silence_beginning = frames;
327 }
328
329 void
330 ExportTempFile::set_silence_end (nframes_t frames)
331 {
332         silence_end = frames;
333 }
334
335 sf_count_t
336 ExportTempFile::get_length ()
337 {
338         sf_count_t pos = get_position();
339         sf_count_t len = sf_seek (sndfile, 0, SEEK_END);
340         locate_to (pos);
341         return len;
342 }
343
344 sf_count_t
345 ExportTempFile::get_position ()
346 {
347         return sf_seek (sndfile, 0, SEEK_CUR);
348 }
349
350 sf_count_t
351 ExportTempFile::get_read_position ()
352 {
353         return sf_seek (sndfile, 0, SEEK_CUR | SFM_READ);
354 }
355
356 sf_count_t
357 ExportTempFile::locate_to (nframes_t frames)
358 {
359         return sf_seek (sndfile, frames, SEEK_SET);
360 }
361
362 sf_count_t
363 ExportTempFile::_read (float * data, nframes_t frames)
364 {
365         return sf_readf_float (sndfile, data, frames);
366 }
367
368 ExportFileFactory::FilePair
369 ExportFileFactory::create (FormatPtr format, uint32_t channels, ustring const & filename)
370 {
371         switch (format->type()) {
372           case ExportFormatBase::T_Sndfile:
373                 return create_sndfile (format, channels, filename);
374
375           default:
376                 throw ExportFailed (X_("Invalid format given for ExportFileFactory::create!"));
377         }
378 }
379
380 bool
381 ExportFileFactory::check (FormatPtr format, uint32_t channels)
382 {
383         switch (format->type()) {
384           case ExportFormatBase::T_Sndfile:
385                 return check_sndfile (format, channels);
386
387           default:
388                 throw ExportFailed (X_("Invalid format given for ExportFileFactory::check!"));
389         }
390 }
391
392 ExportFileFactory::FilePair
393 ExportFileFactory::create_sndfile (FormatPtr format, unsigned int channels, ustring const & filename)
394 {
395         typedef boost::shared_ptr<SampleFormatConverter<short> > ShortConverterPtr;
396         typedef boost::shared_ptr<SampleFormatConverter<int> > IntConverterPtr;
397         typedef boost::shared_ptr<SampleFormatConverter<float> > FloatConverterPtr;
398         
399         typedef boost::shared_ptr<SndfileWriter<short> > ShortWriterPtr;
400         typedef boost::shared_ptr<SndfileWriter<int> > IntWriterPtr;
401         typedef boost::shared_ptr<SndfileWriter<float> > FloatWriterPtr;
402         
403         int real_format = format->format_id() | format->sample_format() | format->endianness();
404
405         uint32_t data_width = sndfile_data_width (real_format);
406
407         if (data_width == 8 || data_width == 16) {
408         
409                 ShortConverterPtr sfc = ShortConverterPtr (new SampleFormatConverter<short> (channels, format->dither_type(), data_width));
410                 ShortWriterPtr sfw = ShortWriterPtr (new SndfileWriter<short> (channels, format->sample_rate(), real_format, filename));
411                 sfc->pipe_to (sfw);
412                 
413                 return std::make_pair (boost::static_pointer_cast<FloatSink> (sfc), boost::static_pointer_cast<ExportFileWriter> (sfw));
414
415         } else if (data_width == 24 || data_width == 32) {
416         
417                 IntConverterPtr sfc = IntConverterPtr (new SampleFormatConverter<int> (channels, format->dither_type(), data_width));
418                 IntWriterPtr sfw = IntWriterPtr (new SndfileWriter<int> (channels, format->sample_rate(), real_format, filename));
419                 sfc->pipe_to (sfw);
420                 
421                 return std::make_pair (boost::static_pointer_cast<FloatSink> (sfc), boost::static_pointer_cast<ExportFileWriter> (sfw));
422
423         }
424         
425         FloatConverterPtr sfc = FloatConverterPtr (new SampleFormatConverter<float> (channels, format->dither_type(), data_width));
426         FloatWriterPtr sfw = FloatWriterPtr (new SndfileWriter<float> (channels, format->sample_rate(), real_format, filename));
427         sfc->pipe_to (sfw);
428         
429         return std::make_pair (boost::static_pointer_cast<FloatSink> (sfc), boost::static_pointer_cast<ExportFileWriter> (sfw));
430 }
431
432 bool
433 ExportFileFactory::check_sndfile (FormatPtr format, unsigned int channels)
434 {
435         SF_INFO sf_info;
436         sf_info.channels = channels;
437         sf_info.samplerate = format->sample_rate ();
438         sf_info.format = format->format_id () | format->sample_format ();
439
440         return (sf_format_check (&sf_info) == SF_TRUE ? true : false);
441 }
442
443 } // namespace ARDOUR