Merge master and multifarious hackery.
[dcpomatic.git] / src / lib / sndfile_decoder.cc
index 7e9e67d0fa2e24fd0ede23561ac47bbe682187e8..c4c6e5f4ee065f6dca6145defbb293bedcb5b7d1 100644 (file)
 
 #include <iostream>
 #include <sndfile.h>
+#include "sndfile_content.h"
 #include "sndfile_decoder.h"
 #include "film.h"
 #include "exceptions.h"
+#include "audio_buffers.h"
 
 #include "i18n.h"
 
 using std::vector;
 using std::string;
-using std::stringstream;
 using std::min;
 using std::cout;
 using boost::shared_ptr;
-using boost::optional;
 
-SndfileDecoder::SndfileDecoder (shared_ptr<Film> f, DecodeOptions o)
-       : Decoder (f, o)
-       , AudioDecoder (f, o)
-       , _done (0)
-       , _frames (0)
+SndfileDecoder::SndfileDecoder (shared_ptr<const Film> f, shared_ptr<const SndfileContent> c)
+       : Decoder (f)
+       , AudioDecoder (f, c)
+       , _sndfile_content (c)
+       , _deinterleave_buffer (0)
 {
-       _done = 0;
-       _frames = 0;
-       
-       vector<string> const files = _film->external_audio ();
-
-       int N = 0;
-       for (size_t i = 0; i < files.size(); ++i) {
-               if (!files[i].empty()) {
-                       N = i + 1;
-               }
-       }
-
-       if (N == 0) {
-               return;
+       _sndfile = sf_open (_sndfile_content->file().string().c_str(), SFM_READ, &_info);
+       if (!_sndfile) {
+               throw DecodeError (_("could not open audio file for reading"));
        }
 
-       bool first = true;
-       
-       for (size_t i = 0; i < (size_t) N; ++i) {
-               if (files[i].empty ()) {
-                       _sndfiles.push_back (0);
-               } else {
-                       SF_INFO info;
-                       SNDFILE* s = sf_open (files[i].c_str(), SFM_READ, &info);
-                       if (!s) {
-                               throw DecodeError (_("could not open external audio file for reading"));
-                       }
+       _done = 0;
+       _remaining = _info.frames;
+}
 
-                       if (info.channels != 1) {
-                               throw DecodeError (_("external audio files must be mono"));
-                       }
-                       
-                       _sndfiles.push_back (s);
-
-                       if (first) {
-                               shared_ptr<SndfileStream> st (
-                                       new SndfileStream (
-                                               info.samplerate, av_get_default_channel_layout (N)
-                                               )
-                                       );
-                               
-                               _audio_streams.push_back (st);
-                               _audio_stream = st;
-                               _frames = info.frames;
-                               first = false;
-                       } else {
-                               if (info.frames != _frames) {
-                                       throw DecodeError (_("external audio files have differing lengths"));
-                               }
-                       }
-               }
-       }
+SndfileDecoder::~SndfileDecoder ()
+{
+       sf_close (_sndfile);
+       delete[] _deinterleave_buffer;
 }
 
-bool
+void
 SndfileDecoder::pass ()
 {
-       if (_audio_streams.empty ()) {
-               return true;
-       }
-       
        /* Do things in half second blocks as I think there may be limits
           to what FFmpeg (and in particular the resampler) can cope with.
        */
-       sf_count_t const block = _audio_stream->sample_rate() / 2;
-       shared_ptr<AudioBuffers> audio (new AudioBuffers (_audio_stream->channels(), block));
-       sf_count_t const this_time = min (block, _frames - _done);
-       for (size_t i = 0; i < _sndfiles.size(); ++i) {
-               if (!_sndfiles[i]) {
-                       audio->make_silent (i);
-               } else {
-                       sf_read_float (_sndfiles[i], audio->data(i), this_time);
-               }
-       }
+       sf_count_t const block = _sndfile_content->content_audio_frame_rate() / 2;
+       sf_count_t const this_time = min (block, _remaining);
 
-       audio->set_frames (this_time);
-       Audio (audio, double(_done) / _audio_stream->sample_rate());
-       _done += this_time;
-
-       return (_done == _frames);
-}
-
-SndfileDecoder::~SndfileDecoder ()
-{
-       for (size_t i = 0; i < _sndfiles.size(); ++i) {
-               if (_sndfiles[i]) {
-                       sf_close (_sndfiles[i]);
+       int const channels = _sndfile_content->audio_channels ();
+       
+       shared_ptr<AudioBuffers> data (new AudioBuffers (channels, this_time));
+
+       if (_sndfile_content->audio_channels() == 1) {
+               /* No de-interleaving required */
+               sf_read_float (_sndfile, data->data(0), this_time);
+       } else {
+               /* Deinterleave */
+               if (!_deinterleave_buffer) {
+                       _deinterleave_buffer = new float[block * channels];
+               }
+               sf_readf_float (_sndfile, _deinterleave_buffer, this_time);
+               vector<float*> out_ptr (channels);
+               for (int i = 0; i < channels; ++i) {
+                       out_ptr[i] = data->data(i);
+               }
+               float* in_ptr = _deinterleave_buffer;
+               for (int i = 0; i < this_time; ++i) {
+                       for (int j = 0; j < channels; ++j) {
+                               *out_ptr[j]++ = *in_ptr++;
+                       }
                }
        }
+               
+       data->set_frames (this_time);
+       audio (data, double(_done) / audio_frame_rate());
+       _done += this_time;
+       _remaining -= this_time;
 }
 
-shared_ptr<SndfileStream>
-SndfileStream::create ()
-{
-       return shared_ptr<SndfileStream> (new SndfileStream);
-}
-
-shared_ptr<SndfileStream>
-SndfileStream::create (string t, optional<int> v)
+int
+SndfileDecoder::audio_channels () const
 {
-       if (!v) {
-               /* version < 1; no type in the string, and there's only FFmpeg streams anyway */
-               return shared_ptr<SndfileStream> ();
-       }
-
-       stringstream s (t);
-       string type;
-       s >> type;
-       if (type != N_("external")) {
-               return shared_ptr<SndfileStream> ();
-       }
-
-       return shared_ptr<SndfileStream> (new SndfileStream (t, v));
+       return _info.channels;
 }
 
-SndfileStream::SndfileStream (string t, optional<int> v)
+ContentAudioFrame
+SndfileDecoder::audio_length () const
 {
-       assert (v);
-
-       stringstream s (t);
-       string type;
-       s >> type >> _sample_rate >> _channel_layout;
+       return _info.frames;
 }
 
-SndfileStream::SndfileStream ()
+int
+SndfileDecoder::audio_frame_rate () const
 {
-
+       return _info.samplerate;
 }
 
-string
-SndfileStream::to_string () const
+Time
+SndfileDecoder::next () const
 {
-       return String::compose (N_("external %1 %2"), _sample_rate, _channel_layout);
+       return _next_audio;
 }