summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2025-04-17 02:09:09 +0200
committerCarl Hetherington <cth@carlh.net>2025-04-20 21:18:05 +0200
commit2136a540da3f2fe55966db8f30a05193bcb83484 (patch)
tree30f7371c4e2e1d59a875f2f0276d9a017bbfe4e7 /src/lib
parent9323d3ec1e168a7003b5c0515247129c2fb3d1e7 (diff)
wip: audio meters.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/butler.cc13
-rw-r--r--src/lib/butler.h5
-rw-r--r--src/lib/level_calculator.cc127
-rw-r--r--src/lib/level_calculator.h74
-rw-r--r--src/lib/wscript1
5 files changed, 220 insertions, 0 deletions
diff --git a/src/lib/butler.cc b/src/lib/butler.cc
index 14f342045..57c891404 100644
--- a/src/lib/butler.cc
+++ b/src/lib/butler.cc
@@ -24,6 +24,7 @@
#include "cross.h"
#include "dcpomatic_log.h"
#include "exceptions.h"
+#include "level_calculator.h"
#include "log.h"
#include "player.h"
#include "util.h"
@@ -33,6 +34,7 @@
using std::cout;
using std::function;
using std::make_pair;
+using std::make_shared;
using std::pair;
using std::shared_ptr;
using std::string;
@@ -88,6 +90,7 @@ Butler::Butler(
, _alignment(alignment)
, _fast(fast)
, _prepare_only_proxy(prepare_only_proxy)
+ , _level_calculator(make_shared<LevelCalculator>())
{
_player_video_connection = _player.Video.connect(bind(&Butler::video, this, _1, _2));
_player_audio_connection = _player.Audio.connect(bind(&Butler::audio, this, _1, _2, _3));
@@ -312,6 +315,7 @@ Butler::seek_unlocked(DCPTime position, bool accurate)
_video.clear();
_audio.clear();
+ _level_calculator->clear();
_closed_caption.clear();
_summon.notify_all();
@@ -370,6 +374,8 @@ Butler::audio(shared_ptr<AudioBuffers> audio, DCPTime time, int frame_rate)
return;
}
+ _level_calculator->put(audio, time, frame_rate);
+
_audio.put(remap(audio, _audio_channels, _audio_mapping), time, frame_rate);
}
@@ -481,3 +487,10 @@ Butler::Error::summary() const
return "";
}
+
+shared_ptr<LevelCalculator>
+Butler::level_calculator() const
+{
+ return _level_calculator;
+}
+
diff --git a/src/lib/butler.h b/src/lib/butler.h
index ad2552769..dd04e7b86 100644
--- a/src/lib/butler.h
+++ b/src/lib/butler.h
@@ -37,6 +37,7 @@
#include <boost/thread/condition.hpp>
+class LevelCalculator;
class Player;
class PlayerVideo;
@@ -94,6 +95,8 @@ public:
boost::optional<dcpomatic::DCPTime> get_audio(Behaviour behaviour, float* out, Frame frames);
boost::optional<TextRingBuffers::Data> get_closed_caption();
+ std::shared_ptr<LevelCalculator> level_calculator() const;
+
std::pair<size_t, std::string> memory_used() const;
private:
@@ -154,6 +157,8 @@ private:
*/
boost::optional<dcpomatic::DCPTime> _awaiting;
+ std::shared_ptr<LevelCalculator> _level_calculator;
+
boost::signals2::scoped_connection _player_video_connection;
boost::signals2::scoped_connection _player_audio_connection;
boost::signals2::scoped_connection _player_text_connection;
diff --git a/src/lib/level_calculator.cc b/src/lib/level_calculator.cc
new file mode 100644
index 000000000..0563d4ed8
--- /dev/null
+++ b/src/lib/level_calculator.cc
@@ -0,0 +1,127 @@
+/*
+ Copyright (C) 2025 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#include "audio_buffers.h"
+#include "level_calculator.h"
+
+
+using std::make_pair;
+using std::shared_ptr;
+using boost::optional;
+
+
+constexpr int frames_per_measurement = 48000 / 30;
+
+
+LevelCalculator::LevelCalculator()
+ : _enabled(false)
+{
+
+}
+
+
+void
+LevelCalculator::put(shared_ptr<const AudioBuffers> audio, dcpomatic::DCPTime time, int frame_rate)
+{
+ if (!_enabled) {
+ std::cout << "no calcs for me.\n";
+ return;
+ }
+
+ boost::mutex::scoped_lock lm(_current_mutex);
+
+ auto const channels = audio->channels();
+
+ if (static_cast<int>(_current_peaks.size()) != channels) {
+ _current_peaks.resize(channels);
+ }
+
+ auto const data = audio->data();
+ auto const frames = audio->frames();
+ for (auto frame = 0; frame < frames; ++frame) {
+ for (auto channel = 0; channel < channels; ++channel) {
+ _current_peaks[channel] = std::max(std::abs(data[channel][frame]), _current_peaks[channel]);
+ }
+
+ ++_current_frames;
+ if (_current_frames == frames_per_measurement) {
+ {
+ boost::mutex::scoped_lock lm(_store_mutex);
+ _peaks.emplace_back(time + dcpomatic::DCPTime::from_frames(frame, frame_rate), _current_peaks);
+ }
+ std::fill(_current_peaks.begin(), _current_peaks.end(), 0.0f);
+ _current_frames = 0;
+ }
+ }
+
+}
+
+
+void
+LevelCalculator::clear()
+{
+ boost::mutex::scoped_lock slm(_store_mutex);
+ _peaks.clear();
+
+ boost::mutex::scoped_lock clm(_current_mutex);
+ std::fill(_current_peaks.begin(), _current_peaks.end(), 0.0f);
+ _current_frames = 0;
+}
+
+
+std::vector<float>
+LevelCalculator::get(dcpomatic::DCPTime time)
+{
+ boost::mutex::scoped_lock slm(_store_mutex);
+
+ auto iter = _peaks.begin();
+ optional<dcpomatic::DCPTime> last_delta;
+ std::list<Measurement>::iterator last_iter;
+ while (iter != _peaks.end()) {
+ auto const delta = dcpomatic::DCPTime(time - iter->time).abs();
+ if (last_delta) {
+ if (delta > *last_delta) {
+ /* This is worse than the last - use the last one */
+ return last_iter->value;
+ } else {
+ /* This is better - keep looking */
+ _peaks.erase(last_iter);
+ }
+ }
+ last_delta = delta;
+ last_iter = iter;
+ ++iter;
+ }
+
+ if (iter == _peaks.end()) {
+ return {};
+ }
+
+ return iter->value;
+}
+
+
+void
+LevelCalculator::enable(bool e)
+{
+ _enabled = e;
+}
+
diff --git a/src/lib/level_calculator.h b/src/lib/level_calculator.h
new file mode 100644
index 000000000..9eeb5b9b8
--- /dev/null
+++ b/src/lib/level_calculator.h
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2025 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+
+#ifndef DCPOMATIC_LEVEL_CALCULATOR_H
+#define DCPOMATIC_LEVEL_CALCULATOR_H
+
+
+#include "dcpomatic_time.h"
+#include <boost/thread.hpp>
+#include <memory>
+
+
+class AudioBuffers;
+
+
+class LevelCalculator
+{
+public:
+ LevelCalculator();
+
+ LevelCalculator(LevelCalculator const&) = delete;
+ LevelCalculator& operator=(LevelCalculator const&) = delete;
+
+ void put(std::shared_ptr<const AudioBuffers> audio, dcpomatic::DCPTime time, int frame_rate);
+ void clear();
+ std::vector<float> get(dcpomatic::DCPTime time);
+
+ void enable(bool e);
+
+private:
+ boost::mutex _current_mutex;
+ std::vector<float> _current_peaks;
+ int _current_frames = 0;
+
+ struct Measurement
+ {
+ Measurement(dcpomatic::DCPTime t, std::vector<float> v)
+ : time(t)
+ , value(std::move(v))
+ {
+
+ }
+
+ dcpomatic::DCPTime time;
+ std::vector<float> value;
+ };
+
+ mutable boost::mutex _store_mutex;
+ std::list<Measurement> _peaks;
+
+ boost::atomic<bool> _enabled;
+};
+
+
+#endif
+
diff --git a/src/lib/wscript b/src/lib/wscript
index dafd655fe..4b0002f04 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -156,6 +156,7 @@ sources = """
kdm_recipient.cc
kdm_with_metadata.cc
kdm_util.cc
+ level_calculator.cc
log.cc
log_entry.cc
make_dcp.cc