Loading...
Searching...
No Matches
MediaFileHandle.hpp
1#pragma once
2#include <Media/AudioDecoder.hpp>
3#include <Media/SndfileDecoder.hpp>
4#include <Media/WaveformSummary.hpp>
5
6#include <score/tools/std/StringHash.hpp>
7
8#include <ossia/audio/drwav_handle.hpp>
9#include <ossia/detail/hash_map.hpp>
10#include <ossia/detail/nullable_variant.hpp>
11#include <ossia/detail/small_vector.hpp>
12
13#include <QFile>
14
15#include <nano_signal_slot.hpp>
16#include <score_plugin_media_export.h>
17
18#include <array>
19#include <atomic>
20#include <mutex>
21#include <verdigris>
22namespace ossia
23{
24struct libav_handle;
25}
26namespace score
27{
28struct DocumentContext;
29}
30namespace Media
31{
32struct RMSData;
33class SoundComponentSetup;
34static constexpr inline int64_t abs_max(int64_t f1, int64_t f2) noexcept
35{
36 return f2 >= 0 ? f1 < f2 ? f2 : f1 : f1 < -f2 ? f2 : f1;
37}
38#if defined(__clang__)
39static constexpr inline float abs_max(float f1, float f2) noexcept
40{
41 const int mul = (f2 >= 0.f) ? 1 : -1;
42 if(f1 < mul * f2)
43 return f2;
44 else
45 return f1;
46}
47#else
48static constexpr inline float abs_max(float f1, float f2) noexcept
49{
50 return f2 >= 0.f ? f1 < f2 ? f2 : f1 : f1 < -f2 ? f2 : f1;
51}
52#endif
53
54enum class DecodingMethod
55{
56 Invalid,
57 Mmap,
58 Libav,
59 Sndfile,
60 LibavStream
61};
62
64{
65 QString filePath;
66 QString absoluteFilePath;
67 DecodingMethod method{};
68 int track{-1};
69};
70
71struct SCORE_PLUGIN_MEDIA_EXPORT AudioFile final : public QObject
72{
73public:
74 static bool isSupported(const QFile& f);
75 static bool isSupportedVideo(const QFile& f);
76
77 AudioFile();
78 ~AudioFile() override;
79
80 void load(DecodingSetup opts);
81
83 QString originalFile() const { return m_originalFile; }
84
86 QString fileName() const { return m_fileName; }
87
89 QString absoluteFileName() const { return m_file; }
90
91 int sampleRate() const { return m_sampleRate; }
92
93 int64_t decodedSamples() const;
94
95 // Number of samples in a channel.
96 int64_t samples() const;
97 int64_t channels() const;
98
99 bool empty() const { return channels() == 0 || samples() == 0; }
101 bool finishedDecoding() const noexcept
102 {
103 return m_fullyDecoded.load(std::memory_order_acquire);
104 }
105
106 const RMSData& rms() const;
107
114 std::shared_ptr<const WaveformSummary> waveformSummary() const noexcept;
115
117 ossia::audio_array getAudioArray() const;
118
119 Nano::Signal<void()> on_mediaChanged;
120 Nano::Signal<void()> on_newData;
121 Nano::Signal<void()> on_finishedDecoding;
122
124 {
125 std::shared_ptr<QFile> file;
126 void* data{};
127 ossia::drwav_handle wav;
128 };
129
131 {
132 ossia::audio_handle handle;
133 ossia::small_vector<ossia::audio_sample*, 8> data;
134 };
135
137 {
138 explicit LibavReader(int rate) noexcept
139 : decoder{rate}
140 {
141 }
142 AudioDecoder decoder;
143 };
145 {
146 std::string path;
147 int stream{-1};
148 int64_t samples{};
149 int channels{};
150 };
152 {
153 SndfileDecoder decoder;
154 };
155
156 using libav_ptr = std::shared_ptr<LibavReader>;
158 using mmap_ptr = MmapReader;
160 using impl_t
161 = ossia::nullable_variant<mmap_ptr, libav_ptr, sndfile_ptr, libav_stream_ptr>;
162
163 struct MmapView
164 {
165 // Copy for thread-safety reasons
166 ossia::drwav_handle wav;
167 };
168
170 {
171 std::shared_ptr<ossia::libav_handle> handle;
172 };
173
174 struct RAMView
175 {
176 ossia::small_vector<audio_sample*, 8> data;
177 };
178
179 struct Handle : impl_t
180 {
181 using impl_t::impl_t;
182 Handle(mmap_ptr&& ptr)
183 : impl_t{std::move(ptr)}
184 {
185 }
186 Handle(libav_ptr&& ptr)
187 : impl_t{std::move(ptr)}
188 {
189 }
191 : impl_t{std::move(ptr)}
192 {
193 }
194 Handle(sndfile_ptr&& ptr)
195 : impl_t{std::move(ptr)}
196 {
197 }
198 Handle& operator=(mmap_ptr&& ptr)
199 {
200 ((impl_t&)*this) = std::move(ptr);
201 return *this;
202 }
203 Handle& operator=(libav_ptr&& ptr)
204 {
205 ((impl_t&)*this) = std::move(ptr);
206 return *this;
207 }
208 Handle& operator=(libav_stream_ptr&& ptr)
209 {
210 ((impl_t&)*this) = std::move(ptr);
211 return *this;
212 }
213 Handle& operator=(sndfile_ptr&& ptr)
214 {
215 ((impl_t&)*this) = std::move(ptr);
216 return *this;
217 }
218 };
219
220 using view_impl_t = ossia::nullable_variant<MmapView, RAMView, StreamView>;
221 struct ViewHandle : view_impl_t
222 {
223 using view_impl_t::view_impl_t;
224 ViewHandle(const Handle&);
225
226 void frame(int64_t start_frame, ossia::small_vector<float, 8>& out) noexcept;
227 void absmax_frame(
228 int64_t start_frame, int64_t end_frame,
229 ossia::small_vector<float, 8>& out) noexcept;
230 void minmax_frame(
231 int64_t start_frame, int64_t end_frame,
232 ossia::small_vector<FloatPair, 8>& out) noexcept;
233
236 std::shared_ptr<const WaveformSummary> summary;
237
239 bool supports_summary() const noexcept;
240
242 bool build_summary(WaveformSummary& s) noexcept;
243
244 private:
245 bool summary_minmax(
246 int64_t start_frame, int64_t end_frame,
247 ossia::small_vector<FloatPair, 8>& out) noexcept;
248 void merge_range(
249 int64_t start_frame, int64_t end_frame,
250 ossia::small_vector<FloatPair, 8>& out) noexcept;
251 };
252
253 // Note : this is a copy, because it's not thread safe.
254 ViewHandle handle() const noexcept { return m_impl; }
255
256 const Handle& unsafe_handle() const noexcept { return m_impl; }
257
258 std::optional<double> knownTempo() const noexcept;
259
260private:
261 void load_libav(int rate);
262 void load_libav_stream();
263 void load_drwav();
264 void load_sndfile();
265
266 friend class SoundComponentSetup;
267
268 QString m_originalFile;
269 QString m_file;
270 QString m_fileName;
271 int m_track{-1};
272
273 RMSData* m_rms{};
274 int m_sampleRate{};
275 std::atomic_bool m_fullyDecoded{};
276
277 mutable std::mutex m_summaryMutex;
278 mutable std::shared_ptr<const WaveformSummary> m_summary;
279 mutable bool m_summaryUnavailable{};
280
281 Handle m_impl;
282};
283
284class SCORE_PLUGIN_MEDIA_EXPORT AudioFileManager final : public QObject
285{
286public:
287 AudioFileManager() noexcept;
288 ~AudioFileManager() noexcept;
289
290 static AudioFileManager& instance() noexcept;
291 std::shared_ptr<AudioFile>
292 get(const QString&, int stream, const score::DocumentContext&);
293
294 std::shared_ptr<AudioFile> get(const QString& absolutePath, int stream);
295
296private:
297 struct StreamInfo
298 {
299 struct hash
300 {
301 std::size_t operator()(const StreamInfo& f) const noexcept
302 {
303 constexpr const QtPrivate::QHashCombine combine{
304#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0)
305 0
306#endif
307 };
308 std::size_t h = 0;
309 h = combine(h, f.file);
310 h = combine(h, f.stream);
311 return h;
312 }
313 };
314 bool operator==(const StreamInfo& other) const noexcept
315 {
316 return file == other.file && stream == other.stream;
317 }
318 QString file;
319 int stream{-1};
320 };
321 ossia::hash_map<StreamInfo, std::shared_ptr<AudioFile>, StreamInfo::hash> m_handles;
322};
323
327SCORE_PLUGIN_MEDIA_EXPORT
328void writeAudioArrayToFile(const QString& path, const ossia::audio_array& arr, int fs);
329
330std::optional<double> estimateTempo(const AudioFile& file);
331std::optional<double> estimateTempo(const QString& filePath);
332
333SCORE_PLUGIN_MEDIA_EXPORT std::optional<AudioInfo> probe(const QString& path);
334
335}
336
337Q_DECLARE_METATYPE(std::shared_ptr<Media::AudioFile>)
338W_REGISTER_ARGTYPE(std::shared_ptr<Media::AudioFile>)
339Q_DECLARE_METATYPE(const Media::AudioFile*)
340W_REGISTER_ARGTYPE(const Media::AudioFile*)
Definition AudioDecoder.hpp:46
Definition MediaFileHandle.hpp:285
Definition SndfileDecoder.hpp:9
Definition SoundComponent.cpp:70
Base toolkit upon which the software is built.
Definition Application.cpp:108
Definition MediaFileHandle.hpp:180
Definition MediaFileHandle.hpp:137
Definition MediaFileHandle.hpp:145
Definition MediaFileHandle.hpp:124
Definition MediaFileHandle.hpp:164
Definition MediaFileHandle.hpp:131
Definition MediaFileHandle.hpp:175
Definition MediaFileHandle.hpp:152
Definition MediaFileHandle.hpp:170
Definition MediaFileHandle.hpp:222
std::shared_ptr< const WaveformSummary > summary
Definition MediaFileHandle.hpp:236
Definition MediaFileHandle.hpp:72
QString fileName() const
Actual filename.
Definition MediaFileHandle.hpp:86
bool finishedDecoding() const noexcept
Atomic: the waveform threads ask while the decoder is still running.
Definition MediaFileHandle.hpp:101
QString originalFile() const
The text passed to the load function.
Definition MediaFileHandle.hpp:83
QString absoluteFileName() const
Absolute resolved filename.
Definition MediaFileHandle.hpp:89
Definition MediaFileHandle.hpp:300
Definition MediaFileHandle.hpp:64
Definition WaveformSummary.hpp:10
Definition RMSData.hpp:16
Definition WaveformSummary.hpp:32
Definition DocumentContext.hpp:18