Merge branch 'master' into panning

This commit is contained in:
Tarmo Johannes 2020-05-13 15:28:23 +03:00
commit 49245e6118
70 changed files with 12786 additions and 4213 deletions

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "libs/oboe"]
path = libs/oboe
url = https://github.com/google/oboe.git

101
ChangeLog
View file

@ -1,12 +1,43 @@
3.5.3git
TODO improve input channel mapping and introduce input/output gains with overrange to amplification (separate dialog, replace Pan, etc.)
- correct unregister of headless server and RPP file creation on
SIGINT/SIGTERM, coded by pljones (Tickets #130, #168)
TODO support internationalization
- for CoreAudio and 4 channel input, support mixing channels 1&2 with 3&4
TODO sometimes I cannot see the central server in the server list
- added bassoon/oboe/harp instrument icons created by dszgit;
congas/bongo created by bspeer (Ticket #131)
- link to docs from application Help menu (Ticket #90)
- support Mac CoreAudio aggregated devices (Ticket #138)
- added translations: french by trebmuh, portuguese by Snayler, spanish by ignotus666 (Ticket #77)
- new design for the About dialog (Ticket #189)
- new command line option -d to disconnect all clients on shutdown of the server (Ticket #161)
- bug fix: for mono capture jack audio interface Jamulus complains it
cannot make connections (Ticket #137)
- bug fix: fixed that Jamulus segfaults when jackd is restarted (Ticket #122, #127)
- bug fix: better handling of disconnect message in the client
TODO check if Tickets #130, #168 are really solved by code from pljones
TODO WIP support internationalization
TODO implement panning for channels (Ticket #52, #145)
TODO show mute state of others
@ -15,73 +46,73 @@ TODO sometimes I cannot see the central server in the server list
3.5.2 (2020-04-24)
* use audio level meter bars for normal skin
- use audio level meter bars for normal skin
* store Show All Musicians setting in the ini-file
- store Show All Musicians setting in the ini-file
* improved Mac installer, coded by doloopuntil
- improved Mac installer, coded by doloopuntil
* support to open ASIO driver setup(s) if startup failed due to incorrect driver settings (Ticket #117)
- support to open ASIO driver setup(s) if startup failed due to incorrect driver settings (Ticket #117)
* added -v/--version command line argument to output version information (Ticket #121)
- added -v/--version command line argument to output version information (Ticket #121)
* added bodhran and other instrument icons, bodhran created by bomm (Ticket #131)
- added bodhran and other instrument icons, bodhran created by bomm (Ticket #131)
* bug fix: if small network buffers are used we get much better audio quality when drop outs occur
- bug fix: if small network buffers are used we get much better audio quality when drop outs occur
* bug fix: if names given with the -o option were too long, the server registration failed (Ticket #91)
- bug fix: if names given with the -o option were too long, the server registration failed (Ticket #91)
* bug fix: audio level changes if Buffer Delay is changed (Ticket #106)
- bug fix: audio level changes if Buffer Delay is changed (Ticket #106)
* bug fix: do not reset fader level meters if number of clients change
- bug fix: do not reset fader level meters if number of clients change
* bug fix: fixed a crash with JackRouter 64 bit ASIO driver (Ticket #93, thanks to elliotclee)
- bug fix: fixed a crash with JackRouter 64 bit ASIO driver (Ticket #93, thanks to elliotclee)
3.5.1 (2020-04-18)
* added a Mute Stream button to hear your signal and the signal of the other clients but
do not transmit your signal to the server so that the other clients cannot hear you
- added a Mute Stream button to hear your signal and the signal of the other clients but
do not transmit your signal to the server so that the other clients cannot hear you
* added Enable Small Network Buffers switch to enable small sound card buffers in
combination with legacy OPUS packets since OPUS packets with 64 samples enable low
latency but can increase audio drop outs
- added Enable Small Network Buffers switch to enable small sound card buffers in
combination with legacy OPUS packets since OPUS packets with 64 samples enable low
latency but can increase audio drop outs
* upgrade OPUS codec library to v1.3.1 by doloopuntil
- upgrade OPUS codec library to v1.3.1 by doloopuntil
* server list registration status indicator added to the server GUI, coded by pljones
- server list registration status indicator added to the server GUI, coded by pljones
* improved auto jitter buffer for 64 samples frame size
- improved auto jitter buffer for 64 samples frame size
* the ping times in the server list are now more stable
- the ping times in the server list are now more stable
3.5.0 (2020-04-15)
* added support for 64 samples OPUS packets in the client (if a sound card buffer size
larger or equal than 128 samples is chosen, the legacy 128 samples OPUS packets are used)
- added support for 64 samples OPUS packets in the client (if a sound card buffer size
larger or equal than 128 samples is chosen, the legacy 128 samples OPUS packets are used)
* added a filter for the server list to, e.g., filter a specific country or search for a musician
- added a filter for the server list to, e.g., filter a specific country or search for a musician
* refresh server list if the Central Server address type is changed
- refresh server list if the Central Server address type is changed
* the unit of the mixer faders is now dB using the range -50 dB to 0 dB
- the unit of the mixer faders is now dB using the range -50 dB to 0 dB
* increased LED luminance (Ticket #71)
- increased LED luminance (Ticket #71)
* bug fix: the server welcome message may appear twice if the server list was double clicked
- bug fix: the server welcome message may appear twice if the server list was double clicked
3.4.7 (2020-04-11)
* added support for alternative Central Servers to solve the 200 server registration limit (Ticket #50)
- added support for alternative Central Servers to solve the 200 server registration limit (Ticket #50)
* added support for 64 samples frame size in the server (if server runs in 64 or 128 samples
mode it is still compatible to both, 64 and 128 samples frame size clients)
- added support for 64 samples frame size in the server (if server runs in 64 or 128 samples
mode it is still compatible to both, 64 and 128 samples frame size clients)
* added multichannel CoreAudio support, coded by emlynmac (#44)
- added multichannel CoreAudio support, coded by emlynmac (#44)
* fixed server not visible if in same local network, coded by pljones (Ticket #27)
- fixed server not visible if in same local network, coded by pljones (Ticket #27)
3.4.6 (2020-04-09)

View file

@ -8,16 +8,17 @@ contains(CONFIG, "noupcasename") {
CONFIG += qt \
thread \
debug
CONFIG(debug, debug|release) { CONFIG += qt warn_on rtti exceptions }
release \
lrelease
QT += widgets \
network \
xml
#TRANSLATIONS = src/res/translation_de_DE.ts \
# src/res/translation_fr_FR.ts
TRANSLATIONS = src/res/translation/translation_de_DE.ts \
src/res/translation/translation_fr_FR.ts \
src/res/translation/translation_pt_PT.ts \
src/res/translation/translation_es_ES.ts
INCLUDEPATH += src
@ -127,11 +128,138 @@ win32 {
LIBS += /usr/local/lib/libjack.dylib
}
} else:android {
# we want to compile with C++14
CONFIG += c++14
QT += androidextras
# enabled only for debugging on android devices
DEFINES += ANDROIDDEBUG
target.path = /tmp/your_executable # path on device
INSTALLS += target
HEADERS += android/sound.h
SOURCES += android/sound.cpp
SOURCES += android/sound.cpp \
android/androiddebug.cpp
LIBS += -lOpenSLES
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
OTHER_FILES += android/AndroidManifest.xml
# if compiling for android you need to use Oboe library which is included as a git submodule
# make sure you git pull with submodules to pull the latest Oboe library
OBOE_SOURCES = libs/oboe/src/aaudio/AAudioLoader.cpp \
libs/oboe/src/aaudio/AudioStreamAAudio.cpp \
libs/oboe/src/common/AudioSourceCaller.cpp \
libs/oboe/src/common/AudioStream.cpp \
libs/oboe/src/common/AudioStreamBuilder.cpp \
libs/oboe/src/common/DataConversionFlowGraph.cpp \
libs/oboe/src/common/FilterAudioStream.cpp \
libs/oboe/src/common/FixedBlockAdapter.cpp \
libs/oboe/src/common/FixedBlockReader.cpp \
libs/oboe/src/common/FixedBlockWriter.cpp \
libs/oboe/src/common/LatencyTuner.cpp \
libs/oboe/src/common/QuirksManager.cpp \
libs/oboe/src/common/SourceFloatCaller.cpp \
libs/oboe/src/common/SourceI16Caller.cpp \
libs/oboe/src/common/StabilizedCallback.cpp \
libs/oboe/src/common/Trace.cpp \
libs/oboe/src/common/Utilities.cpp \
libs/oboe/src/common/Version.cpp \
libs/oboe/src/fifo/FifoBuffer.cpp \
libs/oboe/src/fifo/FifoController.cpp \
libs/oboe/src/fifo/FifoControllerBase.cpp \
libs/oboe/src/fifo/FifoControllerIndirect.cpp \
libs/oboe/src/flowgraph/ClipToRange.cpp \
libs/oboe/src/flowgraph/FlowGraphNode.cpp \
libs/oboe/src/flowgraph/ManyToMultiConverter.cpp \
libs/oboe/src/flowgraph/MonoToMultiConverter.cpp \
libs/oboe/src/flowgraph/RampLinear.cpp \
libs/oboe/src/flowgraph/SampleRateConverter.cpp \
libs/oboe/src/flowgraph/SinkFloat.cpp \
libs/oboe/src/flowgraph/SinkI16.cpp \
libs/oboe/src/flowgraph/SinkI24.cpp \
libs/oboe/src/flowgraph/SourceFloat.cpp \
libs/oboe/src/flowgraph/SourceI16.cpp \
libs/oboe/src/flowgraph/SourceI24.cpp \
libs/oboe/src/flowgraph/resampler/IntegerRatio.cpp \
libs/oboe/src/flowgraph/resampler/LinearResampler.cpp \
libs/oboe/src/flowgraph/resampler/MultiChannelResampler.cpp \
libs/oboe/src/flowgraph/resampler/PolyphaseResampler.cpp \
libs/oboe/src/flowgraph/resampler/PolyphaseResamplerMono.cpp \
libs/oboe/src/flowgraph/resampler/PolyphaseResamplerStereo.cpp \
libs/oboe/src/flowgraph/resampler/SincResampler.cpp \
libs/oboe/src/flowgraph/resampler/SincResamplerStereo.cpp \
libs/oboe/src/opensles/AudioInputStreamOpenSLES.cpp \
libs/oboe/src/opensles/AudioOutputStreamOpenSLES.cpp \
libs/oboe/src/opensles/AudioStreamBuffered.cpp \
libs/oboe/src/opensles/AudioStreamOpenSLES.cpp \
libs/oboe/src/opensles/EngineOpenSLES.cpp \
libs/oboe/src/opensles/OpenSLESUtilities.cpp \
libs/oboe/src/opensles/OutputMixerOpenSLES.cpp
OBOE_HEADERS = libs/oboe/src/aaudio/AAudioLoader.h \
libs/oboe/src/aaudio/AudioStreamAAudio.h \
libs/oboe/src/common/AudioClock.h \
libs/oboe/src/common/AudioSourceCaller.h \
libs/oboe/src/common/DataConversionFlowGraph.h \
libs/oboe/src/common/FilterAudioStream.h \
libs/oboe/src/common/FixedBlockAdapter.h \
libs/oboe/src/common/FixedBlockReader.h \
libs/oboe/src/common/FixedBlockWriter.h \
libs/oboe/src/common/MonotonicCounter.h \
libs/oboe/src/common/OboeDebug.h \
libs/oboe/src/common/QuirksManager.h \
libs/oboe/src/common/SourceFloatCaller.h \
libs/oboe/src/common/SourceI16Caller.h \
libs/oboe/src/common/Trace.h \
libs/oboe/src/fifo/FifoBuffer.h \
libs/oboe/src/fifo/FifoController.h \
libs/oboe/src/fifo/FifoControllerBase.h \
libs/oboe/src/fifo/FifoControllerIndirect.h \
libs/oboe/src/flowgraph/ClipToRange.h \
libs/oboe/src/flowgraph/FlowGraphNode.h \
libs/oboe/src/flowgraph/ManyToMultiConverter.h \
libs/oboe/src/flowgraph/MonoToMultiConverter.h \
libs/oboe/src/flowgraph/RampLinear.h \
libs/oboe/src/flowgraph/SampleRateConverter.h \
libs/oboe/src/flowgraph/SinkFloat.h \
libs/oboe/src/flowgraph/SinkI16.h \
libs/oboe/src/flowgraph/SinkI24.h \
libs/oboe/src/flowgraph/SourceFloat.h \
libs/oboe/src/flowgraph/SourceI16.h \
libs/oboe/src/flowgraph/SourceI24.h \
libs/oboe/src/flowgraph/resampler/HyperbolicCosineWindow.h \
libs/oboe/src/flowgraph/resampler/IntegerRatio.h \
libs/oboe/src/flowgraph/resampler/LinearResampler.h \
libs/oboe/src/flowgraph/resampler/MultiChannelResampler.h \
libs/oboe/src/flowgraph/resampler/PolyphaseResampler.h \
libs/oboe/src/flowgraph/resampler/PolyphaseResamplerMono.h \
libs/oboe/src/flowgraph/resampler/PolyphaseResamplerStereo.h \
libs/oboe/src/flowgraph/resampler/SincResampler.h \
libs/oboe/src/flowgraph/resampler/SincResamplerStereo.h \
libs/oboe/src/opensles/AudioInputStreamOpenSLES.h \
libs/oboe/src/opensles/AudioOutputStreamOpenSLES.h \
libs/oboe/src/opensles/AudioStreamBuffered.h \
libs/oboe/src/opensles/AudioStreamOpenSLES.h \
libs/oboe/src/opensles/EngineOpenSLES.h \
libs/oboe/src/opensles/OpenSLESUtilities.h \
libs/oboe/src/opensles/OutputMixerOpenSLES.h
INCLUDEPATH_OBOE = libs/oboe/include/ \
libs/oboe/src/
DISTFILES_OBOE += libs/oboe/AUTHORS \
libs/oboe/CONTRIBUTING \
libs/oboe/LICENSE \
libs/oboe/README
INCLUDEPATH += $$INCLUDEPATH_OBOE
HEADERS += $$OBOE_HEADERS
SOURCES += $$OBOE_SOURCES
DISTFILES += $$DISTFILES_OBOE
} else:unix {
# we want to compile with C++11
QMAKE_CXXFLAGS += -std=c++11
@ -147,8 +275,14 @@ win32 {
nosoundoption = $$find(CONFIG, "nosound")
count(nosoundoption, 0) {
message(Jack Audio Interface Enabled.)
CONFIG += link_pkgconfig
PKGCONFIG += jack
contains(CONFIG, "raspijamulus") {
message(Using Jack Audio in raspijamulus.sh mode.)
LIBS += -ljack
} else {
CONFIG += link_pkgconfig
PKGCONFIG += jack
}
HEADERS += linux/sound.h
SOURCES += linux/sound.cpp
@ -203,7 +337,8 @@ HEADERS += src/audiomixerboard.h \
src/recorder/jamrecorder.h \
src/recorder/creaperproject.h \
src/recorder/cwavestream.h \
src/historygraph.h
src/historygraph.h \
src/signalhandler.h
HEADERS_OPUS = libs/opus/celt/arch.h \
libs/opus/celt/bands.h \
@ -317,6 +452,7 @@ SOURCES += src/audiomixerboard.cpp \
src/serverlist.cpp \
src/serverlogging.cpp \
src/settings.cpp \
src/signalhandler.cpp \
src/socket.cpp \
src/soundbase.cpp \
src/util.cpp \
@ -485,6 +621,12 @@ DISTFILES += ChangeLog \
COPYING \
INSTALL.md \
README.md \
android/build.gradle \
android/gradle/wrapper/gradle-wrapper.jar \
android/gradle/wrapper/gradle-wrapper.properties \
android/gradlew \
android/gradlew.bat \
android/res/values/libs.xml \
src/res/CLEDBlack.png \
src/res/CLEDBlackSmall.png \
src/res/CLEDDisabledSmall.png \
@ -512,7 +654,6 @@ DISTFILES += ChangeLog \
src/res/ledbuttonnotpressed.png \
src/res/ledbuttonpressed.png \
src/res/fronticon.png \
src/res/logopicture.png \
src/res/mainicon.png \
src/res/mixerboardbackground.png \
src/res/VLEDBlack.png \
@ -566,6 +707,17 @@ DISTFILES += ChangeLog \
src/res/instruments/instrkeyboardvocal.png \
src/res/instruments/bodhran.svg \
src/res/instruments/bodhran.png \
src/res/instruments/bassoon.svg \
src/res/instruments/bassoon.png \
src/res/instruments/oboe.svg \
src/res/instruments/oboe.png \
src/res/instruments/harp.svg \
src/res/instruments/harp.png \
src/res/instruments/viola.png \
src/res/instruments/congas.svg \
src/res/instruments/congas.png \
src/res/instruments/bongo.svg \
src/res/instruments/bongo.png \
src/res/flags/flagnone.png \
src/res/flags/ad.png \
src/res/flags/ae.png \

View file

@ -1,41 +1,94 @@
<?xml version='1.0' encoding='utf-8'?>
<manifest android:versionName="1.0" android:installLocation="auto" package="org.qtproject.jamulus" android:versionCode="1" xmlns:android="http://schemas.android.com/apk/res/android">
<application android:label="Jamulus" android:name="org.qtproject.qt5.android.bindings.QtApplication">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:label="@string/app_name" android:screenOrientation="landscape" android:name="org.qtproject.qt5.android.bindings.QtActivity">
<?xml version="1.0"?>
<manifest package="org.qtproject.jamulus" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/>
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
<uses-feature android:name="android.hardware.microphone" android:required="true"/>
<uses-feature android:name="android.hardware.audio.output" android:required="true"/>
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="Jamulus" android:extractNativeLibs="true">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="@string/app_name" android:screenOrientation="landscape" android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:value="Jamulus" android:name="android.app.lib_name"/>
<meta-data android:resource="@array/qt_sources" android:name="android.app.qt_sources_resource_id"/>
<meta-data android:value="default" android:name="android.app.repository"/>
<meta-data android:resource="@array/qt_libs" android:name="android.app.qt_libs_resource_id"/>
<meta-data android:resource="@array/bundled_libs" android:name="android.app.bundled_libs_resource_id"/>
<!-- Application arguments -->
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="Jamulus"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --" android:name="android.app.bundle_local_qt_libs"/>
<meta-data android:resource="@array/bundled_in_lib" android:name="android.app.bundled_in_lib_resource_id"/>
<meta-data android:resource="@array/bundled_in_assets" android:name="android.app.bundled_in_assets_resource_id"/>
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<!-- Run with local libs -->
<meta-data android:value="-- %%USE_LOCAL_QT_LIBS%% --" android:name="android.app.use_local_qt_libs"/>
<meta-data android:value="/data/local/tmp/qt/" android:name="android.app.libs_prefix"/>
<meta-data android:value="-- %%INSERT_LOCAL_LIBS%% --" android:name="android.app.load_local_libs"/>
<meta-data android:value="-- %%INSERT_LOCAL_JARS%% --" android:name="android.app.load_local_jars"/>
<meta-data android:value="-- %%INSERT_INIT_CLASSES%% --" android:name="android.app.static_init_classes"/>
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Used to specify custom system library path to run with local system libs -->
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
<!-- Messages maps -->
<!-- Splash screen -->
<meta-data android:resource="@layout/splash" android:name="android.app.splash_screen"/>
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
are done populating your window with content. -->
<!-- meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" / -->
<!-- meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" / -->
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
<!-- Splash screen -->
</activity>
<!-- Background running -->
<!-- Warning: changing this value to true may cause unexpected crashes if the
application still try to draw after
"applicationStateChanged(Qt::ApplicationSuspended)"
signal is sent! -->
<meta-data android:name="android.app.background_running" android:value="false"/>
<!-- Background running -->
<!-- auto screen scale factor -->
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<!-- auto screen scale factor -->
<!-- extract android style -->
<!-- available android:values :
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
* full - useful QWidget & Quick Controls 1 apps
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
* none - useful for apps that don't use any of the above Qt modules
-->
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
<!-- extract android style -->
</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
</application>
<uses-sdk android:targetSdkVersion="19" android:minSdkVersion="17"/>
<supports-screens android:normalScreens="true" android:smallScreens="true" android:largeScreens="true" android:anyDensity="true"/>
<!-- %%INSERT_PERMISSIONS -->
<!-- %%INSERT_FEATURES -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

45
android/androiddebug.cpp Normal file
View file

@ -0,0 +1,45 @@
const char*const applicationName="Jamulus";
#ifdef ANDROIDDEBUG // Set in my myapp.pro file for android builds
#include <android/log.h>
#include <QString>
#include <QEvent>
#include <QDebug>
#include <stdio.h>
#include <math.h>
#include <string>
void myMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
{
QString report=msg;
if (context.file && !QString(context.file).isEmpty()) {
report+=" in file ";
report+=QString(context.file);
report+=" line ";
report+=QString::number(context.line);
}
if (context.function && !QString(context.function).isEmpty()) {
report+=+" function ";
report+=QString(context.function);
}
const char*const local=report.toLocal8Bit().constData();
switch (type) {
case QtDebugMsg:
__android_log_write(ANDROID_LOG_DEBUG,applicationName,local);
break;
case QtInfoMsg:
__android_log_write(ANDROID_LOG_INFO,applicationName,local);
break;
case QtWarningMsg:
__android_log_write(ANDROID_LOG_WARN,applicationName,local);
break;
case QtCriticalMsg:
__android_log_write(ANDROID_LOG_ERROR,applicationName,local);
break;
case QtFatalMsg:
default:
__android_log_write(ANDROID_LOG_FATAL,applicationName,local);
abort();
}
}
#endif

View file

@ -2,7 +2,7 @@
* Copyright (c) 2004-2020
*
* Author(s):
* Volker Fischer
* Simon Tomlinson
*
******************************************************************************
*
@ -23,255 +23,157 @@
\******************************************************************************/
#include "sound.h"
#include "androiddebug.cpp"
/* Implementation *************************************************************/
CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect ) :
CSoundBase ( "OpenSL", true, fpNewProcessCallback, arg, iCtrlMIDIChannel, bNoAutoJackConnect )
CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool ,
const QString& ) :
CSoundBase ( "OpenSL", true, fpNewProcessCallback, arg, iCtrlMIDIChannel )
{
pSound = this;
#ifdef ANDROIDDEBUG
qInstallMessageHandler(myMessageHandler);
#endif
}
void CSound::setupCommonStreamParams(oboe::AudioStreamBuilder *builder)
{
// We request EXCLUSIVE mode since this will give us the lowest possible
// latency. If EXCLUSIVE mode isn't available the builder will fall back to SHARED mode
builder->setCallback(this)
->setFormat(oboe::AudioFormat::Float)
->setSharingMode(oboe::SharingMode::Shared)
->setChannelCount(oboe::ChannelCount::Mono)
// ->setSampleRate(48000)
// ->setSampleRateConversionQuality(oboe::SampleRateConversionQuality::Medium)
->setPerformanceMode(oboe::PerformanceMode::None);
return;
}
void CSound::openStreams()
{
// Create callback
mCallback = this;
//Setup output stream
oboe::AudioStreamBuilder inBuilder, outBuilder;
outBuilder.setDirection(oboe::Direction::Output);
setupCommonStreamParams(&outBuilder);
oboe::Result result = outBuilder.openManagedStream(mPlayStream);
if (result != oboe::Result::OK) {
return;
}
mPlayStream->setBufferSizeInFrames(pSound->iOpenSLBufferSizeStereo);
warnIfNotLowLatency(mPlayStream, "PlayStream");
printStreamDetails(mPlayStream);
//Setup input stream
inBuilder.setDirection(oboe::Direction::Input);
setupCommonStreamParams(&inBuilder);
result = inBuilder.openManagedStream(mRecordingStream);
if (result != oboe::Result::OK) {
closeStream(mPlayStream);
return;
}
mRecordingStream->setBufferSizeInFrames(pSound->iOpenSLBufferSizeStereo);
warnIfNotLowLatency(mRecordingStream, "RecordStream");
printStreamDetails(mRecordingStream);
}
void CSound::printStreamDetails(oboe::ManagedStream &stream)
{
QString sDirection = (stream->getDirection()==oboe::Direction::Input?"Input":"Output");
QString sFramesPerBurst = QString::number(stream->getFramesPerBurst());
QString sBufferSizeInFrames = QString::number(stream->getBufferSizeInFrames());
QString sBytesPerFrame = QString::number(stream->getBytesPerFrame());
QString sBytesPerSample = QString::number(stream->getBytesPerSample());
QString sBufferCapacityInFrames = QString::number(stream->getBufferCapacityInFrames());
QString sPerformanceMode = (stream->getPerformanceMode()==oboe::PerformanceMode::LowLatency?"LowLatency":"NotLowLatency");
QString sSharingMode = (stream->getSharingMode() == oboe::SharingMode::Exclusive?"Exclusive":"Shared");
QString sDeviceID = QString::number(stream->getDeviceId());
QString sSampleRate = QString::number(stream->getSampleRate());
QString sAudioFormat = (stream->getFormat()==oboe::AudioFormat::I16?"I16":"Float");
QString sFramesPerCallback = QString::number(stream->getFramesPerCallback());
//QString sSampleRateConversionQuality = (stream.getSampleRateConversionQuality()==oboe::SampleRateConversionQuality::
qInfo() << "Stream details: [sDirection: " << sDirection <<
", FramesPerBurst: " << sFramesPerBurst <<
", BufferSizeInFrames: " << sBufferSizeInFrames <<
", BytesPerFrame: " << sBytesPerFrame <<
", BytesPerSample: " << sBytesPerSample <<
", BufferCapacityInFrames: " << sBufferCapacityInFrames <<
", PerformanceMode: " << sPerformanceMode <<
", SharingMode: " << sSharingMode <<
", DeviceID: " << sDeviceID <<
", SampleRate: " << sSampleRate <<
", AudioFormat: " << sAudioFormat <<
", FramesPerCallback: " << sFramesPerCallback << "]";
}
void CSound::InitializeOpenSL()
{
// set up stream formats for input and output
SLDataFormat_PCM inStreamFormat;
inStreamFormat.formatType = SL_DATAFORMAT_PCM;
inStreamFormat.numChannels = 1;
inStreamFormat.samplesPerSec = SL_SAMPLINGRATE_16;
inStreamFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
inStreamFormat.containerSize = 16;
inStreamFormat.channelMask = SL_SPEAKER_FRONT_CENTER;
inStreamFormat.endianness = SL_BYTEORDER_LITTLEENDIAN;
SLDataFormat_PCM outStreamFormat;
outStreamFormat.formatType = SL_DATAFORMAT_PCM;
outStreamFormat.numChannels = 2;
outStreamFormat.samplesPerSec = SYSTEM_SAMPLE_RATE_HZ * 1000; // unit is mHz
outStreamFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
outStreamFormat.containerSize = 16;
outStreamFormat.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
outStreamFormat.endianness = SL_BYTEORDER_LITTLEENDIAN;
// create the OpenSL root engine object
slCreateEngine ( &engineObject,
0,
nullptr,
0,
nullptr,
nullptr );
// realize the engine
(*engineObject)->Realize ( engineObject,
SL_BOOLEAN_FALSE );
// get the engine interface (required to create other objects)
(*engineObject)->GetInterface ( engineObject,
SL_IID_ENGINE,
&engine );
// create the main output mix
(*engine)->CreateOutputMix ( engine,
&outputMixObject,
0,
nullptr,
nullptr );
// realize the output mix
(*outputMixObject)->Realize ( outputMixObject,
SL_BOOLEAN_FALSE );
// configure the audio (data) source for input
SLDataLocator_IODevice micLocator;
micLocator.locatorType = SL_DATALOCATOR_IODEVICE;
micLocator.deviceType = SL_IODEVICE_AUDIOINPUT;
micLocator.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
micLocator.device = nullptr;
SLDataSource inDataSource;
inDataSource.pLocator = &micLocator;
inDataSource.pFormat = nullptr;
// configure the input buffer queue
SLDataLocator_AndroidSimpleBufferQueue inBufferQueue;
inBufferQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
inBufferQueue.numBuffers = 2; // max number of buffers in queue
// configure the audio (data) sink for input
SLDataSink inDataSink;
inDataSink.pLocator = &inBufferQueue;
inDataSink.pFormat = &inStreamFormat;
// create the audio recorder
const SLInterfaceID recorderIds[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
const SLboolean recorderReq[] = { SL_BOOLEAN_TRUE };
(*engine)->CreateAudioRecorder ( engine,
&recorderObject,
&inDataSource,
&inDataSink,
1,
recorderIds,
recorderReq );
// realize the audio recorder
(*recorderObject)->Realize ( recorderObject,
SL_BOOLEAN_FALSE );
// get the audio recorder interface
(*recorderObject)->GetInterface ( recorderObject,
SL_IID_RECORD,
&recorder );
// get the audio recorder simple buffer queue interface
(*recorderObject)->GetInterface ( recorderObject,
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&recorderSimpleBufQueue );
// register the audio input callback
(*recorderSimpleBufQueue)->RegisterCallback ( recorderSimpleBufQueue,
processInput,
this );
// configure the output buffer queue
SLDataLocator_AndroidSimpleBufferQueue outBufferQueue;
outBufferQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
outBufferQueue.numBuffers = 2; // max number of buffers in queue
// configure the audio (data) source for output
SLDataSource outDataSource;
outDataSource.pLocator = &outBufferQueue;
outDataSource.pFormat = &outStreamFormat;
// configure the output mix
SLDataLocator_OutputMix outputMix;
outputMix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
outputMix.outputMix = outputMixObject;
// configure the audio (data) sink for output
SLDataSink outDataSink;
outDataSink.pLocator = &outputMix;
outDataSink.pFormat = nullptr;
// create the audio player
const SLInterfaceID playerIds[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
const SLboolean playerReq[] = { SL_BOOLEAN_TRUE };
(*engine)->CreateAudioPlayer ( engine,
&playerObject,
&outDataSource,
&outDataSink,
1,
playerIds,
playerReq );
// realize the audio player
(*playerObject)->Realize ( playerObject,
SL_BOOLEAN_FALSE );
// get the audio player interface
(*playerObject)->GetInterface ( playerObject,
SL_IID_PLAY,
&player );
// get the audio player simple buffer queue interface
(*playerObject)->GetInterface ( playerObject,
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&playerSimpleBufQueue );
// register the audio output callback
(*playerSimpleBufQueue)->RegisterCallback ( playerSimpleBufQueue,
processOutput,
this );
void CSound::warnIfNotLowLatency(oboe::ManagedStream &stream, QString streamName) {
if (stream->getPerformanceMode() != oboe::PerformanceMode::LowLatency) {
QString latencyMode = (stream->getPerformanceMode()==oboe::PerformanceMode::None ? "None" : "Power Saving");
// throw CGenErr ( tr ( "Stream is NOT low latency."
// "Check your requested format, sample rate and channel count." ) );
}
}
void CSound::CloseOpenSL()
void CSound::closeStream(oboe::ManagedStream &stream)
{
if (stream) {
oboe::Result requestStopRes = stream->requestStop();
oboe::Result result = stream->close();
if (result != oboe::Result::OK) {
throw CGenErr ( tr ( "Error closing stream: $s",
oboe::convertToText(result) ) );
}
stream.reset();
}
}
void CSound::closeStreams()
{
// clean up
(*recorderObject)->Destroy ( recorderObject );
(*playerObject)->Destroy ( playerObject );
(*outputMixObject)->Destroy ( outputMixObject );
(*engineObject)->Destroy ( engineObject );
closeStream(mRecordingStream);
closeStream(mPlayStream);
}
void CSound::Start()
{
InitializeOpenSL();
// TEST We have to supply the interface with initial buffers, otherwise
// the rendering will not start.
// Note that the number of buffers enqueued here must match the maximum
// numbers of buffers configured in the constructor of this class.
vecsTmpAudioSndCrdStereo.Reset ( 0 );
// enqueue initial buffers for record
(*recorderSimpleBufQueue)->Enqueue ( recorderSimpleBufQueue,
&vecsTmpAudioSndCrdStereo[0],
iOpenSLBufferSizeStereo * 2 /* 2 bytes */ );
(*recorderSimpleBufQueue)->Enqueue ( recorderSimpleBufQueue,
&vecsTmpAudioSndCrdStereo[0],
iOpenSLBufferSizeStereo * 2 /* 2 bytes */ );
// enqueue initial buffers for playback
(*playerSimpleBufQueue)->Enqueue ( playerSimpleBufQueue,
&vecsTmpAudioSndCrdStereo[0],
iOpenSLBufferSizeStereo * 2 /* 2 bytes */ );
(*playerSimpleBufQueue)->Enqueue ( playerSimpleBufQueue,
&vecsTmpAudioSndCrdStereo[0],
iOpenSLBufferSizeStereo * 2 /* 2 bytes */ );
// start the rendering
(*recorder)->SetRecordState ( recorder, SL_RECORDSTATE_RECORDING );
(*player)->SetPlayState ( player, SL_PLAYSTATE_PLAYING );
openStreams();
// call base class
CSoundBase::Start();
// finally start the streams so the callback begins, start with inputstream first.
mRecordingStream->requestStart();
mPlayStream->requestStart();
}
void CSound::Stop()
{
// stop the audio stream
(*recorder)->SetRecordState ( recorder, SL_RECORDSTATE_STOPPED );
(*player)->SetPlayState ( player, SL_PLAYSTATE_STOPPED );
// clear the buffers
(*recorderSimpleBufQueue)->Clear ( recorderSimpleBufQueue );
(*playerSimpleBufQueue)->Clear ( playerSimpleBufQueue );
closeStreams();
// call base class
CSoundBase::Stop();
CloseOpenSL();
}
int CSound::Init ( const int iNewPrefMonoBufferSize )
{
// TODO make use of the following:
// String sampleRate = am.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE));
// String framesPerBuffer = am.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER));
/*
// get the Audio IO DEVICE CAPABILITIES interface
SLAudioIODeviceCapabilitiesItf audioCapabilities;
(*engineObject)->GetInterface ( engineObject,
SL_IID_AUDIOIODEVICECAPABILITIES,
&audioCapabilities );
(*audioCapabilities)->QueryAudioInputCapabilities ( audioCapabilities,
inputDeviceIDs[i],
&audioInputDescriptor );
*/
// store buffer size
iOpenSLBufferSizeMono = iNewPrefMonoBufferSize;
iOpenSLBufferSizeMono = 512 ;
//iNewPrefMonoBufferSize;
// init base class
CSoundBase::Init ( iOpenSLBufferSizeMono );
@ -282,7 +184,6 @@ SLAudioIODeviceCapabilitiesItf audioCapabilities;
// create memory for intermediate audio buffer
vecsTmpAudioSndCrdStereo.Init ( iOpenSLBufferSizeStereo );
// TEST
#if ( SYSTEM_SAMPLE_RATE_HZ != 48000 )
# error "Only a system sample rate of 48 kHz is supported by this module"
@ -295,57 +196,105 @@ SLAudioIODeviceCapabilitiesItf audioCapabilities;
iModifiedInBufSize = iOpenSLBufferSizeMono / 3;
vecsTmpAudioInSndCrd.Init ( iModifiedInBufSize );
return iOpenSLBufferSizeMono;
}
void CSound::processInput ( SLAndroidSimpleBufferQueueItf bufferQueue,
void* instance )
// This is the main callback method for when an audio stream is ready to publish data to an output stream
// or has received data on an input stream. As per manual much be very careful not to do anything in this back that
// can cause delays such as sleeping, file processing, allocate memory, etc
oboe::DataCallbackResult CSound::onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames)
{
CSound* pSound = static_cast<CSound*> ( instance );
// only process if we are running
if ( ! pSound->bRun )
{
return oboe::DataCallbackResult::Continue;
}
// only process if we are running
if ( !pSound->bRun )
// Need to modify the size of the buffer based on the numFrames requested in this callback.
// Buffer size can change regularly by android devices
int& iBufferSizeMono = pSound->iOpenSLBufferSizeMono;
// perform the processing for input and output
// QMutexLocker locker ( &pSound->Mutex );
// locker.mutex();
//This can be called from both input and output at different times
if (oboeStream == pSound->mPlayStream.get() && audioData)
{
return;
float *floatData = static_cast<float *>(audioData);
// Zero out the incoming container array
memset(audioData, 0, sizeof(float) * numFrames * oboeStream->getChannelCount());
// Only copy data if we have data to copy, otherwise fill with silence
if (!pSound->vecsTmpAudioSndCrdStereo.empty())
{
for (int frmNum = 0; frmNum < numFrames; ++frmNum)
{
for (int channelNum = 0; channelNum < oboeStream->getChannelCount(); channelNum++)
{
// copy sample received from server into output buffer
// convert to 32 bit
const int32_t iCurSam = static_cast<int32_t> (
pSound->vecsTmpAudioSndCrdStereo [frmNum * oboeStream->getChannelCount() + channelNum] );
floatData[frmNum * oboeStream->getChannelCount() + channelNum] = (float) iCurSam/ _MAXSHORT;
}
}
}
else
{
// prime output stream buffer with silence
memset(static_cast<float*>(audioData) + numFrames * oboeStream->getChannelCount(), 0,
(numFrames) * oboeStream->getBytesPerFrame());
}
}
QMutexLocker locker ( &pSound->Mutex );
// enqueue the buffer for record
(*bufferQueue)->Enqueue ( bufferQueue,
&pSound->vecsTmpAudioInSndCrd[0],
pSound->iModifiedInBufSize * 2 /* 2 bytes */ );
// upsampling (without filtering) and channel management
pSound->vecsTmpAudioSndCrdStereo.Reset ( 0 );
for ( int i = 0; i < pSound->iModifiedInBufSize; i++ )
{
pSound->vecsTmpAudioSndCrdStereo[6 * i] =
pSound->vecsTmpAudioSndCrdStereo[6 * i + 1] =
pSound->vecsTmpAudioInSndCrd[i];
}
}
void CSound::processOutput ( SLAndroidSimpleBufferQueueItf bufferQueue,
void* instance )
{
CSound* pSound = static_cast<CSound*> ( instance );
// only process if we are running
if ( !pSound->bRun )
else if (oboeStream == pSound->mRecordingStream.get() && audioData)
{
return;
// First things first, we need to discard the input queue a little for 500ms or so
if (pSound->mCountCallbacksToDrain > 0)
{
// discard the input buffer
int32_t numBytes = numFrames * oboeStream->getBytesPerFrame();
memset(audioData, 0 /* value */, numBytes);
pSound->mCountCallbacksToDrain--;
}
// We're good to start recording now
// Take the data from the recording device ouput buffer and move
// it to the vector ready to send up to the server
float *floatData = static_cast<float *>(audioData);
// Copy recording data to internal vector
for (int frmNum = 0; frmNum < numFrames; ++frmNum)
{
for (int channelNum = 0; channelNum < oboeStream->getChannelCount(); channelNum++)
{
pSound->vecsTmpAudioSndCrdStereo [frmNum * oboeStream->getChannelCount() + channelNum] =
(short) floatData[frmNum * oboeStream->getChannelCount() + channelNum] * _MAXSHORT;
}
}
// Tell parent class that we've put some data ready to send to the server
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
}
QMutexLocker locker ( &pSound->Mutex );
// call processing callback function
pSound->ProcessCallback ( pSound->vecsTmpAudioSndCrdStereo );
// enqueue the buffer for playback
(*bufferQueue)->Enqueue ( bufferQueue,
&pSound->vecsTmpAudioSndCrdStereo[0],
pSound->iOpenSLBufferSizeStereo * 2 /* 2 bytes */ );
// locker.unlock();
return oboe::DataCallbackResult::Continue;
}
//TODO better handling of stream closing errors
void CSound::onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result result)
{
qDebug() << "CSound::onErrorAfterClose";
}
//TODO better handling of stream closing errors
void CSound::onErrorBeforeClose(oboe::AudioStream *oboeStream, oboe::Result result)
{
qDebug() << "CSound::onErrorBeforeClose";
}

View file

@ -2,7 +2,7 @@
* Copyright (c) 2004-2020
*
* Author(s):
* Volker Fischer
* Simon Tomlinson
*
******************************************************************************
*
@ -24,31 +24,55 @@
#pragma once
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
/* Deprecated, moving to OBOE
* #include <SLES/OpenSLES.h>
* #include <SLES/OpenSLES_Android.h> */
#include <oboe/Oboe.h>
#include <QMutex>
#include "soundbase.h"
#include "global.h"
#include <QDebug>
#include <android/log.h>
/* Classes ********************************************************************/
class CSound : public CSoundBase
class CSound : public CSoundBase, public oboe::AudioStreamCallback//, public IRenderableAudio, public IRestartable
{
public:
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect );
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool ,
const QString& );
virtual ~CSound() {}
virtual int Init ( const int iNewPrefMonoBufferSize );
virtual void Start();
virtual void Stop();
// Call backs for Oboe
virtual oboe::DataCallbackResult onAudioReady(oboe::AudioStream *oboeStream, void *audioData, int32_t numFrames);
virtual void onErrorAfterClose(oboe::AudioStream *oboeStream, oboe::Result result);
virtual void onErrorBeforeClose(oboe::AudioStream *oboeStream, oboe::Result result);
// these variables should be protected but cannot since we want
// to access them from the callback function
CVector<short> vecsTmpAudioSndCrdStereo;
static void android_message_handler(QtMsgType type,
const QMessageLogContext &context,
const QString &message)
{
android_LogPriority priority = ANDROID_LOG_DEBUG;
switch (type) {
case QtDebugMsg: priority = ANDROID_LOG_DEBUG; break;
case QtWarningMsg: priority = ANDROID_LOG_WARN; break;
case QtCriticalMsg: priority = ANDROID_LOG_ERROR; break;
case QtFatalMsg: priority = ANDROID_LOG_FATAL; break;
};
__android_log_print(priority, "Qt", "%s", qPrintable(message));
};
// TEST
CVector<short> vecsTmpAudioInSndCrd;
int iModifiedInBufSize;
@ -56,27 +80,25 @@ int iModifiedInBufSize;
int iOpenSLBufferSizeMono;
int iOpenSLBufferSizeStereo;
protected:
private:
void setupCommonStreamParams(oboe::AudioStreamBuilder *builder);
void printStreamDetails(oboe::ManagedStream &stream);
void openStreams();
void closeStreams();
void warnIfNotLowLatency(oboe::ManagedStream &stream, QString streamName);
void closeStream(oboe::ManagedStream &stream);
void InitializeOpenSL();
void CloseOpenSL();
oboe::ManagedStream mRecordingStream;
oboe::ManagedStream mPlayStream;
AudioStreamCallback *mCallback;
// callbacks
static void processInput ( SLAndroidSimpleBufferQueueItf bufferQueue,
void* instance );
// used to reach a state where the input buffer is
// empty and the garbage in the first 500ms or so is discarded
static constexpr int32_t kNumCallbacksToDrain = 10;
int32_t mCountCallbacksToDrain = kNumCallbacksToDrain;
static void processOutput ( SLAndroidSimpleBufferQueueItf bufferQueue,
void* instance );
SLObjectItf engineObject;
SLEngineItf engine;
SLObjectItf recorderObject;
SLRecordItf recorder;
SLAndroidSimpleBufferQueueItf recorderSimpleBufQueue;
SLObjectItf outputMixObject;
SLObjectItf playerObject;
SLPlayItf player;
SLAndroidSimpleBufferQueueItf playerSimpleBufQueue;
// Used to reference this instance of class from within the static callback
CSound *pSound;
QMutex Mutex;

View file

@ -11,6 +11,7 @@ Build-Depends:
make,
qt5-default,
qtdeclarative5-dev,
qttools5-dev-tools,
Standards-Version: 3.9.5
Homepage: http://llcon.sourceforge.net/
Vcs-Git: git://github.com/corrados/jamulus.git

View file

@ -5,6 +5,10 @@
override_dh_auto_configure:
qmake CONFIG+=noupcasename Jamulus.pro
override_dh_auto_build:
cd src/res/translation && lrelease *.ts
dh_auto_build
override_dh_usrlocal:
echo $$(pwd)
mkdir -p $$(pwd)/debian/jamulus/usr/bin/

View file

@ -75,7 +75,7 @@ fi
# compile Jamulus with external Opus library
cd ..
qmake "CONFIG+=opus_shared_lib" "INCLUDEPATH+=distributions/${OPUS}/include" "QMAKE_LIBDIR+=distributions/${OPUS}/.libs" "INCLUDEPATH+=distributions/jack2/common" "QMAKE_LIBDIR+=distributions/jack2/build/common" Jamulus.pro
qmake "CONFIG+=opus_shared_lib" "CONFIG+=raspijamulus" "INCLUDEPATH+=distributions/${OPUS}/include" "QMAKE_LIBDIR+=distributions/${OPUS}/.libs" "INCLUDEPATH+=distributions/jack2/common" "QMAKE_LIBDIR+=distributions/jack2/build/common" Jamulus.pro
make -j${NCORES}
# get first USB audio sound card device

1
libs/oboe Submodule

@ -0,0 +1 @@
Subproject commit 55d878a4e85e1994f2b5883366079b991500a25f

View file

@ -27,14 +27,15 @@
#include "sound.h"
#ifdef WITH_SOUND
void CSound::OpenJack()
void CSound::OpenJack ( const bool bNoAutoJackConnect,
const char* jackClientName )
{
jack_status_t JackStatus;
// try to become a client of the JACK server
pJackClient = jack_client_open ( APP_NAME, JackNullOption, &JackStatus );
pJackClient = jack_client_open ( jackClientName, JackNullOption, &JackStatus );
if ( pJackClient == NULL )
if ( pJackClient == nullptr )
{
throw CGenErr ( tr ( "The Jack server is not running. This software "
"requires a Jack server to run. Normally if the Jack server is "
@ -57,13 +58,13 @@ void CSound::OpenJack()
if ( jack_get_sample_rate ( pJackClient ) != SYSTEM_SAMPLE_RATE_HZ )
{
throw CGenErr ( tr ( "The Jack server sample rate is different from "
"the required one. The required sample rate is: <b>" ) +
QString().setNum ( SYSTEM_SAMPLE_RATE_HZ ) + tr ( " Hz</b>. You can "
"the required one. The required sample rate is:" ) + " <b>" +
QString().setNum ( SYSTEM_SAMPLE_RATE_HZ ) + " Hz</b>. " + tr ( "You can "
"use a tool like <i><a href=""http://qjackctl.sourceforge.net"">QJackCtl</a></i> "
"to adjust the Jack server sample rate.<br>Make sure to set the "
"<b>Frames/Period</b> to a low value like <b>" ) +
"to adjust the Jack server sample rate." ) + "<br>" + tr ( "Make sure to set the "
"Frames/Period to a low value like " ) +
QString().setNum ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) +
tr ( "</b> to achieve a low delay." ) );
tr ( " to achieve a low delay." ) );
}
// create four ports (two for input, two for output -> stereo)
@ -79,10 +80,10 @@ void CSound::OpenJack()
output_port_right = jack_port_register ( pJackClient, "output right",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0 );
if ( ( input_port_left == NULL ) ||
( input_port_right == NULL ) ||
( output_port_left == NULL ) ||
( output_port_right == NULL ) )
if ( ( input_port_left == nullptr ) ||
( input_port_right == nullptr ) ||
( output_port_left == nullptr ) ||
( output_port_right == nullptr ) )
{
throw CGenErr ( tr ( "The Jack port registering failed." ) );
}
@ -93,14 +94,14 @@ void CSound::OpenJack()
input_port_midi = jack_port_register ( pJackClient, "input midi",
JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0 );
if ( input_port_midi == NULL )
if ( input_port_midi == nullptr )
{
throw CGenErr ( tr ( "The Jack port registering failed." ) );
}
}
else
{
input_port_midi = NULL;
input_port_midi = nullptr;
}
// tell the JACK server that we are ready to roll
@ -119,23 +120,16 @@ void CSound::OpenJack()
// try to connect physical input ports
if ( ( ports = jack_get_ports ( pJackClient,
NULL,
NULL,
JackPortIsPhysical | JackPortIsOutput ) ) != NULL )
nullptr,
nullptr,
JackPortIsPhysical | JackPortIsOutput ) ) != nullptr )
{
if ( jack_connect ( pJackClient, ports[0], jack_port_name ( input_port_left ) ) )
{
throw CGenErr ( tr ( "Cannot connect the Jack input ports" ) );
}
jack_connect ( pJackClient, ports[0], jack_port_name ( input_port_left ) );
// before connecting the second stereo channel, check if the input is not
// mono
// before connecting the second stereo channel, check if the input is not mono
if ( ports[1] )
{
if ( jack_connect ( pJackClient, ports[1], jack_port_name ( input_port_right ) ) )
{
throw CGenErr ( tr ( "Cannot connect the Jack input ports" ) );
}
jack_connect ( pJackClient, ports[1], jack_port_name ( input_port_right ) );
}
jack_free ( ports );
@ -143,23 +137,16 @@ void CSound::OpenJack()
// try to connect physical output ports
if ( ( ports = jack_get_ports ( pJackClient,
NULL,
NULL,
JackPortIsPhysical | JackPortIsInput ) ) != NULL )
nullptr,
nullptr,
JackPortIsPhysical | JackPortIsInput ) ) != nullptr )
{
if ( jack_connect ( pJackClient, jack_port_name ( output_port_left ), ports[0] ) )
{
throw CGenErr ( tr ( "Cannot connect the Jack output ports." ) );
}
jack_connect ( pJackClient, jack_port_name ( output_port_left ), ports[0] );
// before connecting the second stereo channel, check if the output is not
// mono
// before connecting the second stereo channel, check if the output is not mono
if ( ports[1] )
{
if ( jack_connect ( pJackClient, jack_port_name ( output_port_right ), ports[1] ) )
{
throw CGenErr ( tr ( "Cannot connect the Jack output ports." ) );
}
jack_connect ( pJackClient, jack_port_name ( output_port_right ), ports[1] );
}
jack_free ( ports );
@ -197,16 +184,20 @@ void CSound::Stop()
int CSound::Init ( const int /* iNewPrefMonoBufferSize */ )
{
// try setting buffer size
// TODO seems not to work! -> no audio after this operation!
// Doesn't this give an infinite loop? The set buffer size function will call our
// registerd callback which calls "EmitReinitRequestSignal()". In that function
// this CSound::Init() function is called...
//jack_set_buffer_size ( pJackClient, iNewPrefMonoBufferSize );
// without a Jack server, Jamulus makes no sense to run, throw an error message
if ( bJackWasShutDown )
{
throw CGenErr ( tr ( "The Jack server was shut down. This software "
"requires a Jack server to run. Try to restart the software to "
"solve the issue." ) );
}
// get actual buffer size
iJACKBufferSizeMono = jack_get_buffer_size ( pJackClient );
@ -230,7 +221,7 @@ int CSound::process ( jack_nframes_t nframes, void* arg )
CSound* pSound = static_cast<CSound*> ( arg );
int i;
if ( pSound->IsRunning() )
if ( pSound->IsRunning() && ( nframes == static_cast<jack_nframes_t> ( pSound->iJACKBufferSizeMono ) ) )
{
// get input data pointer
jack_default_audio_sample_t* in_left =
@ -242,7 +233,7 @@ int CSound::process ( jack_nframes_t nframes, void* arg )
pSound->input_port_right, nframes );
// copy input audio data
if ( in_left != 0 && in_right != 0 )
if ( ( in_left != nullptr ) && ( in_right != nullptr ) )
{
for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ )
{
@ -267,7 +258,7 @@ int CSound::process ( jack_nframes_t nframes, void* arg )
pSound->output_port_right, nframes );
// copy output data
if ( out_left != 0 && out_right != 0 )
if ( ( out_left != nullptr ) && ( out_right != nullptr ) )
{
for ( i = 0; i < pSound->iJACKBufferSizeMono; i++ )
{
@ -291,7 +282,7 @@ int CSound::process ( jack_nframes_t nframes, void* arg )
pSound->output_port_right, nframes );
// clear output data
if ( out_left != 0 && out_right != 0 )
if ( ( out_left != nullptr ) && ( out_right != nullptr ) )
{
memset ( out_left,
0,
@ -304,7 +295,7 @@ int CSound::process ( jack_nframes_t nframes, void* arg )
}
// akt on MIDI data if MIDI is enabled
if ( pSound->input_port_midi != NULL )
if ( pSound->input_port_midi != nullptr )
{
void* in_midi = jack_port_get_buffer ( pSound->input_port_midi, nframes );
@ -334,7 +325,7 @@ int CSound::process ( jack_nframes_t nframes, void* arg )
return 0; // zero on success, non-zero on error
}
int CSound::bufferSizeCallback ( jack_nframes_t, void *arg )
int CSound::bufferSizeCallback ( jack_nframes_t, void* arg )
{
CSound* pSound = static_cast<CSound*> ( arg );
@ -343,12 +334,11 @@ int CSound::bufferSizeCallback ( jack_nframes_t, void *arg )
return 0; // zero on success, non-zero on error
}
void CSound::shutdownCallback ( void* )
void CSound::shutdownCallback ( void* arg )
{
// without a Jack server, our software makes no sense to run, throw
// error message
throw CGenErr ( tr ( "The Jack server was shut down. This software "
"requires a Jack server to run. Try to restart the software to "
"solve the issue." ) );
CSound* pSound = static_cast<CSound*> ( arg );
pSound->bJackWasShutDown = true;
pSound->EmitReinitRequestSignal ( RS_ONLY_RESTART_AND_INIT );
}
#endif // WITH_SOUND

View file

@ -60,12 +60,14 @@
class CSound : public CSoundBase
{
public:
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect ) :
CSoundBase ( "Jack", true, fpNewProcessCallback, arg, iCtrlMIDIChannel, bNoAutoJackConnect ), iJACKBufferSizeMono ( 0 ),
iJACKBufferSizeStero ( 0 ) { OpenJack(); }
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect,
const QString& strJackClientName ) :
CSoundBase ( "Jack", true, fpNewProcessCallback, arg, iCtrlMIDIChannel ),
iJACKBufferSizeMono ( 0 ), bJackWasShutDown ( false ) { OpenJack ( bNoAutoJackConnect, strJackClientName.toLocal8Bit().data() ); }
virtual ~CSound() { CloseJack(); }
virtual int Init ( const int iNewPrefMonoBufferSize );
@ -77,6 +79,7 @@ public:
CVector<short> vecsTmpAudioSndCrdStereo;
int iJACKBufferSizeMono;
int iJACKBufferSizeStero;
bool bJackWasShutDown;
jack_port_t* input_port_left;
jack_port_t* input_port_right;
@ -85,7 +88,9 @@ public:
jack_port_t* input_port_midi;
protected:
void OpenJack();
void OpenJack ( const bool bNoAutoJackConnect,
const char* jackClientName );
void CloseJack();
// callbacks
@ -99,11 +104,12 @@ protected:
class CSound : public CSoundBase
{
public:
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* pParg ),
void* pParg,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect ) :
CSoundBase ( "nosound", false, fpNewProcessCallback, pParg, iCtrlMIDIChannel, bNoAutoJackConnect ) {}
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* pParg ),
void* pParg,
const int iCtrlMIDIChannel,
const bool ,
const QString& ) :
CSoundBase ( "nosound", false, fpNewProcessCallback, pParg, iCtrlMIDIChannel ) {}
virtual ~CSound() {}
};
#endif // WITH_SOUND

View file

@ -26,11 +26,12 @@
/* Implementation *************************************************************/
CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect ) :
CSoundBase ( "CoreAudio", true, fpNewProcessCallback, arg, iCtrlMIDIChannel, bNoAutoJackConnect ),
CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool ,
const QString& ) :
CSoundBase ( "CoreAudio", true, fpNewProcessCallback, arg, iCtrlMIDIChannel ),
midiInPortRef ( static_cast<MIDIPortRef> ( NULL ) )
{
// Apple Mailing Lists: Subject: GUI Apps should set kAudioHardwarePropertyRunLoop
@ -166,6 +167,7 @@ CSound::CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, vo
CurrentAudioInputDeviceID = 0;
CurrentAudioOutputDeviceID = 0;
iNumInChan = 0;
iNumInChanPlusAddChan = 0;
iNumOutChan = 0;
iSelInputLeftChannel = 0;
iSelInputRightChannel = 0;
@ -255,47 +257,55 @@ void CSound::GetAudioDeviceInfos ( const AudioDeviceID DeviceID,
}
int CSound::CountChannels ( AudioDeviceID devID,
const int iNumChanPerFrame,
bool isInput )
{
OSStatus err;
UInt32 propSize;
int result = 0;
// check for the case the we have interleaved format, in that case we assume
// that only the very first buffer contains all our channels
if ( iNumChanPerFrame > 1 )
if ( isInput )
{
result = iNumChanPerFrame;
vecNumInBufChan.Init ( 0 );
}
else
{
// it seems we have multiple buffers where each buffer has only one channel,
// in that case we assume that each input channel has its own buffer
AudioObjectPropertyScope theScope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
vecNumOutBufChan.Init ( 0 );
}
AudioObjectPropertyAddress theAddress = { kAudioDevicePropertyStreamConfiguration,
theScope,
0 };
// it seems we have multiple buffers where each buffer has only one channel,
// in that case we assume that each input channel has its own buffer
AudioObjectPropertyScope theScope = isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
AudioObjectGetPropertyDataSize ( devID, &theAddress, 0, NULL, &propSize );
AudioObjectPropertyAddress theAddress = { kAudioDevicePropertyStreamConfiguration,
theScope,
0 };
AudioBufferList *buflist = (AudioBufferList*) malloc ( propSize );
AudioObjectGetPropertyDataSize ( devID, &theAddress, 0, NULL, &propSize );
err = AudioObjectGetPropertyData ( devID, &theAddress, 0, NULL, &propSize, buflist );
AudioBufferList *buflist = (AudioBufferList*) malloc ( propSize );
if ( !err )
err = AudioObjectGetPropertyData ( devID, &theAddress, 0, NULL, &propSize, buflist );
if ( !err )
{
for ( UInt32 i = 0; i < buflist->mNumberBuffers; ++i )
{
for ( UInt32 i = 0; i < buflist->mNumberBuffers; ++i )
// The correct value mNumberChannels for an AudioBuffer can be derived from the mChannelsPerFrame
// and the interleaved flag. For non interleaved formats, mNumberChannels is always 1.
// For interleaved formats, mNumberChannels is equal to mChannelsPerFrame.
result += buflist->mBuffers[i].mNumberChannels;
if ( isInput )
{
// The correct value mNumberChannels for an AudioBuffer can be derived from the mChannelsPerFrame
// and the interleaved flag. For non interleaved formats, mNumberChannels is always 1.
// For interleaved formats, mNumberChannels is equal to mChannelsPerFrame.
result += buflist->mBuffers[i].mNumberChannels;
vecNumInBufChan.Add ( buflist->mBuffers[i].mNumberChannels );
}
else
{
vecNumOutBufChan.Add ( buflist->mBuffers[i].mNumberChannels );
}
}
free ( buflist );
}
free ( buflist );
return result;
}
@ -315,10 +325,10 @@ QString CSound::LoadAndInitializeDriver ( int iDriverIdx, bool )
// the device has changed, per definition we reset the channel
// mapping to the defaults (first two available channels)
iSelInputLeftChannel = 0;
iSelInputRightChannel = min ( iNumInChan - 1, 1 );
iSelOutputLeftChannel = 0;
iSelOutputRightChannel = min ( iNumOutChan - 1, 1 );
SetLeftInputChannel ( 0 );
SetRightInputChannel ( 1 );
SetLeftOutputChannel ( 0 );
SetRightOutputChannel ( 1 );
}
return strStat;
@ -461,9 +471,6 @@ QString CSound::CheckDeviceCapabilities ( const int iDriverIdx )
"not compatible with this software." );
}
// store the input number of channels per frame for this stream
const int iNumInChanPerFrame = CurDevStreamFormat.mChannelsPerFrame;
// check the output
AudioObjectGetPropertyData ( outputStreamID,
&stPropertyAddress,
@ -482,12 +489,9 @@ QString CSound::CheckDeviceCapabilities ( const int iDriverIdx )
"not compatible with this software." );
}
// store the output number of channels per frame for this stream
const int iNumOutChanPerFrame = CurDevStreamFormat.mChannelsPerFrame;
// store the input and out number of channels for this device
iNumInChan = CountChannels ( audioInputDevice[iDriverIdx], iNumInChanPerFrame, true );
iNumOutChan = CountChannels ( audioOutputDevice[iDriverIdx], iNumOutChanPerFrame, false );
iNumInChan = CountChannels ( audioInputDevice[iDriverIdx], true );
iNumOutChan = CountChannels ( audioOutputDevice[iDriverIdx], false );
// clip the number of input/output channels to our allowed maximum
if ( iNumInChan > MAX_NUM_IN_OUT_CHANNELS )
@ -567,25 +571,129 @@ QString CSound::CheckDeviceCapabilities ( const int iDriverIdx )
}
}
// special case with 4 input channels: support adding channels
if ( iNumInChan == 4 )
{
// add four mixed channels (i.e. 4 normal, 4 mixed channels)
iNumInChanPlusAddChan = 8;
for ( int iCh = 0; iCh < iNumInChanPlusAddChan; iCh++ )
{
int iSelCH, iSelAddCH;
GetSelCHAndAddCH ( iCh, iNumInChan, iSelCH, iSelAddCH );
if ( iSelAddCH >= 0 )
{
// for mixed channels, show both audio channel names to be mixed
sChannelNamesInput[iCh] =
sChannelNamesInput[iSelCH] + " + " + sChannelNamesInput[iSelAddCH];
}
}
}
else
{
// regular case: no mixing input channels used
iNumInChanPlusAddChan = iNumInChan;
}
// everything is ok, return empty string for "no error" case
return "";
}
void CSound::UpdateChSelection()
{
// calculate the selected input/output buffer and the selected interleaved
// channel index in the buffer, note that each buffer can have a different
// number of interleaved channels
int iChCnt;
int iSelCHLeft, iSelAddCHLeft;
int iSelCHRight, iSelAddCHRight;
// initialize all buffer indexes with an invalid value
iSelInBufferLeft = -1;
iSelInBufferRight = -1;
iSelAddInBufferLeft = -1; // if no additional channel used, this will stay on the invalid value
iSelAddInBufferRight = -1; // if no additional channel used, this will stay on the invalid value
iSelOutBufferLeft = -1;
iSelOutBufferRight = -1;
// input
GetSelCHAndAddCH ( iSelInputLeftChannel, iNumInChan, iSelCHLeft, iSelAddCHLeft );
GetSelCHAndAddCH ( iSelInputRightChannel, iNumInChan, iSelCHRight, iSelAddCHRight );
iChCnt = 0;
for ( int iBuf = 0; iBuf < vecNumInBufChan.Size(); iBuf++ )
{
iChCnt += vecNumInBufChan[iBuf];
if ( ( iSelInBufferLeft < 0 ) && ( iChCnt > iSelCHLeft ) )
{
iSelInBufferLeft = iBuf;
iSelInInterlChLeft = iSelCHLeft - iChCnt + vecNumInBufChan[iBuf];
}
if ( ( iSelInBufferRight < 0 ) && ( iChCnt > iSelCHRight ) )
{
iSelInBufferRight = iBuf;
iSelInInterlChRight = iSelCHRight - iChCnt + vecNumInBufChan[iBuf];
}
if ( ( iSelAddCHLeft >= 0 ) && ( iSelAddInBufferLeft < 0 ) && ( iChCnt > iSelAddCHLeft ) )
{
iSelAddInBufferLeft = iBuf;
iSelAddInInterlChLeft = iSelAddCHLeft - iChCnt + vecNumInBufChan[iBuf];
}
if ( ( iSelAddCHRight >= 0 ) && ( iSelAddInBufferRight < 0 ) && ( iChCnt > iSelAddCHRight ) )
{
iSelAddInBufferRight = iBuf;
iSelAddInInterlChRight = iSelAddCHRight - iChCnt + vecNumInBufChan[iBuf];
}
}
// output
GetSelCHAndAddCH ( iSelOutputLeftChannel, iNumOutChan, iSelCHLeft, iSelAddCHLeft );
GetSelCHAndAddCH ( iSelOutputRightChannel, iNumOutChan, iSelCHRight, iSelAddCHRight );
iChCnt = 0;
for ( int iBuf = 0; iBuf < vecNumOutBufChan.Size(); iBuf++ )
{
iChCnt += vecNumOutBufChan[iBuf];
if ( ( iSelOutBufferLeft < 0 ) && ( iChCnt > iSelCHLeft ) )
{
iSelOutBufferLeft = iBuf;
iSelOutInterlChLeft = iSelCHLeft - iChCnt + vecNumOutBufChan[iBuf];
}
if ( ( iSelOutBufferRight < 0 ) && ( iChCnt > iSelCHRight ) )
{
iSelOutBufferRight = iBuf;
iSelOutInterlChRight = iSelCHRight - iChCnt + vecNumOutBufChan[iBuf];
}
}
}
void CSound::SetLeftInputChannel ( const int iNewChan )
{
// apply parameter after input parameter check
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumInChan ) )
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumInChanPlusAddChan ) )
{
iSelInputLeftChannel = iNewChan;
UpdateChSelection();
}
}
void CSound::SetRightInputChannel ( const int iNewChan )
{
// apply parameter after input parameter check
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumInChan ) )
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumInChanPlusAddChan ) )
{
iSelInputRightChannel = iNewChan;
UpdateChSelection();
}
}
@ -595,6 +703,7 @@ void CSound::SetLeftOutputChannel ( const int iNewChan )
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumOutChan ) )
{
iSelOutputLeftChannel = iNewChan;
UpdateChSelection();
}
}
@ -604,6 +713,7 @@ void CSound::SetRightOutputChannel ( const int iNewChan )
if ( ( iNewChan >= 0 ) && ( iNewChan < iNumOutChan ) )
{
iSelOutputRightChannel = iNewChan;
UpdateChSelection();
}
}
@ -823,68 +933,70 @@ OSStatus CSound::callbackIO ( AudioDeviceID inDevice,
// both, the input and output device use the same callback function
QMutexLocker locker ( &pSound->Mutex );
const int iCoreAudioBufferSizeMono = pSound->iCoreAudioBufferSizeMono;
const int iNumInChan = pSound->iNumInChan;
const int iNumOutChan = pSound->iNumOutChan;
const int iSelInputLeftChannel = pSound->iSelInputLeftChannel;
const int iSelInputRightChannel = pSound->iSelInputRightChannel;
const int iSelOutputLeftChannel = pSound->iSelOutputLeftChannel;
const int iSelOutputRightChannel = pSound->iSelOutputRightChannel;
const int iCoreAudioBufferSizeMono = pSound->iCoreAudioBufferSizeMono;
const int iSelInBufferLeft = pSound->iSelInBufferLeft;
const int iSelInBufferRight = pSound->iSelInBufferRight;
const int iSelInInterlChLeft = pSound->iSelInInterlChLeft;
const int iSelInInterlChRight = pSound->iSelInInterlChRight;
const int iSelAddInBufferLeft = pSound->iSelAddInBufferLeft;
const int iSelAddInBufferRight = pSound->iSelAddInBufferRight;
const int iSelAddInInterlChLeft = pSound->iSelAddInInterlChLeft;
const int iSelAddInInterlChRight = pSound->iSelAddInInterlChRight;
const int iSelOutBufferLeft = pSound->iSelOutBufferLeft;
const int iSelOutBufferRight = pSound->iSelOutBufferRight;
const int iSelOutInterlChLeft = pSound->iSelOutInterlChLeft;
const int iSelOutInterlChRight = pSound->iSelOutInterlChRight;
const CVector<int>& vecNumInBufChan = pSound->vecNumInBufChan;
const CVector<int>& vecNumOutBufChan = pSound->vecNumOutBufChan;
if ( ( inDevice == pSound->CurrentAudioInputDeviceID ) && inInputData )
{
// check size (float32 has four bytes)
if ( inInputData->mBuffers[0].mDataByteSize ==
static_cast<UInt32> ( iCoreAudioBufferSizeMono * iNumInChan * 4 ) )
// check sizes (note that float32 has four bytes)
if ( ( iSelInBufferLeft >= 0 ) &&
( iSelInBufferLeft < static_cast<int> ( inInputData->mNumberBuffers ) ) &&
( iSelInBufferRight >= 0 ) &&
( iSelInBufferRight < static_cast<int> ( inInputData->mNumberBuffers ) ) &&
( iSelAddInBufferLeft < static_cast<int> ( inInputData->mNumberBuffers ) ) &&
( iSelAddInBufferRight < static_cast<int> ( inInputData->mNumberBuffers ) ) &&
( inInputData->mBuffers[iSelInBufferLeft].mDataByteSize == static_cast<UInt32> ( vecNumInBufChan[iSelInBufferLeft] * iCoreAudioBufferSizeMono * 4 ) ) &&
( inInputData->mBuffers[iSelInBufferRight].mDataByteSize == static_cast<UInt32> ( vecNumInBufChan[iSelInBufferRight] * iCoreAudioBufferSizeMono * 4 ) ) )
{
// one buffer with all the channels in interleaved format:
// get a pointer to the input data of the correct type
Float32* pInData = static_cast<Float32*> ( inInputData->mBuffers[0].mData );
Float32* pLeftData = static_cast<Float32*> ( inInputData->mBuffers[iSelInBufferLeft].mData );
Float32* pRightData = static_cast<Float32*> ( inInputData->mBuffers[iSelInBufferRight].mData );
int iNumChanPerFrameLeft = vecNumInBufChan[iSelInBufferLeft];
int iNumChanPerFrameRight = vecNumInBufChan[iSelInBufferRight];
// copy input data
for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ )
{
// left
pSound->vecsTmpAudioSndCrdStereo[2 * i] =
(short) ( pInData[iNumInChan * i + iSelInputLeftChannel] * _MAXSHORT );
// right
pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] =
(short) ( pInData[iNumInChan * i + iSelInputRightChannel] * _MAXSHORT );
/*
// TEST mix channel with micro to the stereo output
if ( iNumInChan == 4 )
{
// add mic input on input channel 4 to both stereo channels
pSound->vecsTmpAudioSndCrdStereo[2 * i] =
Double2Short ( (double) ( pInData[iNumInChan * i + 3] * _MAXSHORT ) +
(double) pSound->vecsTmpAudioSndCrdStereo[2 * i] );
pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] =
Double2Short ( (double) ( pInData[iNumInChan * i + 3] * _MAXSHORT ) +
(double) pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] );
}
*/
// copy left and right channels separately
pSound->vecsTmpAudioSndCrdStereo[2 * i] = (short) ( pLeftData[iNumChanPerFrameLeft * i + iSelInInterlChLeft] * _MAXSHORT );
pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = (short) ( pRightData[iNumChanPerFrameRight * i + iSelInInterlChRight] * _MAXSHORT );
}
}
else if ( inInputData->mNumberBuffers == (UInt32) iNumInChan && // we should have a matching number of buffers to channels
inInputData->mBuffers[0].mDataByteSize == static_cast<UInt32> ( iCoreAudioBufferSizeMono * 4 ) )
{
// one buffer per channel mode:
AudioBuffer left = inInputData->mBuffers[iSelInputLeftChannel];
Float32* pLeftData = static_cast<Float32*> ( left.mData );
AudioBuffer right = inInputData->mBuffers[iSelInputRightChannel];
Float32* pRightData = static_cast<Float32*> ( right.mData );
// copy input data
for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ )
// add an additional optional channel
if ( iSelAddInBufferLeft >= 0 )
{
// left
pSound->vecsTmpAudioSndCrdStereo[2 * i] = (short) ( pLeftData[i] * _MAXSHORT );
pLeftData = static_cast<Float32*> ( inInputData->mBuffers[iSelAddInBufferLeft].mData );
iNumChanPerFrameLeft = vecNumInBufChan[iSelAddInBufferLeft];
// right
pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = (short) ( pRightData[i] * _MAXSHORT );
for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ )
{
pSound->vecsTmpAudioSndCrdStereo[2 * i] = Double2Short (
pSound->vecsTmpAudioSndCrdStereo[2 * i] + pLeftData[iNumChanPerFrameLeft * i + iSelAddInInterlChLeft] * _MAXSHORT );
}
}
if ( iSelAddInBufferRight >= 0 )
{
pRightData = static_cast<Float32*> ( inInputData->mBuffers[iSelAddInBufferRight].mData );
iNumChanPerFrameRight = vecNumInBufChan[iSelAddInBufferRight];
for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ )
{
pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] = Double2Short (
pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] + pRightData[iNumChanPerFrameRight * i + iSelAddInInterlChRight] * _MAXSHORT );
}
}
}
else
@ -899,42 +1011,26 @@ if ( iNumInChan == 4 )
if ( ( inDevice == pSound->CurrentAudioOutputDeviceID ) && outOutputData )
{
// check size (float32 has four bytes)
if ( outOutputData->mBuffers[0].mDataByteSize ==
static_cast<UInt32> ( iCoreAudioBufferSizeMono * iNumOutChan * 4 ) )
{
// one buffer with all the channels in interleaved format:
// get a pointer to the input data of the correct type
Float32* pOutData = static_cast<Float32*> ( outOutputData->mBuffers[0].mData );
// check sizes (note that float32 has four bytes)
if ( ( iSelOutBufferLeft >= 0 ) &&
( iSelOutBufferLeft < static_cast<int> ( outOutputData->mNumberBuffers ) ) &&
( iSelOutBufferRight >= 0 ) &&
( iSelOutBufferRight < static_cast<int> ( outOutputData->mNumberBuffers ) ) &&
( outOutputData->mBuffers[iSelOutBufferLeft].mDataByteSize == static_cast<UInt32> ( vecNumOutBufChan[iSelOutBufferLeft] * iCoreAudioBufferSizeMono * 4 ) ) &&
( outOutputData->mBuffers[iSelOutBufferRight].mDataByteSize == static_cast<UInt32> ( vecNumOutBufChan[iSelOutBufferRight] * iCoreAudioBufferSizeMono * 4 ) ) )
{
Float32* pLeftData = static_cast<Float32*> ( outOutputData->mBuffers[iSelOutBufferLeft].mData );
Float32* pRightData = static_cast<Float32*> ( outOutputData->mBuffers[iSelOutBufferRight].mData );
int iNumChanPerFrameLeft = vecNumOutBufChan[iSelOutBufferLeft];
int iNumChanPerFrameRight = vecNumOutBufChan[iSelOutBufferRight];
// copy output data
for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ )
{
// left
pOutData[iNumOutChan * i + iSelOutputLeftChannel] =
(Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i] / _MAXSHORT;
// right
pOutData[iNumOutChan * i + iSelOutputRightChannel] =
(Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] / _MAXSHORT;
}
}
else if ( outOutputData->mNumberBuffers == (UInt32) iNumOutChan && // we should have a matching number of buffers to channels
outOutputData->mBuffers[0].mDataByteSize == static_cast<UInt32> ( iCoreAudioBufferSizeMono * 4 ) )
{
// Outputs are to individual buffers too, rather than using channels
Float32* pLeftOutData = static_cast<Float32*> ( outOutputData->mBuffers[iSelOutputLeftChannel].mData );
Float32* pRightOutData = static_cast<Float32*> ( outOutputData->mBuffers[iSelOutputRightChannel].mData );
// copy output data
for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ )
{
// left
pLeftOutData[i] = (Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i] / _MAXSHORT;
// right
pRightOutData[i] = (Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] / _MAXSHORT;
}
// copy output data
for ( int i = 0; i < iCoreAudioBufferSizeMono; i++ )
{
// copy left and right channels separately
pLeftData[iNumChanPerFrameLeft * i + iSelOutInterlChLeft] = (Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i] / _MAXSHORT;
pRightData[iNumChanPerFrameRight * i + iSelOutInterlChRight] = (Float32) pSound->vecsTmpAudioSndCrdStereo[2 * i + 1] / _MAXSHORT;
}
}
}

