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