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
qandroidaudiodevices.cpp
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
5
9
10#include <private/qplatformmediaintegration_p.h>
11
12#include <QtCore/qjniobject.h>
13
15
16Q_DECLARE_JNI_CLASS(QtAudioDeviceManager,
17 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager");
18Q_DECLARE_JNI_CLASS(AudioDeviceInfo, "android/media/AudioDeviceInfo");
19
20using namespace QtJniTypes;
21
22namespace {
23
24QAudioFormat preferredFormatForDevice(const QtJniTypes::AudioDeviceInfo &deviceInfo)
25{
26 QAudioFormat preferredFormat;
27
28 // Set preferred channel count based on what device reports, with default set to stereo (2)
29 QJniArray<jint> channelCounts = deviceInfo.callMethod<QJniArray<jint>>("getChannelCounts");
30 if (channelCounts.isEmpty()) {
31 preferredFormat.setChannelCount(2);
32 } else {
33 const auto [minIt, maxIt] = std::minmax_element(channelCounts.begin(), channelCounts.end());
34 preferredFormat.setChannelCount(std::clamp(2, *minIt, *maxIt));
35 }
36
37 // Get optimal sample rate from AudioManager
38 preferredFormat.setSampleRate(
39 QtAudioDeviceManager::callStaticMethod<jint>("getDefaultSampleRate"));
40
41 // Using Float avoids conversions for processing, so we should prefer that instead of whatever
42 // the device uses natively
43 preferredFormat.setSampleFormat(QAudioFormat::Float);
44
45 return preferredFormat;
46};
47
48QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode)
49{
50 if (mode == QAudioDevice::Null)
51 return {};
52
53 QList<QAudioDevice> devices;
54 const char *getMethod =
55 mode == QAudioDevice::Input ? "getAudioInputDevices" : "getAudioOutputDevices";
56 auto deviceInfos =
57 QtAudioDeviceManager::callStaticMethod<QJniArray<AudioDeviceInfo>>(getMethod);
58
59 if (!deviceInfos.isValid())
60 return {};
61
62 for (int i = 0; i < deviceInfos.size(); ++i) {
63 AudioDeviceInfo deviceInfo = deviceInfos.at(i);
64 int id = deviceInfo.callMethod<jint>("getId");
65 jint deviceType = deviceInfo.callMethod<jint>("getType");
66 auto description = QtAudioDeviceManager::callStaticMethod<QString>(
67 "audioDeviceTypeToString", deviceType);
68 bool isBluetoothDevice =
69 QtAudioDeviceManager::callStaticMethod<jboolean>("isBluetoothDevice", deviceInfo);
70 devices << QAudioDevicePrivate::createQAudioDevice(std::make_unique<QAndroidAudioDevice>(
71 QString::number(id).toUtf8(), description, mode,
72 preferredFormatForDevice(deviceInfo), isBluetoothDevice, i == 0));
73 }
74
75 return devices;
76}
77
78} // namespace
79
80QAndroidAudioDevices::QAndroidAudioDevices() : QPlatformAudioDevices()
81{
82 QtAudioDeviceManager::callStaticMethod<void>("registerAudioHeadsetStateReceiver");
83}
84
85QAndroidAudioDevices::~QAndroidAudioDevices()
86{
87 // Object of QAndroidAudioDevices type is static. Unregistering will happend only when closing
88 // the application. In such case it is probably not needed, but let's leave it for
89 // compatibility with Android documentation
90 QtAudioDeviceManager::callStaticMethod<void>("unregisterAudioHeadsetStateReceiver");
91}
92
93QList<QAudioDevice> QAndroidAudioDevices::findAudioInputs() const
94{
95 return availableDevices(QAudioDevice::Input);
96}
97
98QList<QAudioDevice> QAndroidAudioDevices::findAudioOutputs() const
99{
100 return availableDevices(QAudioDevice::Output);
101}
102
103QPlatformAudioSource *QAndroidAudioDevices::createAudioSource(const QAudioDevice &deviceInfo,
104 const QAudioFormat &fmt,
105 QObject *parent)
106{
107 return new QtAAudio::QAndroidAudioSource(deviceInfo, fmt, parent);
108}
109
110QPlatformAudioSink *QAndroidAudioDevices::createAudioSink(const QAudioDevice &deviceInfo,
111 const QAudioFormat &fmt, QObject *parent)
112{
113 return new QtAAudio::QAndroidAudioSink(deviceInfo, fmt, parent);
114}
115
116static void onAudioInputDevicesUpdated(JNIEnv * /*env*/, jobject /*thiz*/)
117{
118 static_cast<QAndroidAudioDevices *>(QPlatformMediaIntegration::instance()->audioDevices())
119 ->onAudioInputsChanged();
120}
121
122static void onAudioOutputDevicesUpdated(JNIEnv * /*env*/, jobject /*thiz*/)
123{
124 static_cast<QAndroidAudioDevices *>(QPlatformMediaIntegration::instance()->audioDevices())
125 ->onAudioOutputsChanged();
126}
127
128Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
129{
130 static bool initialized = false;
131 if (initialized)
132 return JNI_VERSION_1_6;
133 initialized = true;
134
135 QT_USE_NAMESPACE
136 typedef union {
137 JNIEnv *nativeEnvironment;
138 void *venv;
139 } UnionJNIEnvToVoid;
140
141 UnionJNIEnvToVoid uenv;
142 uenv.venv = NULL;
143
144 if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK)
145 return JNI_ERR;
146
147 const auto context = QNativeInterface::QAndroidApplication::context();
148 QtAudioDeviceManager::callStaticMethod<void>("setContext", context);
149
150 const JNINativeMethod methods[] = {
151 { "onAudioInputDevicesUpdated", "()V", (void *)onAudioInputDevicesUpdated },
152 { "onAudioOutputDevicesUpdated", "()V", (void *)onAudioOutputDevicesUpdated }
153 };
154
155 bool registered = QJniEnvironment().registerNativeMethods(
156 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager", methods,
157 std::size(methods));
158
159 if (!registered)
160 return JNI_ERR;
161
162 return JNI_VERSION_1_6;
163}
164
165QT_END_NAMESPACE
static void onAudioInputDevicesUpdated(JNIEnv *, jobject)
static void onAudioOutputDevicesUpdated(JNIEnv *, jobject)
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")