diff options
| author | Carl Hetherington <cth@carlh.net> | 2025-04-17 02:09:09 +0200 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2025-04-20 21:18:05 +0200 |
| commit | 2136a540da3f2fe55966db8f30a05193bcb83484 (patch) | |
| tree | 30f7371c4e2e1d59a875f2f0276d9a017bbfe4e7 /src/lib | |
| parent | 9323d3ec1e168a7003b5c0515247129c2fb3d1e7 (diff) | |
wip: audio meters.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/butler.cc | 13 | ||||
| -rw-r--r-- | src/lib/butler.h | 5 | ||||
| -rw-r--r-- | src/lib/level_calculator.cc | 127 | ||||
| -rw-r--r-- | src/lib/level_calculator.h | 74 | ||||
| -rw-r--r-- | src/lib/wscript | 1 |
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 |
