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