Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qsamplecache_p.h
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifndef QSAMPLECACHE_P_H
5#define QSAMPLECACHE_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include <QtMultimedia/qaudioformat.h>
19#include <QtCore/private/qexpected_p.h>
20#include <QtCore/private/qglobal_p.h>
21#include <QtCore/qfuture.h>
22#include <QtCore/qmutex.h>
23#include <QtCore/qobject.h>
24#include <QtCore/qpointer.h>
25#include <QtCore/qset.h>
26#include <QtCore/qspan.h>
27#include <QtCore/qthread.h>
28#include <QtCore/qurl.h>
29
30#include <memory>
31#include <optional>
32
33QT_BEGIN_NAMESPACE
34
35enum class QSampleLoadError : uint8_t
36{
37 IoError,
38 FormatError,
39 DecoderError,
40 NotSupported,
41};
42
43namespace QtMultimediaPrivate {
44
45enum class SampleRate : int;
46inline std::optional<SampleRate> asSampleRate(std::optional<int> arg)
47{
48 if (!arg)
49 return std::nullopt;
50 return SampleRate(*arg);
51}
52
53} // namespace QtMultimediaPrivate
54
55class QSampleCache;
56
57class Q_MULTIMEDIA_EXPORT QSample
58{
59public:
60 friend class QSampleCache;
61 using SampleRate = QtMultimediaPrivate::SampleRate;
62
63 enum State : uint8_t {
64 Creating,
65 Error,
66 Ready,
67 };
68 using SharedSamplePromise = QSharedPointer<QPromise<q23::expected<QSample *, QSample::State>>>;
69 ~QSample();
70
71 State state() const;
72 const QByteArray& data() const { Q_ASSERT(state() == Ready); return m_soundData; }
73 QSpan<const float> dataAsFloatSpan() const
74 {
75 Q_ASSERT(state() == Ready);
76 Q_ASSERT(m_audioFormat.sampleFormat() == QAudioFormat::Float);
77 return QSpan<const float>(reinterpret_cast<const float *>(m_soundData.constData()),
78 m_audioFormat.framesForBytes(m_soundData.size())
79 * m_audioFormat.channelCount());
80 }
81
82 const QAudioFormat &format() const
83 {
84 Q_ASSERT(state() == Ready);
85 return m_audioFormat;
86 }
87 qsizetype frameCount() const
88 {
89 Q_ASSERT(state() == Ready);
90 return m_audioFormat.framesForBytes(m_soundData.size());
91 }
92
93 void setError();
94 void setData(QByteArray, QAudioFormat);
95
96 QSample(QUrl url, QSampleCache *parent);
97 QSample(QUrl url, QSampleCache *parent, std::optional<SampleRate> targetSampleRate);
98
99 // For testing only
100 QSample(QByteArray data, QAudioFormat format)
101 : m_parent(nullptr), m_soundData(std::move(data)), m_audioFormat(format), m_url(), m_state(Ready) {}
102
103private:
104 QSample();
105
106 QSampleCache *m_parent;
107 QByteArray m_soundData;
108 QAudioFormat m_audioFormat;
109 const QUrl m_url;
110 const std::optional<SampleRate> m_targetSampleRate;
111 State m_state = State::Creating;
112
113 friend class QSampleCache;
114 void clearParent();
115};
116
117using SharedSamplePtr = std::shared_ptr<QSample>;
118using WeakSamplePtr = std::weak_ptr<QSample>;
119
120class Q_MULTIMEDIA_EXPORT QSampleCache : public QObject
121{
122public:
123 friend class QSample;
124
125 enum class SampleSourceType : uint8_t {
126 File,
127 NetworkManager,
128 AudioDecoder,
129 };
130
131 static QSampleCache *instance();
132
133 explicit QSampleCache(QObject *parent = nullptr);
134 ~QSampleCache() override;
135
136 QFuture<SharedSamplePtr>
137 requestSampleFuture(const QUrl &, std::optional<int> targetSampleRate = std::nullopt,
138 std::optional<SampleSourceType> forceSourceType = std::nullopt);
139
140 // functions below are for testing only
141 bool isCached(const QUrl &url, std::optional<int> targetSampleRate = std::nullopt) const;
142
143 using SampleLoadResult = q23::expected<std::pair<QByteArray, QAudioFormat>, QSampleLoadError>;
144
145 static SampleLoadResult loadSample(QSpan<const char>);
146 static SampleLoadResult loadSampleViaDecoder(std::variant<QUrl, QByteArray>);
147 static QFuture<SampleLoadResult> loadSampleAsyncViaDecoder(QByteArray);
148
149private:
150 std::unique_ptr<QIODevice> createStreamForSample(QSample &sample);
151#if QT_CONFIG(thread)
152 QThreadPool *threadPool();
153#endif
154
155private:
156 using SharedSamplePromise = std::shared_ptr<QPromise<SharedSamplePtr>>;
157
158 mutable QRecursiveMutex m_mutex;
159 using SampleRate = QtMultimediaPrivate::SampleRate;
160
161 using SampleKey = std::pair<QUrl, std::optional<SampleRate>>;
162 std::map<SampleKey, WeakSamplePtr> m_loadedSamples;
163 std::map<SampleKey, std::pair<SharedSamplePtr, QList<SharedSamplePromise>>> m_pendingSamples;
164
165 void removeUnreferencedSample(const QUrl &, std::optional<SampleRate> targetSampleRate);
166
167#if QT_CONFIG(thread)
168 static SampleLoadResult loadSample(const QUrl &,
169 std::optional<SampleSourceType> = std::nullopt);
170# ifndef Q_OS_WASM
171 QThreadPool m_threadPool{ this };
172# endif
173#endif
174 QFuture<SampleLoadResult> loadSampleAsync(const QUrl &,
175 std::optional<SampleSourceType> = std::nullopt);
176};
177
178QT_END_NAMESPACE
179
180#endif // QSAMPLECACHE_P_H
std::optional< SampleRate > asSampleRate(std::optional< int > arg)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)