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
78 AudioFile();
79 ~AudioFile() override;
80
81 void load(DecodingSetup opts);
82
84 QString originalFile() const { return m_originalFile; }
85
87 QString fileName() const { return m_fileName; }
88
90 QString absoluteFileName() const { return m_file; }
91
92 int sampleRate() const { return m_sampleRate; }
93
94 int64_t decodedSamples() const;
95
96 // Number of samples in a channel.
97 int64_t samples() const;
98 int64_t channels() const;
99
100 bool empty() const { return channels() == 0 || samples() == 0; }
101 bool finishedDecoding() const noexcept { return m_fullyDecoded; }
102
103 const RMSData& rms() const;
104
106 ossia::audio_array getAudioArray() const;
107
108 Nano::Signal<void()> on_mediaChanged;
109 Nano::Signal<void()> on_newData;
110 Nano::Signal<void()> on_finishedDecoding;
111
113 {
114 std::shared_ptr<QFile> file;
115 void* data{};
116 ossia::drwav_handle wav;
117 };
118
120 {
121 ossia::audio_handle handle;
122 ossia::small_vector<ossia::audio_sample*, 8> data;
123 };
124
126 {
127 explicit LibavReader(int rate) noexcept
128 : decoder{rate}
129 {
130 }
131 AudioDecoder decoder;
132 };
134 {
135 std::string path;
136 int stream{-1};
137 int64_t samples{};
138 int channels{};
139 };
141 {
142 SndfileDecoder decoder;
143 };
144
145 using libav_ptr = std::shared_ptr<LibavReader>;
147 using mmap_ptr = MmapReader;
149 using impl_t
150 = ossia::nullable_variant<mmap_ptr, libav_ptr, sndfile_ptr, libav_stream_ptr>;
151
152 struct MmapView
153 {
154 // Copy for thread-safety reasons
155 ossia::drwav_handle wav;
156 };
157
159 {
160 std::shared_ptr<ossia::libav_handle> handle;
161 };
162
163 struct RAMView
164 {
165 ossia::small_vector<audio_sample*, 8> data;
166 };
167
168 struct Handle : impl_t
169 {
170 using impl_t::impl_t;
171 Handle(mmap_ptr&& ptr)
172 : impl_t{std::move(ptr)}
173 {
174 }
175 Handle(libav_ptr&& ptr)
176 : impl_t{std::move(ptr)}
177 {
178 }
180 : impl_t{std::move(ptr)}
181 {
182 }
183 Handle(sndfile_ptr&& ptr)
184 : impl_t{std::move(ptr)}
185 {
186 }
187 Handle& operator=(mmap_ptr&& ptr)
188 {
189 ((impl_t&)*this) = std::move(ptr);
190 return *this;
191 }
192 Handle& operator=(libav_ptr&& ptr)
193 {
194 ((impl_t&)*this) = std::move(ptr);
195 return *this;
196 }
197 Handle& operator=(libav_stream_ptr&& ptr)
198 {
199 ((impl_t&)*this) = std::move(ptr);
200 return *this;
201 }
202 Handle& operator=(sndfile_ptr&& ptr)
203 {
204 ((impl_t&)*this) = std::move(ptr);
205 return *this;
206 }
207 };
208
209 using view_impl_t = ossia::nullable_variant<MmapView, RAMView, StreamView>;
210 struct ViewHandle : view_impl_t
211 {
212 using view_impl_t::view_impl_t;
213 ViewHandle(const Handle&);
214
215 void frame(int64_t start_frame, ossia::small_vector<float, 8>& out) noexcept;
216 void absmax_frame(
217 int64_t start_frame, int64_t end_frame,
218 ossia::small_vector<float, 8>& out) noexcept;
219 void minmax_frame(
220 int64_t start_frame, int64_t end_frame,
221 ossia::small_vector<FloatPair, 8>& out) noexcept;
222 };
223
224 // Note : this is a copy, because it's not thread safe.
225 ViewHandle handle() const noexcept { return m_impl; }
226
227 const Handle& unsafe_handle() const noexcept { return m_impl; }
228
229 std::optional<double> knownTempo() const noexcept;
230
231private:
232 void load_libav(int rate);
233 void load_libav_stream();
234 void load_drwav();
235 void load_sndfile();
236
237 friend class SoundComponentSetup;
238
239 QString m_originalFile;
240 QString m_file;
241 QString m_fileName;
242 int m_track{-1};
243
244 RMSData* m_rms{};
245 int m_sampleRate{};
246 bool m_fullyDecoded{};
247
248 Handle m_impl;
249};
250
251class SCORE_PLUGIN_MEDIA_EXPORT AudioFileManager final : public QObject
252{
253public:
254 AudioFileManager() noexcept;
255 ~AudioFileManager() noexcept;
256
257 static AudioFileManager& instance() noexcept;
258 std::shared_ptr<AudioFile>
259 get(const QString&, int stream, const score::DocumentContext&);
260
261 std::shared_ptr<AudioFile> get(const QString& absolutePath, int stream);
262
263private:
264 struct StreamInfo
265 {
266 struct hash
267 {
268 std::size_t operator()(const StreamInfo& f) const noexcept
269 {
270 constexpr const QtPrivate::QHashCombine combine;
271 std::size_t h = 0;
272 h = combine(h, f.file);
273 h = combine(h, f.stream);
274 return h;
275 }
276 };
277 bool operator==(const StreamInfo& other) const noexcept
278 {
279 return file == other.file && stream == other.stream;
280 }
281 QString file;
282 int stream{-1};
283 };
284 ossia::hash_map<StreamInfo, std::shared_ptr<AudioFile>, StreamInfo::hash> m_handles;
285};
286
290SCORE_PLUGIN_MEDIA_EXPORT
291void writeAudioArrayToFile(const QString& path, const ossia::audio_array& arr, int fs);
292
293std::optional<double> estimateTempo(const AudioFile& file);
294std::optional<double> estimateTempo(const QString& filePath);
295
296std::optional<AudioInfo> probe(const QString& path);
297
298}
299
300Q_DECLARE_METATYPE(std::shared_ptr<Media::AudioFile>)
301W_REGISTER_ARGTYPE(std::shared_ptr<Media::AudioFile>)
302Q_DECLARE_METATYPE(const Media::AudioFile*)
303W_REGISTER_ARGTYPE(const Media::AudioFile*)
Definition AudioDecoder.hpp:37
Definition MediaFileHandle.hpp:252
Definition SndfileDecoder.hpp:9
Definition SoundComponent.cpp:37
Base toolkit upon which the software is built.
Definition Application.cpp:90
Definition MediaFileHandle.hpp:169
Definition MediaFileHandle.hpp:126
Definition MediaFileHandle.hpp:134
Definition MediaFileHandle.hpp:113
Definition MediaFileHandle.hpp:153
Definition MediaFileHandle.hpp:120
Definition MediaFileHandle.hpp:164
Definition MediaFileHandle.hpp:141
Definition MediaFileHandle.hpp:159
Definition MediaFileHandle.hpp:211
Definition MediaFileHandle.hpp:74
QString fileName() const
Actual filename.
Definition MediaFileHandle.hpp:87
QString originalFile() const
The text passed to the load function.
Definition MediaFileHandle.hpp:84
QString absoluteFileName() const
Absolute resolved filename.
Definition MediaFileHandle.hpp:90
Definition MediaFileHandle.hpp:267
Definition MediaFileHandle.hpp:66
Definition MediaFileHandle.hpp:31
Definition RMSData.hpp:16
Definition DocumentContext.hpp:18