View file

@ -36,17 +36,18 @@
class CSound : public CSoundBase
{
public:
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect );
CSound ( void (*fpNewProcessCallback) ( CVector<short>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool ,
const QString& );
virtual int Init ( const int iNewPrefMonoBufferSize );
virtual void Start();
virtual void Stop();
// channel selection
virtual int GetNumInputChannels() { return iNumInChan; }
virtual int GetNumInputChannels() { return iNumInChanPlusAddChan; }
virtual QString GetInputChannelName ( const int iDiD ) { return sChannelNamesInput[iDiD]; }
virtual void SetLeftInputChannel ( const int iNewChan );
virtual void SetRightInputChannel ( const int iNewChan );
@ -68,18 +69,34 @@ public:
AudioDeviceID CurrentAudioInputDeviceID;
AudioDeviceID CurrentAudioOutputDeviceID;
int iNumInChan;
int iNumInChanPlusAddChan; // includes additional "added" channels
int iNumOutChan;
int iSelInputLeftChannel;
int iSelInputRightChannel;
int iSelOutputLeftChannel;
int iSelOutputRightChannel;
int iSelInBufferLeft;
int iSelInBufferRight;
int iSelInInterlChLeft;
int iSelInInterlChRight;
int iSelAddInBufferLeft;
int iSelAddInBufferRight;
int iSelAddInInterlChLeft;
int iSelAddInInterlChRight;
int iSelOutBufferLeft;
int iSelOutBufferRight;
int iSelOutInterlChLeft;
int iSelOutInterlChRight;
CVector<int> vecNumInBufChan;
CVector<int> vecNumOutBufChan;
protected:
virtual QString LoadAndInitializeDriver ( int iIdx, bool );
QString CheckDeviceCapabilities ( const int iDriverIdx );
QString CheckDeviceCapabilities ( const int iDriverIdx );
void UpdateChSelection();
int CountChannels ( AudioDeviceID devID,
const int iNumChanPerFrame,
bool isInput );
UInt32 SetBufferSize ( AudioDeviceID& audioDeviceID,

View file

@ -30,174 +30,134 @@
<item>
<layout class="QHBoxLayout">
<item>
<layout class="QVBoxLayout">
<item>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="PixmapLabelDreamLogo">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="pixmap">
<pixmap/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblVersion">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>TextLabelVersion</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="lblAuthorNames">
<property name="text">
<string>Author: Volker Fischer</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lblCopyright">
<property name="text">
<string>Copyright (C) 2005-2020</string>
</property>
<property name="wordWrap">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
<widget class="QLabel" name="pxlLogo">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="pixmap">
<pixmap resource="resources.qrc">:/png/main/res/fronticon.png</pixmap>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="pxlGigPicture">
<widget class="QLabel" name="lblVersion">
<property name="text">
<string/>
<string>TextLabelVersion</string>
</property>
<property name="pixmap">
<pixmap resource="resources.qrc">:/png/main/res/logopicture.png</pixmap>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="margin">
<number>2</number>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Vertical</enum>
<widget class="QLabel" name="lblCopyright">
<property name="text">
<string>Copyright (C) 2005-2020 Volker Fischer and others</string>
</property>
<property name="sizeType">
<enum>QSizePolicy::Minimum</enum>
<property name="wordWrap">
<bool>false</bool>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>1</height>
</size>
<property name="margin">
<number>2</number>
</property>
</spacer>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QTextBrowser" name="txvCredits"/>
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="tab">
<attribute name="title">
<string>A&amp;bout</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QTextBrowser" name="txvAbout">
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_2">
<attribute name="title">
<string>&amp;Libraries</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QTextBrowser" name="txvLibraries">
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_3">
<attribute name="title">
<string>&amp;Contributors</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QTextBrowser" name="txvContributors">
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_4">
<attribute name="title">
<string>&amp;Translation</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QTextBrowser" name="txvTranslation">
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout">
@ -251,7 +211,6 @@
</widget>
<tabstops>
<tabstop>buttonOk</tabstop>
<tabstop>txvCredits</tabstop>
</tabstops>
<resources>
<include location="resources.qrc"/>

View file

@ -43,11 +43,11 @@ CChannelFader::CChannelFader ( QWidget* pNW,
pPan = new QSlider (Qt::Horizontal, pLevelsBox);
pMuteSoloBox = new QWidget ( pFrame );
pcbMute = new QCheckBox ( "Mute", pMuteSoloBox );
pcbSolo = new QCheckBox ( "Solo", pMuteSoloBox );
pcbMute = new QCheckBox ( tr ( "Mute" ), pMuteSoloBox );
pcbSolo = new QCheckBox ( tr ( "Solo" ), pMuteSoloBox );
pLabelInstBox = new QGroupBox ( pFrame );
plblLabel = new QLabel ( "", pFrame );
plblLabel = new QLabel ( "", pFrame );
plblInstrument = new QLabel ( pFrame );
plblCountryFlag = new QLabel ( pFrame );
@ -116,15 +116,15 @@ CChannelFader::CChannelFader ( QWidget* pNW,
Reset();
// add help text to controls
plbrChannelLevel->setWhatsThis ( tr ( "<b>Channel Level:</b> Displays the "
"pre-fader audio level of this channel. All connected clients at the "
plbrChannelLevel->setWhatsThis ( "<b>" + tr ( "Channel Level" ) + ":</b> " +
tr ( "Displays the pre-fader audio level of this channel. All connected clients at the "
"server will be assigned an audio level, the same value for each client." ) );
plbrChannelLevel->setAccessibleName ( tr ( "Input level of the current audio "
"channel at the server" ) );
pFader->setWhatsThis ( tr ( "<b>Mixer Fader:</b> Adjusts the audio level of "
"this channel. All connected clients at the server will be assigned "
"an audio fader at each client, adjusting the local mix." ) );
pFader->setWhatsThis ( "<b>" + tr ( "Mixer Fader" ) + ":</b> " + tr (
"Adjusts the audio level of this channel. All connected clients at the server "
"will be assigned an audio fader at each client, adjusting the local mix." ) );
pFader->setAccessibleName ( tr ( "Local mix level setting of the current audio "
"channel at the server" ) );
@ -132,17 +132,17 @@ CChannelFader::CChannelFader ( QWidget* pNW,
" Works only in stero or preferably mono in/stereo out mode." ) );
pPan->setAccessibleName ( tr ( "Local panning position of the current audio channel at the server" ) );
pcbMute->setWhatsThis ( tr ( "<b>Mute:</b> With the Mute checkbox, the "
"audio channel can be muted." ) );
pcbMute->setWhatsThis ( "<b>" + tr ( "Mute" ) + ":</b> " + tr (
"With the Mute checkbox, the audio channel can be muted." ) );
pcbMute->setAccessibleName ( tr ( "Mute button" ) );
pcbSolo->setWhatsThis ( tr ( "<b>Solo:</b> With the Solo checkbox, the "
pcbSolo->setWhatsThis ( "<b>" + tr ( "Solo" ) + ":</b> " + tr ( "With the Solo checkbox, the "
"audio channel can be set to solo which means that all other channels "
"except of the current channel are muted. It is possible to set more than "
"one channel to solo." ) );
pcbSolo->setAccessibleName ( tr ( "Solo button" ) );
QString strFaderText = tr ( "<b>Fader Tag:</b> The fader tag "
QString strFaderText = "<b>" + tr ( "Fader Tag" ) + ":</b> " + tr ( "The fader tag "
"identifies the connected client. The tag name, the picture of your "
"instrument and a flag of your country can be set in the main window." );
@ -474,13 +474,13 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo )
// alias/name
if ( !strReceivedName.isEmpty() )
{
strToolTip += "<h4>Alias/Name</h4>" + strReceivedName;
strToolTip += "<h4>" + tr ( "Alias/Name" ) + "</h4>" + strReceivedName;
}
// instrument
if ( !CInstPictures::IsNotUsedInstrument ( iTTInstrument ) )
{
strToolTip += "<h4>Instrument</h4>" +
strToolTip += "<h4>" + tr ( "Instrument" ) + "</h4>" +
CInstPictures::GetName ( iTTInstrument );
}
@ -488,7 +488,7 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo )
if ( ( eTTCountry != QLocale::AnyCountry ) ||
( !cChanInfo.strCity.isEmpty() ) )
{
strToolTip += "<h4>Location</h4>";
strToolTip += "<h4>" + tr ( "Location" ) + "</h4>";
if ( !cChanInfo.strCity.isEmpty() )
{
@ -510,15 +510,15 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo )
switch ( cChanInfo.eSkillLevel )
{
case SL_BEGINNER:
strToolTip += "<h4>Skill Level</h4>Beginner";
strToolTip += "<h4>" + tr ( "Skill Level" ) + "</h4>" + tr ( "Beginner" );
break;
case SL_INTERMEDIATE:
strToolTip += "<h4>Skill Level</h4>Intermediate";
strToolTip += "<h4>" + tr ( "Skill Level" ) + "</h4>" + tr ( "Intermediate" );
break;
case SL_PROFESSIONAL:
strToolTip += "<h4>Skill Level</h4>Expert";
strToolTip += "<h4>" + tr ( "Skill Level" ) + "</h4>" + tr ( "Expert" );
break;
case SL_NOT_SET:
@ -529,7 +529,7 @@ void CChannelFader::SetChannelInfos ( const CChannelInfo& cChanInfo )
// if no information is given, leave the tool tip empty, otherwise add header
if ( !strToolTip.isEmpty() )
{
strToolTip.prepend ( "<h3>Musician Profile</h3>" );
strToolTip.prepend ( "<h3>" + tr ( "Musician Profile" ) + "</h3>" );
}
plblCountryFlag->setToolTip ( strToolTip );
@ -746,7 +746,7 @@ void CAudioMixerBoard::SetServerName ( const QString& strNewServerName )
if ( strServerName.isEmpty() )
{
// no connection or connection was reset: show default title
pGroupBox->setTitle ( "Server" );
pGroupBox->setTitle ( tr ( "Server" ) );
}
else
{
@ -755,13 +755,7 @@ void CAudioMixerBoard::SetServerName ( const QString& strNewServerName )
// list was received, the connection was successful and the title is updated
// with the correct server name. Make sure to choose a "try to connect" title
// which is most striking (we use filled blocks and upper case letters).
QByteArray baBlock;
baBlock.append ( 0xE2 );
baBlock.append ( 0x96 );
baBlock.append ( 0x88 );
QString sBlocks = QString().fromUtf8 ( baBlock ).repeated ( 5 );
pGroupBox->setTitle ( sBlocks + " T R Y I N G T O C O N N E C T " + sBlocks );
pGroupBox->setTitle ( u8"\u2588\u2588\u2588\u2588\u2588 " + tr ( "T R Y I N G T O C O N N E C T" ) + u8" \u2588\u2588\u2588\u2588\u2588" );
}
}

View file

@ -52,7 +52,7 @@ public:
void SetChannelInfos ( const CChannelInfo& cChanInfo );
void Show() { pFrame->show(); }
void Hide() { pFrame->hide(); }
bool IsVisible() { return plblLabel->isVisible(); }
bool IsVisible() { return !pFrame->isHidden(); }
bool IsSolo() { return pcbSolo->isChecked(); }
bool IsMute() { return pcbMute->isChecked(); }
void SetGUIDesign ( const EGUIDesign eNewDesign );

View file

@ -34,14 +34,14 @@ CChatDlg::CChatDlg ( QWidget* parent, Qt::WindowFlags f ) :
// Add help text to controls -----------------------------------------------
// chat window
txvChatWindow->setWhatsThis ( tr ( "<b>Chat Window:</b> The chat window "
"shows a history of all chat messages." ) );
txvChatWindow->setWhatsThis ( "<b>" + tr ( "Chat Window" ) + ":</b> " + tr (
"The chat window shows a history of all chat messages." ) );
txvChatWindow->setAccessibleName ( tr ( "Chat history" ) );
// input message text
edtLocalInputText->setWhatsThis ( tr ( "<b>Input Message Text:</b> Enter "
"the chat message text in the edit box and press enter to send the "
edtLocalInputText->setWhatsThis ( "<b>" + tr ( "Input Message Text" ) + ":</b> " + tr (
"Enter the chat message text in the edit box and press enter to send the "
"message to the server which distributes the message to all connected "
"clients. Your message will then show up in the chat window." ) );

View file

@ -29,7 +29,8 @@
CClient::CClient ( const quint16 iPortNumber,
const QString& strConnOnStartupAddress,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect ) :
const bool bNoAutoJackConnect,
const QString& strNClientName ) :
vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ),
ChannelInfo (),
vecStoredFaderTags ( MAX_NUM_STORED_FADER_SETTINGS, "" ),
@ -38,6 +39,7 @@ CClient::CClient ( const quint16 iPortNumber,
vecStoredFaderIsMute ( MAX_NUM_STORED_FADER_SETTINGS, false ),
iNewClientFaderLevel ( 100 ),
bConnectDlgShowAllMusicians ( true ),
strClientName ( strNClientName ),
vecWindowPosMain (), // empty array
vecWindowPosSettings (), // empty array
vecWindowPosChat (), // empty array
@ -59,7 +61,7 @@ CClient::CClient ( const quint16 iPortNumber,
bIsInitializationPhase ( true ),
bMuteOutStream ( false ),
Socket ( &Channel, iPortNumber ),
Sound ( AudioCallback, this, iCtrlMIDIChannel, bNoAutoJackConnect ),
Sound ( AudioCallback, this, iCtrlMIDIChannel, bNoAutoJackConnect, strNClientName ),
iAudioInFader ( AUD_FADER_IN_MIDDLE ),
bReverbOnLeftChan ( false ),
iReverbLevel ( 0 ),
@ -945,7 +947,7 @@ vecsMultChanAudioSndCrd = vecsStereoSndCrdTMP; // TEST just copy the stereo data
void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
{
int i, j;
int i, j, iUnused;
unsigned char* pCurCodedData;
@ -957,7 +959,7 @@ void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
if ( iReverbLevel != 0 )
{
// calculate attenuation amplification factor
const double dRevLev = static_cast<double> ( iReverbLevel ) / AUD_REVERB_MAX / 2;
const double dRevLev = static_cast<double> ( iReverbLevel ) / AUD_REVERB_MAX / 4;
if ( eAudioChannelConf == CC_STEREO )
{
@ -1094,19 +1096,19 @@ void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
{
if ( bMuteOutStream )
{
opus_custom_encode ( CurOpusEncoder,
&vecZeros[i * iNumAudioChannels * iOPUSFrameSizeSamples],
iOPUSFrameSizeSamples,
&vecCeltData[0],
iCeltNumCodedBytes );
iUnused = opus_custom_encode ( CurOpusEncoder,
&vecZeros[i * iNumAudioChannels * iOPUSFrameSizeSamples],
iOPUSFrameSizeSamples,
&vecCeltData[0],
iCeltNumCodedBytes );
}
else
{
opus_custom_encode ( CurOpusEncoder,
&vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples],
iOPUSFrameSizeSamples,
&vecCeltData[0],
iCeltNumCodedBytes );
iUnused = opus_custom_encode ( CurOpusEncoder,
&vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples],
iOPUSFrameSizeSamples,
&vecCeltData[0],
iCeltNumCodedBytes );
}
}
@ -1150,11 +1152,11 @@ void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
// OPUS decoding
if ( CurOpusDecoder != nullptr )
{
opus_custom_decode ( CurOpusDecoder,
pCurCodedData,
iCeltNumCodedBytes,
&vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples],
iOPUSFrameSizeSamples );
iUnused = opus_custom_decode ( CurOpusDecoder,
pCurCodedData,
iCeltNumCodedBytes,
&vecsStereoSndCrd[i * iNumAudioChannels * iOPUSFrameSizeSamples],
iOPUSFrameSizeSamples );
}
}
@ -1204,6 +1206,8 @@ fflush(pFileDelay);
// update socket buffer size
Channel.UpdateSocketBufferSize();
Q_UNUSED ( iUnused )
}
int CClient::EstimatedOverallDelay ( const int iPingTimeMs )

View file

@ -107,7 +107,8 @@ public:
CClient ( const quint16 iPortNumber,
const QString& strConnOnStartupAddress,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect );
const bool bNoAutoJackConnect,
const QString& strNClientName );
void Start();
void Stop();
@ -285,6 +286,7 @@ public:
CVector<int> vecStoredFaderIsMute;
int iNewClientFaderLevel;
bool bConnectDlgShowAllMusicians;
QString strClientName;
// window position/state settings
QByteArray vecWindowPosMain;
@ -399,7 +401,7 @@ public slots:
void OnJittBufSizeChanged ( int iNewJitBufSize );
void OnReqChanInfo() { Channel.SetRemoteInfo ( ChannelInfo ); }
void OnNewConnection();
void OnCLDisconnection ( CHostAddress InetAddr ) { if ( InetAddr == Channel.GetAddress() ) { Stop(); } }
void OnCLDisconnection ( CHostAddress InetAddr ) { if ( InetAddr == Channel.GetAddress() ) { emit Disconnected(); } }
void OnCLPingReceived ( CHostAddress InetAddr,
int iMs );

View file

@ -48,10 +48,10 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
// Add help text to controls -----------------------------------------------
// input level meter
QString strInpLevH = tr ( "<b>Input Level Meter:</b> The input level "
QString strInpLevH = "<b>" + tr ( "Input Level Meter" ) + ":</b> " + tr ( "The input level "
"indicators show the input level of the two stereo channels "
"of the current selected audio input.<br>"
"Make sure not to clip the input signal to avoid distortions of the "
"of the current selected audio input." ) + "<br>" +
tr ( "Make sure not to clip the input signal to avoid distortions of the "
"audio signal." );
QString strInpLevHTT = tr ( "If the " ) + APP_NAME +
@ -60,13 +60,13 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
"meter should flicker. If this is not the case, you have "
"probably selected the wrong input channel (e.g. line in instead "
"of the microphone input) or set the input gain too low in the "
"(Windows) audio mixer.<br>For a proper usage of the " ) + APP_NAME +
tr ( " software, "
"(Windows) audio mixer." ) + "<br>" + tr ( "For a proper usage of the " ) +
APP_NAME + tr ( " software, "
"you should not hear your singing/instrument in the loudspeaker or "
"your headphone when the " ) + APP_NAME +
tr ( " software is not connected. This can "
"be achieved by muting your input audio channel in the Playback "
"mixer (<b>not</b> the Recording mixer!)." ) + TOOLTIP_COM_END_TEXT;
"mixer (not the Recording mixer!)." ) + TOOLTIP_COM_END_TEXT;
QString strInpLevHAccText = tr ( "Input level meter" );
QString strInpLevHAccDescr = tr ( "Simulates an analog LED level meter." );
@ -84,8 +84,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
lbrInputLevelR->setToolTip ( strInpLevHTT );
// connect/disconnect button
butConnect->setWhatsThis ( tr ( "<b>Connect / Disconnect Button:"
"</b> Push this button to connect a server. A dialog where you can "
butConnect->setWhatsThis ( "<b>" + tr ( "Connect/Disconnect Button" ) + ":</b> " +
tr ( "Push this button to connect a server. A dialog where you can "
"select a server will open. If you are connected, pressing this "
"button will end the session." ) );
@ -98,14 +98,15 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
"and disconnecting the " ) + APP_NAME + tr ( " software." ) );
// local audio input fader
QString strAudFader = tr ( "<b>Local Audio Input Fader:</b> With the "
"audio fader, the relative levels of the left and right local audio "
QString strAudFader = "<b>" + tr ( "Local Audio Input Fader" ) + ":</b> " +
tr ( "With the audio fader, the relative levels of the left and right local audio "
"channels can be changed. For a mono signal it acts like a panning "
"between the two channels. If, e.g., a microphone is connected to "
"the right input channel and an instrument is connected to the left "
"input channel which is much louder than the microphone, move the "
"audio fader in a direction where the label above the fader shows "
"<i>L -x</i>, where <i>x</i> is the current attenuation indicator." );
"audio fader in a direction where the label above the fader shows " ) +
"<i>" + tr ( "L" ) + " -x</i>" + tr ( ", where" ) + " <i>x</i> " +
tr ( "is the current attenuation indicator." );
lblAudioPan->setWhatsThis ( strAudFader );
lblAudioPanValue->setWhatsThis ( strAudFader );
@ -114,14 +115,14 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
sldAudioPan->setAccessibleName ( tr ( "Local audio input fader (left/right)" ) );
// reverberation level
QString strAudReverb = tr ( "<b>Reverberation Level:</b> A reverberation "
"effect can be applied to one local mono audio channel or to both "
"channels in stereo mode. The mone channel selection and the "
QString strAudReverb = "<b>" + tr ( "Reverberation Level" ) + ":</b> " +
tr ( "A reverberation effect can be applied to one local mono audio channel or to both "
"channels in stereo mode. The mono channel selection and the "
"reverberation level can be modified. If, e.g., "
"the microphone signal is fed into the right audio channel of the "
"sound card and a reverberation effect shall be applied, set the "
"channel selector to right and move the fader upwards until the "
"desired reverberation level is reached.<br>"
"desired reverberation level is reached." ) + "<br>" + tr (
"The reverberation effect requires significant CPU so that it should "
"only be used on fast PCs. If the reverberation level fader is set to "
"minimum (which is the default setting), the reverberation effect is "
@ -133,8 +134,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
sldAudioReverb->setAccessibleName ( tr ( "Reverberation effect level setting" ) );
// reverberation channel selection
QString strRevChanSel = tr ( "<b>Reverberation Channel Selection:</b> "
"With these radio buttons the audio input channel on which the "
QString strRevChanSel = "<b>" + tr ( "Reverberation Channel Selection" ) + ":</b> " +
tr ( "With these radio buttons the audio input channel on which the "
"reverberation effect is applied can be chosen. Either the left "
"or right input channel can be selected." );
@ -144,8 +145,8 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
rbtReverbSelR->setAccessibleName ( tr ( "Right channel selection for reverberation" ) );
// delay LED
QString strLEDDelay = tr ( "<b>Delay Status LED:</b> "
"The delay status LED indicator shows the current audio delay "
QString strLEDDelay = "<b>" + tr ( "Delay Status LED" ) + ":</b> " +
tr ( "The delay status LED indicator shows the current audio delay "
"status. If the light is green, the delay is perfect for a jam "
"session. If the light is yellow, a session is still possible but "
"it may be harder to play. If the light is red, the delay is too "
@ -160,20 +161,20 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
ledDelay->setAccessibleName ( tr ( "Delay status LED indicator" ) );
// buffers LED
QString strLEDBuffers = tr ( "<b>Buffers Status LED:</b> "
"The buffers status LED indicator shows the current audio/streaming "
QString strLEDBuffers = "<b>" + tr ( "Buffers Status LED" ) + ":</b> " +
tr ( "The buffers status LED indicator shows the current audio/streaming "
"status. If the light is green, there are no buffer overruns/underruns "
"and the audio stream is not interrupted. If the light is red, the "
"audio stream is interrupted caused by one of the following problems:"
"audio stream is interrupted caused by one of the following problems:" ) +
"<ul>"
"<li>The network jitter buffer is not large enough for the current "
"network/audio interface jitter.</li>"
"<li>The sound card buffer delay (buffer size) is set to a too small "
"value.</li>"
"<li>The upload or download stream rate is too high for the current "
"available internet bandwidth.</li>"
"<li>The CPU of the client or server is at 100%.</li>"
"</ul>" );
"<li>" + tr ( "The network jitter buffer is not large enough for the current "
"network/audio interface jitter." ) + "</li>"
"<li>" + tr ( "The sound card buffer delay (buffer size) is set to a too small "
"value." ) + "</li>"
"<li>" + tr ( "The upload or download stream rate is too high for the current "
"available internet bandwidth." ) + "</li>"
"<li>" + tr ( "The CPU of the client or server is at 100%." ) + "</li>"
"</ul>";
lblBuffers->setWhatsThis ( strLEDBuffers );
ledBuffers->setWhatsThis ( strLEDBuffers );
@ -200,7 +201,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
OnTimerStatus();
// init connection button text
butConnect->setText ( CON_BUT_CONNECTTEXT );
butConnect->setText ( tr ( "C&onnect" ) );
// init input level meter bars
lbrInputLevelL->setValue ( 0 );
@ -252,7 +253,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
// View menu --------------------------------------------------------------
pViewMenu = new QMenu ( "&View", this );
pViewMenu = new QMenu ( tr ( "&View" ), this );
pViewMenu->addAction ( tr ( "&Connection Setup..." ), this,
SLOT ( OnOpenConnectionSetupDialog() ) );
@ -283,7 +284,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
pMenu = new QMenuBar ( this );
pMenu->addMenu ( pViewMenu );
pMenu->addMenu ( new CHelpMenu ( this ) );
pMenu->addMenu ( new CHelpMenu ( true, this ) );
// Now tell the layout about the menu
layout()->setMenuBar ( pMenu );
@ -323,7 +324,7 @@ CClientDlg::CClientDlg ( CClient* pNCliP,
{
// default icon and name for no flag selected
CurFlagIcon.addFile ( ":/png/flags/res/flags/flagnone.png" );
sCurCountryName = "None";
sCurCountryName = tr ( "None" );
}
else
{
@ -606,20 +607,20 @@ void CClientDlg::UpdateAudioFaderSlider()
// attenuated
if ( iCurAudInFader == AUD_FADER_IN_MIDDLE )
{
lblAudioPanValue->setText ( "Center" );
lblAudioPanValue->setText ( tr ( "Center" ) );
}
else
{
if ( iCurAudInFader > AUD_FADER_IN_MIDDLE )
{
// attenuation on right channel
lblAudioPanValue->setText ( "R -" +
lblAudioPanValue->setText ( tr ( "R" ) + " -" +
QString().setNum ( iCurAudInFader - AUD_FADER_IN_MIDDLE ) );
}
else
{
// attenuation on left channel
lblAudioPanValue->setText ( "L -" +
lblAudioPanValue->setText ( tr ( "L" ) + " -" +
QString().setNum ( AUD_FADER_IN_MIDDLE - iCurAudInFader ) );
}
}
@ -734,16 +735,6 @@ void CClientDlg::OnConnectDisconBut()
}
}
void CClientDlg::OnDisconnected()
{
// channel is now disconnected, clear mixer board (remove all faders) and
// reset the delay LED (since this is only updated on an active connection)
MainMixerBoard->HideAll();
ledDelay->Reset();
UpdateDisplay();
}
void CClientDlg::OnCentralServerAddressTypeChanged()
{
// if the server list is shown and the server type was changed, update the list
@ -812,18 +803,18 @@ void CClientDlg::SetMyWindowTitle ( const int iNumClients )
if ( iNumClients == 0 )
{
// only application name
setWindowTitle ( APP_NAME );
setWindowTitle ( pClient->strClientName );
}
else
{
if ( iNumClients == 1 )
{
setWindowTitle ( QString ( APP_NAME ) + " (1 user)" );
setWindowTitle ( QString ( pClient->strClientName ) + " (1 " + tr ( "user" ) + ")" );
}
else
{
setWindowTitle ( QString ( APP_NAME ) +
QString ( " (%1 users)" ).arg ( iNumClients ) );
setWindowTitle ( QString ( pClient->strClientName ) +
QString ( " (%1 " + tr ( "users" ) + ")" ).arg ( iNumClients ) );
}
}
@ -1060,7 +1051,7 @@ void CClientDlg::Connect ( const QString& strSelectedAddress,
}
// change connect button text to "disconnect"
butConnect->setText ( CON_BUT_DISCONNECTTEXT );
butConnect->setText ( tr ( "D&isconnect" ) );
// set server name in audio mixer group box title
MainMixerBoard->SetServerName ( strMixerBoardLabel );
@ -1084,7 +1075,7 @@ void CClientDlg::Disconnect()
}
// change connect button text to "connect"
butConnect->setText ( CON_BUT_CONNECTTEXT );
butConnect->setText ( tr ( "C&onnect" ) );
// reset server name in audio mixer group box title
MainMixerBoard->SetServerName ( "" );

View file

@ -51,10 +51,6 @@
/* Definitions ****************************************************************/
// text strings for connection button for connect and disconnect
#define CON_BUT_CONNECTTEXT "C&onnect"
#define CON_BUT_DISCONNECTTEXT "D&isconnect"
// update time for GUI controls
#define LEVELMETER_UPDATE_TIME_MS 100 // ms
#define BUFFER_LED_UPDATE_TIME_MS 300 // ms
@ -102,7 +98,7 @@ protected:
QTimer TimerStatus;
QTimer TimerPing;
virtual void closeEvent ( QCloseEvent* Event );
virtual void closeEvent ( QCloseEvent* Event );
void UpdateDisplay();
QMenu* pViewMenu;
@ -201,7 +197,7 @@ public slots:
{ MainMixerBoard->SetChannelLevels ( vecLevelList ); }
void OnConnectDlgAccepted();
void OnDisconnected();
void OnDisconnected() { Disconnect(); }
void OnCentralServerAddressTypeChanged();
void OnGUIDesignChanged()

View file

@ -249,7 +249,7 @@
</property>
<property name="minimumSize">
<size>
<width>16</width>
<width>19</width>
<height>88</height>
</size>
</property>
@ -265,7 +265,7 @@
</property>
<property name="minimumSize">
<size>
<width>16</width>
<width>19</width>
<height>88</height>
</size>
</property>
@ -548,43 +548,13 @@
</layout>
</item>
<item>
<widget class="QStackedWidget" name="MixerBoardPages">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
<widget class="CAudioMixerBoard" name="MainMixerBoard" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="ServerMixer">
<layout class="QHBoxLayout" name="horizontalLayoutMainMixer">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="CAudioMixerBoard" name="MainMixerBoard" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="InputLeftMixer"/>
</widget>
</item>
</layout>

View file

@ -34,34 +34,34 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
// Add help text to controls -----------------------------------------------
// jitter buffer
QString strJitterBufferSize = tr ( "<b>Jitter Buffer Size:</b> The jitter "
"buffer compensates for network and sound card timing jitters. The "
QString strJitterBufferSize = "<b>" + tr ( "Jitter Buffer Size" ) + ":</b> " + tr (
"The jitter buffer compensates for network and sound card timing jitters. The "
"size of this jitter buffer has therefore influence on the quality of "
"the audio stream (how many dropouts occur) and the overall delay "
"(the longer the buffer, the higher the delay).<br>"
"(the longer the buffer, the higher the delay)." ) + "<br>" + tr (
"The jitter buffer size can be manually chosen for the local client "
"and the remote server. For the local jitter buffer, dropouts in the "
"audio stream are indicated by the light on the bottom "
"of the jitter buffer size faders. If the light turns to red, a buffer "
"overrun/underrun took place and the audio stream is interrupted.<br>"
"overrun/underrun took place and the audio stream is interrupted." ) + "<br>" + tr (
"The jitter buffer setting is therefore a trade-off between audio "
"quality and overall delay.<br>"
"quality and overall delay." ) + "<br>" + tr (
"An auto setting of the jitter buffer size setting is available. If "
"the check Auto is enabled, the jitter buffers of the local client and "
"the remote server are set automatically "
"based on measurements of the network and sound card timing jitter. If "
"the <i>Auto</i> check is enabled, the jitter buffer size faders are "
"the Auto check is enabled, the jitter buffer size faders are "
"disabled (they cannot be moved with the mouse)." );
QString strJitterBufferSizeTT = tr ( "In case the auto setting of the "
"jitter buffer is enabled, the network buffers of the local client and "
"the remote server are set to a conservative "
"value to minimize the audio dropout probability. To <b>tweak the "
"audio delay/latency</b> it is recommended to disable the auto setting "
"functionality and to <b>lower the jitter buffer size manually</b> by "
"value to minimize the audio dropout probability. To tweak the "
"audio delay/latency it is recommended to disable the auto setting "
"functionality and to lower the jitter buffer size manually by "
"using the sliders until your personal acceptable limit of the amount "
"of dropouts is reached. The LED indicator will visualize the audio "
"dropouts of the local jitter buffer by a red light" ) +
"dropouts of the local jitter buffer by a red light." ) +
TOOLTIP_COM_END_TEXT;
lblNetBuf->setWhatsThis ( strJitterBufferSize );
@ -80,11 +80,11 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
ledNetw->setToolTip ( strJitterBufferSizeTT );
// sound card device
cbxSoundcard->setWhatsThis ( tr ( "<b>Sound Card Device:</b> The ASIO "
"driver (sound card) can be selected using " ) + APP_NAME +
cbxSoundcard->setWhatsThis ( "<b>" + tr ( "Sound Card Device" ) + ":</b> " +
tr ( "The ASIO driver (sound card) can be selected using " ) + APP_NAME +
tr ( " under the Windows operating system. Under MacOS/Linux, no sound "
"card selection is possible. If the selected ASIO driver is not valid "
"an error message is shown and the previous valid driver is selected.<br>"
"an error message is shown and the previous valid driver is selected." ) + "<br>" + tr (
"If the driver is selected during an active connection, the connection "
"is stopped, the driver is changed and the connection is started again "
"automatically." ) );
@ -93,19 +93,19 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
#ifdef _WIN32
// set Windows specific tool tip
cbxSoundcard->setToolTip ( tr ( "In case the <b>ASIO4ALL</b> driver is used, "
cbxSoundcard->setToolTip ( tr ( "In case the ASIO4ALL driver is used, "
"please note that this driver usually introduces approx. 10-30 ms of "
"additional audio delay. Using a sound card with a native ASIO driver "
"is therefore recommended.<br>If you are using the <b>kX ASIO</b> "
"is therefore recommended." ) + "<br>" + tr ( "If you are using the kX ASIO "
"driver, make sure to connect the ASIO inputs in the kX DSP settings "
"panel." ) + TOOLTIP_COM_END_TEXT );
#endif
// sound card input/output channel mapping
QString strSndCrdChanMapp = tr ( "<b>Sound Card Channel Mapping:</b> "
"In case the selected sound card device offers more than one "
QString strSndCrdChanMapp = "<b>" + tr ( "Sound Card Channel Mapping" ) + ":</b> " +
tr ( "In case the selected sound card device offers more than one "
"input or output channel, the Input Channel Mapping and Output "
"Channel Mapping settings are visible.<br>"
"Channel Mapping settings are visible." ) + "<br>" + tr (
"For each " ) + APP_NAME + tr ( " input/output channel (Left and "
"Right channel) a different actual sound card channel can be "
"selected." );
@ -122,8 +122,8 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
cbxROutChan->setAccessibleName ( tr ( "Right output channel selection combo box" ) );
// enable OPUS64
chbEnableOPUS64->setWhatsThis ( tr ( "<b>Enable Small Network Buffers:</b> If enabled, "
"the support for very small network audio packets is activated. Very small "
chbEnableOPUS64->setWhatsThis ( "<b>" + tr ( "Enable Small Network Buffers" ) + ":</b> " + tr (
"If enabled, the support for very small network audio packets is activated. Very small "
"network packets are only actually used if the sound card buffer delay is smaller than " ) +
QString().setNum ( DOUBLE_SYSTEM_FRAME_SIZE_SAMPLES ) + tr ( " samples. The "
"smaller the network buffers, the smaller the audio latency. But at the same time "
@ -132,35 +132,35 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
chbEnableOPUS64->setAccessibleName ( tr ( "Enable small network buffers check box" ) );
// sound card buffer delay
QString strSndCrdBufDelay = tr ( "<b>Sound Card Buffer Delay:</b> The "
"buffer delay setting is a fundamental setting of the " ) +
QString strSndCrdBufDelay = "<b>" + tr ( "Sound Card Buffer Delay" ) + ":</b> " +
tr ( "The buffer delay setting is a fundamental setting of the " ) +
APP_NAME + tr ( " software. This setting has influence on many "
"connection properties.<br>"
"Three buffer sizes are supported:"
"<ul>"
"<li>64 samples: This is the preferred setting since it gives lowest "
"latency but does not work with all sound cards.</li>"
"<li>128 samples: This setting should work on most of the available "
"sound cards.</li>"
"<li>256 samples: This setting should only be used if only a very slow "
"computer or a slow internet connection is available.</li>"
"</ul>"
"connection properties." ) + "<br>" + tr (
"Three buffer sizes are supported" ) +
":<ul>"
"<li>" + tr ( "64 samples: This is the preferred setting since it gives lowest "
"latency but does not work with all sound cards." ) + "</li>"
"<li>" + tr ( "128 samples: This setting should work on most of the available "
"sound cards." ) + "</li>"
"<li>" + tr ( "256 samples: This setting should only be used if only a very slow "
"computer or a slow internet connection is available." ) + "</li>"
"</ul>" + tr (
"Some sound card driver do not allow the buffer delay to be changed "
"from within the " ) + APP_NAME +
tr ( " software. In this case the buffer delay setting "
"is disabled. To change the actual buffer delay, this "
"setting has to be changed in the sound card driver. On Windows, press "
"the ASIO Setup button to open the driver settings panel. On Linux, "
"use the Jack configuration tool to change the buffer size.<br>"
"use the Jack configuration tool to change the buffer size." ) + "<br>" + tr (
"If no buffer size is selected and all settings are disabled, an "
"unsupported buffer size is used by the driver. The " ) + APP_NAME +
tr ( " software will still work with this setting but with restricted "
"performannce.<br>"
"performance." ) + "<br>" + tr (
"The actual buffer delay has influence on the connection status, the "
"current upload rate and the overall delay. The lower the buffer size, "
"the higher the probability of red light in the status indicator (drop "
"outs) and the higher the upload rate and the lower the overall "
"delay.<br>"
"delay." ) + "<br>" + tr (
"The buffer setting is therefore a trade-off between audio "
"quality and overall delay." );
@ -185,19 +185,19 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
butDriverSetup->setToolTip ( strSndCrdBufDelayTT );
// fancy skin
chbGUIDesignFancy->setWhatsThis ( tr ( "<b>Fancy Skin:</b> If enabled, "
"a fancy skin will be applied to the main window." ) );
chbGUIDesignFancy->setWhatsThis ( "<b>" + tr ( "Fancy Skin" ) + ":</b> " + tr (
"If enabled, a fancy skin will be applied to the main window." ) );
chbGUIDesignFancy->setAccessibleName ( tr ( "Fancy skin check box" ) );
// display channel levels
chbDisplayChannelLevels->setWhatsThis ( tr ( "<b>Display Channel Levels:</b> "
"If enabled, each client channel will display a pre-fader level bar." ) );
chbDisplayChannelLevels->setWhatsThis ( "<b>" + tr ( "Display Channel Levels" ) + ":</b> " +
tr ( "If enabled, each client channel will display a pre-fader level bar." ) );
chbDisplayChannelLevels->setAccessibleName ( tr ( "Display channel levels check box" ) );
// audio channels
QString strAudioChannels = tr ( "<b>Audio Channels:</b> "
QString strAudioChannels = "<b>" + tr ( "Audio Channels" ) + ":</b> " + tr (
"Select the number of audio channels to be used. There are three "
"modes available. The mono and stereo modes use one and two "
"audio channels respectively. In the mono-in/stereo-out mode "
@ -206,10 +206,10 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
"sound card puts the instrument on one input channel and the "
"microphone on the other channel. In that case the two input signals "
"can be mixed to one mono channel but the server mix can be heard in "
"stereo.<br>"
"stereo." ) + "<br>" + tr (
"Enabling the stereo streaming mode will increase the "
"stream data rate. Make sure that the current upload rate does not "
"exceed the available bandwidth of your internet connection.<br>"
"exceed the available bandwidth of your internet connection." ) + "<br>" + tr (
"In case of the stereo streaming mode, no audio channel selection "
"for the reverberation effect will be available on the main window "
"since the effect is applied on both channels in this case." );
@ -219,7 +219,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
cbxAudioChannels->setAccessibleName ( tr ( "Audio channels combo box" ) );
// audio quality
QString strAudioQuality = tr ( "<b>Audio Quality:</b> "
QString strAudioQuality = "<b>" + tr ( "Audio Quality" ) + ":</b> " + tr (
"Select the desired audio quality. A low, normal or high audio "
"quality can be selected. The higher the audio quality, the higher "
"the audio stream data rate. Make sure that the current "
@ -231,8 +231,8 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
cbxAudioQuality->setAccessibleName ( tr ( "Audio quality combo box" ) );
// new client fader level
QString strNewClientLevel = tr ( "<b>New Client Level:</b> The "
"new client level setting defines the fader level of a new "
QString strNewClientLevel = "<b>" + tr ( "New Client Level" ) + ":</b> " +
tr ( "The new client level setting defines the fader level of a new "
"connected client in percent. I.e. if a new client connects "
"to the current server, it will get the specified initial "
"fader level if no other fader level of a previous connection "
@ -243,8 +243,8 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
edtNewClientLevel->setAccessibleName ( tr ( "New client level edit box" ) );
// central server address
QString strCentrServAddr = tr ( "<b>Central Server Address:</b> The "
"central server address is the IP address or URL of the central server "
QString strCentrServAddr = "<b>" + tr ( "Central Server Address" ) + ":</b> " +
tr ( "The central server address is the IP address or URL of the central server "
"at which the server list of the connection dialog is managed. With the "
"central server address type either the local region can be selected of "
"the default central servers or a manual address can be specified." );
@ -257,15 +257,15 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
edtCentralServerAddress->setAccessibleName ( tr ( "Central server address line edit" ) );
// current connection status parameter
QString strConnStats = tr ( "<b>Current Connection Status "
"Parameter:</b> The ping time is the time required for the audio "
QString strConnStats = "<b>" + tr ( "Current Connection Status "
"Parameter" ) + ":</b> " + tr ( "The ping time is the time required for the audio "
"stream to travel from the client to the server and backwards. This "
"delay is introduced by the network. This delay should be as low as "
"20-30 ms. If this delay is higher (e.g., 50-60 ms), your distance to "
"the server is too large or your internet connection is not "
"sufficient.<br>"
"sufficient." ) + "<br>" + tr (
"The overall delay is calculated from the current ping time and the "
"delay which is introduced by the current buffer settings.<br>"
"delay which is introduced by the current buffer settings." ) + "<br>" + tr (
"The upstream rate depends on the current audio packet size and the "
"audio compression setting. Make sure that the upstream rate is not "
"higher than the available rate (check the upstream capabilities of "
@ -285,7 +285,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
// init driver button
#ifdef _WIN32
butDriverSetup->setText ( "ASIO Setup" );
butDriverSetup->setText ( tr ( "ASIO Setup" ) );
#else
// no use for this button for MacOS/Linux right now -> hide it
butDriverSetup->hide();
@ -332,23 +332,23 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
// "Audio Channels" combo box
cbxAudioChannels->clear();
cbxAudioChannels->addItem ( "Mono" ); // CC_MONO
cbxAudioChannels->addItem ( "Mono-in/Stereo-out" ); // CC_MONO_IN_STEREO_OUT
cbxAudioChannels->addItem ( "Stereo" ); // CC_STEREO
cbxAudioChannels->addItem ( tr ( "Mono" ) ); // CC_MONO
cbxAudioChannels->addItem ( tr ( "Mono-in/Stereo-out" ) ); // CC_MONO_IN_STEREO_OUT
cbxAudioChannels->addItem ( tr ( "Stereo" ) ); // CC_STEREO
cbxAudioChannels->setCurrentIndex ( static_cast<int> ( pClient->GetAudioChannels() ) );
// "Audio Quality" combo box
cbxAudioQuality->clear();
cbxAudioQuality->addItem ( "Low" ); // AQ_LOW
cbxAudioQuality->addItem ( "Normal" ); // AQ_NORMAL
cbxAudioQuality->addItem ( "High" ); // AQ_HIGH
cbxAudioQuality->addItem ( tr ( "Low" ) ); // AQ_LOW
cbxAudioQuality->addItem ( tr ( "Normal" ) ); // AQ_NORMAL
cbxAudioQuality->addItem ( tr ( "High" ) ); // AQ_HIGH
cbxAudioQuality->setCurrentIndex ( static_cast<int> ( pClient->GetAudioQuality() ) );
// central server address type combo box
cbxCentServAddrType->clear();
cbxCentServAddrType->addItem ( "Manual" ); // AT_MANUAL
cbxCentServAddrType->addItem ( "Default" ); // AT_DEFAULT
cbxCentServAddrType->addItem ( "Default (North America)" ); // AT_NORTH_AMERICA
cbxCentServAddrType->addItem ( tr ( "Manual" ) ); // AT_MANUAL
cbxCentServAddrType->addItem ( tr ( "Default" ) ); // AT_DEFAULT
cbxCentServAddrType->addItem ( tr ( "Default (North America)" ) ); // AT_NORTH_AMERICA
cbxCentServAddrType->setCurrentIndex ( static_cast<int> ( pClient->GetCentralServerAddressType() ) );
UpdateCentralServerDependency();
@ -364,7 +364,7 @@ CClientSettingsDlg::CClientSettingsDlg ( CClient* pNCliP, QWidget* parent,
rbtBufferDelayDefault->setText ( GenSndCrdBufferDelayString (
FRAME_SIZE_FACTOR_DEFAULT * SYSTEM_FRAME_SIZE_SAMPLES,
", preferred" ) );
", " + tr ( "preferred" ) ) );
rbtBufferDelaySafe->setText ( GenSndCrdBufferDelayString (
FRAME_SIZE_FACTOR_SAFE * SYSTEM_FRAME_SIZE_SAMPLES ) );
@ -454,11 +454,11 @@ void CClientSettingsDlg::UpdateJitterBufferFrame()
// update slider value and text
const int iCurNumNetBuf = pClient->GetSockBufNumFrames();
sldNetBuf->setValue ( iCurNumNetBuf );
lblNetBuf->setText ( "Size: " + QString().setNum ( iCurNumNetBuf ) );
lblNetBuf->setText ( tr ( "Size: " ) + QString().setNum ( iCurNumNetBuf ) );
const int iCurNumNetBufServer = pClient->GetServerSockBufNumFrames();
sldNetBufServer->setValue ( iCurNumNetBufServer );
lblNetBufServer->setText ( "Size: " + QString().setNum ( iCurNumNetBufServer ) );
lblNetBufServer->setText ( tr ( "Size: " ) + QString().setNum ( iCurNumNetBufServer ) );
// if auto setting is enabled, disable slider control
const bool bIsAutoSockBufSize = pClient->GetDoAutoSockBufSize();
@ -515,12 +515,12 @@ void CClientSettingsDlg::UpdateSoundCardFrame()
if ( bPreferredChecked || bDefaultChecked || bSafeChecked )
{
// default title text
grbSoundCrdBufDelay->setTitle ( "Buffer Delay" );
grbSoundCrdBufDelay->setTitle ( tr ( "Buffer Delay" ) );
}
else
{
// special title text with buffer size information added
grbSoundCrdBufDelay->setTitle ( "Buffer Delay: " +
grbSoundCrdBufDelay->setTitle ( tr ( "Buffer Delay: " ) +
GenSndCrdBufferDelayString ( iCurActualBufSize ) );
}
}
@ -633,7 +633,7 @@ void CClientSettingsDlg::OnSoundcardActivated ( int iSndDevIdx )
QString ( tr ( "The selected audio device could not be used "
"because of the following error: " ) ) + strError +
QString ( tr ( " The previous driver will be selected." ) ),
"Ok", nullptr );
tr ( "Ok" ), nullptr );
// recover old selection
cbxSoundcard->setCurrentIndex ( pClient->GetSndCrdDev() );

View file

@ -44,13 +44,13 @@ CConnectDlg::CConnectDlg ( const bool bNewShowCompleteRegList,
// Add help text to controls -----------------------------------------------
// server list
lvwServers->setWhatsThis ( tr ( "<b>Server List:</b> The server list shows "
"a list of available servers which are registered at the central "
"server. Select a server from the list and press the connect button to "
lvwServers->setWhatsThis ( "<b>" + tr ( "Server List" ) + ":</b> " + tr (
"The server list shows a list of available servers which are registered at the "
"central server. Select a server from the list and press the connect button to "
"connect to this server. Alternatively, double click a server from "
"the list to connect to it. If a server is occupied, a list of the "
"connected musicians is available by expanding the list item. "
"Permanent servers are shown in bold font.<br>"
"Permanent servers are shown in bold font." ) + "<br>" + tr (
"Note that it may take some time to retrieve the server list from the "
"central server. If no valid central server address is specified in "
"the settings, no server list will be available." ) );
@ -58,12 +58,12 @@ CConnectDlg::CConnectDlg ( const bool bNewShowCompleteRegList,
lvwServers->setAccessibleName ( tr ( "Server list view" ) );
// server address
QString strServAddrH = tr ( "<b>Server Address:</b> The IP address or URL "
"of the server running the " ) + APP_NAME + tr ( " server software "
"must be set here. An optional port number can be added after the IP "
"address or URL using a comma as a separator, e.g, <i>"
QString strServAddrH = "<b>" + tr ( "Server Address" ) + ":</b> " + tr (
"The IP address or URL of the server running the " ) + APP_NAME + tr (
" server software must be set here. An optional port number can be added after the IP "
"address or URL using a colon as a separator, e.g, "
"example.org:" ) +
QString().setNum ( LLCON_DEFAULT_PORT_NUMBER ) + tr ( "</i>. A list of "
QString().setNum ( LLCON_DEFAULT_PORT_NUMBER ) + tr ( ". A list of "
"the most recent used server IP addresses or URLs is available for "
"selection." );
@ -75,13 +75,13 @@ CConnectDlg::CConnectDlg ( const bool bNewShowCompleteRegList,
"IP address or URL. It also stores old URLs in the combo box list." ) );
// filter
edtFilter->setWhatsThis ( tr ( "<b>Filter:</b> The server list is filered "
"by the given text. Note that the filter is case insensitive." ) );
edtFilter->setWhatsThis ( "<b>" + tr ( "Filter" ) + ":</b> " + tr ( "The server "
"list is filered by the given text. Note that the filter is case insensitive." ) );
edtFilter->setAccessibleName ( tr ( "Filter edit box" ) );
// show all mucisians
chbExpandAll->setWhatsThis ( tr ( "<b>Show All Musicians:</b> If you check "
"this check box, the musicians of all servers are shown. If you "
chbExpandAll->setWhatsThis ( "<b>" + tr ( "Show All Musicians" ) + ":</b> " + tr (
"If you check this check box, the musicians of all servers are shown. If you "
"uncheck the check box, all list view items are collapsed.") );
chbExpandAll->setAccessibleName ( tr ( "Show all musicians check box" ) );

View file

@ -105,8 +105,10 @@ LED bar: lbr
#define DEFAULT_SERVER_ADDRESS "jamulus.fischvolk.de"
#define DEFAULT_SERVER_NAME "Central Server"
// download URL
#define SOFTWARE_DOWNLOAD_URL "http://sourceforge.net/projects/llcon/files"
// getting started and software manual URL
#define CLIENT_GETTING_STARTED_URL "https://github.com/corrados/jamulus/wiki/Software-Manual"
#define SERVER_GETTING_STARTED_URL "https://github.com/corrados/jamulus/wiki/Running-a-Server"
#define SOFTWARE_MANUAL_URL "https://github.com/corrados/jamulus/blob/master/src/res/homepage/manual.md"
// determining server internal address uses well-known host and port
// (Google DNS, or something else reliable)
@ -218,15 +220,12 @@ LED bar: lbr
#define SERVLIST_TIME_PERMSERV_MINUTES 1440 // minutes, 1440 = 60 min * 24 h
// registration response timeout
#define REGISTER_SERVER_TIME_OUT_MS 500 // ms
#define REGISTER_SERVER_TIME_OUT_MS 500 // ms
// defines the maximum number of times to retry server registration
// when no response is received within the timeout (before reverting
// to SERVLIST_REGIST_INTERV_MINUTES)
#define REGISTER_SERVER_RETRY_LIMIT 5 // count
// length of the moving average buffer for response time measurement
#define TIME_MOV_AV_RESPONSE_SECONDS 30 // seconds
#define REGISTER_SERVER_RETRY_LIMIT 5 // count
// Maximum length of fader tag and text message strings (Since for chat messages
@ -264,6 +263,8 @@ typedef unsigned __int64 uint64_t;
typedef unsigned __int32 uint32_t;
typedef unsigned __int16 uint16_t;
typedef unsigned __int8 uint8_t;
#elif defined ( ANDROID )
// don't redfine types for android as these ones below don't work
#else
typedef long long int64_t;
typedef int int32_t;

View file

@ -33,49 +33,54 @@
#include "settings.h"
#include "testbench.h"
#include "util.h"
#ifdef ANDROID
# include <QtAndroidExtras/QtAndroid>
#endif
// Implementation **************************************************************
int main ( int argc, char** argv )
{
QTextStream& tsConsole = *( ( new ConsoleWriterFactory() )->get() );
QString strArgument;
double rDbleArgument;
// initialize all flags and string which might be changed by command line
// arguments
#if defined( SERVER_BUNDLE ) && ( defined( __APPLE__ ) || defined( __MACOSX ) )
// if we are on MacOS and we are building a server bundle, starts Jamulus in server mode
bool bIsClient = false;
bool bIsClient = false;
#else
bool bIsClient = true;
bool bIsClient = true;
#endif
bool bUseGUI = true;
bool bStartMinimized = false;
bool bShowComplRegConnList = false;
bool bDisconnectAllClients = false;
bool bUseDoubleSystemFrameSize = true; // default is 128 samples frame size
bool bShowAnalyzerConsole = false;
bool bCentServPingServerInList = false;
bool bNoAutoJackConnect = false;
int iNumServerChannels = DEFAULT_USED_NUM_CHANNELS;
int iMaxDaysHistory = DEFAULT_DAYS_HISTORY;
int iCtrlMIDIChannel = INVALID_MIDI_CH;
quint16 iPortNumber = LLCON_DEFAULT_PORT_NUMBER;
ELicenceType eLicenceType = LT_NO_LICENCE;
QString strConnOnStartupAddress = "";
QString strIniFileName = "";
QString strHTMLStatusFileName = "";
QString strServerName = "";
QString strLoggingFileName = "";
QString strHistoryFileName = "";
QString strRecordingDirName = "";
QString strCentralServer = "";
QString strServerInfo = "";
QString strWelcomeMessage = "";
bool bUseGUI = true;
bool bStartMinimized = false;
bool bShowComplRegConnList = false;
bool bDisconnectAllClientsOnQuit = false;
bool bUseDoubleSystemFrameSize = true; // default is 128 samples frame size
bool bShowAnalyzerConsole = false;
bool bCentServPingServerInList = false;
bool bNoAutoJackConnect = false;
bool bUseTranslation = true;
bool bCustomPortNumberGiven = false;
int iNumServerChannels = DEFAULT_USED_NUM_CHANNELS;
int iMaxDaysHistory = DEFAULT_DAYS_HISTORY;
int iCtrlMIDIChannel = INVALID_MIDI_CH;
quint16 iPortNumber = LLCON_DEFAULT_PORT_NUMBER;
ELicenceType eLicenceType = LT_NO_LICENCE;
QString strConnOnStartupAddress = "";
QString strIniFileName = "";
QString strHTMLStatusFileName = "";
QString strServerName = "";
QString strLoggingFileName = "";
QString strHistoryFileName = "";
QString strRecordingDirName = "";
QString strCentralServer = "";
QString strServerInfo = "";
QString strWelcomeMessage = "";
QString strClientName = APP_NAME;
// QT docu: argv()[0] is the program name, argv()[1] is the first
// argument and argv()[argc()-1] is the last argument.
@ -195,6 +200,18 @@ int main ( int argc, char** argv )
}
// Disconnect all clients on quit --------------------------------------
if ( GetFlagArgument ( argv,
i,
"-d",
"--discononquit" ) )
{
bDisconnectAllClientsOnQuit = true;
tsConsole << "- disconnect all clients on quit" << endl;
continue;
}
// Disabling auto Jack connections -------------------------------------
if ( GetFlagArgument ( argv,
i,
@ -207,6 +224,18 @@ int main ( int argc, char** argv )
}
// Disable translations ------------------------------------------------
if ( GetFlagArgument ( argv,
i,
"-t",
"--notranslation" ) )
{
bUseTranslation = false;
tsConsole << "- translations disabled" << endl;
continue;
}
// Show all registered servers in the server list ----------------------
// Undocumented debugging command line argument: Show all registered
// servers in the server list regardless if a ping to the server is
@ -222,20 +251,6 @@ int main ( int argc, char** argv )
}
// Disconnect all clients (emergency mode) -----------------------------
// Undocumented debugging command line argument: Needed to disconnect
// an unwanted client.
if ( GetFlagArgument ( argv,
i,
"--disconnectall", // no short form
"--disconnectall" ) )
{
bDisconnectAllClients = true;
tsConsole << "- disconnect all clients" << endl;
continue;
}
// Show analyzer console -----------------------------------------------
// Undocumented debugging command line argument: Show the analyzer
// console to debug network buffer properties.
@ -293,7 +308,8 @@ int main ( int argc, char** argv )
65535,
rDbleArgument ) )
{
iPortNumber = static_cast<quint16> ( rDbleArgument );
iPortNumber = static_cast<quint16> ( rDbleArgument );
bCustomPortNumberGiven = true;
tsConsole << "- selected port number: " << iPortNumber << endl;
continue;
}
@ -327,7 +343,22 @@ int main ( int argc, char** argv )
}
// HTML status file ----------------------------------------------------
// Client Name ---------------------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
i,
"--clientname",
"--clientname",
strArgument ) )
{
strClientName = QString ( APP_NAME ) + " " + strArgument;
tsConsole << "- client name: " << strClientName << endl;
continue;
}
// Server history file name --------------------------------------------
if ( GetStringArgument ( tsConsole,
argc,
argv,
@ -473,17 +504,41 @@ int main ( int argc, char** argv )
strCentralServer = DEFAULT_SERVER_ADDRESS;
}
// adjust default port number for client: use different default port than the server since
// if the client is started before the server, the server would get a socket bind error
if ( bIsClient && !bCustomPortNumberGiven )
{
iPortNumber += 10; // increment by 10
}
// Application/GUI setup ---------------------------------------------------
// Application object
if ( !bUseGUI && !strHistoryFileName.isEmpty() )
// display a warning if in server no GUI mode and a history file is requested
if ( !bIsClient && !bUseGUI && !strHistoryFileName.isEmpty() )
{
tsConsole << "Qt5 requires a windowing system to paint a JPEG image; image will use SVG" << endl;
}
// Application/GUI setup ---------------------------------------------------
// Application object
QCoreApplication* pApp = bUseGUI
? new QApplication ( argc, argv )
: new QCoreApplication ( argc, argv );
#ifdef ANDROID
// special Android coded needed for record audio permission handling
auto result = QtAndroid::checkPermission ( QString ( "android.permission.RECORD_AUDIO" ) );
if ( result == QtAndroid::PermissionResult::Denied )
{
QtAndroid::PermissionResultMap resultHash = QtAndroid::requestPermissionsSync ( QStringList ( { "android.permission.RECORD_AUDIO" } ) );
if ( resultHash["android.permission.RECORD_AUDIO"] == QtAndroid::PermissionResult::Denied )
{
return 0;
}
}
#endif
#ifdef _WIN32
// set application priority class -> high priority
SetPriorityClass ( GetCurrentProcess(), HIGH_PRIORITY_CLASS );
@ -498,16 +553,16 @@ int main ( int argc, char** argv )
// init resources
Q_INIT_RESOURCE(resources);
// load translations
QTranslator myappTranslator;
// TODO translation loading does not yet work
// // load translations
// if ( bUseGUI )
// {
// QTranslator myappTranslator;
// bool ret = myappTranslator.load ( "src/res/translation_" + QLocale::system().name() );
//qDebug() << "translation successfully loaded: " << ret << " " << "src/res/translation_" + QLocale::system().name();
// pApp->installTranslator ( &myappTranslator );
// }
if ( bUseGUI && bUseTranslation )
{
if ( myappTranslator.load ( ":/translations/translation.qm" ) )
{
pApp->installTranslator ( &myappTranslator );
}
}
// TEST -> activate the following line to activate the test bench,
@ -523,7 +578,8 @@ int main ( int argc, char** argv )
CClient Client ( iPortNumber,
strConnOnStartupAddress,
iCtrlMIDIChannel,
bNoAutoJackConnect );
bNoAutoJackConnect,
strClientName );
// load settings from init-file
CSettings Settings ( &Client, strIniFileName );
@ -568,7 +624,7 @@ int main ( int argc, char** argv )
strWelcomeMessage,
strRecordingDirName,
bCentServPingServerInList,
bDisconnectAllClients,
bDisconnectAllClientsOnQuit,
bUseDoubleSystemFrameSize,
eLicenceType );
if ( bUseGUI )
@ -636,44 +692,45 @@ int main ( int argc, char** argv )
QString UsageArguments ( char **argv )
{
return
"Usage: " + QString ( argv[0] ) + " [option] [argument]\n"
"Usage: " + QString ( argv[0] ) + " [option] [optional argument]\n"
"\nRecognized options:\n"
" -a, --servername server name, required for HTML status (server\n"
" only)\n"
" -c, --connect connect to given server address on startup\n"
" (client only)\n"
" -e, --centralserver address of the central server (server only)\n"
" -F, --fastupdate use 64 samples frame size mode (server only)\n"
" -g, --pingservers ping servers in list to keep NAT port open\n"
" (central server only)\n"
" -h, -?, --help display this help text and exit\n"
" -i, --inifile initialization file name\n"
" -j, --nojackconnect disable auto Jack connections (client only)\n"
" -n, --nogui disable GUI\n"
" -p, --port set your local port number\n"
" -t, --notranslation disable translation (use englisch language)\n"
" -v, --version output version information and exit\n"
"\nServer only:\n"
" -a, --servername server name, required for HTML status\n"
" -d, --discononquit disconnect all clients on quit\n"
" -D, --histdays number of days of history to display\n"
" -e, --centralserver address of the central server\n"
" -F, --fastupdate use 64 samples frame size mode\n"
" -g, --pingservers ping servers in list to keep NAT port open\n"
" (central server only)\n"
" -l, --log enable logging, set file name\n"
" -L, --licence a licence must be accepted on a new\n"
" connection (server only)\n"
" -m, --htmlstatus enable HTML status file, set file name (server\n"
" only)\n"
" -n, --nogui disable GUI\n"
" connection\n"
" -m, --htmlstatus enable HTML status file, set file name\n"
" -o, --serverinfo infos of the server(s) in the format:\n"
" [name];[city];[country as QLocale ID]; ...\n"
" [server1 address];[server1 name]; ...\n"
" [server1 city]; ...\n"
" [server1 country as QLocale ID]; ...\n"
" [server2 address]; ... (server only)\n"
" -p, --port local port number (server only)\n"
" [server2 address]; ...\n"
" -R, --recording enables recording and sets directory to contain\n"
" recorded jams (server only)\n"
" recorded jams\n"
" -s, --server start server\n"
" -u, --numchannels maximum number of channels (server only)\n"
" -v, --version output version information and exit\n"
" -w, --welcomemessage welcome message on connect (server only)\n"
" -y, --history enable connection history and set file\n"
" name (server only)\n"
" -D, --histdays number of days of history to display (server only)\n"
" -z, --startminimized start minimizied (server only)\n"
" --ctrlmidich MIDI controller channel to listen (client only)"
"\nExample: " + QString ( argv[0] ) + " -l -inifile myinifile.ini\n";
" -u, --numchannels maximum number of channels\n"
" -w, --welcomemessage welcome message on connect\n"
" -y, --history enable connection history and set file name\n"
" -z, --startminimized start minimizied\n"
"\nClient only:\n"
" -c, --connect connect to given server address on startup\n"
" -j, --nojackconnect disable auto Jack connections\n"
" --ctrlmidich MIDI controller channel to listen\n"
" --clientname client name (window title and jack client name)\n"
"\nExample: " + QString ( argv[0] ) + " -s -inifile myinifile.ini\n";
}
bool GetFlagArgument ( char** argv,

View file

@ -48,8 +48,7 @@ CMultiColorLEDBar::CMultiColorLEDBar ( QWidget* parent, Qt::WindowFlags f ) :
// create LED object
vecpLEDs[iLEDIdx] = new cLED ( parent );
// add LED to layout with spacer (do not add spacer on the bottom of the
// first LED)
// add LED to layout with spacer (do not add spacer on the bottom of the first LED)
if ( iLEDIdx < NUM_STEPS_LED_BAR - 1 )
{
pLEDLayout->addStretch();
@ -77,7 +76,7 @@ CMultiColorLEDBar::CMultiColorLEDBar ( QWidget* parent, Qt::WindowFlags f ) :
// according to QScrollArea description: "When using a scroll area to display the
// contents of a custom widget, it is important to ensure that the size hint of
// the child widget is set to a suitable value."
pProgressBar->setMinimumSize ( QSize ( 1, 1 ) );
pProgressBar->setMinimumSize ( QSize ( 19, 1 ) ); // 15px + 2 * 1px + 2 * 1px = 19px
pLEDMeter->setMinimumSize ( QSize ( 1, 1 ) );
// update the meter type (using the default value of the meter type)

View file

@ -314,22 +314,31 @@ void CJamRecorder::Init( const CServer* server,
throw std::runtime_error( (recordBaseDir.absolutePath() + " is a directory but cannot be written to").toStdString() );
}
QObject::connect((const QObject *)server, SIGNAL ( Stopped() ),
this, SLOT( OnEnd() ),
Qt::ConnectionType::QueuedConnection);
QObject::connect( (const QObject *)server, SIGNAL ( Stopped() ),
this, SLOT( OnEnd() ),
Qt::ConnectionType::QueuedConnection );
QObject::connect((const QObject *)server, SIGNAL ( ClientDisconnected(int) ),
this, SLOT( OnDisconnected(int) ),
Qt::ConnectionType::QueuedConnection);
QObject::connect( (const QObject *)server, SIGNAL ( ClientDisconnected ( int ) ),
this, SLOT( OnDisconnected ( int ) ),
Qt::ConnectionType::QueuedConnection );
qRegisterMetaType<CVector<int16_t>>();
QObject::connect((const QObject *)server, SIGNAL ( AudioFrame(const int, const QString, const CHostAddress, const int, const CVector<int16_t>) ),
this, SLOT( OnFrame(const int, const QString, const CHostAddress, const int, const CVector<int16_t>) ),
Qt::ConnectionType::QueuedConnection);
QObject::connect( (const QObject *)server, SIGNAL ( AudioFrame( const int, const QString, const CHostAddress, const int, const CVector<int16_t> ) ),
this, SLOT( OnFrame (const int, const QString, const CHostAddress, const int, const CVector<int16_t> ) ),
Qt::ConnectionType::QueuedConnection );
QObject::connect( QCoreApplication::instance(),
SIGNAL ( aboutToQuit() ),
this, SLOT( OnAboutToQuit() ) );
iServerFrameSizeSamples = _iServerFrameSizeSamples;
thisThread = new QThread();
moveToThread ( thisThread );
thisThread->start();
}
/**
* @brief CJamRecorder::OnStart Start up tasks when the first client connects
*/
@ -337,7 +346,7 @@ void CJamRecorder::OnStart() {
// Ensure any previous cleaning up has been done.
OnEnd();
currentSession = new CJamSession(recordBaseDir);
currentSession = new CJamSession( recordBaseDir );
isRecording = true;
}
@ -346,7 +355,7 @@ void CJamRecorder::OnStart() {
*/
void CJamRecorder::OnEnd()
{
if (isRecording)
if ( isRecording )
{
isRecording = false;
currentSession->End();
@ -372,6 +381,13 @@ void CJamRecorder::OnEnd()
}
}
void CJamRecorder::OnAboutToQuit()
{
OnEnd();
thisThread->exit();
}
/**
* @brief CJamRecorder::SessionDirToReaper Replica of CJamRecorder::OnEnd() but using the directory contents to construct the CReaperProject object
* @param strSessionDirName
@ -409,11 +425,11 @@ void CJamRecorder::SessionDirToReaper(QString& strSessionDirName, int serverFram
*/
void CJamRecorder::OnDisconnected(int iChID)
{
if (!isRecording)
if ( !isRecording )
{
qWarning() << "CJamRecorder::OnDisconnected: channel" << iChID << "disconnected but not recording";
}
if (currentSession == nullptr)
if ( currentSession == nullptr )
{
qWarning() << "CJamRecorder::OnDisconnected: channel" << iChID << "disconnected but no currentSession";
return;
@ -434,7 +450,7 @@ void CJamRecorder::OnDisconnected(int iChID)
void CJamRecorder::OnFrame(const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector<int16_t> data)
{
// Make sure we are ready
if (!isRecording)
if ( !isRecording )
{
OnStart();
}

View file

@ -132,13 +132,16 @@ private:
QList<CJamClientConnection*> jamClientConnections;
};
class CJamRecorder : public QThread
class CJamRecorder : public QObject
{
Q_OBJECT
public:
CJamRecorder(const QString recordingDirName) :
recordBaseDir (recordingDirName), isRecording (false) {}
CJamRecorder ( const QString recordingDirName ) :
recordBaseDir ( recordingDirName ),
isRecording ( false )
{
}
void Init( const CServer* server, const int _iServerFrameSizeSamples );
@ -155,16 +158,21 @@ public slots:
*/
void OnEnd();
/**
* @brief Raised when application is stopping
*/
void OnAboutToQuit();
/**
* @brief Raised when an existing client leaves the server.
* @param iChID channel number of client
*/
void OnDisconnected(int iChID);
void OnDisconnected ( int iChID );
/**
* @brief Raised when a frame of data fis available to process
* @brief Raised when a frame of data is available to process
*/
void OnFrame(const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector<int16_t> data);
void OnFrame ( const int iChID, const QString name, const CHostAddress address, const int numAudioChannels, const CVector<int16_t> data );
private:
QDir recordBaseDir;
@ -172,6 +180,8 @@ private:
bool isRecording;
CJamSession* currentSession;
int iServerFrameSizeSamples;
QThread* thisThread;
};
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

View file

@ -0,0 +1,227 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="11in"
height="8.5in"
viewBox="0 0 279.4 215.9"
version="1.1"
id="svg8"
inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)"
sodipodi:docname="bassoon.svg"
inkscape:export-filename="/home/david/Music/Instruments/bassoon.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.70710678"
inkscape:cx="297.27202"
inkscape:cy="623.34622"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
showguides="false"
inkscape:snap-global="false"
units="in"
showborder="false"
inkscape:window-width="1366"
inkscape:window-height="749"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1">
<inkscape:grid
type="xygrid"
id="grid10" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-81.1)">
<g
id="g4796"
transform="rotate(-29.01229,73.046042,121.22491)">
<path
sodipodi:nodetypes="cssccssccc"
inkscape:connector-curvature="0"
id="path12"
d="m 46.367866,43.314518 c 0,0 1.58921,3.69544 0,8.60447 -1.83477,5.66754 -0.701362,12.20664 -0.0767,16.8528 1.21491,9.03635 0.0767,88.764332 0.0767,131.881532 h 8.070518 c 0,-46.46672 -1.75839,-123.432682 -0.0927,-131.850152 0.99457,-5.026 1.68155,-9.92635 0.0927,-16.77595 -1.16203,-5.00954 0,-5.85039 0,-9.51137 h -8.070518 z"
style="opacity:1;vector-effect:none;fill:#803300;fill-opacity:1;stroke:#000000;stroke-width:0.85982138;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<ellipse
ry="2.2148652"
rx="9.4134111"
cy="42.129814"
cx="50.668777"
id="path14"
style="opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:#333333;stroke-width:1.15859199;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
transform="rotate(-90)"
y="54.915047"
x="-201.91927"
height="3.3675961"
width="95.415215"
id="rect3725"
style="opacity:1;vector-effect:none;fill:#aa4400;fill-opacity:1;stroke:#000000;stroke-width:0.70599997;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
transform="rotate(-90)"
y="45.518219"
x="-202.79692"
height="13.548525"
width="11.935585"
id="rect3729"
style="opacity:1;vector-effect:none;fill:#b3b3b3;fill-opacity:1;stroke:#333333;stroke-width:0.63450718;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
sodipodi:nodetypes="csssssc"
inkscape:connector-curvature="0"
id="path3731"
d="m 56.613851,106.16137 c 0,0 -0.24168,-6.398058 1.02098,-9.693309 0.78131,-2.039027 2.42033,-4.09041 4.49792,-4.7625 1.79385,-0.5803 3.09877,-0.51589 4.76513,0.36611 2.53543,1.34199 2.27053,2.97449 4.53263,6.216388 1.36878,1.961631 3.71629,3.306381 6.03967,3.875121 7.39159,1.80937 18.94588,1.58749 16.02175,1.58749"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#8080ff;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
sodipodi:nodetypes="ccccc"
inkscape:connector-curvature="0"
id="path4544"
d="m 94.703667,102.81387 c 3.262067,1.23276 10.142893,-0.35753 10.142893,-0.35753 l -2.96326,4.31989 c 0,0 -3.909421,-2.97182 -7.476951,-2.05203 z"
style="opacity:1;vector-effect:none;fill:#ffcc00;fill-opacity:1;stroke:#ffcc00;stroke-width:0.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
y="81.27993"
x="47.143467"
height="6.2547331"
width="6.2547321"
id="rect4550"
style="opacity:1;vector-effect:none;fill:#b3b3b3;fill-opacity:1;stroke:#b3b3b3;stroke-width:1.83068395;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<rect
style="opacity:1;vector-effect:none;fill:#b3b3b3;fill-opacity:1;stroke:#b3b3b3;stroke-width:2.27667642;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4552"
width="10.416251"
height="5.8087401"
x="47.101879"
y="145.00291" />
<rect
y="106.55724"
x="55.002777"
height="2.5712204"
width="3.1803987"
id="rect4554"
style="opacity:1;vector-effect:none;fill:#b3b3b3;fill-opacity:1;stroke:#b3b3b3;stroke-width:0.83697993;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
r="1.7972932"
cy="69.529976"
cx="50.709747"
id="path4556"
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.60041487;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
r="1.4766704"
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.49330565;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle4580"
cx="49.806404"
cy="185.79163" />
<circle
r="1.1560477"
cy="175.58247"
cx="51.01973"
id="circle4582"
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.38619646;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<circle
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.46652839;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle4584"
cx="50.177826"
cy="171.44269"
r="1.3965148" />
<ellipse
transform="matrix(-0.9676937,-0.25212875,-0.2886886,-0.95742305,0,0)"
ry="0.56005347"
rx="1.2347096"
cy="-137.54945"
cx="-11.971257"
id="path4586"
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.33503741;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<ellipse
inkscape:transform-center-y="0.49806172"
inkscape:transform-center-x="-0.77738863"
transform="matrix(-0.98472888,-0.17409487,0.17196262,-0.98510348,0,0)"
ry="0.62549603"
rx="1.8658425"
cy="-121.87131"
cx="-73.939224"
id="path4588"
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.4565016;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<ellipse
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.32630903;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="ellipse4592"
cx="105.17155"
cy="-179.23906"
rx="1.2218931"
ry="0.536825"
transform="matrix(-0.94648072,0.32276035,-0.84098337,-0.54106097,0,0)" />
<ellipse
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.24506681;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="ellipse4594"
cx="-34.641891"
cy="-123.9094"
rx="0.93775296"
ry="0.39453751"
transform="matrix(-0.92026936,-0.39128546,-0.14973615,-0.98872599,0,0)" />
<ellipse
transform="matrix(-0.76478399,0.64428677,-0.94974484,-0.31302514,0,0)"
ry="0.39453751"
rx="0.93775296"
cy="-154.59048"
cx="125.81667"
id="ellipse4596"
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.24506681;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
inkscape:connector-curvature="0"
id="path4598"
d="M 50.207146,125.41978 50.962303,71.546673"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#aaaaff;stroke-width:0.57336599;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#aaaaff;stroke-width:0.57336599;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 49.002555,185.15586 0.755157,-53.87311"
id="path4600"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path4602"
d="m 48.334921,170.75003 0.755157,-53.8731"
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#aaaaff;stroke-width:0.57336599;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
<path
style="opacity:1;vector-effect:none;fill:none;fill-opacity:1;stroke:#aaaaff;stroke-width:0.39104879;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
d="m 47.585808,166.63427 0.543936,-34.79044"
id="path4604"
inkscape:connector-curvature="0" />
<ellipse
ry="1.2653216"
rx="0.89114428"
style="opacity:1;vector-effect:none;fill:#d5e5ff;fill-opacity:1;stroke:#d5e5ff;stroke-width:0.3547374;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="circle4608"
cx="48.125366"
cy="117.46767" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 360.9 244.9" style="enable-background:new 0 0 360.9 244.9;" xml:space="preserve">
<style type="text/css">
.st0{display:none;}
.st1{fill:url(#SVGID_1_);}
.st2{fill:url(#SVGID_2_);}
.st3{fill:none;stroke:#E6E6E6;stroke-width:1.0844;stroke-miterlimit:10;}
.st4{fill:#D8BD8D;stroke:#000000;stroke-width:0.1213;stroke-miterlimit:10;}
.st5{fill:none;stroke:#E6E6E6;stroke-width:1.0817;stroke-miterlimit:10;}
.st6{stroke:#000000;stroke-width:0.1213;stroke-miterlimit:10;}
.st7{fill:#D8BD8D;stroke:#000000;stroke-width:0.1188;stroke-miterlimit:10;}
.st8{fill:none;stroke:#000000;stroke-width:1.0817;stroke-miterlimit:10;}
.st9{fill:none;}
.st10{stroke:#000000;stroke-width:0.1456;stroke-miterlimit:10;}
.st11{stroke:#000000;stroke-width:0.1335;stroke-miterlimit:10;}
.st12{stroke:#000000;stroke-width:0.1165;stroke-miterlimit:10;}
.st13{fill:url(#SVGID_3_);stroke:#000000;stroke-width:0.1213;stroke-miterlimit:10;}
.st14{fill:url(#SVGID_4_);stroke:#000000;stroke-width:0.1896;stroke-miterlimit:10;}
</style>
<g id="Layer_1_copy" class="st0">
<image style="display:inline;overflow:visible;" width="2912" height="1976" xlink:href="bongobase.jpg" transform="matrix(0.1239 0 0 0.1239 0 0)">
</image>
</g>
<g id="Layer_1" class="st0">
<image style="display:inline;overflow:visible;" width="2912" height="1976" xlink:href="bongobase.jpg" transform="matrix(0.1239 0 0 0.1239 0 0)">
</image>
</g>
<g id="Layer_10">
</g>
<g id="Layer_6">
</g>
<g id="Layer_7">
</g>
<g id="Layer_4">
</g>
<g id="Layer_9">
</g>
<g id="Layer_8">
</g>
<g id="Layer_2_copy">
</g>
<g id="Layer_2">
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="134.371" y1="104.3634" x2="157.2497" y2="90.1605">
<stop offset="0.1132" style="stop-color:#BF6F00"/>
<stop offset="0.2222" style="stop-color:#FF7300"/>
<stop offset="0.2734" style="stop-color:#FC7C00"/>
<stop offset="0.3585" style="stop-color:#F49600"/>
<stop offset="0.4664" style="stop-color:#E7BF00"/>
<stop offset="0.4807" style="stop-color:#E5C500"/>
<stop offset="0.6614" style="stop-color:#FF7300"/>
<stop offset="0.7093" style="stop-color:#F97C01"/>
<stop offset="0.7888" style="stop-color:#EA9602"/>
<stop offset="0.8109" style="stop-color:#E59E03"/>
<stop offset="0.9573" style="stop-color:#BF6F00"/>
</linearGradient>
<path class="st1" d="M129.8,95l10.6,11.2c0,0,7.8,1.7,10.8,0s5.4-4.6,5.5-4.5c0.1,0.1-3.3-4.5-2.8-7.6c0.4-3.1,1.6-6.9,1.6-6.9
s-7,5.9-12.4,7.2c-5.4,1.4-8.8,1.5-10.4,1.1C131.1,95.2,129.8,95,129.8,95z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="161.3593" y1="113.4498" x2="194.464" y2="110.1142">
<stop offset="0.1132" style="stop-color:#BF6F00"/>
<stop offset="0.2222" style="stop-color:#FF7300"/>
<stop offset="0.2734" style="stop-color:#FC7C00"/>
<stop offset="0.3585" style="stop-color:#F49600"/>
<stop offset="0.4664" style="stop-color:#E7BF00"/>
<stop offset="0.4807" style="stop-color:#E5C500"/>
<stop offset="0.6614" style="stop-color:#FF7300"/>
<stop offset="0.7093" style="stop-color:#F97C01"/>
<stop offset="0.7888" style="stop-color:#EA9602"/>
<stop offset="0.8109" style="stop-color:#E59E03"/>
<stop offset="0.9573" style="stop-color:#BF6F00"/>
</linearGradient>
<path class="st2" d="M161.1,105c0,0,10.2,3.3,15.8,3.1s8.1-1.1,11.4-2.3c3.3-1.2,7.3-4.3,7.3-4.3l-4.8,12.5c0,0-2.8,5.2-6.5,5.6
s-10,0.4-12.6-0.6s-5.6-3.1-6.4-5S161.1,105,161.1,105L161.1,105"/>
<ellipse transform="matrix(0.9359 -0.3524 0.3524 0.9359 -20.9237 55.2979)" class="st3" cx="141.4" cy="85.1" rx="16.6" ry="8.8"/>
<path class="st4" d="M127,83.3c0,0-2.1,5.5,8.3,5.1c10.4-0.4,16.3-7.1,16.3-7.1s2.1-3.5,1.9-4.4c-0.2-0.9-2.5-2.4-2.5-2.4l3.4,1
c1.5,0.4,2.6,1.7,2.9,3.3l0.1,0.8l-1.5,2.9l-1,2.6l-2.8,2.4l-5.6,3.4l-10.1,2.8l-7-1.6l-4.2-2.7l1-3L127,83.3z"/>
<path class="st5" d="M201.3,89.7c1,9.8-8.5,16.4-21.5,18.1c-13.1,1.7-24.6-3.5-25.6-11.7c-1.1-8.2,8.7-16.2,21.8-17.9
S200.5,81.5,201.3,89.7z"/>
<path class="st6" d="M148.5,73.5l2.6,0.9c0.7,0.3-0.9,0.1,1.3,0.3l3.1,2.6c0.8,0.7,1.2,1.5-0.7,4.5c-2.9,4.5-10.5,8.8-18.9,9.5
c-6.7,0.5-9.4-2.5-9.4-2.5L126,87v-1.8c0,0,1-3,4.6-5.6c3.6-2.6,5.2-3.2,5.2-3.2s4.2-1.7,4.7-1.9
C142.3,73.8,146.3,73.5,148.5,73.5c-2.3-0.1-4.5,0.1-5.9,0.3c-11.5,2-15.7,8.1-17.3,10.8c-1.6,2.7,0,5.3,0,5.3l1.3,1.9
c0,0,5.7,5.9,17.5,0.8s14.4-10.2,13.1-14.8C156.5,75.4,150.1,73.7,148.5,73.5z"/>
<path class="st7" d="M165.7,78.3c0,0-5.5,1.8-7.1,5.8c-1.6,4-2.6,10,4.2,12.6c6.7,2.6,15.6,3.2,24-0.8c8.4-4,11.3-10.8,9.1-15.6
l5.3,5.9c0,0-0.2,5.3-2.1,8.5s-1.9,3.3-1.9,3.3s-8.4,8.3-21.2,8.1c-12.8-0.2-18-4.4-20.1-6.9c-2-2.4-1.8-3.9-2.3-4.9
c-0.5-0.9-0.3-3.4,0-4.8c0.3-1.4,5.4-7.4,5.4-7.4L165.7,78.3z"/>
<g>
<path class="st8" d="M155.7,98.5c0,0-1.3,0.5-2.3,1c-0.5,0.3-0.7,1-0.3,1.4c1.6,1.8,5.5,6,5.9,6.5c0,0,0,0.1,0.1,0.1l5.9,12.3"/>
<path class="st8" d="M153.4,93l-1.1,0.7c-0.1,0.1-0.2,0.2-0.1,0.3l1.9,5.3c0.1,0.3,0.5,0.2,0.5-0.1v0c0-0.1,0.1-0.2,0.2-0.2
l1-0.5"/>
<path class="st8" d="M146.1,90.2l1.4,1.8c0.1,0.1,0.1,0.2,0,0.3l-2.6,4.4c-0.1,0.1-0.2,0.2-0.3,0.1l-4.7-1.6
c-0.1,0-0.2-0.1-0.2-0.2l-0.4-2.4"/>
<path class="st8" d="M143.3,95.3c0,0,0.8-1.1,1.7-0.6c0.9,0.5,1.8,0.8,1.1,2.2c-0.7,1.3,0.2,4.7,0.3,5c0,0,0,0,0,0.1l4.7,8.6"/>
<path class="st8" d="M138.8,106.9l-9.1-10.8l-5.5-4.8c-0.1-0.1-0.1-0.4,0.1-0.5l0.8-0.3"/>
<path class="st8" d="M181.4,105.5l1.4,2.8c0,0.1,0.1,0.1,0.2,0.1l3.8,1.4c0.1,0,0.2,0,0.3-0.1l4.6-4.8c0.1-0.1,0.1-0.2,0-0.3
l-1.2-1.8"/>
<path class="st8" d="M185.2,108c0,0,1.1-0.7,1.9,0c0.9,0.7,1.6,2.4,0.8,4c-0.8,1.5-2.4,3.8-2.6,4.2c0,0,0,0.1,0,0.2v9.5"/>
</g>
<path d="M163.7,110.6c0,0,2.6,5.3,8.4,7.2s11.5,1,15-1.2s4.1-3.8,4.3-4.4c0.3-0.5,0.3-0.5,0.3-0.5l0.5-1.8c0,0-0.3,5.5-3.2,7.7
c-2.9,2.2-8,4.8-15.1,3.1c-7.2-1.7-9-5.6-9.4-6.7S163.7,110.6,163.7,110.6z"/>
<path class="st8" d="M183,119.5L183,119.5c-0.6,0-1,0.8-0.6,1.3l1.1,1.4c0.1,0.1,0.1,0.2,0.1,0.3c0.1,0.3,0.4,0.8,1.6,0.3l0.8-0.3
c0.4-0.2,0.6-0.7,0.4-1.1L185,119"/>
<path class="st8" d="M166.4,115.6l-2.7,1.8c0,0-1.1,0.4-1.8-0.2c-0.6-0.6,0.4-1.6,0.4-1.6l2.7-1.5"/>
<line class="st9" x1="185.2" y1="123.7" x2="185.2" y2="126.5"/>
<path d="M137.2,102c0,0,1.2,3.2,7.1,4c5.9,0.8,12-4,12-4l1.8,1.7c0,0-2.5,1.7-4,2.6c-3.5,2.1-7.7,2.5-11.4,1.8
c0,0-2.4-1.2-3.9-3.2C138,104,137.2,102,137.2,102z"/>
<path class="st8" d="M138,103.8l-1.1,1c0,0-1.3,0.3-1.4,1.2c-0.1,1,1.7,0,1.7,0l1.5-1.1"/>
<path class="st6" d="M136.3,104.5l1.8-0.8l1,0.7l1.3,2c0,0,0.4,0.5,0,0.6c-0.4,0.1-1.4,1.3-1.4,1.3h-1.6l-1.6-2.3L136.3,104.5z"/>
<path class="st6" d="M148.2,107.7l1.8-0.8l1,0.7l1.3,2c0,0,0.4,0.5,0,0.6c-0.4,0.1-1.4,1.3-1.4,1.3h-1.6l-1.6-2.3L148.2,107.7z"/>
<path class="st10" d="M183.7,121.3l2.3-0.3l0.9,1.1l0.8,2.8c0,0,0.3,0.8-0.2,0.7c-0.5,0-2,1-2,1l-1.8-0.5l-1.1-3.2L183.7,121.3z"
/>
<path class="st11" d="M162,116.4l1.9-0.9l1.1,0.8l1.4,2.3c0,0,0.5,0.6,0,0.7c-0.5,0.1-1.5,1.4-1.5,1.4h-1.7l-1.8-2.6L162,116.4z"
/>
<path class="st6" d="M179.6,95l1.8-0.8l1,0.7l1.3,2c0,0,0.4,0.5,0,0.6c-0.4,0.1-1.4,1.3-1.4,1.3h-1.6l-1.6-2.3L179.6,95z"/>
<path class="st12" d="M201.4,86.4c-0.3-3.1-4.6-6.8-8.2-9c-3.6-2.3-12.1-3-12.9-2.9c-8.1,0.4-14.2,2.9-14.2,2.9
c-5.6,2.4-11,7.4-12.3,11.3c-1.3,3.9,0.8,10.3,4.5,13.9c3.8,3.6,14.6,5.9,22.9,4.6c8.3-1.3,15.1-6,17.1-9S201.7,89.5,201.4,86.4z
M191.8,100.8c-9.9,4.8-18.3,4.9-27.5,2.2c-9.2-2.7-9.8-8.4-9.8-8.4s0.1-2.2,0-3.6c-0.1-1.4,0.6-1.9,1-3.1
c0.3-1.2,1.2-2.5,2.5-3.7s2.2-2.3,2.2-2.3s1,0.5,3.1-1.4c2.2-1.9,10.8-4.3,10.8-4.3s4.1-0.5,6.8-0.7c2.7-0.1,7,0.8,10.8,2.2
c3.8,1.4,6.6,5.1,7.4,7.1c0.9,2,0.4,0.7,1,2.7C200.5,89.7,201.7,96,191.8,100.8z"/>
<g>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="125.5923" y1="81.5875" x2="153.9333" y2="81.5875">
<stop offset="5.076142e-03" style="stop-color:#FFE794"/>
<stop offset="1" style="stop-color:#ECDEFF"/>
</linearGradient>
<path class="st13" d="M125.8,85.8c-1.2-3.9,4.1-9,11.9-11.3c7.7-2.3,15-1.1,16.1,2.8c1.2,3.9-4.1,9-11.9,11.3
C134.2,91,126.9,89.7,125.8,85.8z"/>
</g>
<g>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="156.165" y1="88.117" x2="197.6382" y2="88.117">
<stop offset="5.076142e-03" style="stop-color:#FFE794"/>
<stop offset="1" style="stop-color:#ECDEFF"/>
</linearGradient>
<path class="st14" d="M156.3,91.8c-1.2-6.8,7-14,18.4-16.1c11.4-2,21.6,1.9,22.8,8.7c1.2,6.8-7,14-18.4,16.1
C167.7,102.5,157.5,98.6,156.3,91.8z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.0.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 29 21.9" style="enable-background:new 0 0 29 21.9;" xml:space="preserve">
<style type="text/css">
.st0{display:none;fill:#DDD6CE;}
.st1{fill:url(#SVGID_1_);}
.st2{fill:url(#SVGID_2_);}
.st3{fill:none;stroke:#898989;stroke-width:0.4152;stroke-linecap:round;stroke-miterlimit:10;}
.st4{fill:url(#SVGID_3_);}
.st5{fill:url(#SVGID_4_);}
.st6{fill:url(#SVGID_5_);}
.st7{fill:url(#SVGID_6_);}
.st8{fill:#231F20;}
.st9{fill:#9B7550;}
.st10{fill:#C1AC97;}
.st11{stroke:#000000;stroke-width:0.4152;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;}
</style>
<path class="st0" d="M6.2,47.6"/>
<g>
<g id="Layer_2_1_">
<g>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-6.7753" y1="181.6339" x2="-2.3834" y2="181.6339" gradientTransform="matrix(0.9952 -9.790000e-02 -9.790000e-02 -0.9952 34.2217 190.0392)">
<stop offset="0" style="stop-color:#A5161B"/>
<stop offset="0.2559" style="stop-color:#AB1F23"/>
<stop offset="0.6801" style="stop-color:#BA3939"/>
<stop offset="0.8934" style="stop-color:#C34846"/>
</linearGradient>
<path class="st1" d="M9.7,3.2l3.4-0.7c0.1,0,0.3,0.1,0.3,0.2l1.3,13.5c0,0.1-0.1,0.2-0.2,0.3c-0.2,0.1-0.6,0.2-1.1,0.1
c-0.6-0.1-1-0.6-1.1-0.7c0,0,0,0,0-0.1c-2.2-5.3-1.6-3.5-2.2-5.3c-0.6-1.7-0.8-5-0.8-5.4c0,0,0-0.4,0-0.4l0.1-1.2
C9.4,3.4,9.5,3.3,9.7,3.2z"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="-63.7946" y1="181.6438" x2="-59.2975" y2="181.6438" gradientTransform="matrix(-0.9952 9.790000e-02 -9.790000e-02 -0.9952 -27.5088 196.0988)">
<stop offset="0" style="stop-color:#A5161B"/>
<stop offset="0.2559" style="stop-color:#AB1F23"/>
<stop offset="0.6801" style="stop-color:#BA3939"/>
<stop offset="0.8934" style="stop-color:#C34846"/>
</linearGradient>
<path class="st2" d="M17,2.4l-3.7,0.1l1.1,14c0,0,0.6,0.2,1.3-0.2c0.7-0.3,1-1,1-1c1.3-5.7,1-3.8,1.3-5.7s-0.3-5.3-0.3-5.3
l-0.3-1.6C17.3,2.5,17.2,2.4,17,2.4z"/>
</g>
</g>
<path class="st3" d="M10.6,8.5c0-0.2,0.2-0.8,0.1-0.9l-0.6-2.4"/>
<g>
<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="-32.4531" y1="177.8433" x2="-27.3982" y2="177.8433" gradientTransform="matrix(0.9815 -0.1915 -0.1915 -0.9815 67.819 182.7466)">
<stop offset="0" style="stop-color:#FF7300"/>
<stop offset="0.1217" style="stop-color:#F97C01"/>
<stop offset="0.3234" style="stop-color:#EA9602"/>
<stop offset="0.3795" style="stop-color:#E59E03"/>
<stop offset="0.8652" style="stop-color:#E5C500"/>
</linearGradient>
<path class="st4" d="M1.1,6.4L5,5.2c0.2-0.1,0.3,0.1,0.4,0.2l3,15.9c0,0.1,0,0.3-0.2,0.3c-0.3,0.1-0.7,0.3-1.2,0.2
c-0.7-0.1-1.2-0.6-1.3-0.7c0,0,0,0,0-0.1c-3.2-6-2.3-4-3.2-6c-0.9-2-1.5-5.9-1.5-6.3c0,0,0-0.5,0-0.5l0-1.4
C0.8,6.6,0.9,6.5,1.1,6.4z"/>
<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="-39.4058" y1="177.8697" x2="-34.2269" y2="177.8697" gradientTransform="matrix(-0.9815 0.1915 -0.1915 -0.9815 6.937 194.6229)">
<stop offset="0" style="stop-color:#FF7300"/>
<stop offset="0.1217" style="stop-color:#F97C01"/>
<stop offset="0.3234" style="stop-color:#EA9602"/>
<stop offset="0.3795" style="stop-color:#E59E03"/>
<stop offset="0.8652" style="stop-color:#E5C500"/>
</linearGradient>
<path class="st5" d="M9.4,4.7L5.2,5.2l2.9,16.5c0,0,0.7,0.1,1.5-0.3s1-1.2,1-1.2c0.8-6.9,0.7-4.6,0.8-6.9c0.1-2.3-1-6.3-1-6.3
L9.8,5C9.8,4.8,9.6,4.7,9.4,4.7z"/>
</g>
<g>
<g>
<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="34.4733" y1="159.7419" x2="39.6473" y2="159.7419" gradientTransform="matrix(0.996 8.950000e-02 8.950000e-02 -0.996 -30.5432 168.0219)">
<stop offset="0" style="stop-color:#FF7300"/>
<stop offset="0.1217" style="stop-color:#F97C01"/>
<stop offset="0.3234" style="stop-color:#EA9602"/>
<stop offset="0.3795" style="stop-color:#E59E03"/>
<stop offset="0.8652" style="stop-color:#E5C500"/>
</linearGradient>
<path class="st6" d="M19.3,4l4.2,0c0.2,0,0.3,0.2,0.3,0.3l-1.3,16.3c0,0.1-0.1,0.3-0.3,0.3c-0.3,0-0.8,0-1.3-0.2
c-0.7-0.3-1.1-0.9-1.1-1.1c0,0,0,0,0-0.1c-1.5-6.8-1.2-4.5-1.5-6.8c-0.3-2.2,0.6-6.6,0.7-7.1c0,0,0,0,0-0.1l0-1.3
C19,4.1,19.1,4,19.3,4z"/>
<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="-106.4789" y1="159.7513" x2="-101.3623" y2="159.7513" gradientTransform="matrix(-0.996 -8.950000e-02 8.950000e-02 -0.996 -92.3257 162.4685)">
<stop offset="0" style="stop-color:#FF7300"/>
<stop offset="0.1217" style="stop-color:#F97C01"/>
<stop offset="0.3234" style="stop-color:#EA9602"/>
<stop offset="0.3795" style="stop-color:#E59E03"/>
<stop offset="0.8652" style="stop-color:#E5C500"/>
</linearGradient>
<path class="st7" d="M28.2,4.8L23.8,4l-1.6,16.8c0,0,0.7,0.3,1.6,0.1c0.9-0.2,1.3-0.9,1.3-0.9c2.7-6.4,2-4.2,2.7-6.4
c0.7-2.2,0.5-7.1,0.5-7.1l0.2-1.3C28.5,5,28.4,4.8,28.2,4.8z"/>
</g>
<path class="st8" d="M19.6,19.2c0,0,1.1,1.1,2.3,1.2c1.2,0.1,2,0,2.6-0.4c0.6-0.3,1-0.7,1-0.7l-0.2,1c0,0-0.8,1-2.7,0.9
c-2-0.1-2.7-1.1-2.7-1.1L19.6,19.2z"/>
</g>
<path class="st8" d="M5.3,20.7c0,0,1,0.7,2.5,0.4c1.6-0.3,1.8-0.6,2.3-1.1c0.5-0.5,0.5-0.7,0.5-0.7V20c0,0.2-0.2,0.4-0.3,0.6
c-0.4,0.8-1.2,1-2.2,1.2c-0.7,0.1-1.3,0.1-1.9,0c-0.4-0.1-0.7-0.2-0.9-0.5L5.3,20.7z"/>
<path class="st9" d="M1.1,5.1l8-0.9c0.2,0,0,0,0.4,0l0.3,0.2C9.5,4.3,9.7,4.5,9.7,4.6c0,0.1,0.1,0.2,0.1,0.2l0,0
c0.1,0,0.1,0.4,0.2,0.5c0,0.1,0,0-0.1,0.1C9.5,5.9,8,7.1,5.9,7.5C3.6,8,2.8,7.8,1.7,7.6C1,7.5,0.7,7,0.6,6.7C0.5,6.4,0.3,6.3,0.4,6
l0.2-0.6C0.7,5.2,0.9,5.1,1.1,5.1z"/>
<path class="st9" d="M19.5,3l9.1,1.1c0,0,0,0.1,0.1,0.1l0.2,0.3c0,0.1-0.2,0.3-0.1,0.3l0-0.1c0.1,0,0.1,0.4,0.1,0.5
c0.1,0.4-0.1,0.5-0.2,0.6c-0.3,0.7-2.1,0.9-4,0.7c-3-0.3-2.7-0.4-3.8-0.8c-0.6-0.2-1.1-0.5-1.5-0.7c-0.3-0.2-0.7-0.8-0.5-1.1
L19.5,3C19.4,3.1,19.5,3,19.5,3z"/>
<path class="st9" d="M9.5,1.8L9.3,2.6c0,0,0.4,0.8,2.7,1s4.9-1.4,4.9-1.4c0.2-0.1,0.1-0.3,0.2-0.4c0,0,0.1-0.1,0-0.2l-0.6-0.8"/>
<ellipse transform="matrix(0.9875 -0.1577 0.1577 0.9875 -0.7287 0.8601)" class="st10" cx="5.1" cy="5" rx="4.6" ry="1.7"/>
<ellipse transform="matrix(0.9945 -0.1051 0.1051 0.9945 -7.567539e-02 1.3796)" class="st10" cx="13.1" cy="1.4" rx="3.5" ry="1.4"/>
<ellipse transform="matrix(0.1216 -0.9926 0.9926 0.1216 17.2543 27.095)" class="st10" cx="23.9" cy="3.8" rx="1.7" ry="4.6"/>
<path class="st8" d="M0.4,5.7c0,0-0.4,0.5-0.4,0.8s0.4,1.9,0.4,1.9l0.3,0.3l0.5-0.3c0,0,0.6,0.2,1.7,0.3s1.7,0,1.7,0l1,1.5l0.6-0.1
l0.6-1.8c0,0,1-0.3,1.7-0.8s1.5-1.1,1.5-1.1l0.4,0.9L10.7,7l-0.6-2.5L9.6,4.3l0.3,1c0,0-0.2,0.7-1.9,1.5C6.6,7.4,5.3,7.7,5.3,7.7
S4,7.8,2.8,7.7C0.6,7.5,0.4,6.6,0.4,6.6V5.7z"/>
<path class="st3" d="M0.3,8c0,0.3-0.1,1-0.1,1.3L0.8,10L1,11.7"/>
<polygon class="st8" points="0.6,10.6 1.2,10.6 1.3,11.3 0.7,11.5 "/>
<polygon class="st11" points="4.9,11.1 6.2,11.5 7.3,10.6 6.6,13.3 "/>
<line class="st3" x1="5.9" y1="9.3" x2="6.3" y2="12.2"/>
<path class="st8" d="M9.5,1.7c0,0-0.5,0.4-0.6,0.6c0,0.2,0.2,1.5,0.2,1.5L9.4,4l0.4-0.2c0,0,0.5,0.3,1.4,0.4
c0.9,0.1,1.4,0.1,1.4,0.1l0.8,1.1L14,5.3l0.6-1.3c0,0,0.9-0.2,1.5-0.5c0.6-0.3,1.3-0.8,1.3-0.8l0.2,0.8l0.3-0.3l-0.3-1.8
c0-0.1-0.1-0.2-0.2-0.3l-0.5-0.2l0.4,0.6c0,0-0.1,0.6-1.5,1.2c-1.1,0.4-2.3,0.6-2.3,0.6s-1.1,0.1-2.1,0c-1.9-0.3-1.9-1-1.9-1
L9.5,1.7z"/>
<g>
<polygon class="st11" points="12.7,6.5 13.9,6.9 15,6.2 14.1,8.6 "/>
<line class="st3" x1="13.6" y1="4.8" x2="13.9" y2="7.6"/>
</g>
<path class="st8" d="M14.5,15.7c-1.7,0-2.4-0.4-2.4-0.4l0.2,0.6c0,0,0.4,0.7,2.2,0.6c1.8-0.1,2.2-0.9,2.2-0.9v-0.7
c0,0-0.2,0.3-0.9,0.6C15.1,15.8,14.5,15.7,14.5,15.7z"/>
<path class="st8" d="M24.2,7.4c1.4,0,1.6,0.2,3-0.2c0.6-0.2,1.1-0.4,1.1-0.4l0.4-0.4l0.2-0.5l0-1.1c0-0.2-0.1-0.4-0.2-0.5l-0.1-0.1
l0.1,0.9c0,0,0,0.7-1.9,1c-1.5,0.2-2.9,0.1-2.9,0.1S22.4,6,21.3,5.6c-2.3-0.8-2.2-1.8-2.2-1.8L19.4,3c0,0-0.5,0.3-0.6,0.5
c-0.1,0.2-0.1,1.4-0.1,1.4l0.3,0.4l0,0c0,0,1.3,1.5,3,1.8l0,0L24.2,7.4z"/>
<polygon class="st8" points="21.5,6.6 20.7,7.6 20,7.3 19.9,6 "/>
<path class="st8" d="M25.7,7l1.7-0.3l-0.2,1.4c0-0.2,0-0.2,0-0.2l-0.9,0.2L25.7,7z"/>
<g>
<polygon class="st11" points="25.7,9.4 26.9,9.7 28,8.8 27.3,11.7 "/>
<line class="st3" x1="26.6" y1="7.3" x2="27" y2="10.6"/>
</g>
<g>
<polygon class="st11" points="18.9,7.8 19.8,8.8 21.1,8.7 19.4,10.7 "/>
<line class="st3" x1="20.5" y1="6.7" x2="19.6" y2="9.6"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,16 @@
<RCC>
<qresource prefix="/translations" lang="de">
<file alias="translation.qm">res/translation/translation_de_DE.qm</file>
</qresource>
<qresource prefix="/translations" lang="fr">
<file alias="translation.qm">res/translation/translation_fr_FR.qm</file>
</qresource>
<qresource prefix="/translations" lang="pt">
<file alias="translation.qm">res/translation/translation_pt_PT.qm</file>
</qresource>
<qresource prefix="/translations" lang="es">
<file alias="translation.qm">res/translation/translation_es_ES.qm</file>
</qresource>
<qresource prefix="/png/LEDs">
<file>res/CLEDDisabledSmall.png</file>
<file>res/CLEDGreenArrow.png</file>
@ -50,10 +62,15 @@
<file>res/instruments/instrguitarvocal.png</file>
<file>res/instruments/instrkeyboardvocal.png</file>
<file>res/instruments/bodhran.png</file>
<file>res/instruments/bassoon.png</file>
<file>res/instruments/oboe.png</file>
<file>res/instruments/harp.png</file>
<file>res/instruments/viola.png</file>
<file>res/instruments/congas.png</file>
<file>res/instruments/bongo.png</file>
</qresource>
<qresource prefix="/png/main">
<file>res/fronticon.png</file>
<file>res/logopicture.png</file>
<file>res/mainicon.png</file>
</qresource>
<qresource prefix="/png/flags">

View file

@ -231,27 +231,28 @@ CServer::CServer ( const int iNewMaxNumChan,
const QString& strNewWelcomeMessage,
const QString& strRecordingDirName,
const bool bNCentServPingServerInList,
const bool bNDisconnectAllClients,
const bool bNDisconnectAllClientsOnQuit,
const bool bNUseDoubleSystemFrameSize,
const ELicenceType eNLicenceType ) :
bUseDoubleSystemFrameSize ( bNUseDoubleSystemFrameSize ),
iMaxNumChannels ( iNewMaxNumChan ),
Socket ( this, iPortNumber ),
Logging ( iMaxDaysHistory ),
JamRecorder ( strRecordingDirName ),
bEnableRecording ( !strRecordingDirName.isEmpty() ),
bWriteStatusHTMLFile ( false ),
HighPrecisionTimer ( bNUseDoubleSystemFrameSize ),
ServerListManager ( iPortNumber,
strCentralServer,
strServerInfo,
iNewMaxNumChan,
bNCentServPingServerInList,
&ConnLessProtocol ),
bAutoRunMinimized ( false ),
strWelcomeMessage ( strNewWelcomeMessage ),
eLicenceType ( eNLicenceType ),
bDisconnectAllClients ( bNDisconnectAllClients )
bUseDoubleSystemFrameSize ( bNUseDoubleSystemFrameSize ),
iMaxNumChannels ( iNewMaxNumChan ),
Socket ( this, iPortNumber ),
Logging ( iMaxDaysHistory ),
JamRecorder ( strRecordingDirName ),
bEnableRecording ( !strRecordingDirName.isEmpty() ),
bWriteStatusHTMLFile ( false ),
HighPrecisionTimer ( bNUseDoubleSystemFrameSize ),
ServerListManager ( iPortNumber,
strCentralServer,
strServerInfo,
iNewMaxNumChan,
bNCentServPingServerInList,
&ConnLessProtocol ),
bAutoRunMinimized ( false ),
strWelcomeMessage ( strNewWelcomeMessage ),
eLicenceType ( eNLicenceType ),
bDisconnectAllClientsOnQuit ( bNDisconnectAllClientsOnQuit ),
pSignalHandler ( CSignalHandler::getSingletonP() )
{
int iOpusError;
int i;
@ -400,11 +401,10 @@ CServer::CServer ( const int iNewMaxNumChan,
QString().number( static_cast<int> ( iPortNumber ) ) );
}
// Enable jam recording (if requested)
// Enable jam recording (if requested) - kicks off the thread
if ( bEnableRecording )
{
JamRecorder.Init ( this, iServerFrameSizeSamples );
JamRecorder.start();
}
// enable all channels (for the server all channel must be enabled the
@ -468,6 +468,14 @@ CServer::CServer ( const int iNewMaxNumChan,
SIGNAL ( SvrRegStatusChanged() ),
this, SLOT ( OnSvrRegStatusChanged() ) );
QObject::connect ( QCoreApplication::instance(),
SIGNAL ( aboutToQuit() ),
this, SLOT ( OnAboutToQuit() ) );
QObject::connect ( pSignalHandler,
SIGNAL ( ShutdownSignal ( int ) ),
this, SLOT ( OnShutdown ( int ) ) );
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
connectChannelSignalsToServerSlots<MAX_NUM_CHANNELS>();
@ -804,14 +812,6 @@ void CServer::SendProtMessage ( int iChID, CVector<uint8_t> vecMessage )
void CServer::OnNewConnection ( int iChID,
CHostAddress RecHostAddr )
{
// in the special case that all clients shall be disconnected, just send the
// disconnect message and leave this function
if ( bDisconnectAllClients )
{
ConnLessProtocol.CreateCLDisconnection ( RecHostAddr );
return;
}
// on a new connection we query the network transport properties for the
// audio packets (to use the correct network block size and audio
// compression properties, etc.)
@ -910,6 +910,39 @@ void CServer::OnCLDisconnection ( CHostAddress InetAddr )
}
}
void CServer::OnAboutToQuit()
{
// if enabled, disconnect all clients on quit
if ( bDisconnectAllClientsOnQuit )
{
Mutex.lock();
{
for ( int i = 0; i < iMaxNumChannels; i++ )
{
if ( vecChannels[i].IsConnected() )
{
ConnLessProtocol.CreateCLDisconnection ( vecChannels[i].GetAddress() );
}
}
}
Mutex.unlock(); // release mutex
}
Stop();
// if server was registered at the central server, unregister on shutdown
if ( GetServerListEnabled() )
{
UnregisterSlaveServer();
}
}
void CServer::OnShutdown ( int )
{
// This should trigger OnAboutToQuit
QCoreApplication::instance()->exit();
}
void CServer::Start()
{
// only start if not already running
@ -944,7 +977,7 @@ void CServer::Stop()
void CServer::OnTimer()
{
int i, j;
int i, j, iUnused;
int iClientFrameSizeSamples;
OpusCustomDecoder* CurOpusDecoder;
OpusCustomEncoder* CurOpusEncoder;
@ -1099,11 +1132,11 @@ JitterMeas.Measure();
// OPUS decode received data stream
if ( CurOpusDecoder != nullptr )
{
opus_custom_decode ( CurOpusDecoder,
pCurCodedData,
iCeltNumCodedBytes,
&vecvecsData[i][iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i]],
iClientFrameSizeSamples );
iUnused = opus_custom_decode ( CurOpusDecoder,
pCurCodedData,
iCeltNumCodedBytes,
&vecvecsData[i][iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i]],
iClientFrameSizeSamples );
}
}
@ -1247,11 +1280,11 @@ JitterMeas.Measure();
opus_custom_encoder_ctl ( CurOpusEncoder,
OPUS_SET_BITRATE ( CalcBitRateBitsPerSecFromCodedBytes ( iCeltNumCodedBytes, iClientFrameSizeSamples ) ) );
opus_custom_encode ( CurOpusEncoder,
&vecsSendData[iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i]],
iClientFrameSizeSamples,
&vecbyCodedData[0],
iCeltNumCodedBytes );
iUnused = opus_custom_encode ( CurOpusEncoder,
&vecsSendData[iB * SYSTEM_FRAME_SIZE_SAMPLES * vecNumAudioChannels[i]],
iClientFrameSizeSamples,
&vecbyCodedData[0],
iCeltNumCodedBytes );
}
// send separate mix to current clients
@ -1277,6 +1310,8 @@ opus_custom_encoder_ctl ( CurOpusEncoder,
// does not consume any significant CPU when no client is connected.
Stop();
}
Q_UNUSED ( iUnused )
}
/// @brief Mix all audio data from all clients together.

View file

@ -36,6 +36,7 @@
#endif
#include "global.h"
#include "buffer.h"
#include "signalhandler.h"
#include "socket.h"
#include "channel.h"
#include "util.h"
@ -177,7 +178,7 @@ public:
const QString& strNewWelcomeMessage,
const QString& strRecordingDirName,
const bool bNCentServPingServerInList,
const bool bNDisconnectAllClients,
const bool bNDisconnectAllClientsOnQuit,
const bool bNUseDoubleSystemFrameSize,
const ELicenceType eNLicenceType );
@ -370,7 +371,9 @@ protected:
// messaging
QString strWelcomeMessage;
ELicenceType eLicenceType;
bool bDisconnectAllClients;
bool bDisconnectAllClientsOnQuit;
CSignalHandler* pSignalHandler;
signals:
void Started();
@ -457,6 +460,10 @@ public slots:
void OnCLDisconnection ( CHostAddress InetAddr );
void OnAboutToQuit();
void OnShutdown ( int );
#if QT_VERSION < 0x50000 // MOC does not expand macros in Qt 4, so we cannot use QT_VERSION_CHECK(5, 0, 0)
// CODE TAG: MAX_NUM_CHANNELS_TAG
// make sure we have MAX_NUM_CHANNELS connections!!!

View file

@ -42,28 +42,28 @@ CServerDlg::CServerDlg ( CServer* pNServP,
// Add help text to controls -----------------------------------------------
// client list
lvwClients->setWhatsThis ( tr ( "<b>Client List:</b> The client list "
"shows all clients which are currently connected to this server. Some "
"information about the clients like the IP address and name are given "
"for each connected client." ) );
lvwClients->setWhatsThis ( "<b>" + tr ( "Client List" ) + ":</b> " + tr (
"The client list shows all clients which are currently connected to this "
"server. Some information about the clients like the IP address and name "
"are given for each connected client." ) );
lvwClients->setAccessibleName ( tr ( "Connected clients list view" ) );
// start minimized on operating system start
chbStartOnOSStart->setWhatsThis ( tr ( "<b>Start Minimized on Operating "
"System Start:</b> If the start minimized on operating system start "
chbStartOnOSStart->setWhatsThis ( "<b>" + tr ( "Start Minimized on Operating "
"System Start" ) + ":</b> " + tr ( "If the start minimized on operating system start "
"check box is checked, the " ) + APP_NAME + tr ( " server will be "
"started when the operating system starts up and is automatically "
"minimized to a system task bar icon." ) );
// CC licence dialog switch
chbUseCCLicence->setWhatsThis ( tr ( "<b>Show Creative Commons Licence "
"Dialog:</b> If enabled, a Creative Commons BY-NC-SA 4.0 Licence "
chbUseCCLicence->setWhatsThis ( "<b>" + tr ( "Show Creative Commons Licence "
"Dialog" ) + ":</b> " + tr ( "If enabled, a Creative Commons BY-NC-SA 4.0 Licence "
"dialog is shown each time a new user connects the server." ) );
// Make My Server Public flag
chbRegisterServer->setWhatsThis ( tr ( "<b>Make My Server Public:</b> If "
"the Make My Server Public check box is checked, this server registers "
chbRegisterServer->setWhatsThis ( "<b>" + tr ( "Make My Server Public" ) + ":</b> " +
tr ( "If the Make My Server Public check box is checked, this server registers "
"itself at the central server so that all " ) + APP_NAME +
tr ( " users can see the server in the connect dialog server list and "
"connect to it. The registering of the server is renewed periodically "
@ -71,13 +71,13 @@ CServerDlg::CServerDlg ( CServer* pNServP,
"actually available." ) );
// register server status label
lblRegSvrStatus->setWhatsThis ( tr ( "<b>Register Server Status:</b> If "
"the Make My Server Public check box is checked, this will show "
lblRegSvrStatus->setWhatsThis ( "<b>" + tr ( "Register Server Status" ) + ":</b> " +
tr ( "If the Make My Server Public check box is checked, this will show "
"the success of registration with the central server." ) );
// central server address
QString strCentrServAddr = tr ( "<b>Central Server Address:</b> The "
"Central server address is the IP address or URL of the central server "
QString strCentrServAddr = "<b>" + tr ( "Central Server Address" ) + ":</b> " +
tr ( "The Central server address is the IP address or URL of the central server "
"at which this server is registered. With the central server address "
"type either the local region can be selected of the default central "
"servers or a manual address can be specified." );
@ -90,7 +90,7 @@ CServerDlg::CServerDlg ( CServer* pNServP,
edtCentralServerAddress->setAccessibleName ( tr ( "Central server address line edit" ) );
// server name
QString strServName = tr ( "<b>Server Name:</b> The server name identifies "
QString strServName = "<b>" + tr ( "Server Name" ) + ":</b> " + tr ( "The server name identifies "
"your server in the connect dialog server list at the clients. If no "
"name is given, the IP address is shown instead." );
@ -100,18 +100,17 @@ CServerDlg::CServerDlg ( CServer* pNServP,
edtServerName->setAccessibleName ( tr ( "Server name line edit" ) );
// location city
QString strLocCity = tr ( "<b>Location City:</b> The city in which this "
QString strLocCity = "<b>" + tr ( "Location City" ) + ":</b> " + tr ( "The city in which this "
"server is located can be set here. If a city name is entered, it "
"will be shown in the connect dialog server list at the clients." );
lblLocationCity->setWhatsThis ( strLocCity );
edtLocationCity->setWhatsThis ( strLocCity );
edtLocationCity->setAccessibleName ( tr (
"City where the server is located line edit" ) );
edtLocationCity->setAccessibleName ( tr ( "City where the server is located line edit" ) );
// location country
QString strLocCountry = tr ( "<b>Location country:</b> The country in "
QString strLocCountry = "<b>" + tr ( "Location country" ) + ":</b> " + tr ( "The country in "
"which this server is located can be set here. If a country is "
"entered, it will be shown in the connect dialog server list at the "
"clients." );
@ -189,9 +188,9 @@ lvwClients->setMinimumHeight ( 140 );
// central server address type combo box
cbxCentServAddrType->clear();
cbxCentServAddrType->addItem ( "Manual" ); // AT_MANUAL
cbxCentServAddrType->addItem ( "Default" ); // AT_DEFAULT
cbxCentServAddrType->addItem ( "Default (North America)" ); // AT_NORTH_AMERICA
cbxCentServAddrType->addItem ( tr ( "Manual" ) ); // AT_MANUAL
cbxCentServAddrType->addItem ( tr ( "Default" ) ); // AT_DEFAULT
cbxCentServAddrType->addItem ( tr ( "Default (North America)" ) ); // AT_NORTH_AMERICA
cbxCentServAddrType->setCurrentIndex ( static_cast<int> ( pServer->GetCentralServerAddressType() ) );
// update server name line edit
@ -273,7 +272,7 @@ lvwClients->setMinimumHeight ( 140 );
// View menu --------------------------------------------------------------
QMenu* pViewMenu = new QMenu ( "&Window", this );
QMenu* pViewMenu = new QMenu ( tr ( "&Window" ), this );
pViewMenu->addAction ( tr ( "E&xit" ), this,
SLOT ( close() ), QKeySequence ( Qt::CTRL + Qt::Key_Q ) );
@ -283,7 +282,7 @@ lvwClients->setMinimumHeight ( 140 );
pMenu = new QMenuBar ( this );
pMenu->addMenu ( pViewMenu );
pMenu->addMenu ( new CHelpMenu ( this ) );
pMenu->addMenu ( new CHelpMenu ( false, this ) );
// Now tell the layout about the menu
layout()->setMenuBar ( pMenu );
@ -343,18 +342,6 @@ lvwClients->setMinimumHeight ( 140 );
Timer.start ( GUI_CONTRL_UPDATE_TIME );
}
void CServerDlg::closeEvent ( QCloseEvent* Event )
{
// if server was registered at the central server, unregister on shutdown
if ( pServer->GetServerListEnabled() )
{
pServer->UnregisterSlaveServer();
}
// default implementation of this event handler routine
Event->accept();
}
void CServerDlg::OnStartOnOSStartStateChanged ( int value )
{
const bool bCurAutoStartMinState = ( value == Qt::Checked );
@ -447,6 +434,12 @@ void CServerDlg::OnLocationCountryActivated ( int iCntryListItem )
void CServerDlg::OnCentServAddrTypeActivated ( int iTypeIdx )
{
// if server was registered, unregister first
if ( pServer->GetServerListEnabled() )
{
pServer->UnregisterSlaveServer();
}
// apply new setting to the server and update it
pServer->SetCentralServerAddressType ( static_cast<ECSAddType> ( iTypeIdx ) );
pServer->UpdateServerList();

View file

@ -58,7 +58,6 @@ public:
protected:
virtual void changeEvent ( QEvent* pEvent );
virtual void closeEvent ( QCloseEvent* Event );
void UpdateGUIDependencies();
void UpdateSystemTrayIcon ( const bool bIsActive );

View file

@ -31,7 +31,8 @@ CServerListManager::CServerListManager ( const quint16 iNPortNum,
const int iNumChannels,
const bool bNCentServPingServerInList,
CProtocol* pNConLProt )
: iNumPredefinedServers ( 0 ),
: tsConsoleStream ( *( ( new ConsoleWriterFactory() )->get() ) ),
iNumPredefinedServers ( 0 ),
eCentralServerAddressType ( AT_MANUAL ), // must be AT_MANUAL for the "no GUI" case
bCentServPingServerInList ( bNCentServPingServerInList ),
pConnLessProtocol ( pNConLProt ),
@ -307,6 +308,8 @@ void CServerListManager::OnTimerPingServerInList()
void CServerListManager::OnTimerPollList()
{
CVector<CHostAddress> vecRemovedHostAddr;
QMutexLocker locker ( &Mutex );
// Check all list entries except of the very first one (which is the central
@ -319,6 +322,7 @@ void CServerListManager::OnTimerPollList()
if ( ServerList[iIdx].RegisterTime.elapsed() > ( SERVLIST_TIME_OUT_MINUTES * 60000 ) )
{
// remove this list entry
vecRemovedHostAddr.Add ( ServerList[iIdx].HostAddr );
ServerList.removeAt ( iIdx );
}
else
@ -327,16 +331,27 @@ void CServerListManager::OnTimerPollList()
iIdx++;
}
}
locker.unlock();
foreach ( const CHostAddress HostAddr, vecRemovedHostAddr )
{
tsConsoleStream << "Expired entry for " << HostAddr.toString() << endl;
}
}
void CServerListManager::CentralServerRegisterServer ( const CHostAddress& InetAddr,
const CHostAddress& LInetAddr,
const CServerCoreInfo& ServerInfo )
{
QMutexLocker locker ( &Mutex );
if ( bIsCentralServer && bEnabled )
{
tsConsoleStream << "Requested to register entry for "
<< InetAddr.toString() << " (" << LInetAddr.toString() << ")"
<< ": " << ServerInfo.strName << endl;
QMutexLocker locker ( &Mutex );
const int iCurServerListSize = ServerList.size();
// define invalid index used as a flag
@ -394,10 +409,13 @@ void CServerListManager::CentralServerRegisterServer ( const CHostAddress& In
void CServerListManager::CentralServerUnregisterServer ( const CHostAddress& InetAddr )
{
QMutexLocker locker ( &Mutex );
if ( bIsCentralServer && bEnabled )
{
tsConsoleStream << "Requested to unregister entry for "
<< InetAddr.toString() << endl;
QMutexLocker locker ( &Mutex );
const int iCurServerListSize = ServerList.size();
// Find the server to unregister in the list. The very first list entry
@ -587,7 +605,6 @@ void CServerListManager::SlaveServerRegisterServer ( const bool bIsRegister )
void CServerListManager::SetSvrRegStatus ( ESvrRegStatus eNSvrRegStatus )
{
// output regirstation result/update on the console
QTextStream& tsConsoleStream = *( ( new ConsoleWriterFactory() )->get() );
tsConsoleStream << "Server Registration Status update: " << svrRegStatusToString ( eNSvrRegStatus ) << endl;
// store the state and inform the GUI about the new status

View file

@ -186,6 +186,7 @@ protected:
QTimer TimerCLRegisterServerResp;
QMutex Mutex;
QTextStream& tsConsoleStream;
QList<CServerListEntry> ServerList;

183
src/signalhandler.cpp Executable file
View file

@ -0,0 +1,183 @@
/******************************************************************************\
* Copyright (c) 2020
*
* Author(s):
* Peter L Jones
*
******************************************************************************
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************
*
* This code contains some ideas derived from QCtrlSignals
* https://github.com/Skycoder42/QCtrlSignals.git
* - mostly the singleton and emitSignal code, plus some of the structure
* - virtually everything else is common knowledge across SourceForge answers
*
* BSD 3-Clause License
*
* Copyright (c) 2016, Felix Barz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
\******************************************************************************/
#include "signalhandler.h"
class CSignalHandlerSingleton : public CSignalHandler {
public:
inline CSignalHandlerSingleton() : CSignalHandler() {}
};
Q_GLOBAL_STATIC ( CSignalHandlerSingleton, singleton )
CSignalHandler::CSignalHandler() : pSignalBase ( CSignalBase::withSignalHandler ( this ) ) {}
CSignalHandler::~CSignalHandler() = default;
CSignalHandler* CSignalHandler::getSingletonP() { return singleton; }
bool CSignalHandler::emitSignal ( int sigNum )
{
return QMetaObject::invokeMethod( singleton, "ShutdownSignal", Qt::QueuedConnection, Q_ARG( int, sigNum ) );
}
#ifndef _WIN32
void CSignalHandler::OnSocketNotify( int socket )
{
int sigNum;
if ( ::read ( socket, &sigNum, sizeof ( int ) ) == sizeof ( int ) )
{
emitSignal ( sigNum );
}
}
#endif
// ----------------------------------------------------------
CSignalBase::CSignalBase ( CSignalHandler* pSignalHandler ) :
pSignalHandler ( pSignalHandler )
{
}
CSignalBase::~CSignalBase() = default;
CSignalBase* CSignalBase::withSignalHandler ( CSignalHandler* pSignalHandler )
{
#ifdef _WIN32
return new CSignalWin ( pSignalHandler );
#else
return new CSignalUnix ( pSignalHandler );
#endif
}
#ifdef _WIN32
CSignalWin::CSignalWin ( CSignalHandler* nPSignalHandler ) :
CSignalBase ( nPSignalHandler ),
lock ( QReadWriteLock::Recursive )
{
SetConsoleCtrlHandler ( signalHandler, true );
}
CSignalWin::~CSignalWin() {
SetConsoleCtrlHandler ( signalHandler, false );
}
QReadWriteLock* CSignalWin::getLock() const
{
return &lock;
}
BOOL WINAPI CSignalWin::signalHandler ( _In_ DWORD sigNum )
{
auto self = getSelf<CSignalWin>();
QReadLocker lock ( self->getLock() );
return self->pSignalHandler->emitSignal ( static_cast<int>( sigNum ) );
}
#else
int CSignalUnix::socketPair[2];
CSignalUnix::CSignalUnix ( CSignalHandler* nPSignalHandler ) :
CSignalBase ( nPSignalHandler )
{
if ( ::socketpair ( AF_UNIX, SOCK_STREAM, 0, socketPair ) == 0 )
{
socketNotifier = new QSocketNotifier ( socketPair[ 1 ], QSocketNotifier::Read );
QObject::connect ( socketNotifier, &QSocketNotifier::activated, nPSignalHandler, &CSignalHandler::OnSocketNotify );
socketNotifier->setEnabled ( true );
setSignalHandled ( SIGINT, true );
setSignalHandled ( SIGTERM, true );
}
}
CSignalUnix::~CSignalUnix() {
setSignalHandled ( SIGINT, false );
setSignalHandled ( SIGTERM, false );
}
QReadWriteLock* CSignalUnix::getLock() const { return nullptr; }
bool CSignalUnix::setSignalHandled ( int sigNum, bool state )
{
struct sigaction sa;
sigemptyset ( &sa.sa_mask );
if ( state )
{
sa.sa_handler = CSignalUnix::signalHandler;
sa.sa_flags |= SA_RESTART;
}
else
{
sa.sa_handler = SIG_DFL;
}
return ::sigaction ( sigNum, &sa, nullptr ) == 0;
}
void CSignalUnix::signalHandler ( int sigNum )
{
const auto res = ::write ( socketPair[ 0 ], &sigNum, sizeof ( int ) );
Q_UNUSED ( res );
}
#endif

177
src/signalhandler.h Executable file
View file

@ -0,0 +1,177 @@
/******************************************************************************\
* Copyright (c) 2020
*
* Author(s):
* Peter L Jones
*
******************************************************************************
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
******************************************************************************
*
* This code contains some ideas derived from QCtrlSignals
* https://github.com/Skycoder42/QCtrlSignals.git
* - mostly the singleton and emitSignal code, plus some of the structure
* - virtually everything else is common knowledge across SourceForge answers
*
* BSD 3-Clause License
*
* Copyright (c) 2016, Felix Barz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
\******************************************************************************/
#pragma once
#include <QtCore/QObject>
#include <QtCore/QScopedPointer>
#include <QtCore/QHash>
#include <QtCore/QReadWriteLock>
#include <QtCore/QSet>
#include <QGlobalStatic>
#ifdef _WIN32
#include <qt_windows.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QSemaphore>
#include <QtCore/QThread>
#include <QtCore/QDebug>
#else
#include <QSocketNotifier>
#include <QObject>
#include <QCoreApplication>
#include <csignal>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#endif
class CSignalBase;
class CSignalHandler : public QObject
{
Q_OBJECT
friend class CSignalBase;
friend class CSignalHandlerSingleton;
public:
static CSignalHandler* getSingletonP();
bool emitSignal ( int );
#ifndef _WIN32
public slots:
void OnSocketNotify ( int socket );
#endif
signals:
void ShutdownSignal ( int sigNum );
private:
QScopedPointer<CSignalBase> pSignalBase;
explicit CSignalHandler();
~CSignalHandler() override;
};
// ----------------------------------------------------------
class CSignalBase
{
Q_DISABLE_COPY ( CSignalBase )
public:
static CSignalBase* withSignalHandler ( CSignalHandler* );
virtual ~CSignalBase();
virtual QReadWriteLock* getLock() const = 0;
QSet<int> sHandledSigNums;
protected:
CSignalBase ( CSignalHandler* );
CSignalHandler* pSignalHandler;
template <typename T>
static T *getSelf()
{
return static_cast<T*>( CSignalHandler::getSingletonP()->pSignalBase.data() );
}
};
#ifdef _WIN32
class CSignalWin : public CSignalBase
{
public:
CSignalWin ( CSignalHandler* );
~CSignalWin() override;
virtual QReadWriteLock* getLock() const override;
private:
mutable QReadWriteLock lock;
static BOOL WINAPI signalHandler ( _In_ DWORD sigNum );
};
#else
class CSignalUnix : public CSignalBase
{
public:
CSignalUnix ( CSignalHandler* );
~CSignalUnix() override;
virtual QReadWriteLock* getLock() const override;
private:
QSocketNotifier* socketNotifier = nullptr;
bool setSignalHandled ( int sigNum, bool state );
static int socketPair[2];
static void signalHandler ( int sigNum );
};
#endif

View file

@ -65,11 +65,9 @@ void CSocket::Init ( const quint16 iPortNumber )
}
else
{
// Per definition use the port number plus ten for the client to make
// it possible to run server and client on the same computer. If the
// port is not available, try "NUM_SOCKET_PORTS_TO_TRY" times with
// incremented port numbers.
quint16 iClientPortIncrement = 10; // start value: port nubmer plus ten
// if the port is not available, try "NUM_SOCKET_PORTS_TO_TRY" times
// with incremented port numbers
quint16 iClientPortIncrement = 0;
bSuccess = false; // initialization for while loop
while ( !bSuccess && ( iClientPortIncrement <= NUM_SOCKET_PORTS_TO_TRY ) )

View file

@ -30,14 +30,12 @@ CSoundBase::CSoundBase ( const QString& strNewSystemDriverTechniqueName,
const bool bNewIsCallbackAudioInterface,
void (*fpNewProcessCallback) ( CVector<int16_t>& psData, void* pParg ),
void* pParg,
const int iNewCtrlMIDIChannel,
const bool bNewNoAutoJackConnect ) :
fpProcessCallback ( fpNewProcessCallback ),
pProcessCallbackArg ( pParg ), bRun ( false ),
bIsCallbackAudioInterface ( bNewIsCallbackAudioInterface ),
const int iNewCtrlMIDIChannel ) :
fpProcessCallback ( fpNewProcessCallback ),
pProcessCallbackArg ( pParg ), bRun ( false ),
bIsCallbackAudioInterface ( bNewIsCallbackAudioInterface ),
strSystemDriverTechniqueName ( strNewSystemDriverTechniqueName ),
iCtrlMIDIChannel ( iNewCtrlMIDIChannel ),
bNoAutoJackConnect ( bNewNoAutoJackConnect )
iCtrlMIDIChannel ( iNewCtrlMIDIChannel )
{
// initializations for the sound card names (default)
lNumDevs = 1;
@ -143,10 +141,10 @@ QString CSoundBase::SetDev ( const int iNewDev )
nullptr, APP_NAME, QString ( tr ( "The audio driver properties "
"have changed to a state which is incompatible to this "
"software. The selected audio device could not be used "
"because of the following error: <b>" ) ) +
"because of the following error:" ) + " <b>" ) +
strErrorMessage +
QString ( tr ( "</b><br><br>Please restart the software." ) ),
"Close", nullptr );
QString ( "</b><br><br>" + tr ( "Please restart the software." ) ),
tr ( "Close" ), nullptr );
_exit ( 0 );
}
@ -188,11 +186,11 @@ QString CSoundBase::SetDev ( const int iNewDev )
if ( !vsErrorList.isEmpty() )
{
// create error message with all details
QString sErrorMessage = tr ( "<b>No usable " ) +
QString sErrorMessage = "<b>" + tr ( "No usable " ) +
strSystemDriverTechniqueName + tr ( " audio device "
"(driver) found.</b><br><br>"
"(driver) found." ) + "</b><br><br>" + tr (
"In the following there is a list of all available drivers "
"with the associated error message:<ul>" );
"with the associated error message:" ) + "<ul>";
for ( int i = 0; i < lNumDevs; i++ )
{
@ -204,7 +202,7 @@ QString CSoundBase::SetDev ( const int iNewDev )
// to be able to access the ASIO driver setup for changing, e.g., the sample rate, we
// offer the user under Windows that we open the driver setups of all registered
// ASIO drivers
sErrorMessage = sErrorMessage + tr ( "<br/>Do you want to open the ASIO driver setups?" );
sErrorMessage = sErrorMessage + "<br/>" + tr ( "Do you want to open the ASIO driver setups?" );
if ( QMessageBox::Yes == QMessageBox::information ( nullptr, APP_NAME, sErrorMessage, QMessageBox::Yes|QMessageBox::No ) )
{

View file

@ -51,8 +51,7 @@ public:
const bool bNewIsCallbackAudioInterface,
void (*fpNewProcessCallback) ( CVector<int16_t>& psData, void* pParg ),
void* pParg,
const int iNewCtrlMIDIChannel,
const bool bNewNoAutoJackConnect );
const int iNewCtrlMIDIChannel );
virtual int Init ( const int iNewPrefMonoBufferSize );
virtual void Start();
@ -98,6 +97,27 @@ protected:
virtual void UnloadCurrentDriver() {}
QVector<QString> LoadAndInitializeFirstValidDriver ( const bool bOpenDriverSetup = false );
static void GetSelCHAndAddCH ( const int iSelCH, const int iNumInChan,
int& iSelCHOut, int& iSelAddCHOut )
{
// we have a mixed channel setup, definitions:
// - mixed channel setup only for 4 physical inputs:
// SelCH == 4: Ch 0 + Ch 2
// SelCh == 5: Ch 0 + Ch 3
// SelCh == 6: Ch 1 + Ch 2
// SelCh == 7: Ch 1 + Ch 3
if ( iSelCH >= iNumInChan )
{
iSelAddCHOut = ( ( iSelCH - iNumInChan ) % 2 ) + 2;
iSelCHOut = ( iSelCH - iNumInChan ) / 2;
}
else
{
iSelAddCHOut = -1; // set it to an invalid number
iSelCHOut = iSelCH;
}
}
// function pointer to callback function
void (*fpProcessCallback) ( CVector<int16_t>& psData, void* arg );
void* pProcessCallbackArg;
@ -121,7 +141,6 @@ protected:
bool bIsCallbackAudioInterface;
QString strSystemDriverTechniqueName;
int iCtrlMIDIChannel;
bool bNoAutoJackConnect;
CVector<int16_t> vecsAudioSndCrdStereo;

View file

@ -348,16 +348,14 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent )
{
setupUi ( this );
// set the text for the about dialog html text control
txvCredits->setOpenExternalLinks ( true );
txvCredits->setText (
"<p>" // general description of software
"<big>" + tr ( "The " ) + APP_NAME +
// general description of software
txvAbout->setText (
"<p>" + tr ( "The " ) + APP_NAME +
tr ( " software enables musicians to perform real-time jam sessions "
"over the internet. There is a " ) + APP_NAME + tr ( " "
"over the internet." ) + "<br>" + tr ( "There is a " ) + APP_NAME + tr ( " "
"server which collects the audio data from each " ) +
APP_NAME + tr ( " client, mixes the audio data and sends the mix back "
"to each client." ) + "</big></p><br>"
"to each client." ) + "</p>"
"<p><font face=\"courier\">" // GPL header text
"This program is free software; you can redistribute it and/or modify "
"it under the terms of the GNU General Public License as published by "
@ -370,27 +368,48 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent )
"License along with his program; if not, write to the Free Software "
"Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 "
"USA"
"</font></p><br>"
"<p><b>" + APP_NAME + // libraries used by this compilation
"</font></p>" );
// libraries used by this compilation
txvLibraries->setText ( APP_NAME +
tr ( " uses the following libraries, resources or code snippets:" ) +
"</b></p>"
"<ul>"
"<li>Qt cross-platform application framework: "
"<i><a href=""http://www.qt.io"">http://www.qt.io</a></i></li>"
"<li>Opus Interactive Audio Codec: "
"<i><a href=""http://www.opus-codec.org"">http://www.opus-codec.org</a></i></li>"
"<li>Audio reverberation code: by Perry R. Cook and Gary P. Scavone, "
"1995 - 2004 (taken from "
"<i><a href=""http://ccrma.stanford.edu/software/stk"">"
"The Synthesis ToolKit in C++ (STK)</a></i>)</li>"
"<li>Some pixmaps are from the Open Clip Art Library (OCAL): "
"<i><a href=""http://openclipart.org"">http://openclipart.org</a></i></li>"
"<li>Country flag icons from Mark James: "
"<i><a href=""http://www.famfamfam.com"">http://www.famfamfam.com</a></i></li>"
"</ul>"
"We would like to acknowledge the contributors listed in the "
"<a href=""https://github.com/corrados/jamulus/graphs/contributors"">Github Contributors list</a>"
"</center><br>");
"<br><p>" + tr ( "Qt cross-platform application framework" ) +
", <i><a href=""http://www.qt.io"">http://www.qt.io</a></i></p>"
"<p>Opus Interactive Audio Codec"
", <i><a href=""http://www.opus-codec.org"">http://www.opus-codec.org</a></i></p>"
"<p>" + tr ( "Audio reverberation code by Perry R. Cook and Gary P. Scavone" ) +
", 1995 - 2004, <i><a href=""http://ccrma.stanford.edu/software/stk"">"
"The Synthesis ToolKit in C++ (STK)</a></i></p>"
"<p>" + tr ( "Some pixmaps are from the" ) + " Open Clip Art Library (OCAL), "
"<i><a href=""http://openclipart.org"">http://openclipart.org</a></i></p>"
"<p>" + tr ( "Country flag icons from Mark James" ) +
", <i><a href=""http://www.famfamfam.com"">http://www.famfamfam.com</a></i></p>" );
// contributors list
txvContributors->setText (
"<p>Peter L. Jones (<a href=""https://github.com/pljones"">pljones</a>)</p>"
"<p>Jonathan Baker-Bates (<a href=""https://github.com/gilgongo"">gilgongo</a>)</p>"
"<p>Daniele Masato (<a href=""https://github.com/doloopuntil"">doloopuntil</a>)</p>"
"<p>Simon Tomlinson (<a href=""https://github.com/sthenos"">sthenos</a>)</p>"
"<p>Marc jr. Landolt (<a href=""https://github.com/braindef"">braindef</a>)</p>"
"<p>Olivier Humbert (<a href=""https://github.com/trebmuh"">trebmuh</a>)</p>"
"<p>mirabilos (<a href=""https://github.com/mirabilos"">mirabilos</a>)</p>"
"<p>newlaurent62 (<a href=""https://github.com/newlaurent62"">newlaurent62</a>)</p>"
"<p>Emlyn Bolton (<a href=""https://github.com/emlynmac"">emlynmac</a>)</p>"
"<p>Jos van den Oever (<a href=""https://github.com/vandenoever"">vandenoever</a>)</p>"
"<p>Tormod Volden (<a href=""https://github.com/tormodvolden"">tormodvolden</a>)</p>"
"<p>Stanislas Michalak (<a href=""https://github.com/stanislas-m"">stanislas-m</a>)</p>"
"<br>" + tr ( "For details on the contributions check out the " ) +
"<a href=""https://github.com/corrados/jamulus/graphs/contributors"">" + tr ( "Github Contributors list" ) + "</a>." );
// translators
txvTranslation->setText (
"<p><b>Spanish</b></p>"
"<p>Daryl Hanlon (<a href=""https://github.com/ignotus666"">ignotus666</a>)</p>"
"<p><b>French</b></p>"
"<p>Olivier Humbert (<a href=""https://github.com/trebmuh"">trebmuh</a>)</p>"
"<p><b>Portuguese</b></p>"
"<p>Miguel de Matos (<a href=""https://github.com/Snayler"">Snayler</a>)</p>" );
// set version number in about dialog
lblVersion->setText ( GetVersionAndNameStr() );
@ -406,7 +425,7 @@ QString CAboutDlg::GetVersionAndNameStr ( const bool bWithHtml )
// name, short description and GPL hint
if ( bWithHtml )
{
strVersionText += "<center><b>";
strVersionText += "<b>";
}
else
{
@ -424,24 +443,14 @@ QString CAboutDlg::GetVersionAndNameStr ( const bool bWithHtml )
strVersionText += "\n *** ";
}
strVersionText += tr ( "Internet Jam Session Software" );
if ( bWithHtml )
{
strVersionText += "<br>";
}
else
if ( !bWithHtml )
{
strVersionText += tr ( "Internet Jam Session Software" );
strVersionText += "\n *** ";
}
strVersionText += tr ( "Under the GNU General Public License (GPL)" );
if ( bWithHtml )
{
strVersionText += "</center>";
}
return strVersionText;
}
@ -462,9 +471,9 @@ CLicenceDlg::CLicenceDlg ( QWidget* parent ) : QDialog ( parent )
QVBoxLayout* pLayout = new QVBoxLayout ( this );
QHBoxLayout* pSubLayout = new QHBoxLayout;
QTextBrowser* txvLicence = new QTextBrowser ( this );
QCheckBox* chbAgree = new QCheckBox ( "I &agree to the above licence terms", this );
butAccept = new QPushButton ( "Accept", this );
QPushButton* butDecline = new QPushButton ( "Decline", this );
QCheckBox* chbAgree = new QCheckBox ( tr ( "I &agree to the above licence terms" ), this );
butAccept = new QPushButton ( tr ( "Accept" ), this );
QPushButton* butDecline = new QPushButton ( tr ( "Decline" ), this );
pSubLayout->addStretch();
pSubLayout->addWidget ( chbAgree );
@ -486,10 +495,10 @@ CLicenceDlg::CLicenceDlg ( QWidget* parent ) : QDialog ( parent )
"You agree that all data, sounds, or other works transmitted to this server "
"are owned and created by you or your licensors, and that you are making these "
"data, sounds or other works available via the following Creative Commons "
"License (for more information on this license, see "
"License (for more information on this license, see " ) +
"<i><a href=""http://creativecommons.org/licenses/by-nc-sa/4.0"">"
"http://creativecommons.org/licenses/by-nc-sa/4.0</a></i>):" ) + "</big></p>" +
"<h3>Attribution-NonCommercial-ShareAlike 4.0</h3>" +
"http://creativecommons.org/licenses/by-nc-sa/4.0</a></i>):</big></p>"
"<h3>Attribution-NonCommercial-ShareAlike 4.0</h3>"
"<p>" + tr ( "You are free to:" ) +
"<ul>"
"<li><b>" + tr ( "Share" ) + "</b> - " +
@ -539,22 +548,22 @@ CMusProfDlg::CMusProfDlg ( CClient* pNCliP,
- label with combo box for skill level
- OK button
*/
setWindowTitle ( "Musician Profile" );
setWindowTitle ( tr ( "Musician Profile" ) );
setWindowIcon ( QIcon ( QString::fromUtf8 ( ":/png/main/res/fronticon.png" ) ) );
QVBoxLayout* pLayout = new QVBoxLayout ( this );
QHBoxLayout* pButSubLayout = new QHBoxLayout;
QLabel* plblAlias = new QLabel ( "Alias/Name", this );
QLabel* plblAlias = new QLabel ( tr ( "Alias/Name" ), this );
pedtAlias = new QLineEdit ( this );
QLabel* plblInstrument = new QLabel ( "Instrument", this );
QLabel* plblInstrument = new QLabel ( tr ( "Instrument" ), this );
pcbxInstrument = new QComboBox ( this );
QLabel* plblCountry = new QLabel ( "Country", this );
QLabel* plblCountry = new QLabel ( tr ( "Country" ), this );
pcbxCountry = new QComboBox ( this );
QLabel* plblCity = new QLabel ( "City", this );
QLabel* plblCity = new QLabel ( tr ( "City" ), this );
pedtCity = new QLineEdit ( this );
QLabel* plblSkill = new QLabel ( "Skill", this );
QLabel* plblSkill = new QLabel ( tr ( "Skill" ), this );
pcbxSkill = new QComboBox ( this );
QPushButton* butClose = new QPushButton ( "&Close", this );
QPushButton* butClose = new QPushButton ( tr ( "&Close" ), this );
QGridLayout* pGridLayout = new QGridLayout;
plblAlias->setSizePolicy ( QSizePolicy::Minimum, QSizePolicy::Expanding );
@ -592,13 +601,31 @@ CMusProfDlg::CMusProfDlg ( CClient* pNCliP,
// add an entry for all known instruments
for ( int iCurInst = 0; iCurInst < CInstPictures::GetNumAvailableInst(); iCurInst++ )
{
// create a combo box item with text and image
pcbxInstrument->addItem (
QIcon ( CInstPictures::GetResourceReference ( iCurInst ) ),
CInstPictures::GetName ( iCurInst ),
iCurInst );
// create a combo box item with text, image and background color
QColor InstrColor;
pcbxInstrument->addItem ( QIcon ( CInstPictures::GetResourceReference ( iCurInst ) ),
CInstPictures::GetName ( iCurInst ),
iCurInst );
switch ( CInstPictures::GetCategory ( iCurInst ) )
{
case CInstPictures::IC_OTHER_INSTRUMENT: InstrColor = QColor ( Qt::blue ); break;
case CInstPictures::IC_WIND_INSTRUMENT: InstrColor = QColor ( Qt::green ); break;
case CInstPictures::IC_STRING_INSTRUMENT: InstrColor = QColor ( Qt::red ); break;
case CInstPictures::IC_PLUCKING_INSTRUMENT: InstrColor = QColor ( Qt::cyan ); break;
case CInstPictures::IC_PERCUSSION_INSTRUMENT: InstrColor = QColor ( Qt::white ); break;
case CInstPictures::IC_KEYBOARD_INSTRUMENT: InstrColor = QColor ( Qt::yellow ); break;
case CInstPictures::IC_MULTIPLE_INSTRUMENT: InstrColor = QColor ( Qt::black ); break;
}
InstrColor.setAlpha ( 10 );
pcbxInstrument->setItemData ( iCurInst, InstrColor, Qt::BackgroundRole );
}
// sort the items in alphabetical order
pcbxInstrument->model()->sort ( 0 );
// Country flag icons combo box --------------------------------------------
// add an entry for all known country flags
@ -609,8 +636,7 @@ CMusProfDlg::CMusProfDlg ( CClient* pNCliP,
if ( static_cast<QLocale::Country> ( iCurCntry ) != QLocale::AnyCountry )
{
// get current country enum
QLocale::Country eCountry =
static_cast<QLocale::Country> ( iCurCntry );
QLocale::Country eCountry = static_cast<QLocale::Country> ( iCurCntry );
// try to load icon from resource file name
QIcon CurFlagIcon;
@ -635,7 +661,7 @@ CMusProfDlg::CMusProfDlg ( CClient* pNCliP,
FlagNoneIcon.addFile ( ":/png/flags/res/flags/flagnone.png" );
pcbxCountry->insertItem ( 0,
FlagNoneIcon,
"None",
tr ( "None" ),
static_cast<int> ( QLocale::AnyCountry ) );
@ -647,35 +673,35 @@ CMusProfDlg::CMusProfDlg ( CClient* pNCliP,
RGBCOL_G_SL_NOT_SET,
RGBCOL_B_SL_NOT_SET ) );
pcbxSkill->addItem ( QIcon ( SLPixmap ), "None", SL_NOT_SET );
pcbxSkill->addItem ( QIcon ( SLPixmap ), tr ( "None" ), SL_NOT_SET );
SLPixmap.fill ( QColor::fromRgb ( RGBCOL_R_SL_BEGINNER,
RGBCOL_G_SL_BEGINNER,
RGBCOL_B_SL_BEGINNER ) );
pcbxSkill->addItem ( QIcon ( SLPixmap ), "Beginner", SL_BEGINNER );
pcbxSkill->addItem ( QIcon ( SLPixmap ), tr ( "Beginner" ), SL_BEGINNER );
SLPixmap.fill ( QColor::fromRgb ( RGBCOL_R_SL_INTERMEDIATE,
RGBCOL_G_SL_INTERMEDIATE,
RGBCOL_B_SL_INTERMEDIATE ) );
pcbxSkill->addItem ( QIcon ( SLPixmap ), "Intermediate", SL_INTERMEDIATE );
pcbxSkill->addItem ( QIcon ( SLPixmap ), tr ( "Intermediate" ), SL_INTERMEDIATE );
SLPixmap.fill ( QColor::fromRgb ( RGBCOL_R_SL_SL_PROFESSIONAL,
RGBCOL_G_SL_SL_PROFESSIONAL,
RGBCOL_B_SL_SL_PROFESSIONAL ) );
pcbxSkill->addItem ( QIcon ( SLPixmap ), "Expert", SL_PROFESSIONAL );
pcbxSkill->addItem ( QIcon ( SLPixmap ), tr ( "Expert" ), SL_PROFESSIONAL );
// Add help text to controls -----------------------------------------------
// fader tag
QString strFaderTag = tr ( "<b>Musician Profile:</b> Set your name "
"or an alias here so that the other musicians you want to play with "
QString strFaderTag = "<b>" + tr ( "Musician Profile" ) + ":</b> " + tr (
"Set your name or an alias here so that the other musicians you want to play with "
"know who you are. Additionally you may set an instrument picture of "
"the instrument you play and a flag of the country you are living. "
"The city you live in and the skill level of playing your instrument "
"may also be added.\n"
"may also be added." ) + "<br>" + tr (
"What you set here will appear at your fader on the mixer board when "
"you are connected to a " ) + APP_NAME + tr ( " server. This tag will "
"also show up at each client which is connected to the same server as "
@ -806,16 +832,20 @@ void CMusProfDlg::OnSkillActivated ( int iCntryListItem )
// Help menu -------------------------------------------------------------------
CHelpMenu::CHelpMenu ( QWidget* parent ) : QMenu ( "&?", parent )
CHelpMenu::CHelpMenu ( const bool bIsClient, QWidget* parent ) : QMenu ( tr ( "&Help" ), parent )
{
// standard help menu consists of about and what's this help
addAction ( tr ( "What's &This" ), this,
SLOT ( OnHelpWhatsThis() ), QKeySequence ( Qt::SHIFT + Qt::Key_F1 ) );
if ( bIsClient )
{
addAction ( tr ( "Getting &Started..." ), this, SLOT ( OnHelpClientGetStarted() ) );
addAction ( tr ( "Software &Manual..." ), this, SLOT ( OnHelpSoftwareMan() ) );
}
else
{
addAction ( tr ( "Getting &Started..." ), this, SLOT ( OnHelpServerGetStarted() ) );
}
addSeparator();
addAction ( tr ( "&Download Link..." ), this,
SLOT ( OnHelpDownloadLink() ) );
addAction ( tr ( "What's &This" ), this, SLOT ( OnHelpWhatsThis() ), QKeySequence ( Qt::SHIFT + Qt::Key_F1 ) );
addSeparator();
addAction ( tr ( "&About..." ), this, SLOT ( OnHelpAbout() ) );
}
@ -935,35 +965,41 @@ CVector<CInstPictures::CInstPictProps>& CInstPictures::GetTable()
// instrument picture data base initialization
// NOTE: Do not change the order of any instrument in the future!
// NOTE: The very first entry is the "not used" element per definition.
vecDataBase.Add ( CInstPictProps ( "None", ":/png/instr/res/instruments/instrnone.png", IC_OTHER_INSTRUMENT ) ); // special first element
vecDataBase.Add ( CInstPictProps ( "Drum Set", ":/png/instr/res/instruments/instrdrumset.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Djembe", ":/png/instr/res/instruments/instrdjembe.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Electric Guitar", ":/png/instr/res/instruments/instreguitar.png", IC_PLUCKING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Acoustic Guitar", ":/png/instr/res/instruments/instraguitar.png", IC_PLUCKING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Bass Guitar", ":/png/instr/res/instruments/instrbassguitar.png", IC_PLUCKING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Keyboard", ":/png/instr/res/instruments/instrkeyboard.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Synthesizer", ":/png/instr/res/instruments/instrsynthesizer.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Grand Piano", ":/png/instr/res/instruments/instrgrandpiano.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Accordion", ":/png/instr/res/instruments/instraccordeon.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Vocal", ":/png/instr/res/instruments/instrvocal.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Microphone", ":/png/instr/res/instruments/instrmicrophone.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Harmonica", ":/png/instr/res/instruments/instrharmonica.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Trumpet", ":/png/instr/res/instruments/instrtrumpet.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Trombone", ":/png/instr/res/instruments/instrtrombone.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "French Horn", ":/png/instr/res/instruments/instrfrenchhorn.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Tuba", ":/png/instr/res/instruments/instrtuba.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Saxophone", ":/png/instr/res/instruments/instrsaxophone.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Clarinet", ":/png/instr/res/instruments/instrclarinet.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Flute", ":/png/instr/res/instruments/instrflute.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Violin", ":/png/instr/res/instruments/instrviolin.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Cello", ":/png/instr/res/instruments/instrcello.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Double Bass", ":/png/instr/res/instruments/instrdoublebass.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Recorder", ":/png/instr/res/instruments/instrrecorder.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Streamer", ":/png/instr/res/instruments/instrstreamer.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Listener", ":/png/instr/res/instruments/instrlistener.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Guitar+Vocal", ":/png/instr/res/instruments/instrguitarvocal.png", IC_MULTIPLE_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Keyboard+Vocal", ":/png/instr/res/instruments/instrkeyboardvocal.png", IC_MULTIPLE_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( "Bodhran", ":/png/instr/res/instruments/bodhran.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "None" ), ":/png/instr/res/instruments/instrnone.png", IC_OTHER_INSTRUMENT ) ); // special first element
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Drum Set" ), ":/png/instr/res/instruments/instrdrumset.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Djembe" ), ":/png/instr/res/instruments/instrdjembe.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Electric Guitar" ), ":/png/instr/res/instruments/instreguitar.png", IC_PLUCKING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Acoustic Guitar" ), ":/png/instr/res/instruments/instraguitar.png", IC_PLUCKING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Bass Guitar" ), ":/png/instr/res/instruments/instrbassguitar.png", IC_PLUCKING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Keyboard" ), ":/png/instr/res/instruments/instrkeyboard.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Synthesizer" ), ":/png/instr/res/instruments/instrsynthesizer.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Grand Piano" ), ":/png/instr/res/instruments/instrgrandpiano.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Accordion" ), ":/png/instr/res/instruments/instraccordeon.png", IC_KEYBOARD_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Vocal" ), ":/png/instr/res/instruments/instrvocal.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Microphone" ), ":/png/instr/res/instruments/instrmicrophone.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Harmonica" ), ":/png/instr/res/instruments/instrharmonica.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Trumpet" ), ":/png/instr/res/instruments/instrtrumpet.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Trombone" ), ":/png/instr/res/instruments/instrtrombone.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "French Horn" ), ":/png/instr/res/instruments/instrfrenchhorn.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Tuba" ), ":/png/instr/res/instruments/instrtuba.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Saxophone" ), ":/png/instr/res/instruments/instrsaxophone.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Clarinet" ), ":/png/instr/res/instruments/instrclarinet.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Flute" ), ":/png/instr/res/instruments/instrflute.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Violin" ), ":/png/instr/res/instruments/instrviolin.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Cello" ), ":/png/instr/res/instruments/instrcello.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Double Bass" ), ":/png/instr/res/instruments/instrdoublebass.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Recorder" ), ":/png/instr/res/instruments/instrrecorder.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Streamer" ), ":/png/instr/res/instruments/instrstreamer.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Listener" ), ":/png/instr/res/instruments/instrlistener.png", IC_OTHER_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Guitar+Vocal" ), ":/png/instr/res/instruments/instrguitarvocal.png", IC_MULTIPLE_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Keyboard+Vocal" ), ":/png/instr/res/instruments/instrkeyboardvocal.png", IC_MULTIPLE_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Bodhran" ), ":/png/instr/res/instruments/bodhran.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Bassoon" ), ":/png/instr/res/instruments/bassoon.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Oboe" ), ":/png/instr/res/instruments/oboe.png", IC_WIND_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Harp" ), ":/png/instr/res/instruments/harp.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Viola" ), ":/png/instr/res/instruments/viola.png", IC_STRING_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Congas" ), ":/png/instr/res/instruments/congas.png", IC_PERCUSSION_INSTRUMENT ) );
vecDataBase.Add ( CInstPictProps ( QCoreApplication::translate ( "CMusProfDlg", "Bongo" ), ":/png/instr/res/instruments/bongo.png", IC_PERCUSSION_INSTRUMENT ) );
// now the table is initialized
TableIsInitialized = true;
@ -1006,6 +1042,20 @@ QString CInstPictures::GetName ( const int iInstrument )
}
}
CInstPictures::EInstCategory CInstPictures::GetCategory ( const int iInstrument )
{
// range check
if ( IsInstIndexInRange ( iInstrument ) )
{
// return the name of the instrument
return GetTable()[iInstrument].eInstCategory;
}
else
{
return IC_OTHER_INSTRUMENT;
}
}
// Locale management class -----------------------------------------------------
QString CLocale::GetCountryFlagIconsResourceReference ( const QLocale::Country eCountry )

View file

@ -473,16 +473,17 @@ class CHelpMenu : public QMenu
Q_OBJECT
public:
CHelpMenu ( QWidget* parent = nullptr );
CHelpMenu ( const bool bIsClient, QWidget* parent = nullptr );
protected:
CAboutDlg AboutDlg;
public slots:
void OnHelpWhatsThis() { QWhatsThis::enterWhatsThisMode(); }
void OnHelpAbout() { AboutDlg.exec(); }
void OnHelpDownloadLink()
{ QDesktopServices::openUrl ( QUrl ( SOFTWARE_DOWNLOAD_URL ) ); }
void OnHelpWhatsThis() { QWhatsThis::enterWhatsThisMode(); }
void OnHelpAbout() { AboutDlg.exec(); }
void OnHelpClientGetStarted() { QDesktopServices::openUrl ( QUrl ( CLIENT_GETTING_STARTED_URL ) ); }
void OnHelpServerGetStarted() { QDesktopServices::openUrl ( QUrl ( SERVER_GETTING_STARTED_URL ) ); }
void OnHelpSoftwareMan() { QDesktopServices::openUrl ( QUrl ( SOFTWARE_MANUAL_URL ) ); }
};
@ -589,28 +590,28 @@ inline QString svrRegStatusToString ( ESvrRegStatus eSvrRegStatus )
switch ( eSvrRegStatus )
{
case SRS_UNREGISTERED:
return "Unregistered";
return QCoreApplication::translate ( "CServerDlg", "Unregistered" );
case SRS_BAD_ADDRESS:
return "Bad address";
return QCoreApplication::translate ( "CServerDlg", "Bad address" );
case SRS_REQUESTED:
return "Registration requested";
return QCoreApplication::translate ( "CServerDlg", "Registration requested" );
case SRS_TIME_OUT:
return "Registration failed";
return QCoreApplication::translate ( "CServerDlg", "Registration failed" );
case SRS_UNKNOWN_RESP:
return "Check server version";
return QCoreApplication::translate ( "CServerDlg", "Check server version" );
case SRS_REGISTERED:
return "Registered";
return QCoreApplication::translate ( "CServerDlg", "Registered" );
case SRS_CENTRAL_SVR_FULL:
return "Central Server full";
return QCoreApplication::translate ( "CServerDlg", "Central Server full" );
}
return QString ( "Unknown value " ).append ( eSvrRegStatus );
return QString ( QCoreApplication::translate ( "CServerDlg", "Unknown value " ) ).append ( eSvrRegStatus );
}
@ -657,7 +658,7 @@ public:
void Update ( const CVector<short>& vecsAudio );
double MicLeveldBLeft() { return CalcLogResult ( dCurLevelL ); }
double MicLeveldBRight() { return CalcLogResult ( dCurLevelR ); }
static double CalcLogResult ( const double& dLinearLevel );
static double CalcLogResult ( const double& dLinearLevel );
void Reset()
{
@ -761,9 +762,10 @@ public:
static int GetNotUsedInstrument() { return 0; }
static bool IsNotUsedInstrument ( const int iInstrument ) { return iInstrument == 0; }
static int GetNumAvailableInst() { return GetTable().Size(); }
static QString GetResourceReference ( const int iInstrument );
static QString GetName ( const int iInstrument );
static int GetNumAvailableInst() { return GetTable().Size(); }
static QString GetResourceReference ( const int iInstrument );
static QString GetName ( const int iInstrument );
static EInstCategory GetCategory ( const int iInstrument );
// TODO make use of instrument category (not yet implemented)

View file

@ -483,11 +483,12 @@ void CSound::Stop()
}
}
CSound::CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect) :
CSoundBase ( "ASIO", true, fpNewCallback, arg, iCtrlMIDIChannel, bNoAutoJackConnect ),
CSound::CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool ,
const QString& ) :
CSoundBase ( "ASIO", true, fpNewCallback, arg, iCtrlMIDIChannel ),
lNumInChan ( 0 ),
lNumInChanPlusAddChan ( 0 ),
lNumOutChan ( 0 ),
@ -514,9 +515,9 @@ CSound::CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* a
// in case we do not have a driver available, throw error
if ( lNumDevs == 0 )
{
throw CGenErr ( tr ( "<b>No ASIO audio device (driver) found.</b><br><br>"
"The " ) + APP_NAME + tr ( " software requires the low latency audio "
"interface <b>ASIO</b> to work properly. This is no standard "
throw CGenErr ( "<b>" + tr ( "No ASIO audio device (driver) found." ) + "</b><br><br>" +
tr ( "The " ) + APP_NAME + tr ( " software requires the low latency audio "
"interface ASIO to work properly. This is no standard "
"Windows audio interface and therefore a special audio driver is "
"required. Either your sound card has a native ASIO driver (which "
"is recommended) or you might want to use alternative drivers like "

View file

@ -46,10 +46,12 @@
class CSound : public CSoundBase
{
public:
CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool bNoAutoJackConnect );
CSound ( void (*fpNewCallback) ( CVector<int16_t>& psData, void* arg ),
void* arg,
const int iCtrlMIDIChannel,
const bool ,
const QString& );
virtual ~CSound() { UnloadCurrentDriver(); }
virtual int Init ( const int iNewPrefMonoBufferSize );
@ -85,28 +87,6 @@ protected:
bool CheckSampleTypeSupportedForCHMixing ( const ASIOSampleType SamType );
void ResetChannelMapping();
static void GetSelCHAndAddCH ( const int iSelCH, const int iNumInChan,
int& iSelCHOut, int& iSelAddCHOut )
{
// we have a mixed channel setup
// definitions:
// - mixed channel setup only for 4 physical inputs:
// SelCH == 4: Ch 0 + Ch 2
// SelCh == 5: Ch 0 + Ch 3
// SelCh == 6: Ch 1 + Ch 2
// SelCh == 7: Ch 1 + Ch 3
if ( iSelCH >= iNumInChan )
{
iSelAddCHOut = ( ( iSelCH - iNumInChan ) % 2 ) + 2;
iSelCHOut = ( iSelCH - iNumInChan ) / 2;
}
else
{
iSelAddCHOut = -1;
iSelCHOut = iSelCH;
}
}
int iASIOBufferSizeMono;
int iASIOBufferSizeStereo;