+
+/** Construct an AudioBuffers. Audio data is undefined after this constructor.
+ * @param channels Number of channels.
+ * @param frames Number of frames to reserve space for.
+ */
+AudioBuffers::AudioBuffers (int channels, int frames)
+ : _channels (channels)
+ , _frames (frames)
+ , _allocated_frames (frames)
+{
+ _data = new float*[_channels];
+ for (int i = 0; i < _channels; ++i) {
+ _data[i] = new float[frames];
+ }
+}
+
+/** Copy constructor.
+ * @param other Other AudioBuffers; data is copied.
+ */
+AudioBuffers::AudioBuffers (AudioBuffers const & other)
+ : _channels (other._channels)
+ , _frames (other._frames)
+ , _allocated_frames (other._frames)
+{
+ _data = new float*[_channels];
+ for (int i = 0; i < _channels; ++i) {
+ _data[i] = new float[_frames];
+ memcpy (_data[i], other._data[i], _frames * sizeof (float));
+ }
+}
+
+/** AudioBuffers destructor */
+AudioBuffers::~AudioBuffers ()
+{
+ for (int i = 0; i < _channels; ++i) {
+ delete[] _data[i];
+ }
+
+ delete[] _data;
+}
+
+/** @param c Channel index.
+ * @return Buffer for this channel.
+ */
+float*
+AudioBuffers::data (int c) const
+{
+ assert (c >= 0 && c < _channels);
+ return _data[c];
+}
+
+/** Set the number of frames that these AudioBuffers will report themselves
+ * as having.
+ * @param f Frames; must be less than or equal to the number of allocated frames.
+ */
+void
+AudioBuffers::set_frames (int f)
+{
+ assert (f <= _allocated_frames);
+ _frames = f;
+}
+
+/** Make all samples on all channels silent */
+void
+AudioBuffers::make_silent ()
+{
+ for (int i = 0; i < _channels; ++i) {
+ make_silent (i);
+ }
+}
+
+/** Make all samples on a given channel silent.
+ * @param c Channel.
+ */
+void
+AudioBuffers::make_silent (int c)
+{
+ assert (c >= 0 && c < _channels);
+
+ for (int i = 0; i < _frames; ++i) {
+ _data[c][i] = 0;
+ }
+}
+
+/** Copy data from another AudioBuffers to this one. All channels are copied.
+ * @param from AudioBuffers to copy from; must have the same number of channels as this.
+ * @param frames_to_copy Number of frames to copy.
+ * @param read_offset Offset to read from in `from'.
+ * @param write_offset Offset to write to in `to'.
+ */
+void
+AudioBuffers::copy_from (AudioBuffers* from, int frames_to_copy, int read_offset, int write_offset)
+{
+ assert (from->channels() == channels());
+
+ assert (from);
+ assert (read_offset >= 0 && (read_offset + frames_to_copy) <= from->_allocated_frames);
+ assert (write_offset >= 0 && (write_offset + frames_to_copy) <= _allocated_frames);
+
+ for (int i = 0; i < _channels; ++i) {
+ memcpy (_data[i] + write_offset, from->_data[i] + read_offset, frames_to_copy * sizeof(float));
+ }
+}
+
+/** Move audio data around.
+ * @param from Offset to move from.
+ * @param to Offset to move to.
+ * @param frames Number of frames to move.
+ */
+
+void
+AudioBuffers::move (int from, int to, int frames)
+{
+ if (frames == 0) {
+ return;
+ }
+
+ assert (from >= 0);
+ assert (from < _frames);
+ assert (to >= 0);
+ assert (to < _frames);
+ assert (frames > 0);
+ assert (frames <= _frames);
+ assert ((from + frames) <= _frames);
+ assert ((to + frames) <= _frames);
+
+ for (int i = 0; i < _channels; ++i) {
+ memmove (_data[i] + to, _data[i] + from, frames * sizeof(float));
+ }
+}
+
+/** Trip an assert if the caller is not in the UI thread */
+void
+ensure_ui_thread ()
+{
+ assert (this_thread::get_id() == ui_thread);
+}
+
+/** @param v Source video frame.
+ * @param audio_sample_rate Source audio sample rate.
+ * @param frames_per_second Number of video frames per second.
+ * @return Equivalent number of audio frames for `v'.
+ */
+int64_t
+video_frames_to_audio_frames (SourceFrame v, float audio_sample_rate, float frames_per_second)
+{
+ return ((int64_t) v * audio_sample_rate / frames_per_second);
+}
+
+/** @param f Filename.
+ * @return true if this file is a still image, false if it is something else.
+ */
+bool
+still_image_file (string f)
+{
+ string ext = boost::filesystem::path(f).extension().string();
+
+ transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
+
+ return (ext == N_(".tif") || ext == N_(".tiff") || ext == N_(".jpg") || ext == N_(".jpeg") || ext == N_(".png") || ext == N_(".bmp"));
+}
+
+/** @return A pair containing CPU model name and the number of processors */
+pair<string, int>
+cpu_info ()
+{
+ pair<string, int> info;
+ info.second = 0;
+
+#ifdef DVDOMATIC_POSIX
+ ifstream f (N_("/proc/cpuinfo"));
+ while (f.good ()) {
+ string l;
+ getline (f, l);
+ if (boost::algorithm::starts_with (l, N_("model name"))) {
+ string::size_type const c = l.find (':');
+ if (c != string::npos) {
+ info.first = l.substr (c + 2);
+ }
+ } else if (boost::algorithm::starts_with (l, N_("processor"))) {
+ ++info.second;
+ }
+ }
+#endif
+
+ return info;
+}
+
+string
+audio_channel_name (int c)
+{
+ assert (MAX_AUDIO_CHANNELS == 6);
+
+ /* TRANSLATORS: these are the names of audio channels; Lfe (sub) is the low-frequency
+ enhancement channel (sub-woofer)./
+ */
+ string const channels[] = {
+ "Left",
+ "Right",
+ "Centre",
+ "Lfe (sub)",
+ "Left surround",
+ "Right surround",
+ };
+
+ return channels[c];
+}
+
+AudioMapping::AudioMapping (int c)
+ : _source_channels (c)
+{
+
+}
+
+optional<libdcp::Channel>
+AudioMapping::source_to_dcp (int c) const
+{
+ if (c >= _source_channels) {
+ return optional<libdcp::Channel> ();
+ }
+
+ if (_source_channels == 1) {
+ /* mono sources to centre */
+ return libdcp::CENTRE;
+ }
+
+ return static_cast<libdcp::Channel> (c);
+}
+
+optional<int>
+AudioMapping::dcp_to_source (libdcp::Channel c) const
+{
+ if (_source_channels == 1) {
+ if (c == libdcp::CENTRE) {
+ return 0;
+ } else {
+ return optional<int> ();
+ }
+ }
+
+ if (static_cast<int> (c) >= _source_channels) {
+ return optional<int> ();
+ }
+
+ return static_cast<int> (c);
+}
+
+int
+AudioMapping::dcp_channels () const
+{
+ if (_source_channels == 1) {
+ /* The source is mono, so to put the mono channel into
+ the centre we need to generate a 5.1 soundtrack.
+ */
+ return 6;
+ }
+
+ return _source_channels;
+}
+
+FrameRateConversion::FrameRateConversion (float source, int dcp)
+ : skip (false)
+ , repeat (false)
+ , change_speed (false)
+{
+ if (fabs (source / 2.0 - dcp) < (fabs (source - dcp))) {
+ skip = true;
+ } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) {
+ repeat = true;
+ }
+
+ change_speed = !about_equal (source * factor(), dcp);
+
+ if (!skip && !repeat && !change_speed) {
+ explanation = _("DCP and source have the same rate.\n");
+ } else {
+ if (skip) {
+ explanation = _("DCP will use every other frame of the source.\n");
+ } else if (repeat) {
+ explanation = _("Each source frame will be doubled in the DCP.\n");
+ }
+
+ if (change_speed) {
+ float const pc = (source * factor()) * 100 / dcp;
+ explanation += String::compose (_("DCP will run at %1%% of the source speed."), pc);
+ }
+ }
+}