Merge branch '1.0' into 1.0-seek
[dcpomatic.git] / src / lib / sndfile_content.cc
1 /*
2     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
3
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.
8
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.
13
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.
17
18 */
19
20 #include <libcxml/cxml.h>
21 #include "sndfile_content.h"
22 #include "sndfile_decoder.h"
23 #include "film.h"
24 #include "compose.hpp"
25 #include "job.h"
26 #include "util.h"
27
28 #include "i18n.h"
29
30 using std::string;
31 using std::stringstream;
32 using std::cout;
33 using boost::shared_ptr;
34 using boost::lexical_cast;
35
36 SndfileContent::SndfileContent (shared_ptr<const Film> f, boost::filesystem::path p)
37         : Content (f, p)
38         , AudioContent (f, p)
39         , _audio_channels (0)
40         , _audio_length (0)
41         , _audio_frame_rate (0)
42 {
43
44 }
45
46 SndfileContent::SndfileContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int)
47         : Content (f, node)
48         , AudioContent (f, node)
49         , _audio_mapping (node->node_child ("AudioMapping"))
50 {
51         _audio_channels = node->number_child<int> ("AudioChannels");
52         _audio_length = node->number_child<AudioContent::Frame> ("AudioLength");
53         _audio_frame_rate = node->number_child<int> ("AudioFrameRate");
54 }
55
56 string
57 SndfileContent::summary () const
58 {
59         /* Get the string() here so that the name does not have quotes around it */
60         return String::compose (_("%1 [audio]"), path_summary ());
61 }
62
63 string
64 SndfileContent::technical_summary () const
65 {
66         return Content::technical_summary() + " - "
67                 + AudioContent::technical_summary ()
68                 + " - sndfile";
69 }
70
71 string
72 SndfileContent::information () const
73 {
74         if (_audio_frame_rate == 0) {
75                 return "";
76         }
77         
78         stringstream s;
79
80         s << String::compose (
81                 _("%1 channels, %2kHz, %3 samples"),
82                 audio_channels(),
83                 content_audio_frame_rate() / 1000.0,
84                 audio_length()
85                 );
86         
87         return s.str ();
88 }
89
90 bool
91 SndfileContent::valid_file (boost::filesystem::path f)
92 {
93         /* XXX: more extensions */
94         string ext = f.extension().string();
95         transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
96         return (ext == ".wav" || ext == ".aif" || ext == ".aiff");
97 }
98
99 void
100 SndfileContent::examine (shared_ptr<Job> job)
101 {
102         job->set_progress_unknown ();
103         Content::examine (job);
104
105         shared_ptr<const Film> film = _film.lock ();
106         assert (film);
107
108         SndfileDecoder dec (film, shared_from_this());
109
110         {
111                 boost::mutex::scoped_lock lm (_mutex);
112                 _audio_channels = dec.audio_channels ();
113                 _audio_length = dec.audio_length ();
114                 _audio_frame_rate = dec.audio_frame_rate ();
115         }
116
117         signal_changed (AudioContentProperty::AUDIO_CHANNELS);
118         signal_changed (AudioContentProperty::AUDIO_LENGTH);
119         signal_changed (AudioContentProperty::AUDIO_FRAME_RATE);
120
121         {
122                 boost::mutex::scoped_lock lm (_mutex);
123                 /* XXX: do this in signal_changed...? */
124                 _audio_mapping = AudioMapping (_audio_channels);
125                 _audio_mapping.make_default ();
126         }
127         
128         signal_changed (AudioContentProperty::AUDIO_MAPPING);
129 }
130
131 void
132 SndfileContent::as_xml (xmlpp::Node* node) const
133 {
134         node->add_child("Type")->add_child_text ("Sndfile");
135         Content::as_xml (node);
136         AudioContent::as_xml (node);
137
138         node->add_child("AudioChannels")->add_child_text (lexical_cast<string> (audio_channels ()));
139         node->add_child("AudioLength")->add_child_text (lexical_cast<string> (audio_length ()));
140         node->add_child("AudioFrameRate")->add_child_text (lexical_cast<string> (content_audio_frame_rate ()));
141         _audio_mapping.as_xml (node->add_child("AudioMapping"));
142 }
143
144 DCPTime
145 SndfileContent::full_length () const
146 {
147         shared_ptr<const Film> film = _film.lock ();
148         assert (film);
149
150         OutputAudioFrame const len = audio_length() * output_audio_frame_rate() / content_audio_frame_rate ();
151         
152         /* XXX: this depends on whether, alongside this audio, we are running video slower or faster than
153            it should be.  The calculation above works out the output audio frames assuming that we are just
154            resampling the audio: it would be incomplete if, for example, we were running this audio alongside
155            25fps video that was being run at 24fps.
156         */
157         
158         return film->audio_frames_to_time (len);
159 }
160
161 int
162 SndfileContent::output_audio_frame_rate () const
163 {
164         shared_ptr<const Film> film = _film.lock ();
165         assert (film);
166         
167         return film->audio_frame_rate ();
168 }
169
170 void
171 SndfileContent::set_audio_mapping (AudioMapping m)
172 {
173         {
174                 boost::mutex::scoped_lock lm (_mutex);
175                 _audio_mapping = m;
176         }
177
178         signal_changed (AudioContentProperty::AUDIO_MAPPING);
179 }