diff --git a/DroidStar.pro b/DroidStar.pro index b35ced5..ea0ba14 100644 --- a/DroidStar.pro +++ b/DroidStar.pro @@ -1,7 +1,6 @@ QT += quick quickcontrols2 network multimedia android:QT += androidextras -linux:QT += serialport -unix:QT += serialport +unix:!ios:QT += serialport CONFIG += c++11 LFLAGS += android:INCLUDEPATH += $$(HOME)/Android/android-build/include @@ -15,7 +14,6 @@ win32:QMAKE_LFLAGS += -static QMAKE_LFLAGS_WINDOWS += --enable-stdcall-fixup RC_ICONS = images/droidstar.ico ICON = images/droidstar.icns -macx:QT += serialport macx::INCLUDEPATH += /usr/local/include macx:LIBS += -L/usr/local/lib -framework AVFoundation macx:QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.14 @@ -24,6 +22,9 @@ ios:LIBS += -lvocoder -framework AVFoundation ios:QMAKE_TARGET_BUNDLE_PREFIX = com.dudetronics ios:QMAKE_BUNDLE = droidstar ios:VERSION = 0.43.20 +ios:Q_ENABLE_BITCODE.name = ENABLE_BITCODE +ios:Q_ENABLE_BITCODE.value = NO +ios:QMAKE_MAC_XCODE_SETTINGS += Q_ENABLE_BITCODE ios:QMAKE_ASSET_CATALOGS += Images.xcassets ios:QMAKE_INFO_PLIST = Info.plist VERSION_BUILD='$(shell cd $$PWD;git rev-parse --short HEAD)' @@ -150,9 +151,10 @@ contains(ANDROID_TARGET_ARCH,arm64-v8a) { LIBS += -L$$(HOME)/Android/local/lib64 ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android OTHER_FILES += android/src + #ANDROID_ABIS = arm64-v8a } -ANDROID_ABIS = armeabi-v7a arm64-v8a +#ANDROID_ABIS = armeabi-v7a arm64-v8a contains(DEFINES, USE_FLITE){ LIBS += -lflite_cmu_us_slt -lflite_cmu_us_kal16 -lflite_cmu_us_awb -lflite_cmu_us_rms -lflite_usenglish -lflite_cmulex -lflite -lasound diff --git a/README.md b/README.md index cf32d1a..dfb84b3 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Port: UDP port of node, usually 4569. Add DTMF commands like \*3node, \*1node, \*70, etc in the IAX DTMF box and hit send to send the DTMF string. Details on various commands can be found at the AllStar wiki and others. # General building instructions -This software is written primarily in C++ on Linux and requires Qt5 >= Qt5.15, and natually the devel packages to build. The imbe_vocoder library is also required. Java, QML (Javascript based), and C# code is also used where necessary. The preferred way to obtain Qt 5.15 is to use the Qt open source online installer from the Qt website. Run this installer as a user (not root) to keep the Qt installation separate from your system libs. Select the option as shown in this pic https://imgur.com/i0WuFCY which will install everything under ~/Qt. +This software is written primarily in C++ on Linux and requires Qt5 >= Qt5.15 or Qt6 >= Qt6.3, and natually the devel packages to build. The imbe_vocoder library is also required. Java, QML (Javascript based), and C# code is also used where necessary. The preferred way to obtain Qt 5.15 is to use the Qt open source online installer from the Qt website. Run this installer as a user (not root) to keep the Qt installation separate from your system libs. Select the option as shown in this pic https://imgur.com/i0WuFCY which will install everything under ~/Qt. The imbe_vocoder library is a prerequisite: https://github.com/nostar/imbe_vocoder diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index c077f73..5ec4085 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,6 +1,6 @@ - + @@ -41,8 +41,6 @@ - - @@ -78,8 +76,5 @@ - - - - + diff --git a/android/build.gradle b/android/build.gradle index 3087d08..7f78543 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,7 +5,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' + classpath 'com.android.tools.build:gradle:4.1.0' } } diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 4b7e1f3..186b715 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/androidserialport.cpp b/androidserialport.cpp index 8996938..6729c42 100644 --- a/androidserialport.cpp +++ b/androidserialport.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - +#include #include "androidserialport.h" #ifdef Q_OS_ANDROID @@ -41,7 +41,11 @@ QStringList AndroidSerialPort::discover_devices() QStringList l; l.clear(); qDebug() << "AndroidSerialPort::discover_devices()"; +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) QAndroidJniObject d = serialJavaObject.callObjectMethod("discover_devices", "(Landroid/content/Context;)[Ljava/lang/String;", QtAndroid::androidContext().object()); +#else + QAndroidJniObject d = serialJavaObject.callObjectMethod("discover_devices", "(Landroid/content/Context;)[Ljava/lang/String;", QNativeInterface::QAndroidApplication::context()); +#endif jobjectArray devices = d.object(); int size = env->GetArrayLength(devices); @@ -54,7 +58,11 @@ QStringList AndroidSerialPort::discover_devices() int AndroidSerialPort::open(int p) { +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) qDebug() << serialJavaObject.callObjectMethod("setup_serial", "(Landroid/content/Context;)Ljava/lang/String;", QtAndroid::androidContext().object()).toString(); +#else + serialJavaObject.callObjectMethod("setup_serial", "(Landroid/content/Context;)Ljava/lang/String;", QNativeInterface::QAndroidApplication::context()); +#endif return p; } diff --git a/androidserialport.h b/androidserialport.h index 52368d5..7734434 100644 --- a/androidserialport.h +++ b/androidserialport.h @@ -20,7 +20,12 @@ #include #ifdef Q_OS_ANDROID +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) #include +#else +#include +#include +#endif class AndroidSerialPort : public QObject { @@ -51,7 +56,13 @@ signals: private: explicit AndroidSerialPort(QObject * parent = nullptr); static void java_data_received(JNIEnv *env, jobject t, jbyteArray data); +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) QAndroidJniObject serialJavaObject; +#else + QJniObject serialJavaObject; + typedef QJniObject QAndroidJniObject; + typedef QJniEnvironment QAndroidJniEnvironment; +#endif QByteArray m_received; }; #endif diff --git a/audioengine.cpp b/audioengine.cpp index b35acbd..2f0caba 100644 --- a/audioengine.cpp +++ b/audioengine.cpp @@ -44,6 +44,7 @@ AudioEngine::~AudioEngine() { } +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) QStringList AudioEngine::discover_audio_devices(uint8_t d) { QStringList list; @@ -151,6 +152,112 @@ void AudioEngine::init() fprintf(stderr, "Capture device: %s SR: %d resample factor: %f\n", info.deviceName().toStdString().c_str(), sr, m_srm);fflush(stderr); } } +#else +QStringList AudioEngine::discover_audio_devices(uint8_t d) +{ + QStringList list; + QList devices; + + if(d){ + devices = QMediaDevices::audioOutputs(); + } + else{ + devices = QMediaDevices::audioInputs(); + } + + for (QList::ConstIterator it = devices.constBegin(); it != devices.constEnd(); ++it ) { + //fprintf(stderr, "Playback device name = %s\n", (*it).deviceName().toStdString().c_str());fflush(stderr); + list.append((*it).description()); + } + + return list; +} + +void AudioEngine::init() +{ + QAudioFormat format; + QAudioFormat tempformat; + format.setSampleRate(8000); + format.setChannelCount(1); + format.setSampleFormat(QAudioFormat::Int16); + //format.setSampleSize(16); + //format.setCodec("audio/pcm"); + //format.setByteOrder(QAudioFormat::LittleEndian); + //format.setSampleType(QAudioFormat::SignedInt); + + m_agc = true; + + QList devices = QMediaDevices::audioOutputs(); + if(devices.size() == 0){ + fprintf(stderr, "No audio playback hardware found\n");fflush(stderr); + } + else{ + QAudioDevice device(QMediaDevices::defaultAudioOutput()); + for (QList::ConstIterator it = devices.constBegin(); it != devices.constEnd(); ++it ) { + if(MACHAK){ + qDebug() << "Playback device name = " << (*it).description(); + qDebug() << (*it).supportedSampleFormats(); + qDebug() << (*it).preferredFormat(); + } + if((*it).description() == m_outputdevice){ + device = *it; + } + } + if (!device.isFormatSupported(format)) { + qWarning() << "Raw audio format not supported by backend, trying nearest format."; + //tempformat = device.nearestFormat(format); + //qWarning() << "Format now set to " << format.sampleRate() << ":" << format.sampleSize(); + } + else{ + tempformat = format; + } + fprintf(stderr, "Using playback device %s\n", device.description().toStdString().c_str());fflush(stderr); + + m_out = new QAudioSink(device, tempformat, this); + m_out->setBufferSize(1280); + connect(m_out, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State))); + //m_outdev = m_out->start(); + } + + devices = QMediaDevices::audioInputs(); + + if(devices.size() == 0){ + fprintf(stderr, "No audio recording hardware found\n");fflush(stderr); + } + else{ + QAudioDevice device(QMediaDevices::defaultAudioInput()); + for (QList::ConstIterator it = devices.constBegin(); it != devices.constEnd(); ++it ) { + if(MACHAK){ + qDebug() << "Playback device name = " << (*it).description(); + qDebug() << (*it).supportedSampleFormats(); + qDebug() << (*it).preferredFormat(); + } + if((*it).description() == m_inputdevice){ + device = *it; + } + } + if (!device.isFormatSupported(format)) { + qWarning() << "Raw audio format not supported by backend, trying nearest format."; + //tempformat = device.nearestFormat(format); + //qWarning() << "Format now set to " << format.sampleRate() << ":" << format.sampleSize(); + } + else{ + tempformat = format; + } + + int sr = 8000; + if(MACHAK){ + sr = device.preferredFormat().sampleRate(); + m_srm = (float)sr / 8000.0; + } + format.setSampleRate(sr); + m_in = new QAudioSource(device, format, this); + fprintf(stderr, "Capture device: %s SR: %d resample factor: %f\n", device.description().toStdString().c_str(), sr, m_srm);fflush(stderr); + } + + //start_playback(); +} +#endif void AudioEngine::start_capture() { @@ -184,12 +291,9 @@ void AudioEngine::stop_playback() void AudioEngine::input_data_received() { - QByteArray data; - qint64 len = m_in->bytesReady(); + QByteArray data = m_indev->readAll(); - if (len > 0){ - data.resize(len); - m_indev->read(data.data(), len); + if (data.size() > 0){ /* fprintf(stderr, "AUDIOIN: "); for(int i = 0; i < len; ++i){ @@ -200,15 +304,15 @@ void AudioEngine::input_data_received() */ if(MACHAK){ std::vector samples; - for(int i = 0; i < len; i += 2){ + for(int i = 0; i < data.size(); i += 2){ samples.push_back(((data.data()[i+1] << 8) & 0xff00) | (data.data()[i] & 0xff)); } - for(float i = 0; i < (float)len/2; i += m_srm){ + for(float i = 0; i < (float)data.size()/2; i += m_srm){ m_audioinq.enqueue(samples[i]); } } else{ - for(int i = 0; i < len; i += (2 * m_srm)){ + for(int i = 0; i < data.size(); i += (2 * m_srm)){ m_audioinq.enqueue(((data.data()[i+1] << 8) & 0xff00) | (data.data()[i] & 0xff)); } } diff --git a/audioengine.h b/audioengine.h index 2ac6db4..b294984 100644 --- a/audioengine.h +++ b/audioengine.h @@ -19,10 +19,17 @@ #define AUDIOENGINE_H #include +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) #include #include -#include #include +#else +#include +#include +#include +#include +#endif +#include #include #define AUDIO_OUT 1 @@ -56,8 +63,13 @@ signals: private: QString m_outputdevice; QString m_inputdevice; +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) QAudioOutput *m_out; QAudioInput *m_in; +#else + QAudioSink *m_out; + QAudioSource *m_in; +#endif QIODevice *m_outdev; QIODevice *m_indev; QQueue m_audioinq; diff --git a/droidstar.cpp b/droidstar.cpp index d621668..270b895 100644 --- a/droidstar.cpp +++ b/droidstar.cpp @@ -18,7 +18,12 @@ #include "droidstar.h" #include "httpmanager.h" #ifdef Q_OS_ANDROID +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) #include +#else +#include +#include +#endif #endif #ifdef Q_OS_IOS #include "micpermission.h" @@ -52,15 +57,7 @@ DroidStar::DroidStar(QObject *parent) : #endif #if defined(Q_OS_ANDROID) keepScreenOn(); - const QString permission("android.permission.READ_EXTERNAL_STORAGE"); - if(QtAndroid::checkPermission(permission) != QtAndroid::PermissionResult::Granted){ - auto resultHash = QtAndroid::requestPermissionsSync(QStringList({permission})); - if(resultHash[permission] == QtAndroid::PermissionResult::Denied){ - qDebug() << "Storage read permissions denied"; - } - } #endif - check_host_files(); discover_devices(); process_settings(); @@ -81,6 +78,7 @@ DroidStar::~DroidStar() } #ifdef Q_OS_ANDROID +#if QT_VERSION < QT_VERSION_CHECK(6, 3, 0) void DroidStar::keepScreenOn() { char const * const action = "addFlags"; @@ -95,7 +93,22 @@ void DroidStar::keepScreenOn() } }}); } +#else +void DroidStar::keepScreenOn() +{ + char const * const action = "addFlags"; + QNativeInterface::QAndroidApplication::runOnAndroidMainThread([action](){ + QJniObject activity = QNativeInterface::QAndroidApplication::context(); + if (activity.isValid()) { + QJniObject window = activity.callObjectMethod("getWindow", "()Landroid/view/Window;"); + if (window.isValid()) { + const int FLAG_KEEP_SCREEN_ON = 128; + window.callMethod("addFlags", "(I)V", FLAG_KEEP_SCREEN_ON); + } + }}); +} +#endif void DroidStar::reset_connect_status() { if(connect_status == Mode::CONNECTED_RW){ @@ -1267,6 +1280,7 @@ void DroidStar::update_data(Mode::MODEINFO info) } else{ emit update_log("Vocoder plugin not loaded"); + emit open_vocoder_dialog(); } } diff --git a/micpermission.mm b/micpermission.mm index 6dfd4af..f219e3f 100755 --- a/micpermission.mm +++ b/micpermission.mm @@ -18,9 +18,15 @@ #include "micpermission.h" //#import //#import +#ifdef Q_OS_IOS +#import +#endif #import int MicPermission::check_permission() { +#ifdef Q_OS_IOS + [UIApplication sharedApplication].idleTimerDisabled = YES; +#endif [UIApplication sharedApplication].idleTimerDisabled = YES; AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio]; if(status != AVAuthorizationStatusAuthorized){ diff --git a/serialambe.cpp b/serialambe.cpp index fe54bbb..49a5d41 100755 --- a/serialambe.cpp +++ b/serialambe.cpp @@ -80,13 +80,9 @@ QMap SerialAMBE::discover_devices() + "Manufacturer: " + (!serialPortInfo.manufacturer().isEmpty() ? serialPortInfo.manufacturer() : blankString) + ENDLINE + "Serial number: " + (!serialPortInfo.serialNumber().isEmpty() ? serialPortInfo.serialNumber() : blankString) + ENDLINE + "Vendor Identifier: " + (serialPortInfo.hasVendorIdentifier() ? QByteArray::number(serialPortInfo.vendorIdentifier(), 16) : blankString) + ENDLINE - + "Product Identifier: " + (serialPortInfo.hasProductIdentifier() ? QByteArray::number(serialPortInfo.productIdentifier(), 16) : blankString) + ENDLINE - + "Busy: " + (serialPortInfo.isBusy() ? "Yes" : "No") + ENDLINE; + + "Product Identifier: " + (serialPortInfo.hasProductIdentifier() ? QByteArray::number(serialPortInfo.productIdentifier(), 16) : blankString) + ENDLINE; fprintf(stderr, "%s", out.toStdString().c_str());fflush(stderr); - if(!serialPortInfo.isBusy()){ - //devlist[serialPortInfo.systemLocation()] = serialPortInfo.portName() + " - " + serialPortInfo.manufacturer() + " " + serialPortInfo.description() + " - " + serialPortInfo.serialNumber(); - devlist[serialPortInfo.systemLocation()] = serialPortInfo.description() + ":" + serialPortInfo.systemLocation(); - } + devlist[serialPortInfo.systemLocation()] = serialPortInfo.description() + ":" + serialPortInfo.systemLocation(); } } #else @@ -113,8 +109,7 @@ void SerialAMBE::connect_to_serial(QString p) + "Manufacturer: " + (!info.manufacturer().isEmpty() ? info.manufacturer() : blankString) + ENDLINE + "Serial number: " + (!info.serialNumber().isEmpty() ? info.serialNumber() : blankString) + ENDLINE + "Vendor Identifier: " + (info.hasVendorIdentifier() ? QByteArray::number(info.vendorIdentifier(), 16) : blankString) + ENDLINE - + "Product Identifier: " + (info.hasProductIdentifier() ? QByteArray::number(info.productIdentifier(), 16) : blankString) + ENDLINE - + "Busy: " + (info.isBusy() ? "Yes" : "No") + ENDLINE; + + "Product Identifier: " + (info.hasProductIdentifier() ? QByteArray::number(info.productIdentifier(), 16) : blankString) + ENDLINE; fprintf(stderr, "%s", out.toStdString().c_str());fflush(stderr); m_description = info.description(); @@ -457,7 +452,6 @@ bool SerialAMBE::get_audio(int16_t *audio) m_serialdata.dequeue(); m_serialdata.dequeue(); m_serialdata.dequeue(); - //qDebug() << "Found header, deleting....................................................................."; for(int i = 0; i < 160; i++){ //Byte swap BE to LE audio[i] = ((m_serialdata.dequeue() << 8) & 0xff00) | (m_serialdata.dequeue() & 0xff); @@ -466,7 +460,6 @@ bool SerialAMBE::get_audio(int16_t *audio) r = true; } else{ - qDebug() << "Header not found..............................................................................." ; while( (m_serialdata.size() > 5) && ( ((uint8_t)m_serialdata[0] != header[0]) || ((uint8_t)m_serialdata[1] != header[1]) || @@ -475,7 +468,6 @@ bool SerialAMBE::get_audio(int16_t *audio) ((uint8_t)m_serialdata[4] != header[4]) || ((uint8_t)m_serialdata[5] != header[5]))){ m_serialdata.dequeue(); - //qDebug() << "Finding start of audio frame"; } } }